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