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