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