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