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