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