--- trunk/mkinitrd-magellan/busybox/editors/patch.c 2010/12/15 21:16:52 1178 +++ trunk/mkinitrd-magellan/busybox/editors/patch.c 2010/12/15 21:33:41 1179 @@ -1,306 +1,621 @@ -/* vi: set sw=4 ts=4: */ -/* - * busybox patch applet to handle the unified diff format. - * Copyright (C) 2003 Glenn McGrath +/* Adapted from toybox's patch. */ + +/* vi: set sw=4 ts=4: * - * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + * patch.c - Apply a "universal" diff. * - * This applet is written to work with patches generated by GNU diff, - * where there is equivalent functionality busybox patch shall behave - * as per GNU patch. + * Copyright 2007 Rob Landley * - * There is a SUSv3 specification for patch, however it looks to be - * incomplete, it doesnt even mention unified diff format. - * http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html + * see http://www.opengroup.org/onlinepubs/009695399/utilities/patch.html + * (But only does -u, because who still cares about "ed"?) * - * Issues - * - Non-interactive - * - Patches must apply cleanly or patch (not just one hunk) will fail. - * - Reject file isnt saved - */ - + * TODO: + * -b backup + * -l treat all whitespace as a single space + * -d chdir first + * -D define wrap #ifdef and #ifndef around changes + * -o outfile output here instead of in place + * -r rejectfile write rejected hunks to this file + * + * -E remove empty files --remove-empty-files + * -f force (no questions asked) + * -F fuzz (number, default 2) + * [file] which file to patch + +USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"up#i:R", TOYFLAG_USR|TOYFLAG_BIN)) + +config PATCH + bool "patch" + default y + help + usage: patch [-i file] [-p depth] [-Ru] + + Apply a unified diff to one or more files. + + -i Input file (defaults=stdin) + -p number of '/' to strip from start of file paths (default=all) + -R Reverse patch. + -u Ignored (only handles "unified" diffs) + + This version of patch only handles unified diffs, and only modifies + a file when all all hunks to that file apply. Patch prints failed + hunks to stderr, and exits with nonzero status if any hunks fail. + + A file compared against /dev/null (or with a date <= the epoch) is + created/deleted as appropriate. +*/ #include "libbb.h" -static unsigned copy_lines(FILE *src_stream, FILE *dst_stream, unsigned lines_count) +struct double_list { + struct double_list *next; + struct double_list *prev; + char *data; +}; + +// Return the first item from the list, advancing the list (which must be called +// as &list) +static +void *TOY_llist_pop(void *list) { - while (src_stream && lines_count) { - char *line; - line = xmalloc_fgets(src_stream); - if (line == NULL) { - break; - } - if (fputs(line, dst_stream) == EOF) { - bb_perror_msg_and_die("error writing to new file"); + // I'd use a void ** for the argument, and even accept the typecast in all + // callers as documentation you need the &, except the stupid compiler + // would then scream about type-punned pointers. Screw it. + void **llist = (void **)list; + void **next = (void **)*llist; + *llist = *next; + + return (void *)next; +} + +// Free all the elements of a linked list +// if freeit!=NULL call freeit() on each element before freeing it. +static +void TOY_llist_free(void *list, void (*freeit)(void *data)) +{ + while (list) { + void *pop = TOY_llist_pop(&list); + if (freeit) freeit(pop); + else free(pop); + + // End doubly linked list too. + if (list==pop) break; + } +} +//Override bbox's names +#define llist_pop TOY_llist_pop +#define llist_free TOY_llist_free + +// Add an entry to the end off a doubly linked list +static +struct double_list *dlist_add(struct double_list **list, char *data) +{ + struct double_list *line = xmalloc(sizeof(struct double_list)); + + line->data = data; + if (*list) { + line->next = *list; + line->prev = (*list)->prev; + (*list)->prev->next = line; + (*list)->prev = line; + } else *list = line->next = line->prev = line; + + return line; +} + +// Ensure entire path exists. +// If mode != -1 set permissions on newly created dirs. +// Requires that path string be writable (for temporary null terminators). +static +void xmkpath(char *path, int mode) +{ + char *p, old; + mode_t mask; + int rc; + struct stat st; + + for (p = path; ; p++) { + if (!*p || *p == '/') { + old = *p; + *p = rc = 0; + if (stat(path, &st) || !S_ISDIR(st.st_mode)) { + if (mode != -1) { + mask = umask(0); + rc = mkdir(path, mode); + umask(mask); + } else rc = mkdir(path, 0777); + } + *p = old; + if(rc) bb_perror_msg_and_die("mkpath '%s'", path); } - free(line); - lines_count--; + if (!*p) break; + } +} + +// Slow, but small. +static +char *get_rawline(int fd, long *plen, char end) +{ + char c, *buf = NULL; + long len = 0; + + for (;;) { + if (1>read(fd, &c, 1)) break; + if (!(len & 63)) buf=xrealloc(buf, len+65); + if ((buf[len++]=c) == end) break; + } + if (buf) buf[len]=0; + if (plen) *plen = len; + + return buf; +} + +static +char *get_line(int fd) +{ + long len; + char *buf = get_rawline(fd, &len, '\n'); + + if (buf && buf[--len]=='\n') buf[len]=0; + + return buf; +} + +// Copy the rest of in to out and close both files. +static +void xsendfile(int in, int out) +{ + long len; + char buf[4096]; + + if (in<0) return; + for (;;) { + len = safe_read(in, buf, 4096); + if (len<1) break; + xwrite(out, buf, len); } - return lines_count; } -/* If patch_level is -1 it will remove all directory names - * char *line must be greater than 4 chars - * returns NULL if the file doesnt exist or error - * returns malloc'ed filename - * NB: frees 1st argument! - */ -static char *extract_filename(char *line, int patch_level, const char *pat) -{ - char *temp = NULL, *filename_start_ptr = line + 4; - - if (strncmp(line, pat, 4) == 0) { - /* Terminate string at end of source filename */ - line[strcspn(line, "\t\n\r")] = '\0'; - - /* Skip over (patch_level) number of leading directories */ - while (patch_level--) { - temp = strchr(filename_start_ptr, '/'); - if (!temp) - break; - filename_start_ptr = temp + 1; +// Copy the rest of the data and replace the original with the copy. +static +void replace_tempfile(int fdin, int fdout, char **tempname) +{ + char *temp = xstrdup(*tempname); + + temp[strlen(temp)-6]=0; + if (fdin != -1) { + xsendfile(fdin, fdout); + xclose(fdin); + } + xclose(fdout); + rename(*tempname, temp); + free(*tempname); + free(temp); + *tempname = NULL; +} + +// Open a temporary file to copy an existing file into. +static +int copy_tempfile(int fdin, char *name, char **tempname) +{ + struct stat statbuf; + int fd; + + *tempname = xasprintf("%sXXXXXX", name); + fd = mkstemp(*tempname); + if(-1 == fd) bb_perror_msg_and_die("no temp file"); + + // Set permissions of output file + fstat(fdin, &statbuf); + fchmod(fd, statbuf.st_mode); + + return fd; +} + +// Abort the copy and delete the temporary file. +static +void delete_tempfile(int fdin, int fdout, char **tempname) +{ + close(fdin); + close(fdout); + unlink(*tempname); + free(*tempname); + *tempname = NULL; +} + + + +struct globals { + char *infile; + long prefix; + + struct double_list *current_hunk; + long oldline, oldlen, newline, newlen; + long linenum; + int context, state, filein, fileout, filepatch, hunknum; + char *tempname; + + // was toys.foo: + int exitval; +}; +#define TT (*ptr_to_globals) +#define INIT_TT() do { \ + SET_PTR_TO_GLOBALS(xzalloc(sizeof(TT))); \ +} while (0) + + +#define FLAG_STR "Rup:i:Nx" +/* FLAG_REVERSE must be == 1! Code uses this fact. */ +#define FLAG_REVERSE (1 << 0) +#define FLAG_u (1 << 1) +#define FLAG_PATHLEN (1 << 2) +#define FLAG_INPUT (1 << 3) +#define FLAG_IGNORE (1 << 4) +//non-standard: +#define FLAG_DEBUG (1 << 5) + +// Dispose of a line of input, either by writing it out or discarding it. + +// state < 2: just free +// state = 2: write whole line to stderr +// state = 3: write whole line to fileout +// state > 3: write line+1 to fileout when *line != state + +#define PATCH_DEBUG (option_mask32 & FLAG_DEBUG) + +static void do_line(void *data) +{ + struct double_list *dlist = (struct double_list *)data; + + if (TT.state>1 && *dlist->data != TT.state) + fdprintf(TT.state == 2 ? 2 : TT.fileout, + "%s\n", dlist->data+(TT.state>3 ? 1 : 0)); + + if (PATCH_DEBUG) fdprintf(2, "DO %d: %s\n", TT.state, dlist->data); + + free(dlist->data); + free(data); +} + +static void finish_oldfile(void) +{ + if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname); + TT.fileout = TT.filein = -1; +} + +static void fail_hunk(void) +{ + if (!TT.current_hunk) return; + TT.current_hunk->prev->next = 0; + + fdprintf(2, "Hunk %d FAILED %ld/%ld.\n", TT.hunknum, TT.oldline, TT.newline); + TT.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.current_hunk, do_line); + TT.current_hunk = NULL; + delete_tempfile(TT.filein, TT.fileout, &TT.tempname); + TT.state = 0; +} + +// Given a hunk of a unified diff, make the appropriate change to the file. +// This does not use the location information, but instead treats a hunk +// as a sort of regex. Copies data from input to output until it finds +// the change to be made, then outputs the changed data and returns. +// (Finding EOF first is an error.) This is a single pass operation, so +// multiple hunks must occur in order in the file. + +static int apply_one_hunk(void) +{ + struct double_list *plist, *buf = NULL, *check; + int matcheof = 0, reverse = option_mask32 & FLAG_REVERSE, backwarn = 0; + /* Do we try "dummy" revert to check whether + * to silently skip this hunk? Used to implement -N. + */ + int dummy_revert = 0; + + // Break doubly linked list so we can use singly linked traversal function. + TT.current_hunk->prev->next = NULL; + + // Match EOF if there aren't as many ending context lines as beginning + for (plist = TT.current_hunk; plist; plist = plist->next) { + if (plist->data[0]==' ') matcheof++; + else matcheof = 0; + if (PATCH_DEBUG) fdprintf(2, "HUNK:%s\n", plist->data); + } + matcheof = matcheof < TT.context; + + if (PATCH_DEBUG) fdprintf(2,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N'); + + // Loop through input data searching for this hunk. Match all context + // lines and all lines to be removed until we've found the end of a + // complete hunk. + plist = TT.current_hunk; + buf = NULL; + if (TT.context) for (;;) { + char *data = get_line(TT.filein); + + TT.linenum++; + + // Figure out which line of hunk to compare with next. (Skip lines + // of the hunk we'd be adding.) + while (plist && *plist->data == "+-"[reverse]) { + if (data && !strcmp(data, plist->data+1)) { + if (!backwarn) { + backwarn++; + if (option_mask32 & FLAG_IGNORE) { + dummy_revert = 1; + reverse ^= 1; + continue; + } + fdprintf(2,"Possibly reversed hunk %d at %ld\n", + TT.hunknum, TT.linenum); + } + } + plist = plist->next; + } + + // Is this EOF? + if (!data) { + if (PATCH_DEBUG) fdprintf(2, "INEOF\n"); + + // Does this hunk need to match EOF? + if (!plist && matcheof) break; + + // File ended before we found a place for this hunk. + fail_hunk(); + goto done; + } else if (PATCH_DEBUG) fdprintf(2, "IN: %s\n", data); + check = dlist_add(&buf, data); + + // Compare this line with next expected line of hunk. + // todo: teach the strcmp() to ignore whitespace. + + // A match can fail because the next line doesn't match, or because + // we hit the end of a hunk that needed EOF, and this isn't EOF. + + // If match failed, flush first line of buffered data and + // recheck buffered data for a new match until we find one or run + // out of buffer. + + for (;;) { + if (!plist || strcmp(check->data, plist->data+1)) { + // Match failed. Write out first line of buffered data and + // recheck remaining buffered data for a new match. + + if (PATCH_DEBUG) + fdprintf(2, "NOT: %s\n", plist->data); + + TT.state = 3; + check = llist_pop(&buf); + check->prev->next = buf; + buf->prev = check->prev; + do_line(check); + plist = TT.current_hunk; + + // If we've reached the end of the buffer without confirming a + // match, read more lines. + if (check==buf) { + buf = 0; + break; + } + check = buf; + } else { + if (PATCH_DEBUG) + fdprintf(2, "MAYBE: %s\n", plist->data); + // This line matches. Advance plist, detect successful match. + plist = plist->next; + if (!plist && !matcheof) goto out; + check = check->next; + if (check == buf) break; + } } - temp = xstrdup(filename_start_ptr); } - free(line); - return temp; +out: + // We have a match. Emit changed data. + TT.state = "-+"[reverse ^ dummy_revert]; + llist_free(TT.current_hunk, do_line); + TT.current_hunk = NULL; + TT.state = 1; +done: + if (buf) { + buf->prev->next = NULL; + llist_free(buf, do_line); + } + + return TT.state; } +// Read a patch file and find hunks, opening/creating/deleting files. +// Call apply_one_hunk() on each hunk. + +// state 0: Not in a hunk, look for +++. +// state 1: Found +++ file indicator, look for @@ +// state 2: In hunk: counting initial context lines +// state 3: In hunk: getting body + int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int patch_main(int argc UNUSED_PARAM, char **argv) { - struct stat saved_stat; - char *patch_line; - FILE *patch_file; - int patch_level; - int ret = 0; - char plus = '+'; - unsigned opt; - enum { - OPT_R = (1 << 2), - OPT_N = (1 << 3), - /*OPT_f = (1 << 4), ignored */ - /*OPT_E = (1 << 5), ignored, this is the default */ - /*OPT_g = (1 << 6), ignored */ - OPT_dry_run = (1 << 7) * ENABLE_LONG_OPTS, - }; - - xfunc_error_retval = 2; - { - const char *p = "-1"; - const char *i = "-"; /* compat */ -#if ENABLE_LONG_OPTS - static const char patch_longopts[] ALIGN1 = - "strip\0" Required_argument "p" - "input\0" Required_argument "i" - "reverse\0" No_argument "R" - "forward\0" No_argument "N" - /* "Assume user knows what [s]he is doing, do not ask any questions": */ - "force\0" No_argument "f" /*ignored*/ -# if ENABLE_DESKTOP - "remove-empty-files\0" No_argument "E" /*ignored*/ - /* "Controls actions when a file is under RCS or SCCS control, - * and does not exist or is read-only and matches the default version, - * or when a file is under ClearCase control and does not exist..." - * IOW: rather obscure option. - * But Gentoo's portage does use -g0 */ - "get\0" Required_argument "g" /*ignored*/ -# endif - "dry-run\0" No_argument "\xfd" -# if ENABLE_DESKTOP - "backup-if-mismatch\0" No_argument "\xfe" /*ignored*/ - "no-backup-if-mismatch\0" No_argument "\xff" /*ignored*/ -# endif - ; - applet_long_options = patch_longopts; -#endif - /* -f,-E,-g are ignored */ - opt = getopt32(argv, "p:i:RN""fEg:", &p, &i, NULL); - if (opt & OPT_R) - plus = '-'; - patch_level = xatoi(p); /* can be negative! */ - patch_file = xfopen_stdin(i); - } - - patch_line = xmalloc_fgetline(patch_file); - while (patch_line) { - FILE *src_stream; - FILE *dst_stream; - //char *old_filename; - char *new_filename; - char *backup_filename = NULL; - unsigned src_cur_line = 1; - unsigned dst_cur_line = 0; - unsigned dst_beg_line; - unsigned bad_hunk_count = 0; - unsigned hunk_count = 0; - smallint copy_trailing_lines_flag = 0; - - /* Skip everything upto the "---" marker - * No need to parse the lines "Only in ", and "diff " - */ - do { - /* Extract the filename used before the patch was generated */ - new_filename = extract_filename(patch_line, patch_level, "--- "); - // was old_filename above - patch_line = xmalloc_fgetline(patch_file); - if (!patch_line) goto quit; - } while (!new_filename); - free(new_filename); // "source" filename is irrelevant - - new_filename = extract_filename(patch_line, patch_level, "+++ "); - if (!new_filename) { - bb_error_msg_and_die("invalid patch"); + int opts; + int reverse, state = 0; + char *oldname = NULL, *newname = NULL; + char *opt_p, *opt_i; + + INIT_TT(); + + opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i); + argv += optind; + reverse = opts & FLAG_REVERSE; + TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative! + TT.filein = TT.fileout = -1; + if (opts & FLAG_INPUT) { + TT.filepatch = xopen_stdin(opt_i); + } else { + if (argv[0] && argv[1]) { + TT.filepatch = xopen_stdin(argv[1]); } + } + if (argv[0]) { + oldname = xstrdup(argv[0]); + newname = xstrdup(argv[0]); + } - /* Get access rights from the file to be patched */ - if (stat(new_filename, &saved_stat) != 0) { - char *slash = strrchr(new_filename, '/'); - if (slash) { - /* Create leading directories */ - *slash = '\0'; - bb_make_directory(new_filename, -1, FILEUTILS_RECUR); - *slash = '/'; - } - src_stream = NULL; - saved_stat.st_mode = 0644; - } else if (!(opt & OPT_dry_run)) { - backup_filename = xasprintf("%s.orig", new_filename); - xrename(new_filename, backup_filename); - src_stream = xfopen_for_read(backup_filename); - } else - src_stream = xfopen_for_read(new_filename); - - if (opt & OPT_dry_run) { - dst_stream = xfopen_for_write("/dev/null"); - } else { - dst_stream = xfopen_for_write(new_filename); - fchmod(fileno(dst_stream), saved_stat.st_mode); + // Loop through the lines in the patch + for(;;) { + char *patchline; + + patchline = get_line(TT.filepatch); + if (!patchline) break; + + // Other versions of patch accept damaged patches, + // so we need to also. + if (!*patchline) { + free(patchline); + patchline = xstrdup(" "); } - printf("patching file %s\n", new_filename); + // Are we assembling a hunk? + if (state >= 2) { + if (*patchline==' ' || *patchline=='+' || *patchline=='-') { + dlist_add(&TT.current_hunk, patchline); + + if (*patchline != '+') TT.oldlen--; + if (*patchline != '-') TT.newlen--; + + // Context line? + if (*patchline==' ' && state==2) TT.context++; + else state=3; + + // If we've consumed all expected hunk lines, apply the hunk. - /* Handle all hunks for this file */ - patch_line = xmalloc_fgets(patch_file); - while (patch_line) { - unsigned count; - unsigned src_beg_line; - unsigned hunk_offset_start; - unsigned src_last_line = 1; - unsigned dst_last_line = 1; - - if ((sscanf(patch_line, "@@ -%d,%d +%d,%d", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3) - && (sscanf(patch_line, "@@ -%d +%d,%d", &src_beg_line, &dst_beg_line, &dst_last_line) < 2) - ) { - /* No more hunks for this file */ - break; + if (!TT.oldlen && !TT.newlen) state = apply_one_hunk(); + continue; } - if (plus != '+') { - /* reverse patch */ - unsigned tmp = src_last_line; - src_last_line = dst_last_line; - dst_last_line = tmp; - tmp = src_beg_line; - src_beg_line = dst_beg_line; - dst_beg_line = tmp; + fail_hunk(); + state = 0; + continue; + } + + // Open a new file? + if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) { + char *s, **name = reverse ? &newname : &oldname; + int i; + + if (*patchline == '+') { + name = reverse ? &oldname : &newname; + state = 1; } - hunk_count++; - if (src_beg_line && dst_beg_line) { - /* Copy unmodified lines upto start of hunk */ - /* src_beg_line will be 0 if it's a new file */ - count = src_beg_line - src_cur_line; - if (copy_lines(src_stream, dst_stream, count)) { - bb_error_msg_and_die("bad src file"); + finish_oldfile(); + + if (!argv[0]) { + free(*name); + // Trim date from end of filename (if any). We don't care. + for (s = patchline+4; *s && *s!='\t'; s++) + if (*s=='\\' && s[1]) s++; + i = atoi(s); + if (i>1900 && i<=1970) + *name = xstrdup("/dev/null"); + else { + *s = 0; + *name = xstrdup(patchline+4); } - src_cur_line += count; - dst_cur_line += count; - copy_trailing_lines_flag = 1; } - src_last_line += hunk_offset_start = src_cur_line; - dst_last_line += dst_cur_line; - while (1) { - free(patch_line); - patch_line = xmalloc_fgets(patch_file); - if (patch_line == NULL) - break; /* EOF */ - if (!*patch_line) { - /* whitespace-damaged patch with "" lines */ - free(patch_line); - patch_line = xstrdup(" "); + // We defer actually opening the file because svn produces broken + // patches that don't signal they want to create a new file the + // way the patch man page says, so you have to read the first hunk + // and _guess_. + + // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@ + // but a missing ,value means the value is 1. + } else if (state == 1 && !strncmp("@@ -", patchline, 4)) { + int i; + char *s = patchline+4; + + // Read oldline[,oldlen] +newline[,newlen] + + TT.oldlen = TT.newlen = 1; + TT.oldline = strtol(s, &s, 10); + if (*s == ',') TT.oldlen=strtol(s+1, &s, 10); + TT.newline = strtol(s+2, &s, 10); + if (*s == ',') TT.newlen = strtol(s+1, &s, 10); + + TT.context = 0; + state = 2; + + // If this is the first hunk, open the file. + if (TT.filein == -1) { + int oldsum, newsum, del = 0; + char *name; + + oldsum = TT.oldline + TT.oldlen; + newsum = TT.newline + TT.newlen; + + name = reverse ? oldname : newname; + + // We're deleting oldname if new file is /dev/null (before -p) + // or if new hunk is empty (zero context) after patching + if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) + { + name = reverse ? newname : oldname; + del++; } - if ((*patch_line != '-') && (*patch_line != '+') - && (*patch_line != ' ') - ) { - break; /* End of hunk */ + + // handle -p path truncation. + for (i=0, s = name; *s;) { + if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i) break; + if (*(s++)=='/') { + name = s; + i++; + } } - if (*patch_line != plus) { /* '-' or ' ' */ - char *src_line = NULL; - if (src_cur_line == src_last_line) - break; - if (src_stream) { - src_line = xmalloc_fgets(src_stream); - if (src_line) { - int diff = strcmp(src_line, patch_line + 1); - src_cur_line++; - free(src_line); - if (diff) - src_line = NULL; + + if (del) { + printf("removing %s\n", name); + xunlink(name); + state = 0; + // If we've got a file to open, do so. + } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) { + // If the old file was null, we're creating a new one. + if (!strcmp(oldname, "/dev/null") || !oldsum) { + printf("creating %s\n", name); + s = strrchr(name, '/'); + if (s) { + *s = 0; + xmkpath(name, -1); + *s = '/'; } + TT.filein = xopen3(name, O_CREAT|O_EXCL|O_RDWR, 0666); + } else { + printf("patching file %s\n", name); + TT.filein = xopen(name, O_RDWR); } - /* Do not patch an already patched hunk with -N */ - if (src_line == 0 && (opt & OPT_N)) { - continue; - } - if (!src_line) { - bb_error_msg("hunk #%u FAILED at %u", hunk_count, hunk_offset_start); - bad_hunk_count++; - break; - } - if (*patch_line != ' ') { /* '-' */ - continue; - } + TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname); + TT.linenum = 0; + TT.hunknum = 0; } - if (dst_cur_line == dst_last_line) - break; - fputs(patch_line + 1, dst_stream); - dst_cur_line++; - } /* end of while loop handling one hunk */ - } /* end of while loop handling one file */ - - /* Cleanup last patched file */ - if (copy_trailing_lines_flag) { - copy_lines(src_stream, dst_stream, (unsigned)(-1)); - } - if (src_stream) { - fclose(src_stream); - } - fclose(dst_stream); - if (bad_hunk_count) { - ret = 1; - bb_error_msg("%u out of %u hunk FAILED", bad_hunk_count, hunk_count); - } else { - /* It worked, we can remove the backup */ - if (backup_filename) { - unlink(backup_filename); - } - if (!(opt & OPT_dry_run) - && ((dst_cur_line == 0) || (dst_beg_line == 0)) - ) { - /* The new patched file is empty, remove it */ - xunlink(new_filename); - // /* old_filename and new_filename may be the same file */ - // unlink(old_filename); } + + TT.hunknum++; + + continue; } - free(backup_filename); - //free(old_filename); - free(new_filename); - } /* end of "while there are patch lines" */ - quit: - /* 0 = SUCCESS - * 1 = Some hunks failed - * 2 = More serious problems (exited earlier) - */ - return ret; + + // If we didn't continue above, discard this line. + free(patchline); + } + + finish_oldfile(); + + if (ENABLE_FEATURE_CLEAN_UP) { + close(TT.filepatch); + free(oldname); + free(newname); + } + + return TT.exitval; }