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