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