aboutsummaryrefslogtreecommitdiff
path: root/editors/patch.c
diff options
context:
space:
mode:
Diffstat (limited to 'editors/patch.c')
-rw-r--r--editors/patch.c336
1 files changed, 128 insertions, 208 deletions
diff --git a/editors/patch.c b/editors/patch.c
index 33ff8b569..9c6d967b9 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,30 +19,45 @@
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
24USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"up#i:R", TOYFLAG_USR|TOYFLAG_BIN)) 57#include "libbb.h"
25
26config 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 58
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 59
39 This version of patch only handles unified diffs, and only modifies 60// libbb candidate?
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"
47 61
48struct double_list { 62struct double_list {
49 struct double_list *next; 63 struct double_list *next;
@@ -51,188 +65,55 @@ struct double_list {
51 char *data; 65 char *data;
52}; 66};
53 67
54// Return the first item from the list, advancing the list (which must be called
55// as &list)
56static
57void *TOY_llist_pop(void *list)
58{
59 // I'd use a void ** for the argument, and even accept the typecast in all
60 // callers as documentation you need the &, except the stupid compiler
61 // would then scream about type-punned pointers. Screw it.
62 void **llist = (void **)list;
63 void **next = (void **)*llist;
64 *llist = *next;
65
66 return (void *)next;
67}
68
69// Free all the elements of a linked list 68// Free all the elements of a linked list
70// if freeit!=NULL call freeit() on each element before freeing it. 69// Call freeit() on each element before freeing it.
71static 70static
72void TOY_llist_free(void *list, void (*freeit)(void *data)) 71void dlist_free(struct double_list *list, void (*freeit)(void *data))
73{ 72{
74 while (list) { 73 while (list) {
75 void *pop = TOY_llist_pop(&list); 74 void *pop = list;
76 if (freeit) freeit(pop); 75 list = list->next;
77 else free(pop); 76 freeit(pop);
78 77 // Bail out also if list is circular.
79 // End doubly linked list too. 78 if (list == pop) break;
80 if (list==pop) break;
81 } 79 }
82} 80}
83//Override bbox's names
84#define llist_pop TOY_llist_pop
85#define llist_free TOY_llist_free
86 81
87// Add an entry to the end off a doubly linked list 82// Add an entry before "list" element in (circular) doubly linked list
88static 83static
89struct double_list *dlist_add(struct double_list **list, char *data) 84struct double_list *dlist_add(struct double_list **list, char *data)
90{ 85{
91 struct double_list *line = xmalloc(sizeof(struct double_list)); 86 struct double_list *llist;
87 struct double_list *line = xmalloc(sizeof(*line));
92 88
93 line->data = data; 89 line->data = data;
94 if (*list) { 90 llist = *list;
95 line->next = *list; 91 if (llist) {
96 line->prev = (*list)->prev; 92 struct double_list *p;
97 (*list)->prev->next = line; 93 line->next = llist;
98 (*list)->prev = line; 94 p = line->prev = llist->prev;
99 } else *list = line->next = line->prev = line; 95 // (list is circular, we assume p is never NULL)
96 p->next = line;
97 llist->prev = line;
98 } else
99 *list = line->next = line->prev = line;
100 100
101 return line; 101 return line;
102} 102}
103 103
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).
107static
108void 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.
134static
135char *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
151static
152char *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.
163static
164void 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.
178static
179void 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.
196static
197int 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.
213static
214void 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
224 104
225struct globals { 105struct globals {
226 char *infile; 106 char *infile;
227 long prefix; 107 long prefix;
228 108
229 struct double_list *current_hunk; 109 struct double_list *current_hunk;
110
230 long oldline, oldlen, newline, newlen; 111 long oldline, oldlen, newline, newlen;
231 long linenum; 112 long linenum;
232 int context, state, filein, fileout, filepatch, hunknum; 113 int context, state, hunknum;
114 int filein, fileout;
233 char *tempname; 115 char *tempname;
234 116
235 // was toys.foo:
236 int exitval; 117 int exitval;
237}; 118};
238#define TT (*ptr_to_globals) 119#define TT (*ptr_to_globals)
@@ -263,7 +144,7 @@ struct globals {
263 144
264static void do_line(void *data) 145static void do_line(void *data)
265{ 146{
266 struct double_list *dlist = (struct double_list *)data; 147 struct double_list *dlist = data;
267 148
268 if (TT.state>1 && *dlist->data != TT.state) 149 if (TT.state>1 && *dlist->data != TT.state)
269 fdprintf(TT.state == 2 ? 2 : TT.fileout, 150 fdprintf(TT.state == 2 ? 2 : TT.fileout,
@@ -272,19 +153,35 @@ static void do_line(void *data)
272 if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data); 153 if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data);
273 154
274 free(dlist->data); 155 free(dlist->data);
275 free(data); 156 free(dlist);
276} 157}
277 158
278static void finish_oldfile(void) 159static void finish_oldfile(void)
279{ 160{
280 if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname); 161 if (TT.tempname) {
162 // Copy the rest of the data and replace the original with the copy.
163 char *temp;
164
165 if (TT.filein != -1) {
166 bb_copyfd_eof(TT.filein, TT.fileout);
167 xclose(TT.filein);
168 }
169 xclose(TT.fileout);
170
171 temp = xstrdup(TT.tempname);
172 temp[strlen(temp) - 6] = '\0';
173 rename(TT.tempname, temp);
174 free(temp);
175
176 free(TT.tempname);
177 TT.tempname = NULL;
178 }
281 TT.fileout = TT.filein = -1; 179 TT.fileout = TT.filein = -1;
282} 180}
283 181
284static void fail_hunk(void) 182static void fail_hunk(void)
285{ 183{
286 if (!TT.current_hunk) return; 184 if (!TT.current_hunk) return;
287 TT.current_hunk->prev->next = 0;
288 185
289 fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline); 186 fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline);
290 TT.exitval = 1; 187 TT.exitval = 1;
@@ -293,9 +190,17 @@ static void fail_hunk(void)
293 // this file and advance to next file. 190 // this file and advance to next file.
294 191
295 TT.state = 2; 192 TT.state = 2;
296 llist_free(TT.current_hunk, do_line); 193 TT.current_hunk->prev->next = NULL;
194 dlist_free(TT.current_hunk, do_line);
297 TT.current_hunk = NULL; 195 TT.current_hunk = NULL;
298 delete_tempfile(TT.filein, TT.fileout, &TT.tempname); 196
197 // Abort the copy and delete the temporary file.
198 close(TT.filein);
199 close(TT.fileout);
200 unlink(TT.tempname);
201 free(TT.tempname);
202 TT.tempname = NULL;
203
299 TT.state = 0; 204 TT.state = 0;
300} 205}
301 206
@@ -333,8 +238,8 @@ static int apply_one_hunk(void)
333 // complete hunk. 238 // complete hunk.
334 plist = TT.current_hunk; 239 plist = TT.current_hunk;
335 buf = NULL; 240 buf = NULL;
336 if (TT.context) for (;;) { 241 if (reverse ? TT.oldlen : TT.newlen) for (;;) {
337 char *data = get_line(TT.filein); 242 char *data = xmalloc_reads(TT.filein, NULL, NULL);
338 243
339 TT.linenum++; 244 TT.linenum++;
340 245
@@ -368,7 +273,9 @@ static int apply_one_hunk(void)
368 // File ended before we found a place for this hunk. 273 // File ended before we found a place for this hunk.
369 fail_hunk(); 274 fail_hunk();
370 goto done; 275 goto done;
371 } else if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data); 276 }
277
278 if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data);
372 check = dlist_add(&buf, data); 279 check = dlist_add(&buf, data);
373 280
374 // Compare this line with next expected line of hunk. 281 // Compare this line with next expected line of hunk.
@@ -390,7 +297,8 @@ static int apply_one_hunk(void)
390 fdprintf(2, "NOT: %s\n", plist->data); 297 fdprintf(2, "NOT: %s\n", plist->data);
391 298
392 TT.state = 3; 299 TT.state = 3;
393 check = llist_pop(&buf); 300 check = buf;
301 buf = buf->next;
394 check->prev->next = buf; 302 check->prev->next = buf;
395 buf->prev = check->prev; 303 buf->prev = check->prev;
396 do_line(check); 304 do_line(check);
@@ -398,8 +306,8 @@ static int apply_one_hunk(void)
398 306
399 // If we've reached the end of the buffer without confirming a 307 // If we've reached the end of the buffer without confirming a
400 // match, read more lines. 308 // match, read more lines.
401 if (check==buf) { 309 if (check == buf) {
402 buf = 0; 310 buf = NULL;
403 break; 311 break;
404 } 312 }
405 check = buf; 313 check = buf;
@@ -417,13 +325,13 @@ static int apply_one_hunk(void)
417out: 325out:
418 // We have a match. Emit changed data. 326 // We have a match. Emit changed data.
419 TT.state = "-+"[reverse ^ dummy_revert]; 327 TT.state = "-+"[reverse ^ dummy_revert];
420 llist_free(TT.current_hunk, do_line); 328 dlist_free(TT.current_hunk, do_line);
421 TT.current_hunk = NULL; 329 TT.current_hunk = NULL;
422 TT.state = 1; 330 TT.state = 1;
423done: 331done:
424 if (buf) { 332 if (buf) {
425 buf->prev->next = NULL; 333 buf->prev->next = NULL;
426 llist_free(buf, do_line); 334 dlist_free(buf, do_line);
427 } 335 }
428 336
429 return TT.state; 337 return TT.state;
@@ -444,6 +352,8 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
444 int reverse, state = 0; 352 int reverse, state = 0;
445 char *oldname = NULL, *newname = NULL; 353 char *oldname = NULL, *newname = NULL;
446 char *opt_p, *opt_i; 354 char *opt_p, *opt_i;
355 long oldlen = oldlen; /* for compiler */
356 long newlen = newlen; /* for compiler */
447 357
448 INIT_TT(); 358 INIT_TT();
449 359
@@ -453,10 +363,10 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
453 TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative! 363 TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative!
454 TT.filein = TT.fileout = -1; 364 TT.filein = TT.fileout = -1;
455 if (opts & FLAG_INPUT) { 365 if (opts & FLAG_INPUT) {
456 TT.filepatch = xopen_stdin(opt_i); 366 xmove_fd(xopen_stdin(opt_i), STDIN_FILENO);
457 } else { 367 } else {
458 if (argv[0] && argv[1]) { 368 if (argv[0] && argv[1]) {
459 TT.filepatch = xopen_stdin(argv[1]); 369 xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO);
460 } 370 }
461 } 371 }
462 if (argv[0]) { 372 if (argv[0]) {
@@ -468,7 +378,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
468 for(;;) { 378 for(;;) {
469 char *patchline; 379 char *patchline;
470 380
471 patchline = get_line(TT.filepatch); 381 patchline = xmalloc_fgetline(stdin);
472 if (!patchline) break; 382 if (!patchline) break;
473 383
474 // Other versions of patch accept damaged patches, 384 // Other versions of patch accept damaged patches,
@@ -483,8 +393,8 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
483 if (*patchline==' ' || *patchline=='+' || *patchline=='-') { 393 if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
484 dlist_add(&TT.current_hunk, patchline); 394 dlist_add(&TT.current_hunk, patchline);
485 395
486 if (*patchline != '+') TT.oldlen--; 396 if (*patchline != '+') oldlen--;
487 if (*patchline != '-') TT.newlen--; 397 if (*patchline != '-') newlen--;
488 398
489 // Context line? 399 // Context line?
490 if (*patchline==' ' && state==2) TT.context++; 400 if (*patchline==' ' && state==2) TT.context++;
@@ -492,7 +402,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
492 402
493 // If we've consumed all expected hunk lines, apply the hunk. 403 // If we've consumed all expected hunk lines, apply the hunk.
494 404
495 if (!TT.oldlen && !TT.newlen) state = apply_one_hunk(); 405 if (!oldlen && !newlen) state = apply_one_hunk();
496 continue; 406 continue;
497 } 407 }
498 fail_hunk(); 408 fail_hunk();
@@ -539,11 +449,14 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
539 449
540 // Read oldline[,oldlen] +newline[,newlen] 450 // Read oldline[,oldlen] +newline[,newlen]
541 451
542 TT.oldlen = TT.newlen = 1; 452 TT.oldlen = oldlen = TT.newlen = newlen = 1;
543 TT.oldline = strtol(s, &s, 10); 453 TT.oldline = strtol(s, &s, 10);
544 if (*s == ',') TT.oldlen=strtol(s+1, &s, 10); 454 if (*s == ',') TT.oldlen = oldlen = strtol(s+1, &s, 10);
545 TT.newline = strtol(s+2, &s, 10); 455 TT.newline = strtol(s+2, &s, 10);
546 if (*s == ',') TT.newlen = strtol(s+1, &s, 10); 456 if (*s == ',') TT.newlen = newlen = strtol(s+1, &s, 10);
457
458 if (oldlen < 1 && newlen < 1)
459 bb_error_msg_and_die("Really? %s", patchline);
547 460
548 TT.context = 0; 461 TT.context = 0;
549 state = 2; 462 state = 2;
@@ -553,8 +466,8 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
553 int oldsum, newsum, empty = 0; 466 int oldsum, newsum, empty = 0;
554 char *name; 467 char *name;
555 468
556 oldsum = TT.oldline + TT.oldlen; 469 oldsum = TT.oldline + oldlen;
557 newsum = TT.newline + TT.newlen; 470 newsum = TT.newline + newlen;
558 471
559 name = reverse ? oldname : newname; 472 name = reverse ? oldname : newname;
560 473
@@ -588,13 +501,15 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
588 } 501 }
589 // If we've got a file to open, do so. 502 // If we've got a file to open, do so.
590 } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) { 503 } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) {
504 struct stat statbuf;
505
591 // If the old file was null, we're creating a new one. 506 // If the old file was null, we're creating a new one.
592 if (!strcmp(oldname, "/dev/null") || !oldsum) { 507 if (!strcmp(oldname, "/dev/null") || !oldsum) {
593 printf("creating %s\n", name); 508 printf("creating %s\n", name);
594 s = strrchr(name, '/'); 509 s = strrchr(name, '/');
595 if (s) { 510 if (s) {
596 *s = 0; 511 *s = 0;
597 xmkpath(name, -1); 512 bb_make_directory(name, -1, FILEUTILS_RECUR);
598 *s = '/'; 513 *s = '/';
599 } 514 }
600 TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR); 515 TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR);
@@ -602,7 +517,13 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
602 printf("patching file %s\n", name); 517 printf("patching file %s\n", name);
603 TT.filein = xopen(name, O_RDONLY); 518 TT.filein = xopen(name, O_RDONLY);
604 } 519 }
605 TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname); 520
521 TT.tempname = xasprintf("%sXXXXXX", name);
522 TT.fileout = xmkstemp(TT.tempname);
523 // Set permissions of output file
524 fstat(TT.filein, &statbuf);
525 fchmod(TT.fileout, statbuf.st_mode);
526
606 TT.linenum = 0; 527 TT.linenum = 0;
607 TT.hunknum = 0; 528 TT.hunknum = 0;
608 } 529 }
@@ -620,7 +541,6 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
620 finish_oldfile(); 541 finish_oldfile();
621 542
622 if (ENABLE_FEATURE_CLEAN_UP) { 543 if (ENABLE_FEATURE_CLEAN_UP) {
623 close(TT.filepatch);
624 free(oldname); 544 free(oldname);
625 free(newname); 545 free(newname);
626 } 546 }