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