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