| 1 | #ifndef lint |
| 2 | static char sccsid[] = "@(#)patch.c 5.8 (Berkeley) %G%"; |
| 3 | #endif not lint |
| 4 | |
| 5 | /* patch - a program to apply diffs to original files |
| 6 | * |
| 7 | * $Header: patch.c,v 1.3 85/03/26 15:07:43 lwall Exp $ |
| 8 | * |
| 9 | * Copyright 1984, Larry Wall |
| 10 | * |
| 11 | * This program may be copied as long as you don't try to make any |
| 12 | * money off of it, or pretend that you wrote it. |
| 13 | * |
| 14 | * $Log: patch.c,v $ |
| 15 | * 85/08/15 van%ucbmonet@berkeley |
| 16 | * Changes for 4.3bsd diff -c. |
| 17 | * |
| 18 | * Revision 1.3 85/03/26 15:07:43 lwall |
| 19 | * Frozen. |
| 20 | * |
| 21 | * Revision 1.2.1.9 85/03/12 17:03:35 lwall |
| 22 | * Changed pfp->_file to fileno(pfp). |
| 23 | * |
| 24 | * Revision 1.2.1.8 85/03/12 16:30:43 lwall |
| 25 | * Check i_ptr and i_womp to make sure they aren't null before freeing. |
| 26 | * Also allow ed output to be suppressed. |
| 27 | * |
| 28 | * Revision 1.2.1.7 85/03/12 15:56:13 lwall |
| 29 | * Added -p option from jromine@uci-750a. |
| 30 | * |
| 31 | * Revision 1.2.1.6 85/03/12 12:12:51 lwall |
| 32 | * Now checks for normalness of file to patch. |
| 33 | * |
| 34 | * Revision 1.2.1.5 85/03/12 11:52:12 lwall |
| 35 | * Added -D (#ifdef) option from joe@fluke. |
| 36 | * |
| 37 | * Revision 1.2.1.4 84/12/06 11:14:15 lwall |
| 38 | * Made smarter about SCCS subdirectories. |
| 39 | * |
| 40 | * Revision 1.2.1.3 84/12/05 11:18:43 lwall |
| 41 | * Added -l switch to do loose string comparison. |
| 42 | * |
| 43 | * Revision 1.2.1.2 84/12/04 09:47:13 lwall |
| 44 | * Failed hunk count not reset on multiple patch file. |
| 45 | * |
| 46 | * Revision 1.2.1.1 84/12/04 09:42:37 lwall |
| 47 | * Branch for sdcrdcf changes. |
| 48 | * |
| 49 | * Revision 1.2 84/11/29 13:29:51 lwall |
| 50 | * Linted. Identifiers uniqified. Fixed i_ptr malloc() bug. Fixed |
| 51 | * multiple calls to mktemp(). Will now work on machines that can only |
| 52 | * read 32767 chars. Added -R option for diffs with new and old swapped. |
| 53 | * Various cosmetic changes. |
| 54 | * |
| 55 | * Revision 1.1 84/11/09 17:03:58 lwall |
| 56 | * Initial revision |
| 57 | * |
| 58 | */ |
| 59 | |
| 60 | #define DEBUGGING |
| 61 | |
| 62 | /* shut lint up about the following when return value ignored */ |
| 63 | |
| 64 | #define Signal (void)signal |
| 65 | #define Unlink (void)unlink |
| 66 | #define Lseek (void)lseek |
| 67 | #define Fseek (void)fseek |
| 68 | #define Fstat (void)fstat |
| 69 | #define Pclose (void)pclose |
| 70 | #define Close (void)close |
| 71 | #define Fclose (void)fclose |
| 72 | #define Fflush (void)fflush |
| 73 | #define Sprintf (void)sprintf |
| 74 | #define Mktemp (void)mktemp |
| 75 | #define Strcpy (void)strcpy |
| 76 | #define Strcat (void)strcat |
| 77 | |
| 78 | #include <stdio.h> |
| 79 | #include <assert.h> |
| 80 | #include <sys/types.h> |
| 81 | #include <sys/stat.h> |
| 82 | #include <ctype.h> |
| 83 | #include <signal.h> |
| 84 | |
| 85 | /* constants */ |
| 86 | |
| 87 | #define TRUE (1) |
| 88 | #define FALSE (0) |
| 89 | |
| 90 | #define MAXHUNKSIZE 2000 |
| 91 | #define MAXLINELEN 1024 |
| 92 | #define BUFFERSIZE 1024 |
| 93 | #define ORIGEXT ".orig" |
| 94 | #define SCCSPREFIX "s." |
| 95 | #define GET "get -e %s" |
| 96 | #define RCSSUFFIX ",v" |
| 97 | #define CHECKOUT "co -l %s" |
| 98 | |
| 99 | /* handy definitions */ |
| 100 | |
| 101 | #define Null(t) ((t)0) |
| 102 | #define Nullch Null(char *) |
| 103 | #define Nullfp Null(FILE *) |
| 104 | |
| 105 | #define Ctl(ch) (ch & 037) |
| 106 | |
| 107 | #define strNE(s1,s2) (strcmp(s1,s2)) |
| 108 | #define strEQ(s1,s2) (!strcmp(s1,s2)) |
| 109 | #define strnNE(s1,s2,l) (strncmp(s1,s2,l)) |
| 110 | #define strnEQ(s1,s2,l) (!strncmp(s1,s2,l)) |
| 111 | |
| 112 | /* typedefs */ |
| 113 | |
| 114 | typedef char bool; |
| 115 | typedef long LINENUM; /* must be signed */ |
| 116 | typedef unsigned MEM; /* what to feed malloc */ |
| 117 | |
| 118 | /* globals */ |
| 119 | |
| 120 | int Argc; /* guess */ |
| 121 | char **Argv; |
| 122 | |
| 123 | struct stat filestat; /* file statistics area */ |
| 124 | |
| 125 | char serrbuf[BUFSIZ]; /* buffer for stderr */ |
| 126 | char buf[MAXLINELEN]; /* general purpose buffer */ |
| 127 | FILE *pfp = Nullfp; /* patch file pointer */ |
| 128 | FILE *ofp = Nullfp; /* output file pointer */ |
| 129 | FILE *rejfp = Nullfp; /* reject file pointer */ |
| 130 | |
| 131 | LINENUM input_lines = 0; /* how long is input file in lines */ |
| 132 | LINENUM last_frozen_line = 0; /* how many input lines have been */ |
| 133 | /* irretractibly output */ |
| 134 | |
| 135 | #define MAXFILEC 2 |
| 136 | int filec = 0; /* how many file arguments? */ |
| 137 | char *filearg[MAXFILEC]; |
| 138 | |
| 139 | char *outname = Nullch; |
| 140 | char rejname[128]; |
| 141 | |
| 142 | char *origext = Nullch; |
| 143 | |
| 144 | char TMPOUTNAME[] = "/tmp/patchoXXXXXX"; |
| 145 | char TMPINNAME[] = "/tmp/patchiXXXXXX"; /* you might want /usr/tmp here */ |
| 146 | char TMPREJNAME[] = "/tmp/patchrXXXXXX"; |
| 147 | char TMPPATNAME[] = "/tmp/patchpXXXXXX"; |
| 148 | |
| 149 | LINENUM last_offset = 0; |
| 150 | #ifdef DEBUGGING |
| 151 | int debug = 0; |
| 152 | #endif |
| 153 | bool verbose = TRUE; |
| 154 | bool reverse = FALSE; |
| 155 | bool noreverse = FALSE; |
| 156 | bool skip_this_patch = FALSE; |
| 157 | bool usepath = FALSE; |
| 158 | bool canonicalize = FALSE; |
| 159 | |
| 160 | #define CONTEXT_DIFF 1 |
| 161 | #define NORMAL_DIFF 2 |
| 162 | #define ED_DIFF 3 |
| 163 | #define NEW_CONTEXT_DIFF 4 |
| 164 | int diff_type = 0; |
| 165 | |
| 166 | int do_defines = 0; /* patch using ifdef, ifndef, etc. */ |
| 167 | char if_defined[128]; /* #ifdef xyzzy */ |
| 168 | char not_defined[128]; /* #ifndef xyzzy */ |
| 169 | char else_defined[] = "#else\n"; /* #else */ |
| 170 | char end_defined[128]; /* #endif xyzzy */ |
| 171 | |
| 172 | char *revision = Nullch; /* prerequisite revision, if any */ |
| 173 | |
| 174 | /* procedures */ |
| 175 | |
| 176 | LINENUM locate_hunk(); |
| 177 | bool patch_match(); |
| 178 | bool similar(); |
| 179 | char *malloc(); |
| 180 | char *savestr(); |
| 181 | char *strcpy(); |
| 182 | char *strcat(); |
| 183 | char *sprintf(); /* usually */ |
| 184 | int my_exit(); |
| 185 | bool rev_in_string(); |
| 186 | char *fetchname(); |
| 187 | long atol(); |
| 188 | long lseek(); |
| 189 | char *mktemp(); |
| 190 | |
| 191 | /* patch type */ |
| 192 | |
| 193 | bool there_is_another_patch(); |
| 194 | bool another_hunk(); |
| 195 | char *pfetch(); |
| 196 | int pch_line_len(); |
| 197 | LINENUM pch_first(); |
| 198 | LINENUM pch_ptrn_lines(); |
| 199 | LINENUM pch_newfirst(); |
| 200 | LINENUM pch_repl_lines(); |
| 201 | LINENUM pch_end(); |
| 202 | LINENUM pch_context(); |
| 203 | LINENUM pch_hunk_beg(); |
| 204 | char pch_char(); |
| 205 | char *pfetch(); |
| 206 | char *pgets(); |
| 207 | |
| 208 | /* input file type */ |
| 209 | |
| 210 | char *ifetch(); |
| 211 | |
| 212 | /* apply a context patch to a named file */ |
| 213 | |
| 214 | main(argc,argv) |
| 215 | int argc; |
| 216 | char **argv; |
| 217 | { |
| 218 | LINENUM where; |
| 219 | int hunk = 0; |
| 220 | int failed = 0; |
| 221 | int i; |
| 222 | |
| 223 | setbuf(stderr,serrbuf); |
| 224 | for (i = 0; i<MAXFILEC; i++) |
| 225 | filearg[i] = Nullch; |
| 226 | Mktemp(TMPOUTNAME); |
| 227 | Mktemp(TMPINNAME); |
| 228 | Mktemp(TMPREJNAME); |
| 229 | Mktemp(TMPPATNAME); |
| 230 | |
| 231 | /* parse switches */ |
| 232 | Argc = argc; |
| 233 | Argv = argv; |
| 234 | get_some_switches(); |
| 235 | |
| 236 | /* make sure we clean up /tmp in case of disaster */ |
| 237 | set_signals(); |
| 238 | |
| 239 | for ( |
| 240 | open_patch_file(filearg[1]); |
| 241 | there_is_another_patch(); |
| 242 | reinitialize_almost_everything() |
| 243 | ) { /* for each patch in patch file */ |
| 244 | |
| 245 | if (outname == Nullch) |
| 246 | outname = savestr(filearg[0]); |
| 247 | |
| 248 | /* initialize the patched file */ |
| 249 | init_output(TMPOUTNAME); |
| 250 | |
| 251 | /* for ed script just up and do it and exit */ |
| 252 | if (diff_type == ED_DIFF) { |
| 253 | do_ed_script(); |
| 254 | continue; |
| 255 | } |
| 256 | |
| 257 | /* initialize reject file */ |
| 258 | init_reject(TMPREJNAME); |
| 259 | |
| 260 | /* find out where all the lines are */ |
| 261 | scan_input(filearg[0]); |
| 262 | |
| 263 | /* from here on, open no standard i/o files, because malloc */ |
| 264 | /* might misfire */ |
| 265 | |
| 266 | /* apply each hunk of patch */ |
| 267 | hunk = 0; |
| 268 | failed = 0; |
| 269 | while (another_hunk()) { |
| 270 | hunk++; |
| 271 | where = locate_hunk(); |
| 272 | if (hunk == 1 && where == Null(LINENUM)) { |
| 273 | /* dwim for reversed patch? */ |
| 274 | pch_swap(); |
| 275 | reverse = !reverse; |
| 276 | where = locate_hunk(); /* try again */ |
| 277 | if (where == Null(LINENUM)) { |
| 278 | pch_swap(); /* no, put it back to normal */ |
| 279 | reverse = !reverse; |
| 280 | } else if (noreverse) { |
| 281 | pch_swap(); /* put it back to normal */ |
| 282 | reverse = !reverse; |
| 283 | say("Ignoring previously applied (or reversed) patch.\n"); |
| 284 | skip_this_patch = TRUE; |
| 285 | } |
| 286 | else { |
| 287 | say("%seversed (or previously applied) patch detected! %s -R.\n", |
| 288 | reverse ? "R" : "Unr", |
| 289 | reverse ? "Assuming" : "Ignoring"); |
| 290 | } |
| 291 | } |
| 292 | if (where == Null(LINENUM) || skip_this_patch) { |
| 293 | abort_hunk(); |
| 294 | failed++; |
| 295 | if (verbose) |
| 296 | say("Hunk #%d failed.\n",hunk); |
| 297 | } |
| 298 | else { |
| 299 | apply_hunk(where); |
| 300 | if (verbose) |
| 301 | if (last_offset) |
| 302 | say("Hunk #%d succeeded (offset %d line%s).\n", |
| 303 | hunk,last_offset,last_offset==1?"":"s"); |
| 304 | else |
| 305 | say("Hunk #%d succeeded.\n", hunk); |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | assert(hunk); |
| 310 | |
| 311 | /* finish spewing out the new file */ |
| 312 | spew_output(); |
| 313 | |
| 314 | /* and put the output where desired */ |
| 315 | ignore_signals(); |
| 316 | move_file(TMPOUTNAME,outname); |
| 317 | Fclose(rejfp); |
| 318 | rejfp = Nullfp; |
| 319 | if (failed) { |
| 320 | if (!*rejname) { |
| 321 | Strcpy(rejname, outname); |
| 322 | Strcat(rejname, ".rej"); |
| 323 | } |
| 324 | say("%d out of %d hunks failed--saving rejects to %s\n", |
| 325 | failed, hunk, rejname); |
| 326 | move_file(TMPREJNAME,rejname); |
| 327 | } |
| 328 | set_signals(); |
| 329 | } |
| 330 | my_exit(0); |
| 331 | } |
| 332 | |
| 333 | reinitialize_almost_everything() |
| 334 | { |
| 335 | re_patch(); |
| 336 | re_input(); |
| 337 | |
| 338 | input_lines = 0; |
| 339 | last_frozen_line = 0; |
| 340 | |
| 341 | filec = 0; |
| 342 | if (filearg[0] != Nullch) { |
| 343 | free(filearg[0]); |
| 344 | filearg[0] = Nullch; |
| 345 | } |
| 346 | |
| 347 | if (outname != Nullch) { |
| 348 | free(outname); |
| 349 | outname = Nullch; |
| 350 | } |
| 351 | |
| 352 | last_offset = 0; |
| 353 | |
| 354 | diff_type = 0; |
| 355 | |
| 356 | if (revision != Nullch) { |
| 357 | free(revision); |
| 358 | revision = Nullch; |
| 359 | } |
| 360 | |
| 361 | reverse = FALSE; |
| 362 | skip_this_patch = FALSE; |
| 363 | |
| 364 | get_some_switches(); |
| 365 | |
| 366 | if (filec >= 2) |
| 367 | fatal("You may not change to a different patch file.\n"); |
| 368 | } |
| 369 | |
| 370 | get_some_switches() |
| 371 | { |
| 372 | register char *s; |
| 373 | |
| 374 | rejname[0] = '\0'; |
| 375 | if (!Argc) |
| 376 | return; |
| 377 | for (Argc--,Argv++; Argc; Argc--,Argv++) { |
| 378 | s = Argv[0]; |
| 379 | if (strEQ(s,"+")) { |
| 380 | return; /* + will be skipped by for loop */ |
| 381 | } |
| 382 | if (*s != '-' || !s[1]) { |
| 383 | if (filec == MAXFILEC) |
| 384 | fatal("Too many file arguments.\n"); |
| 385 | filearg[filec++] = savestr(s); |
| 386 | } |
| 387 | else { |
| 388 | switch (*++s) { |
| 389 | case 'b': |
| 390 | origext = savestr(Argv[1]); |
| 391 | Argc--,Argv++; |
| 392 | break; |
| 393 | case 'c': |
| 394 | diff_type = CONTEXT_DIFF; |
| 395 | break; |
| 396 | case 'd': |
| 397 | if (chdir(Argv[1]) < 0) |
| 398 | fatal("Can't cd to %s.\n",Argv[1]); |
| 399 | Argc--,Argv++; |
| 400 | break; |
| 401 | case 'D': |
| 402 | do_defines++; |
| 403 | Sprintf(if_defined, "#ifdef %s\n", Argv[1]); |
| 404 | Sprintf(not_defined, "#ifndef %s\n", Argv[1]); |
| 405 | Sprintf(end_defined, "#endif %s\n", Argv[1]); |
| 406 | Argc--,Argv++; |
| 407 | break; |
| 408 | case 'e': |
| 409 | diff_type = ED_DIFF; |
| 410 | break; |
| 411 | case 'l': |
| 412 | canonicalize = TRUE; |
| 413 | break; |
| 414 | case 'n': |
| 415 | diff_type = NORMAL_DIFF; |
| 416 | break; |
| 417 | case 'o': |
| 418 | outname = savestr(Argv[1]); |
| 419 | Argc--,Argv++; |
| 420 | break; |
| 421 | case 'p': |
| 422 | usepath = TRUE; /* do not strip path names */ |
| 423 | break; |
| 424 | case 'r': |
| 425 | Strcpy(rejname,Argv[1]); |
| 426 | Argc--,Argv++; |
| 427 | break; |
| 428 | case 'R': |
| 429 | reverse = TRUE; |
| 430 | break; |
| 431 | case 'N': |
| 432 | noreverse = TRUE; |
| 433 | break; |
| 434 | case 's': |
| 435 | verbose = FALSE; |
| 436 | break; |
| 437 | #ifdef DEBUGGING |
| 438 | case 'x': |
| 439 | debug = atoi(s+1); |
| 440 | break; |
| 441 | #endif |
| 442 | default: |
| 443 | fatal("Unrecognized switch: %s\n",Argv[0]); |
| 444 | } |
| 445 | } |
| 446 | } |
| 447 | } |
| 448 | |
| 449 | LINENUM |
| 450 | locate_hunk() |
| 451 | { |
| 452 | register LINENUM first_guess = pch_first() + last_offset; |
| 453 | register LINENUM offset; |
| 454 | LINENUM pat_lines = pch_ptrn_lines(); |
| 455 | register LINENUM max_pos_offset = input_lines - first_guess |
| 456 | - pat_lines + 1; |
| 457 | register LINENUM max_neg_offset = first_guess - last_frozen_line - 1 |
| 458 | - pch_context(); |
| 459 | |
| 460 | if (!pat_lines) /* null range matches always */ |
| 461 | return first_guess; |
| 462 | if (max_neg_offset >= first_guess) /* do not try lines < 0 */ |
| 463 | max_neg_offset = first_guess - 1; |
| 464 | if (first_guess <= input_lines && patch_match(first_guess,(LINENUM)0)) |
| 465 | return first_guess; |
| 466 | for (offset = 1; ; offset++) { |
| 467 | bool check_after = (offset <= max_pos_offset); |
| 468 | bool check_before = (offset <= max_pos_offset); |
| 469 | |
| 470 | if (check_after && patch_match(first_guess,offset)) { |
| 471 | #ifdef DEBUGGING |
| 472 | if (debug & 1) |
| 473 | printf("Offset changing from %d to %d\n",last_offset,offset); |
| 474 | #endif |
| 475 | last_offset = offset; |
| 476 | return first_guess+offset; |
| 477 | } |
| 478 | else if (check_before && patch_match(first_guess,-offset)) { |
| 479 | #ifdef DEBUGGING |
| 480 | if (debug & 1) |
| 481 | printf("Offset changing from %d to %d\n",last_offset,-offset); |
| 482 | #endif |
| 483 | last_offset = -offset; |
| 484 | return first_guess-offset; |
| 485 | } |
| 486 | else if (!check_before && !check_after) |
| 487 | return Null(LINENUM); |
| 488 | } |
| 489 | } |
| 490 | |
| 491 | /* we did not find the pattern, dump out the hunk so they can handle it */ |
| 492 | |
| 493 | abort_hunk() |
| 494 | { |
| 495 | register LINENUM i; |
| 496 | register LINENUM pat_end = pch_end(); |
| 497 | /* add in last_offset to guess the same as the previous successful hunk */ |
| 498 | int oldfirst = pch_first() + last_offset; |
| 499 | int newfirst = pch_newfirst() + last_offset; |
| 500 | int oldlast = oldfirst + pch_ptrn_lines() - 1; |
| 501 | int newlast = newfirst + pch_repl_lines() - 1; |
| 502 | |
| 503 | fprintf(rejfp,"***************\n"); |
| 504 | for (i=0; i<=pat_end; i++) { |
| 505 | switch (pch_char(i)) { |
| 506 | case '*': |
| 507 | if (diff_type == NEW_CONTEXT_DIFF) |
| 508 | fprintf(rejfp,"*** %d,%d ****\n", oldfirst, oldlast); |
| 509 | else |
| 510 | fprintf(rejfp,"*** %d,%d\n", oldfirst, oldlast); |
| 511 | break; |
| 512 | case '=': |
| 513 | fprintf(rejfp,"--- %d,%d -----\n", newfirst, newlast); |
| 514 | break; |
| 515 | case '\n': |
| 516 | fprintf(rejfp,"%s", pfetch(i)); |
| 517 | break; |
| 518 | case ' ': case '-': case '+': case '!': |
| 519 | fprintf(rejfp,"%c %s", pch_char(i), pfetch(i)); |
| 520 | break; |
| 521 | default: |
| 522 | say("Fatal internal error in abort_hunk().\n"); |
| 523 | abort(); |
| 524 | } |
| 525 | } |
| 526 | } |
| 527 | |
| 528 | /* we found where to apply it (we hope), so do it */ |
| 529 | |
| 530 | apply_hunk(where) |
| 531 | LINENUM where; |
| 532 | { |
| 533 | register LINENUM old = 1; |
| 534 | register LINENUM lastline = pch_ptrn_lines(); |
| 535 | register LINENUM new = lastline+1; |
| 536 | register int def_state = 0; /* -1 = ifndef, 1 = ifdef */ |
| 537 | |
| 538 | where--; |
| 539 | while (pch_char(new) == '=' || pch_char(new) == '\n') |
| 540 | new++; |
| 541 | |
| 542 | while (old <= lastline) { |
| 543 | if (pch_char(old) == '-') { |
| 544 | copy_till(where + old - 1); |
| 545 | if (do_defines) { |
| 546 | if (def_state == 0) { |
| 547 | fputs(not_defined, ofp); |
| 548 | def_state = -1; |
| 549 | } else |
| 550 | if (def_state == 1) { |
| 551 | fputs(else_defined, ofp); |
| 552 | def_state = 2; |
| 553 | } |
| 554 | fputs(pfetch(old), ofp); |
| 555 | } |
| 556 | last_frozen_line++; |
| 557 | old++; |
| 558 | } |
| 559 | else if (pch_char(new) == '+') { |
| 560 | copy_till(where + old - 1); |
| 561 | if (do_defines) { |
| 562 | if (def_state == -1) { |
| 563 | fputs(else_defined, ofp); |
| 564 | def_state = 2; |
| 565 | } else |
| 566 | if (def_state == 0) { |
| 567 | fputs(if_defined, ofp); |
| 568 | def_state = 1; |
| 569 | } |
| 570 | } |
| 571 | fputs(pfetch(new),ofp); |
| 572 | new++; |
| 573 | } |
| 574 | else { |
| 575 | if (pch_char(new) != pch_char(old)) { |
| 576 | say("Out-of-sync patch, lines %d,%d\n", |
| 577 | pch_hunk_beg() + old - 1, |
| 578 | pch_hunk_beg() + new - 1); |
| 579 | #ifdef DEBUGGING |
| 580 | printf("oldchar = '%c', newchar = '%c'\n", |
| 581 | pch_char(old), pch_char(new)); |
| 582 | #endif |
| 583 | my_exit(1); |
| 584 | } |
| 585 | if (pch_char(new) == '!') { |
| 586 | copy_till(where + old - 1); |
| 587 | if (do_defines) { |
| 588 | fputs(not_defined,ofp); |
| 589 | def_state = -1; |
| 590 | } |
| 591 | while (pch_char(old) == '!') { |
| 592 | if (do_defines) { |
| 593 | fputs(pfetch(old),ofp); |
| 594 | } |
| 595 | last_frozen_line++; |
| 596 | old++; |
| 597 | } |
| 598 | if (do_defines) { |
| 599 | fputs(else_defined, ofp); |
| 600 | def_state = 2; |
| 601 | } |
| 602 | while (pch_char(new) == '!') { |
| 603 | fputs(pfetch(new),ofp); |
| 604 | new++; |
| 605 | } |
| 606 | if (do_defines) { |
| 607 | fputs(end_defined, ofp); |
| 608 | def_state = 0; |
| 609 | } |
| 610 | } |
| 611 | else { |
| 612 | assert(pch_char(new) == ' '); |
| 613 | old++; |
| 614 | new++; |
| 615 | } |
| 616 | } |
| 617 | } |
| 618 | if (new <= pch_end() && pch_char(new) == '+') { |
| 619 | copy_till(where + old - 1); |
| 620 | if (do_defines) { |
| 621 | if (def_state == 0) { |
| 622 | fputs(if_defined, ofp); |
| 623 | def_state = 1; |
| 624 | } else |
| 625 | if (def_state == -1) { |
| 626 | fputs(else_defined, ofp); |
| 627 | def_state = 2; |
| 628 | } |
| 629 | } |
| 630 | while (new <= pch_end() && pch_char(new) == '+') { |
| 631 | fputs(pfetch(new),ofp); |
| 632 | new++; |
| 633 | } |
| 634 | } |
| 635 | if (do_defines && def_state) { |
| 636 | fputs(end_defined, ofp); |
| 637 | } |
| 638 | } |
| 639 | |
| 640 | do_ed_script() |
| 641 | { |
| 642 | FILE *pipefp, *popen(); |
| 643 | bool this_line_is_command = FALSE; |
| 644 | register char *t; |
| 645 | long beginning_of_this_line; |
| 646 | |
| 647 | Unlink(TMPOUTNAME); |
| 648 | copy_file(filearg[0],TMPOUTNAME); |
| 649 | if (verbose) |
| 650 | Sprintf(buf,"/bin/ed %s",TMPOUTNAME); |
| 651 | else |
| 652 | Sprintf(buf,"/bin/ed - %s",TMPOUTNAME); |
| 653 | pipefp = popen(buf,"w"); |
| 654 | for (;;) { |
| 655 | beginning_of_this_line = ftell(pfp); |
| 656 | if (pgets(buf,sizeof buf,pfp) == Nullch) { |
| 657 | next_intuit_at(beginning_of_this_line); |
| 658 | break; |
| 659 | } |
| 660 | for (t=buf; isdigit(*t) || *t == ','; t++) ; |
| 661 | this_line_is_command = (isdigit(*buf) && |
| 662 | (*t == 'd' || *t == 'c' || *t == 'a') ); |
| 663 | if (this_line_is_command) { |
| 664 | fputs(buf,pipefp); |
| 665 | if (*t != 'd') { |
| 666 | while (pgets(buf,sizeof buf,pfp) != Nullch) { |
| 667 | fputs(buf,pipefp); |
| 668 | if (strEQ(buf,".\n")) |
| 669 | break; |
| 670 | } |
| 671 | } |
| 672 | } |
| 673 | else { |
| 674 | next_intuit_at(beginning_of_this_line); |
| 675 | break; |
| 676 | } |
| 677 | } |
| 678 | fprintf(pipefp,"w\n"); |
| 679 | fprintf(pipefp,"q\n"); |
| 680 | Fflush(pipefp); |
| 681 | Pclose(pipefp); |
| 682 | ignore_signals(); |
| 683 | move_file(TMPOUTNAME,outname); |
| 684 | set_signals(); |
| 685 | } |
| 686 | |
| 687 | init_output(name) |
| 688 | char *name; |
| 689 | { |
| 690 | ofp = fopen(name,"w"); |
| 691 | if (ofp == Nullfp) |
| 692 | fatal("patch: can't create %s.\n",name); |
| 693 | } |
| 694 | |
| 695 | init_reject(name) |
| 696 | char *name; |
| 697 | { |
| 698 | rejfp = fopen(name,"w"); |
| 699 | if (rejfp == Nullfp) |
| 700 | fatal("patch: can't create %s.\n",name); |
| 701 | } |
| 702 | |
| 703 | move_file(from,to) |
| 704 | char *from, *to; |
| 705 | { |
| 706 | char bakname[512]; |
| 707 | register char *s; |
| 708 | int fromfd; |
| 709 | register int i; |
| 710 | |
| 711 | /* to stdout? */ |
| 712 | |
| 713 | if (strEQ(to,"-")) { |
| 714 | #ifdef DEBUGGING |
| 715 | if (debug & 4) |
| 716 | say("Moving %s to stdout.\n",from); |
| 717 | #endif |
| 718 | fromfd = open(from,0); |
| 719 | if (fromfd < 0) |
| 720 | fatal("patch: internal error, can't reopen %s\n",from); |
| 721 | while ((i=read(fromfd,buf,sizeof buf)) > 0) |
| 722 | if (write(1,buf,i) != 1) |
| 723 | fatal("patch: write failed\n"); |
| 724 | Close(fromfd); |
| 725 | return; |
| 726 | } |
| 727 | |
| 728 | Strcpy(bakname,to); |
| 729 | Strcat(bakname,origext?origext:ORIGEXT); |
| 730 | if (stat(to,&filestat) >= 0) { /* output file exists */ |
| 731 | dev_t to_device = filestat.st_dev; |
| 732 | ino_t to_inode = filestat.st_ino; |
| 733 | char *simplename = bakname; |
| 734 | |
| 735 | for (s=bakname; *s; s++) { |
| 736 | if (*s == '/') |
| 737 | simplename = s+1; |
| 738 | } |
| 739 | /* find a backup name that is not the same file */ |
| 740 | while (stat(bakname,&filestat) >= 0 && |
| 741 | to_device == filestat.st_dev && to_inode == filestat.st_ino) { |
| 742 | for (s=simplename; *s && !islower(*s); s++) ; |
| 743 | if (*s) |
| 744 | *s = toupper(*s); |
| 745 | else |
| 746 | Strcpy(simplename, simplename+1); |
| 747 | } |
| 748 | while (unlink(bakname) >= 0) ; /* while() is for benefit of Eunice */ |
| 749 | #ifdef DEBUGGING |
| 750 | if (debug & 4) |
| 751 | say("Moving %s to %s.\n",to,bakname); |
| 752 | #endif |
| 753 | if (link(to,bakname) < 0) { |
| 754 | say("patch: can't backup %s, output is in %s\n", |
| 755 | to,from); |
| 756 | return; |
| 757 | } |
| 758 | while (unlink(to) >= 0) ; |
| 759 | } |
| 760 | #ifdef DEBUGGING |
| 761 | if (debug & 4) |
| 762 | say("Moving %s to %s.\n",from,to); |
| 763 | #endif |
| 764 | if (link(from,to) < 0) { /* different file system? */ |
| 765 | int tofd; |
| 766 | |
| 767 | tofd = creat(to,0666); |
| 768 | if (tofd < 0) { |
| 769 | say("patch: can't create %s, output is in %s.\n", |
| 770 | to, from); |
| 771 | return; |
| 772 | } |
| 773 | fromfd = open(from,0); |
| 774 | if (fromfd < 0) |
| 775 | fatal("patch: internal error, can't reopen %s\n",from); |
| 776 | while ((i=read(fromfd,buf,sizeof buf)) > 0) |
| 777 | if (write(tofd,buf,i) != i) |
| 778 | fatal("patch: write failed\n"); |
| 779 | Close(fromfd); |
| 780 | Close(tofd); |
| 781 | } |
| 782 | Unlink(from); |
| 783 | } |
| 784 | |
| 785 | copy_file(from,to) |
| 786 | char *from, *to; |
| 787 | { |
| 788 | int tofd; |
| 789 | int fromfd; |
| 790 | register int i; |
| 791 | |
| 792 | tofd = creat(to,0666); |
| 793 | if (tofd < 0) |
| 794 | fatal("patch: can't create %s.\n", to); |
| 795 | fromfd = open(from,0); |
| 796 | if (fromfd < 0) |
| 797 | fatal("patch: internal error, can't reopen %s\n",from); |
| 798 | while ((i=read(fromfd,buf,sizeof buf)) > 0) |
| 799 | if (write(tofd,buf,i) != i) |
| 800 | fatal("patch: write (%s) failed\n", to); |
| 801 | Close(fromfd); |
| 802 | Close(tofd); |
| 803 | } |
| 804 | |
| 805 | copy_till(lastline) |
| 806 | register LINENUM lastline; |
| 807 | { |
| 808 | if (last_frozen_line > lastline) |
| 809 | say("patch: misordered hunks! output will be garbled.\n"); |
| 810 | while (last_frozen_line < lastline) { |
| 811 | dump_line(++last_frozen_line); |
| 812 | } |
| 813 | } |
| 814 | |
| 815 | spew_output() |
| 816 | { |
| 817 | copy_till(input_lines); /* dump remainder of file */ |
| 818 | Fclose(ofp); |
| 819 | ofp = Nullfp; |
| 820 | } |
| 821 | |
| 822 | dump_line(line) |
| 823 | LINENUM line; |
| 824 | { |
| 825 | register char *s; |
| 826 | |
| 827 | for (s=ifetch(line,0); putc(*s,ofp) != '\n'; s++) ; |
| 828 | } |
| 829 | |
| 830 | /* does the patch pattern match at line base+offset? */ |
| 831 | |
| 832 | bool |
| 833 | patch_match(base,offset) |
| 834 | LINENUM base; |
| 835 | LINENUM offset; |
| 836 | { |
| 837 | register LINENUM pline; |
| 838 | register LINENUM iline; |
| 839 | register LINENUM pat_lines = pch_ptrn_lines(); |
| 840 | |
| 841 | for (pline = 1, iline=base+offset; pline <= pat_lines; pline++,iline++) { |
| 842 | if (canonicalize) { |
| 843 | if (!similar(ifetch(iline,(offset >= 0)), |
| 844 | pfetch(pline), |
| 845 | pch_line_len(pline) )) |
| 846 | return FALSE; |
| 847 | } |
| 848 | else if (strnNE(ifetch(iline,(offset >= 0)), |
| 849 | pfetch(pline), |
| 850 | pch_line_len(pline) )) |
| 851 | return FALSE; |
| 852 | } |
| 853 | return TRUE; |
| 854 | } |
| 855 | |
| 856 | /* match two lines with canonicalized white space */ |
| 857 | |
| 858 | bool |
| 859 | similar(a,b,len) |
| 860 | register char *a, *b; |
| 861 | register int len; |
| 862 | { |
| 863 | while (len) { |
| 864 | if (isspace(*b)) { /* whitespace (or \n) to match? */ |
| 865 | if (!isspace(*a)) /* no corresponding whitespace? */ |
| 866 | return FALSE; |
| 867 | while (len && isspace(*b) && *b != '\n') |
| 868 | b++,len--; /* skip pattern whitespace */ |
| 869 | while (isspace(*a) && *a != '\n') |
| 870 | a++; /* skip target whitespace */ |
| 871 | if (*a == '\n' || *b == '\n') |
| 872 | return (*a == *b); /* should end in sync */ |
| 873 | } |
| 874 | else if (*a++ != *b++) /* match non-whitespace chars */ |
| 875 | return FALSE; |
| 876 | else |
| 877 | len--; /* probably not necessary */ |
| 878 | } |
| 879 | return TRUE; /* actually, this is not reached */ |
| 880 | /* since there is always a \n */ |
| 881 | } |
| 882 | |
| 883 | /* input file with indexable lines abstract type */ |
| 884 | |
| 885 | bool using_plan_a = TRUE; |
| 886 | static long i_size; /* size of the input file */ |
| 887 | static char *i_womp; /* plan a buffer for entire file */ |
| 888 | static char **i_ptr; /* pointers to lines in i_womp */ |
| 889 | |
| 890 | static int tifd = -1; /* plan b virtual string array */ |
| 891 | static char *tibuf[2]; /* plan b buffers */ |
| 892 | static LINENUM tiline[2] = {-1,-1}; /* 1st line in each buffer */ |
| 893 | static LINENUM lines_per_buf; /* how many lines per buffer */ |
| 894 | static int tireclen; /* length of records in tmp file */ |
| 895 | |
| 896 | re_input() |
| 897 | { |
| 898 | if (using_plan_a) { |
| 899 | i_size = 0; |
| 900 | /*NOSTRICT*/ |
| 901 | if (i_ptr != Null(char**)) |
| 902 | free((char *)i_ptr); |
| 903 | if (i_womp != Nullch) |
| 904 | free(i_womp); |
| 905 | i_womp = Nullch; |
| 906 | i_ptr = Null(char **); |
| 907 | } |
| 908 | else { |
| 909 | using_plan_a = TRUE; /* maybe the next one is smaller */ |
| 910 | Close(tifd); |
| 911 | tifd = -1; |
| 912 | free(tibuf[0]); |
| 913 | free(tibuf[1]); |
| 914 | tibuf[0] = tibuf[1] = Nullch; |
| 915 | tiline[0] = tiline[1] = -1; |
| 916 | tireclen = 0; |
| 917 | } |
| 918 | } |
| 919 | |
| 920 | scan_input(filename) |
| 921 | char *filename; |
| 922 | { |
| 923 | bool plan_a(); |
| 924 | |
| 925 | if (!plan_a(filename)) |
| 926 | plan_b(filename); |
| 927 | } |
| 928 | |
| 929 | /* try keeping everything in memory */ |
| 930 | |
| 931 | bool |
| 932 | plan_a(filename) |
| 933 | char *filename; |
| 934 | { |
| 935 | int ifd; |
| 936 | register char *s; |
| 937 | register LINENUM iline; |
| 938 | |
| 939 | if (stat(filename,&filestat) < 0) { |
| 940 | Sprintf(buf,"RCS/%s%s",filename,RCSSUFFIX); |
| 941 | if (stat(buf,&filestat) >= 0 || stat(buf+4,&filestat) >= 0) { |
| 942 | Sprintf(buf,CHECKOUT,filename); |
| 943 | if (verbose) |
| 944 | say("Can't find %s--attempting to check it out from RCS.\n", |
| 945 | filename); |
| 946 | if (system(buf) || stat(filename,&filestat)) |
| 947 | fatal("Can't check out %s.\n",filename); |
| 948 | } |
| 949 | else { |
| 950 | Sprintf(buf,"SCCS/%s%s",SCCSPREFIX,filename); |
| 951 | if (stat(buf,&filestat) >= 0 || stat(buf+5,&filestat) >= 0) { |
| 952 | Sprintf(buf,GET,filename); |
| 953 | if (verbose) |
| 954 | say("Can't find %s--attempting to get it from SCCS.\n", |
| 955 | filename); |
| 956 | if (system(buf) || stat(filename,&filestat)) |
| 957 | fatal("Can't get %s.\n",filename); |
| 958 | } |
| 959 | else |
| 960 | fatal("Can't find %s.\n",filename); |
| 961 | } |
| 962 | } |
| 963 | if ((filestat.st_mode & S_IFMT) & ~S_IFREG) |
| 964 | fatal("%s is not a normal file--can't patch.\n",filename); |
| 965 | i_size = filestat.st_size; |
| 966 | /*NOSTRICT*/ |
| 967 | i_womp = malloc((MEM)(i_size+2)); |
| 968 | if (i_womp == Nullch) |
| 969 | return FALSE; |
| 970 | if ((ifd = open(filename,0)) < 0) |
| 971 | fatal("Can't open file %s\n",filename); |
| 972 | /*NOSTRICT*/ |
| 973 | if (read(ifd,i_womp,(int)i_size) != i_size) { |
| 974 | Close(ifd); |
| 975 | free(i_womp); |
| 976 | return FALSE; |
| 977 | } |
| 978 | Close(ifd); |
| 979 | if (i_womp[i_size-1] != '\n') |
| 980 | i_womp[i_size++] = '\n'; |
| 981 | i_womp[i_size] = '\0'; |
| 982 | |
| 983 | /* count the lines in the buffer so we know how many pointers we need */ |
| 984 | |
| 985 | iline = 0; |
| 986 | for (s=i_womp; *s; s++) { |
| 987 | if (*s == '\n') |
| 988 | iline++; |
| 989 | } |
| 990 | /*NOSTRICT*/ |
| 991 | i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *))); |
| 992 | if (i_ptr == Null(char **)) { /* shucks, it was a near thing */ |
| 993 | free((char *)i_womp); |
| 994 | return FALSE; |
| 995 | } |
| 996 | |
| 997 | /* now scan the buffer and build pointer array */ |
| 998 | |
| 999 | iline = 1; |
| 1000 | i_ptr[iline] = i_womp; |
| 1001 | for (s=i_womp; *s; s++) { |
| 1002 | if (*s == '\n') |
| 1003 | i_ptr[++iline] = s+1; /* these are NOT null terminated */ |
| 1004 | } |
| 1005 | input_lines = iline - 1; |
| 1006 | |
| 1007 | /* now check for revision, if any */ |
| 1008 | |
| 1009 | if (revision != Nullch) { |
| 1010 | if (!rev_in_string(i_womp)) { |
| 1011 | ask("This file doesn't appear to be the %s version--patch anyway? [n] ", |
| 1012 | revision); |
| 1013 | if (*buf != 'y') |
| 1014 | fatal("Aborted.\n"); |
| 1015 | } |
| 1016 | else if (verbose) |
| 1017 | say("Good. This file appears to be the %s version.\n", |
| 1018 | revision); |
| 1019 | } |
| 1020 | return TRUE; /* plan a will work */ |
| 1021 | } |
| 1022 | |
| 1023 | /* keep (virtually) nothing in memory */ |
| 1024 | |
| 1025 | plan_b(filename) |
| 1026 | char *filename; |
| 1027 | { |
| 1028 | FILE *ifp; |
| 1029 | register int i = 0; |
| 1030 | register int maxlen = 1; |
| 1031 | bool found_revision = (revision == Nullch); |
| 1032 | |
| 1033 | using_plan_a = FALSE; |
| 1034 | if ((ifp = fopen(filename,"r")) == Nullfp) |
| 1035 | fatal("Can't open file %s\n",filename); |
| 1036 | if ((tifd = creat(TMPINNAME,0666)) < 0) |
| 1037 | fatal("Can't open file %s\n",TMPINNAME); |
| 1038 | while (fgets(buf,sizeof buf, ifp) != Nullch) { |
| 1039 | if (revision != Nullch && !found_revision && rev_in_string(buf)) |
| 1040 | found_revision = TRUE; |
| 1041 | if ((i = strlen(buf)) > maxlen) |
| 1042 | maxlen = i; /* find longest line */ |
| 1043 | } |
| 1044 | if (revision != Nullch) { |
| 1045 | if (!found_revision) { |
| 1046 | ask("This file doesn't appear to be the %s version--patch anyway? [n] ", |
| 1047 | revision); |
| 1048 | if (*buf != 'y') |
| 1049 | fatal("Aborted.\n"); |
| 1050 | } |
| 1051 | else if (verbose) |
| 1052 | say("Good. This file appears to be the %s version.\n", |
| 1053 | revision); |
| 1054 | } |
| 1055 | Fseek(ifp,0L,0); /* rewind file */ |
| 1056 | lines_per_buf = BUFFERSIZE / maxlen; |
| 1057 | tireclen = maxlen; |
| 1058 | tibuf[0] = malloc((MEM)(BUFFERSIZE + 1)); |
| 1059 | tibuf[1] = malloc((MEM)(BUFFERSIZE + 1)); |
| 1060 | if (tibuf[1] == Nullch) |
| 1061 | fatal("Can't seem to get enough memory.\n"); |
| 1062 | for (i=1; ; i++) { |
| 1063 | if (! (i % lines_per_buf)) /* new block */ |
| 1064 | if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE) |
| 1065 | fatal("patch: can't write temp file.\n"); |
| 1066 | if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp) |
| 1067 | == Nullch) { |
| 1068 | input_lines = i - 1; |
| 1069 | if (i % lines_per_buf) |
| 1070 | if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE) |
| 1071 | fatal("patch: can't write temp file.\n"); |
| 1072 | break; |
| 1073 | } |
| 1074 | } |
| 1075 | Fclose(ifp); |
| 1076 | Close(tifd); |
| 1077 | if ((tifd = open(TMPINNAME,0)) < 0) { |
| 1078 | fatal("Can't reopen file %s\n",TMPINNAME); |
| 1079 | } |
| 1080 | } |
| 1081 | |
| 1082 | /* fetch a line from the input file, \n terminated, not necessarily \0 */ |
| 1083 | char * |
| 1084 | ifetch(line,whichbuf) |
| 1085 | register LINENUM line; |
| 1086 | int whichbuf; /* ignored when file in memory */ |
| 1087 | { |
| 1088 | if (line < 1 || line > input_lines) |
| 1089 | return ""; |
| 1090 | if (using_plan_a) |
| 1091 | return i_ptr[line]; |
| 1092 | else { |
| 1093 | LINENUM offline = line % lines_per_buf; |
| 1094 | LINENUM baseline = line - offline; |
| 1095 | |
| 1096 | if (tiline[0] == baseline) |
| 1097 | whichbuf = 0; |
| 1098 | else if (tiline[1] == baseline) |
| 1099 | whichbuf = 1; |
| 1100 | else { |
| 1101 | tiline[whichbuf] = baseline; |
| 1102 | Lseek(tifd,(long)baseline / lines_per_buf * BUFFERSIZE,0); |
| 1103 | if (read(tifd,tibuf[whichbuf],BUFFERSIZE) < 0) |
| 1104 | fatal("Error reading tmp file %s.\n",TMPINNAME); |
| 1105 | } |
| 1106 | return tibuf[whichbuf] + (tireclen*offline); |
| 1107 | } |
| 1108 | } |
| 1109 | |
| 1110 | /* patch abstract type */ |
| 1111 | |
| 1112 | static long p_filesize; /* size of the patch file */ |
| 1113 | static LINENUM p_first; /* 1st line number */ |
| 1114 | static LINENUM p_newfirst; /* 1st line number of replacement */ |
| 1115 | static LINENUM p_ptrn_lines; /* # lines in pattern */ |
| 1116 | static LINENUM p_repl_lines; /* # lines in replacement text */ |
| 1117 | static LINENUM p_end = -1; /* last line in hunk */ |
| 1118 | static LINENUM p_max; /* max allowed value of p_end */ |
| 1119 | static LINENUM p_context = 3; /* # of context lines */ |
| 1120 | static LINENUM p_input_line = 0; /* current line # from patch file */ |
| 1121 | static char *p_line[MAXHUNKSIZE]; /* the text of the hunk */ |
| 1122 | static char p_char[MAXHUNKSIZE]; /* +, -, and ! */ |
| 1123 | static int p_len[MAXHUNKSIZE]; /* length of each line */ |
| 1124 | static int p_indent; /* indent to patch */ |
| 1125 | static long p_base; /* where to intuit this time */ |
| 1126 | static long p_start; /* where intuit found a patch */ |
| 1127 | |
| 1128 | re_patch() |
| 1129 | { |
| 1130 | p_first = (LINENUM)0; |
| 1131 | p_newfirst = (LINENUM)0; |
| 1132 | p_ptrn_lines = (LINENUM)0; |
| 1133 | p_repl_lines = (LINENUM)0; |
| 1134 | p_end = (LINENUM)-1; |
| 1135 | p_max = (LINENUM)0; |
| 1136 | p_indent = 0; |
| 1137 | } |
| 1138 | |
| 1139 | open_patch_file(filename) |
| 1140 | char *filename; |
| 1141 | { |
| 1142 | if (filename == Nullch || !*filename || strEQ(filename,"-")) { |
| 1143 | pfp = fopen(TMPPATNAME,"w"); |
| 1144 | if (pfp == Nullfp) |
| 1145 | fatal("patch: can't create %s.\n",TMPPATNAME); |
| 1146 | while (fgets(buf,sizeof buf,stdin) != NULL) |
| 1147 | fputs(buf,pfp); |
| 1148 | Fclose(pfp); |
| 1149 | filename = TMPPATNAME; |
| 1150 | } |
| 1151 | pfp = fopen(filename,"r"); |
| 1152 | if (pfp == Nullfp) |
| 1153 | fatal("patch file %s not found\n",filename); |
| 1154 | Fstat(fileno(pfp), &filestat); |
| 1155 | p_filesize = filestat.st_size; |
| 1156 | next_intuit_at(0L); /* start at the beginning */ |
| 1157 | } |
| 1158 | |
| 1159 | bool |
| 1160 | there_is_another_patch() |
| 1161 | { |
| 1162 | bool no_input_file = (filearg[0] == Nullch); |
| 1163 | |
| 1164 | if (p_base != 0L && p_base >= p_filesize) { |
| 1165 | if (verbose) |
| 1166 | say("done\n"); |
| 1167 | return FALSE; |
| 1168 | } |
| 1169 | if (verbose) |
| 1170 | say("Hmm..."); |
| 1171 | diff_type = intuit_diff_type(); |
| 1172 | if (!diff_type) { |
| 1173 | if (p_base != 0L) { |
| 1174 | if (verbose) |
| 1175 | say(" Ignoring the trailing garbage.\ndone\n"); |
| 1176 | } |
| 1177 | else |
| 1178 | say(" I can't seem to find a patch in there anywhere.\n"); |
| 1179 | return FALSE; |
| 1180 | } |
| 1181 | if (verbose) |
| 1182 | say(" %sooks like %s to me...\n", |
| 1183 | (p_base == 0L ? "L" : "The next patch l"), |
| 1184 | diff_type == CONTEXT_DIFF ? "a context diff" : |
| 1185 | diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : |
| 1186 | diff_type == NORMAL_DIFF ? "a normal diff" : |
| 1187 | "an ed script" ); |
| 1188 | if (p_indent && verbose) |
| 1189 | say("(Patch is indented %d space%s.)\n",p_indent,p_indent==1?"":"s"); |
| 1190 | skip_to(p_start); |
| 1191 | if (no_input_file) { |
| 1192 | while (filearg[0] == Nullch) { |
| 1193 | ask("File to patch: "); |
| 1194 | filearg[0] = fetchname(buf); |
| 1195 | } |
| 1196 | if (verbose) { |
| 1197 | say("Patching file %s...\n",filearg[0]); |
| 1198 | } |
| 1199 | } |
| 1200 | return TRUE; |
| 1201 | } |
| 1202 | |
| 1203 | intuit_diff_type() |
| 1204 | { |
| 1205 | long this_line = 0; |
| 1206 | long previous_line; |
| 1207 | long first_command_line = -1; |
| 1208 | bool last_line_was_command = FALSE; |
| 1209 | bool this_line_is_command = FALSE; |
| 1210 | bool last_line_was_stars = FALSE; |
| 1211 | bool this_line_is_stars = FALSE; |
| 1212 | register int indent; |
| 1213 | register char *s, *t; |
| 1214 | char *oldname = Nullch; |
| 1215 | char *newname = Nullch; |
| 1216 | bool no_filearg = (filearg[0] == Nullch); |
| 1217 | |
| 1218 | Fseek(pfp,p_base,0); |
| 1219 | for (;;) { |
| 1220 | previous_line = this_line; |
| 1221 | last_line_was_command = this_line_is_command; |
| 1222 | last_line_was_stars = this_line_is_stars; |
| 1223 | this_line = ftell(pfp); |
| 1224 | indent = 0; |
| 1225 | if (fgets(buf,sizeof buf,pfp) == Nullch) { |
| 1226 | if (first_command_line >= 0L) { |
| 1227 | /* nothing but deletes!? */ |
| 1228 | p_start = first_command_line; |
| 1229 | return ED_DIFF; |
| 1230 | } |
| 1231 | else { |
| 1232 | p_start = this_line; |
| 1233 | return 0; |
| 1234 | } |
| 1235 | } |
| 1236 | for (s = buf; *s == ' ' || *s == '\t'; s++) { |
| 1237 | if (*s == '\t') |
| 1238 | indent += 8 - (indent % 8); |
| 1239 | else |
| 1240 | indent++; |
| 1241 | } |
| 1242 | for (t=s; isdigit(*t) || *t == ','; t++) ; |
| 1243 | this_line_is_command = (isdigit(*s) && |
| 1244 | (*t == 'd' || *t == 'c' || *t == 'a') ); |
| 1245 | if (first_command_line < 0L && this_line_is_command) { |
| 1246 | first_command_line = this_line; |
| 1247 | p_indent = indent; /* assume this for now */ |
| 1248 | } |
| 1249 | if (strnEQ(s,"*** ",4)) |
| 1250 | oldname = fetchname(s+4); |
| 1251 | else if (strnEQ(s,"--- ",4)) { |
| 1252 | newname = fetchname(s+4); |
| 1253 | if (no_filearg) { |
| 1254 | if (oldname && newname) { |
| 1255 | if (strlen(oldname) < strlen(newname)) |
| 1256 | filearg[0] = oldname; |
| 1257 | else |
| 1258 | filearg[0] = newname; |
| 1259 | } |
| 1260 | else if (oldname) |
| 1261 | filearg[0] = oldname; |
| 1262 | else if (newname) |
| 1263 | filearg[0] = newname; |
| 1264 | } |
| 1265 | } |
| 1266 | else if (strnEQ(s,"Index:",6)) { |
| 1267 | if (no_filearg) |
| 1268 | filearg[0] = fetchname(s+6); |
| 1269 | /* this filearg might get limboed */ |
| 1270 | } |
| 1271 | else if (strnEQ(s,"Prereq:",7)) { |
| 1272 | for (t=s+7; isspace(*t); t++) ; |
| 1273 | revision = savestr(t); |
| 1274 | for (t=revision; *t && !isspace(*t); t++) ; |
| 1275 | *t = '\0'; |
| 1276 | if (!*revision) { |
| 1277 | free(revision); |
| 1278 | revision = Nullch; |
| 1279 | } |
| 1280 | } |
| 1281 | if ((!diff_type || diff_type == ED_DIFF) && |
| 1282 | first_command_line >= 0L && |
| 1283 | strEQ(s,".\n") ) { |
| 1284 | p_indent = indent; |
| 1285 | p_start = first_command_line; |
| 1286 | return ED_DIFF; |
| 1287 | } |
| 1288 | this_line_is_stars = strnEQ(s,"********",8); |
| 1289 | if ((!diff_type || diff_type == CONTEXT_DIFF) && last_line_was_stars && |
| 1290 | strnEQ(s,"*** ",4)) { |
| 1291 | /* if this is a new context diff the character just before */ |
| 1292 | /* the newline is a '*'. */ |
| 1293 | while (*s != '\n') |
| 1294 | s++; |
| 1295 | p_indent = indent; |
| 1296 | p_start = previous_line; |
| 1297 | return (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); |
| 1298 | } |
| 1299 | if ((!diff_type || diff_type == NORMAL_DIFF) && |
| 1300 | last_line_was_command && |
| 1301 | (strnEQ(s,"< ",2) || strnEQ(s,"> ",2)) ) { |
| 1302 | p_start = previous_line; |
| 1303 | p_indent = indent; |
| 1304 | return NORMAL_DIFF; |
| 1305 | } |
| 1306 | } |
| 1307 | } |
| 1308 | |
| 1309 | char * |
| 1310 | fetchname(at) |
| 1311 | char *at; |
| 1312 | { |
| 1313 | char *s = savestr(at); |
| 1314 | char *name; |
| 1315 | register char *t; |
| 1316 | char tmpbuf[200]; |
| 1317 | |
| 1318 | for (t=s; isspace(*t); t++) ; |
| 1319 | name = t; |
| 1320 | for (; *t && !isspace(*t); t++) |
| 1321 | if (!usepath) |
| 1322 | if (*t == '/') |
| 1323 | name = t+1; |
| 1324 | *t = '\0'; |
| 1325 | name = savestr(name); |
| 1326 | Sprintf(tmpbuf,"RCS/%s",name); |
| 1327 | free(s); |
| 1328 | if (stat(name,&filestat) < 0) { |
| 1329 | Strcat(tmpbuf,RCSSUFFIX); |
| 1330 | if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+4,&filestat) < 0) { |
| 1331 | Sprintf(tmpbuf,"SCCS/%s%s",SCCSPREFIX,name); |
| 1332 | if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+5,&filestat) < 0) { |
| 1333 | free(name); |
| 1334 | name = Nullch; |
| 1335 | } |
| 1336 | } |
| 1337 | } |
| 1338 | return name; |
| 1339 | } |
| 1340 | |
| 1341 | next_intuit_at(file_pos) |
| 1342 | long file_pos; |
| 1343 | { |
| 1344 | p_base = file_pos; |
| 1345 | } |
| 1346 | |
| 1347 | skip_to(file_pos) |
| 1348 | long file_pos; |
| 1349 | { |
| 1350 | char *ret; |
| 1351 | |
| 1352 | assert(p_base <= file_pos); |
| 1353 | if (verbose && p_base < file_pos) { |
| 1354 | Fseek(pfp,p_base,0); |
| 1355 | say("The text leading up to this was:\n--------------------------\n"); |
| 1356 | while (ftell(pfp) < file_pos) { |
| 1357 | ret = fgets(buf,sizeof buf,pfp); |
| 1358 | assert(ret != Nullch); |
| 1359 | say("|%s",buf); |
| 1360 | } |
| 1361 | say("--------------------------\n"); |
| 1362 | } |
| 1363 | else |
| 1364 | Fseek(pfp,file_pos,0); |
| 1365 | } |
| 1366 | |
| 1367 | bool |
| 1368 | another_hunk() |
| 1369 | { |
| 1370 | register char *s; |
| 1371 | char *ret; |
| 1372 | register int context = 0; |
| 1373 | |
| 1374 | while (p_end >= 0) { |
| 1375 | free(p_line[p_end--]); |
| 1376 | } |
| 1377 | assert(p_end == -1); |
| 1378 | |
| 1379 | p_max = MAXHUNKSIZE; /* gets reduced when --- found */ |
| 1380 | if (diff_type == CONTEXT_DIFF) { |
| 1381 | long line_beginning = ftell(pfp); |
| 1382 | LINENUM repl_beginning = 0; |
| 1383 | |
| 1384 | ret = pgets(buf,sizeof buf, pfp); |
| 1385 | if (ret == Nullch || strnNE(buf,"********",8)) { |
| 1386 | next_intuit_at(line_beginning); |
| 1387 | return FALSE; |
| 1388 | } |
| 1389 | p_context = 100; |
| 1390 | while (p_end < p_max) { |
| 1391 | ret = pgets(buf,sizeof buf, pfp); |
| 1392 | if (ret == Nullch) { |
| 1393 | if (p_max - p_end < 4) |
| 1394 | Strcpy(buf," \n"); /* assume blank lines got chopped */ |
| 1395 | else |
| 1396 | fatal("Unexpected end of file in patch.\n"); |
| 1397 | } |
| 1398 | p_input_line++; |
| 1399 | if (strnEQ(buf,"********",8)) |
| 1400 | fatal("Unexpected end of hunk at line %d.\n", |
| 1401 | p_input_line); |
| 1402 | p_char[++p_end] = *buf; |
| 1403 | switch (*buf) { |
| 1404 | case '*': |
| 1405 | if (p_end != 0) |
| 1406 | fatal("Unexpected *** at line %d: %s", p_input_line, buf); |
| 1407 | context = 0; |
| 1408 | p_line[p_end] = savestr(buf); |
| 1409 | for (s=buf; *s && !isdigit(*s); s++) ; |
| 1410 | if (!isdigit(*s)) |
| 1411 | fatal("Malformed patch at line %d: %s", p_input_line, buf); |
| 1412 | p_first = (LINENUM) atol(s); |
| 1413 | while (isdigit(*s)) s++; |
| 1414 | for (; *s && !isdigit(*s); s++) ; |
| 1415 | if (!isdigit(*s)) |
| 1416 | p_ptrn_lines = 1; |
| 1417 | else |
| 1418 | p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1; |
| 1419 | break; |
| 1420 | case '-': |
| 1421 | if (buf[1] == '-') { |
| 1422 | if (p_end != p_ptrn_lines + 1 && |
| 1423 | p_end != p_ptrn_lines + 2) |
| 1424 | fatal("Unexpected --- at line %d: %s", |
| 1425 | p_input_line,buf); |
| 1426 | repl_beginning = p_end; |
| 1427 | context = 0; |
| 1428 | p_line[p_end] = savestr(buf); |
| 1429 | p_char[p_end] = '='; |
| 1430 | for (s=buf; *s && !isdigit(*s); s++) ; |
| 1431 | if (!isdigit(*s)) |
| 1432 | fatal("Malformed patch at line %d: %s", |
| 1433 | p_input_line, buf); |
| 1434 | p_newfirst = (LINENUM) atol(s); |
| 1435 | while (isdigit(*s)) s++; |
| 1436 | for (; *s && !isdigit(*s); s++) ; |
| 1437 | if (!isdigit(*s)) |
| 1438 | p_max = p_newfirst; |
| 1439 | else |
| 1440 | p_max = ((LINENUM)atol(s)); |
| 1441 | p_max += 1 + p_end - p_newfirst; |
| 1442 | if (p_max >= MAXHUNKSIZE) |
| 1443 | fatal("Hunk too large (%d lines) at line %d: %s", |
| 1444 | p_max - p_end, p_input_line, buf); |
| 1445 | break; |
| 1446 | } |
| 1447 | /* FALL THROUGH */ |
| 1448 | case '+': case '!': |
| 1449 | if (context > 0) { |
| 1450 | if (context < p_context) |
| 1451 | p_context = context; |
| 1452 | context = -100; |
| 1453 | } |
| 1454 | p_line[p_end] = savestr(buf+2); |
| 1455 | break; |
| 1456 | case '\t': case '\n': /* assume the 2 spaces got eaten */ |
| 1457 | p_line[p_end] = savestr(buf); |
| 1458 | if (p_end != p_ptrn_lines + 1) { |
| 1459 | context++; |
| 1460 | p_char[p_end] = ' '; |
| 1461 | } |
| 1462 | break; |
| 1463 | case ' ': |
| 1464 | context++; |
| 1465 | p_line[p_end] = savestr(buf+2); |
| 1466 | break; |
| 1467 | default: |
| 1468 | fatal("Malformed patch at line %d: %s",p_input_line,buf); |
| 1469 | } |
| 1470 | /* set up p_len for strncmp() so we don't have to */ |
| 1471 | /* assume null termination */ |
| 1472 | if (p_line[p_end]) |
| 1473 | p_len[p_end] = strlen(p_line[p_end]); |
| 1474 | else |
| 1475 | p_len[p_end] = 0; |
| 1476 | } |
| 1477 | if (p_end >=0 && !p_ptrn_lines) |
| 1478 | fatal("No --- found in patch at line %d\n", pch_hunk_beg()); |
| 1479 | p_repl_lines = p_end - repl_beginning; |
| 1480 | p_char[p_end+1] = '^'; /* add a stopper for apply_hunk */ |
| 1481 | } |
| 1482 | else if (diff_type == NEW_CONTEXT_DIFF) { |
| 1483 | long line_beginning = ftell(pfp); |
| 1484 | LINENUM repl_beginning = 0; |
| 1485 | LINENUM fillcnt = 0; |
| 1486 | LINENUM fillsrc; |
| 1487 | LINENUM filldst; |
| 1488 | |
| 1489 | ret = pgets(buf,sizeof buf, pfp); |
| 1490 | if (ret == Nullch || strnNE(buf,"********",8)) { |
| 1491 | next_intuit_at(line_beginning); |
| 1492 | return FALSE; |
| 1493 | } |
| 1494 | p_context = 0; |
| 1495 | while (p_end < p_max) { |
| 1496 | ret = pgets(buf,sizeof buf, pfp); |
| 1497 | if (ret == Nullch) { |
| 1498 | if (p_max - p_end < 4) |
| 1499 | Strcpy(buf," \n"); /* assume blank lines got chopped */ |
| 1500 | else |
| 1501 | fatal("Unexpected end of file in patch.\n"); |
| 1502 | } |
| 1503 | p_input_line++; |
| 1504 | p_char[++p_end] = *buf; |
| 1505 | switch (*buf) { |
| 1506 | case '*': /* another hunk */ |
| 1507 | if (strnEQ(buf,"********",8)) |
| 1508 | fatal("Unexpected end of hunk at line %d.\n", |
| 1509 | p_input_line); |
| 1510 | |
| 1511 | if (p_end != 0) |
| 1512 | fatal("Unexpected *** at line %d: %s", p_input_line, buf); |
| 1513 | context = 0; |
| 1514 | p_line[p_end] = savestr(buf); |
| 1515 | for (s=buf; *s && !isdigit(*s); s++) ; |
| 1516 | if (!isdigit(*s)) |
| 1517 | fatal("Malformed patch at line %d: %s", p_input_line, buf); |
| 1518 | p_first = (LINENUM) atol(s); |
| 1519 | while (isdigit(*s)) s++; |
| 1520 | for (; *s && !isdigit(*s); s++) ; |
| 1521 | if (!isdigit(*s)) |
| 1522 | p_ptrn_lines = 1; |
| 1523 | else |
| 1524 | p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1; |
| 1525 | break; |
| 1526 | case '-': |
| 1527 | if (buf[1] == '-') { |
| 1528 | if (p_end != p_ptrn_lines + 1) { |
| 1529 | if (p_end == 1) { |
| 1530 | /* `old' lines were omitted - set up to fill them */ |
| 1531 | /* in from 'new' context lines. */ |
| 1532 | p_end = p_ptrn_lines + 1; |
| 1533 | fillsrc = p_end + 1; |
| 1534 | filldst = 1; |
| 1535 | fillcnt = p_ptrn_lines; |
| 1536 | } else |
| 1537 | fatal("Unexpected --- at line %d: %s", |
| 1538 | p_input_line,buf); |
| 1539 | } |
| 1540 | repl_beginning = p_end; |
| 1541 | p_line[p_end] = savestr(buf); |
| 1542 | p_char[p_end] = '='; |
| 1543 | for (s=buf; *s && !isdigit(*s); s++) ; |
| 1544 | if (!isdigit(*s)) |
| 1545 | fatal("Malformed patch at line %d: %s", |
| 1546 | p_input_line, buf); |
| 1547 | p_newfirst = (LINENUM) atol(s); |
| 1548 | while (isdigit(*s)) s++; |
| 1549 | for (; *s && !isdigit(*s); s++) ; |
| 1550 | if (!isdigit(*s)) |
| 1551 | p_max = p_newfirst; |
| 1552 | else |
| 1553 | p_max = ((LINENUM)atol(s)); |
| 1554 | p_max += 1 + p_end - p_newfirst; |
| 1555 | if (p_max >= MAXHUNKSIZE) |
| 1556 | fatal("Hunk too large (%d lines) at line %d: %s", |
| 1557 | p_max - p_end, p_input_line, buf); |
| 1558 | if (p_max - p_end == context) { |
| 1559 | /* redundant 'new' context lines were omitted */ |
| 1560 | /* set up to fill them in from the the old file's */ |
| 1561 | /* context */ |
| 1562 | fillsrc = 1; |
| 1563 | filldst = p_end + 1; |
| 1564 | fillcnt = p_max - repl_beginning; |
| 1565 | p_end = p_max; |
| 1566 | } |
| 1567 | break; |
| 1568 | } |
| 1569 | /* FALL THROUGH */ |
| 1570 | case '+': case '!': |
| 1571 | if (context > 0 && p_context == 0) { |
| 1572 | p_context = context; |
| 1573 | } |
| 1574 | p_line[p_end] = savestr(buf+2); |
| 1575 | break; |
| 1576 | case '\t': case '\n': /* assume the 2 spaces got eaten */ |
| 1577 | p_line[p_end] = savestr(buf); |
| 1578 | context++; |
| 1579 | p_char[p_end] = ' '; |
| 1580 | break; |
| 1581 | case ' ': |
| 1582 | context++; |
| 1583 | p_line[p_end] = savestr(buf+2); |
| 1584 | break; |
| 1585 | default: |
| 1586 | fatal("Malformed patch at line %d: %s",p_input_line,buf); |
| 1587 | } |
| 1588 | /* set up p_len for strncmp() so we don't have to */ |
| 1589 | /* assume null termination */ |
| 1590 | if (p_line[p_end]) |
| 1591 | p_len[p_end] = strlen(p_line[p_end]); |
| 1592 | else |
| 1593 | p_len[p_end] = 0; |
| 1594 | } |
| 1595 | if (p_end >=0 && !p_ptrn_lines) |
| 1596 | fatal("No --- found in patch at line %d\n", pch_hunk_beg()); |
| 1597 | |
| 1598 | /* if there were omitted context lines, fill them in */ |
| 1599 | if (fillcnt) { |
| 1600 | while (fillcnt-- > 0) { |
| 1601 | while (p_char[fillsrc] != ' ') |
| 1602 | fillsrc++; |
| 1603 | if (p_line[fillsrc]) |
| 1604 | p_line[filldst] = savestr (p_line[fillsrc]); |
| 1605 | else |
| 1606 | p_line[filldst] = p_line[fillsrc]; |
| 1607 | p_char[filldst] = p_char[fillsrc]; |
| 1608 | p_len[filldst] = p_len[fillsrc]; |
| 1609 | fillsrc++; filldst++; |
| 1610 | } |
| 1611 | assert(filldst==p_end+1 || filldst==repl_beginning); |
| 1612 | } |
| 1613 | p_repl_lines = p_end - repl_beginning; |
| 1614 | p_char[p_end+1] = '^'; /* add a stopper for apply_hunk */ |
| 1615 | } |
| 1616 | else { /* normal diff--fake it up */ |
| 1617 | char hunk_type; |
| 1618 | register int i; |
| 1619 | LINENUM min, max; |
| 1620 | long line_beginning = ftell(pfp); |
| 1621 | |
| 1622 | p_context = 0; |
| 1623 | ret = pgets(buf,sizeof buf, pfp); |
| 1624 | p_input_line++; |
| 1625 | if (ret == Nullch || !isdigit(*buf)) { |
| 1626 | next_intuit_at(line_beginning); |
| 1627 | return FALSE; |
| 1628 | } |
| 1629 | p_first = (LINENUM)atol(buf); |
| 1630 | for (s=buf; isdigit(*s); s++) ; |
| 1631 | if (*s == ',') { |
| 1632 | p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1; |
| 1633 | while (isdigit(*s)) s++; |
| 1634 | } |
| 1635 | else |
| 1636 | p_ptrn_lines = (*s != 'a'); |
| 1637 | hunk_type = *s; |
| 1638 | if (hunk_type == 'a') |
| 1639 | p_first++; /* do append rather than insert */ |
| 1640 | min = (LINENUM)atol(++s); |
| 1641 | for (; isdigit(*s); s++) ; |
| 1642 | if (*s == ',') |
| 1643 | max = (LINENUM)atol(++s); |
| 1644 | else |
| 1645 | max = min; |
| 1646 | if (hunk_type == 'd') |
| 1647 | min++; |
| 1648 | p_end = p_ptrn_lines + 1 + max - min + 1; |
| 1649 | p_newfirst = min; |
| 1650 | p_repl_lines = max - min + 1; |
| 1651 | Sprintf(buf,"*** %d,%d\n", p_first, p_first + p_ptrn_lines - 1); |
| 1652 | p_line[0] = savestr(buf); |
| 1653 | p_char[0] = '*'; |
| 1654 | for (i=1; i<=p_ptrn_lines; i++) { |
| 1655 | ret = pgets(buf,sizeof buf, pfp); |
| 1656 | p_input_line++; |
| 1657 | if (ret == Nullch) |
| 1658 | fatal("Unexpected end of file in patch at line %d.\n", |
| 1659 | p_input_line); |
| 1660 | if (*buf != '<') |
| 1661 | fatal("< expected at line %d of patch.\n", p_input_line); |
| 1662 | p_line[i] = savestr(buf+2); |
| 1663 | if (p_line[i]) |
| 1664 | p_len[i] = strlen(p_line[i]); |
| 1665 | else |
| 1666 | p_len[i] = 0; |
| 1667 | p_char[i] = '-'; |
| 1668 | } |
| 1669 | if (hunk_type == 'c') { |
| 1670 | ret = pgets(buf,sizeof buf, pfp); |
| 1671 | p_input_line++; |
| 1672 | if (ret == Nullch) |
| 1673 | fatal("Unexpected end of file in patch at line %d.\n", |
| 1674 | p_input_line); |
| 1675 | if (*buf != '-') |
| 1676 | fatal("--- expected at line %d of patch.\n", p_input_line); |
| 1677 | } |
| 1678 | Sprintf(buf,"--- %d,%d\n",min,max); |
| 1679 | p_line[i] = savestr(buf); |
| 1680 | p_char[i] = '='; |
| 1681 | for (i++; i<=p_end; i++) { |
| 1682 | ret = pgets(buf,sizeof buf, pfp); |
| 1683 | p_input_line++; |
| 1684 | if (ret == Nullch) |
| 1685 | fatal("Unexpected end of file in patch at line %d.\n", |
| 1686 | p_input_line); |
| 1687 | if (*buf != '>') |
| 1688 | fatal("> expected at line %d of patch.\n", p_input_line); |
| 1689 | p_line[i] = savestr(buf+2); |
| 1690 | /* set up p_len for strncmp() so we don't have to */ |
| 1691 | /* assume null termination */ |
| 1692 | if (p_line[i]) |
| 1693 | p_len[i] = strlen(p_line[i]); |
| 1694 | else |
| 1695 | p_len[i] = 0; |
| 1696 | p_char[i] = '+'; |
| 1697 | } |
| 1698 | } |
| 1699 | if (reverse) /* backwards patch? */ |
| 1700 | pch_swap(); |
| 1701 | #ifdef DEBUGGING |
| 1702 | if (debug & 2) { |
| 1703 | int i; |
| 1704 | char special; |
| 1705 | |
| 1706 | for (i=0; i <= p_end; i++) { |
| 1707 | if (i == p_ptrn_lines) |
| 1708 | special = '^'; |
| 1709 | else |
| 1710 | special = ' '; |
| 1711 | printf("%3d %c %c %s",i,p_char[i],special,p_line[i]); |
| 1712 | } |
| 1713 | } |
| 1714 | #endif |
| 1715 | return TRUE; |
| 1716 | } |
| 1717 | |
| 1718 | char * |
| 1719 | pgets(bf,sz,fp) |
| 1720 | char *bf; |
| 1721 | int sz; |
| 1722 | FILE *fp; |
| 1723 | { |
| 1724 | char *ret = fgets(bf,sz,fp); |
| 1725 | register char *s; |
| 1726 | register int indent = 0; |
| 1727 | |
| 1728 | if (p_indent && ret != Nullch) { |
| 1729 | for (s=buf; indent < p_indent && (*s == ' ' || *s == '\t'); s++) { |
| 1730 | if (*s == '\t') |
| 1731 | indent += 8 - (indent % 7); |
| 1732 | else |
| 1733 | indent++; |
| 1734 | } |
| 1735 | if (buf != s) |
| 1736 | Strcpy(buf,s); |
| 1737 | } |
| 1738 | return ret; |
| 1739 | } |
| 1740 | |
| 1741 | pch_swap() |
| 1742 | { |
| 1743 | char *tp_line[MAXHUNKSIZE]; /* the text of the hunk */ |
| 1744 | char tp_char[MAXHUNKSIZE]; /* +, -, and ! */ |
| 1745 | int tp_len[MAXHUNKSIZE]; /* length of each line */ |
| 1746 | register LINENUM i, n; |
| 1747 | bool blankline = FALSE; |
| 1748 | register char *s; |
| 1749 | |
| 1750 | i = p_first; |
| 1751 | p_first = p_newfirst; |
| 1752 | p_newfirst = i; |
| 1753 | |
| 1754 | /* make a scratch copy */ |
| 1755 | |
| 1756 | for (i=0; i<=p_end; i++) { |
| 1757 | tp_line[i] = p_line[i]; |
| 1758 | tp_char[i] = p_char[i]; |
| 1759 | tp_len[i] = p_len[i]; |
| 1760 | } |
| 1761 | |
| 1762 | /* now turn the new into the old */ |
| 1763 | |
| 1764 | i = p_ptrn_lines + 1; |
| 1765 | if (tp_char[i] == '\n') { /* account for possible blank line */ |
| 1766 | blankline = TRUE; |
| 1767 | i++; |
| 1768 | } |
| 1769 | for (n=0; i <= p_end; i++,n++) { |
| 1770 | p_line[n] = tp_line[i]; |
| 1771 | p_char[n] = tp_char[i]; |
| 1772 | if (p_char[n] == '+') |
| 1773 | p_char[n] = '-'; |
| 1774 | p_len[n] = tp_len[i]; |
| 1775 | } |
| 1776 | if (blankline) { |
| 1777 | i = p_ptrn_lines + 1; |
| 1778 | p_line[n] = tp_line[i]; |
| 1779 | p_char[n] = tp_char[i]; |
| 1780 | p_len[n] = tp_len[i]; |
| 1781 | n++; |
| 1782 | } |
| 1783 | assert(p_char[0] == '='); |
| 1784 | p_char[0] = '*'; |
| 1785 | for (s=p_line[0]; *s; s++) |
| 1786 | if (*s == '-') |
| 1787 | *s = '*'; |
| 1788 | |
| 1789 | /* now turn the old into the new */ |
| 1790 | |
| 1791 | assert(tp_char[0] == '*'); |
| 1792 | tp_char[0] = '='; |
| 1793 | for (s=tp_line[0]; *s; s++) |
| 1794 | if (*s == '*') |
| 1795 | *s = '-'; |
| 1796 | for (i=0; n <= p_end; i++,n++) { |
| 1797 | p_line[n] = tp_line[i]; |
| 1798 | p_char[n] = tp_char[i]; |
| 1799 | if (p_char[n] == '-') |
| 1800 | p_char[n] = '+'; |
| 1801 | p_len[n] = tp_len[i]; |
| 1802 | } |
| 1803 | assert(i == p_ptrn_lines + 1); |
| 1804 | i = p_ptrn_lines; |
| 1805 | p_ptrn_lines = p_repl_lines; |
| 1806 | p_repl_lines = i; |
| 1807 | } |
| 1808 | |
| 1809 | LINENUM |
| 1810 | pch_first() |
| 1811 | { |
| 1812 | return p_first; |
| 1813 | } |
| 1814 | |
| 1815 | LINENUM |
| 1816 | pch_ptrn_lines() |
| 1817 | { |
| 1818 | return p_ptrn_lines; |
| 1819 | } |
| 1820 | |
| 1821 | LINENUM |
| 1822 | pch_newfirst() |
| 1823 | { |
| 1824 | return p_newfirst; |
| 1825 | } |
| 1826 | |
| 1827 | LINENUM |
| 1828 | pch_repl_lines() |
| 1829 | { |
| 1830 | return p_repl_lines; |
| 1831 | } |
| 1832 | |
| 1833 | LINENUM |
| 1834 | pch_end() |
| 1835 | { |
| 1836 | return p_end; |
| 1837 | } |
| 1838 | |
| 1839 | LINENUM |
| 1840 | pch_context() |
| 1841 | { |
| 1842 | return p_context; |
| 1843 | } |
| 1844 | |
| 1845 | pch_line_len(line) |
| 1846 | LINENUM line; |
| 1847 | { |
| 1848 | return p_len[line]; |
| 1849 | } |
| 1850 | |
| 1851 | char |
| 1852 | pch_char(line) |
| 1853 | LINENUM line; |
| 1854 | { |
| 1855 | return p_char[line]; |
| 1856 | } |
| 1857 | |
| 1858 | char * |
| 1859 | pfetch(line) |
| 1860 | LINENUM line; |
| 1861 | { |
| 1862 | return p_line[line]; |
| 1863 | } |
| 1864 | |
| 1865 | LINENUM |
| 1866 | pch_hunk_beg() |
| 1867 | { |
| 1868 | return p_input_line - p_end - 1; |
| 1869 | } |
| 1870 | |
| 1871 | char * |
| 1872 | savestr(s) |
| 1873 | register char *s; |
| 1874 | { |
| 1875 | register char *rv, |
| 1876 | *t; |
| 1877 | |
| 1878 | t = s; |
| 1879 | while (*t++); |
| 1880 | rv = malloc((MEM) (t - s)); |
| 1881 | if (rv == NULL) |
| 1882 | fatal ("patch: out of memory (savestr)\n"); |
| 1883 | t = rv; |
| 1884 | while (*t++ = *s++); |
| 1885 | return rv; |
| 1886 | } |
| 1887 | |
| 1888 | my_exit(status) |
| 1889 | int status; |
| 1890 | { |
| 1891 | Unlink(TMPINNAME); |
| 1892 | Unlink(TMPOUTNAME); |
| 1893 | Unlink(TMPREJNAME); |
| 1894 | Unlink(TMPPATNAME); |
| 1895 | exit(status); |
| 1896 | } |
| 1897 | |
| 1898 | #ifdef lint |
| 1899 | |
| 1900 | /*VARARGS ARGSUSED*/ |
| 1901 | say(pat) char *pat; { ; } |
| 1902 | /*VARARGS ARGSUSED*/ |
| 1903 | fatal(pat) char *pat; { ; } |
| 1904 | /*VARARGS ARGSUSED*/ |
| 1905 | ask(pat) char *pat; { ; } |
| 1906 | |
| 1907 | #else lint |
| 1908 | |
| 1909 | say(pat,arg1,arg2,arg3) |
| 1910 | char *pat; |
| 1911 | int arg1,arg2,arg3; |
| 1912 | { |
| 1913 | fprintf(stderr,pat,arg1,arg2,arg3); |
| 1914 | Fflush(stderr); |
| 1915 | } |
| 1916 | |
| 1917 | fatal(pat,arg1,arg2,arg3) |
| 1918 | char *pat; |
| 1919 | int arg1,arg2,arg3; |
| 1920 | { |
| 1921 | say(pat,arg1,arg2,arg3); |
| 1922 | my_exit(1); |
| 1923 | } |
| 1924 | |
| 1925 | ask(pat,arg1,arg2,arg3) |
| 1926 | char *pat; |
| 1927 | int arg1,arg2,arg3; |
| 1928 | { |
| 1929 | int ttyfd = open("/dev/tty",2); |
| 1930 | int r; |
| 1931 | |
| 1932 | say(pat,arg1,arg2,arg3); |
| 1933 | if (ttyfd >= 0) { |
| 1934 | r = read(ttyfd, buf, sizeof buf); |
| 1935 | Close(ttyfd); |
| 1936 | } |
| 1937 | else |
| 1938 | r = read(2, buf, sizeof buf); |
| 1939 | if (r <= 0) |
| 1940 | buf[0] = 0; |
| 1941 | } |
| 1942 | #endif lint |
| 1943 | |
| 1944 | bool |
| 1945 | rev_in_string(string) |
| 1946 | char *string; |
| 1947 | { |
| 1948 | register char *s; |
| 1949 | register int patlen; |
| 1950 | |
| 1951 | if (revision == Nullch) |
| 1952 | return TRUE; |
| 1953 | patlen = strlen(revision); |
| 1954 | for (s = string; *s; s++) { |
| 1955 | if (isspace(*s) && strnEQ(s+1,revision,patlen) && |
| 1956 | isspace(s[patlen+1] )) { |
| 1957 | return TRUE; |
| 1958 | } |
| 1959 | } |
| 1960 | return FALSE; |
| 1961 | } |
| 1962 | |
| 1963 | set_signals() |
| 1964 | { |
| 1965 | /*NOSTRICT*/ |
| 1966 | if (signal(SIGHUP, SIG_IGN) != SIG_IGN) |
| 1967 | Signal(SIGHUP, my_exit); |
| 1968 | /*NOSTRICT*/ |
| 1969 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) |
| 1970 | Signal(SIGINT, my_exit); |
| 1971 | } |
| 1972 | |
| 1973 | ignore_signals() |
| 1974 | { |
| 1975 | /*NOSTRICT*/ |
| 1976 | Signal(SIGHUP, SIG_IGN); |
| 1977 | /*NOSTRICT*/ |
| 1978 | Signal(SIGINT, SIG_IGN); |
| 1979 | } |