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