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