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