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