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