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