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