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