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