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