Annotation of src/usr.bin/patch/patch.c, Revision 1.76
1.76 ! jcs 1: /* $OpenBSD: patch.c,v 1.75 2023/10/25 20:05:43 bluhm Exp $ */
1.2 niklas 2:
1.20 deraadt 3: /*
4: * patch - a program to apply diffs to original files
5: *
1.1 deraadt 6: * Copyright 1986, Larry Wall
1.20 deraadt 7: *
1.14 niklas 8: * Redistribution and use in source and binary forms, with or without
1.20 deraadt 9: * modification, are permitted provided that the following condition is met:
10: * 1. Redistributions of source code must retain the above copyright notice,
11: * this condition and the following disclaimer.
12: *
13: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
14: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
17: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1.14 niklas 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.20 deraadt 24: *
25: * -C option added in 1998, original code by Marc Espie, based on FreeBSD
26: * behaviour
1.1 deraadt 27: */
28:
1.23 otto 29: #include <sys/types.h>
30: #include <sys/stat.h>
31: #include <unistd.h>
32:
33: #include <ctype.h>
1.24 millert 34: #include <getopt.h>
1.75 bluhm 35: #include <libgen.h>
1.25 millert 36: #include <limits.h>
1.65 anton 37: #include <paths.h>
1.29 otto 38: #include <stdio.h>
1.23 otto 39: #include <string.h>
40: #include <stdlib.h>
41:
1.1 deraadt 42: #include "common.h"
43: #include "util.h"
44: #include "pch.h"
45: #include "inp.h"
46: #include "backupfile.h"
1.60 tobias 47: #include "ed.h"
1.1 deraadt 48:
1.44 otto 49: mode_t filemode = 0644;
1.29 otto 50:
1.69 jca 51: char *buf; /* general purpose buffer */
52: size_t bufsz; /* general purpose buffer size */
1.29 otto 53:
1.35 otto 54: bool using_plan_a = true; /* try to keep everything in memory */
55: bool out_of_mem = false; /* ran out of memory in plan a */
1.29 otto 56:
57: #define MAXFILEC 2
58:
59: char *filearg[MAXFILEC];
1.35 otto 60: bool ok_to_create_file = false;
1.29 otto 61: char *outname = NULL;
62: char *origprae = NULL;
63: char *TMPOUTNAME;
64: char *TMPINNAME;
65: char *TMPREJNAME;
66: char *TMPPATNAME;
1.35 otto 67: bool toutkeep = false;
68: bool trejkeep = false;
1.33 otto 69: bool warn_on_invalid_line;
1.37 otto 70: bool last_line_missing_eol;
1.29 otto 71:
72: #ifdef DEBUGGING
1.30 deraadt 73: int debug = 0;
1.29 otto 74: #endif
75:
1.35 otto 76: bool force = false;
77: bool batch = false;
78: bool verbose = true;
79: bool reverse = false;
80: bool noreverse = false;
81: bool skip_rest_of_patch = false;
1.29 otto 82: int strippath = 957;
1.35 otto 83: bool canonicalize = false;
84: bool check_only = false;
1.29 otto 85: int diff_type = 0;
86: char *revision = NULL; /* prerequisite revision, if any */
87: LINENUM input_lines = 0; /* how long is input file in lines */
1.38 millert 88: int posix = 0; /* strict POSIX mode? */
1.23 otto 89:
90: static void reinitialize_almost_everything(void);
91: static void get_some_switches(void);
92: static LINENUM locate_hunk(LINENUM);
1.43 otto 93: static void abort_context_hunk(void);
94: static void rej_line(int, LINENUM);
1.23 otto 95: static void abort_hunk(void);
96: static void apply_hunk(LINENUM);
1.29 otto 97: static void init_output(const char *);
98: static void init_reject(const char *);
1.37 otto 99: static void copy_till(LINENUM, bool);
1.23 otto 100: static void spew_output(void);
1.37 otto 101: static void dump_line(LINENUM, bool);
1.23 otto 102: static bool patch_match(LINENUM, LINENUM, LINENUM);
1.72 tb 103: static bool similar(const char *, const char *, ssize_t);
1.24 millert 104: static __dead void usage(void);
1.1 deraadt 105:
1.35 otto 106: /* true if -E was specified on command line. */
107: static bool remove_empty_files = false;
1.1 deraadt 108:
1.35 otto 109: /* true if -R was specified on command line. */
110: static bool reverse_flag_specified = false;
1.1 deraadt 111:
1.76 ! jcs 112: static bool Vflag = false;
! 113:
1.25 millert 114: /* buffer holding the name of the rejected patch file. */
1.64 deraadt 115: static char rejname[PATH_MAX];
1.25 millert 116:
1.29 otto 117: /* how many input lines have been irretractibly output */
118: static LINENUM last_frozen_line = 0;
119:
120: static int Argc; /* guess */
121: static char **Argv;
122: static int Argc_last; /* for restarting plan_b */
123: static char **Argv_last;
124:
125: static FILE *ofp = NULL; /* output file pointer */
126: static FILE *rejfp = NULL; /* reject file pointer */
127:
128: static int filec = 0; /* how many file arguments? */
129: static LINENUM last_offset = 0;
130: static LINENUM maxfuzz = 2;
131:
132: /* patch using ifdef, ifndef, etc. */
1.35 otto 133: static bool do_defines = false;
1.29 otto 134: /* #ifdef xyzzy */
135: static char if_defined[128];
136: /* #ifndef xyzzy */
137: static char not_defined[128];
138: /* #else */
139: static const char else_defined[] = "#else\n";
140: /* #endif xyzzy */
141: static char end_defined[128];
142:
1.11 espie 143:
1.1 deraadt 144: /* Apply a set of diffs as appropriate. */
145:
146: int
1.19 deraadt 147: main(int argc, char *argv[])
1.1 deraadt 148: {
1.42 deraadt 149: int error = 0, hunk, failed, i, fd;
1.47 stsp 150: bool patch_seen;
1.35 otto 151: LINENUM where = 0, newwhere, fuzz, mymaxfuzz;
1.31 millert 152: const char *tmpdir;
153: char *v;
1.55 deraadt 154:
1.73 florian 155: if (pledge("stdio rpath wpath cpath tmppath fattr unveil", NULL) == -1) {
1.59 deraadt 156: perror("pledge");
1.62 gsoares 157: my_exit(2);
1.61 deraadt 158: }
1.69 jca 159:
160: bufsz = INITLINELEN;
161: if ((buf = malloc(bufsz)) == NULL)
162: pfatal("allocating input buffer");
163: buf[0] = '\0';
1.20 deraadt 164:
1.52 millert 165: setvbuf(stdout, NULL, _IOLBF, 0);
166: setvbuf(stderr, NULL, _IOLBF, 0);
1.20 deraadt 167: for (i = 0; i < MAXFILEC; i++)
1.23 otto 168: filearg[i] = NULL;
1.20 deraadt 169:
170: /* Cons up the names of the temporary files. */
1.31 millert 171: if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
172: tmpdir = _PATH_TMP;
173: for (i = strlen(tmpdir) - 1; i > 0 && tmpdir[i] == '/'; i--)
174: ;
175: i++;
176: if (asprintf(&TMPOUTNAME, "%.*s/patchoXXXXXXXXXX", i, tmpdir) == -1)
1.20 deraadt 177: fatal("cannot allocate memory");
1.67 deraadt 178: if ((fd = mkstemp(TMPOUTNAME)) == -1)
1.20 deraadt 179: pfatal("can't create %s", TMPOUTNAME);
1.31 millert 180: close(fd);
1.20 deraadt 181:
1.31 millert 182: if (asprintf(&TMPINNAME, "%.*s/patchiXXXXXXXXXX", i, tmpdir) == -1)
1.20 deraadt 183: fatal("cannot allocate memory");
1.67 deraadt 184: if ((fd = mkstemp(TMPINNAME)) == -1)
1.20 deraadt 185: pfatal("can't create %s", TMPINNAME);
1.31 millert 186: close(fd);
1.20 deraadt 187:
1.31 millert 188: if (asprintf(&TMPREJNAME, "%.*s/patchrXXXXXXXXXX", i, tmpdir) == -1)
1.20 deraadt 189: fatal("cannot allocate memory");
1.67 deraadt 190: if ((fd = mkstemp(TMPREJNAME)) == -1)
1.20 deraadt 191: pfatal("can't create %s", TMPREJNAME);
1.31 millert 192: close(fd);
1.20 deraadt 193:
1.31 millert 194: if (asprintf(&TMPPATNAME, "%.*s/patchpXXXXXXXXXX", i, tmpdir) == -1)
1.20 deraadt 195: fatal("cannot allocate memory");
1.67 deraadt 196: if ((fd = mkstemp(TMPPATNAME)) == -1)
1.20 deraadt 197: pfatal("can't create %s", TMPPATNAME);
1.31 millert 198: close(fd);
1.20 deraadt 199:
200: v = getenv("SIMPLE_BACKUP_SUFFIX");
201: if (v)
202: simple_backup_suffix = v;
203: else
204: simple_backup_suffix = ORIGEXT;
205:
206: /* parse switches */
207: Argc = argc;
208: Argv = argv;
209: get_some_switches();
1.73 florian 210: if (unveil(tmpdir, "rwc") == -1) {
211: perror("unveil");
212: my_exit(2);
213: }
214: if (outname != NULL)
215: if (unveil(outname, "rwc") == -1) {
216: perror("unveil");
217: my_exit(2);
218: }
1.75 bluhm 219: if (filearg[0] != NULL) {
220: char *origdir;
221:
1.73 florian 222: if (unveil(filearg[0], "rwc") == -1) {
223: perror("unveil");
224: my_exit(2);
225: }
1.75 bluhm 226: if ((origdir = dirname(filearg[0])) == NULL) {
227: perror("dirname");
228: my_exit(2);
229: }
230: if (unveil(origdir, "rwc") == -1) {
231: perror("unveil");
232: my_exit(2);
233: }
234: } else {
235: if (unveil(".", "rwc") == -1) {
236: perror("unveil");
237: my_exit(2);
238: }
239: }
1.73 florian 240: if (filearg[1] != NULL)
241: if (unveil(filearg[1], "r") == -1) {
1.74 tb 242: perror("unveil");
243: my_exit(2);
244: }
245: if (!force && !batch)
246: if (unveil(_PATH_TTY, "r") == -1) {
1.73 florian 247: perror("unveil");
248: my_exit(2);
249: }
250: if (*rejname != '\0')
251: if (unveil(rejname, "rwc") == -1) {
252: perror("unveil");
253: my_exit(2);
254: }
255: if (pledge("stdio rpath wpath cpath tmppath fattr", NULL) == -1) {
256: perror("pledge");
257: my_exit(2);
258: }
1.20 deraadt 259:
1.76 ! jcs 260: if (!Vflag) {
1.38 millert 261: if ((v = getenv("PATCH_VERSION_CONTROL")) == NULL)
262: v = getenv("VERSION_CONTROL");
263: if (v != NULL || !posix)
1.27 millert 264: backup_type = get_version(v); /* OK to pass NULL. */
265: }
266:
1.20 deraadt 267: /* make sure we clean up /tmp in case of disaster */
268: set_signals(0);
269:
1.47 stsp 270: patch_seen = false;
1.20 deraadt 271: for (open_patch_file(filearg[1]); there_is_another_patch();
272: reinitialize_almost_everything()) {
273: /* for each patch in patch file */
1.53 deraadt 274:
1.47 stsp 275: patch_seen = true;
1.20 deraadt 276:
1.35 otto 277: warn_on_invalid_line = true;
1.20 deraadt 278:
1.23 otto 279: if (outname == NULL)
1.54 tobias 280: outname = xstrdup(filearg[0]);
1.1 deraadt 281:
1.20 deraadt 282: /* initialize the patched file */
283: if (!skip_rest_of_patch)
284: init_output(TMPOUTNAME);
285:
286: /* initialize reject file */
287: init_reject(TMPREJNAME);
288:
289: /* find out where all the lines are */
290: if (!skip_rest_of_patch)
291: scan_input(filearg[0]);
1.60 tobias 292:
293: /* for ed script just up and do it and exit */
294: if (diff_type == ED_DIFF) {
295: do_ed_script();
296: continue;
297: }
1.20 deraadt 298:
299: /* from here on, open no standard i/o files, because malloc */
300: /* might misfire and we can't catch it easily */
301:
302: /* apply each hunk of patch */
303: hunk = 0;
304: failed = 0;
1.35 otto 305: out_of_mem = false;
1.20 deraadt 306: while (another_hunk()) {
307: hunk++;
1.29 otto 308: fuzz = 0;
1.20 deraadt 309: mymaxfuzz = pch_context();
310: if (maxfuzz < mymaxfuzz)
311: mymaxfuzz = maxfuzz;
312: if (!skip_rest_of_patch) {
313: do {
314: where = locate_hunk(fuzz);
1.70 op 315: if ((hunk == 1 && where == 0 && !force) ||
316: (where == 1 && pch_ptrn_lines() == 0 && !force)) {
1.1 deraadt 317: /* dwim for reversed patch? */
1.20 deraadt 318: if (!pch_swap()) {
1.29 otto 319: if (fuzz == 0)
1.20 deraadt 320: say("Not enough memory to try swapped hunk! Assuming unswapped.\n");
321: continue;
322: }
323: reverse = !reverse;
324: /* try again */
325: where = locate_hunk(fuzz);
1.29 otto 326: if (where == 0) {
1.20 deraadt 327: /* didn't find it swapped */
328: if (!pch_swap())
329: /* put it back to normal */
330: fatal("lost hunk on alloc error!\n");
331: reverse = !reverse;
1.70 op 332:
333: /* restore position if this patch creates a file */
334: if (pch_ptrn_lines() == 0)
335: where = 1;
1.20 deraadt 336: } else if (noreverse) {
337: if (!pch_swap())
338: /* put it back to normal */
339: fatal("lost hunk on alloc error!\n");
340: reverse = !reverse;
341: say("Ignoring previously applied (or reversed) patch.\n");
1.35 otto 342: skip_rest_of_patch = true;
1.20 deraadt 343: } else if (batch) {
344: if (verbose)
345: say("%seversed (or previously applied) patch detected! %s -R.",
346: reverse ? "R" : "Unr",
347: reverse ? "Assuming" : "Ignoring");
348: } else {
349: ask("%seversed (or previously applied) patch detected! %s -R? [y] ",
350: reverse ? "R" : "Unr",
351: reverse ? "Assume" : "Ignore");
352: if (*buf == 'n') {
353: ask("Apply anyway? [n] ");
354: if (*buf != 'y')
1.35 otto 355: skip_rest_of_patch = true;
1.29 otto 356: where = 0;
1.20 deraadt 357: reverse = !reverse;
358: if (!pch_swap())
359: /* put it back to normal */
360: fatal("lost hunk on alloc error!\n");
361: }
362: }
363: }
1.29 otto 364: } while (!skip_rest_of_patch && where == 0 &&
1.30 deraadt 365: ++fuzz <= mymaxfuzz);
1.20 deraadt 366:
367: if (skip_rest_of_patch) { /* just got decided */
368: fclose(ofp);
1.23 otto 369: ofp = NULL;
1.20 deraadt 370: }
371: }
372: newwhere = pch_newfirst() + last_offset;
373: if (skip_rest_of_patch) {
374: abort_hunk();
375: failed++;
376: if (verbose)
377: say("Hunk #%d ignored at %ld.\n",
378: hunk, newwhere);
1.29 otto 379: } else if (where == 0) {
1.20 deraadt 380: abort_hunk();
381: failed++;
382: if (verbose)
383: say("Hunk #%d failed at %ld.\n",
384: hunk, newwhere);
385: } else {
386: apply_hunk(where);
387: if (verbose) {
388: say("Hunk #%d succeeded at %ld",
389: hunk, newwhere);
1.29 otto 390: if (fuzz != 0)
1.20 deraadt 391: say(" with fuzz %ld", fuzz);
392: if (last_offset)
393: say(" (offset %ld line%s)",
394: last_offset,
395: last_offset == 1L ? "" : "s");
396: say(".\n");
397: }
398: }
399: }
400:
401: if (out_of_mem && using_plan_a) {
402: Argc = Argc_last;
403: Argv = Argv_last;
404: say("\n\nRan out of memory using Plan A--trying again...\n\n");
405: if (ofp)
406: fclose(ofp);
1.23 otto 407: ofp = NULL;
1.20 deraadt 408: if (rejfp)
409: fclose(rejfp);
1.23 otto 410: rejfp = NULL;
1.20 deraadt 411: continue;
1.11 espie 412: }
1.35 otto 413: if (hunk == 0)
414: fatal("Internal error: hunk should not be 0\n");
1.1 deraadt 415:
1.20 deraadt 416: /* finish spewing out the new file */
417: if (!skip_rest_of_patch)
418: spew_output();
419:
420: /* and put the output where desired */
421: ignore_signals();
422: if (!skip_rest_of_patch) {
423: struct stat statbuf;
424: char *realout = outname;
425:
426: if (!check_only) {
427: if (move_file(TMPOUTNAME, outname) < 0) {
1.35 otto 428: toutkeep = true;
1.20 deraadt 429: realout = TMPOUTNAME;
430: chmod(TMPOUTNAME, filemode);
431: } else
432: chmod(outname, filemode);
433:
434: if (remove_empty_files &&
435: stat(realout, &statbuf) == 0 &&
436: statbuf.st_size == 0) {
437: if (verbose)
438: say("Removing %s (empty after patching).\n",
439: realout);
440: unlink(realout);
441: }
442: }
443: }
444: fclose(rejfp);
1.23 otto 445: rejfp = NULL;
1.20 deraadt 446: if (failed) {
1.28 millert 447: error = 1;
1.29 otto 448: if (*rejname == '\0') {
1.20 deraadt 449: if (strlcpy(rejname, outname,
450: sizeof(rejname)) >= sizeof(rejname))
451: fatal("filename %s is too long\n", outname);
452: if (strlcat(rejname, REJEXT,
453: sizeof(rejname)) >= sizeof(rejname))
454: fatal("filename %s is too long\n", outname);
455: }
1.50 millert 456: if (!check_only)
457: say("%d out of %d hunks %s--saving rejects to %s\n",
458: failed, hunk, skip_rest_of_patch ? "ignored" : "failed", rejname);
459: else
460: say("%d out of %d hunks %s\n",
461: failed, hunk, skip_rest_of_patch ? "ignored" : "failed");
1.20 deraadt 462: if (!check_only && move_file(TMPREJNAME, rejname) < 0)
1.35 otto 463: trejkeep = true;
1.20 deraadt 464: }
465: set_signals(1);
466: }
1.53 deraadt 467:
1.47 stsp 468: if (!patch_seen)
469: error = 2;
470:
1.28 millert 471: my_exit(error);
1.20 deraadt 472: /* NOTREACHED */
1.1 deraadt 473: }
474:
475: /* Prepare to find the next patch to do in the patch file. */
476:
1.23 otto 477: static void
1.20 deraadt 478: reinitialize_almost_everything(void)
1.1 deraadt 479: {
1.20 deraadt 480: re_patch();
481: re_input();
1.1 deraadt 482:
1.20 deraadt 483: input_lines = 0;
484: last_frozen_line = 0;
1.1 deraadt 485:
1.20 deraadt 486: filec = 0;
1.35 otto 487: if (!out_of_mem) {
1.20 deraadt 488: free(filearg[0]);
1.23 otto 489: filearg[0] = NULL;
1.20 deraadt 490: }
1.35 otto 491:
492: free(outname);
493: outname = NULL;
494:
1.20 deraadt 495: last_offset = 0;
1.35 otto 496: diff_type = 0;
1.1 deraadt 497:
1.35 otto 498: free(revision);
499: revision = NULL;
1.1 deraadt 500:
1.20 deraadt 501: reverse = reverse_flag_specified;
1.35 otto 502: skip_rest_of_patch = false;
1.1 deraadt 503:
1.20 deraadt 504: get_some_switches();
1.1 deraadt 505: }
506:
1.24 millert 507: /* Process switches and filenames. */
1.1 deraadt 508:
1.23 otto 509: static void
1.20 deraadt 510: get_some_switches(void)
1.1 deraadt 511: {
1.34 millert 512: const char *options = "b::B:cCd:D:eEfF:i:lnNo:p:r:RstuvV:x:z:";
1.24 millert 513: static struct option longopts[] = {
1.27 millert 514: {"backup", no_argument, 0, 'b'},
1.24 millert 515: {"batch", no_argument, 0, 't'},
516: {"check", no_argument, 0, 'C'},
517: {"context", no_argument, 0, 'c'},
518: {"debug", required_argument, 0, 'x'},
519: {"directory", required_argument, 0, 'd'},
1.66 zhuk 520: {"dry-run", no_argument, 0, 'C'},
1.24 millert 521: {"ed", no_argument, 0, 'e'},
522: {"force", no_argument, 0, 'f'},
523: {"forward", no_argument, 0, 'N'},
524: {"fuzz", required_argument, 0, 'F'},
525: {"ifdef", required_argument, 0, 'D'},
1.32 millert 526: {"input", required_argument, 0, 'i'},
1.24 millert 527: {"ignore-whitespace", no_argument, 0, 'l'},
528: {"normal", no_argument, 0, 'n'},
529: {"output", required_argument, 0, 'o'},
530: {"prefix", required_argument, 0, 'B'},
531: {"quiet", no_argument, 0, 's'},
532: {"reject-file", required_argument, 0, 'r'},
533: {"remove-empty-files", no_argument, 0, 'E'},
534: {"reverse", no_argument, 0, 'R'},
535: {"silent", no_argument, 0, 's'},
1.34 millert 536: {"strip", required_argument, 0, 'p'},
1.27 millert 537: {"suffix", required_argument, 0, 'z'},
1.24 millert 538: {"unified", no_argument, 0, 'u'},
539: {"version", no_argument, 0, 'v'},
540: {"version-control", required_argument, 0, 'V'},
1.38 millert 541: {"posix", no_argument, &posix, 1},
1.24 millert 542: {NULL, 0, 0, 0}
543: };
544: int ch;
1.1 deraadt 545:
1.20 deraadt 546: rejname[0] = '\0';
547: Argc_last = Argc;
548: Argv_last = Argv;
549: if (!Argc)
550: return;
1.24 millert 551: optreset = optind = 1;
552: while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) {
553: switch (ch) {
554: case 'b':
1.27 millert 555: if (backup_type == none)
556: backup_type = numbered_existing;
557: if (optarg == NULL)
558: break;
559: if (verbose)
560: say("Warning, the ``-b suffix'' option has been"
561: " obsoleted by the -z option.\n");
562: /* FALLTHROUGH */
563: case 'z':
564: /* must directly follow 'b' case for backwards compat */
1.54 tobias 565: simple_backup_suffix = xstrdup(optarg);
1.24 millert 566: break;
567: case 'B':
1.54 tobias 568: origprae = xstrdup(optarg);
1.24 millert 569: break;
570: case 'c':
571: diff_type = CONTEXT_DIFF;
572: break;
573: case 'C':
1.35 otto 574: check_only = true;
1.24 millert 575: break;
576: case 'd':
1.68 deraadt 577: if (chdir(optarg) == -1)
1.24 millert 578: pfatal("can't cd to %s", optarg);
579: break;
580: case 'D':
1.35 otto 581: do_defines = true;
1.51 deraadt 582: if (!isalpha((unsigned char)*optarg) && *optarg != '_')
1.24 millert 583: fatal("argument to -D is not an identifier\n");
584: snprintf(if_defined, sizeof if_defined,
585: "#ifdef %s\n", optarg);
586: snprintf(not_defined, sizeof not_defined,
587: "#ifndef %s\n", optarg);
588: snprintf(end_defined, sizeof end_defined,
589: "#endif /* %s */\n", optarg);
590: break;
591: case 'e':
592: diff_type = ED_DIFF;
593: break;
594: case 'E':
1.35 otto 595: remove_empty_files = true;
1.24 millert 596: break;
597: case 'f':
1.35 otto 598: force = true;
1.24 millert 599: break;
600: case 'F':
601: maxfuzz = atoi(optarg);
602: break;
1.32 millert 603: case 'i':
604: if (++filec == MAXFILEC)
605: fatal("too many file arguments\n");
1.54 tobias 606: filearg[filec] = xstrdup(optarg);
1.32 millert 607: break;
1.24 millert 608: case 'l':
1.35 otto 609: canonicalize = true;
1.24 millert 610: break;
611: case 'n':
612: diff_type = NORMAL_DIFF;
613: break;
614: case 'N':
1.35 otto 615: noreverse = true;
1.24 millert 616: break;
617: case 'o':
1.54 tobias 618: outname = xstrdup(optarg);
1.24 millert 619: break;
620: case 'p':
1.34 millert 621: strippath = atoi(optarg);
1.24 millert 622: break;
623: case 'r':
624: if (strlcpy(rejname, optarg,
625: sizeof(rejname)) >= sizeof(rejname))
626: fatal("argument for -r is too long\n");
627: break;
628: case 'R':
1.35 otto 629: reverse = true;
630: reverse_flag_specified = true;
1.24 millert 631: break;
632: case 's':
1.35 otto 633: verbose = false;
1.24 millert 634: break;
635: case 't':
1.35 otto 636: batch = true;
1.24 millert 637: break;
638: case 'u':
639: diff_type = UNI_DIFF;
640: break;
641: case 'v':
642: version();
643: break;
644: case 'V':
645: backup_type = get_version(optarg);
1.76 ! jcs 646: Vflag = true;
1.24 millert 647: break;
1.1 deraadt 648: #ifdef DEBUGGING
1.24 millert 649: case 'x':
650: debug = atoi(optarg);
651: break;
1.1 deraadt 652: #endif
1.24 millert 653: default:
1.38 millert 654: if (ch != '\0')
655: usage();
1.24 millert 656: break;
1.20 deraadt 657: }
1.1 deraadt 658: }
1.24 millert 659: Argc -= optind;
660: Argv += optind;
661:
1.32 millert 662: if (Argc > 0) {
1.54 tobias 663: filearg[0] = xstrdup(*Argv++);
1.24 millert 664: Argc--;
1.32 millert 665: while (Argc > 0) {
666: if (++filec == MAXFILEC)
667: fatal("too many file arguments\n");
1.54 tobias 668: filearg[filec] = xstrdup(*Argv++);
1.32 millert 669: Argc--;
670: }
1.24 millert 671: }
1.38 millert 672:
673: if (getenv("POSIXLY_CORRECT") != NULL)
674: posix = 1;
1.24 millert 675: }
676:
677: static __dead void
678: usage(void)
679: {
680: fprintf(stderr,
1.45 sobrado 681: "usage: patch [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] [-d directory]\n"
1.34 millert 682: " [-F max-fuzz] [-i patchfile] [-o out-file] [-p strip-count]\n"
1.76 ! jcs 683: " [-r rej-name] [-V t | nil | never | none] [-x number]\n"
! 684: " [-z backup-ext] [--posix] [origfile [patchfile]]\n"
1.45 sobrado 685: " patch <patchfile\n");
1.63 gsoares 686: my_exit(2);
1.1 deraadt 687: }
688:
1.20 deraadt 689: /*
690: * Attempt to find the right place to apply this hunk of patch.
691: */
1.23 otto 692: static LINENUM
1.20 deraadt 693: locate_hunk(LINENUM fuzz)
1.1 deraadt 694: {
1.20 deraadt 695: LINENUM first_guess = pch_first() + last_offset;
696: LINENUM offset;
697: LINENUM pat_lines = pch_ptrn_lines();
698: LINENUM max_pos_offset = input_lines - first_guess - pat_lines + 1;
699: LINENUM max_neg_offset = first_guess - last_frozen_line - 1 + pch_context();
700:
1.36 otto 701: if (pat_lines == 0) { /* null range matches always */
1.39 otto 702: if (verbose && fuzz == 0 && (diff_type == CONTEXT_DIFF
1.36 otto 703: || diff_type == NEW_CONTEXT_DIFF
704: || diff_type == UNI_DIFF)) {
705: say("Empty context always matches.\n");
706: }
1.71 op 707: if (first_guess == 0)
708: return 1;
1.46 otto 709: return (first_guess);
1.36 otto 710: }
1.20 deraadt 711: if (max_neg_offset >= first_guess) /* do not try lines < 0 */
712: max_neg_offset = first_guess - 1;
1.29 otto 713: if (first_guess <= input_lines && patch_match(first_guess, 0, fuzz))
1.20 deraadt 714: return first_guess;
715: for (offset = 1; ; offset++) {
716: bool check_after = (offset <= max_pos_offset);
717: bool check_before = (offset <= max_neg_offset);
1.1 deraadt 718:
1.20 deraadt 719: if (check_after && patch_match(first_guess, offset, fuzz)) {
1.1 deraadt 720: #ifdef DEBUGGING
1.20 deraadt 721: if (debug & 1)
722: say("Offset changing from %ld to %ld\n",
723: last_offset, offset);
1.1 deraadt 724: #endif
1.20 deraadt 725: last_offset = offset;
726: return first_guess + offset;
727: } else if (check_before && patch_match(first_guess, -offset, fuzz)) {
1.1 deraadt 728: #ifdef DEBUGGING
1.20 deraadt 729: if (debug & 1)
730: say("Offset changing from %ld to %ld\n",
731: last_offset, -offset);
1.1 deraadt 732: #endif
1.20 deraadt 733: last_offset = -offset;
734: return first_guess - offset;
735: } else if (!check_before && !check_after)
1.29 otto 736: return 0;
1.1 deraadt 737: }
738: }
739:
740: /* We did not find the pattern, dump out the hunk so they can handle it. */
741:
1.23 otto 742: static void
1.43 otto 743: abort_context_hunk(void)
1.1 deraadt 744: {
1.20 deraadt 745: LINENUM i;
1.29 otto 746: const LINENUM pat_end = pch_end();
1.20 deraadt 747: /*
748: * add in last_offset to guess the same as the previous successful
749: * hunk
750: */
1.29 otto 751: const LINENUM oldfirst = pch_first() + last_offset;
752: const LINENUM newfirst = pch_newfirst() + last_offset;
753: const LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
754: const LINENUM newlast = newfirst + pch_repl_lines() - 1;
755: const char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
756: const char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
1.20 deraadt 757:
758: fprintf(rejfp, "***************\n");
759: for (i = 0; i <= pat_end; i++) {
760: switch (pch_char(i)) {
761: case '*':
762: if (oldlast < oldfirst)
763: fprintf(rejfp, "*** 0%s\n", stars);
764: else if (oldlast == oldfirst)
765: fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
766: else
767: fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst,
768: oldlast, stars);
769: break;
770: case '=':
771: if (newlast < newfirst)
772: fprintf(rejfp, "--- 0%s\n", minuses);
773: else if (newlast == newfirst)
774: fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
775: else
776: fprintf(rejfp, "--- %ld,%ld%s\n", newfirst,
777: newlast, minuses);
778: break;
779: case '\n':
780: fprintf(rejfp, "%s", pfetch(i));
781: break;
782: case ' ':
783: case '-':
784: case '+':
785: case '!':
786: fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
787: break;
788: default:
1.43 otto 789: fatal("fatal internal error in abort_context_hunk\n");
790: }
791: }
792: }
793:
794: static void
795: rej_line(int ch, LINENUM i)
796: {
797: size_t len;
798: const char *line = pfetch(i);
799:
800: len = strlen(line);
801:
802: fprintf(rejfp, "%c%s", ch, line);
803: if (len == 0 || line[len-1] != '\n')
804: fprintf(rejfp, "\n\\ No newline at end of file\n");
805: }
806:
807: static void
808: abort_hunk(void)
809: {
810: LINENUM i, j, split;
811: int ch1, ch2;
812: const LINENUM pat_end = pch_end();
813: const LINENUM oldfirst = pch_first() + last_offset;
814: const LINENUM newfirst = pch_newfirst() + last_offset;
815:
816: if (diff_type != UNI_DIFF) {
817: abort_context_hunk();
818: return;
819: }
820: split = -1;
821: for (i = 0; i <= pat_end; i++) {
822: if (pch_char(i) == '=') {
823: split = i;
824: break;
825: }
826: }
827: if (split == -1) {
828: fprintf(rejfp, "malformed hunk: no split found\n");
829: return;
830: }
831: i = 0;
832: j = split + 1;
833: fprintf(rejfp, "@@ -%ld,%ld +%ld,%ld @@\n",
834: pch_ptrn_lines() ? oldfirst : 0,
835: pch_ptrn_lines(), newfirst, pch_repl_lines());
836: while (i < split || j <= pat_end) {
837: ch1 = i < split ? pch_char(i) : -1;
838: ch2 = j <= pat_end ? pch_char(j) : -1;
839: if (ch1 == '-') {
840: rej_line('-', i);
841: i++;
842: } else if (ch1 == ' ' && ch2 == ' ') {
843: rej_line(' ', i);
844: i++;
845: j++;
846: } else if (ch1 == '!' && ch2 == '!') {
847: while (i < split && ch1 == '!') {
848: rej_line('-', i);
849: i++;
850: ch1 = i < split ? pch_char(i) : -1;
851: }
852: while (j <= pat_end && ch2 == '!') {
853: rej_line('+', j);
854: j++;
855: ch2 = j <= pat_end ? pch_char(j) : -1;
856: }
857: } else if (ch1 == '*') {
858: i++;
859: } else if (ch2 == '+' || ch2 == ' ') {
860: rej_line(ch2, j);
861: j++;
862: } else {
863: fprintf(rejfp, "internal error on (%ld %ld %ld)\n",
864: i, split, j);
865: rej_line(ch1, i);
866: rej_line(ch2, j);
867: return;
1.20 deraadt 868: }
1.1 deraadt 869: }
870: }
871:
872: /* We found where to apply it (we hope), so do it. */
873:
1.23 otto 874: static void
1.20 deraadt 875: apply_hunk(LINENUM where)
1.1 deraadt 876: {
1.29 otto 877: LINENUM old = 1;
878: const LINENUM lastline = pch_ptrn_lines();
879: LINENUM new = lastline + 1;
1.1 deraadt 880: #define OUTSIDE 0
881: #define IN_IFNDEF 1
882: #define IN_IFDEF 2
883: #define IN_ELSE 3
1.29 otto 884: int def_state = OUTSIDE;
885: const LINENUM pat_end = pch_end();
1.20 deraadt 886:
887: where--;
888: while (pch_char(new) == '=' || pch_char(new) == '\n')
889: new++;
890:
891: while (old <= lastline) {
892: if (pch_char(old) == '-') {
1.37 otto 893: copy_till(where + old - 1, false);
1.29 otto 894: if (do_defines) {
1.20 deraadt 895: if (def_state == OUTSIDE) {
896: fputs(not_defined, ofp);
897: def_state = IN_IFNDEF;
898: } else if (def_state == IN_IFDEF) {
899: fputs(else_defined, ofp);
900: def_state = IN_ELSE;
901: }
902: fputs(pfetch(old), ofp);
903: }
904: last_frozen_line++;
905: old++;
906: } else if (new > pat_end) {
907: break;
908: } else if (pch_char(new) == '+') {
1.37 otto 909: copy_till(where + old - 1, false);
1.29 otto 910: if (do_defines) {
1.20 deraadt 911: if (def_state == IN_IFNDEF) {
912: fputs(else_defined, ofp);
913: def_state = IN_ELSE;
914: } else if (def_state == OUTSIDE) {
915: fputs(if_defined, ofp);
916: def_state = IN_IFDEF;
917: }
918: }
919: fputs(pfetch(new), ofp);
920: new++;
921: } else if (pch_char(new) != pch_char(old)) {
922: say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
923: pch_hunk_beg() + old,
924: pch_hunk_beg() + new);
1.1 deraadt 925: #ifdef DEBUGGING
1.20 deraadt 926: say("oldchar = '%c', newchar = '%c'\n",
927: pch_char(old), pch_char(new));
1.1 deraadt 928: #endif
1.28 millert 929: my_exit(2);
1.20 deraadt 930: } else if (pch_char(new) == '!') {
1.37 otto 931: copy_till(where + old - 1, false);
1.29 otto 932: if (do_defines) {
1.20 deraadt 933: fputs(not_defined, ofp);
934: def_state = IN_IFNDEF;
935: }
936: while (pch_char(old) == '!') {
1.29 otto 937: if (do_defines) {
1.20 deraadt 938: fputs(pfetch(old), ofp);
939: }
940: last_frozen_line++;
941: old++;
942: }
1.29 otto 943: if (do_defines) {
1.20 deraadt 944: fputs(else_defined, ofp);
945: def_state = IN_ELSE;
946: }
947: while (pch_char(new) == '!') {
948: fputs(pfetch(new), ofp);
949: new++;
950: }
951: } else {
1.35 otto 952: if (pch_char(new) != ' ')
953: fatal("Internal error: expected ' '\n");
1.20 deraadt 954: old++;
955: new++;
1.29 otto 956: if (do_defines && def_state != OUTSIDE) {
1.20 deraadt 957: fputs(end_defined, ofp);
958: def_state = OUTSIDE;
959: }
960: }
1.1 deraadt 961: }
1.20 deraadt 962: if (new <= pat_end && pch_char(new) == '+') {
1.37 otto 963: copy_till(where + old - 1, false);
1.29 otto 964: if (do_defines) {
1.20 deraadt 965: if (def_state == OUTSIDE) {
966: fputs(if_defined, ofp);
967: def_state = IN_IFDEF;
968: } else if (def_state == IN_IFNDEF) {
969: fputs(else_defined, ofp);
970: def_state = IN_ELSE;
971: }
972: }
973: while (new <= pat_end && pch_char(new) == '+') {
974: fputs(pfetch(new), ofp);
975: new++;
1.1 deraadt 976: }
977: }
1.29 otto 978: if (do_defines && def_state != OUTSIDE) {
1.1 deraadt 979: fputs(end_defined, ofp);
980: }
981: }
982:
1.20 deraadt 983: /*
984: * Open the new file.
985: */
1.23 otto 986: static void
1.29 otto 987: init_output(const char *name)
1.1 deraadt 988: {
1.20 deraadt 989: ofp = fopen(name, "w");
1.23 otto 990: if (ofp == NULL)
1.20 deraadt 991: pfatal("can't create %s", name);
1.1 deraadt 992: }
993:
1.20 deraadt 994: /*
995: * Open a file to put hunks we can't locate.
996: */
1.23 otto 997: static void
1.29 otto 998: init_reject(const char *name)
1.1 deraadt 999: {
1.20 deraadt 1000: rejfp = fopen(name, "w");
1.23 otto 1001: if (rejfp == NULL)
1.20 deraadt 1002: pfatal("can't create %s", name);
1.1 deraadt 1003: }
1004:
1.20 deraadt 1005: /*
1006: * Copy input file to output, up to wherever hunk is to be applied.
1.37 otto 1007: * If endoffile is true, treat the last line specially since it may
1008: * lack a newline.
1.20 deraadt 1009: */
1.23 otto 1010: static void
1.37 otto 1011: copy_till(LINENUM lastline, bool endoffile)
1.1 deraadt 1012: {
1.29 otto 1013: if (last_frozen_line > lastline)
1.20 deraadt 1014: fatal("misordered hunks! output would be garbled\n");
1.37 otto 1015: while (last_frozen_line < lastline) {
1016: if (++last_frozen_line == lastline && endoffile)
1017: dump_line(last_frozen_line, !last_line_missing_eol);
1018: else
1019: dump_line(last_frozen_line, true);
1020: }
1.1 deraadt 1021: }
1022:
1.20 deraadt 1023: /*
1024: * Finish copying the input file to the output file.
1025: */
1.23 otto 1026: static void
1.20 deraadt 1027: spew_output(void)
1.1 deraadt 1028: {
1029: #ifdef DEBUGGING
1.20 deraadt 1030: if (debug & 256)
1031: say("il=%ld lfl=%ld\n", input_lines, last_frozen_line);
1.1 deraadt 1032: #endif
1.20 deraadt 1033: if (input_lines)
1.37 otto 1034: copy_till(input_lines, true); /* dump remainder of file */
1.20 deraadt 1035: fclose(ofp);
1.23 otto 1036: ofp = NULL;
1.1 deraadt 1037: }
1038:
1.20 deraadt 1039: /*
1040: * Copy one line from input to output.
1041: */
1.23 otto 1042: static void
1.37 otto 1043: dump_line(LINENUM line, bool write_newline)
1.1 deraadt 1044: {
1.29 otto 1045: char *s;
1.1 deraadt 1046:
1.26 otto 1047: s = ifetch(line, 0);
1048: if (s == NULL)
1049: return;
1.30 deraadt 1050: /* Note: string is not NUL terminated. */
1.37 otto 1051: for (; *s != '\n'; s++)
1052: putc(*s, ofp);
1053: if (write_newline)
1054: putc('\n', ofp);
1.1 deraadt 1055: }
1056:
1.20 deraadt 1057: /*
1058: * Does the patch pattern match at line base+offset?
1059: */
1.23 otto 1060: static bool
1.20 deraadt 1061: patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
1062: {
1.29 otto 1063: LINENUM pline = 1 + fuzz;
1064: LINENUM iline;
1065: LINENUM pat_lines = pch_ptrn_lines() - fuzz;
1066: const char *ilineptr;
1.30 deraadt 1067: const char *plineptr;
1.72 tb 1068: ssize_t plinelen;
1.20 deraadt 1069:
1070: for (iline = base + offset + fuzz; pline <= pat_lines; pline++, iline++) {
1.26 otto 1071: ilineptr = ifetch(iline, offset >= 0);
1072: if (ilineptr == NULL)
1.35 otto 1073: return false;
1.26 otto 1074: plineptr = pfetch(pline);
1075: plinelen = pch_line_len(pline);
1.20 deraadt 1076: if (canonicalize) {
1.26 otto 1077: if (!similar(ilineptr, plineptr, plinelen))
1.35 otto 1078: return false;
1.26 otto 1079: } else if (strnNE(ilineptr, plineptr, plinelen))
1.35 otto 1080: return false;
1.41 otto 1081: if (iline == input_lines) {
1082: /*
1083: * We are looking at the last line of the file.
1084: * If the file has no eol, the patch line should
1085: * not have one either and vice-versa. Note that
1086: * plinelen > 0.
1087: */
1088: if (last_line_missing_eol) {
1089: if (plineptr[plinelen - 1] == '\n')
1090: return false;
1091: } else {
1092: if (plineptr[plinelen - 1] != '\n')
1093: return false;
1094: }
1095: }
1.1 deraadt 1096: }
1.35 otto 1097: return true;
1.1 deraadt 1098: }
1099:
1.20 deraadt 1100: /*
1101: * Do two lines match with canonicalized white space?
1102: */
1.23 otto 1103: static bool
1.72 tb 1104: similar(const char *a, const char *b, ssize_t len)
1.20 deraadt 1105: {
1106: while (len) {
1.51 deraadt 1107: if (isspace((unsigned char)*b)) { /* whitespace (or \n) to match? */
1108: if (!isspace((unsigned char)*a))
1109: return false; /* no corresponding whitespace */
1110: while (len && isspace((unsigned char)*b) && *b != '\n')
1.20 deraadt 1111: b++, len--; /* skip pattern whitespace */
1.51 deraadt 1112: while (isspace((unsigned char)*a) && *a != '\n')
1.20 deraadt 1113: a++; /* skip target whitespace */
1114: if (*a == '\n' || *b == '\n')
1115: return (*a == *b); /* should end in sync */
1116: } else if (*a++ != *b++) /* match non-whitespace chars */
1.35 otto 1117: return false;
1.20 deraadt 1118: else
1119: len--; /* probably not necessary */
1.1 deraadt 1120: }
1.35 otto 1121: return true; /* actually, this is not reached */
1.20 deraadt 1122: /* since there is always a \n */
1.1 deraadt 1123: }