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