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