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