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