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