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