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