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