1: /* $Header: pch.c,v 2.0.1.7 88/06/03 15:13:28 lwall Locked $ 2: * 3: * $Log: pch.c,v $ 4: * Revision 2.0.1.7 88/06/03 15:13:28 lwall 5: * patch10: Can now find patches in shar scripts. 6: * patch10: Hunks that swapped and then swapped back could core dump. 7: * 8: * Revision 2.0.1.6 87/06/04 16:18:13 lwall 9: * pch_swap didn't swap p_bfake and p_efake. 10: * 11: * Revision 2.0.1.5 87/01/30 22:47:42 lwall 12: * Improved responses to mangled patches. 13: * 14: * Revision 2.0.1.4 87/01/05 16:59:53 lwall 15: * New-style context diffs caused double call to free(). 16: * 17: * Revision 2.0.1.3 86/11/14 10:08:33 lwall 18: * Fixed problem where a long pattern wouldn't grow the hunk. 19: * Also restored p_input_line when backtracking so error messages are right. 20: * 21: * Revision 2.0.1.2 86/11/03 17:49:52 lwall 22: * New-style delete triggers spurious assertion error. 23: * 24: * Revision 2.0.1.1 86/10/29 15:52:08 lwall 25: * Could falsely report new-style context diff. 26: * 27: * Revision 2.0 86/09/17 15:39:37 lwall 28: * Baseline for netwide release. 29: * 30: */ 31: 32: #include "EXTERN.h" 33: #include "common.h" 34: #include "util.h" 35: #include "INTERN.h" 36: #include "pch.h" 37: 38: /* Patch (diff listing) abstract type. */ 39: 40: static long p_filesize; /* size of the patch file */ 41: static LINENUM p_first; /* 1st line number */ 42: static LINENUM p_newfirst; /* 1st line number of replacement */ 43: static LINENUM p_ptrn_lines; /* # lines in pattern */ 44: static LINENUM p_repl_lines; /* # lines in replacement text */ 45: static LINENUM p_end = -1; /* last line in hunk */ 46: static LINENUM p_max; /* max allowed value of p_end */ 47: static LINENUM p_context = 3; /* # of context lines */ 48: static LINENUM p_input_line = 0; /* current line # from patch file */ 49: static char **p_line = Null(char**); /* the text of the hunk */ 50: static short *p_len = Null(short*); /* length of each line */ 51: static char *p_char = Nullch; /* +, -, and ! */ 52: static int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */ 53: static int p_indent; /* indent to patch */ 54: static LINENUM p_base; /* where to intuit this time */ 55: static LINENUM p_bline; /* line # of p_base */ 56: static LINENUM p_start; /* where intuit found a patch */ 57: static LINENUM p_sline; /* and the line number for it */ 58: static LINENUM p_hunk_beg; /* line number of current hunk */ 59: static LINENUM p_efake = -1; /* end of faked up lines--don't free */ 60: static LINENUM p_bfake = -1; /* beg of faked up lines */ 61: 62: /* Prepare to look for the next patch in the patch file. */ 63: 64: void 65: re_patch() 66: { 67: p_first = Nulline; 68: p_newfirst = Nulline; 69: p_ptrn_lines = Nulline; 70: p_repl_lines = Nulline; 71: p_end = (LINENUM)-1; 72: p_max = Nulline; 73: p_indent = 0; 74: } 75: 76: /* Open the patch file at the beginning of time. */ 77: 78: void 79: open_patch_file(filename) 80: char *filename; 81: { 82: if (filename == Nullch || !*filename || strEQ(filename, "-")) { 83: pfp = fopen(TMPPATNAME, "w"); 84: if (pfp == Nullfp) 85: fatal2("patch: can't create %s.\n", TMPPATNAME); 86: while (fgets(buf, sizeof buf, stdin) != Nullch) 87: fputs(buf, pfp); 88: Fclose(pfp); 89: filename = TMPPATNAME; 90: } 91: pfp = fopen(filename, "r"); 92: if (pfp == Nullfp) 93: fatal2("patch file %s not found\n", filename); 94: Fstat(fileno(pfp), &filestat); 95: p_filesize = filestat.st_size; 96: next_intuit_at(0L,1L); /* start at the beginning */ 97: set_hunkmax(); 98: } 99: 100: /* Make sure our dynamically realloced tables are malloced to begin with. */ 101: 102: void 103: set_hunkmax() 104: { 105: #ifndef lint 106: if (p_line == Null(char**)) 107: p_line = (char**) malloc((MEM)hunkmax * sizeof(char *)); 108: if (p_len == Null(short*)) 109: p_len = (short*) malloc((MEM)hunkmax * sizeof(short)); 110: #endif 111: if (p_char == Nullch) 112: p_char = (char*) malloc((MEM)hunkmax * sizeof(char)); 113: } 114: 115: /* Enlarge the arrays containing the current hunk of patch. */ 116: 117: void 118: grow_hunkmax() 119: { 120: hunkmax *= 2; 121: /* 122: * Note that on most systems, only the p_line array ever gets fresh memory 123: * since p_len can move into p_line's old space, and p_char can move into 124: * p_len's old space. Not on PDP-11's however. But it doesn't matter. 125: */ 126: assert(p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch); 127: #ifndef lint 128: p_line = (char**) realloc((char*)p_line, (MEM)hunkmax * sizeof(char *)); 129: p_len = (short*) realloc((char*)p_len, (MEM)hunkmax * sizeof(short)); 130: p_char = (char*) realloc((char*)p_char, (MEM)hunkmax * sizeof(char)); 131: #endif 132: if (p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch) 133: return; 134: if (!using_plan_a) 135: fatal1("patch: out of memory (grow_hunkmax)\n"); 136: out_of_mem = TRUE; /* whatever is null will be allocated again */ 137: /* from within plan_a(), of all places */ 138: } 139: 140: /* True if the remainder of the patch file contains a diff of some sort. */ 141: 142: bool 143: there_is_another_patch() 144: { 145: if (p_base != 0L && p_base >= p_filesize) { 146: if (verbose) 147: say1("done\n"); 148: return FALSE; 149: } 150: if (verbose) 151: say1("Hmm..."); 152: diff_type = intuit_diff_type(); 153: if (!diff_type) { 154: if (p_base != 0L) { 155: if (verbose) 156: say1(" Ignoring the trailing garbage.\ndone\n"); 157: } 158: else 159: say1(" I can't seem to find a patch in there anywhere.\n"); 160: return FALSE; 161: } 162: if (verbose) 163: say3(" %sooks like %s to me...\n", 164: (p_base == 0L ? "L" : "The next patch l"), 165: diff_type == CONTEXT_DIFF ? "a context diff" : 166: diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : 167: diff_type == NORMAL_DIFF ? "a normal diff" : 168: "an ed script" ); 169: if (p_indent && verbose) 170: say3("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s"); 171: skip_to(p_start,p_sline); 172: while (filearg[0] == Nullch) { 173: if (force) { 174: say1("No file to patch. Skipping...\n"); 175: filearg[0] = savestr(bestguess); 176: return TRUE; 177: } 178: ask1("File to patch: "); 179: if (*buf != '\n') { 180: if (bestguess) 181: free(bestguess); 182: bestguess = savestr(buf); 183: filearg[0] = fetchname(buf, 0, FALSE); 184: } 185: if (filearg[0] == Nullch) { 186: ask1("No file found--skip this patch? [n] "); 187: if (*buf != 'y') { 188: continue; 189: } 190: if (verbose) 191: say1("Skipping patch...\n"); 192: filearg[0] = fetchname(bestguess, 0, TRUE); 193: skip_rest_of_patch = TRUE; 194: return TRUE; 195: } 196: } 197: return TRUE; 198: } 199: 200: /* Determine what kind of diff is in the remaining part of the patch file. */ 201: 202: int 203: intuit_diff_type() 204: { 205: Reg4 long this_line = 0; 206: Reg5 long previous_line; 207: Reg6 long first_command_line = -1; 208: long fcl_line; 209: Reg7 bool last_line_was_command = FALSE; 210: Reg8 bool this_is_a_command = FALSE; 211: Reg9 bool stars_last_line = FALSE; 212: Reg10 bool stars_this_line = FALSE; 213: Reg3 int indent; 214: Reg1 char *s; 215: Reg2 char *t; 216: char *indtmp = Nullch; 217: char *oldtmp = Nullch; 218: char *newtmp = Nullch; 219: char *indname = Nullch; 220: char *oldname = Nullch; 221: char *newname = Nullch; 222: Reg11 int retval; 223: bool no_filearg = (filearg[0] == Nullch); 224: 225: ok_to_create_file = FALSE; 226: Fseek(pfp, p_base, 0); 227: p_input_line = p_bline - 1; 228: for (;;) { 229: previous_line = this_line; 230: last_line_was_command = this_is_a_command; 231: stars_last_line = stars_this_line; 232: this_line = ftell(pfp); 233: indent = 0; 234: p_input_line++; 235: if (fgets(buf, sizeof buf, pfp) == Nullch) { 236: if (first_command_line >= 0L) { 237: /* nothing but deletes!? */ 238: p_start = first_command_line; 239: p_sline = fcl_line; 240: retval = ED_DIFF; 241: goto scan_exit; 242: } 243: else { 244: p_start = this_line; 245: p_sline = p_input_line; 246: retval = 0; 247: goto scan_exit; 248: } 249: } 250: for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) { 251: if (*s == '\t') 252: indent += 8 - (indent % 8); 253: else 254: indent++; 255: } 256: for (t=s; isdigit(*t) || *t == ','; t++) ; 257: this_is_a_command = (isdigit(*s) && 258: (*t == 'd' || *t == 'c' || *t == 'a') ); 259: if (first_command_line < 0L && this_is_a_command) { 260: first_command_line = this_line; 261: fcl_line = p_input_line; 262: p_indent = indent; /* assume this for now */ 263: } 264: if (!stars_last_line && strnEQ(s, "*** ", 4)) 265: oldtmp = savestr(s+4); 266: else if (strnEQ(s, "--- ", 4)) 267: newtmp = savestr(s+4); 268: else if (strnEQ(s, "Index:", 6)) 269: indtmp = savestr(s+6); 270: else if (strnEQ(s, "Prereq:", 7)) { 271: for (t=s+7; isspace(*t); t++) ; 272: revision = savestr(t); 273: for (t=revision; *t && !isspace(*t); t++) ; 274: *t = '\0'; 275: if (!*revision) { 276: free(revision); 277: revision = Nullch; 278: } 279: } 280: if ((!diff_type || diff_type == ED_DIFF) && 281: first_command_line >= 0L && 282: strEQ(s, ".\n") ) { 283: p_indent = indent; 284: p_start = first_command_line; 285: p_sline = fcl_line; 286: retval = ED_DIFF; 287: goto scan_exit; 288: } 289: stars_this_line = strnEQ(s, "********", 8); 290: if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && 291: strnEQ(s, "*** ", 4)) { 292: if (!atol(s+4)) 293: ok_to_create_file = TRUE; 294: /* if this is a new context diff the character just before */ 295: /* the newline is a '*'. */ 296: while (*s != '\n') 297: s++; 298: p_indent = indent; 299: p_start = previous_line; 300: p_sline = p_input_line - 1; 301: retval = (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); 302: goto scan_exit; 303: } 304: if ((!diff_type || diff_type == NORMAL_DIFF) && 305: last_line_was_command && 306: (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) { 307: p_start = previous_line; 308: p_sline = p_input_line - 1; 309: p_indent = indent; 310: retval = NORMAL_DIFF; 311: goto scan_exit; 312: } 313: } 314: scan_exit: 315: if (no_filearg) { 316: if (indtmp != Nullch) 317: indname = fetchname(indtmp, strippath, ok_to_create_file); 318: if (oldtmp != Nullch) 319: oldname = fetchname(oldtmp, strippath, ok_to_create_file); 320: if (newtmp != Nullch) 321: newname = fetchname(newtmp, strippath, ok_to_create_file); 322: if (oldname && newname) { 323: if (strlen(oldname) < strlen(newname)) 324: filearg[0] = savestr(oldname); 325: else 326: filearg[0] = savestr(newname); 327: } 328: else if (oldname) 329: filearg[0] = savestr(oldname); 330: else if (newname) 331: filearg[0] = savestr(newname); 332: else if (indname) 333: filearg[0] = savestr(indname); 334: } 335: if (bestguess) { 336: free(bestguess); 337: bestguess = Nullch; 338: } 339: if (filearg[0] != Nullch) 340: bestguess = savestr(filearg[0]); 341: else if (indtmp != Nullch) 342: bestguess = fetchname(indtmp, strippath, TRUE); 343: else { 344: if (oldtmp != Nullch) 345: oldname = fetchname(oldtmp, strippath, TRUE); 346: if (newtmp != Nullch) 347: newname = fetchname(newtmp, strippath, TRUE); 348: if (oldname && newname) { 349: if (strlen(oldname) < strlen(newname)) 350: bestguess = savestr(oldname); 351: else 352: bestguess = savestr(newname); 353: } 354: else if (oldname) 355: bestguess = savestr(oldname); 356: else if (newname) 357: bestguess = savestr(newname); 358: } 359: if (indtmp != Nullch) 360: free(indtmp); 361: if (oldtmp != Nullch) 362: free(oldtmp); 363: if (newtmp != Nullch) 364: free(newtmp); 365: if (indname != Nullch) 366: free(indname); 367: if (oldname != Nullch) 368: free(oldname); 369: if (newname != Nullch) 370: free(newname); 371: return retval; 372: } 373: 374: /* Remember where this patch ends so we know where to start up again. */ 375: 376: void 377: next_intuit_at(file_pos,file_line) 378: long file_pos; 379: long file_line; 380: { 381: p_base = file_pos; 382: p_bline = file_line; 383: } 384: 385: /* Basically a verbose fseek() to the actual diff listing. */ 386: 387: void 388: skip_to(file_pos,file_line) 389: long file_pos; 390: long file_line; 391: { 392: char *ret; 393: 394: assert(p_base <= file_pos); 395: if (verbose && p_base < file_pos) { 396: Fseek(pfp, p_base, 0); 397: say1("The text leading up to this was:\n--------------------------\n"); 398: while (ftell(pfp) < file_pos) { 399: ret = fgets(buf, sizeof buf, pfp); 400: assert(ret != Nullch); 401: say2("|%s", buf); 402: } 403: say1("--------------------------\n"); 404: } 405: else 406: Fseek(pfp, file_pos, 0); 407: p_input_line = file_line - 1; 408: } 409: 410: /* True if there is more of the current diff listing to process. */ 411: 412: bool 413: another_hunk() 414: { 415: Reg1 char *s; 416: Reg8 char *ret; 417: Reg2 int context = 0; 418: 419: while (p_end >= 0) { 420: if (p_end == p_efake) 421: p_end = p_bfake; /* don't free twice */ 422: else 423: free(p_line[p_end]); 424: p_end--; 425: } 426: assert(p_end == -1); 427: p_efake = -1; 428: 429: p_max = hunkmax; /* gets reduced when --- found */ 430: if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) { 431: long line_beginning = ftell(pfp); 432: /* file pos of the current line */ 433: LINENUM repl_beginning = 0; /* index of --- line */ 434: Reg4 LINENUM fillcnt = 0; /* #lines of missing ptrn or repl */ 435: Reg5 LINENUM fillsrc; /* index of first line to copy */ 436: Reg6 LINENUM filldst; /* index of first missing line */ 437: bool ptrn_spaces_eaten = FALSE; /* ptrn was slightly misformed */ 438: Reg9 bool repl_could_be_missing = TRUE; 439: /* no + or ! lines in this hunk */ 440: bool repl_missing = FALSE; /* we are now backtracking */ 441: long repl_backtrack_position = 0; 442: /* file pos of first repl line */ 443: LINENUM repl_patch_line; /* input line number for same */ 444: Reg7 LINENUM ptrn_copiable = 0; 445: /* # of copiable lines in ptrn */ 446: 447: ret = pgets(buf, sizeof buf, pfp); 448: p_input_line++; 449: if (ret == Nullch || strnNE(buf, "********", 8)) { 450: next_intuit_at(line_beginning,p_input_line); 451: return FALSE; 452: } 453: p_context = 100; 454: p_hunk_beg = p_input_line + 1; 455: while (p_end < p_max) { 456: line_beginning = ftell(pfp); 457: ret = pgets(buf, sizeof buf, pfp); 458: p_input_line++; 459: if (ret == Nullch) { 460: if (p_max - p_end < 4) 461: Strcpy(buf, " \n"); /* assume blank lines got chopped */ 462: else { 463: if (repl_beginning && repl_could_be_missing) { 464: repl_missing = TRUE; 465: goto hunk_done; 466: } 467: fatal1("Unexpected end of file in patch.\n"); 468: } 469: } 470: p_end++; 471: assert(p_end < hunkmax); 472: p_char[p_end] = *buf; 473: #ifdef zilog 474: p_line[(short)p_end] = Nullch; 475: #else 476: p_line[p_end] = Nullch; 477: #endif 478: switch (*buf) { 479: case '*': 480: if (strnEQ(buf, "********", 8)) { 481: if (repl_beginning && repl_could_be_missing) { 482: repl_missing = TRUE; 483: goto hunk_done; 484: } 485: else 486: fatal2("Unexpected end of hunk at line %ld.\n", 487: p_input_line); 488: } 489: if (p_end != 0) { 490: if (repl_beginning && repl_could_be_missing) { 491: repl_missing = TRUE; 492: goto hunk_done; 493: } 494: fatal3("Unexpected *** at line %ld: %s", p_input_line, buf); 495: } 496: context = 0; 497: p_line[p_end] = savestr(buf); 498: if (out_of_mem) { 499: p_end--; 500: return FALSE; 501: } 502: for (s=buf; *s && !isdigit(*s); s++) ; 503: if (!*s) 504: goto malformed; 505: if (strnEQ(s,"0,0",3)) 506: strcpy(s,s+2); 507: p_first = (LINENUM) atol(s); 508: while (isdigit(*s)) s++; 509: if (*s == ',') { 510: for (; *s && !isdigit(*s); s++) ; 511: if (!*s) 512: goto malformed; 513: p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1; 514: } 515: else if (p_first) 516: p_ptrn_lines = 1; 517: else { 518: p_ptrn_lines = 0; 519: p_first = 1; 520: } 521: p_max = p_ptrn_lines + 6; /* we need this much at least */ 522: while (p_max >= hunkmax) 523: grow_hunkmax(); 524: p_max = hunkmax; 525: break; 526: case '-': 527: if (buf[1] == '-') { 528: if (repl_beginning || 529: (p_end != p_ptrn_lines + 1 + (p_char[p_end-1] == '\n'))) 530: { 531: if (p_end == 1) { 532: /* `old' lines were omitted - set up to fill */ 533: /* them in from 'new' context lines. */ 534: p_end = p_ptrn_lines + 1; 535: fillsrc = p_end + 1; 536: filldst = 1; 537: fillcnt = p_ptrn_lines; 538: } 539: else { 540: if (repl_beginning) { 541: if (repl_could_be_missing){ 542: repl_missing = TRUE; 543: goto hunk_done; 544: } 545: fatal3( 546: "Duplicate \"---\" at line %ld--check line numbers at line %ld.\n", 547: p_input_line, p_hunk_beg + repl_beginning); 548: } 549: else { 550: fatal4( 551: "%s \"---\" at line %ld--check line numbers at line %ld.\n", 552: (p_end <= p_ptrn_lines 553: ? "Premature" 554: : "Overdue" ), 555: p_input_line, p_hunk_beg); 556: } 557: } 558: } 559: repl_beginning = p_end; 560: repl_backtrack_position = ftell(pfp); 561: repl_patch_line = p_input_line; 562: p_line[p_end] = savestr(buf); 563: if (out_of_mem) { 564: p_end--; 565: return FALSE; 566: } 567: p_char[p_end] = '='; 568: for (s=buf; *s && !isdigit(*s); s++) ; 569: if (!*s) 570: goto malformed; 571: p_newfirst = (LINENUM) atol(s); 572: while (isdigit(*s)) s++; 573: if (*s == ',') { 574: for (; *s && !isdigit(*s); s++) ; 575: if (!*s) 576: goto malformed; 577: p_repl_lines = ((LINENUM)atol(s)) - p_newfirst + 1; 578: } 579: else if (p_newfirst) 580: p_repl_lines = 1; 581: else { 582: p_repl_lines = 0; 583: p_newfirst = 1; 584: } 585: p_max = p_repl_lines + p_end; 586: if (p_max > MAXHUNKSIZE) 587: fatal4("Hunk too large (%ld lines) at line %ld: %s", 588: p_max, p_input_line, buf); 589: while (p_max >= hunkmax) 590: grow_hunkmax(); 591: if (p_repl_lines != ptrn_copiable) 592: repl_could_be_missing = FALSE; 593: break; 594: } 595: goto change_line; 596: case '+': case '!': 597: repl_could_be_missing = FALSE; 598: change_line: 599: if (buf[1] == '\n' && canonicalize) 600: strcpy(buf+1," \n"); 601: if (!isspace(buf[1]) && buf[1] != '>' && buf[1] != '<' && 602: repl_beginning && repl_could_be_missing) { 603: repl_missing = TRUE; 604: goto hunk_done; 605: } 606: if (context > 0) { 607: if (context < p_context) 608: p_context = context; 609: context = -1000; 610: } 611: p_line[p_end] = savestr(buf+2); 612: if (out_of_mem) { 613: p_end--; 614: return FALSE; 615: } 616: break; 617: case '\t': case '\n': /* assume the 2 spaces got eaten */ 618: if (repl_beginning && repl_could_be_missing && 619: (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF) ) { 620: repl_missing = TRUE; 621: goto hunk_done; 622: } 623: p_line[p_end] = savestr(buf); 624: if (out_of_mem) { 625: p_end--; 626: return FALSE; 627: } 628: if (p_end != p_ptrn_lines + 1) { 629: ptrn_spaces_eaten |= (repl_beginning != 0); 630: context++; 631: if (!repl_beginning) 632: ptrn_copiable++; 633: p_char[p_end] = ' '; 634: } 635: break; 636: case ' ': 637: if (!isspace(buf[1]) && 638: repl_beginning && repl_could_be_missing) { 639: repl_missing = TRUE; 640: goto hunk_done; 641: } 642: context++; 643: if (!repl_beginning) 644: ptrn_copiable++; 645: p_line[p_end] = savestr(buf+2); 646: if (out_of_mem) { 647: p_end--; 648: return FALSE; 649: } 650: break; 651: default: 652: if (repl_beginning && repl_could_be_missing) { 653: repl_missing = TRUE; 654: goto hunk_done; 655: } 656: goto malformed; 657: } 658: /* set up p_len for strncmp() so we don't have to */ 659: /* assume null termination */ 660: if (p_line[p_end]) 661: p_len[p_end] = strlen(p_line[p_end]); 662: else 663: p_len[p_end] = 0; 664: } 665: 666: hunk_done: 667: if (p_end >=0 && !repl_beginning) 668: fatal2("No --- found in patch at line %ld\n", pch_hunk_beg()); 669: 670: if (repl_missing) { 671: 672: /* reset state back to just after --- */ 673: p_input_line = repl_patch_line; 674: for (p_end--; p_end > repl_beginning; p_end--) 675: free(p_line[p_end]); 676: Fseek(pfp, repl_backtrack_position, 0); 677: 678: /* redundant 'new' context lines were omitted - set */ 679: /* up to fill them in from the old file context */ 680: fillsrc = 1; 681: filldst = repl_beginning+1; 682: fillcnt = p_repl_lines; 683: p_end = p_max; 684: } 685: 686: if (diff_type == CONTEXT_DIFF && 687: (fillcnt || (p_first > 1 && ptrn_copiable > 2*p_context)) ) { 688: if (verbose) 689: say4("%s\n%s\n%s\n", 690: "(Fascinating--this is really a new-style context diff but without", 691: "the telltale extra asterisks on the *** line that usually indicate", 692: "the new style...)"); 693: diff_type = NEW_CONTEXT_DIFF; 694: } 695: 696: /* if there were omitted context lines, fill them in now */ 697: if (fillcnt) { 698: p_bfake = filldst; /* remember where not to free() */ 699: p_efake = filldst + fillcnt - 1; 700: while (fillcnt-- > 0) { 701: while (fillsrc <= p_end && p_char[fillsrc] != ' ') 702: fillsrc++; 703: if (fillsrc > p_end) 704: fatal2("Replacement text or line numbers mangled in hunk at line %ld\n", 705: p_hunk_beg); 706: p_line[filldst] = p_line[fillsrc]; 707: p_char[filldst] = p_char[fillsrc]; 708: p_len[filldst] = p_len[fillsrc]; 709: fillsrc++; filldst++; 710: } 711: while (fillsrc <= p_end && fillsrc != repl_beginning && 712: p_char[fillsrc] != ' ') 713: fillsrc++; 714: #ifdef DEBUGGING 715: if (debug & 64) 716: printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n", 717: fillsrc,filldst,repl_beginning,p_end+1); 718: #endif 719: assert(fillsrc==p_end+1 || fillsrc==repl_beginning); 720: assert(filldst==p_end+1 || filldst==repl_beginning); 721: } 722: } 723: else { /* normal diff--fake it up */ 724: char hunk_type; 725: Reg3 int i; 726: LINENUM min, max; 727: long line_beginning = ftell(pfp); 728: 729: p_context = 0; 730: ret = pgets(buf, sizeof buf, pfp); 731: p_input_line++; 732: if (ret == Nullch || !isdigit(*buf)) { 733: next_intuit_at(line_beginning,p_input_line); 734: return FALSE; 735: } 736: p_first = (LINENUM)atol(buf); 737: for (s=buf; isdigit(*s); s++) ; 738: if (*s == ',') { 739: p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1; 740: while (isdigit(*s)) s++; 741: } 742: else 743: p_ptrn_lines = (*s != 'a'); 744: hunk_type = *s; 745: if (hunk_type == 'a') 746: p_first++; /* do append rather than insert */ 747: min = (LINENUM)atol(++s); 748: for (; isdigit(*s); s++) ; 749: if (*s == ',') 750: max = (LINENUM)atol(++s); 751: else 752: max = min; 753: if (hunk_type == 'd') 754: min++; 755: p_end = p_ptrn_lines + 1 + max - min + 1; 756: if (p_end > MAXHUNKSIZE) 757: fatal4("Hunk too large (%ld lines) at line %ld: %s", 758: p_end, p_input_line, buf); 759: while (p_end >= hunkmax) 760: grow_hunkmax(); 761: p_newfirst = min; 762: p_repl_lines = max - min + 1; 763: Sprintf(buf, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1); 764: p_line[0] = savestr(buf); 765: if (out_of_mem) { 766: p_end = -1; 767: return FALSE; 768: } 769: p_char[0] = '*'; 770: for (i=1; i<=p_ptrn_lines; i++) { 771: ret = pgets(buf, sizeof buf, pfp); 772: p_input_line++; 773: if (ret == Nullch) 774: fatal2("Unexpected end of file in patch at line %ld.\n", 775: p_input_line); 776: if (*buf != '<') 777: fatal2("< expected at line %ld of patch.\n", p_input_line); 778: p_line[i] = savestr(buf+2); 779: if (out_of_mem) { 780: p_end = i-1; 781: return FALSE; 782: } 783: p_len[i] = strlen(p_line[i]); 784: p_char[i] = '-'; 785: } 786: if (hunk_type == 'c') { 787: ret = pgets(buf, sizeof buf, pfp); 788: p_input_line++; 789: if (ret == Nullch) 790: fatal2("Unexpected end of file in patch at line %ld.\n", 791: p_input_line); 792: if (*buf != '-') 793: fatal2("--- expected at line %ld of patch.\n", p_input_line); 794: } 795: Sprintf(buf, "--- %ld,%ld\n", min, max); 796: p_line[i] = savestr(buf); 797: if (out_of_mem) { 798: p_end = i-1; 799: return FALSE; 800: } 801: p_char[i] = '='; 802: for (i++; i<=p_end; i++) { 803: ret = pgets(buf, sizeof buf, pfp); 804: p_input_line++; 805: if (ret == Nullch) 806: fatal2("Unexpected end of file in patch at line %ld.\n", 807: p_input_line); 808: if (*buf != '>') 809: fatal2("> expected at line %ld of patch.\n", p_input_line); 810: p_line[i] = savestr(buf+2); 811: if (out_of_mem) { 812: p_end = i-1; 813: return FALSE; 814: } 815: p_len[i] = strlen(p_line[i]); 816: p_char[i] = '+'; 817: } 818: } 819: if (reverse) /* backwards patch? */ 820: if (!pch_swap()) 821: say1("Not enough memory to swap next hunk!\n"); 822: #ifdef DEBUGGING 823: if (debug & 2) { 824: int i; 825: char special; 826: 827: for (i=0; i <= p_end; i++) { 828: if (i == p_ptrn_lines) 829: special = '^'; 830: else 831: special = ' '; 832: fprintf(stderr, "%3d %c %c %s", i, p_char[i], special, p_line[i]); 833: Fflush(stderr); 834: } 835: } 836: #endif 837: if (p_end+1 < hunkmax) /* paranoia reigns supreme... */ 838: p_char[p_end+1] = '^'; /* add a stopper for apply_hunk */ 839: return TRUE; 840: 841: malformed: 842: fatal3("Malformed patch at line %ld: %s", p_input_line, buf); 843: /* about as informative as "Syntax error" in C */ 844: return FALSE; /* for lint */ 845: } 846: 847: /* Input a line from the patch file, worrying about indentation. */ 848: 849: char * 850: pgets(bf,sz,fp) 851: char *bf; 852: int sz; 853: FILE *fp; 854: { 855: char *ret = fgets(bf, sz, fp); 856: Reg1 char *s; 857: Reg2 int indent = 0; 858: 859: if (p_indent && ret != Nullch) { 860: for (s=buf; 861: indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X'); s++) { 862: if (*s == '\t') 863: indent += 8 - (indent % 7); 864: else 865: indent++; 866: } 867: if (buf != s) 868: Strcpy(buf, s); 869: } 870: return ret; 871: } 872: 873: /* Reverse the old and new portions of the current hunk. */ 874: 875: bool 876: pch_swap() 877: { 878: char **tp_line; /* the text of the hunk */ 879: short *tp_len; /* length of each line */ 880: char *tp_char; /* +, -, and ! */ 881: Reg1 LINENUM i; 882: Reg2 LINENUM n; 883: bool blankline = FALSE; 884: Reg3 char *s; 885: 886: i = p_first; 887: p_first = p_newfirst; 888: p_newfirst = i; 889: 890: /* make a scratch copy */ 891: 892: tp_line = p_line; 893: tp_len = p_len; 894: tp_char = p_char; 895: p_line = Null(char**); /* force set_hunkmax to allocate again */ 896: p_len = Null(short*); 897: p_char = Nullch; 898: set_hunkmax(); 899: if (p_line == Null(char**) || p_len == Null(short*) || p_char == Nullch) { 900: #ifndef lint 901: if (p_line == Null(char**)) 902: free((char*)p_line); 903: p_line = tp_line; 904: if (p_len == Null(short*)) 905: free((char*)p_len); 906: p_len = tp_len; 907: #endif 908: if (p_char == Nullch) 909: free((char*)p_char); 910: p_char = tp_char; 911: return FALSE; /* not enough memory to swap hunk! */ 912: } 913: 914: /* now turn the new into the old */ 915: 916: i = p_ptrn_lines + 1; 917: if (tp_char[i] == '\n') { /* account for possible blank line */ 918: blankline = TRUE; 919: i++; 920: } 921: if (p_efake >= 0) { /* fix non-freeable ptr range */ 922: if (p_efake <= i) 923: n = p_end - i + 1; 924: else 925: n = -i; 926: p_efake += n; 927: p_bfake += n; 928: } 929: for (n=0; i <= p_end; i++,n++) { 930: p_line[n] = tp_line[i]; 931: p_char[n] = tp_char[i]; 932: if (p_char[n] == '+') 933: p_char[n] = '-'; 934: p_len[n] = tp_len[i]; 935: } 936: if (blankline) { 937: i = p_ptrn_lines + 1; 938: p_line[n] = tp_line[i]; 939: p_char[n] = tp_char[i]; 940: p_len[n] = tp_len[i]; 941: n++; 942: } 943: assert(p_char[0] == '='); 944: p_char[0] = '*'; 945: for (s=p_line[0]; *s; s++) 946: if (*s == '-') 947: *s = '*'; 948: 949: /* now turn the old into the new */ 950: 951: assert(tp_char[0] == '*'); 952: tp_char[0] = '='; 953: for (s=tp_line[0]; *s; s++) 954: if (*s == '*') 955: *s = '-'; 956: for (i=0; n <= p_end; i++,n++) { 957: p_line[n] = tp_line[i]; 958: p_char[n] = tp_char[i]; 959: if (p_char[n] == '-') 960: p_char[n] = '+'; 961: p_len[n] = tp_len[i]; 962: } 963: assert(i == p_ptrn_lines + 1); 964: i = p_ptrn_lines; 965: p_ptrn_lines = p_repl_lines; 966: p_repl_lines = i; 967: #ifndef lint 968: if (tp_line == Null(char**)) 969: free((char*)tp_line); 970: if (tp_len == Null(short*)) 971: free((char*)tp_len); 972: #endif 973: if (tp_char == Nullch) 974: free((char*)tp_char); 975: return TRUE; 976: } 977: 978: /* Return the specified line position in the old file of the old context. */ 979: 980: LINENUM 981: pch_first() 982: { 983: return p_first; 984: } 985: 986: /* Return the number of lines of old context. */ 987: 988: LINENUM 989: pch_ptrn_lines() 990: { 991: return p_ptrn_lines; 992: } 993: 994: /* Return the probable line position in the new file of the first line. */ 995: 996: LINENUM 997: pch_newfirst() 998: { 999: return p_newfirst; 1000: } 1001: 1002: /* Return the number of lines in the replacement text including context. */ 1003: 1004: LINENUM 1005: pch_repl_lines() 1006: { 1007: return p_repl_lines; 1008: } 1009: 1010: /* Return the number of lines in the whole hunk. */ 1011: 1012: LINENUM 1013: pch_end() 1014: { 1015: return p_end; 1016: } 1017: 1018: /* Return the number of context lines before the first changed line. */ 1019: 1020: LINENUM 1021: pch_context() 1022: { 1023: return p_context; 1024: } 1025: 1026: /* Return the length of a particular patch line. */ 1027: 1028: short 1029: pch_line_len(line) 1030: LINENUM line; 1031: { 1032: return p_len[line]; 1033: } 1034: 1035: /* Return the control character (+, -, *, !, etc) for a patch line. */ 1036: 1037: char 1038: pch_char(line) 1039: LINENUM line; 1040: { 1041: return p_char[line]; 1042: } 1043: 1044: /* Return a pointer to a particular patch line. */ 1045: 1046: char * 1047: pfetch(line) 1048: LINENUM line; 1049: { 1050: return p_line[line]; 1051: } 1052: 1053: /* Return where in the patch file this hunk began, for error messages. */ 1054: 1055: LINENUM 1056: pch_hunk_beg() 1057: { 1058: return p_hunk_beg; 1059: } 1060: 1061: /* Apply an ed script by feeding ed itself. */ 1062: 1063: void 1064: do_ed_script() 1065: { 1066: Reg1 char *t; 1067: Reg2 long beginning_of_this_line; 1068: Reg3 bool this_line_is_command = FALSE; 1069: Reg4 FILE *pipefp; 1070: FILE *popen(); 1071: 1072: if (!skip_rest_of_patch) { 1073: Unlink(TMPOUTNAME); 1074: copy_file(filearg[0], TMPOUTNAME); 1075: if (verbose) 1076: Sprintf(buf, "/bin/ed %s", TMPOUTNAME); 1077: else 1078: Sprintf(buf, "/bin/ed - %s", TMPOUTNAME); 1079: pipefp = popen(buf, "w"); 1080: } 1081: for (;;) { 1082: beginning_of_this_line = ftell(pfp); 1083: if (pgets(buf, sizeof buf, pfp) == Nullch) { 1084: next_intuit_at(beginning_of_this_line,p_input_line); 1085: break; 1086: } 1087: p_input_line++; 1088: for (t=buf; isdigit(*t) || *t == ','; t++) ; 1089: this_line_is_command = (isdigit(*buf) && 1090: (*t == 'd' || *t == 'c' || *t == 'a') ); 1091: if (this_line_is_command) { 1092: if (!skip_rest_of_patch) 1093: fputs(buf, pipefp); 1094: if (*t != 'd') { 1095: while (pgets(buf, sizeof buf, pfp) != Nullch) { 1096: p_input_line++; 1097: if (!skip_rest_of_patch) 1098: fputs(buf, pipefp); 1099: if (strEQ(buf, ".\n")) 1100: break; 1101: } 1102: } 1103: } 1104: else { 1105: next_intuit_at(beginning_of_this_line,p_input_line); 1106: break; 1107: } 1108: } 1109: if (skip_rest_of_patch) 1110: return; 1111: fprintf(pipefp, "w\n"); 1112: fprintf(pipefp, "q\n"); 1113: Fflush(pipefp); 1114: Pclose(pipefp); 1115: ignore_signals(); 1116: if (move_file(TMPOUTNAME, outname) < 0) { 1117: toutkeep = TRUE; 1118: chmod(TMPOUTNAME, filemode); 1119: } 1120: else 1121: chmod(outname, filemode); 1122: set_signals(1); 1123: }