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