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