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