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