diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-11-21 05:53:34 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-11-21 05:53:34 +0100 |
commit | b82ae98ea4d5216d20a5deedab2aa6153562337b (patch) | |
tree | 20e89268f222d4e463f65706d2f9a2bce61b9d4c | |
parent | f718e3a0dbe47ed7ed0398fe4461d33c8f69c669 (diff) | |
download | busybox-w32-b82ae98ea4d5216d20a5deedab2aa6153562337b.tar.gz busybox-w32-b82ae98ea4d5216d20a5deedab2aa6153562337b.tar.bz2 busybox-w32-b82ae98ea4d5216d20a5deedab2aa6153562337b.zip |
patch: busyboxify by migrating from toybox to busybox helpers
function old new delta
get_line 90 128 +38
bbconfig_config_bz2 4959 4965 +6
makedevs_main 1038 1035 -3
fail_hunk 133 130 -3
finish_oldfile 174 124 -50
patch_main 2066 1987 -79
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/4 up/down: 44/-135) Total: -91 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | editors/patch.c | 245 | ||||
-rwxr-xr-x | testsuite/patch.tests | 4 |
2 files changed, 87 insertions, 162 deletions
diff --git a/editors/patch.c b/editors/patch.c index 33ff8b569..4fadbb8fe 100644 --- a/editors/patch.c +++ b/editors/patch.c | |||
@@ -1,8 +1,7 @@ | |||
1 | /* Adapted from toybox's patch. */ | ||
2 | |||
3 | /* vi: set sw=4 ts=4: | 1 | /* vi: set sw=4 ts=4: |
4 | * | 2 | * |
5 | * patch.c - Apply a "universal" diff. | 3 | * Apply a "universal" diff. |
4 | * Adapted from toybox's patch implementation. | ||
6 | * | 5 | * |
7 | * Copyright 2007 Rob Landley <rob@landley.net> | 6 | * Copyright 2007 Rob Landley <rob@landley.net> |
8 | * | 7 | * |
@@ -20,29 +19,41 @@ | |||
20 | * -f force (no questions asked) | 19 | * -f force (no questions asked) |
21 | * -F fuzz (number, default 2) | 20 | * -F fuzz (number, default 2) |
22 | * [file] which file to patch | 21 | * [file] which file to patch |
22 | */ | ||
23 | |||
24 | //applet:IF_PATCH(APPLET(patch, _BB_DIR_USR_BIN, _BB_SUID_DROP)) | ||
25 | |||
26 | //kbuild:lib-$(CONFIG_PATCH) += patch.o | ||
27 | |||
28 | //config:config PATCH | ||
29 | //config: bool "patch" | ||
30 | //config: default y | ||
31 | //config: help | ||
32 | //config: Apply a unified diff formatted patch. | ||
33 | |||
34 | //usage:#define patch_trivial_usage | ||
35 | //usage: "[OPTIONS] [ORIGFILE [PATCHFILE]]" | ||
36 | //usage:#define patch_full_usage "\n\n" | ||
37 | //usage: IF_LONG_OPTS( | ||
38 | //usage: " -p,--strip N Strip N leading components from file names" | ||
39 | //usage: "\n -i,--input DIFF Read DIFF instead of stdin" | ||
40 | //usage: "\n -R,--reverse Reverse patch" | ||
41 | //usage: "\n -N,--forward Ignore already applied patches" | ||
42 | //usage: "\n --dry-run Don't actually change files" | ||
43 | //usage: "\n -E,--remove-empty-files Remove output files if they become empty" | ||
44 | //usage: ) | ||
45 | //usage: IF_NOT_LONG_OPTS( | ||
46 | //usage: " -p N Strip N leading components from file names" | ||
47 | //usage: "\n -i DIFF Read DIFF instead of stdin" | ||
48 | //usage: "\n -R Reverse patch" | ||
49 | //usage: "\n -N Ignore already applied patches" | ||
50 | //usage: "\n -E Remove output files if they become empty" | ||
51 | //usage: ) | ||
52 | //usage: | ||
53 | //usage:#define patch_example_usage | ||
54 | //usage: "$ patch -p1 < example.diff\n" | ||
55 | //usage: "$ patch -p0 -i example.diff" | ||
23 | 56 | ||
24 | USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"up#i:R", TOYFLAG_USR|TOYFLAG_BIN)) | ||
25 | |||
26 | config PATCH | ||
27 | bool "patch" | ||
28 | default y | ||
29 | help | ||
30 | usage: patch [-i file] [-p depth] [-Ru] | ||
31 | |||
32 | Apply a unified diff to one or more files. | ||
33 | |||
34 | -i Input file (defaults=stdin) | ||
35 | -p number of '/' to strip from start of file paths (default=all) | ||
36 | -R Reverse patch. | ||
37 | -u Ignored (only handles "unified" diffs) | ||
38 | |||
39 | This version of patch only handles unified diffs, and only modifies | ||
40 | a file when all all hunks to that file apply. Patch prints failed | ||
41 | hunks to stderr, and exits with nonzero status if any hunks fail. | ||
42 | |||
43 | A file compared against /dev/null (or with a date <= the epoch) is | ||
44 | created or deleted if -E or --remove-empty-files set. | ||
45 | */ | ||
46 | #include "libbb.h" | 57 | #include "libbb.h" |
47 | 58 | ||
48 | struct double_list { | 59 | struct double_list { |
@@ -101,125 +112,6 @@ struct double_list *dlist_add(struct double_list **list, char *data) | |||
101 | return line; | 112 | return line; |
102 | } | 113 | } |
103 | 114 | ||
104 | // Ensure entire path exists. | ||
105 | // If mode != -1 set permissions on newly created dirs. | ||
106 | // Requires that path string be writable (for temporary null terminators). | ||
107 | static | ||
108 | void xmkpath(char *path, int mode) | ||
109 | { | ||
110 | char *p, old; | ||
111 | mode_t mask; | ||
112 | int rc; | ||
113 | struct stat st; | ||
114 | |||
115 | for (p = path; ; p++) { | ||
116 | if (!*p || *p == '/') { | ||
117 | old = *p; | ||
118 | *p = rc = 0; | ||
119 | if (stat(path, &st) || !S_ISDIR(st.st_mode)) { | ||
120 | if (mode != -1) { | ||
121 | mask = umask(0); | ||
122 | rc = mkdir(path, mode); | ||
123 | umask(mask); | ||
124 | } else rc = mkdir(path, 0777); | ||
125 | } | ||
126 | *p = old; | ||
127 | if(rc) bb_perror_msg_and_die("mkpath '%s'", path); | ||
128 | } | ||
129 | if (!*p) break; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | // Slow, but small. | ||
134 | static | ||
135 | char *get_rawline(int fd, long *plen, char end) | ||
136 | { | ||
137 | char c, *buf = NULL; | ||
138 | long len = 0; | ||
139 | |||
140 | for (;;) { | ||
141 | if (1>read(fd, &c, 1)) break; | ||
142 | if (!(len & 63)) buf=xrealloc(buf, len+65); | ||
143 | if ((buf[len++]=c) == end) break; | ||
144 | } | ||
145 | if (buf) buf[len]=0; | ||
146 | if (plen) *plen = len; | ||
147 | |||
148 | return buf; | ||
149 | } | ||
150 | |||
151 | static | ||
152 | char *get_line(int fd) | ||
153 | { | ||
154 | long len; | ||
155 | char *buf = get_rawline(fd, &len, '\n'); | ||
156 | |||
157 | if (buf && buf[--len]=='\n') buf[len]=0; | ||
158 | |||
159 | return buf; | ||
160 | } | ||
161 | |||
162 | // Copy the rest of in to out and close both files. | ||
163 | static | ||
164 | void xsendfile(int in, int out) | ||
165 | { | ||
166 | long len; | ||
167 | char buf[4096]; | ||
168 | |||
169 | if (in<0) return; | ||
170 | for (;;) { | ||
171 | len = safe_read(in, buf, 4096); | ||
172 | if (len<1) break; | ||
173 | xwrite(out, buf, len); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | // Copy the rest of the data and replace the original with the copy. | ||
178 | static | ||
179 | void replace_tempfile(int fdin, int fdout, char **tempname) | ||
180 | { | ||
181 | char *temp = xstrdup(*tempname); | ||
182 | |||
183 | temp[strlen(temp)-6]=0; | ||
184 | if (fdin != -1) { | ||
185 | xsendfile(fdin, fdout); | ||
186 | xclose(fdin); | ||
187 | } | ||
188 | xclose(fdout); | ||
189 | rename(*tempname, temp); | ||
190 | free(*tempname); | ||
191 | free(temp); | ||
192 | *tempname = NULL; | ||
193 | } | ||
194 | |||
195 | // Open a temporary file to copy an existing file into. | ||
196 | static | ||
197 | int copy_tempfile(int fdin, char *name, char **tempname) | ||
198 | { | ||
199 | struct stat statbuf; | ||
200 | int fd; | ||
201 | |||
202 | *tempname = xasprintf("%sXXXXXX", name); | ||
203 | fd = xmkstemp(*tempname); | ||
204 | |||
205 | // Set permissions of output file | ||
206 | fstat(fdin, &statbuf); | ||
207 | fchmod(fd, statbuf.st_mode); | ||
208 | |||
209 | return fd; | ||
210 | } | ||
211 | |||
212 | // Abort the copy and delete the temporary file. | ||
213 | static | ||
214 | void delete_tempfile(int fdin, int fdout, char **tempname) | ||
215 | { | ||
216 | close(fdin); | ||
217 | close(fdout); | ||
218 | unlink(*tempname); | ||
219 | free(*tempname); | ||
220 | *tempname = NULL; | ||
221 | } | ||
222 | |||
223 | 115 | ||
224 | 116 | ||
225 | struct globals { | 117 | struct globals { |
@@ -229,7 +121,7 @@ struct globals { | |||
229 | struct double_list *current_hunk; | 121 | struct double_list *current_hunk; |
230 | long oldline, oldlen, newline, newlen; | 122 | long oldline, oldlen, newline, newlen; |
231 | long linenum; | 123 | long linenum; |
232 | int context, state, filein, fileout, filepatch, hunknum; | 124 | int context, state, filein, fileout, hunknum; |
233 | char *tempname; | 125 | char *tempname; |
234 | 126 | ||
235 | // was toys.foo: | 127 | // was toys.foo: |
@@ -263,7 +155,7 @@ struct globals { | |||
263 | 155 | ||
264 | static void do_line(void *data) | 156 | static void do_line(void *data) |
265 | { | 157 | { |
266 | struct double_list *dlist = (struct double_list *)data; | 158 | struct double_list *dlist = data; |
267 | 159 | ||
268 | if (TT.state>1 && *dlist->data != TT.state) | 160 | if (TT.state>1 && *dlist->data != TT.state) |
269 | fdprintf(TT.state == 2 ? 2 : TT.fileout, | 161 | fdprintf(TT.state == 2 ? 2 : TT.fileout, |
@@ -272,19 +164,36 @@ static void do_line(void *data) | |||
272 | if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data); | 164 | if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data); |
273 | 165 | ||
274 | free(dlist->data); | 166 | free(dlist->data); |
275 | free(data); | 167 | free(dlist); |
276 | } | 168 | } |
277 | 169 | ||
278 | static void finish_oldfile(void) | 170 | static void finish_oldfile(void) |
279 | { | 171 | { |
280 | if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname); | 172 | if (TT.tempname) { |
173 | // Copy the rest of the data and replace the original with the copy. | ||
174 | char *temp; | ||
175 | |||
176 | if (TT.filein != -1) { | ||
177 | bb_copyfd_eof(TT.filein, TT.fileout); | ||
178 | xclose(TT.filein); | ||
179 | } | ||
180 | xclose(TT.fileout); | ||
181 | |||
182 | temp = xstrdup(TT.tempname); | ||
183 | temp[strlen(temp) - 6] = '\0'; | ||
184 | rename(TT.tempname, temp); | ||
185 | free(temp); | ||
186 | |||
187 | free(TT.tempname); | ||
188 | TT.tempname = NULL; | ||
189 | } | ||
281 | TT.fileout = TT.filein = -1; | 190 | TT.fileout = TT.filein = -1; |
282 | } | 191 | } |
283 | 192 | ||
284 | static void fail_hunk(void) | 193 | static void fail_hunk(void) |
285 | { | 194 | { |
286 | if (!TT.current_hunk) return; | 195 | if (!TT.current_hunk) return; |
287 | TT.current_hunk->prev->next = 0; | 196 | TT.current_hunk->prev->next = NULL; |
288 | 197 | ||
289 | fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline); | 198 | fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline); |
290 | TT.exitval = 1; | 199 | TT.exitval = 1; |
@@ -295,7 +204,14 @@ static void fail_hunk(void) | |||
295 | TT.state = 2; | 204 | TT.state = 2; |
296 | llist_free(TT.current_hunk, do_line); | 205 | llist_free(TT.current_hunk, do_line); |
297 | TT.current_hunk = NULL; | 206 | TT.current_hunk = NULL; |
298 | delete_tempfile(TT.filein, TT.fileout, &TT.tempname); | 207 | |
208 | // Abort the copy and delete the temporary file. | ||
209 | close(TT.filein); | ||
210 | close(TT.fileout); | ||
211 | unlink(TT.tempname); | ||
212 | free(TT.tempname); | ||
213 | TT.tempname = NULL; | ||
214 | |||
299 | TT.state = 0; | 215 | TT.state = 0; |
300 | } | 216 | } |
301 | 217 | ||
@@ -334,7 +250,7 @@ static int apply_one_hunk(void) | |||
334 | plist = TT.current_hunk; | 250 | plist = TT.current_hunk; |
335 | buf = NULL; | 251 | buf = NULL; |
336 | if (TT.context) for (;;) { | 252 | if (TT.context) for (;;) { |
337 | char *data = get_line(TT.filein); | 253 | char *data = xmalloc_reads(TT.filein, NULL, NULL); |
338 | 254 | ||
339 | TT.linenum++; | 255 | TT.linenum++; |
340 | 256 | ||
@@ -368,7 +284,9 @@ static int apply_one_hunk(void) | |||
368 | // File ended before we found a place for this hunk. | 284 | // File ended before we found a place for this hunk. |
369 | fail_hunk(); | 285 | fail_hunk(); |
370 | goto done; | 286 | goto done; |
371 | } else if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data); | 287 | } |
288 | |||
289 | if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data); | ||
372 | check = dlist_add(&buf, data); | 290 | check = dlist_add(&buf, data); |
373 | 291 | ||
374 | // Compare this line with next expected line of hunk. | 292 | // Compare this line with next expected line of hunk. |
@@ -398,8 +316,8 @@ static int apply_one_hunk(void) | |||
398 | 316 | ||
399 | // If we've reached the end of the buffer without confirming a | 317 | // If we've reached the end of the buffer without confirming a |
400 | // match, read more lines. | 318 | // match, read more lines. |
401 | if (check==buf) { | 319 | if (check == buf) { |
402 | buf = 0; | 320 | buf = NULL; |
403 | break; | 321 | break; |
404 | } | 322 | } |
405 | check = buf; | 323 | check = buf; |
@@ -453,10 +371,10 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||
453 | TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative! | 371 | TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative! |
454 | TT.filein = TT.fileout = -1; | 372 | TT.filein = TT.fileout = -1; |
455 | if (opts & FLAG_INPUT) { | 373 | if (opts & FLAG_INPUT) { |
456 | TT.filepatch = xopen_stdin(opt_i); | 374 | xmove_fd(xopen_stdin(opt_i), STDIN_FILENO); |
457 | } else { | 375 | } else { |
458 | if (argv[0] && argv[1]) { | 376 | if (argv[0] && argv[1]) { |
459 | TT.filepatch = xopen_stdin(argv[1]); | 377 | xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO); |
460 | } | 378 | } |
461 | } | 379 | } |
462 | if (argv[0]) { | 380 | if (argv[0]) { |
@@ -468,7 +386,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||
468 | for(;;) { | 386 | for(;;) { |
469 | char *patchline; | 387 | char *patchline; |
470 | 388 | ||
471 | patchline = get_line(TT.filepatch); | 389 | patchline = xmalloc_fgetline(stdin); |
472 | if (!patchline) break; | 390 | if (!patchline) break; |
473 | 391 | ||
474 | // Other versions of patch accept damaged patches, | 392 | // Other versions of patch accept damaged patches, |
@@ -588,13 +506,15 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||
588 | } | 506 | } |
589 | // If we've got a file to open, do so. | 507 | // If we've got a file to open, do so. |
590 | } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) { | 508 | } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) { |
509 | struct stat statbuf; | ||
510 | |||
591 | // If the old file was null, we're creating a new one. | 511 | // If the old file was null, we're creating a new one. |
592 | if (!strcmp(oldname, "/dev/null") || !oldsum) { | 512 | if (!strcmp(oldname, "/dev/null") || !oldsum) { |
593 | printf("creating %s\n", name); | 513 | printf("creating %s\n", name); |
594 | s = strrchr(name, '/'); | 514 | s = strrchr(name, '/'); |
595 | if (s) { | 515 | if (s) { |
596 | *s = 0; | 516 | *s = 0; |
597 | xmkpath(name, -1); | 517 | bb_make_directory(name, -1, FILEUTILS_RECUR); |
598 | *s = '/'; | 518 | *s = '/'; |
599 | } | 519 | } |
600 | TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR); | 520 | TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR); |
@@ -602,7 +522,13 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||
602 | printf("patching file %s\n", name); | 522 | printf("patching file %s\n", name); |
603 | TT.filein = xopen(name, O_RDONLY); | 523 | TT.filein = xopen(name, O_RDONLY); |
604 | } | 524 | } |
605 | TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname); | 525 | |
526 | TT.tempname = xasprintf("%sXXXXXX", name); | ||
527 | TT.fileout = xmkstemp(TT.tempname); | ||
528 | // Set permissions of output file | ||
529 | fstat(TT.filein, &statbuf); | ||
530 | fchmod(TT.fileout, statbuf.st_mode); | ||
531 | |||
606 | TT.linenum = 0; | 532 | TT.linenum = 0; |
607 | TT.hunknum = 0; | 533 | TT.hunknum = 0; |
608 | } | 534 | } |
@@ -620,7 +546,6 @@ int patch_main(int argc UNUSED_PARAM, char **argv) | |||
620 | finish_oldfile(); | 546 | finish_oldfile(); |
621 | 547 | ||
622 | if (ENABLE_FEATURE_CLEAN_UP) { | 548 | if (ENABLE_FEATURE_CLEAN_UP) { |
623 | close(TT.filepatch); | ||
624 | free(oldname); | 549 | free(oldname); |
625 | free(newname); | 550 | free(newname); |
626 | } | 551 | } |
diff --git a/testsuite/patch.tests b/testsuite/patch.tests index e482304f6..ba37e8218 100755 --- a/testsuite/patch.tests +++ b/testsuite/patch.tests | |||
@@ -75,7 +75,7 @@ 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 | 78 | Possibly reversed hunk 1 at 4 |
79 | Hunk 1 FAILED 1/1. | 79 | Hunk 1 FAILED 1/1. |
80 | abc | 80 | abc |
81 | +def | 81 | +def |
@@ -103,7 +103,7 @@ def | |||
103 | testing "patch detects already applied hunk at the EOF" \ | 103 | testing "patch detects already applied hunk at the EOF" \ |
104 | 'patch 2>&1; echo $?; cat input' \ | 104 | 'patch 2>&1; echo $?; cat input' \ |
105 | "\ | 105 | "\ |
106 | Possibly reversed hunk 1 at 3 | 106 | Possibly reversed hunk 1 at 4 |
107 | Hunk 1 FAILED 1/1. | 107 | Hunk 1 FAILED 1/1. |
108 | abc | 108 | abc |
109 | 123 | 109 | 123 |