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