1 /* $OpenBSD: patch.c,v 1.8 1996/09/24 04:19:28 millert 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[] = "$OpenBSD: patch.c,v 1.8 1996/09/24 04:19:28 millert 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 /* procedures */ 25 26 void reinitialize_almost_everything(); 27 void get_some_switches(); 28 LINENUM locate_hunk(); 29 void abort_hunk(); 30 void apply_hunk(); 31 void init_output(); 32 void init_reject(); 33 void copy_till(); 34 void spew_output(); 35 void dump_line(); 36 bool patch_match(); 37 bool similar(); 38 void re_input(); 39 void my_exit(); 40 41 /* TRUE if -E was specified on command line. */ 42 static int remove_empty_files = FALSE; 43 44 /* TRUE if -R was specified on command line. */ 45 static int reverse_flag_specified = FALSE; 46 47 /* Apply a set of diffs as appropriate. */ 48 49 int 50 main(argc,argv) 51 int argc; 52 char **argv; 53 { 54 LINENUM where; 55 LINENUM newwhere; 56 LINENUM fuzz; 57 LINENUM mymaxfuzz; 58 int hunk = 0; 59 int failed = 0; 60 int failtotal = 0; 61 int i; 62 63 setbuf(stderr, serrbuf); 64 for (i = 0; i<MAXFILEC; i++) 65 filearg[i] = Nullch; 66 67 myuid = getuid(); 68 69 /* Cons up the names of the temporary files. */ 70 { 71 /* Directory for temporary files. */ 72 char *tmpdir; 73 int tmpname_len; 74 75 tmpdir = getenv ("TMPDIR"); 76 if (tmpdir == NULL) { 77 tmpdir = "/tmp"; 78 } 79 tmpname_len = strlen (tmpdir) + 20; 80 81 TMPOUTNAME = (char *) malloc (tmpname_len); 82 strcpy (TMPOUTNAME, tmpdir); 83 strcat (TMPOUTNAME, "/patchoXXXXXX"); 84 if ((i = mkstemp(TMPOUTNAME)) < 0) 85 pfatal2("can't create %s", TMPOUTNAME); 86 Close(i); 87 88 TMPINNAME = (char *) malloc (tmpname_len); 89 strcpy (TMPINNAME, tmpdir); 90 strcat (TMPINNAME, "/patchiXXXXXX"); 91 if ((i = mkstemp(TMPINNAME)) < 0) 92 pfatal2("can't create %s", TMPINNAME); 93 Close(i); 94 95 TMPREJNAME = (char *) malloc (tmpname_len); 96 strcpy (TMPREJNAME, tmpdir); 97 strcat (TMPREJNAME, "/patchrXXXXXX"); 98 if ((i = mkstemp(TMPREJNAME)) < 0) 99 pfatal2("can't create %s", TMPREJNAME); 100 Close(i); 101 102 TMPPATNAME = (char *) malloc (tmpname_len); 103 strcpy (TMPPATNAME, tmpdir); 104 strcat (TMPPATNAME, "/patchpXXXXXX"); 105 if ((i = mkstemp(TMPPATNAME)) < 0) 106 pfatal2("can't create %s", TMPPATNAME); 107 Close(i); 108 } 109 110 { 111 char *v; 112 113 v = getenv ("SIMPLE_BACKUP_SUFFIX"); 114 if (v) 115 simple_backup_suffix = v; 116 else 117 simple_backup_suffix = ORIGEXT; 118 #ifndef NODIR 119 v = getenv ("VERSION_CONTROL"); 120 backup_type = get_version (v); /* OK to pass NULL. */ 121 #endif 122 } 123 124 /* parse switches */ 125 Argc = argc; 126 Argv = argv; 127 get_some_switches(); 128 129 /* make sure we clean up /tmp in case of disaster */ 130 set_signals(0); 131 132 for ( 133 open_patch_file(filearg[1]); 134 there_is_another_patch(); 135 reinitialize_almost_everything() 136 ) { /* for each patch in patch file */ 137 138 if (outname == Nullch) 139 outname = savestr(filearg[0]); 140 141 /* for ed script just up and do it and exit */ 142 if (diff_type == ED_DIFF) { 143 do_ed_script(); 144 continue; 145 } 146 147 /* initialize the patched file */ 148 if (!skip_rest_of_patch) 149 init_output(TMPOUTNAME); 150 151 /* initialize reject file */ 152 init_reject(TMPREJNAME); 153 154 /* find out where all the lines are */ 155 if (!skip_rest_of_patch) 156 scan_input(filearg[0]); 157 158 /* from here on, open no standard i/o files, because malloc */ 159 /* might misfire and we can't catch it easily */ 160 161 /* apply each hunk of patch */ 162 hunk = 0; 163 failed = 0; 164 out_of_mem = FALSE; 165 while (another_hunk()) { 166 hunk++; 167 fuzz = Nulline; 168 mymaxfuzz = pch_context(); 169 if (maxfuzz < mymaxfuzz) 170 mymaxfuzz = maxfuzz; 171 if (!skip_rest_of_patch) { 172 do { 173 where = locate_hunk(fuzz); 174 if (hunk == 1 && where == Nulline && !force) { 175 /* dwim for reversed patch? */ 176 if (!pch_swap()) { 177 if (fuzz == Nulline) 178 say1( 179 "Not enough memory to try swapped hunk! Assuming unswapped.\n"); 180 continue; 181 } 182 reverse = !reverse; 183 where = locate_hunk(fuzz); /* try again */ 184 if (where == Nulline) { /* didn't find it swapped */ 185 if (!pch_swap()) /* put it back to normal */ 186 fatal1("lost hunk on alloc error!\n"); 187 reverse = !reverse; 188 } 189 else if (noreverse) { 190 if (!pch_swap()) /* put it back to normal */ 191 fatal1("lost hunk on alloc error!\n"); 192 reverse = !reverse; 193 say1( 194 "Ignoring previously applied (or reversed) patch.\n"); 195 skip_rest_of_patch = TRUE; 196 } 197 else if (batch) { 198 if (verbose) 199 say3( 200 "%seversed (or previously applied) patch detected! %s -R.", 201 reverse ? "R" : "Unr", 202 reverse ? "Assuming" : "Ignoring"); 203 } 204 else { 205 ask3( 206 "%seversed (or previously applied) patch detected! %s -R? [y] ", 207 reverse ? "R" : "Unr", 208 reverse ? "Assume" : "Ignore"); 209 if (*buf == 'n') { 210 ask1("Apply anyway? [n] "); 211 if (*buf != 'y') 212 skip_rest_of_patch = TRUE; 213 where = Nulline; 214 reverse = !reverse; 215 if (!pch_swap()) /* put it back to normal */ 216 fatal1("lost hunk on alloc error!\n"); 217 } 218 } 219 } 220 } while (!skip_rest_of_patch && where == Nulline && 221 ++fuzz <= mymaxfuzz); 222 223 if (skip_rest_of_patch) { /* just got decided */ 224 Fclose(ofp); 225 ofp = Nullfp; 226 } 227 } 228 229 newwhere = pch_newfirst() + last_offset; 230 if (skip_rest_of_patch) { 231 abort_hunk(); 232 failed++; 233 if (verbose) 234 say3("Hunk #%d ignored at %ld.\n", hunk, newwhere); 235 } 236 else if (where == Nulline) { 237 abort_hunk(); 238 failed++; 239 if (verbose) 240 say3("Hunk #%d failed at %ld.\n", hunk, newwhere); 241 } 242 else { 243 apply_hunk(where); 244 if (verbose) { 245 say3("Hunk #%d succeeded at %ld", hunk, newwhere); 246 if (fuzz) 247 say2(" with fuzz %ld", fuzz); 248 if (last_offset) 249 say3(" (offset %ld line%s)", 250 last_offset, last_offset==1L?"":"s"); 251 say1(".\n"); 252 } 253 } 254 } 255 256 if (out_of_mem && using_plan_a) { 257 Argc = Argc_last; 258 Argv = Argv_last; 259 say1("\n\nRan out of memory using Plan A--trying again...\n\n"); 260 if (ofp) 261 Fclose(ofp); 262 ofp = Nullfp; 263 if (rejfp) 264 Fclose(rejfp); 265 rejfp = Nullfp; 266 continue; 267 } 268 269 assert(hunk); 270 271 /* finish spewing out the new file */ 272 if (!skip_rest_of_patch) 273 spew_output(); 274 275 /* and put the output where desired */ 276 ignore_signals(); 277 if (!skip_rest_of_patch) { 278 struct stat statbuf; 279 char *realout = outname; 280 281 if (move_file(TMPOUTNAME, outname) < 0) { 282 toutkeep = TRUE; 283 realout = TMPOUTNAME; 284 chmod(TMPOUTNAME, filemode); 285 } 286 else 287 chmod(outname, filemode); 288 289 if (remove_empty_files && stat(realout, &statbuf) == 0 290 && statbuf.st_size == 0) { 291 if (verbose) 292 say2("Removing %s (empty after patching).\n", realout); 293 while (unlink(realout) >= 0) ; /* while is for Eunice. */ 294 } 295 } 296 Fclose(rejfp); 297 rejfp = Nullfp; 298 if (failed) { 299 failtotal += failed; 300 if (!*rejname) { 301 Strcpy(rejname, outname); 302 #ifndef FLEXFILENAMES 303 { 304 char *s = rindex(rejname,'/'); 305 306 if (!s) 307 s = rejname; 308 if (strlen(s) > 13) 309 if (s[12] == '.') /* try to preserve difference */ 310 s[12] = s[13]; /* between .h, .c, .y, etc. */ 311 s[13] = '\0'; 312 } 313 #endif 314 Strcat(rejname, REJEXT); 315 } 316 if (skip_rest_of_patch) { 317 say4("%d out of %d hunks ignored--saving rejects to %s\n", 318 failed, hunk, rejname); 319 } 320 else { 321 say4("%d out of %d hunks failed--saving rejects to %s\n", 322 failed, hunk, rejname); 323 } 324 if (move_file(TMPREJNAME, rejname) < 0) 325 trejkeep = TRUE; 326 } 327 set_signals(1); 328 } 329 my_exit(failtotal); 330 } 331 332 /* Prepare to find the next patch to do in the patch file. */ 333 334 void 335 reinitialize_almost_everything() 336 { 337 re_patch(); 338 re_input(); 339 340 input_lines = 0; 341 last_frozen_line = 0; 342 343 filec = 0; 344 if (filearg[0] != Nullch && !out_of_mem) { 345 free(filearg[0]); 346 filearg[0] = Nullch; 347 } 348 349 if (outname != Nullch) { 350 free(outname); 351 outname = Nullch; 352 } 353 354 last_offset = 0; 355 356 diff_type = 0; 357 358 if (revision != Nullch) { 359 free(revision); 360 revision = Nullch; 361 } 362 363 reverse = reverse_flag_specified; 364 skip_rest_of_patch = FALSE; 365 366 get_some_switches(); 367 368 if (filec >= 2) 369 fatal1("you may not change to a different patch file\n"); 370 } 371 372 static char * 373 nextarg() 374 { 375 if (!--Argc) 376 fatal2("missing argument after `%s'\n", *Argv); 377 return *++Argv; 378 } 379 380 /* Module for handling of long options. */ 381 382 struct option { 383 char *long_opt; 384 char short_opt; 385 }; 386 387 int 388 optcmp(a, b) 389 struct option *a, *b; 390 { 391 return strcmp (a->long_opt, b->long_opt); 392 } 393 394 /* Decode Long options beginning with "--" to their short equivalents. */ 395 396 char 397 decode_long_option(opt) 398 char *opt; 399 { 400 /* This table must be sorted on the first field. We also decode 401 unimplemented options as those will be handled later anyway. */ 402 static struct option options[] = { 403 { "batch", 't' }, 404 { "check", 'C' }, 405 { "context", 'c' }, 406 { "debug", 'x' }, 407 { "directory", 'd' }, 408 { "ed", 'e' }, 409 { "force", 'f' }, 410 { "forward", 'N' }, 411 { "fuzz", 'F' }, 412 { "ifdef", 'D' }, 413 { "ignore-whitespace", 'l' }, 414 { "normal", 'n' }, 415 { "output", 'o' }, 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 = (struct option *)bsearch(&key, options, 433 sizeof(options) / sizeof(options[0]), 434 sizeof(options[0]), optcmp); 435 return found ? found->short_opt : '\0'; 436 } 437 438 /* Process switches and filenames up to next '+' or end of list. */ 439 440 void 441 get_some_switches() 442 { 443 Reg1 char *s; 444 445 rejname[0] = '\0'; 446 Argc_last = Argc; 447 Argv_last = Argv; 448 if (!Argc) 449 return; 450 for (Argc--,Argv++; Argc; Argc--,Argv++) { 451 s = Argv[0]; 452 if (strEQ(s, "+")) { 453 return; /* + will be skipped by for loop */ 454 } 455 if (*s != '-' || !s[1]) { 456 if (filec == MAXFILEC) 457 fatal1("too many file arguments\n"); 458 filearg[filec++] = savestr(s); 459 } 460 else { 461 char opt; 462 463 if (*(s + 1) == '-') { 464 opt = decode_long_option(s + 2); 465 s += strlen(s) - 1; 466 } 467 else 468 opt = *++s; 469 switch (opt) { 470 case 'b': 471 simple_backup_suffix = savestr(nextarg()); 472 break; 473 case 'B': 474 origprae = savestr(nextarg()); 475 break; 476 case 'c': 477 diff_type = CONTEXT_DIFF; 478 break; 479 case 'd': 480 if (!*++s) 481 s = nextarg(); 482 if (chdir(s) < 0) 483 pfatal2("can't cd to %s", s); 484 break; 485 case 'D': 486 do_defines = TRUE; 487 if (!*++s) 488 s = nextarg(); 489 if (!isalpha(*s) && '_' != *s) 490 fatal1("argument to -D is not an identifier\n"); 491 Snprintf(if_defined, sizeof if_defined, "#ifdef %s\n", s); 492 Snprintf(not_defined, sizeof not_defined, "#ifndef %s\n", s); 493 Snprintf(end_defined, sizeof end_defined, "#endif /* %s */\n", s); 494 break; 495 case 'e': 496 diff_type = ED_DIFF; 497 break; 498 case 'E': 499 remove_empty_files = TRUE; 500 break; 501 case 'f': 502 force = TRUE; 503 break; 504 case 'F': 505 if (!*++s) 506 s = nextarg(); 507 else if (*s == '=') 508 s++; 509 maxfuzz = atoi(s); 510 break; 511 case 'l': 512 canonicalize = TRUE; 513 break; 514 case 'n': 515 diff_type = NORMAL_DIFF; 516 break; 517 case 'N': 518 noreverse = TRUE; 519 break; 520 case 'o': 521 outname = savestr(nextarg()); 522 break; 523 case 'p': 524 if (!*++s) 525 s = nextarg(); 526 else if (*s == '=') 527 s++; 528 strippath = atoi(s); 529 break; 530 case 'r': 531 Strcpy(rejname, nextarg()); 532 break; 533 case 'R': 534 reverse = TRUE; 535 reverse_flag_specified = TRUE; 536 break; 537 case 's': 538 verbose = FALSE; 539 break; 540 case 'S': 541 skip_rest_of_patch = TRUE; 542 break; 543 case 't': 544 batch = TRUE; 545 break; 546 case 'u': 547 diff_type = UNI_DIFF; 548 break; 549 case 'v': 550 version(); 551 break; 552 case 'V': 553 #ifndef NODIR 554 backup_type = get_version (nextarg ()); 555 #endif 556 break; 557 #ifdef DEBUGGING 558 case 'x': 559 if (!*++s) 560 s = nextarg(); 561 debug = atoi(s); 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