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