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