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