diff options
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | debianutils/mktemp.c | 2 | ||||
-rw-r--r-- | editors/patch.c | 851 | ||||
-rw-r--r-- | editors/patch_bbox.c | 306 | ||||
-rw-r--r-- | editors/sed.c | 27 | ||||
-rw-r--r-- | findutils/grep.c | 10 | ||||
-rw-r--r-- | include/libbb.h | 1 | ||||
-rw-r--r-- | libbb/wfopen_input.c | 10 | ||||
-rw-r--r-- | shell/ash.c | 1 | ||||
-rwxr-xr-x[-rw-r--r--] | shell/ash_test/ash-redir/redir9.tests | 0 | ||||
-rw-r--r-- | shell/ash_test/ash-signals/signal7.right | 1 | ||||
-rwxr-xr-x | shell/ash_test/ash-signals/signal7.tests | 18 | ||||
-rw-r--r-- | shell/hush.c | 4 | ||||
-rw-r--r-- | shell/hush_test/hush-trap/signal7.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-trap/signal7.tests | 18 | ||||
-rw-r--r-- | shell/shell_common.c | 7 | ||||
-rwxr-xr-x | testsuite/grep.tests | 4 | ||||
-rwxr-xr-x | testsuite/mdev.tests | 10 | ||||
-rwxr-xr-x | testsuite/patch.tests | 74 | ||||
-rwxr-xr-x | testsuite/sed.tests | 21 | ||||
-rw-r--r-- | util-linux/mdev.c | 5 |
21 files changed, 1083 insertions, 303 deletions
@@ -1,6 +1,6 @@ | |||
1 | VERSION = 1 | 1 | VERSION = 1 |
2 | PATCHLEVEL = 17 | 2 | PATCHLEVEL = 17 |
3 | SUBLEVEL = 1 | 3 | SUBLEVEL = 2 |
4 | EXTRAVERSION = | 4 | EXTRAVERSION = |
5 | NAME = Unnamed | 5 | NAME = Unnamed |
6 | 6 | ||
@@ -433,7 +433,12 @@ ifeq ($(config-targets),1) | |||
433 | -include $(srctree)/arch/$(ARCH)/Makefile | 433 | -include $(srctree)/arch/$(ARCH)/Makefile |
434 | export KBUILD_DEFCONFIG | 434 | export KBUILD_DEFCONFIG |
435 | 435 | ||
436 | config %config: scripts_basic outputmakefile gen_build_files FORCE | 436 | config: scripts_basic outputmakefile gen_build_files FORCE |
437 | $(Q)mkdir -p include | ||
438 | $(Q)$(MAKE) $(build)=scripts/kconfig $@ | ||
439 | $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease | ||
440 | |||
441 | %config: scripts_basic outputmakefile gen_build_files FORCE | ||
437 | $(Q)mkdir -p include | 442 | $(Q)mkdir -p include |
438 | $(Q)$(MAKE) $(build)=scripts/kconfig $@ | 443 | $(Q)$(MAKE) $(build)=scripts/kconfig $@ |
439 | $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease | 444 | $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease |
@@ -1285,9 +1290,13 @@ endif | |||
1285 | $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) | 1290 | $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) |
1286 | 1291 | ||
1287 | # Modules | 1292 | # Modules |
1288 | / %/: prepare scripts FORCE | 1293 | %/: prepare scripts FORCE |
1289 | $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ | 1294 | $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ |
1290 | $(build)=$(build-dir) | 1295 | $(build)=$(build-dir) |
1296 | /: prepare scripts FORCE | ||
1297 | $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ | ||
1298 | $(build)=$(build-dir) | ||
1299 | |||
1291 | %.ko: prepare scripts FORCE | 1300 | %.ko: prepare scripts FORCE |
1292 | $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ | 1301 | $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ |
1293 | $(build)=$(build-dir) $(@:.ko=.o) | 1302 | $(build)=$(build-dir) $(@:.ko=.o) |
diff --git a/debianutils/mktemp.c b/debianutils/mktemp.c index 2c4e19670..7ce9f1096 100644 --- a/debianutils/mktemp.c +++ b/debianutils/mktemp.c | |||
@@ -50,7 +50,7 @@ int mktemp_main(int argc UNUSED_PARAM, char **argv) | |||
50 | opts = getopt32(argv, "dqtp:", &path); | 50 | opts = getopt32(argv, "dqtp:", &path); |
51 | 51 | ||
52 | chp = argv[optind] ? argv[optind] : xstrdup("tmp.XXXXXX"); | 52 | chp = argv[optind] ? argv[optind] : xstrdup("tmp.XXXXXX"); |
53 | if (chp[0] != '/' || (opts & 8)) | 53 | if (!strchr(chp, '/') || (opts & 8)) |
54 | chp = concat_path_file(path, chp); | 54 | chp = concat_path_file(path, chp); |
55 | 55 | ||
56 | if (opts & 1) { /* -d */ | 56 | if (opts & 1) { /* -d */ |
diff --git a/editors/patch.c b/editors/patch.c index 62477af16..c40f54155 100644 --- a/editors/patch.c +++ b/editors/patch.c | |||
@@ -1,306 +1,621 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | 1 | /* Adapted from toybox's patch. */ |
2 | /* | 2 | |
3 | * busybox patch applet to handle the unified diff format. | 3 | /* vi: set sw=4 ts=4: |
4 | * Copyright (C) 2003 Glenn McGrath | 4 | * |
5 | * patch.c - Apply a "universal" diff. | ||
5 | * | 6 | * |
6 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | 7 | * Copyright 2007 Rob Landley <rob@landley.net> |
7 | * | 8 | * |
8 | * This applet is written to work with patches generated by GNU diff, | 9 | * see http://www.opengroup.org/onlinepubs/009695399/utilities/patch.html |
9 | * where there is equivalent functionality busybox patch shall behave | 10 | * (But only does -u, because who still cares about "ed"?) |
10 | * as per GNU patch. | ||
11 | * | 11 | * |
12 | * There is a SUSv3 specification for patch, however it looks to be | 12 | * TODO: |
13 | * incomplete, it doesnt even mention unified diff format. | 13 | * -b backup |
14 | * http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html | 14 | * -l treat all whitespace as a single space |
15 | * -d chdir first | ||
16 | * -D define wrap #ifdef and #ifndef around changes | ||
17 | * -o outfile output here instead of in place | ||
18 | * -r rejectfile write rejected hunks to this file | ||
15 | * | 19 | * |
16 | * Issues | 20 | * -E remove empty files --remove-empty-files |
17 | * - Non-interactive | 21 | * -f force (no questions asked) |
18 | * - Patches must apply cleanly or patch (not just one hunk) will fail. | 22 | * -F fuzz (number, default 2) |
19 | * - Reject file isnt saved | 23 | * [file] which file to patch |
20 | */ | 24 | |
25 | USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"up#i:R", TOYFLAG_USR|TOYFLAG_BIN)) | ||
26 | |||
27 | config PATCH | ||
28 | bool "patch" | ||
29 | default y | ||
30 | help | ||
31 | usage: patch [-i file] [-p depth] [-Ru] | ||
32 | |||
33 | Apply a unified diff to one or more files. | ||
21 | 34 | ||
35 | -i Input file (defaults=stdin) | ||
36 | -p number of '/' to strip from start of file paths (default=all) | ||
37 | -R Reverse patch. | ||
38 | -u Ignored (only handles "unified" diffs) | ||
39 | |||
40 | This version of patch only handles unified diffs, and only modifies | ||
41 | a file when all all hunks to that file apply. Patch prints failed | ||
42 | hunks to stderr, and exits with nonzero status if any hunks fail. | ||
43 | |||
44 | A file compared against /dev/null (or with a date <= the epoch) is | ||
45 | created/deleted as appropriate. | ||
46 | */ | ||
22 | #include "libbb.h" | 47 | #include "libbb.h" |
23 | 48 | ||
24 | static unsigned copy_lines(FILE *src_stream, FILE *dst_stream, unsigned lines_count) | 49 | struct double_list { |
50 | struct double_list *next; | ||
51 | struct double_list *prev; | ||
52 | char *data; | ||
53 | }; | ||
54 | |||
55 | // Return the first item from the list, advancing the list (which must be called | ||
56 | // as &list) | ||
57 | static | ||
58 | void *TOY_llist_pop(void *list) | ||
25 | { | 59 | { |
26 | while (src_stream && lines_count) { | 60 | // I'd use a void ** for the argument, and even accept the typecast in all |
27 | char *line; | 61 | // callers as documentation you need the &, except the stupid compiler |
28 | line = xmalloc_fgets(src_stream); | 62 | // would then scream about type-punned pointers. Screw it. |
29 | if (line == NULL) { | 63 | void **llist = (void **)list; |
30 | break; | 64 | void **next = (void **)*llist; |
31 | } | 65 | *llist = *next; |
32 | if (fputs(line, dst_stream) == EOF) { | 66 | |
33 | bb_perror_msg_and_die("error writing to new file"); | 67 | return (void *)next; |
68 | } | ||
69 | |||
70 | // Free all the elements of a linked list | ||
71 | // if freeit!=NULL call freeit() on each element before freeing it. | ||
72 | static | ||
73 | void TOY_llist_free(void *list, void (*freeit)(void *data)) | ||
74 | { | ||
75 | while (list) { | ||
76 | void *pop = TOY_llist_pop(&list); | ||
77 | if (freeit) freeit(pop); | ||
78 | else free(pop); | ||
79 | |||
80 | // End doubly linked list too. | ||
81 | if (list==pop) break; | ||
82 | } | ||
83 | } | ||
84 | //Override bbox's names | ||
85 | #define llist_pop TOY_llist_pop | ||
86 | #define llist_free TOY_llist_free | ||
87 | |||
88 | // Add an entry to the end off a doubly linked list | ||
89 | static | ||
90 | struct double_list *dlist_add(struct double_list **list, char *data) | ||
91 | { | ||
92 | struct double_list *line = xmalloc(sizeof(struct double_list)); | ||
93 | |||
94 | line->data = data; | ||
95 | if (*list) { | ||
96 | line->next = *list; | ||
97 | line->prev = (*list)->prev; | ||
98 | (*list)->prev->next = line; | ||
99 | (*list)->prev = line; | ||
100 | } else *list = line->next = line->prev = line; | ||
101 | |||
102 | return line; | ||
103 | } | ||
104 | |||
105 | // Ensure entire path exists. | ||
106 | // If mode != -1 set permissions on newly created dirs. | ||
107 | // Requires that path string be writable (for temporary null terminators). | ||
108 | static | ||
109 | void xmkpath(char *path, int mode) | ||
110 | { | ||
111 | char *p, old; | ||
112 | mode_t mask; | ||
113 | int rc; | ||
114 | struct stat st; | ||
115 | |||
116 | for (p = path; ; p++) { | ||
117 | if (!*p || *p == '/') { | ||
118 | old = *p; | ||
119 | *p = rc = 0; | ||
120 | if (stat(path, &st) || !S_ISDIR(st.st_mode)) { | ||
121 | if (mode != -1) { | ||
122 | mask = umask(0); | ||
123 | rc = mkdir(path, mode); | ||
124 | umask(mask); | ||
125 | } else rc = mkdir(path, 0777); | ||
126 | } | ||
127 | *p = old; | ||
128 | if(rc) bb_perror_msg_and_die("mkpath '%s'", path); | ||
34 | } | 129 | } |
35 | free(line); | 130 | if (!*p) break; |
36 | lines_count--; | 131 | } |
132 | } | ||
133 | |||
134 | // Slow, but small. | ||
135 | static | ||
136 | char *get_rawline(int fd, long *plen, char end) | ||
137 | { | ||
138 | char c, *buf = NULL; | ||
139 | long len = 0; | ||
140 | |||
141 | for (;;) { | ||
142 | if (1>read(fd, &c, 1)) break; | ||
143 | if (!(len & 63)) buf=xrealloc(buf, len+65); | ||
144 | if ((buf[len++]=c) == end) break; | ||
145 | } | ||
146 | if (buf) buf[len]=0; | ||
147 | if (plen) *plen = len; | ||
148 | |||
149 | return buf; | ||
150 | } | ||
151 | |||
152 | static | ||
153 | char *get_line(int fd) | ||
154 | { | ||
155 | long len; | ||
156 | char *buf = get_rawline(fd, &len, '\n'); | ||
157 | |||
158 | if (buf && buf[--len]=='\n') buf[len]=0; | ||
159 | |||
160 | return buf; | ||
161 | } | ||
162 | |||
163 | // Copy the rest of in to out and close both files. | ||
164 | static | ||
165 | void xsendfile(int in, int out) | ||
166 | { | ||
167 | long len; | ||
168 | char buf[4096]; | ||
169 | |||
170 | if (in<0) return; | ||
171 | for (;;) { | ||
172 | len = safe_read(in, buf, 4096); | ||
173 | if (len<1) break; | ||
174 | xwrite(out, buf, len); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | // Copy the rest of the data and replace the original with the copy. | ||
179 | static | ||
180 | void replace_tempfile(int fdin, int fdout, char **tempname) | ||
181 | { | ||
182 | char *temp = xstrdup(*tempname); | ||
183 | |||
184 | temp[strlen(temp)-6]=0; | ||
185 | if (fdin != -1) { | ||
186 | xsendfile(fdin, fdout); | ||
187 | xclose(fdin); | ||
37 | } | 188 | } |
38 | return lines_count; | 189 | xclose(fdout); |
190 | rename(*tempname, temp); | ||
191 | free(*tempname); | ||
192 | free(temp); | ||
193 | *tempname = NULL; | ||
194 | } | ||
195 | |||
196 | // Open a temporary file to copy an existing file into. | ||
197 | static | ||
198 | int copy_tempfile(int fdin, char *name, char **tempname) | ||
199 | { | ||
200 | struct stat statbuf; | ||
201 | int fd; | ||
202 | |||
203 | *tempname = xasprintf("%sXXXXXX", name); | ||
204 | fd = mkstemp(*tempname); | ||
205 | if(-1 == fd) bb_perror_msg_and_die("no temp file"); | ||
206 | |||
207 | // Set permissions of output file | ||
208 | fstat(fdin, &statbuf); | ||
209 | fchmod(fd, statbuf.st_mode); | ||
210 | |||
211 | return fd; | ||
39 | } | 212 | } |
40 | 213 | ||
41 | /* If patch_level is -1 it will remove all directory names | 214 | // Abort the copy and delete the temporary file. |
42 | * char *line must be greater than 4 chars | 215 | static |
43 | * returns NULL if the file doesnt exist or error | 216 | void delete_tempfile(int fdin, int fdout, char **tempname) |
44 | * returns malloc'ed filename | ||
45 | * NB: frees 1st argument! | ||
46 | */ | ||
47 | static char *extract_filename(char *line, int patch_level, const char *pat) | ||
48 | { | 217 | { |
49 | char *temp = NULL, *filename_start_ptr = line + 4; | 218 | close(fdin); |
50 | 219 | close(fdout); | |
51 | if (strncmp(line, pat, 4) == 0) { | 220 | unlink(*tempname); |
52 | /* Terminate string at end of source filename */ | 221 | free(*tempname); |
53 | line[strcspn(line, "\t\n\r")] = '\0'; | 222 | *tempname = NULL; |
54 | 223 | } | |
55 | /* Skip over (patch_level) number of leading directories */ | 224 | |
56 | while (patch_level--) { | 225 | |
57 | temp = strchr(filename_start_ptr, '/'); | 226 | |
58 | if (!temp) | 227 | struct globals { |
59 | break; | 228 | char *infile; |
60 | filename_start_ptr = temp + 1; | 229 | long prefix; |
230 | |||
231 | struct double_list *current_hunk; | ||
232 | long oldline, oldlen, newline, newlen; | ||
233 | long linenum; | ||
234 | int context, state, filein, fileout, filepatch, hunknum; | ||
235 | char *tempname; | ||
236 | |||
237 | // was toys.foo: | ||
238 | int exitval; | ||
239 | }; | ||
240 | #define TT (*ptr_to_globals) | ||
241 | #define INIT_TT() do { \ | ||
242 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(TT))); \ | ||
243 | } while (0) | ||
244 | |||
245 | |||
246 | #define FLAG_STR "Rup:i:Nx" | ||
247 | /* FLAG_REVERSE must be == 1! Code uses this fact. */ | ||
248 | #define FLAG_REVERSE (1 << 0) | ||
249 | #define FLAG_u (1 << 1) | ||
250 | #define FLAG_PATHLEN (1 << 2) | ||
251 | #define FLAG_INPUT (1 << 3) | ||
252 | #define FLAG_IGNORE (1 << 4) | ||
253 | //non-standard: | ||
254 | #define FLAG_DEBUG (1 << 5) | ||
255 | |||
256 | // Dispose of a line of input, either by writing it out or discarding it. | ||
257 | |||
258 | // state < 2: just free | ||
259 | // state = 2: write whole line to stderr | ||
260 | // state = 3: write whole line to fileout | ||
261 | // state > 3: write line+1 to fileout when *line != state | ||
262 | |||
263 | #define PATCH_DEBUG (option_mask32 & FLAG_DEBUG) | ||
264 | |||
265 | static void do_line(void *data) | ||
266 | { | ||
267 | struct double_list *dlist = (struct double_list *)data; | ||
268 | |||
269 | if (TT.state>1 && *dlist->data != TT.state) | ||
270 | fdprintf(TT.state == 2 ? 2 : TT.fileout, | ||
271 | "%s\n", dlist->data+(TT.state>3 ? 1 : 0)); | ||
272 | |||
273 | if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data); | ||
274 | |||
275 | free(dlist->data); | ||
276 | free(data); | ||
277 | } | ||
278 | |||
279 | static void finish_oldfile(void) | ||
280 | { | ||
281 | if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname); | ||
282 | TT.fileout = TT.filein = -1; | ||
283 | } | ||
284 | |||
285 | static void fail_hunk(void) | ||
286 | { | ||
287 | if (!TT.current_hunk) return; | ||
288 | TT.current_hunk->prev->next = 0; | ||
289 | |||
290 | fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline); | ||
291 | TT.exitval = 1; | ||
292 | |||
293 | // If we got to this point, we've seeked to the end. Discard changes to | ||
294 | // this file and advance to next file. | ||
295 | |||
296 | TT.state = 2; | ||
297 | llist_free(TT.current_hunk, do_line); | ||
298 | TT.current_hunk = NULL; | ||
299 | delete_tempfile(TT.filein, TT.fileout, &TT.tempname); | ||
300 | TT.state = 0; | ||
301 | } | ||
302 | |||
303 | // Given a hunk of a unified diff, make the appropriate change to the file. | ||
304 | // This does not use the location information, but instead treats a hunk | ||
305 | // as a sort of regex. Copies data from input to output until it finds | ||
306 | // the change to be made, then outputs the changed data and returns. | ||
307 | // (Finding EOF first is an error.) This is a single pass operation, so | ||
308 | // multiple hunks must occur in order in the file. | ||
309 | |||
310 | static int apply_one_hunk(void) | ||
311 | { | ||
312 | struct double_list *plist, *buf = NULL, *check; | ||
313 | int matcheof = 0, reverse = option_mask32 & FLAG_REVERSE, backwarn = 0; | ||
314 | /* Do we try "dummy" revert to check whether | ||
315 | * to silently skip this hunk? Used to implement -N. | ||
316 | */ | ||
317 | int dummy_revert = 0; | ||
318 | |||
319 | // Break doubly linked list so we can use singly linked traversal function. | ||
320 | TT.current_hunk->prev->next = NULL; | ||
321 | |||
322 | // Match EOF if there aren't as many ending context lines as beginning | ||
323 | for (plist = TT.current_hunk; plist; plist = plist->next) { | ||
324 | if (plist->data[0]==' ') matcheof++; | ||
325 | else matcheof = 0; | ||
326 | if (PATCH_DEBUG) fdprintf(2, "HUNK:%s\n", plist->data); | ||
327 | } | ||
328 | matcheof = matcheof < TT.context; | ||
329 | |||
330 | if (PATCH_DEBUG) fdprintf(2,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N'); | ||
331 | |||
332 | // Loop through input data searching for this hunk. Match all context | ||
333 | // lines and all lines to be removed until we've found the end of a | ||
334 | // complete hunk. | ||
335 | plist = TT.current_hunk; | ||
336 | buf = NULL; | ||
337 | if (TT.context) for (;;) { | ||
338 | char *data = get_line(TT.filein); | ||
339 | |||
340 | TT.linenum++; | ||
341 | |||
342 | // Figure out which line of hunk to compare with next. (Skip lines | ||
343 | // of the hunk we'd be adding.) | ||
344 | while (plist && *plist->data == "+-"[reverse]) { | ||
345 | if (data && !strcmp(data, plist->data+1)) { | ||
346 | if (!backwarn) { | ||
347 | backwarn++; | ||
348 | if (option_mask32 & FLAG_IGNORE) { | ||
349 | dummy_revert = 1; | ||
350 | reverse ^= 1; | ||
351 | continue; | ||
352 | } | ||
353 | fdprintf(2,"Possibly reversed hunk %d at %ld\n", | ||
354 | TT.hunknum, TT.linenum); | ||
355 | } | ||
356 | } | ||
357 | plist = plist->next; | ||
61 | } | 358 | } |
62 | temp = xstrdup(filename_start_ptr); | 359 | |
360 | // Is this EOF? | ||
361 | if (!data) { | ||
362 | if (PATCH_DEBUG) fdprintf(2, "INEOF\n"); | ||
363 | |||
364 | // Does this hunk need to match EOF? | ||
365 | if (!plist && matcheof) break; | ||
366 | |||
367 | // File ended before we found a place for this hunk. | ||
368 | fail_hunk(); | ||
369 | goto done; | ||
370 | } else if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data); | ||
371 | check = dlist_add(&buf, data); | ||
372 | |||
373 | // Compare this line with next expected line of hunk. | ||
374 | // todo: teach the strcmp() to ignore whitespace. | ||
375 | |||
376 | // A match can fail because the next line doesn't match, or because | ||
377 | // we hit the end of a hunk that needed EOF, and this isn't EOF. | ||
378 | |||
379 | // If match failed, flush first line of buffered data and | ||
380 | // recheck buffered data for a new match until we find one or run | ||
381 | // out of buffer. | ||
382 | |||
383 | for (;;) { | ||
384 | if (!plist || strcmp(check->data, plist->data+1)) { | ||
385 | // Match failed. Write out first line of buffered data and | ||
386 | // recheck remaining buffered data for a new match. | ||
387 | |||
388 | if (PATCH_DEBUG) | ||
389 | fdprintf(2, "NOT: %s\n", plist->data); | ||
390 | |||
391 | TT.state = 3; | ||
392 | check = llist_pop(&buf); | ||
393 | check->prev->next = buf; | ||
394 | buf->prev = check->prev; | ||
395 | do_line(check); | ||
396 | plist = TT.current_hunk; | ||
397 | |||
398 | // If we've reached the end of the buffer without confirming a | ||
399 | // match, read more lines. | ||
400 | if (check==buf) { | ||
401 | buf = 0; | ||
402 | break; | ||
403 | } | ||
404 | check = buf; | ||
405 | } else { | ||
406 | if (PATCH_DEBUG) | ||
407 | fdprintf(2, "MAYBE: %s\n", plist->data); | ||
408 | // This line matches. Advance plist, detect successful match. | ||
409 | plist = plist->next; | ||
410 | if (!plist && !matcheof) goto out; | ||
411 | check = check->next; | ||
412 | if (check == buf) break; | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | out: | ||
417 | // We have a match. Emit changed data. | ||
418 | TT.state = "-+"[reverse ^ dummy_revert]; | ||
419 | llist_free(TT.current_hunk, do_line); | ||
420 | TT.current_hunk = NULL; | ||
421 | TT.state = 1; | ||
422 | done: | ||
423 | if (buf) { | ||
424 | buf->prev->next = NULL; | ||
425 | llist_free(buf, do_line); | ||
63 | } | 426 | } |
64 | free(line); | 427 | |
65 | return temp; | 428 | return TT.state; |
66 | } | 429 | } |
67 | 430 | ||
431 | // Read a patch file and find hunks, opening/creating/deleting files. | ||
432 | // Call apply_one_hunk() on each hunk. | ||
433 | |||
434 | // state 0: Not in a hunk, look for +++. | ||
435 | // state 1: Found +++ file indicator, look for @@ | ||
436 | // state 2: In hunk: counting initial context lines | ||
437 | // state 3: In hunk: getting body | ||
438 | |||
68 | int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 439 | int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
69 | int patch_main(int argc UNUSED_PARAM, char **argv) | 440 | int patch_main(int argc UNUSED_PARAM, char **argv) |
70 | { | 441 | { |
71 | struct stat saved_stat; | 442 | int opts; |
72 | char *patch_line; | 443 | int reverse, state = 0; |
73 | FILE *patch_file; | 444 | char *oldname = NULL, *newname = NULL; |
74 | int patch_level; | 445 | char *opt_p, *opt_i; |
75 | int ret = 0; | 446 | |
76 | char plus = '+'; | 447 | INIT_TT(); |
77 | unsigned opt; | 448 | |
78 | enum { | 449 | opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i); |
79 | OPT_R = (1 << 2), | 450 | argv += optind; |
80 | OPT_N = (1 << 3), | 451 | reverse = opts & FLAG_REVERSE; |
81 | /*OPT_f = (1 << 4), ignored */ | 452 | TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative! |
82 | /*OPT_E = (1 << 5), ignored, this is the default */ | 453 | TT.filein = TT.fileout = -1; |
83 | /*OPT_g = (1 << 6), ignored */ | 454 | if (opts & FLAG_INPUT) { |
84 | OPT_dry_run = (1 << 7) * ENABLE_LONG_OPTS, | 455 | TT.filepatch = xopen_stdin(opt_i); |
85 | }; | 456 | } else { |
86 | 457 | if (argv[0] && argv[1]) { | |
87 | xfunc_error_retval = 2; | 458 | TT.filepatch = xopen_stdin(argv[1]); |
88 | { | 459 | } |
89 | const char *p = "-1"; | 460 | } |
90 | const char *i = "-"; /* compat */ | 461 | if (argv[0]) { |
91 | #if ENABLE_LONG_OPTS | 462 | oldname = xstrdup(argv[0]); |
92 | static const char patch_longopts[] ALIGN1 = | 463 | newname = xstrdup(argv[0]); |
93 | "strip\0" Required_argument "p" | ||
94 | "input\0" Required_argument "i" | ||
95 | "reverse\0" No_argument "R" | ||
96 | "forward\0" No_argument "N" | ||
97 | /* "Assume user knows what [s]he is doing, do not ask any questions": */ | ||
98 | "force\0" No_argument "f" /*ignored*/ | ||
99 | # if ENABLE_DESKTOP | ||
100 | "remove-empty-files\0" No_argument "E" /*ignored*/ | ||
101 | /* "Controls actions when a file is under RCS or SCCS control, | ||
102 | * and does not exist or is read-only and matches the default version, | ||
103 | * or when a file is under ClearCase control and does not exist..." | ||
104 | * IOW: rather obscure option. | ||
105 | * But Gentoo's portage does use -g0 */ | ||
106 | "get\0" Required_argument "g" /*ignored*/ | ||
107 | # endif | ||
108 | "dry-run\0" No_argument "\xfd" | ||
109 | # if ENABLE_DESKTOP | ||
110 | "backup-if-mismatch\0" No_argument "\xfe" /*ignored*/ | ||
111 | "no-backup-if-mismatch\0" No_argument "\xff" /*ignored*/ | ||
112 | # endif | ||
113 | ; | ||
114 | applet_long_options = patch_longopts; | ||
115 | #endif | ||
116 | /* -f,-E,-g are ignored */ | ||
117 | opt = getopt32(argv, "p:i:RN""fEg:", &p, &i, NULL); | ||
118 | if (opt & OPT_R) | ||
119 | plus = '-'; | ||
120 | patch_level = xatoi(p); /* can be negative! */ | ||
121 | patch_file = xfopen_stdin(i); | ||
122 | } | 464 | } |
123 | 465 | ||
124 | patch_line = xmalloc_fgetline(patch_file); | 466 | // Loop through the lines in the patch |
125 | while (patch_line) { | 467 | for(;;) { |
126 | FILE *src_stream; | 468 | char *patchline; |
127 | FILE *dst_stream; | 469 | |
128 | //char *old_filename; | 470 | patchline = get_line(TT.filepatch); |
129 | char *new_filename; | 471 | if (!patchline) break; |
130 | char *backup_filename = NULL; | 472 | |
131 | unsigned src_cur_line = 1; | 473 | // Other versions of patch accept damaged patches, |
132 | unsigned dst_cur_line = 0; | 474 | // so we need to also. |
133 | unsigned dst_beg_line; | 475 | if (!*patchline) { |
134 | unsigned bad_hunk_count = 0; | 476 | free(patchline); |
135 | unsigned hunk_count = 0; | 477 | patchline = xstrdup(" "); |
136 | smallint copy_trailing_lines_flag = 0; | ||
137 | |||
138 | /* Skip everything upto the "---" marker | ||
139 | * No need to parse the lines "Only in <dir>", and "diff <args>" | ||
140 | */ | ||
141 | do { | ||
142 | /* Extract the filename used before the patch was generated */ | ||
143 | new_filename = extract_filename(patch_line, patch_level, "--- "); | ||
144 | // was old_filename above | ||
145 | patch_line = xmalloc_fgetline(patch_file); | ||
146 | if (!patch_line) goto quit; | ||
147 | } while (!new_filename); | ||
148 | free(new_filename); // "source" filename is irrelevant | ||
149 | |||
150 | new_filename = extract_filename(patch_line, patch_level, "+++ "); | ||
151 | if (!new_filename) { | ||
152 | bb_error_msg_and_die("invalid patch"); | ||
153 | } | 478 | } |
154 | 479 | ||
155 | /* Get access rights from the file to be patched */ | 480 | // Are we assembling a hunk? |
156 | if (stat(new_filename, &saved_stat) != 0) { | 481 | if (state >= 2) { |
157 | char *slash = strrchr(new_filename, '/'); | 482 | if (*patchline==' ' || *patchline=='+' || *patchline=='-') { |
158 | if (slash) { | 483 | dlist_add(&TT.current_hunk, patchline); |
159 | /* Create leading directories */ | 484 | |
160 | *slash = '\0'; | 485 | if (*patchline != '+') TT.oldlen--; |
161 | bb_make_directory(new_filename, -1, FILEUTILS_RECUR); | 486 | if (*patchline != '-') TT.newlen--; |
162 | *slash = '/'; | 487 | |
488 | // Context line? | ||
489 | if (*patchline==' ' && state==2) TT.context++; | ||
490 | else state=3; | ||
491 | |||
492 | // If we've consumed all expected hunk lines, apply the hunk. | ||
493 | |||
494 | if (!TT.oldlen && !TT.newlen) state = apply_one_hunk(); | ||
495 | continue; | ||
163 | } | 496 | } |
164 | src_stream = NULL; | 497 | fail_hunk(); |
165 | saved_stat.st_mode = 0644; | 498 | state = 0; |
166 | } else if (!(opt & OPT_dry_run)) { | 499 | continue; |
167 | backup_filename = xasprintf("%s.orig", new_filename); | ||
168 | xrename(new_filename, backup_filename); | ||
169 | src_stream = xfopen_for_read(backup_filename); | ||
170 | } else | ||
171 | src_stream = xfopen_for_read(new_filename); | ||
172 | |||
173 | if (opt & OPT_dry_run) { | ||
174 | dst_stream = xfopen_for_write("/dev/null"); | ||
175 | } else { | ||
176 | dst_stream = xfopen_for_write(new_filename); | ||
177 | fchmod(fileno(dst_stream), saved_stat.st_mode); | ||
178 | } | 500 | } |
179 | 501 | ||
180 | printf("patching file %s\n", new_filename); | 502 | // Open a new file? |
181 | 503 | if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) { | |
182 | /* Handle all hunks for this file */ | 504 | char *s, **name = reverse ? &newname : &oldname; |
183 | patch_line = xmalloc_fgets(patch_file); | 505 | int i; |
184 | while (patch_line) { | 506 | |
185 | unsigned count; | 507 | if (*patchline == '+') { |
186 | unsigned src_beg_line; | 508 | name = reverse ? &oldname : &newname; |
187 | unsigned hunk_offset_start; | 509 | state = 1; |
188 | unsigned src_last_line = 1; | ||
189 | unsigned dst_last_line = 1; | ||
190 | |||
191 | if ((sscanf(patch_line, "@@ -%d,%d +%d,%d", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3) | ||
192 | && (sscanf(patch_line, "@@ -%d +%d,%d", &src_beg_line, &dst_beg_line, &dst_last_line) < 2) | ||
193 | ) { | ||
194 | /* No more hunks for this file */ | ||
195 | break; | ||
196 | } | ||
197 | if (plus != '+') { | ||
198 | /* reverse patch */ | ||
199 | unsigned tmp = src_last_line; | ||
200 | src_last_line = dst_last_line; | ||
201 | dst_last_line = tmp; | ||
202 | tmp = src_beg_line; | ||
203 | src_beg_line = dst_beg_line; | ||
204 | dst_beg_line = tmp; | ||
205 | } | 510 | } |
206 | hunk_count++; | 511 | |
207 | 512 | finish_oldfile(); | |
208 | if (src_beg_line && dst_beg_line) { | 513 | |
209 | /* Copy unmodified lines upto start of hunk */ | 514 | if (!argv[0]) { |
210 | /* src_beg_line will be 0 if it's a new file */ | 515 | free(*name); |
211 | count = src_beg_line - src_cur_line; | 516 | // Trim date from end of filename (if any). We don't care. |
212 | if (copy_lines(src_stream, dst_stream, count)) { | 517 | for (s = patchline+4; *s && *s!='\t'; s++) |
213 | bb_error_msg_and_die("bad src file"); | 518 | if (*s=='\\' && s[1]) s++; |
519 | i = atoi(s); | ||
520 | if (i>1900 && i<=1970) | ||
521 | *name = xstrdup("/dev/null"); | ||
522 | else { | ||
523 | *s = 0; | ||
524 | *name = xstrdup(patchline+4); | ||
214 | } | 525 | } |
215 | src_cur_line += count; | ||
216 | dst_cur_line += count; | ||
217 | copy_trailing_lines_flag = 1; | ||
218 | } | 526 | } |
219 | src_last_line += hunk_offset_start = src_cur_line; | 527 | |
220 | dst_last_line += dst_cur_line; | 528 | // We defer actually opening the file because svn produces broken |
221 | 529 | // patches that don't signal they want to create a new file the | |
222 | while (1) { | 530 | // way the patch man page says, so you have to read the first hunk |
223 | free(patch_line); | 531 | // and _guess_. |
224 | patch_line = xmalloc_fgets(patch_file); | 532 | |
225 | if (patch_line == NULL) | 533 | // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@ |
226 | break; /* EOF */ | 534 | // but a missing ,value means the value is 1. |
227 | if (!*patch_line) { | 535 | } else if (state == 1 && !strncmp("@@ -", patchline, 4)) { |
228 | /* whitespace-damaged patch with "" lines */ | 536 | int i; |
229 | free(patch_line); | 537 | char *s = patchline+4; |
230 | patch_line = xstrdup(" "); | 538 | |
539 | // Read oldline[,oldlen] +newline[,newlen] | ||
540 | |||
541 | TT.oldlen = TT.newlen = 1; | ||
542 | TT.oldline = strtol(s, &s, 10); | ||
543 | if (*s == ',') TT.oldlen=strtol(s+1, &s, 10); | ||
544 | TT.newline = strtol(s+2, &s, 10); | ||
545 | if (*s == ',') TT.newlen = strtol(s+1, &s, 10); | ||
546 | |||
547 | TT.context = 0; | ||
548 | state = 2; | ||
549 | |||
550 | // If this is the first hunk, open the file. | ||
551 | if (TT.filein == -1) { | ||
552 | int oldsum, newsum, del = 0; | ||
553 | char *name; | ||
554 | |||
555 | oldsum = TT.oldline + TT.oldlen; | ||
556 | newsum = TT.newline + TT.newlen; | ||
557 | |||
558 | name = reverse ? oldname : newname; | ||
559 | |||
560 | // We're deleting oldname if new file is /dev/null (before -p) | ||
561 | // or if new hunk is empty (zero context) after patching | ||
562 | if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) | ||
563 | { | ||
564 | name = reverse ? newname : oldname; | ||
565 | del++; | ||
231 | } | 566 | } |
232 | if ((*patch_line != '-') && (*patch_line != '+') | 567 | |
233 | && (*patch_line != ' ') | 568 | // handle -p path truncation. |
234 | ) { | 569 | for (i=0, s = name; *s;) { |
235 | break; /* End of hunk */ | 570 | if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i) break; |
571 | if (*(s++)=='/') { | ||
572 | name = s; | ||
573 | i++; | ||
574 | } | ||
236 | } | 575 | } |
237 | if (*patch_line != plus) { /* '-' or ' ' */ | 576 | |
238 | char *src_line = NULL; | 577 | if (del) { |
239 | if (src_cur_line == src_last_line) | 578 | printf("removing %s\n", name); |
240 | break; | 579 | xunlink(name); |
241 | if (src_stream) { | 580 | state = 0; |
242 | src_line = xmalloc_fgets(src_stream); | 581 | // If we've got a file to open, do so. |
243 | if (src_line) { | 582 | } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) { |
244 | int diff = strcmp(src_line, patch_line + 1); | 583 | // If the old file was null, we're creating a new one. |
245 | src_cur_line++; | 584 | if (!strcmp(oldname, "/dev/null") || !oldsum) { |
246 | free(src_line); | 585 | printf("creating %s\n", name); |
247 | if (diff) | 586 | s = strrchr(name, '/'); |
248 | src_line = NULL; | 587 | if (s) { |
588 | *s = 0; | ||
589 | xmkpath(name, -1); | ||
590 | *s = '/'; | ||
249 | } | 591 | } |
592 | TT.filein = xopen3(name, O_CREAT|O_EXCL|O_RDWR, 0666); | ||
593 | } else { | ||
594 | printf("patching file %s\n", name); | ||
595 | TT.filein = xopen(name, O_RDWR); | ||
250 | } | 596 | } |
251 | /* Do not patch an already patched hunk with -N */ | 597 | TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname); |
252 | if (src_line == 0 && (opt & OPT_N)) { | 598 | TT.linenum = 0; |
253 | continue; | 599 | TT.hunknum = 0; |
254 | } | ||
255 | if (!src_line) { | ||
256 | bb_error_msg("hunk #%u FAILED at %u", hunk_count, hunk_offset_start); | ||
257 | bad_hunk_count++; | ||
258 | break; | ||
259 | } | ||
260 | if (*patch_line != ' ') { /* '-' */ | ||
261 | continue; | ||
262 | } | ||
263 | } | 600 | } |
264 | if (dst_cur_line == dst_last_line) | ||
265 | break; | ||
266 | fputs(patch_line + 1, dst_stream); | ||
267 | dst_cur_line++; | ||
268 | } /* end of while loop handling one hunk */ | ||
269 | } /* end of while loop handling one file */ | ||
270 | |||
271 | /* Cleanup last patched file */ | ||
272 | if (copy_trailing_lines_flag) { | ||
273 | copy_lines(src_stream, dst_stream, (unsigned)(-1)); | ||
274 | } | ||
275 | if (src_stream) { | ||
276 | fclose(src_stream); | ||
277 | } | ||
278 | fclose(dst_stream); | ||
279 | if (bad_hunk_count) { | ||
280 | ret = 1; | ||
281 | bb_error_msg("%u out of %u hunk FAILED", bad_hunk_count, hunk_count); | ||
282 | } else { | ||
283 | /* It worked, we can remove the backup */ | ||
284 | if (backup_filename) { | ||
285 | unlink(backup_filename); | ||
286 | } | ||
287 | if (!(opt & OPT_dry_run) | ||
288 | && ((dst_cur_line == 0) || (dst_beg_line == 0)) | ||
289 | ) { | ||
290 | /* The new patched file is empty, remove it */ | ||
291 | xunlink(new_filename); | ||
292 | // /* old_filename and new_filename may be the same file */ | ||
293 | // unlink(old_filename); | ||
294 | } | 601 | } |
602 | |||
603 | TT.hunknum++; | ||
604 | |||
605 | continue; | ||
295 | } | 606 | } |
296 | free(backup_filename); | 607 | |
297 | //free(old_filename); | 608 | // If we didn't continue above, discard this line. |
298 | free(new_filename); | 609 | free(patchline); |
299 | } /* end of "while there are patch lines" */ | 610 | } |
300 | quit: | 611 | |
301 | /* 0 = SUCCESS | 612 | finish_oldfile(); |
302 | * 1 = Some hunks failed | 613 | |
303 | * 2 = More serious problems (exited earlier) | 614 | if (ENABLE_FEATURE_CLEAN_UP) { |
304 | */ | 615 | close(TT.filepatch); |
305 | return ret; | 616 | free(oldname); |
617 | free(newname); | ||
618 | } | ||
619 | |||
620 | return TT.exitval; | ||
306 | } | 621 | } |
diff --git a/editors/patch_bbox.c b/editors/patch_bbox.c new file mode 100644 index 000000000..78aa5fde8 --- /dev/null +++ b/editors/patch_bbox.c | |||
@@ -0,0 +1,306 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * busybox patch applet to handle the unified diff format. | ||
4 | * Copyright (C) 2003 Glenn McGrath | ||
5 | * | ||
6 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
7 | * | ||
8 | * This applet is written to work with patches generated by GNU diff, | ||
9 | * where there is equivalent functionality busybox patch shall behave | ||
10 | * as per GNU patch. | ||
11 | * | ||
12 | * There is a SUSv3 specification for patch, however it looks to be | ||
13 | * incomplete, it doesnt even mention unified diff format. | ||
14 | * http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html | ||
15 | * | ||
16 | * Issues | ||
17 | * - Non-interactive | ||
18 | * - Patches must apply cleanly or patch (not just one hunk) will fail. | ||
19 | * - Reject file isnt saved | ||
20 | */ | ||
21 | |||
22 | #include "libbb.h" | ||
23 | |||
24 | static unsigned copy_lines(FILE *src_stream, FILE *dst_stream, unsigned lines_count) | ||
25 | { | ||
26 | while (src_stream && lines_count) { | ||
27 | char *line; | ||
28 | line = xmalloc_fgets(src_stream); | ||
29 | if (line == NULL) { | ||
30 | break; | ||
31 | } | ||
32 | if (fputs(line, dst_stream) == EOF) { | ||
33 | bb_perror_msg_and_die("error writing to new file"); | ||
34 | } | ||
35 | free(line); | ||
36 | lines_count--; | ||
37 | } | ||
38 | return lines_count; | ||
39 | } | ||
40 | |||
41 | /* If patch_level is -1 it will remove all directory names | ||
42 | * char *line must be greater than 4 chars | ||
43 | * returns NULL if the file doesnt exist or error | ||
44 | * returns malloc'ed filename | ||
45 | * NB: frees 1st argument! | ||
46 | */ | ||
47 | static char *extract_filename(char *line, int patch_level, const char *pat) | ||
48 | { | ||
49 | char *temp = NULL, *filename_start_ptr = line + 4; | ||
50 | |||
51 | if (strncmp(line, pat, 4) == 0) { | ||
52 | /* Terminate string at end of source filename */ | ||
53 | line[strcspn(line, "\t\n\r")] = '\0'; | ||
54 | |||
55 | /* Skip over (patch_level) number of leading directories */ | ||
56 | while (patch_level--) { | ||
57 | temp = strchr(filename_start_ptr, '/'); | ||
58 | if (!temp) | ||
59 | break; | ||
60 | filename_start_ptr = temp + 1; | ||
61 | } | ||
62 | temp = xstrdup(filename_start_ptr); | ||
63 | } | ||
64 | free(line); | ||
65 | return temp; | ||
66 | } | ||
67 | |||
68 | int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
69 | int patch_main(int argc UNUSED_PARAM, char **argv) | ||
70 | { | ||
71 | struct stat saved_stat; | ||
72 | char *patch_line; | ||
73 | FILE *patch_file; | ||
74 | int patch_level; | ||
75 | int ret = 0; | ||
76 | char plus = '+'; | ||
77 | unsigned opt; | ||
78 | enum { | ||
79 | OPT_R = (1 << 2), | ||
80 | OPT_N = (1 << 3), | ||
81 | /*OPT_f = (1 << 4), ignored */ | ||
82 | /*OPT_E = (1 << 5), ignored, this is the default */ | ||
83 | /*OPT_g = (1 << 6), ignored */ | ||
84 | OPT_dry_run = (1 << 7) * ENABLE_LONG_OPTS, | ||
85 | }; | ||
86 | |||
87 | xfunc_error_retval = 2; | ||
88 | { | ||
89 | const char *p = "-1"; | ||
90 | const char *i = "-"; /* compat */ | ||
91 | #if ENABLE_LONG_OPTS | ||
92 | static const char patch_longopts[] ALIGN1 = | ||
93 | "strip\0" Required_argument "p" | ||
94 | "input\0" Required_argument "i" | ||
95 | "reverse\0" No_argument "R" | ||
96 | "forward\0" No_argument "N" | ||
97 | /* "Assume user knows what [s]he is doing, do not ask any questions": */ | ||
98 | "force\0" No_argument "f" /*ignored*/ | ||
99 | # if ENABLE_DESKTOP | ||
100 | "remove-empty-files\0" No_argument "E" /*ignored*/ | ||
101 | /* "Controls actions when a file is under RCS or SCCS control, | ||
102 | * and does not exist or is read-only and matches the default version, | ||
103 | * or when a file is under ClearCase control and does not exist..." | ||
104 | * IOW: rather obscure option. | ||
105 | * But Gentoo's portage does use -g0 */ | ||
106 | "get\0" Required_argument "g" /*ignored*/ | ||
107 | # endif | ||
108 | "dry-run\0" No_argument "\xfd" | ||
109 | # if ENABLE_DESKTOP | ||
110 | "backup-if-mismatch\0" No_argument "\xfe" /*ignored*/ | ||
111 | "no-backup-if-mismatch\0" No_argument "\xff" /*ignored*/ | ||
112 | # endif | ||
113 | ; | ||
114 | applet_long_options = patch_longopts; | ||
115 | #endif | ||
116 | /* -f,-E,-g are ignored */ | ||
117 | opt = getopt32(argv, "p:i:RN""fEg:", &p, &i, NULL); | ||
118 | if (opt & OPT_R) | ||
119 | plus = '-'; | ||
120 | patch_level = xatoi(p); /* can be negative! */ | ||
121 | patch_file = xfopen_stdin(i); | ||
122 | } | ||
123 | |||
124 | patch_line = xmalloc_fgetline(patch_file); | ||
125 | while (patch_line) { | ||
126 | FILE *src_stream; | ||
127 | FILE *dst_stream; | ||
128 | //char *old_filename; | ||
129 | char *new_filename; | ||
130 | char *backup_filename = NULL; | ||
131 | unsigned src_cur_line = 1; | ||
132 | unsigned dst_cur_line = 0; | ||
133 | unsigned dst_beg_line; | ||
134 | unsigned bad_hunk_count = 0; | ||
135 | unsigned hunk_count = 0; | ||
136 | smallint copy_trailing_lines_flag = 0; | ||
137 | |||
138 | /* Skip everything upto the "---" marker | ||
139 | * No need to parse the lines "Only in <dir>", and "diff <args>" | ||
140 | */ | ||
141 | do { | ||
142 | /* Extract the filename used before the patch was generated */ | ||
143 | new_filename = extract_filename(patch_line, patch_level, "--- "); | ||
144 | // was old_filename above | ||
145 | patch_line = xmalloc_fgetline(patch_file); | ||
146 | if (!patch_line) goto quit; | ||
147 | } while (!new_filename); | ||
148 | free(new_filename); // "source" filename is irrelevant | ||
149 | |||
150 | new_filename = extract_filename(patch_line, patch_level, "+++ "); | ||
151 | if (!new_filename) { | ||
152 | bb_error_msg_and_die("invalid patch"); | ||
153 | } | ||
154 | |||
155 | /* Get access rights from the file to be patched */ | ||
156 | if (stat(new_filename, &saved_stat) != 0) { | ||
157 | char *slash = strrchr(new_filename, '/'); | ||
158 | if (slash) { | ||
159 | /* Create leading directories */ | ||
160 | *slash = '\0'; | ||
161 | bb_make_directory(new_filename, -1, FILEUTILS_RECUR); | ||
162 | *slash = '/'; | ||
163 | } | ||
164 | src_stream = NULL; | ||
165 | saved_stat.st_mode = 0644; | ||
166 | } else if (!(opt & OPT_dry_run)) { | ||
167 | backup_filename = xasprintf("%s.orig", new_filename); | ||
168 | xrename(new_filename, backup_filename); | ||
169 | src_stream = xfopen_for_read(backup_filename); | ||
170 | } else | ||
171 | src_stream = xfopen_for_read(new_filename); | ||
172 | |||
173 | if (opt & OPT_dry_run) { | ||
174 | dst_stream = xfopen_for_write("/dev/null"); | ||
175 | } else { | ||
176 | dst_stream = xfopen_for_write(new_filename); | ||
177 | fchmod(fileno(dst_stream), saved_stat.st_mode); | ||
178 | } | ||
179 | |||
180 | printf("patching file %s\n", new_filename); | ||
181 | |||
182 | /* Handle all hunks for this file */ | ||
183 | patch_line = xmalloc_fgets(patch_file); | ||
184 | while (patch_line) { | ||
185 | unsigned count; | ||
186 | unsigned src_beg_line; | ||
187 | unsigned hunk_offset_start; | ||
188 | unsigned src_last_line = 1; | ||
189 | unsigned dst_last_line = 1; | ||
190 | |||
191 | if ((sscanf(patch_line, "@@ -%d,%d +%d,%d", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3) | ||
192 | && (sscanf(patch_line, "@@ -%d +%d,%d", &src_beg_line, &dst_beg_line, &dst_last_line) < 2) | ||
193 | ) { | ||
194 | /* No more hunks for this file */ | ||
195 | break; | ||
196 | } | ||
197 | if (plus != '+') { | ||
198 | /* reverse patch */ | ||
199 | unsigned tmp = src_last_line; | ||
200 | src_last_line = dst_last_line; | ||
201 | dst_last_line = tmp; | ||
202 | tmp = src_beg_line; | ||
203 | src_beg_line = dst_beg_line; | ||
204 | dst_beg_line = tmp; | ||
205 | } | ||
206 | hunk_count++; | ||
207 | |||
208 | if (src_beg_line && dst_beg_line) { | ||
209 | /* Copy unmodified lines upto start of hunk */ | ||
210 | /* src_beg_line will be 0 if it's a new file */ | ||
211 | count = src_beg_line - src_cur_line; | ||
212 | if (copy_lines(src_stream, dst_stream, count)) { | ||
213 | bb_error_msg_and_die("bad src file"); | ||
214 | } | ||
215 | src_cur_line += count; | ||
216 | dst_cur_line += count; | ||
217 | copy_trailing_lines_flag = 1; | ||
218 | } | ||
219 | src_last_line += hunk_offset_start = src_cur_line; | ||
220 | dst_last_line += dst_cur_line; | ||
221 | |||
222 | while (1) { | ||
223 | free(patch_line); | ||
224 | patch_line = xmalloc_fgets(patch_file); | ||
225 | if (patch_line == NULL) | ||
226 | break; /* EOF */ | ||
227 | if (!*patch_line) { | ||
228 | /* whitespace-damaged patch with "" lines */ | ||
229 | free(patch_line); | ||
230 | patch_line = xstrdup(" "); | ||
231 | } | ||
232 | if ((*patch_line != '-') && (*patch_line != '+') | ||
233 | && (*patch_line != ' ') | ||
234 | ) { | ||
235 | break; /* End of hunk */ | ||
236 | } | ||
237 | if (*patch_line != plus) { /* '-' or ' ' */ | ||
238 | char *src_line = NULL; | ||
239 | if (src_cur_line == src_last_line) | ||
240 | break; | ||
241 | if (src_stream) { | ||
242 | src_line = xmalloc_fgets(src_stream); | ||
243 | if (src_line) { | ||
244 | int diff = strcmp(src_line, patch_line + 1); | ||
245 | src_cur_line++; | ||
246 | free(src_line); | ||
247 | if (diff) | ||
248 | src_line = NULL; | ||
249 | } | ||
250 | } | ||
251 | /* Do not patch an already patched hunk with -N */ | ||
252 | if (src_line == 0 && (opt & OPT_N)) { | ||
253 | continue; | ||
254 | } | ||
255 | if (!src_line) { | ||
256 | bb_error_msg("hunk #%u FAILED at %u", hunk_count, hunk_offset_start); | ||
257 | bad_hunk_count++; | ||
258 | break; | ||
259 | } | ||
260 | if (*patch_line != ' ') { /* '-' */ | ||
261 | continue; | ||
262 | } | ||
263 | } | ||
264 | if (dst_cur_line == dst_last_line) | ||
265 | break; | ||
266 | fputs(patch_line + 1, dst_stream); | ||
267 | dst_cur_line++; | ||
268 | } /* end of while loop handling one hunk */ | ||
269 | } /* end of while loop handling one file */ | ||
270 | |||
271 | /* Cleanup last patched file */ | ||
272 | if (copy_trailing_lines_flag) { | ||
273 | copy_lines(src_stream, dst_stream, (unsigned)(-1)); | ||
274 | } | ||
275 | if (src_stream) { | ||
276 | fclose(src_stream); | ||
277 | } | ||
278 | fclose(dst_stream); | ||
279 | if (bad_hunk_count) { | ||
280 | ret = 1; | ||
281 | bb_error_msg("%u out of %u hunk FAILED", bad_hunk_count, hunk_count); | ||
282 | } else { | ||
283 | /* It worked, we can remove the backup */ | ||
284 | if (backup_filename) { | ||
285 | unlink(backup_filename); | ||
286 | } | ||
287 | if (!(opt & OPT_dry_run) | ||
288 | && ((dst_cur_line == 0) || (dst_beg_line == 0)) | ||
289 | ) { | ||
290 | /* The new patched file is empty, remove it */ | ||
291 | xunlink(new_filename); | ||
292 | // /* old_filename and new_filename may be the same file */ | ||
293 | // unlink(old_filename); | ||
294 | } | ||
295 | } | ||
296 | free(backup_filename); | ||
297 | //free(old_filename); | ||
298 | free(new_filename); | ||
299 | } /* end of "while there are patch lines" */ | ||
300 | quit: | ||
301 | /* 0 = SUCCESS | ||
302 | * 1 = Some hunks failed | ||
303 | * 2 = More serious problems (exited earlier) | ||
304 | */ | ||
305 | return ret; | ||
306 | } | ||
diff --git a/editors/sed.c b/editors/sed.c index 7af8f867a..6cf54afe9 100644 --- a/editors/sed.c +++ b/editors/sed.c | |||
@@ -61,6 +61,10 @@ | |||
61 | #include "libbb.h" | 61 | #include "libbb.h" |
62 | #include "xregex.h" | 62 | #include "xregex.h" |
63 | 63 | ||
64 | enum { | ||
65 | OPT_in_place = 1 << 0, | ||
66 | }; | ||
67 | |||
64 | /* Each sed command turns into one of these structures. */ | 68 | /* Each sed command turns into one of these structures. */ |
65 | typedef struct sed_cmd_s { | 69 | typedef struct sed_cmd_s { |
66 | /* Ordered by alignment requirements: currently 36 bytes on x86 */ | 70 | /* Ordered by alignment requirements: currently 36 bytes on x86 */ |
@@ -938,8 +942,11 @@ static void process_files(void) | |||
938 | 942 | ||
939 | if (matched) { | 943 | if (matched) { |
940 | /* once matched, "n,xxx" range is dead, disabling it */ | 944 | /* once matched, "n,xxx" range is dead, disabling it */ |
941 | if (sed_cmd->beg_line > 0) | 945 | if (sed_cmd->beg_line > 0 |
946 | && !(option_mask32 & OPT_in_place) /* but not for -i */ | ||
947 | ) { | ||
942 | sed_cmd->beg_line = -2; | 948 | sed_cmd->beg_line = -2; |
949 | } | ||
943 | sed_cmd->in_match = !( | 950 | sed_cmd->in_match = !( |
944 | /* has the ending line come, or is this a single address command? */ | 951 | /* has the ending line come, or is this a single address command? */ |
945 | (sed_cmd->end_line ? | 952 | (sed_cmd->end_line ? |
@@ -985,6 +992,8 @@ static void process_files(void) | |||
985 | } | 992 | } |
986 | 993 | ||
987 | /* actual sedding */ | 994 | /* actual sedding */ |
995 | //bb_error_msg("pattern_space:'%s' next_line:'%s' cmd:%c", | ||
996 | //pattern_space, next_line, sed_cmd->cmd); | ||
988 | switch (sed_cmd->cmd) { | 997 | switch (sed_cmd->cmd) { |
989 | 998 | ||
990 | /* Print line number */ | 999 | /* Print line number */ |
@@ -1111,10 +1120,16 @@ static void process_files(void) | |||
1111 | { | 1120 | { |
1112 | int len; | 1121 | int len; |
1113 | /* If no next line, jump to end of script and exit. */ | 1122 | /* If no next line, jump to end of script and exit. */ |
1123 | /* http://www.gnu.org/software/sed/manual/sed.html: | ||
1124 | * "Most versions of sed exit without printing anything | ||
1125 | * when the N command is issued on the last line of | ||
1126 | * a file. GNU sed prints pattern space before exiting | ||
1127 | * unless of course the -n command switch has been | ||
1128 | * specified. This choice is by design." | ||
1129 | */ | ||
1114 | if (next_line == NULL) { | 1130 | if (next_line == NULL) { |
1115 | free(next_line); | 1131 | //goto discard_line; |
1116 | next_line = NULL; | 1132 | goto discard_commands; /* GNU behavior */ |
1117 | goto discard_line; | ||
1118 | } | 1133 | } |
1119 | /* Append next_line, read new next_line. */ | 1134 | /* Append next_line, read new next_line. */ |
1120 | len = strlen(pattern_space); | 1135 | len = strlen(pattern_space); |
@@ -1270,9 +1285,6 @@ static void add_cmd_block(char *cmdstr) | |||
1270 | int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1285 | int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1271 | int sed_main(int argc UNUSED_PARAM, char **argv) | 1286 | int sed_main(int argc UNUSED_PARAM, char **argv) |
1272 | { | 1287 | { |
1273 | enum { | ||
1274 | OPT_in_place = 1 << 0, | ||
1275 | }; | ||
1276 | unsigned opt; | 1288 | unsigned opt; |
1277 | llist_t *opt_e, *opt_f; | 1289 | llist_t *opt_e, *opt_f; |
1278 | int status = EXIT_SUCCESS; | 1290 | int status = EXIT_SUCCESS; |
@@ -1292,6 +1304,7 @@ int sed_main(int argc UNUSED_PARAM, char **argv) | |||
1292 | opt_e = opt_f = NULL; | 1304 | opt_e = opt_f = NULL; |
1293 | opt_complementary = "e::f::" /* can occur multiple times */ | 1305 | opt_complementary = "e::f::" /* can occur multiple times */ |
1294 | "nn"; /* count -n */ | 1306 | "nn"; /* count -n */ |
1307 | /* -i must be first, to match OPT_in_place definition */ | ||
1295 | opt = getopt32(argv, "irne:f:", &opt_e, &opt_f, | 1308 | opt = getopt32(argv, "irne:f:", &opt_e, &opt_f, |
1296 | &G.be_quiet); /* counter for -n */ | 1309 | &G.be_quiet); /* counter for -n */ |
1297 | //argc -= optind; | 1310 | //argc -= optind; |
diff --git a/findutils/grep.c b/findutils/grep.c index ac290a911..183d02606 100644 --- a/findutils/grep.c +++ b/findutils/grep.c | |||
@@ -461,15 +461,19 @@ static int grep_file(FILE *file) | |||
461 | if (found) | 461 | if (found) |
462 | print_line(gl->pattern, strlen(gl->pattern), linenum, ':'); | 462 | print_line(gl->pattern, strlen(gl->pattern), linenum, ':'); |
463 | } else while (1) { | 463 | } else while (1) { |
464 | unsigned start = gl->matched_range.rm_so; | ||
464 | unsigned end = gl->matched_range.rm_eo; | 465 | unsigned end = gl->matched_range.rm_eo; |
466 | unsigned len = end - start; | ||
465 | char old = line[end]; | 467 | char old = line[end]; |
466 | line[end] = '\0'; | 468 | line[end] = '\0'; |
467 | print_line(line + gl->matched_range.rm_so, | 469 | /* Empty match is not printed: try "echo test | grep -o ''" */ |
468 | end - gl->matched_range.rm_so, | 470 | if (len != 0) |
469 | linenum, ':'); | 471 | print_line(line + start, len, linenum, ':'); |
470 | if (old == '\0') | 472 | if (old == '\0') |
471 | break; | 473 | break; |
472 | line[end] = old; | 474 | line[end] = old; |
475 | if (len == 0) | ||
476 | end++; | ||
473 | #if !ENABLE_EXTRA_COMPAT | 477 | #if !ENABLE_EXTRA_COMPAT |
474 | if (regexec(&gl->compiled_regex, line + end, | 478 | if (regexec(&gl->compiled_regex, line + end, |
475 | 1, &gl->matched_range, REG_NOTBOL) != 0) | 479 | 1, &gl->matched_range, REG_NOTBOL) != 0) |
diff --git a/include/libbb.h b/include/libbb.h index e2a8322b8..9e9d556f1 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -417,6 +417,7 @@ int xopen3(const char *pathname, int flags, int mode) FAST_FUNC; | |||
417 | int open_or_warn(const char *pathname, int flags) FAST_FUNC; | 417 | int open_or_warn(const char *pathname, int flags) FAST_FUNC; |
418 | int open3_or_warn(const char *pathname, int flags, int mode) FAST_FUNC; | 418 | int open3_or_warn(const char *pathname, int flags, int mode) FAST_FUNC; |
419 | int open_or_warn_stdin(const char *pathname) FAST_FUNC; | 419 | int open_or_warn_stdin(const char *pathname) FAST_FUNC; |
420 | int xopen_stdin(const char *pathname) FAST_FUNC; | ||
420 | void xrename(const char *oldpath, const char *newpath) FAST_FUNC; | 421 | void xrename(const char *oldpath, const char *newpath) FAST_FUNC; |
421 | int rename_or_warn(const char *oldpath, const char *newpath) FAST_FUNC; | 422 | int rename_or_warn(const char *oldpath, const char *newpath) FAST_FUNC; |
422 | off_t xlseek(int fd, off_t offset, int whence) FAST_FUNC; | 423 | off_t xlseek(int fd, off_t offset, int whence) FAST_FUNC; |
diff --git a/libbb/wfopen_input.c b/libbb/wfopen_input.c index 46ff7a6de..422a58ecf 100644 --- a/libbb/wfopen_input.c +++ b/libbb/wfopen_input.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * | 4 | * |
5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> | 5 | * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> |
6 | * | 6 | * |
7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | 9 | ||
10 | /* A number of applets need to open a file for reading, where the filename | 10 | /* A number of applets need to open a file for reading, where the filename |
@@ -46,3 +46,11 @@ int FAST_FUNC open_or_warn_stdin(const char *filename) | |||
46 | 46 | ||
47 | return fd; | 47 | return fd; |
48 | } | 48 | } |
49 | |||
50 | int FAST_FUNC xopen_stdin(const char *filename) | ||
51 | { | ||
52 | int fd = open_or_warn_stdin(filename); | ||
53 | if (fd >= 0) | ||
54 | return fd; | ||
55 | xfunc_die(); /* We already output an error message. */ | ||
56 | } | ||
diff --git a/shell/ash.c b/shell/ash.c index 0337a5535..7cc5a913a 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -4515,6 +4515,7 @@ clear_traps(void) | |||
4515 | INT_ON; | 4515 | INT_ON; |
4516 | } | 4516 | } |
4517 | } | 4517 | } |
4518 | may_have_traps = 0; | ||
4518 | } | 4519 | } |
4519 | 4520 | ||
4520 | /* Lives far away from here, needed for forkchild */ | 4521 | /* Lives far away from here, needed for forkchild */ |
diff --git a/shell/ash_test/ash-redir/redir9.tests b/shell/ash_test/ash-redir/redir9.tests index 8befa611c..8befa611c 100644..100755 --- a/shell/ash_test/ash-redir/redir9.tests +++ b/shell/ash_test/ash-redir/redir9.tests | |||
diff --git a/shell/ash_test/ash-signals/signal7.right b/shell/ash_test/ash-signals/signal7.right new file mode 100644 index 000000000..ba7453e42 --- /dev/null +++ b/shell/ash_test/ash-signals/signal7.right | |||
@@ -0,0 +1 @@ | |||
Bug detected: 0 | |||
diff --git a/shell/ash_test/ash-signals/signal7.tests b/shell/ash_test/ash-signals/signal7.tests new file mode 100755 index 000000000..c2b1381f9 --- /dev/null +++ b/shell/ash_test/ash-signals/signal7.tests | |||
@@ -0,0 +1,18 @@ | |||
1 | bug() { | ||
2 | trap : exit | ||
3 | # Bug was causing sh to be run in subshell, | ||
4 | # as if this line is replaced with (sh -c ...; exit $?) & | ||
5 | # here: | ||
6 | sh -c 'echo REAL_CHILD=$$' & | ||
7 | echo PARENTS_IDEA_OF_CHILD=$! | ||
8 | wait # make sure bkgd shell completes | ||
9 | } | ||
10 | |||
11 | bug | { | ||
12 | while read varval; do | ||
13 | eval $varval | ||
14 | done | ||
15 | test x"$REAL_CHILD" != x"" \ | ||
16 | && test x"$REAL_CHILD" = x"$PARENTS_IDEA_OF_CHILD" | ||
17 | echo "Bug detected: $?" | ||
18 | } | ||
diff --git a/shell/hush.c b/shell/hush.c index 31ca22a2e..01bc96ce6 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -3901,8 +3901,6 @@ static void insert_bg_job(struct pipe *pi) | |||
3901 | 3901 | ||
3902 | if (G_interactive_fd) | 3902 | if (G_interactive_fd) |
3903 | printf("[%d] %d %s\n", job->jobid, job->cmds[0].pid, job->cmdtext); | 3903 | printf("[%d] %d %s\n", job->jobid, job->cmds[0].pid, job->cmdtext); |
3904 | /* Last command's pid goes to $! */ | ||
3905 | G.last_bg_pid = job->cmds[job->num_cmds - 1].pid; | ||
3906 | G.last_jobid = job->jobid; | 3904 | G.last_jobid = job->jobid; |
3907 | } | 3905 | } |
3908 | 3906 | ||
@@ -4825,6 +4823,8 @@ static int run_list(struct pipe *pi) | |||
4825 | if (G.run_list_level == 1) | 4823 | if (G.run_list_level == 1) |
4826 | insert_bg_job(pi); | 4824 | insert_bg_job(pi); |
4827 | #endif | 4825 | #endif |
4826 | /* Last command's pid goes to $! */ | ||
4827 | G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; | ||
4828 | G.last_exitcode = rcode = EXIT_SUCCESS; | 4828 | G.last_exitcode = rcode = EXIT_SUCCESS; |
4829 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); | 4829 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); |
4830 | } else { | 4830 | } else { |
diff --git a/shell/hush_test/hush-trap/signal7.right b/shell/hush_test/hush-trap/signal7.right new file mode 100644 index 000000000..ba7453e42 --- /dev/null +++ b/shell/hush_test/hush-trap/signal7.right | |||
@@ -0,0 +1 @@ | |||
Bug detected: 0 | |||
diff --git a/shell/hush_test/hush-trap/signal7.tests b/shell/hush_test/hush-trap/signal7.tests new file mode 100755 index 000000000..c2b1381f9 --- /dev/null +++ b/shell/hush_test/hush-trap/signal7.tests | |||
@@ -0,0 +1,18 @@ | |||
1 | bug() { | ||
2 | trap : exit | ||
3 | # Bug was causing sh to be run in subshell, | ||
4 | # as if this line is replaced with (sh -c ...; exit $?) & | ||
5 | # here: | ||
6 | sh -c 'echo REAL_CHILD=$$' & | ||
7 | echo PARENTS_IDEA_OF_CHILD=$! | ||
8 | wait # make sure bkgd shell completes | ||
9 | } | ||
10 | |||
11 | bug | { | ||
12 | while read varval; do | ||
13 | eval $varval | ||
14 | done | ||
15 | test x"$REAL_CHILD" != x"" \ | ||
16 | && test x"$REAL_CHILD" = x"$PARENTS_IDEA_OF_CHILD" | ||
17 | echo "Bug detected: $?" | ||
18 | } | ||
diff --git a/shell/shell_common.c b/shell/shell_common.c index 3114ff3f7..dc363e298 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -428,9 +428,14 @@ shell_builtin_ulimit(char **argv) | |||
428 | val <<= l->factor_shift; | 428 | val <<= l->factor_shift; |
429 | } | 429 | } |
430 | //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); | 430 | //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); |
431 | /* from man bash: "If neither -H nor -S | ||
432 | * is specified, both the soft and hard | ||
433 | * limits are set. */ | ||
434 | if (!opts) | ||
435 | opts = OPT_hard + OPT_soft; | ||
431 | if (opts & OPT_hard) | 436 | if (opts & OPT_hard) |
432 | limit.rlim_max = val; | 437 | limit.rlim_max = val; |
433 | if ((opts & OPT_soft) || opts == 0) | 438 | if (opts & OPT_soft) |
434 | limit.rlim_cur = val; | 439 | limit.rlim_cur = val; |
435 | //bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); | 440 | //bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); |
436 | if (setrlimit(l->cmd, &limit) < 0) { | 441 | if (setrlimit(l->cmd, &limit) < 0) { |
diff --git a/testsuite/grep.tests b/testsuite/grep.tests index d4bf80d83..e0135dcbb 100755 --- a/testsuite/grep.tests +++ b/testsuite/grep.tests | |||
@@ -98,5 +98,9 @@ testing "grep -o does not loop forever" \ | |||
98 | 'grep -o "[^/]*$"' \ | 98 | 'grep -o "[^/]*$"' \ |
99 | "test\n" \ | 99 | "test\n" \ |
100 | "" "/var/test\n" | 100 | "" "/var/test\n" |
101 | testing "grep -o does not loop forever on zero-length match" \ | ||
102 | 'grep -o "" | head -n1' \ | ||
103 | "" \ | ||
104 | "" "test\n" | ||
101 | 105 | ||
102 | exit $FAILCOUNT | 106 | exit $FAILCOUNT |
diff --git a/testsuite/mdev.tests b/testsuite/mdev.tests index c375fc774..a46929b3a 100755 --- a/testsuite/mdev.tests +++ b/testsuite/mdev.tests | |||
@@ -38,6 +38,16 @@ brw-rw---- 1 0 0 8,0 sda | |||
38 | SKIP= | 38 | SKIP= |
39 | 39 | ||
40 | # continuing to use directory structure from prev test | 40 | # continuing to use directory structure from prev test |
41 | optional STATIC FEATURE_MDEV_CONF FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME | ||
42 | testing "mdev deletes /block/sda" \ | ||
43 | "env - PATH=$PATH ACTION=remove DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1; | ||
44 | ls -ln mdev.testdir/dev | $FILTER_LS" \ | ||
45 | "\ | ||
46 | " \ | ||
47 | "" "" | ||
48 | SKIP= | ||
49 | |||
50 | # continuing to use directory structure from prev test | ||
41 | rm -rf mdev.testdir/dev/* | 51 | rm -rf mdev.testdir/dev/* |
42 | echo ".* 1:1 666" >mdev.testdir/etc/mdev.conf | 52 | echo ".* 1:1 666" >mdev.testdir/etc/mdev.conf |
43 | echo "sda 2:2 444" >>mdev.testdir/etc/mdev.conf | 53 | echo "sda 2:2 444" >>mdev.testdir/etc/mdev.conf |
diff --git a/testsuite/patch.tests b/testsuite/patch.tests index 749d936ea..e482304f6 100755 --- a/testsuite/patch.tests +++ b/testsuite/patch.tests | |||
@@ -1,13 +1,13 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # Copyright 2008 by Denys Vlasenko | 2 | # Copyright 2008 by Denys Vlasenko |
3 | # Licensed under GPL v2, see file LICENSE for details. | 3 | # Licensed under GPLv2, see file LICENSE in this source tree. |
4 | 4 | ||
5 | . ./testing.sh | 5 | . ./testing.sh |
6 | 6 | ||
7 | # testing "test name" "options" "expected result" "file input" "stdin" | 7 | # testing "test name" "command(s)" "expected result" "file input" "stdin" |
8 | 8 | ||
9 | testing "patch with old_file == new_file" \ | 9 | testing "patch with old_file == new_file" \ |
10 | 'patch; echo $?; cat input' \ | 10 | 'patch 2>&1; echo $?; cat input' \ |
11 | "\ | 11 | "\ |
12 | patching file input | 12 | patching file input |
13 | 0 | 13 | 0 |
@@ -29,7 +29,7 @@ zxc | |||
29 | " \ | 29 | " \ |
30 | 30 | ||
31 | testing "patch with nonexistent old_file" \ | 31 | testing "patch with nonexistent old_file" \ |
32 | 'patch; echo $?; cat input' \ | 32 | 'patch 2>&1; echo $?; cat input' \ |
33 | "\ | 33 | "\ |
34 | patching file input | 34 | patching file input |
35 | 0 | 35 | 0 |
@@ -51,7 +51,7 @@ zxc | |||
51 | " \ | 51 | " \ |
52 | 52 | ||
53 | testing "patch -R with nonexistent old_file" \ | 53 | testing "patch -R with nonexistent old_file" \ |
54 | 'patch -R; echo $?; cat input' \ | 54 | 'patch -R 2>&1; echo $?; cat input' \ |
55 | "\ | 55 | "\ |
56 | patching file input | 56 | patching file input |
57 | 0 | 57 | 0 |
@@ -75,9 +75,12 @@ zxc | |||
75 | testing "patch detects already applied hunk" \ | 75 | testing "patch detects already applied hunk" \ |
76 | 'patch 2>&1; echo $?; cat input' \ | 76 | 'patch 2>&1; echo $?; cat input' \ |
77 | "\ | 77 | "\ |
78 | Possibly reversed hunk 1 at 2 | ||
79 | Hunk 1 FAILED 1/1. | ||
80 | abc | ||
81 | +def | ||
82 | 123 | ||
78 | patching file input | 83 | patching file input |
79 | patch: hunk #1 FAILED at 1 | ||
80 | patch: 1 out of 1 hunk FAILED | ||
81 | 1 | 84 | 1 |
82 | abc | 85 | abc |
83 | def | 86 | def |
@@ -97,13 +100,15 @@ def | |||
97 | 123 | 100 | 123 |
98 | " \ | 101 | " \ |
99 | 102 | ||
100 | # Currently fails (erroneously appends second "456" line): | 103 | testing "patch detects already applied hunk at the EOF" \ |
101 | false && testing "patch detects already applied hunk" \ | ||
102 | 'patch 2>&1; echo $?; cat input' \ | 104 | 'patch 2>&1; echo $?; cat input' \ |
103 | "\ | 105 | "\ |
106 | Possibly reversed hunk 1 at 3 | ||
107 | Hunk 1 FAILED 1/1. | ||
108 | abc | ||
109 | 123 | ||
110 | +456 | ||
104 | patching file input | 111 | patching file input |
105 | patch: hunk #1 FAILED at 2 | ||
106 | patch: 1 out of 1 hunk FAILED | ||
107 | 1 | 112 | 1 |
108 | abc | 113 | abc |
109 | 123 | 114 | 123 |
@@ -123,6 +128,53 @@ abc | |||
123 | +456 | 128 | +456 |
124 | " \ | 129 | " \ |
125 | 130 | ||
131 | # testing "test name" "command(s)" "expected result" "file input" "stdin" | ||
132 | testing "patch -N ignores already applied hunk" \ | ||
133 | 'patch -N 2>&1; echo $?; cat input' \ | ||
134 | "\ | ||
135 | patching file input | ||
136 | 0 | ||
137 | abc | ||
138 | def | ||
139 | 123 | ||
140 | " \ | ||
141 | "\ | ||
142 | abc | ||
143 | def | ||
144 | 123 | ||
145 | " \ | ||
146 | "\ | ||
147 | --- input | ||
148 | +++ input | ||
149 | @@ -1,2 +1,3 @@ | ||
150 | abc | ||
151 | +def | ||
152 | 123 | ||
153 | " \ | ||
154 | |||
155 | # testing "test name" "command(s)" "expected result" "file input" "stdin" | ||
156 | testing "patch FILE PATCH" \ | ||
157 | 'cat >a.patch; patch input a.patch 2>&1; echo $?; cat input; rm a.patch' \ | ||
158 | "\ | ||
159 | patching file input | ||
160 | 0 | ||
161 | abc | ||
162 | def | ||
163 | 123 | ||
164 | " \ | ||
165 | "\ | ||
166 | abc | ||
167 | 123 | ||
168 | " \ | ||
169 | "\ | ||
170 | --- foo.old | ||
171 | +++ foo | ||
172 | @@ -1,2 +1,3 @@ | ||
173 | abc | ||
174 | +def | ||
175 | 123 | ||
176 | " \ | ||
177 | |||
126 | rm input.orig 2>/dev/null | 178 | rm input.orig 2>/dev/null |
127 | 179 | ||
128 | exit $FAILCOUNT | 180 | exit $FAILCOUNT |
diff --git a/testsuite/sed.tests b/testsuite/sed.tests index 3301a25f8..61551e31c 100755 --- a/testsuite/sed.tests +++ b/testsuite/sed.tests | |||
@@ -80,10 +80,18 @@ test x"$SKIP_KNOWN_BUGS" = x"" && { | |||
80 | # Query: how does this interact with no newline at EOF? | 80 | # Query: how does this interact with no newline at EOF? |
81 | testing "sed n (flushes pattern space, terminates early)" "sed -e 'n;p'" \ | 81 | testing "sed n (flushes pattern space, terminates early)" "sed -e 'n;p'" \ |
82 | "a\nb\nb\nc\n" "" "a\nb\nc\n" | 82 | "a\nb\nb\nc\n" "" "a\nb\nc\n" |
83 | # N does _not_ flush pattern space, therefore c is still in there @ script end. | ||
84 | testing "sed N (doesn't flush pattern space when terminating)" "sed -e 'N;p'" \ | ||
85 | "a\nb\na\nb\nc\n" "" "a\nb\nc\n" | ||
86 | } | 83 | } |
84 | # non-GNU sed: N does _not_ flush pattern space, therefore c is eaten @ script end | ||
85 | # GNU sed: N flushes pattern space, therefore c is printed too @ script end | ||
86 | testing "sed N (flushes pattern space (GNU behavior))" "sed -e 'N;p'" \ | ||
87 | "a\nb\na\nb\nc\n" "" "a\nb\nc\n" | ||
88 | |||
89 | testing "sed N test2" "sed ':a;N;s/\n/ /;ta'" \ | ||
90 | "a b c\n" "" "a\nb\nc\n" | ||
91 | |||
92 | testing "sed N test3" "sed 'N;s/\n/ /'" \ | ||
93 | "a b\nc\n" "" "a\nb\nc\n" | ||
94 | |||
87 | testing "sed address match newline" 'sed "/b/N;/b\\nc/i woo"' \ | 95 | testing "sed address match newline" 'sed "/b/N;/b\\nc/i woo"' \ |
88 | "a\nwoo\nb\nc\nd\n" "" "a\nb\nc\nd\n" | 96 | "a\nwoo\nb\nc\nd\n" "" "a\nb\nc\nd\n" |
89 | 97 | ||
@@ -270,11 +278,16 @@ testing "sed a cmd ended by double backslash" \ | |||
270 | | two \\ | 278 | | two \\ |
271 | ' | 279 | ' |
272 | 280 | ||
273 | # fisrt three lines are deleted; 4th line is matched and printed by "2,3" and by "4" ranges | 281 | # first three lines are deleted; 4th line is matched and printed by "2,3" and by "4" ranges |
274 | testing "sed with N skipping lines past ranges on next cmds" \ | 282 | testing "sed with N skipping lines past ranges on next cmds" \ |
275 | "sed -n '1{N;N;d};1p;2,3p;3p;4p'" \ | 283 | "sed -n '1{N;N;d};1p;2,3p;3p;4p'" \ |
276 | "4\n4\n" "" "1\n2\n3\n4\n" | 284 | "4\n4\n" "" "1\n2\n3\n4\n" |
277 | 285 | ||
286 | testing "sed -i with address modifies all files, not only first" \ | ||
287 | "cp input input2; sed -i -e '1s/foo/bar/' input input2 && cat input input2; rm input2" \ | ||
288 | "bar\nbar\n" "foo\n" "" | ||
289 | |||
290 | |||
278 | # testing "description" "arguments" "result" "infile" "stdin" | 291 | # testing "description" "arguments" "result" "infile" "stdin" |
279 | 292 | ||
280 | exit $FAILCOUNT | 293 | exit $FAILCOUNT |
diff --git a/util-linux/mdev.c b/util-linux/mdev.c index b4042c07e..cd6c1a89d 100644 --- a/util-linux/mdev.c +++ b/util-linux/mdev.c | |||
@@ -132,6 +132,7 @@ static void make_device(char *path, int delete) | |||
132 | major = -1; | 132 | major = -1; |
133 | } | 133 | } |
134 | } | 134 | } |
135 | /* else: for delete, -1 still deletes the node, but < -1 suppresses that */ | ||
135 | 136 | ||
136 | /* Determine device name, type, major and minor */ | 137 | /* Determine device name, type, major and minor */ |
137 | device_name = (char*) bb_basename(path); | 138 | device_name = (char*) bb_basename(path); |
@@ -279,7 +280,7 @@ static void make_device(char *path, int delete) | |||
279 | if (aliaslink == '!' && s == a+1) { | 280 | if (aliaslink == '!' && s == a+1) { |
280 | val = st; | 281 | val = st; |
281 | /* "!": suppress node creation/deletion */ | 282 | /* "!": suppress node creation/deletion */ |
282 | major = -1; | 283 | major = -2; |
283 | } | 284 | } |
284 | else if (aliaslink == '>' || aliaslink == '=') { | 285 | else if (aliaslink == '>' || aliaslink == '=') { |
285 | val = st; | 286 | val = st; |
@@ -379,7 +380,7 @@ static void make_device(char *path, int delete) | |||
379 | free(command); | 380 | free(command); |
380 | } | 381 | } |
381 | 382 | ||
382 | if (delete && major >= 0) { | 383 | if (delete && major >= -1) { |
383 | if (ENABLE_FEATURE_MDEV_RENAME && alias) { | 384 | if (ENABLE_FEATURE_MDEV_RENAME && alias) { |
384 | if (aliaslink == '>') | 385 | if (aliaslink == '>') |
385 | unlink(device_name); | 386 | unlink(device_name); |