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