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 |
