1 /* $NetBSD: patch.c,v 1.8 2000/10/16 07:05:04 kleink 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.8 2000/10/16 07:05:04 kleink 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 { "patchfile", 'i' }, 428 { "prefix", 'B' }, 429 { "quiet", 's' }, 430 { "reject-file", 'r' }, 431 { "remove-empty-files", 'E' }, 432 { "reverse", 'R' }, 433 { "silent", 's' }, 434 { "skip", 'S' }, 435 { "strip", 'p' }, 436 { "suffix", 'b' }, 437 { "unified", 'u' }, 438 { "version", 'v' }, 439 { "version-control", 'V' }, 440 }; 441 struct option key, *found; 442 443 key.long_opt = opt; 444 found = (struct option *)bsearch(&key, options, 445 sizeof(options) / sizeof(options[0]), sizeof(options[0]), optcmp); 446 447 return found ? found->short_opt : '\0'; 448 } 449 450 /* Process switches and filenames up to next '+' or end of list. */ 451 452 static void 453 get_some_switches() 454 { 455 Reg1 char *s; 456 457 rejname[0] = '\0'; 458 Argc_last = Argc; 459 Argv_last = Argv; 460 if (!Argc) 461 return; 462 for (Argc--,Argv++; Argc; Argc--,Argv++) { 463 s = Argv[0]; 464 if (strEQ(s, "+")) { 465 return; /* + will be skipped by for loop */ 466 } 467 if (*s != '-' || !s[1]) { 468 if (filec == MAXFILEC) 469 fatal1("too many file arguments\n"); 470 if (filec == 1 && filearg[filec] != Nullch) 471 fatal1("-i option and patchfile argument are mutually\ 472 exclusive\n"); 473 filearg[filec++] = savestr(s); 474 } 475 else { 476 char opt; 477 478 if (*(s + 1) == '-') { 479 opt = decode_long_option(s + 2); 480 s += strlen(s) - 1; 481 } 482 else 483 opt = *++s; 484 485 switch (opt) { 486 case 'b': 487 simple_backup_suffix = savestr(nextarg()); 488 break; 489 case 'B': 490 origprae = savestr(nextarg()); 491 break; 492 case 'c': 493 diff_type = CONTEXT_DIFF; 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((unsigned char)*s) && '_' != *s) 506 fatal1("argument to -D is not an identifier\n"); 507 Sprintf(if_defined, "#ifdef %s\n", s); 508 Sprintf(not_defined, "#ifndef %s\n", s); 509 Sprintf(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++; 523 maxfuzz = atoi(s); 524 break; 525 case 'i': 526 if (filearg[1] != Nullch) 527 free(filearg[1]); 528 filearg[1] = savestr(nextarg()); 529 break; 530 case 'l': 531 canonicalize = TRUE; 532 break; 533 case 'n': 534 diff_type = NORMAL_DIFF; 535 break; 536 case 'N': 537 noreverse = TRUE; 538 break; 539 case 'o': 540 outname = savestr(nextarg()); 541 break; 542 case 'p': 543 if (*++s == '=') 544 s++; 545 strippath = atoi(s); 546 break; 547 case 'r': 548 Strcpy(rejname, nextarg()); 549 break; 550 case 'R': 551 reverse = TRUE; 552 reverse_flag_specified = TRUE; 553 break; 554 case 's': 555 verbose = FALSE; 556 break; 557 case 'S': 558 skip_rest_of_patch = TRUE; 559 break; 560 case 't': 561 batch = TRUE; 562 break; 563 case 'u': 564 diff_type = UNI_DIFF; 565 break; 566 case 'v': 567 version(); 568 break; 569 case 'V': 570 #ifndef NODIR 571 backup_type = get_version (nextarg ()); 572 #endif 573 break; 574 #ifdef DEBUGGING 575 case 'x': 576 debug = atoi(s+1); 577 break; 578 #endif 579 default: 580 fprintf(stderr, "patch: unrecognized option `%s'\n", Argv[0]); 581 fprintf(stderr, "\ 582 Usage: patch [options] [origfile [patchfile]] [+ [options] [origfile]]...\n\ 583 Options:\n\ 584 [-ceEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\ 585 [-D symbol] [-Fmax-fuzz] [-o out-file] [-p[strip-count]]\n\ 586 [-r rej-name] [-V {numbered,existing,simple}]\n"); 587 my_exit(1); 588 } 589 } 590 } 591 } 592 593 /* Attempt to find the right place to apply this hunk of patch. */ 594 595 static LINENUM 596 locate_hunk(fuzz) 597 LINENUM fuzz; 598 { 599 Reg1 LINENUM first_guess = pch_first() + last_offset; 600 Reg2 LINENUM offset; 601 LINENUM pat_lines = pch_ptrn_lines(); 602 Reg3 LINENUM max_pos_offset = input_lines - first_guess 603 - pat_lines + 1; 604 Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1 605 + pch_context(); 606 607 if (!pat_lines) /* null range matches always */ 608 return first_guess; 609 if (max_neg_offset >= first_guess) /* do not try lines < 0 */ 610 max_neg_offset = first_guess - 1; 611 if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz)) 612 return first_guess; 613 for (offset = 1; ; offset++) { 614 Reg5 bool check_after = (offset <= max_pos_offset); 615 Reg6 bool check_before = (offset <= max_neg_offset); 616 617 if (check_after && patch_match(first_guess, offset, fuzz)) { 618 #ifdef DEBUGGING 619 if (debug & 1) 620 say3("Offset changing from %ld to %ld\n", last_offset, offset); 621 #endif 622 last_offset = offset; 623 return first_guess+offset; 624 } 625 else if (check_before && patch_match(first_guess, -offset, fuzz)) { 626 #ifdef DEBUGGING 627 if (debug & 1) 628 say3("Offset changing from %ld to %ld\n", last_offset, -offset); 629 #endif 630 last_offset = -offset; 631 return first_guess-offset; 632 } 633 else if (!check_before && !check_after) 634 return Nulline; 635 } 636 } 637 638 /* We did not find the pattern, dump out the hunk so they can handle it. */ 639 640 static void 641 abort_hunk() 642 { 643 Reg1 LINENUM i; 644 Reg2 LINENUM pat_end = pch_end(); 645 /* add in last_offset to guess the same as the previous successful hunk */ 646 LINENUM oldfirst = pch_first() + last_offset; 647 LINENUM newfirst = pch_newfirst() + last_offset; 648 LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1; 649 LINENUM newlast = newfirst + pch_repl_lines() - 1; 650 char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : ""); 651 char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----"); 652 653 fprintf(rejfp, "***************\n"); 654 for (i=0; i<=pat_end; i++) { 655 switch (pch_char(i)) { 656 case '*': 657 if (oldlast < oldfirst) 658 fprintf(rejfp, "*** 0%s\n", stars); 659 else if (oldlast == oldfirst) 660 fprintf(rejfp, "*** %ld%s\n", oldfirst, stars); 661 else 662 fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars); 663 break; 664 case '=': 665 if (newlast < newfirst) 666 fprintf(rejfp, "--- 0%s\n", minuses); 667 else if (newlast == newfirst) 668 fprintf(rejfp, "--- %ld%s\n", newfirst, minuses); 669 else 670 fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses); 671 break; 672 case '\n': 673 fprintf(rejfp, "%s", pfetch(i)); 674 break; 675 case ' ': case '-': case '+': case '!': 676 fprintf(rejfp, "%c %s", pch_char(i), pfetch(i)); 677 break; 678 default: 679 fatal1("fatal internal error in abort_hunk\n"); 680 } 681 } 682 } 683 684 /* We found where to apply it (we hope), so do it. */ 685 686 static void 687 apply_hunk(where) 688 LINENUM where; 689 { 690 Reg1 LINENUM old = 1; 691 Reg2 LINENUM lastline = pch_ptrn_lines(); 692 Reg3 LINENUM new = lastline+1; 693 #define OUTSIDE 0 694 #define IN_IFNDEF 1 695 #define IN_IFDEF 2 696 #define IN_ELSE 3 697 Reg4 int def_state = OUTSIDE; 698 Reg5 bool R_do_defines = do_defines; 699 Reg6 LINENUM pat_end = pch_end(); 700 701 where--; 702 while (pch_char(new) == '=' || pch_char(new) == '\n') 703 new++; 704 705 while (old <= lastline) { 706 if (pch_char(old) == '-') { 707 copy_till(where + old - 1); 708 if (R_do_defines) { 709 if (def_state == OUTSIDE) { 710 fputs(not_defined, ofp); 711 def_state = IN_IFNDEF; 712 } 713 else if (def_state == IN_IFDEF) { 714 fputs(else_defined, ofp); 715 def_state = IN_ELSE; 716 } 717 fputs(pfetch(old), ofp); 718 } 719 last_frozen_line++; 720 old++; 721 } 722 else if (new > pat_end) { 723 break; 724 } 725 else if (pch_char(new) == '+') { 726 copy_till(where + old - 1); 727 if (R_do_defines) { 728 if (def_state == IN_IFNDEF) { 729 fputs(else_defined, ofp); 730 def_state = IN_ELSE; 731 } 732 else if (def_state == OUTSIDE) { 733 fputs(if_defined, ofp); 734 def_state = IN_IFDEF; 735 } 736 } 737 fputs(pfetch(new), ofp); 738 new++; 739 } 740 else if (pch_char(new) != pch_char(old)) { 741 say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n", 742 pch_hunk_beg() + old, 743 pch_hunk_beg() + new); 744 #ifdef DEBUGGING 745 say3("oldchar = '%c', newchar = '%c'\n", 746 pch_char(old), pch_char(new)); 747 #endif 748 my_exit(1); 749 } 750 else if (pch_char(new) == '!') { 751 copy_till(where + old - 1); 752 if (R_do_defines) { 753 fputs(not_defined, ofp); 754 def_state = IN_IFNDEF; 755 } 756 while (pch_char(old) == '!') { 757 if (R_do_defines) { 758 fputs(pfetch(old), ofp); 759 } 760 last_frozen_line++; 761 old++; 762 } 763 if (R_do_defines) { 764 fputs(else_defined, ofp); 765 def_state = IN_ELSE; 766 } 767 while (pch_char(new) == '!') { 768 fputs(pfetch(new), ofp); 769 new++; 770 } 771 } 772 else { 773 assert(pch_char(new) == ' '); 774 old++; 775 new++; 776 if (R_do_defines && def_state != OUTSIDE) { 777 fputs(end_defined, ofp); 778 def_state = OUTSIDE; 779 } 780 } 781 } 782 if (new <= pat_end && pch_char(new) == '+') { 783 copy_till(where + old - 1); 784 if (R_do_defines) { 785 if (def_state == OUTSIDE) { 786 fputs(if_defined, ofp); 787 def_state = IN_IFDEF; 788 } 789 else if (def_state == IN_IFNDEF) { 790 fputs(else_defined, ofp); 791 def_state = IN_ELSE; 792 } 793 } 794 while (new <= pat_end && pch_char(new) == '+') { 795 fputs(pfetch(new), ofp); 796 new++; 797 } 798 } 799 if (R_do_defines && def_state != OUTSIDE) { 800 fputs(end_defined, ofp); 801 } 802 } 803 804 /* Open the new file. */ 805 806 static void 807 init_output(name) 808 char *name; 809 { 810 ofp = fopen(name, "w"); 811 if (ofp == Nullfp) 812 pfatal2("can't create %s", name); 813 } 814 815 /* Open a file to put hunks we can't locate. */ 816 817 static void 818 init_reject(name) 819 char *name; 820 { 821 rejfp = fopen(name, "w"); 822 if (rejfp == Nullfp) 823 pfatal2("can't create %s", name); 824 } 825 826 /* Copy input file to output, up to wherever hunk is to be applied. */ 827 828 static void 829 copy_till(lastline) 830 Reg1 LINENUM lastline; 831 { 832 Reg2 LINENUM R_last_frozen_line = last_frozen_line; 833 834 if (R_last_frozen_line > lastline) 835 fatal1("misordered hunks! output would be garbled\n"); 836 while (R_last_frozen_line < lastline) { 837 dump_line(++R_last_frozen_line); 838 } 839 last_frozen_line = R_last_frozen_line; 840 } 841 842 /* Finish copying the input file to the output file. */ 843 844 static void 845 spew_output() 846 { 847 #ifdef DEBUGGING 848 if (debug & 256) 849 say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line); 850 #endif 851 if (input_lines) 852 copy_till(input_lines); /* dump remainder of file */ 853 Fclose(ofp); 854 ofp = Nullfp; 855 } 856 857 /* Copy one line from input to output. */ 858 859 static void 860 dump_line(line) 861 LINENUM line; 862 { 863 Reg1 char *s; 864 Reg2 char R_newline = '\n'; 865 866 /* Note: string is not null terminated. */ 867 for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ; 868 } 869 870 /* Does the patch pattern match at line base+offset? */ 871 872 static bool 873 patch_match(base, offset, fuzz) 874 LINENUM base; 875 LINENUM offset; 876 LINENUM fuzz; 877 { 878 Reg1 LINENUM pline = 1 + fuzz; 879 Reg2 LINENUM iline; 880 Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz; 881 882 for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) { 883 if (canonicalize) { 884 if (!similar(ifetch(iline, (offset >= 0)), 885 pfetch(pline), 886 pch_line_len(pline) )) 887 return FALSE; 888 } 889 else if (strnNE(ifetch(iline, (offset >= 0)), 890 pfetch(pline), 891 pch_line_len(pline) )) 892 return FALSE; 893 } 894 return TRUE; 895 } 896 897 /* Do two lines match with canonicalized white space? */ 898 899 static bool 900 similar(a,b,len) 901 Reg1 char *a; 902 Reg2 char *b; 903 Reg3 int len; 904 { 905 while (len) { 906 if (isspace((unsigned char)*b)) {/* whitespace (or \n) to match? */ 907 if (!isspace((unsigned char)*a))/* no corresponding whitespace? */ 908 return FALSE; 909 while (len && isspace((unsigned char)*b) && *b != '\n') 910 b++,len--; /* skip pattern whitespace */ 911 while (isspace((unsigned char)*a) && *a != '\n') 912 a++; /* skip target whitespace */ 913 if (*a == '\n' || *b == '\n') 914 return (*a == *b); /* should end in sync */ 915 } 916 else if (*a++ != *b++) /* match non-whitespace chars */ 917 return FALSE; 918 else 919 len--; /* probably not necessary */ 920 } 921 return TRUE; /* actually, this is not reached */ 922 /* since there is always a \n */ 923 } 924 925 /* Exit with cleanup. */ 926 927 void 928 my_exit(status) 929 int status; 930 { 931 Unlink(TMPINNAME); 932 if (!toutkeep) { 933 Unlink(TMPOUTNAME); 934 } 935 if (!trejkeep) { 936 Unlink(TMPREJNAME); 937 } 938 Unlink(TMPPATNAME); 939 exit(status); 940 } 941