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