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