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