Annotation of src/usr.bin/patch/patch.c, Revision 1.23
1.23 ! otto 1: /* $OpenBSD: patch.c,v 1.22 2003/07/21 21:01:45 otto Exp $ */
1.2 niklas 2:
1.20 deraadt 3: /*
4: * patch - a program to apply diffs to original files
5: *
1.1 deraadt 6: * Copyright 1986, Larry Wall
1.20 deraadt 7: *
1.14 niklas 8: * Redistribution and use in source and binary forms, with or without
1.20 deraadt 9: * modification, are permitted provided that the following condition is met:
10: * 1. Redistributions of source code must retain the above copyright notice,
11: * this condition and the following disclaimer.
12: *
13: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
14: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
17: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1.14 niklas 21: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23: * SUCH DAMAGE.
1.20 deraadt 24: *
25: * -C option added in 1998, original code by Marc Espie, based on FreeBSD
26: * behaviour
1.1 deraadt 27: */
28:
29: #ifndef lint
1.23 ! otto 30: static const char rcsid[] = "$OpenBSD: patch.c,v 1.22 2003/07/21 21:01:45 otto Exp $";
1.1 deraadt 31: #endif /* not lint */
32:
1.23 ! otto 33: #include <sys/types.h>
! 34: #include <sys/stat.h>
! 35: #include <unistd.h>
! 36:
! 37: #include <assert.h>
! 38: #include <ctype.h>
! 39: #include <string.h>
! 40: #include <stdlib.h>
! 41:
1.1 deraadt 42: #include "INTERN.h"
43: #include "common.h"
44: #include "EXTERN.h"
45: #include "util.h"
46: #include "pch.h"
47: #include "inp.h"
48: #include "backupfile.h"
49:
1.23 ! otto 50:
! 51: static void reinitialize_almost_everything(void);
! 52: static void get_some_switches(void);
! 53: static LINENUM locate_hunk(LINENUM);
! 54: static void abort_hunk(void);
! 55: static void apply_hunk(LINENUM);
! 56: static void init_output(char *);
! 57: static void init_reject(char *);
! 58: static void copy_till(LINENUM);
! 59: static void spew_output(void);
! 60: static void dump_line(LINENUM);
! 61: static bool patch_match(LINENUM, LINENUM, LINENUM);
! 62: static bool similar(char *, char *, int);
! 63: static int optcmp(const void *, const void *);
! 64: static char decode_long_option(char *);
1.1 deraadt 65:
66: /* TRUE if -E was specified on command line. */
1.20 deraadt 67: static int remove_empty_files = FALSE;
1.1 deraadt 68:
69: /* TRUE if -R was specified on command line. */
1.20 deraadt 70: static int reverse_flag_specified = FALSE;
1.1 deraadt 71:
1.23 ! otto 72: /* buffer for stderr */
! 73: static char serrbuf[BUFSIZ];
1.11 espie 74:
1.1 deraadt 75: /* Apply a set of diffs as appropriate. */
76:
77: int
1.19 deraadt 78: main(int argc, char *argv[])
1.1 deraadt 79: {
1.20 deraadt 80: int hunk = 0, failed = 0, failtotal = 0, patch_seen = 0, i;
81: LINENUM where, newwhere, fuzz, mymaxfuzz;
82: char *tmpdir, *v;
83:
84: setbuf(stderr, serrbuf);
85: for (i = 0; i < MAXFILEC; i++)
1.23 ! otto 86: filearg[i] = NULL;
1.20 deraadt 87:
88: myuid = getuid();
89:
90: /* Cons up the names of the temporary files. */
91: tmpdir = getenv("TMPDIR");
92: if (tmpdir == NULL) {
93: tmpdir = "/tmp";
94: }
95: if (asprintf(&TMPOUTNAME, "%s/patchoXXXXXXXXXX", tmpdir) == -1)
96: fatal("cannot allocate memory");
97: if ((i = mkstemp(TMPOUTNAME)) < 0)
98: pfatal("can't create %s", TMPOUTNAME);
99: close(i);
100:
101: if (asprintf(&TMPINNAME, "%s/patchiXXXXXXXXXX", tmpdir) == -1)
102: fatal("cannot allocate memory");
103: if ((i = mkstemp(TMPINNAME)) < 0)
104: pfatal("can't create %s", TMPINNAME);
105: close(i);
106:
107: if (asprintf(&TMPREJNAME, "%s/patchrXXXXXXXXXX", tmpdir) == -1)
108: fatal("cannot allocate memory");
109: if ((i = mkstemp(TMPREJNAME)) < 0)
110: pfatal("can't create %s", TMPREJNAME);
111: close(i);
112:
113: if (asprintf(&TMPPATNAME, "%s/patchpXXXXXXXXXX", tmpdir) == -1)
114: fatal("cannot allocate memory");
115: if ((i = mkstemp(TMPPATNAME)) < 0)
116: pfatal("can't create %s", TMPPATNAME);
117: close(i);
118:
119: v = getenv("SIMPLE_BACKUP_SUFFIX");
120: if (v)
121: simple_backup_suffix = v;
122: else
123: simple_backup_suffix = ORIGEXT;
124:
125: v = getenv("VERSION_CONTROL");
126: backup_type = get_version(v); /* OK to pass NULL. */
127:
128: /* parse switches */
129: Argc = argc;
130: Argv = argv;
131: get_some_switches();
132:
133: /* make sure we clean up /tmp in case of disaster */
134: set_signals(0);
135:
136: for (open_patch_file(filearg[1]); there_is_another_patch();
137: reinitialize_almost_everything()) {
138: /* for each patch in patch file */
139:
140: patch_seen = TRUE;
141:
1.23 ! otto 142: if (outname == NULL)
1.20 deraadt 143: outname = savestr(filearg[0]);
1.1 deraadt 144:
1.20 deraadt 145: /* for ed script just up and do it and exit */
146: if (diff_type == ED_DIFF) {
147: do_ed_script();
148: continue;
149: }
150: /* initialize the patched file */
151: if (!skip_rest_of_patch)
152: init_output(TMPOUTNAME);
153:
154: /* initialize reject file */
155: init_reject(TMPREJNAME);
156:
157: /* find out where all the lines are */
158: if (!skip_rest_of_patch)
159: scan_input(filearg[0]);
160:
161: /* from here on, open no standard i/o files, because malloc */
162: /* might misfire and we can't catch it easily */
163:
164: /* apply each hunk of patch */
165: hunk = 0;
166: failed = 0;
167: out_of_mem = FALSE;
168: while (another_hunk()) {
169: hunk++;
1.23 ! otto 170: fuzz = NULL;
1.20 deraadt 171: mymaxfuzz = pch_context();
172: if (maxfuzz < mymaxfuzz)
173: mymaxfuzz = maxfuzz;
174: if (!skip_rest_of_patch) {
175: do {
176: where = locate_hunk(fuzz);
1.23 ! otto 177: if (hunk == 1 && where == NULL && !force) {
1.1 deraadt 178: /* dwim for reversed patch? */
1.20 deraadt 179: if (!pch_swap()) {
1.23 ! otto 180: if (fuzz == NULL)
1.20 deraadt 181: say("Not enough memory to try swapped hunk! Assuming unswapped.\n");
182: continue;
183: }
184: reverse = !reverse;
185: /* try again */
186: where = locate_hunk(fuzz);
1.23 ! otto 187: if (where == NULL) {
1.20 deraadt 188: /* didn't find it swapped */
189: if (!pch_swap())
190: /* put it back to normal */
191: fatal("lost hunk on alloc error!\n");
192: reverse = !reverse;
193: } else if (noreverse) {
194: if (!pch_swap())
195: /* put it back to normal */
196: fatal("lost hunk on alloc error!\n");
197: reverse = !reverse;
198: say("Ignoring previously applied (or reversed) patch.\n");
199: skip_rest_of_patch = TRUE;
200: } else if (batch) {
201: if (verbose)
202: say("%seversed (or previously applied) patch detected! %s -R.",
203: reverse ? "R" : "Unr",
204: reverse ? "Assuming" : "Ignoring");
205: } else {
206: ask("%seversed (or previously applied) patch detected! %s -R? [y] ",
207: reverse ? "R" : "Unr",
208: reverse ? "Assume" : "Ignore");
209: if (*buf == 'n') {
210: ask("Apply anyway? [n] ");
211: if (*buf != 'y')
212: skip_rest_of_patch = TRUE;
1.23 ! otto 213: where = NULL;
1.20 deraadt 214: reverse = !reverse;
215: if (!pch_swap())
216: /* put it back to normal */
217: fatal("lost hunk on alloc error!\n");
218: }
219: }
220: }
1.23 ! otto 221: } while (!skip_rest_of_patch && where == NULL &&
1.20 deraadt 222: ++fuzz <= mymaxfuzz);
223:
224: if (skip_rest_of_patch) { /* just got decided */
225: fclose(ofp);
1.23 ! otto 226: ofp = NULL;
1.20 deraadt 227: }
228: }
229: newwhere = pch_newfirst() + last_offset;
230: if (skip_rest_of_patch) {
231: abort_hunk();
232: failed++;
233: if (verbose)
234: say("Hunk #%d ignored at %ld.\n",
235: hunk, newwhere);
1.23 ! otto 236: } else if (where == NULL) {
1.20 deraadt 237: abort_hunk();
238: failed++;
239: if (verbose)
240: say("Hunk #%d failed at %ld.\n",
241: hunk, newwhere);
242: } else {
243: apply_hunk(where);
244: if (verbose) {
245: say("Hunk #%d succeeded at %ld",
246: hunk, newwhere);
247: if (fuzz)
248: say(" with fuzz %ld", fuzz);
249: if (last_offset)
250: say(" (offset %ld line%s)",
251: last_offset,
252: last_offset == 1L ? "" : "s");
253: say(".\n");
254: }
255: }
256: }
257:
258: if (out_of_mem && using_plan_a) {
259: Argc = Argc_last;
260: Argv = Argv_last;
261: say("\n\nRan out of memory using Plan A--trying again...\n\n");
262: if (ofp)
263: fclose(ofp);
1.23 ! otto 264: ofp = NULL;
1.20 deraadt 265: if (rejfp)
266: fclose(rejfp);
1.23 ! otto 267: rejfp = NULL;
1.20 deraadt 268: continue;
1.11 espie 269: }
1.20 deraadt 270: assert(hunk);
1.1 deraadt 271:
1.20 deraadt 272: /* finish spewing out the new file */
273: if (!skip_rest_of_patch)
274: spew_output();
275:
276: /* and put the output where desired */
277: ignore_signals();
278: if (!skip_rest_of_patch) {
279: struct stat statbuf;
280: char *realout = outname;
281:
282: if (!check_only) {
283: if (move_file(TMPOUTNAME, outname) < 0) {
284: toutkeep = TRUE;
285: realout = TMPOUTNAME;
286: chmod(TMPOUTNAME, filemode);
287: } else
288: chmod(outname, filemode);
289:
290: if (remove_empty_files &&
291: stat(realout, &statbuf) == 0 &&
292: statbuf.st_size == 0) {
293: if (verbose)
294: say("Removing %s (empty after patching).\n",
295: realout);
296: unlink(realout);
297: }
298: }
299: }
300: fclose(rejfp);
1.23 ! otto 301: rejfp = NULL;
1.20 deraadt 302: if (failed) {
303: failtotal += failed;
304: if (!*rejname) {
305: if (strlcpy(rejname, outname,
306: sizeof(rejname)) >= sizeof(rejname))
307: fatal("filename %s is too long\n", outname);
308: if (strlcat(rejname, REJEXT,
309: sizeof(rejname)) >= sizeof(rejname))
310: fatal("filename %s is too long\n", outname);
311: }
312: if (skip_rest_of_patch) {
313: say("%d out of %d hunks ignored--saving rejects to %s\n",
314: failed, hunk, rejname);
315: } else {
316: say("%d out of %d hunks failed--saving rejects to %s\n",
317: failed, hunk, rejname);
318: }
319: if (!check_only && move_file(TMPREJNAME, rejname) < 0)
320: trejkeep = TRUE;
321: }
322: set_signals(1);
323: }
324: if (!patch_seen)
325: failtotal++;
326: my_exit(failtotal);
327: /* NOTREACHED */
1.1 deraadt 328: }
329:
330: /* Prepare to find the next patch to do in the patch file. */
331:
1.23 ! otto 332: static void
1.20 deraadt 333: reinitialize_almost_everything(void)
1.1 deraadt 334: {
1.20 deraadt 335: re_patch();
336: re_input();
1.1 deraadt 337:
1.20 deraadt 338: input_lines = 0;
339: last_frozen_line = 0;
1.1 deraadt 340:
1.20 deraadt 341: filec = 0;
1.23 ! otto 342: if (filearg[0] != NULL && !out_of_mem) {
1.20 deraadt 343: free(filearg[0]);
1.23 ! otto 344: filearg[0] = NULL;
1.20 deraadt 345: }
1.23 ! otto 346: if (outname != NULL) {
1.20 deraadt 347: free(outname);
1.23 ! otto 348: outname = NULL;
1.20 deraadt 349: }
350: last_offset = 0;
1.1 deraadt 351:
1.20 deraadt 352: diff_type = 0;
1.1 deraadt 353:
1.23 ! otto 354: if (revision != NULL) {
1.20 deraadt 355: free(revision);
1.23 ! otto 356: revision = NULL;
1.20 deraadt 357: }
358: reverse = reverse_flag_specified;
359: skip_rest_of_patch = FALSE;
1.1 deraadt 360:
1.20 deraadt 361: get_some_switches();
1.1 deraadt 362:
1.20 deraadt 363: if (filec >= 2)
364: fatal("you may not change to a different patch file\n");
1.1 deraadt 365: }
366:
367: static char *
1.19 deraadt 368: nextarg(void)
1.1 deraadt 369: {
1.20 deraadt 370: if (!--Argc)
371: fatal("missing argument after `%s'\n", *Argv);
372: return *++Argv;
1.1 deraadt 373: }
374:
1.2 niklas 375: /* Module for handling of long options. */
376:
377: struct option {
1.20 deraadt 378: char *long_opt;
379: char short_opt;
1.2 niklas 380: };
381:
1.23 ! otto 382: static int
1.19 deraadt 383: optcmp(const void *v1, const void *v2)
1.2 niklas 384: {
1.20 deraadt 385: const struct option *a = v1, *b = v2;
1.19 deraadt 386:
1.20 deraadt 387: return strcmp(a->long_opt, b->long_opt);
1.2 niklas 388: }
389:
390: /* Decode Long options beginning with "--" to their short equivalents. */
391:
1.23 ! otto 392: static char
1.19 deraadt 393: decode_long_option(char *opt)
1.2 niklas 394: {
1.20 deraadt 395: /*
396: * This table must be sorted on the first field. We also decode
397: * unimplemented options as those will be handled later anyway.
398: */
399: static struct option options[] = {
400: {"batch", 't'},
401: {"check", 'C'},
402: {"context", 'c'},
403: {"debug", 'x'},
404: {"directory", 'd'},
405: {"ed", 'e'},
406: {"force", 'f'},
407: {"forward", 'N'},
408: {"fuzz", 'F'},
409: {"ifdef", 'D'},
410: {"ignore-whitespace", 'l'},
411: {"normal", 'n'},
412: {"output", 'o'},
413: {"prefix", 'B'},
414: {"quiet", 's'},
415: {"reject-file", 'r'},
416: {"remove-empty-files", 'E'},
417: {"reverse", 'R'},
418: {"silent", 's'},
419: {"skip", 'S'},
420: {"strip", 'p'},
421: {"suffix", 'b'},
422: {"unified", 'u'},
423: {"version", 'v'},
424: {"version-control", 'V'},
425: };
426: struct option key, *found;
427:
428: key.long_opt = opt;
429: found = (struct option *) bsearch(&key, options,
430: sizeof(options) / sizeof(options[0]),
431: sizeof(options[0]), optcmp);
432: return found ? found->short_opt : '\0';
1.2 niklas 433: }
434:
1.1 deraadt 435: /* Process switches and filenames up to next '+' or end of list. */
436:
1.23 ! otto 437: static void
1.20 deraadt 438: get_some_switches(void)
1.1 deraadt 439: {
1.20 deraadt 440: char *s;
1.1 deraadt 441:
1.20 deraadt 442: rejname[0] = '\0';
443: Argc_last = Argc;
444: Argv_last = Argv;
445: if (!Argc)
446: return;
447: for (Argc--, Argv++; Argc; Argc--, Argv++) {
448: s = Argv[0];
449: if (strEQ(s, "+")) {
450: return; /* + will be skipped by for loop */
451: }
452: if (*s != '-' || !s[1]) {
453: if (filec == MAXFILEC)
454: fatal("too many file arguments\n");
455: filearg[filec++] = savestr(s);
456: } else {
457: char opt;
458:
459: if (*(s + 1) == '-') {
460: opt = decode_long_option(s + 2);
461: s += strlen(s) - 1;
462: } else
463: opt = *++s;
464: switch (opt) {
465: case 'b':
466: simple_backup_suffix = savestr(nextarg());
467: break;
468: case 'B':
469: origprae = savestr(nextarg());
470: break;
471: case 'c':
472: diff_type = CONTEXT_DIFF;
473: break;
474: case 'C':
475: check_only = TRUE;
476: break;
477: case 'd':
478: if (!*++s)
479: s = nextarg();
480: if (chdir(s) < 0)
481: pfatal("can't cd to %s", s);
482: break;
483: case 'D':
484: do_defines = TRUE;
485: if (!*++s)
486: s = nextarg();
487: if (!isalpha(*s) && '_' != *s)
488: fatal("argument to -D is not an identifier\n");
489: snprintf(if_defined, sizeof if_defined,
490: "#ifdef %s\n", s);
491: snprintf(not_defined, sizeof not_defined,
492: "#ifndef %s\n", s);
493: snprintf(end_defined, sizeof end_defined,
494: "#endif /* %s */\n", s);
495: break;
496: case 'e':
497: diff_type = ED_DIFF;
498: break;
499: case 'E':
500: remove_empty_files = TRUE;
501: break;
502: case 'f':
503: force = TRUE;
504: break;
505: case 'F':
506: if (!*++s)
507: s = nextarg();
508: else if (*s == '=')
509: s++;
510: maxfuzz = atoi(s);
511: break;
512: case 'l':
513: canonicalize = TRUE;
514: break;
515: case 'n':
516: diff_type = NORMAL_DIFF;
517: break;
518: case 'N':
519: noreverse = TRUE;
520: break;
521: case 'o':
522: outname = savestr(nextarg());
523: break;
524: case 'p':
525: if (!*++s)
526: s = nextarg();
527: else if (*s == '=')
528: s++;
529: strippath = atoi(s);
530: break;
531: case 'r':
532: if (strlcpy(rejname, nextarg(),
533: sizeof(rejname)) >= sizeof(rejname))
534: fatal("argument for -r is too long\n");
535: break;
536: case 'R':
537: reverse = TRUE;
538: reverse_flag_specified = TRUE;
539: break;
540: case 's':
541: verbose = FALSE;
542: break;
543: case 'S':
544: skip_rest_of_patch = TRUE;
545: break;
546: case 't':
547: batch = TRUE;
548: break;
549: case 'u':
550: diff_type = UNI_DIFF;
551: break;
552: case 'v':
553: version();
554: break;
555: case 'V':
556: backup_type = get_version(nextarg());
557: break;
1.1 deraadt 558: #ifdef DEBUGGING
1.20 deraadt 559: case 'x':
560: if (!*++s)
561: s = nextarg();
562: debug = atoi(s);
563: break;
1.1 deraadt 564: #endif
1.20 deraadt 565: default:
566: fprintf(stderr, "patch: unrecognized option `%s'\n",
567: Argv[0]);
568: fprintf(stderr, "\
1.1 deraadt 569: Usage: patch [options] [origfile [patchfile]] [+ [options] [origfile]]...\n\
570: Options:\n\
1.11 espie 571: [-cCeEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\
1.1 deraadt 572: [-D symbol] [-Fmax-fuzz] [-o out-file] [-p[strip-count]]\n\
573: [-r rej-name] [-V {numbered,existing,simple}]\n");
1.20 deraadt 574: my_exit(1);
575: }
576: }
1.1 deraadt 577: }
578: }
579:
1.20 deraadt 580: /*
581: * Attempt to find the right place to apply this hunk of patch.
582: */
1.23 ! otto 583: static LINENUM
1.20 deraadt 584: locate_hunk(LINENUM fuzz)
1.1 deraadt 585: {
1.20 deraadt 586: LINENUM first_guess = pch_first() + last_offset;
587: LINENUM offset;
588: LINENUM pat_lines = pch_ptrn_lines();
589: LINENUM max_pos_offset = input_lines - first_guess - pat_lines + 1;
590: LINENUM max_neg_offset = first_guess - last_frozen_line - 1 + pch_context();
591:
592: if (!pat_lines) /* null range matches always */
593: return first_guess;
594: if (max_neg_offset >= first_guess) /* do not try lines < 0 */
595: max_neg_offset = first_guess - 1;
1.23 ! otto 596: if (first_guess <= input_lines && patch_match(first_guess, NULL, fuzz))
1.20 deraadt 597: return first_guess;
598: for (offset = 1; ; offset++) {
599: bool check_after = (offset <= max_pos_offset);
600: bool check_before = (offset <= max_neg_offset);
1.1 deraadt 601:
1.20 deraadt 602: if (check_after && patch_match(first_guess, offset, fuzz)) {
1.1 deraadt 603: #ifdef DEBUGGING
1.20 deraadt 604: if (debug & 1)
605: say("Offset changing from %ld to %ld\n",
606: last_offset, offset);
1.1 deraadt 607: #endif
1.20 deraadt 608: last_offset = offset;
609: return first_guess + offset;
610: } else if (check_before && patch_match(first_guess, -offset, fuzz)) {
1.1 deraadt 611: #ifdef DEBUGGING
1.20 deraadt 612: if (debug & 1)
613: say("Offset changing from %ld to %ld\n",
614: last_offset, -offset);
1.1 deraadt 615: #endif
1.20 deraadt 616: last_offset = -offset;
617: return first_guess - offset;
618: } else if (!check_before && !check_after)
1.23 ! otto 619: return NULL;
1.1 deraadt 620: }
621: }
622:
623: /* We did not find the pattern, dump out the hunk so they can handle it. */
624:
1.23 ! otto 625: static void
1.20 deraadt 626: abort_hunk(void)
1.1 deraadt 627: {
1.20 deraadt 628: LINENUM i;
629: LINENUM pat_end = pch_end();
630: /*
631: * add in last_offset to guess the same as the previous successful
632: * hunk
633: */
634: LINENUM oldfirst = pch_first() + last_offset;
635: LINENUM newfirst = pch_newfirst() + last_offset;
636: LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
637: LINENUM newlast = newfirst + pch_repl_lines() - 1;
638: char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
639: char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
640:
641: fprintf(rejfp, "***************\n");
642: for (i = 0; i <= pat_end; i++) {
643: switch (pch_char(i)) {
644: case '*':
645: if (oldlast < oldfirst)
646: fprintf(rejfp, "*** 0%s\n", stars);
647: else if (oldlast == oldfirst)
648: fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
649: else
650: fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst,
651: oldlast, stars);
652: break;
653: case '=':
654: if (newlast < newfirst)
655: fprintf(rejfp, "--- 0%s\n", minuses);
656: else if (newlast == newfirst)
657: fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
658: else
659: fprintf(rejfp, "--- %ld,%ld%s\n", newfirst,
660: newlast, minuses);
661: break;
662: case '\n':
663: fprintf(rejfp, "%s", pfetch(i));
664: break;
665: case ' ':
666: case '-':
667: case '+':
668: case '!':
669: fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
670: break;
671: default:
672: fatal("fatal internal error in abort_hunk\n");
673: }
1.1 deraadt 674: }
675: }
676:
677: /* We found where to apply it (we hope), so do it. */
678:
1.23 ! otto 679: static void
1.20 deraadt 680: apply_hunk(LINENUM where)
1.1 deraadt 681: {
1.20 deraadt 682: LINENUM old = 1;
683: LINENUM lastline = pch_ptrn_lines();
684: LINENUM new = lastline + 1;
1.1 deraadt 685: #define OUTSIDE 0
686: #define IN_IFNDEF 1
687: #define IN_IFDEF 2
688: #define IN_ELSE 3
1.20 deraadt 689: int def_state = OUTSIDE;
690: bool R_do_defines = do_defines;
691: LINENUM pat_end = pch_end();
692:
693: where--;
694: while (pch_char(new) == '=' || pch_char(new) == '\n')
695: new++;
696:
697: while (old <= lastline) {
698: if (pch_char(old) == '-') {
699: copy_till(where + old - 1);
700: if (R_do_defines) {
701: if (def_state == OUTSIDE) {
702: fputs(not_defined, ofp);
703: def_state = IN_IFNDEF;
704: } else if (def_state == IN_IFDEF) {
705: fputs(else_defined, ofp);
706: def_state = IN_ELSE;
707: }
708: fputs(pfetch(old), ofp);
709: }
710: last_frozen_line++;
711: old++;
712: } else if (new > pat_end) {
713: break;
714: } else if (pch_char(new) == '+') {
715: copy_till(where + old - 1);
716: if (R_do_defines) {
717: if (def_state == IN_IFNDEF) {
718: fputs(else_defined, ofp);
719: def_state = IN_ELSE;
720: } else if (def_state == OUTSIDE) {
721: fputs(if_defined, ofp);
722: def_state = IN_IFDEF;
723: }
724: }
725: fputs(pfetch(new), ofp);
726: new++;
727: } else if (pch_char(new) != pch_char(old)) {
728: say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
729: pch_hunk_beg() + old,
730: pch_hunk_beg() + new);
1.1 deraadt 731: #ifdef DEBUGGING
1.20 deraadt 732: say("oldchar = '%c', newchar = '%c'\n",
733: pch_char(old), pch_char(new));
1.1 deraadt 734: #endif
1.20 deraadt 735: my_exit(1);
736: } else if (pch_char(new) == '!') {
737: copy_till(where + old - 1);
738: if (R_do_defines) {
739: fputs(not_defined, ofp);
740: def_state = IN_IFNDEF;
741: }
742: while (pch_char(old) == '!') {
743: if (R_do_defines) {
744: fputs(pfetch(old), ofp);
745: }
746: last_frozen_line++;
747: old++;
748: }
749: if (R_do_defines) {
750: fputs(else_defined, ofp);
751: def_state = IN_ELSE;
752: }
753: while (pch_char(new) == '!') {
754: fputs(pfetch(new), ofp);
755: new++;
756: }
757: } else {
758: assert(pch_char(new) == ' ');
759: old++;
760: new++;
761: if (R_do_defines && def_state != OUTSIDE) {
762: fputs(end_defined, ofp);
763: def_state = OUTSIDE;
764: }
765: }
1.1 deraadt 766: }
1.20 deraadt 767: if (new <= pat_end && pch_char(new) == '+') {
768: copy_till(where + old - 1);
1.1 deraadt 769: if (R_do_defines) {
1.20 deraadt 770: if (def_state == OUTSIDE) {
771: fputs(if_defined, ofp);
772: def_state = IN_IFDEF;
773: } else if (def_state == IN_IFNDEF) {
774: fputs(else_defined, ofp);
775: def_state = IN_ELSE;
776: }
777: }
778: while (new <= pat_end && pch_char(new) == '+') {
779: fputs(pfetch(new), ofp);
780: new++;
1.1 deraadt 781: }
782: }
1.20 deraadt 783: if (R_do_defines && def_state != OUTSIDE) {
1.1 deraadt 784: fputs(end_defined, ofp);
785: }
786: }
787:
1.20 deraadt 788: /*
789: * Open the new file.
790: */
1.23 ! otto 791: static void
1.20 deraadt 792: init_output(char *name)
1.1 deraadt 793: {
1.20 deraadt 794: ofp = fopen(name, "w");
1.23 ! otto 795: if (ofp == NULL)
1.20 deraadt 796: pfatal("can't create %s", name);
1.1 deraadt 797: }
798:
1.20 deraadt 799: /*
800: * Open a file to put hunks we can't locate.
801: */
1.23 ! otto 802: static void
1.20 deraadt 803: init_reject(char *name)
1.1 deraadt 804: {
1.20 deraadt 805: rejfp = fopen(name, "w");
1.23 ! otto 806: if (rejfp == NULL)
1.20 deraadt 807: pfatal("can't create %s", name);
1.1 deraadt 808: }
809:
1.20 deraadt 810: /*
811: * Copy input file to output, up to wherever hunk is to be applied.
812: */
1.23 ! otto 813: static void
1.20 deraadt 814: copy_till(LINENUM lastline)
1.1 deraadt 815: {
1.20 deraadt 816: LINENUM R_last_frozen_line = last_frozen_line;
1.1 deraadt 817:
1.20 deraadt 818: if (R_last_frozen_line > lastline)
819: fatal("misordered hunks! output would be garbled\n");
820: while (R_last_frozen_line < lastline)
821: dump_line(++R_last_frozen_line);
822: last_frozen_line = R_last_frozen_line;
1.1 deraadt 823: }
824:
1.20 deraadt 825: /*
826: * Finish copying the input file to the output file.
827: */
1.23 ! otto 828: static void
1.20 deraadt 829: spew_output(void)
1.1 deraadt 830: {
831: #ifdef DEBUGGING
1.20 deraadt 832: if (debug & 256)
833: say("il=%ld lfl=%ld\n", input_lines, last_frozen_line);
1.1 deraadt 834: #endif
1.20 deraadt 835: if (input_lines)
836: copy_till(input_lines); /* dump remainder of file */
837: fclose(ofp);
1.23 ! otto 838: ofp = NULL;
1.1 deraadt 839: }
840:
1.20 deraadt 841: /*
842: * Copy one line from input to output.
843: */
1.23 ! otto 844: static void
1.20 deraadt 845: dump_line(LINENUM line)
1.1 deraadt 846: {
1.20 deraadt 847: char *s, R_newline = '\n';
1.1 deraadt 848:
1.20 deraadt 849: /* Note: string is not null terminated. */
1.22 otto 850: for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++)
1.20 deraadt 851: ;
1.1 deraadt 852: }
853:
1.20 deraadt 854: /*
855: * Does the patch pattern match at line base+offset?
856: */
1.23 ! otto 857: static bool
1.20 deraadt 858: patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
859: {
860: LINENUM pline = 1 + fuzz;
861: LINENUM iline;
862: LINENUM pat_lines = pch_ptrn_lines() - fuzz;
863:
864: for (iline = base + offset + fuzz; pline <= pat_lines; pline++, iline++) {
865: if (canonicalize) {
866: if (!similar(ifetch(iline, (offset >= 0)),
867: pfetch(pline), pch_line_len(pline)))
868: return FALSE;
869: } else if (strnNE(ifetch(iline, (offset >= 0)),
870: pfetch(pline), pch_line_len(pline)))
871: return FALSE;
1.1 deraadt 872: }
1.20 deraadt 873: return TRUE;
1.1 deraadt 874: }
875:
1.20 deraadt 876: /*
877: * Do two lines match with canonicalized white space?
878: */
1.23 ! otto 879: static bool
1.20 deraadt 880: similar(char *a, char *b, int len)
881: {
882: while (len) {
883: if (isspace(*b)) { /* whitespace (or \n) to match? */
884: if (!isspace(*a)) /* no corresponding whitespace? */
885: return FALSE;
886: while (len && isspace(*b) && *b != '\n')
887: b++, len--; /* skip pattern whitespace */
888: while (isspace(*a) && *a != '\n')
889: a++; /* skip target whitespace */
890: if (*a == '\n' || *b == '\n')
891: return (*a == *b); /* should end in sync */
892: } else if (*a++ != *b++) /* match non-whitespace chars */
893: return FALSE;
894: else
895: len--; /* probably not necessary */
1.1 deraadt 896: }
1.20 deraadt 897: return TRUE; /* actually, this is not reached */
898: /* since there is always a \n */
1.1 deraadt 899: }