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