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