1 #ifndef lint 2 static char sccsid[] = "@(#)patch.c 5.5 (Berkeley) 01/09/86"; 3 #endif not lint 4 5 /* patch - a program to apply diffs to original files 6 * 7 * $Header: patch.c,v 1.3 85/03/26 15:07:43 lwall Exp $ 8 * 9 * Copyright 1984, Larry Wall 10 * 11 * This program may be copied as long as you don't try to make any 12 * money off of it, or pretend that you wrote it. 13 * 14 * $Log: patch.c,v $ 15 * 85/08/15 van%ucbmonet@berkeley 16 * Changes for 4.3bsd diff -c. 17 * 18 * Revision 1.3 85/03/26 15:07:43 lwall 19 * Frozen. 20 * 21 * Revision 1.2.1.9 85/03/12 17:03:35 lwall 22 * Changed pfp->_file to fileno(pfp). 23 * 24 * Revision 1.2.1.8 85/03/12 16:30:43 lwall 25 * Check i_ptr and i_womp to make sure they aren't null before freeing. 26 * Also allow ed output to be suppressed. 27 * 28 * Revision 1.2.1.7 85/03/12 15:56:13 lwall 29 * Added -p option from jromine@uci-750a. 30 * 31 * Revision 1.2.1.6 85/03/12 12:12:51 lwall 32 * Now checks for normalness of file to patch. 33 * 34 * Revision 1.2.1.5 85/03/12 11:52:12 lwall 35 * Added -D (#ifdef) option from joe@fluke. 36 * 37 * Revision 1.2.1.4 84/12/06 11:14:15 lwall 38 * Made smarter about SCCS subdirectories. 39 * 40 * Revision 1.2.1.3 84/12/05 11:18:43 lwall 41 * Added -l switch to do loose string comparison. 42 * 43 * Revision 1.2.1.2 84/12/04 09:47:13 lwall 44 * Failed hunk count not reset on multiple patch file. 45 * 46 * Revision 1.2.1.1 84/12/04 09:42:37 lwall 47 * Branch for sdcrdcf changes. 48 * 49 * Revision 1.2 84/11/29 13:29:51 lwall 50 * Linted. Identifiers uniqified. Fixed i_ptr malloc() bug. Fixed 51 * multiple calls to mktemp(). Will now work on machines that can only 52 * read 32767 chars. Added -R option for diffs with new and old swapped. 53 * Various cosmetic changes. 54 * 55 * Revision 1.1 84/11/09 17:03:58 lwall 56 * Initial revision 57 * 58 */ 59 60 #define DEBUGGING 61 62 /* shut lint up about the following when return value ignored */ 63 64 #define Signal (void)signal 65 #define Unlink (void)unlink 66 #define Lseek (void)lseek 67 #define Fseek (void)fseek 68 #define Fstat (void)fstat 69 #define Pclose (void)pclose 70 #define Close (void)close 71 #define Fclose (void)fclose 72 #define Fflush (void)fflush 73 #define Sprintf (void)sprintf 74 #define Mktemp (void)mktemp 75 #define Strcpy (void)strcpy 76 #define Strcat (void)strcat 77 78 #include <stdio.h> 79 #include <assert.h> 80 #include <sys/types.h> 81 #include <sys/stat.h> 82 #include <ctype.h> 83 #include <signal.h> 84 85 /* constants */ 86 87 #define TRUE (1) 88 #define FALSE (0) 89 90 #define MAXHUNKSIZE 500 91 #define MAXLINELEN 1024 92 #define BUFFERSIZE 1024 93 #define ORIGEXT ".orig" 94 #define SCCSPREFIX "s." 95 #define GET "get -e %s" 96 #define RCSSUFFIX ",v" 97 #define CHECKOUT "co -l %s" 98 99 /* handy definitions */ 100 101 #define Null(t) ((t)0) 102 #define Nullch Null(char *) 103 #define Nullfp Null(FILE *) 104 105 #define Ctl(ch) (ch & 037) 106 107 #define strNE(s1,s2) (strcmp(s1,s2)) 108 #define strEQ(s1,s2) (!strcmp(s1,s2)) 109 #define strnNE(s1,s2,l) (strncmp(s1,s2,l)) 110 #define strnEQ(s1,s2,l) (!strncmp(s1,s2,l)) 111 112 /* typedefs */ 113 114 typedef char bool; 115 typedef long LINENUM; /* must be signed */ 116 typedef unsigned MEM; /* what to feed malloc */ 117 118 /* globals */ 119 120 int Argc; /* guess */ 121 char **Argv; 122 123 struct stat filestat; /* file statistics area */ 124 125 char serrbuf[BUFSIZ]; /* buffer for stderr */ 126 char buf[MAXLINELEN]; /* general purpose buffer */ 127 FILE *pfp = Nullfp; /* patch file pointer */ 128 FILE *ofp = Nullfp; /* output file pointer */ 129 FILE *rejfp = Nullfp; /* reject file pointer */ 130 131 LINENUM input_lines = 0; /* how long is input file in lines */ 132 LINENUM last_frozen_line = 0; /* how many input lines have been */ 133 /* irretractibly output */ 134 135 #define MAXFILEC 2 136 int filec = 0; /* how many file arguments? */ 137 char *filearg[MAXFILEC]; 138 139 char *outname = Nullch; 140 char rejname[128]; 141 142 char *origext = Nullch; 143 144 char TMPOUTNAME[] = "/tmp/patchoXXXXXX"; 145 char TMPINNAME[] = "/tmp/patchiXXXXXX"; /* you might want /usr/tmp here */ 146 char TMPREJNAME[] = "/tmp/patchrXXXXXX"; 147 char TMPPATNAME[] = "/tmp/patchpXXXXXX"; 148 149 LINENUM last_offset = 0; 150 #ifdef DEBUGGING 151 int debug = 0; 152 #endif 153 bool verbose = TRUE; 154 bool reverse = FALSE; 155 bool usepath = FALSE; 156 bool canonicalize = FALSE; 157 158 #define CONTEXT_DIFF 1 159 #define NORMAL_DIFF 2 160 #define ED_DIFF 3 161 #define NEW_CONTEXT_DIFF 4 162 int diff_type = 0; 163 164 int do_defines = 0; /* patch using ifdef, ifndef, etc. */ 165 char if_defined[128]; /* #ifdef xyzzy */ 166 char not_defined[128]; /* #ifndef xyzzy */ 167 char else_defined[] = "#else\n"; /* #else */ 168 char end_defined[128]; /* #endif xyzzy */ 169 170 char *revision = Nullch; /* prerequisite revision, if any */ 171 172 /* procedures */ 173 174 LINENUM locate_hunk(); 175 bool patch_match(); 176 bool similar(); 177 char *malloc(); 178 char *savestr(); 179 char *strcpy(); 180 char *strcat(); 181 char *sprintf(); /* usually */ 182 int my_exit(); 183 bool rev_in_string(); 184 char *fetchname(); 185 long atol(); 186 long lseek(); 187 char *mktemp(); 188 189 /* patch type */ 190 191 bool there_is_another_patch(); 192 bool another_hunk(); 193 char *pfetch(); 194 int pch_line_len(); 195 LINENUM pch_first(); 196 LINENUM pch_ptrn_lines(); 197 LINENUM pch_newfirst(); 198 LINENUM pch_repl_lines(); 199 LINENUM pch_end(); 200 LINENUM pch_context(); 201 LINENUM pch_hunk_beg(); 202 char pch_char(); 203 char *pfetch(); 204 char *pgets(); 205 206 /* input file type */ 207 208 char *ifetch(); 209 210 /* apply a context patch to a named file */ 211 212 main(argc,argv) 213 int argc; 214 char **argv; 215 { 216 LINENUM where; 217 int hunk = 0; 218 int failed = 0; 219 int i; 220 221 setbuf(stderr,serrbuf); 222 for (i = 0; i<MAXFILEC; i++) 223 filearg[i] = Nullch; 224 Mktemp(TMPOUTNAME); 225 Mktemp(TMPINNAME); 226 Mktemp(TMPREJNAME); 227 Mktemp(TMPPATNAME); 228 229 /* parse switches */ 230 Argc = argc; 231 Argv = argv; 232 get_some_switches(); 233 234 /* make sure we clean up /tmp in case of disaster */ 235 set_signals(); 236 237 for ( 238 open_patch_file(filearg[1]); 239 there_is_another_patch(); 240 reinitialize_almost_everything() 241 ) { /* for each patch in patch file */ 242 243 if (outname == Nullch) 244 outname = savestr(filearg[0]); 245 246 /* initialize the patched file */ 247 init_output(TMPOUTNAME); 248 249 /* for ed script just up and do it and exit */ 250 if (diff_type == ED_DIFF) { 251 do_ed_script(); 252 continue; 253 } 254 255 /* initialize reject file */ 256 init_reject(TMPREJNAME); 257 258 /* find out where all the lines are */ 259 scan_input(filearg[0]); 260 261 /* from here on, open no standard i/o files, because malloc */ 262 /* might misfire */ 263 264 /* apply each hunk of patch */ 265 hunk = 0; 266 failed = 0; 267 while (another_hunk()) { 268 hunk++; 269 where = locate_hunk(); 270 if (hunk == 1 && where == Null(LINENUM)) { 271 /* dwim for reversed patch? */ 272 pch_swap(); 273 reverse = !reverse; 274 where = locate_hunk(); /* try again */ 275 if (where == Null(LINENUM)) { 276 pch_swap(); /* no, put it back to normal */ 277 reverse = !reverse; 278 } 279 else { 280 say("%seversed (or previously applied) patch detected! %s -R.\n", 281 reverse ? "R" : "Unr", 282 reverse ? "Assuming" : "Ignoring"); 283 } 284 } 285 if (where == Null(LINENUM)) { 286 abort_hunk(); 287 failed++; 288 if (verbose) 289 say("Hunk #%d failed.\n",hunk); 290 } 291 else { 292 apply_hunk(where); 293 if (verbose) 294 if (last_offset) 295 say("Hunk #%d succeeded (offset %d line%s).\n", 296 hunk,last_offset,last_offset==1?"":"s"); 297 else 298 say("Hunk #%d succeeded.\n", hunk); 299 } 300 } 301 302 assert(hunk); 303 304 /* finish spewing out the new file */ 305 spew_output(); 306 307 /* and put the output where desired */ 308 ignore_signals(); 309 move_file(TMPOUTNAME,outname); 310 Fclose(rejfp); 311 rejfp = Nullfp; 312 if (failed) { 313 if (!*rejname) { 314 Strcpy(rejname, outname); 315 Strcat(rejname, ".rej"); 316 } 317 say("%d out of %d hunks failed--saving rejects to %s\n", 318 failed, hunk, rejname); 319 move_file(TMPREJNAME,rejname); 320 } 321 set_signals(); 322 } 323 my_exit(0); 324 } 325 326 reinitialize_almost_everything() 327 { 328 re_patch(); 329 re_input(); 330 331 input_lines = 0; 332 last_frozen_line = 0; 333 334 filec = 0; 335 if (filearg[0] != Nullch) { 336 free(filearg[0]); 337 filearg[0] = Nullch; 338 } 339 340 if (outname != Nullch) { 341 free(outname); 342 outname = Nullch; 343 } 344 345 last_offset = 0; 346 347 diff_type = 0; 348 349 if (revision != Nullch) { 350 free(revision); 351 revision = Nullch; 352 } 353 354 reverse = FALSE; 355 356 get_some_switches(); 357 358 if (filec >= 2) 359 fatal("You may not change to a different patch file.\n"); 360 } 361 362 get_some_switches() 363 { 364 register char *s; 365 366 rejname[0] = '\0'; 367 if (!Argc) 368 return; 369 for (Argc--,Argv++; Argc; Argc--,Argv++) { 370 s = Argv[0]; 371 if (strEQ(s,"+")) { 372 return; /* + will be skipped by for loop */ 373 } 374 if (*s != '-' || !s[1]) { 375 if (filec == MAXFILEC) 376 fatal("Too many file arguments.\n"); 377 filearg[filec++] = savestr(s); 378 } 379 else { 380 switch (*++s) { 381 case 'b': 382 origext = savestr(Argv[1]); 383 Argc--,Argv++; 384 break; 385 case 'c': 386 diff_type = CONTEXT_DIFF; 387 break; 388 case 'd': 389 if (chdir(Argv[1]) < 0) 390 fatal("Can't cd to %s.\n",Argv[1]); 391 Argc--,Argv++; 392 break; 393 case 'D': 394 do_defines++; 395 Sprintf(if_defined, "#ifdef %s\n", Argv[1]); 396 Sprintf(not_defined, "#ifndef %s\n", Argv[1]); 397 Sprintf(end_defined, "#endif %s\n", Argv[1]); 398 Argc--,Argv++; 399 break; 400 case 'e': 401 diff_type = ED_DIFF; 402 break; 403 case 'l': 404 canonicalize = TRUE; 405 break; 406 case 'n': 407 diff_type = NORMAL_DIFF; 408 break; 409 case 'o': 410 outname = savestr(Argv[1]); 411 Argc--,Argv++; 412 break; 413 case 'p': 414 usepath = TRUE; /* do not strip path names */ 415 break; 416 case 'r': 417 Strcpy(rejname,Argv[1]); 418 Argc--,Argv++; 419 break; 420 case 'R': 421 reverse = TRUE; 422 break; 423 case 's': 424 verbose = FALSE; 425 break; 426 #ifdef DEBUGGING 427 case 'x': 428 debug = atoi(s+1); 429 break; 430 #endif 431 default: 432 fatal("Unrecognized switch: %s\n",Argv[0]); 433 } 434 } 435 } 436 } 437 438 LINENUM 439 locate_hunk() 440 { 441 register LINENUM first_guess = pch_first() + last_offset; 442 register LINENUM offset; 443 LINENUM pat_lines = pch_ptrn_lines(); 444 register LINENUM max_pos_offset = input_lines - first_guess 445 - pat_lines + 1; 446 register LINENUM max_neg_offset = first_guess - last_frozen_line - 1 447 - pch_context(); 448 449 if (!pat_lines) /* null range matches always */ 450 return first_guess; 451 if (max_neg_offset >= first_guess) /* do not try lines < 0 */ 452 max_neg_offset = first_guess - 1; 453 if (first_guess <= input_lines && patch_match(first_guess,(LINENUM)0)) 454 return first_guess; 455 for (offset = 1; ; offset++) { 456 bool check_after = (offset <= max_pos_offset); 457 bool check_before = (offset <= max_pos_offset); 458 459 if (check_after && patch_match(first_guess,offset)) { 460 #ifdef DEBUGGING 461 if (debug & 1) 462 printf("Offset changing from %d to %d\n",last_offset,offset); 463 #endif 464 last_offset = offset; 465 return first_guess+offset; 466 } 467 else if (check_before && patch_match(first_guess,-offset)) { 468 #ifdef DEBUGGING 469 if (debug & 1) 470 printf("Offset changing from %d to %d\n",last_offset,-offset); 471 #endif 472 last_offset = -offset; 473 return first_guess-offset; 474 } 475 else if (!check_before && !check_after) 476 return Null(LINENUM); 477 } 478 } 479 480 /* we did not find the pattern, dump out the hunk so they can handle it */ 481 482 abort_hunk() 483 { 484 register LINENUM i; 485 register LINENUM pat_end = pch_end(); 486 /* add in last_offset to guess the same as the previous successful hunk */ 487 int oldfirst = pch_first() + last_offset; 488 int newfirst = pch_newfirst() + last_offset; 489 int oldlast = oldfirst + pch_ptrn_lines() - 1; 490 int newlast = newfirst + pch_repl_lines() - 1; 491 492 fprintf(rejfp,"***************\n"); 493 for (i=0; i<=pat_end; i++) { 494 switch (pch_char(i)) { 495 case '*': 496 fprintf(rejfp,"*** %d,%d\n", oldfirst, oldlast); 497 break; 498 case '=': 499 fprintf(rejfp,"--- %d,%d -----\n", newfirst, newlast); 500 break; 501 case '\n': 502 fprintf(rejfp,"%s", pfetch(i)); 503 break; 504 case ' ': case '-': case '+': case '!': 505 fprintf(rejfp,"%c %s", pch_char(i), pfetch(i)); 506 break; 507 default: 508 say("Fatal internal error in abort_hunk().\n"); 509 abort(); 510 } 511 } 512 } 513 514 /* we found where to apply it (we hope), so do it */ 515 516 apply_hunk(where) 517 LINENUM where; 518 { 519 register LINENUM old = 1; 520 register LINENUM lastline = pch_ptrn_lines(); 521 register LINENUM new = lastline+1; 522 register int def_state = 0; /* -1 = ifndef, 1 = ifdef */ 523 524 where--; 525 while (pch_char(new) == '=' || pch_char(new) == '\n') 526 new++; 527 528 while (old <= lastline) { 529 if (pch_char(old) == '-') { 530 copy_till(where + old - 1); 531 if (do_defines) { 532 if (def_state == 0) { 533 fputs(not_defined, ofp); 534 def_state = -1; 535 } else 536 if (def_state == 1) { 537 fputs(else_defined, ofp); 538 def_state = 2; 539 } 540 fputs(pfetch(old), ofp); 541 } 542 last_frozen_line++; 543 old++; 544 } 545 else if (pch_char(new) == '+') { 546 copy_till(where + old - 1); 547 if (do_defines) { 548 if (def_state == -1) { 549 fputs(else_defined, ofp); 550 def_state = 2; 551 } else 552 if (def_state == 0) { 553 fputs(if_defined, ofp); 554 def_state = 1; 555 } 556 } 557 fputs(pfetch(new),ofp); 558 new++; 559 } 560 else { 561 if (pch_char(new) != pch_char(old)) { 562 say("Out-of-sync patch, lines %d,%d\n", 563 pch_hunk_beg() + old - 1, 564 pch_hunk_beg() + new - 1); 565 #ifdef DEBUGGING 566 printf("oldchar = '%c', newchar = '%c'\n", 567 pch_char(old), pch_char(new)); 568 #endif 569 my_exit(1); 570 } 571 if (pch_char(new) == '!') { 572 copy_till(where + old - 1); 573 if (do_defines) { 574 fputs(not_defined,ofp); 575 def_state = -1; 576 } 577 while (pch_char(old) == '!') { 578 if (do_defines) { 579 fputs(pfetch(old),ofp); 580 } 581 last_frozen_line++; 582 old++; 583 } 584 if (do_defines) { 585 fputs(else_defined, ofp); 586 def_state = 2; 587 } 588 while (pch_char(new) == '!') { 589 fputs(pfetch(new),ofp); 590 new++; 591 } 592 if (do_defines) { 593 fputs(end_defined, ofp); 594 def_state = 0; 595 } 596 } 597 else { 598 assert(pch_char(new) == ' '); 599 old++; 600 new++; 601 } 602 } 603 } 604 if (new <= pch_end() && pch_char(new) == '+') { 605 copy_till(where + old - 1); 606 if (do_defines) { 607 if (def_state == 0) { 608 fputs(if_defined, ofp); 609 def_state = 1; 610 } else 611 if (def_state == -1) { 612 fputs(else_defined, ofp); 613 def_state = 2; 614 } 615 } 616 while (new <= pch_end() && pch_char(new) == '+') { 617 fputs(pfetch(new),ofp); 618 new++; 619 } 620 } 621 if (do_defines && def_state) { 622 fputs(end_defined, ofp); 623 } 624 } 625 626 do_ed_script() 627 { 628 FILE *pipefp, *popen(); 629 bool this_line_is_command = FALSE; 630 register char *t; 631 long beginning_of_this_line; 632 633 Unlink(TMPOUTNAME); 634 copy_file(filearg[0],TMPOUTNAME); 635 if (verbose) 636 Sprintf(buf,"/bin/ed %s",TMPOUTNAME); 637 else 638 Sprintf(buf,"/bin/ed - %s",TMPOUTNAME); 639 pipefp = popen(buf,"w"); 640 for (;;) { 641 beginning_of_this_line = ftell(pfp); 642 if (pgets(buf,sizeof buf,pfp) == Nullch) { 643 next_intuit_at(beginning_of_this_line); 644 break; 645 } 646 for (t=buf; isdigit(*t) || *t == ','; t++) ; 647 this_line_is_command = (isdigit(*buf) && 648 (*t == 'd' || *t == 'c' || *t == 'a') ); 649 if (this_line_is_command) { 650 fputs(buf,pipefp); 651 if (*t != 'd') { 652 while (pgets(buf,sizeof buf,pfp) != Nullch) { 653 fputs(buf,pipefp); 654 if (strEQ(buf,".\n")) 655 break; 656 } 657 } 658 } 659 else { 660 next_intuit_at(beginning_of_this_line); 661 break; 662 } 663 } 664 fprintf(pipefp,"w\n"); 665 fprintf(pipefp,"q\n"); 666 Fflush(pipefp); 667 Pclose(pipefp); 668 ignore_signals(); 669 move_file(TMPOUTNAME,outname); 670 set_signals(); 671 } 672 673 init_output(name) 674 char *name; 675 { 676 ofp = fopen(name,"w"); 677 if (ofp == Nullfp) 678 fatal("patch: can't create %s.\n",name); 679 } 680 681 init_reject(name) 682 char *name; 683 { 684 rejfp = fopen(name,"w"); 685 if (rejfp == Nullfp) 686 fatal("patch: can't create %s.\n",name); 687 } 688 689 move_file(from,to) 690 char *from, *to; 691 { 692 char bakname[512]; 693 register char *s; 694 int fromfd; 695 register int i; 696 697 /* to stdout? */ 698 699 if (strEQ(to,"-")) { 700 #ifdef DEBUGGING 701 if (debug & 4) 702 say("Moving %s to stdout.\n",from); 703 #endif 704 fromfd = open(from,0); 705 if (fromfd < 0) 706 fatal("patch: internal error, can't reopen %s\n",from); 707 while ((i=read(fromfd,buf,sizeof buf)) > 0) 708 if (write(1,buf,i) != 1) 709 fatal("patch: write failed\n"); 710 Close(fromfd); 711 return; 712 } 713 714 Strcpy(bakname,to); 715 Strcat(bakname,origext?origext:ORIGEXT); 716 if (stat(to,&filestat) >= 0) { /* output file exists */ 717 dev_t to_device = filestat.st_dev; 718 ino_t to_inode = filestat.st_ino; 719 char *simplename = bakname; 720 721 for (s=bakname; *s; s++) { 722 if (*s == '/') 723 simplename = s+1; 724 } 725 /* find a backup name that is not the same file */ 726 while (stat(bakname,&filestat) >= 0 && 727 to_device == filestat.st_dev && to_inode == filestat.st_ino) { 728 for (s=simplename; *s && !islower(*s); s++) ; 729 if (*s) 730 *s = toupper(*s); 731 else 732 Strcpy(simplename, simplename+1); 733 } 734 while (unlink(bakname) >= 0) ; /* while() is for benefit of Eunice */ 735 #ifdef DEBUGGING 736 if (debug & 4) 737 say("Moving %s to %s.\n",to,bakname); 738 #endif 739 if (link(to,bakname) < 0) { 740 say("patch: can't backup %s, output is in %s\n", 741 to,from); 742 return; 743 } 744 while (unlink(to) >= 0) ; 745 } 746 #ifdef DEBUGGING 747 if (debug & 4) 748 say("Moving %s to %s.\n",from,to); 749 #endif 750 if (link(from,to) < 0) { /* different file system? */ 751 int tofd; 752 753 tofd = creat(to,0666); 754 if (tofd < 0) { 755 say("patch: can't create %s, output is in %s.\n", 756 to, from); 757 return; 758 } 759 fromfd = open(from,0); 760 if (fromfd < 0) 761 fatal("patch: internal error, can't reopen %s\n",from); 762 while ((i=read(fromfd,buf,sizeof buf)) > 0) 763 if (write(tofd,buf,i) != i) 764 fatal("patch: write failed\n"); 765 Close(fromfd); 766 Close(tofd); 767 } 768 Unlink(from); 769 } 770 771 copy_file(from,to) 772 char *from, *to; 773 { 774 int tofd; 775 int fromfd; 776 register int i; 777 778 tofd = creat(to,0666); 779 if (tofd < 0) 780 fatal("patch: can't create %s.\n", to); 781 fromfd = open(from,0); 782 if (fromfd < 0) 783 fatal("patch: internal error, can't reopen %s\n",from); 784 while ((i=read(fromfd,buf,sizeof buf)) > 0) 785 if (write(tofd,buf,i) != i) 786 fatal("patch: write (%s) failed\n", to); 787 Close(fromfd); 788 Close(tofd); 789 } 790 791 copy_till(lastline) 792 register LINENUM lastline; 793 { 794 if (last_frozen_line > lastline) 795 say("patch: misordered hunks! output will be garbled.\n"); 796 while (last_frozen_line < lastline) { 797 dump_line(++last_frozen_line); 798 } 799 } 800 801 spew_output() 802 { 803 copy_till(input_lines); /* dump remainder of file */ 804 Fclose(ofp); 805 ofp = Nullfp; 806 } 807 808 dump_line(line) 809 LINENUM line; 810 { 811 register char *s; 812 813 for (s=ifetch(line,0); putc(*s,ofp) != '\n'; s++) ; 814 } 815 816 /* does the patch pattern match at line base+offset? */ 817 818 bool 819 patch_match(base,offset) 820 LINENUM base; 821 LINENUM offset; 822 { 823 register LINENUM pline; 824 register LINENUM iline; 825 register LINENUM pat_lines = pch_ptrn_lines(); 826 827 for (pline = 1, iline=base+offset; pline <= pat_lines; pline++,iline++) { 828 if (canonicalize) { 829 if (!similar(ifetch(iline,(offset >= 0)), 830 pfetch(pline), 831 pch_line_len(pline) )) 832 return FALSE; 833 } 834 else if (strnNE(ifetch(iline,(offset >= 0)), 835 pfetch(pline), 836 pch_line_len(pline) )) 837 return FALSE; 838 } 839 return TRUE; 840 } 841 842 /* match two lines with canonicalized white space */ 843 844 bool 845 similar(a,b,len) 846 register char *a, *b; 847 register int len; 848 { 849 while (len) { 850 if (isspace(*b)) { /* whitespace (or \n) to match? */ 851 if (!isspace(*a)) /* no corresponding whitespace? */ 852 return FALSE; 853 while (len && isspace(*b) && *b != '\n') 854 b++,len--; /* skip pattern whitespace */ 855 while (isspace(*a) && *a != '\n') 856 a++; /* skip target whitespace */ 857 if (*a == '\n' || *b == '\n') 858 return (*a == *b); /* should end in sync */ 859 } 860 else if (*a++ != *b++) /* match non-whitespace chars */ 861 return FALSE; 862 else 863 len--; /* probably not necessary */ 864 } 865 return TRUE; /* actually, this is not reached */ 866 /* since there is always a \n */ 867 } 868 869 /* input file with indexable lines abstract type */ 870 871 bool using_plan_a = TRUE; 872 static long i_size; /* size of the input file */ 873 static char *i_womp; /* plan a buffer for entire file */ 874 static char **i_ptr; /* pointers to lines in i_womp */ 875 876 static int tifd = -1; /* plan b virtual string array */ 877 static char *tibuf[2]; /* plan b buffers */ 878 static LINENUM tiline[2] = {-1,-1}; /* 1st line in each buffer */ 879 static LINENUM lines_per_buf; /* how many lines per buffer */ 880 static int tireclen; /* length of records in tmp file */ 881 882 re_input() 883 { 884 if (using_plan_a) { 885 i_size = 0; 886 /*NOSTRICT*/ 887 if (i_ptr != Null(char**)) 888 free((char *)i_ptr); 889 if (i_womp != Nullch) 890 free(i_womp); 891 i_womp = Nullch; 892 i_ptr = Null(char **); 893 } 894 else { 895 using_plan_a = TRUE; /* maybe the next one is smaller */ 896 Close(tifd); 897 tifd = -1; 898 free(tibuf[0]); 899 free(tibuf[1]); 900 tibuf[0] = tibuf[1] = Nullch; 901 tiline[0] = tiline[1] = -1; 902 tireclen = 0; 903 } 904 } 905 906 scan_input(filename) 907 char *filename; 908 { 909 bool plan_a(); 910 911 if (!plan_a(filename)) 912 plan_b(filename); 913 } 914 915 /* try keeping everything in memory */ 916 917 bool 918 plan_a(filename) 919 char *filename; 920 { 921 int ifd; 922 register char *s; 923 register LINENUM iline; 924 925 if (stat(filename,&filestat) < 0) { 926 Sprintf(buf,"RCS/%s%s",filename,RCSSUFFIX); 927 if (stat(buf,&filestat) >= 0 || stat(buf+4,&filestat) >= 0) { 928 Sprintf(buf,CHECKOUT,filename); 929 if (verbose) 930 say("Can't find %s--attempting to check it out from RCS.\n", 931 filename); 932 if (system(buf) || stat(filename,&filestat)) 933 fatal("Can't check out %s.\n",filename); 934 } 935 else { 936 Sprintf(buf,"SCCS/%s%s",SCCSPREFIX,filename); 937 if (stat(buf,&filestat) >= 0 || stat(buf+5,&filestat) >= 0) { 938 Sprintf(buf,GET,filename); 939 if (verbose) 940 say("Can't find %s--attempting to get it from SCCS.\n", 941 filename); 942 if (system(buf) || stat(filename,&filestat)) 943 fatal("Can't get %s.\n",filename); 944 } 945 else 946 fatal("Can't find %s.\n",filename); 947 } 948 } 949 if ((filestat.st_mode & S_IFMT) & ~S_IFREG) 950 fatal("%s is not a normal file--can't patch.\n",filename); 951 i_size = filestat.st_size; 952 /*NOSTRICT*/ 953 i_womp = malloc((MEM)(i_size+2)); 954 if (i_womp == Nullch) 955 return FALSE; 956 if ((ifd = open(filename,0)) < 0) 957 fatal("Can't open file %s\n",filename); 958 /*NOSTRICT*/ 959 if (read(ifd,i_womp,(int)i_size) != i_size) { 960 Close(ifd); 961 free(i_womp); 962 return FALSE; 963 } 964 Close(ifd); 965 if (i_womp[i_size-1] != '\n') 966 i_womp[i_size++] = '\n'; 967 i_womp[i_size] = '\0'; 968 969 /* count the lines in the buffer so we know how many pointers we need */ 970 971 iline = 0; 972 for (s=i_womp; *s; s++) { 973 if (*s == '\n') 974 iline++; 975 } 976 /*NOSTRICT*/ 977 i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *))); 978 if (i_ptr == Null(char **)) { /* shucks, it was a near thing */ 979 free((char *)i_womp); 980 return FALSE; 981 } 982 983 /* now scan the buffer and build pointer array */ 984 985 iline = 1; 986 i_ptr[iline] = i_womp; 987 for (s=i_womp; *s; s++) { 988 if (*s == '\n') 989 i_ptr[++iline] = s+1; /* these are NOT null terminated */ 990 } 991 input_lines = iline - 1; 992 993 /* now check for revision, if any */ 994 995 if (revision != Nullch) { 996 if (!rev_in_string(i_womp)) { 997 ask("This file doesn't appear to be the %s version--patch anyway? [n] ", 998 revision); 999 if (*buf != 'y') 1000 fatal("Aborted.\n"); 1001 } 1002 else if (verbose) 1003 say("Good. This file appears to be the %s version.\n", 1004 revision); 1005 } 1006 return TRUE; /* plan a will work */ 1007 } 1008 1009 /* keep (virtually) nothing in memory */ 1010 1011 plan_b(filename) 1012 char *filename; 1013 { 1014 FILE *ifp; 1015 register int i = 0; 1016 register int maxlen = 1; 1017 bool found_revision = (revision == Nullch); 1018 1019 using_plan_a = FALSE; 1020 if ((ifp = fopen(filename,"r")) == Nullfp) 1021 fatal("Can't open file %s\n",filename); 1022 if ((tifd = creat(TMPINNAME,0666)) < 0) 1023 fatal("Can't open file %s\n",TMPINNAME); 1024 while (fgets(buf,sizeof buf, ifp) != Nullch) { 1025 if (revision != Nullch && !found_revision && rev_in_string(buf)) 1026 found_revision = TRUE; 1027 if ((i = strlen(buf)) > maxlen) 1028 maxlen = i; /* find longest line */ 1029 } 1030 if (revision != Nullch) { 1031 if (!found_revision) { 1032 ask("This file doesn't appear to be the %s version--patch anyway? [n] ", 1033 revision); 1034 if (*buf != 'y') 1035 fatal("Aborted.\n"); 1036 } 1037 else if (verbose) 1038 say("Good. This file appears to be the %s version.\n", 1039 revision); 1040 } 1041 Fseek(ifp,0L,0); /* rewind file */ 1042 lines_per_buf = BUFFERSIZE / maxlen; 1043 tireclen = maxlen; 1044 tibuf[0] = malloc((MEM)(BUFFERSIZE + 1)); 1045 tibuf[1] = malloc((MEM)(BUFFERSIZE + 1)); 1046 if (tibuf[1] == Nullch) 1047 fatal("Can't seem to get enough memory.\n"); 1048 for (i=1; ; i++) { 1049 if (! (i % lines_per_buf)) /* new block */ 1050 if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE) 1051 fatal("patch: can't write temp file.\n"); 1052 if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp) 1053 == Nullch) { 1054 input_lines = i - 1; 1055 if (i % lines_per_buf) 1056 if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE) 1057 fatal("patch: can't write temp file.\n"); 1058 break; 1059 } 1060 } 1061 Fclose(ifp); 1062 Close(tifd); 1063 if ((tifd = open(TMPINNAME,0)) < 0) { 1064 fatal("Can't reopen file %s\n",TMPINNAME); 1065 } 1066 } 1067 1068 /* fetch a line from the input file, \n terminated, not necessarily \0 */ 1069 char * 1070 ifetch(line,whichbuf) 1071 register LINENUM line; 1072 int whichbuf; /* ignored when file in memory */ 1073 { 1074 if (line < 1 || line > input_lines) 1075 return ""; 1076 if (using_plan_a) 1077 return i_ptr[line]; 1078 else { 1079 LINENUM offline = line % lines_per_buf; 1080 LINENUM baseline = line - offline; 1081 1082 if (tiline[0] == baseline) 1083 whichbuf = 0; 1084 else if (tiline[1] == baseline) 1085 whichbuf = 1; 1086 else { 1087 tiline[whichbuf] = baseline; 1088 Lseek(tifd,(long)baseline / lines_per_buf * BUFFERSIZE,0); 1089 if (read(tifd,tibuf[whichbuf],BUFFERSIZE) < 0) 1090 fatal("Error reading tmp file %s.\n",TMPINNAME); 1091 } 1092 return tibuf[whichbuf] + (tireclen*offline); 1093 } 1094 } 1095 1096 /* patch abstract type */ 1097 1098 static long p_filesize; /* size of the patch file */ 1099 static LINENUM p_first; /* 1st line number */ 1100 static LINENUM p_newfirst; /* 1st line number of replacement */ 1101 static LINENUM p_ptrn_lines; /* # lines in pattern */ 1102 static LINENUM p_repl_lines; /* # lines in replacement text */ 1103 static LINENUM p_end = -1; /* last line in hunk */ 1104 static LINENUM p_max; /* max allowed value of p_end */ 1105 static LINENUM p_context = 3; /* # of context lines */ 1106 static LINENUM p_input_line = 0; /* current line # from patch file */ 1107 static char *p_line[MAXHUNKSIZE]; /* the text of the hunk */ 1108 static char p_char[MAXHUNKSIZE]; /* +, -, and ! */ 1109 static int p_len[MAXHUNKSIZE]; /* length of each line */ 1110 static int p_indent; /* indent to patch */ 1111 static long p_base; /* where to intuit this time */ 1112 static long p_start; /* where intuit found a patch */ 1113 1114 re_patch() 1115 { 1116 p_first = (LINENUM)0; 1117 p_newfirst = (LINENUM)0; 1118 p_ptrn_lines = (LINENUM)0; 1119 p_repl_lines = (LINENUM)0; 1120 p_end = (LINENUM)-1; 1121 p_max = (LINENUM)0; 1122 p_indent = 0; 1123 } 1124 1125 open_patch_file(filename) 1126 char *filename; 1127 { 1128 if (filename == Nullch || !*filename || strEQ(filename,"-")) { 1129 pfp = fopen(TMPPATNAME,"w"); 1130 if (pfp == Nullfp) 1131 fatal("patch: can't create %s.\n",TMPPATNAME); 1132 while (fgets(buf,sizeof buf,stdin) != NULL) 1133 fputs(buf,pfp); 1134 Fclose(pfp); 1135 filename = TMPPATNAME; 1136 } 1137 pfp = fopen(filename,"r"); 1138 if (pfp == Nullfp) 1139 fatal("patch file %s not found\n",filename); 1140 Fstat(fileno(pfp), &filestat); 1141 p_filesize = filestat.st_size; 1142 next_intuit_at(0L); /* start at the beginning */ 1143 } 1144 1145 bool 1146 there_is_another_patch() 1147 { 1148 bool no_input_file = (filearg[0] == Nullch); 1149 1150 if (p_base != 0L && p_base >= p_filesize) { 1151 if (verbose) 1152 say("done\n"); 1153 return FALSE; 1154 } 1155 if (verbose) 1156 say("Hmm..."); 1157 diff_type = intuit_diff_type(); 1158 if (!diff_type) { 1159 if (p_base != 0L) { 1160 if (verbose) 1161 say(" Ignoring the trailing garbage.\ndone\n"); 1162 } 1163 else 1164 say(" I can't seem to find a patch in there anywhere.\n"); 1165 return FALSE; 1166 } 1167 if (verbose) 1168 say(" %sooks like %s to me...\n", 1169 (p_base == 0L ? "L" : "The next patch l"), 1170 diff_type == CONTEXT_DIFF ? "a context diff" : 1171 diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : 1172 diff_type == NORMAL_DIFF ? "a normal diff" : 1173 "an ed script" ); 1174 if (p_indent && verbose) 1175 say("(Patch is indented %d space%s.)\n",p_indent,p_indent==1?"":"s"); 1176 skip_to(p_start); 1177 if (no_input_file) { 1178 if (filearg[0] == Nullch) { 1179 ask("File to patch: "); 1180 filearg[0] = fetchname(buf); 1181 } 1182 else if (verbose) { 1183 say("Patching file %s...\n",filearg[0]); 1184 } 1185 } 1186 return TRUE; 1187 } 1188 1189 intuit_diff_type() 1190 { 1191 long this_line = 0; 1192 long previous_line; 1193 long first_command_line = -1; 1194 bool last_line_was_command = FALSE; 1195 bool this_line_is_command = FALSE; 1196 bool last_line_was_stars = FALSE; 1197 bool this_line_is_stars = FALSE; 1198 register int indent; 1199 register char *s, *t; 1200 char *oldname = Nullch; 1201 char *newname = Nullch; 1202 bool no_filearg = (filearg[0] == Nullch); 1203 1204 Fseek(pfp,p_base,0); 1205 for (;;) { 1206 previous_line = this_line; 1207 last_line_was_command = this_line_is_command; 1208 last_line_was_stars = this_line_is_stars; 1209 this_line = ftell(pfp); 1210 indent = 0; 1211 if (fgets(buf,sizeof buf,pfp) == Nullch) { 1212 if (first_command_line >= 0L) { 1213 /* nothing but deletes!? */ 1214 p_start = first_command_line; 1215 return ED_DIFF; 1216 } 1217 else { 1218 p_start = this_line; 1219 return 0; 1220 } 1221 } 1222 for (s = buf; *s == ' ' || *s == '\t'; s++) { 1223 if (*s == '\t') 1224 indent += 8 - (indent % 8); 1225 else 1226 indent++; 1227 } 1228 for (t=s; isdigit(*t) || *t == ','; t++) ; 1229 this_line_is_command = (isdigit(*s) && 1230 (*t == 'd' || *t == 'c' || *t == 'a') ); 1231 if (first_command_line < 0L && this_line_is_command) { 1232 first_command_line = this_line; 1233 p_indent = indent; /* assume this for now */ 1234 } 1235 if (strnEQ(s,"*** ",4)) 1236 oldname = fetchname(s+4); 1237 else if (strnEQ(s,"--- ",4)) { 1238 newname = fetchname(s+4); 1239 if (no_filearg) { 1240 if (oldname && newname) { 1241 if (strlen(oldname) < strlen(newname)) 1242 filearg[0] = oldname; 1243 else 1244 filearg[0] = newname; 1245 } 1246 else if (oldname) 1247 filearg[0] = oldname; 1248 else if (newname) 1249 filearg[0] = newname; 1250 } 1251 } 1252 else if (strnEQ(s,"Index:",6)) { 1253 if (no_filearg) 1254 filearg[0] = fetchname(s+6); 1255 /* this filearg might get limboed */ 1256 } 1257 else if (strnEQ(s,"Prereq:",7)) { 1258 for (t=s+7; isspace(*t); t++) ; 1259 revision = savestr(t); 1260 for (t=revision; *t && !isspace(*t); t++) ; 1261 *t = '\0'; 1262 if (!*revision) { 1263 free(revision); 1264 revision = Nullch; 1265 } 1266 } 1267 if ((!diff_type || diff_type == ED_DIFF) && 1268 first_command_line >= 0L && 1269 strEQ(s,".\n") ) { 1270 p_indent = indent; 1271 p_start = first_command_line; 1272 return ED_DIFF; 1273 } 1274 this_line_is_stars = strnEQ(s,"********",8); 1275 if ((!diff_type || diff_type == CONTEXT_DIFF) && last_line_was_stars && 1276 strnEQ(s,"*** ",4)) { 1277 /* if this is a new context diff the character just before */ 1278 /* the newline is a '*'. */ 1279 while (*s != '\n') 1280 s++; 1281 p_indent = indent; 1282 p_start = previous_line; 1283 return (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); 1284 } 1285 if ((!diff_type || diff_type == NORMAL_DIFF) && 1286 last_line_was_command && 1287 (strnEQ(s,"< ",2) || strnEQ(s,"> ",2)) ) { 1288 p_start = previous_line; 1289 p_indent = indent; 1290 return NORMAL_DIFF; 1291 } 1292 } 1293 } 1294 1295 char * 1296 fetchname(at) 1297 char *at; 1298 { 1299 char *s = savestr(at); 1300 char *name; 1301 register char *t; 1302 char tmpbuf[200]; 1303 1304 for (t=s; isspace(*t); t++) ; 1305 name = t; 1306 for (; *t && !isspace(*t); t++) 1307 if (!usepath) 1308 if (*t == '/') 1309 name = t+1; 1310 *t = '\0'; 1311 name = savestr(name); 1312 Sprintf(tmpbuf,"RCS/%s",name); 1313 free(s); 1314 if (stat(name,&filestat) < 0) { 1315 Strcat(tmpbuf,RCSSUFFIX); 1316 if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+4,&filestat) < 0) { 1317 Sprintf(tmpbuf,"SCCS/%s%s",SCCSPREFIX,name); 1318 if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+5,&filestat) < 0) { 1319 free(name); 1320 name = Nullch; 1321 } 1322 } 1323 } 1324 return name; 1325 } 1326 1327 next_intuit_at(file_pos) 1328 long file_pos; 1329 { 1330 p_base = file_pos; 1331 } 1332 1333 skip_to(file_pos) 1334 long file_pos; 1335 { 1336 char *ret; 1337 1338 assert(p_base <= file_pos); 1339 if (verbose && p_base < file_pos) { 1340 Fseek(pfp,p_base,0); 1341 say("The text leading up to this was:\n--------------------------\n"); 1342 while (ftell(pfp) < file_pos) { 1343 ret = fgets(buf,sizeof buf,pfp); 1344 assert(ret != Nullch); 1345 say("|%s",buf); 1346 } 1347 say("--------------------------\n"); 1348 } 1349 else 1350 Fseek(pfp,file_pos,0); 1351 } 1352 1353 bool 1354 another_hunk() 1355 { 1356 register char *s; 1357 char *ret; 1358 register int context = 0; 1359 1360 while (p_end >= 0) { 1361 free(p_line[p_end--]); 1362 } 1363 assert(p_end == -1); 1364 1365 p_max = MAXHUNKSIZE; /* gets reduced when --- found */ 1366 if (diff_type == CONTEXT_DIFF) { 1367 long line_beginning = ftell(pfp); 1368 LINENUM repl_beginning = 0; 1369 1370 ret = pgets(buf,sizeof buf, pfp); 1371 if (ret == Nullch || strnNE(buf,"********",8)) { 1372 next_intuit_at(line_beginning); 1373 return FALSE; 1374 } 1375 p_context = 100; 1376 while (p_end < p_max) { 1377 ret = pgets(buf,sizeof buf, pfp); 1378 if (ret == Nullch) { 1379 if (p_max - p_end < 4) 1380 Strcpy(buf," \n"); /* assume blank lines got chopped */ 1381 else 1382 fatal("Unexpected end of file in patch.\n"); 1383 } 1384 p_input_line++; 1385 if (strnEQ(buf,"********",8)) 1386 fatal("Unexpected end of hunk at line %d.\n", 1387 p_input_line); 1388 p_char[++p_end] = *buf; 1389 switch (*buf) { 1390 case '*': 1391 if (p_end != 0) 1392 fatal("Unexpected *** at line %d: %s", p_input_line, buf); 1393 context = 0; 1394 p_line[p_end] = savestr(buf); 1395 for (s=buf; *s && !isdigit(*s); s++) ; 1396 p_first = (LINENUM) atol(s); 1397 while (isdigit(*s)) s++; 1398 for (; *s && !isdigit(*s); s++) ; 1399 p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1; 1400 break; 1401 case '-': 1402 if (buf[1] == '-') { 1403 if (p_end != p_ptrn_lines + 1 && 1404 p_end != p_ptrn_lines + 2) 1405 fatal("Unexpected --- at line %d: %s", 1406 p_input_line,buf); 1407 repl_beginning = p_end; 1408 context = 0; 1409 p_line[p_end] = savestr(buf); 1410 p_char[p_end] = '='; 1411 for (s=buf; *s && !isdigit(*s); s++) ; 1412 p_newfirst = (LINENUM) atol(s); 1413 while (isdigit(*s)) s++; 1414 for (; *s && !isdigit(*s); s++) ; 1415 p_max = ((LINENUM)atol(s)) - p_newfirst + 1 + p_end; 1416 break; 1417 } 1418 /* FALL THROUGH */ 1419 case '+': case '!': 1420 if (context > 0) { 1421 if (context < p_context) 1422 p_context = context; 1423 context = -100; 1424 } 1425 p_line[p_end] = savestr(buf+2); 1426 break; 1427 case '\t': case '\n': /* assume the 2 spaces got eaten */ 1428 p_line[p_end] = savestr(buf); 1429 if (p_end != p_ptrn_lines + 1) { 1430 context++; 1431 p_char[p_end] = ' '; 1432 } 1433 break; 1434 case ' ': 1435 context++; 1436 p_line[p_end] = savestr(buf+2); 1437 break; 1438 default: 1439 fatal("Malformed patch at line %d: %s",p_input_line,buf); 1440 } 1441 p_len[p_end] = 0; 1442 if (p_line[p_end] != 0) 1443 p_len[p_end] = strlen(p_line[p_end]); 1444 /* for strncmp() so we do not have */ 1445 /* to assume null termination */ 1446 } 1447 if (p_end >=0 && !p_ptrn_lines) 1448 fatal("No --- found in patch at line %d\n", pch_hunk_beg()); 1449 p_repl_lines = p_end - repl_beginning; 1450 } 1451 else if (diff_type == NEW_CONTEXT_DIFF) { 1452 long line_beginning = ftell(pfp); 1453 LINENUM repl_beginning = 0; 1454 LINENUM fillcnt = 0; 1455 LINENUM fillsrc; 1456 LINENUM filldst; 1457 1458 ret = pgets(buf,sizeof buf, pfp); 1459 if (ret == Nullch || strnNE(buf,"********",8)) { 1460 next_intuit_at(line_beginning); 1461 return FALSE; 1462 } 1463 p_context = 0; 1464 while (p_end < p_max) { 1465 line_beginning = ftell(pfp); 1466 ret = pgets(buf,sizeof buf, pfp); 1467 if (ret == Nullch) { 1468 if (p_max - p_end < 4) 1469 Strcpy(buf," \n"); /* assume blank lines got chopped */ 1470 else if (p_end == repl_beginning) { 1471 /* redundant 'new' context lines were omitted - set up */ 1472 /* to fill them in from the the old file's context */ 1473 fillsrc = 1; 1474 filldst = p_end + 1; 1475 fillcnt = p_max - repl_beginning; 1476 p_end = p_max; 1477 break; 1478 } else 1479 fatal("Unexpected end of file in patch.\n"); 1480 } 1481 p_input_line++; 1482 p_char[++p_end] = *buf; 1483 switch (*buf) { 1484 case '*': 1485 if (strnEQ(buf,"********",8)) { 1486 if (p_end != repl_beginning + 1) 1487 fatal("Unexpected end of hunk at line %d.\n", 1488 p_input_line); 1489 /* redundant 'new' context lines were omitted - set up */ 1490 /* to fill them in from the the old file's context */ 1491 fillsrc = 1; 1492 filldst = p_end; 1493 fillcnt = p_max - repl_beginning; 1494 p_end = p_max; 1495 Fseek(pfp, line_beginning, 0); /* backup the diff input */ 1496 break; 1497 } 1498 if (p_end != 0) 1499 fatal("Unexpected *** at line %d: %s", p_input_line, buf); 1500 context = 0; 1501 p_line[p_end] = savestr(buf); 1502 for (s=buf; *s && !isdigit(*s); s++) ; 1503 p_first = (LINENUM) atol(s); 1504 while (isdigit(*s)) s++; 1505 for (; *s && !isdigit(*s); s++) ; 1506 p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1; 1507 break; 1508 case '-': 1509 if (buf[1] == '-') { 1510 if (p_end != p_ptrn_lines + 1) { 1511 if (p_end == 1) { 1512 /* `old' lines were omitted - set up to fill them */ 1513 /* in from 'new' context lines. */ 1514 p_end = p_ptrn_lines + 1; 1515 fillsrc = p_end + 1; 1516 filldst = 1; 1517 fillcnt = p_ptrn_lines; 1518 } else 1519 fatal("Unexpected --- at line %d: %s", 1520 p_input_line,buf); 1521 } 1522 repl_beginning = p_end; 1523 p_line[p_end] = savestr(buf); 1524 p_char[p_end] = '='; 1525 for (s=buf; *s && !isdigit(*s); s++) ; 1526 p_newfirst = (LINENUM) atol(s); 1527 while (isdigit(*s)) s++; 1528 for (; *s && !isdigit(*s); s++) ; 1529 p_max = ((LINENUM)atol(s)) - p_newfirst + 1 + p_end; 1530 break; 1531 } 1532 /* FALL THROUGH */ 1533 case '+': case '!': 1534 if (context > 0 && p_context == 0) { 1535 p_context = context; 1536 } 1537 p_line[p_end] = savestr(buf+2); 1538 break; 1539 case '\t': case '\n': /* assume the 2 spaces got eaten */ 1540 p_line[p_end] = savestr(buf); 1541 if (p_end != p_ptrn_lines + 1) { 1542 context++; 1543 p_char[p_end] = ' '; 1544 } 1545 break; 1546 case ' ': 1547 context++; 1548 p_line[p_end] = savestr(buf+2); 1549 break; 1550 default: 1551 fatal("Malformed patch at line %d: %s",p_input_line,buf); 1552 } 1553 p_len[p_end] = 0; 1554 if (p_line[p_end] != 0) 1555 p_len[p_end] = strlen(p_line[p_end]); 1556 /* for strncmp() so we do not have */ 1557 /* to assume null termination */ 1558 } 1559 if (p_end >=0 && !p_ptrn_lines) 1560 fatal("No --- found in patch at line %d\n", pch_hunk_beg()); 1561 1562 /* if there were omitted context lines, fill them in */ 1563 if (fillcnt) { 1564 while (fillcnt-- > 0) { 1565 while (p_char[fillsrc] != ' ') 1566 fillsrc++; 1567 p_line[filldst] = p_line[fillsrc]; 1568 p_char[filldst] = p_char[fillsrc]; 1569 p_len[filldst] = p_len[fillsrc]; 1570 fillsrc++; filldst++; 1571 } 1572 assert(fillsrc==p_end+1 || fillsrc==repl_beginning); 1573 assert(filldst==p_end+1 || filldst==repl_beginning); 1574 } 1575 p_repl_lines = p_end - repl_beginning; 1576 } 1577 else { /* normal diff--fake it up */ 1578 char hunk_type; 1579 register int i; 1580 LINENUM min, max; 1581 long line_beginning = ftell(pfp); 1582 1583 p_context = 0; 1584 ret = pgets(buf,sizeof buf, pfp); 1585 p_input_line++; 1586 if (ret == Nullch || !isdigit(*buf)) { 1587 next_intuit_at(line_beginning); 1588 return FALSE; 1589 } 1590 p_first = (LINENUM)atol(buf); 1591 for (s=buf; isdigit(*s); s++) ; 1592 if (*s == ',') { 1593 p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1; 1594 while (isdigit(*s)) s++; 1595 } 1596 else 1597 p_ptrn_lines = (*s != 'a'); 1598 hunk_type = *s; 1599 if (hunk_type == 'a') 1600 p_first++; /* do append rather than insert */ 1601 min = (LINENUM)atol(++s); 1602 for (; isdigit(*s); s++) ; 1603 if (*s == ',') 1604 max = (LINENUM)atol(++s); 1605 else 1606 max = min; 1607 if (hunk_type == 'd') 1608 min++; 1609 p_end = p_ptrn_lines + 1 + max - min + 1; 1610 p_newfirst = min; 1611 p_repl_lines = max - min + 1; 1612 Sprintf(buf,"*** %d,%d\n", p_first, p_first + p_ptrn_lines - 1); 1613 p_line[0] = savestr(buf); 1614 p_char[0] = '*'; 1615 for (i=1; i<=p_ptrn_lines; i++) { 1616 ret = pgets(buf,sizeof buf, pfp); 1617 p_input_line++; 1618 if (ret == Nullch) 1619 fatal("Unexpected end of file in patch at line %d.\n", 1620 p_input_line); 1621 if (*buf != '<') 1622 fatal("< expected at line %d of patch.\n", p_input_line); 1623 p_line[i] = savestr(buf+2); 1624 p_len[i] = 0; 1625 if (p_line[i] != 0) 1626 p_len[i] = strlen(p_line[i]); 1627 p_char[i] = '-'; 1628 } 1629 if (hunk_type == 'c') { 1630 ret = pgets(buf,sizeof buf, pfp); 1631 p_input_line++; 1632 if (ret == Nullch) 1633 fatal("Unexpected end of file in patch at line %d.\n", 1634 p_input_line); 1635 if (*buf != '-') 1636 fatal("--- expected at line %d of patch.\n", p_input_line); 1637 } 1638 Sprintf(buf,"--- %d,%d\n",min,max); 1639 p_line[i] = savestr(buf); 1640 p_char[i] = '='; 1641 for (i++; i<=p_end; i++) { 1642 ret = pgets(buf,sizeof buf, pfp); 1643 p_input_line++; 1644 if (ret == Nullch) 1645 fatal("Unexpected end of file in patch at line %d.\n", 1646 p_input_line); 1647 if (*buf != '>') 1648 fatal("> expected at line %d of patch.\n", p_input_line); 1649 p_line[i] = savestr(buf+2); 1650 p_len[i] = 0; 1651 if (p_line[i] != 0) 1652 p_len[i] = strlen(p_line[i]); 1653 p_char[i] = '+'; 1654 } 1655 } 1656 if (reverse) /* backwards patch? */ 1657 pch_swap(); 1658 #ifdef DEBUGGING 1659 if (debug & 2) { 1660 int i; 1661 char special; 1662 1663 for (i=0; i <= p_end; i++) { 1664 if (i == p_ptrn_lines) 1665 special = '^'; 1666 else 1667 special = ' '; 1668 printf("%3d %c %c %s",i,p_char[i],special,p_line[i]); 1669 } 1670 } 1671 #endif 1672 return TRUE; 1673 } 1674 1675 char * 1676 pgets(bf,sz,fp) 1677 char *bf; 1678 int sz; 1679 FILE *fp; 1680 { 1681 char *ret = fgets(bf,sz,fp); 1682 register char *s; 1683 register int indent = 0; 1684 1685 if (p_indent && ret != Nullch) { 1686 for (s=buf; indent < p_indent && (*s == ' ' || *s == '\t'); s++) { 1687 if (*s == '\t') 1688 indent += 8 - (indent % 7); 1689 else 1690 indent++; 1691 } 1692 if (buf != s) 1693 Strcpy(buf,s); 1694 } 1695 return ret; 1696 } 1697 1698 pch_swap() 1699 { 1700 char *tp_line[MAXHUNKSIZE]; /* the text of the hunk */ 1701 char tp_char[MAXHUNKSIZE]; /* +, -, and ! */ 1702 int tp_len[MAXHUNKSIZE]; /* length of each line */ 1703 register LINENUM i, n; 1704 bool blankline = FALSE; 1705 register char *s; 1706 1707 i = p_first; 1708 p_first = p_newfirst; 1709 p_newfirst = i; 1710 1711 /* make a scratch copy */ 1712 1713 for (i=0; i<=p_end; i++) { 1714 tp_line[i] = p_line[i]; 1715 tp_char[i] = p_char[i]; 1716 tp_len[i] = p_len[i]; 1717 } 1718 1719 /* now turn the new into the old */ 1720 1721 i = p_ptrn_lines + 1; 1722 if (tp_char[i] == '\n') { /* account for possible blank line */ 1723 blankline = TRUE; 1724 i++; 1725 } 1726 for (n=0; i <= p_end; i++,n++) { 1727 p_line[n] = tp_line[i]; 1728 p_char[n] = tp_char[i]; 1729 if (p_char[n] == '+') 1730 p_char[n] = '-'; 1731 p_len[n] = tp_len[i]; 1732 } 1733 if (blankline) { 1734 i = p_ptrn_lines + 1; 1735 p_line[n] = tp_line[i]; 1736 p_char[n] = tp_char[i]; 1737 p_len[n] = tp_len[i]; 1738 n++; 1739 } 1740 assert(p_char[0] == '='); 1741 p_char[0] = '*'; 1742 for (s=p_line[0]; *s; s++) 1743 if (*s == '-') 1744 *s = '*'; 1745 1746 /* now turn the old into the new */ 1747 1748 assert(tp_char[0] == '*'); 1749 tp_char[0] = '='; 1750 for (s=tp_line[0]; *s; s++) 1751 if (*s == '*') 1752 *s = '-'; 1753 for (i=0; n <= p_end; i++,n++) { 1754 p_line[n] = tp_line[i]; 1755 p_char[n] = tp_char[i]; 1756 if (p_char[n] == '-') 1757 p_char[n] = '+'; 1758 p_len[n] = tp_len[i]; 1759 } 1760 assert(i == p_ptrn_lines + 1); 1761 i = p_ptrn_lines; 1762 p_ptrn_lines = p_repl_lines; 1763 p_repl_lines = i; 1764 } 1765 1766 LINENUM 1767 pch_first() 1768 { 1769 return p_first; 1770 } 1771 1772 LINENUM 1773 pch_ptrn_lines() 1774 { 1775 return p_ptrn_lines; 1776 } 1777 1778 LINENUM 1779 pch_newfirst() 1780 { 1781 return p_newfirst; 1782 } 1783 1784 LINENUM 1785 pch_repl_lines() 1786 { 1787 return p_repl_lines; 1788 } 1789 1790 LINENUM 1791 pch_end() 1792 { 1793 return p_end; 1794 } 1795 1796 LINENUM 1797 pch_context() 1798 { 1799 return p_context; 1800 } 1801 1802 pch_line_len(line) 1803 LINENUM line; 1804 { 1805 return p_len[line]; 1806 } 1807 1808 char 1809 pch_char(line) 1810 LINENUM line; 1811 { 1812 return p_char[line]; 1813 } 1814 1815 char * 1816 pfetch(line) 1817 LINENUM line; 1818 { 1819 return p_line[line]; 1820 } 1821 1822 LINENUM 1823 pch_hunk_beg() 1824 { 1825 return p_input_line - p_end - 1; 1826 } 1827 1828 char * 1829 savestr(s) 1830 register char *s; 1831 { 1832 register char *rv, 1833 *t; 1834 1835 t = s; 1836 while (*t++); 1837 rv = malloc((MEM) (t - s)); 1838 if (rv == NULL) 1839 fatal ("patch: out of memory (savestr)\n"); 1840 t = rv; 1841 while (*t++ = *s++); 1842 return rv; 1843 } 1844 1845 my_exit(status) 1846 int status; 1847 { 1848 Unlink(TMPINNAME); 1849 Unlink(TMPOUTNAME); 1850 Unlink(TMPREJNAME); 1851 Unlink(TMPPATNAME); 1852 exit(status); 1853 } 1854 1855 #ifdef lint 1856 1857 /*VARARGS ARGSUSED*/ 1858 say(pat) char *pat; { ; } 1859 /*VARARGS ARGSUSED*/ 1860 fatal(pat) char *pat; { ; } 1861 /*VARARGS ARGSUSED*/ 1862 ask(pat) char *pat; { ; } 1863 1864 #else lint 1865 1866 say(pat,arg1,arg2,arg3) 1867 char *pat; 1868 int arg1,arg2,arg3; 1869 { 1870 fprintf(stderr,pat,arg1,arg2,arg3); 1871 Fflush(stderr); 1872 } 1873 1874 fatal(pat,arg1,arg2,arg3) 1875 char *pat; 1876 int arg1,arg2,arg3; 1877 { 1878 say(pat,arg1,arg2,arg3); 1879 my_exit(1); 1880 } 1881 1882 ask(pat,arg1,arg2,arg3) 1883 char *pat; 1884 int arg1,arg2,arg3; 1885 { 1886 int ttyfd = open("/dev/tty",2); 1887 int r; 1888 1889 say(pat,arg1,arg2,arg3); 1890 if (ttyfd >= 0) { 1891 r = read(ttyfd, buf, sizeof buf); 1892 Close(ttyfd); 1893 } 1894 else 1895 r = read(2, buf, sizeof buf); 1896 if (r <= 0) 1897 buf[0] = 0; 1898 } 1899 #endif lint 1900 1901 bool 1902 rev_in_string(string) 1903 char *string; 1904 { 1905 register char *s; 1906 register int patlen; 1907 1908 if (revision == Nullch) 1909 return TRUE; 1910 patlen = strlen(revision); 1911 for (s = string; *s; s++) { 1912 if (isspace(*s) && strnEQ(s+1,revision,patlen) && 1913 isspace(s[patlen+1] )) { 1914 return TRUE; 1915 } 1916 } 1917 return FALSE; 1918 } 1919 1920 set_signals() 1921 { 1922 /*NOSTRICT*/ 1923 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 1924 Signal(SIGHUP, my_exit); 1925 /*NOSTRICT*/ 1926 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 1927 Signal(SIGINT, my_exit); 1928 } 1929 1930 ignore_signals() 1931 { 1932 /*NOSTRICT*/ 1933 Signal(SIGHUP, SIG_IGN); 1934 /*NOSTRICT*/ 1935 Signal(SIGINT, SIG_IGN); 1936 } 1937