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