diff options
-rw-r--r-- | toys/Config.in | 3 | ||||
-rw-r--r-- | toys/patch.c | 138 |
2 files changed, 75 insertions, 66 deletions
diff --git a/toys/Config.in b/toys/Config.in index d743bc95..267bcd46 100644 --- a/toys/Config.in +++ b/toys/Config.in @@ -286,6 +286,9 @@ config PATCH -R Reverse patch. -u Ignored (only handles "unified" diffs) + If a hunk fails to apply to a file, patch discards all changes to + that file and prints the failed hunk to stderr. + config PWD bool "pwd" default y diff --git a/toys/patch.c b/toys/patch.c index a8a21728..2f1db0ec 100644 --- a/toys/patch.c +++ b/toys/patch.c @@ -40,7 +40,6 @@ static void do_line(void *data) fdprintf(TT.state == 2 ? 2: TT.fileout, "%s\n", dlist->data+(TT.state>2 ? 1 : 0)); free(dlist->data); - free(dlist); } @@ -57,55 +56,77 @@ static void dlist_add(struct double_list **list, char *data) } else *list = line->next = line->prev = line; } -static void apply_hunk(void) +static void finish_oldfile(void) { - struct double_list *plist, *temp, *buf; - int i = 0, backwards = 0, reverse = toys.optflags & FLAG_REVERSE; - - TT.state = 0; + if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname); +} +static void fail_hunk(void) +{ if (!TT.plines) return; - temp = buf = NULL; + TT.plines->prev->next = 0; + + printf("Hunk FAILED.\n"); + toys.exitval = 1; + + // If we got to this point, we've seeked to the end. Discard changes to + // this file and advance to next file. + + TT.state = 2; + llist_free(TT.plines, do_line); + TT.plines = NULL; + delete_tempfile(TT.filein, TT.fileout, &TT.tempname); + TT.filein = -1; +} - // Hunk is complete, break doubly linked list so we can use singly linked - // traversal function. +static void apply_hunk(void) +{ + struct double_list *plist, *buf = NULL; + int i = 0, backwards = 0, matcheof = 0, + reverse = toys.optflags & FLAG_REVERSE; + + // Break doubly linked list so we can use singly linked traversal function. TT.plines->prev->next = NULL; - // Trim extra context lines, if any. If there aren't as many ending - // context lines as beginning lines, this isn't a valid hunk. + // Match EOF if there aren't as many ending context lines as beginning for (plist = TT.plines; plist; plist = plist->next) { - if (plist->data[0]==' ') { - if (i<TT.context) temp = plist; - i++; - } else i = 0; - } - if (i < TT.context) goto fail_hunk; - if (temp) { - llist_free(temp->next, do_line); - temp->next = NULL; + if (plist->data[0]==' ') i++; + else i = 0; } + if (i < TT.context) matcheof++; // Search for a place to apply this hunk. Match all context lines and // lines to be removed. plist = TT.plines; buf = NULL; i = 0; + + // Start of for loop if (TT.context) for (;;) { char *data = get_line(TT.filein); TT.linenum++; - // If the file ended before we found a home for this hunk, fail. - if (!data) goto fail_hunk; + // Skip lines we'd add. + while (plist && *plist->data == "+-"[reverse]) { + if (data && !backwards && !strcmp(data, plist->data+1)) { + backwards = 1; + fdprintf(2,"Possibly reversed hunk at %ld\n", TT.linenum); + } + plist = plist->next; + } - dlist_add(&buf, data); - if (!backwards && *plist->data == "+-"[reverse]) { - backwards = 1; - if (!strcmp(data, plist->data+1)) - fdprintf(1,"Possibly reversed hunk at %ld\n", TT.linenum); + if (!data) { + // Matched EOF? + if (!plist && matcheof) break; + // File ended before we found a home for this hunk? + fail_hunk(); + goto done; } - while (*plist->data == "+-"[reverse]) plist = plist->next; - if (strcmp(data, plist->data+1)) { // Ignore whitespace? - // Hunk doesn't go here, flush accumulated buffer so far. + dlist_add(&buf, data); + + if (!plist || strcmp(data, plist->data+1)) { // Ignore whitespace? + // Match failed, hunk doesn't go here. Flush accumulated buffer + // so far. buf->prev->next = NULL; TT.state = 1; @@ -113,8 +134,9 @@ static void apply_hunk(void) buf = NULL; plist = TT.plines; } else { + // Match, advance plist. plist = plist->next; - if (!plist) break; + if (!plist && !matcheof) break; } } @@ -122,29 +144,12 @@ static void apply_hunk(void) TT.state = "-+"[reverse]; llist_free(TT.plines, do_line); TT.plines = NULL; +done: TT.state = 0; if (buf) { buf->prev->next = NULL; llist_free(buf, do_line); } - return; - -fail_hunk: - printf("Hunk FAILED.\n"); - - // If we got to this point, we've seeked to the end. Discard changes to - // this file and advance to next file. - - TT.state = 2; - llist_free(TT.plines, do_line); - TT.plines = 0; - if (buf) { - buf->prev->next = NULL; - llist_free(buf, do_line); - } - delete_tempfile(TT.filein, TT.fileout, &TT.tempname); - TT.filein = -1; - TT.state = 0; } // state 0: Not in a hunk, look for +++. @@ -155,7 +160,6 @@ fail_hunk: void patch_main(void) { if (TT.infile) TT.filepatch = xopen(TT.infile, O_RDONLY); - else TT.filepatch = 0; TT.filein = TT.fileout = -1; // Loop through the lines in the patch @@ -167,20 +171,24 @@ void patch_main(void) // Are we processing a hunk? if (TT.state >= 2) { - // Context line? if (*patchline==' ' || *patchline=='+' || *patchline=='-') { dlist_add(&TT.plines, patchline); + if (*patchline != '+') TT.oldlen--; + if (*patchline != '-') TT.newlen--; + + // Context line? if (*patchline==' ' && TT.state==2) TT.context++; else TT.state=3; + if (!TT.oldlen && !TT.newlen) apply_hunk(); continue; } + fail_hunk(); + TT.state = 0; + continue; } - // If we have a hunk at this point, it's ready to apply. - apply_hunk(); - // Open a new file? if (!strncmp("--- ", patchline, 4)) { char *s; @@ -196,9 +204,7 @@ void patch_main(void) int i = 0, del = 0; char *s, *start; - // Finish old file. - if (TT.tempname) - replace_tempfile(TT.filein, TT.fileout, &TT.tempname); + finish_oldfile(); // Trim date from end of filename (if any). We don't care. for (s = patchline+4; *s && *s!='\t'; s++) @@ -206,7 +212,7 @@ void patch_main(void) *s = 0; - // If new file is null (before -p trim), we're deleting oldname + // If new file is /dev/null (before -p), we're deleting oldname start = patchline+4; if (!strcmp(start, "/dev/null")) { start = TT.oldname; @@ -246,22 +252,22 @@ void patch_main(void) } // Start a new hunk? - } else if (TT.filein!=-1 && !strncmp("@@ ", patchline, 3)) { + // Test filein rather than state to report only the first failed hunk. + } else if (TT.filein!=-1 && !strncmp("@@ -", patchline, 4) && + 4 == sscanf(patchline+4, "%ld,%ld +%ld,%ld", &TT.oldline, + &TT.oldlen, &TT.newline, &TT.newlen)) + { TT.context = 0; TT.state = 2; - sscanf(patchline+3, "%ld,%ld %ld,%ld", &TT.oldline, - &TT.oldlen, &TT.newline, &TT.newlen); - // Don't free it. continue; } - // This line is noise, discard it. + // If we didn't continue above, discard this line. free(patchline); } - // Flush pending hunk and flush data - apply_hunk(); - if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname); + finish_oldfile(); + if (CFG_TOYBOX_FREE) { close(TT.filepatch); free(TT.oldname); |