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