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