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