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