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