Home | History | Annotate | Line # | Download | only in patch
patch.c revision 1.1.1.1
      1 #ifndef lint
      2 static char sccsid[] = "@(#)patch.c	8.1 (Berkeley) 6/6/93";
      3 #endif not lint
      4 
      5 char rcsid[] =
      6 	"$Header: /tank/opengrok/rsync2/NetBSD/src/usr.bin/patch/patch.c,v 1.1.1.1 1997/01/09 14:47:39 tls Exp $";
      7 
      8 /* patch - a program to apply diffs to original files
      9  *
     10  * Copyright 1986, Larry Wall
     11  *
     12  * This program may be copied as long as you don't try to make any
     13  * money off of it, or pretend that you wrote it.
     14  *
     15  * $Log: patch.c,v $
     16  * Revision 1.1.1.1  1997/01/09 14:47:39  tls
     17  * Import from 4.4BSD-Lite2
     18  *
     19  * Revision 2.0.1.4  87/02/16  14:00:04  lwall
     20  * Short replacement caused spurious "Out of sync" message.
     21  *
     22  * Revision 2.0.1.3  87/01/30  22:45:50  lwall
     23  * Improved diagnostic on sync error.
     24  * Moved do_ed_script() to pch.c.
     25  *
     26  * Revision 2.0.1.2  86/11/21  09:39:15  lwall
     27  * Fuzz factor caused offset of installed lines.
     28  *
     29  * Revision 2.0.1.1  86/10/29  13:10:22  lwall
     30  * Backwards search could terminate prematurely.
     31  *
     32  * Revision 2.0  86/09/17  15:37:32  lwall
     33  * Baseline for netwide release.
     34  *
     35  * Revision 1.5  86/08/01  20:53:24  lwall
     36  * Changed some %d's to %ld's.
     37  * Linted.
     38  *
     39  * Revision 1.4  86/08/01  19:17:29  lwall
     40  * Fixes for machines that can't vararg.
     41  * Added fuzz factor.
     42  * Generalized -p.
     43  * General cleanup.
     44  *
     45  * 85/08/15 van%ucbmonet@berkeley
     46  * Changes for 4.3bsd diff -c.
     47  *
     48  * Revision 1.3  85/03/26  15:07:43  lwall
     49  * Frozen.
     50  *
     51  * Revision 1.2.1.9  85/03/12  17:03:35  lwall
     52  * Changed pfp->_file to fileno(pfp).
     53  *
     54  * Revision 1.2.1.8  85/03/12  16:30:43  lwall
     55  * Check i_ptr and i_womp to make sure they aren't null before freeing.
     56  * Also allow ed output to be suppressed.
     57  *
     58  * Revision 1.2.1.7  85/03/12  15:56:13  lwall
     59  * Added -p option from jromine@uci-750a.
     60  *
     61  * Revision 1.2.1.6  85/03/12  12:12:51  lwall
     62  * Now checks for normalness of file to patch.
     63  *
     64  * Revision 1.2.1.5  85/03/12  11:52:12  lwall
     65  * Added -D (#ifdef) option from joe@fluke.
     66  *
     67  * Revision 1.2.1.4  84/12/06  11:14:15  lwall
     68  * Made smarter about SCCS subdirectories.
     69  *
     70  * Revision 1.2.1.3  84/12/05  11:18:43  lwall
     71  * Added -l switch to do loose string comparison.
     72  *
     73  * Revision 1.2.1.2  84/12/04  09:47:13  lwall
     74  * Failed hunk count not reset on multiple patch file.
     75  *
     76  * Revision 1.2.1.1  84/12/04  09:42:37  lwall
     77  * Branch for sdcrdcf changes.
     78  *
     79  * Revision 1.2  84/11/29  13:29:51  lwall
     80  * Linted.  Identifiers uniqified.  Fixed i_ptr malloc() bug.  Fixed
     81  * multiple calls to mktemp().  Will now work on machines that can only
     82  * read 32767 chars.  Added -R option for diffs with new and old swapped.
     83  * Various cosmetic changes.
     84  *
     85  * Revision 1.1  84/11/09  17:03:58  lwall
     86  * Initial revision
     87  *
     88  */
     89 
     90 #include "INTERN.h"
     91 #include "common.h"
     92 #include "EXTERN.h"
     93 #include "version.h"
     94 #include "util.h"
     95 #include "pch.h"
     96 #include "inp.h"
     97 
     98 /* procedures */
     99 
    100 void reinitialize_almost_everything();
    101 void get_some_switches();
    102 LINENUM locate_hunk();
    103 void abort_hunk();
    104 void apply_hunk();
    105 void init_output();
    106 void init_reject();
    107 void copy_till();
    108 void spew_output();
    109 void dump_line();
    110 bool patch_match();
    111 bool similar();
    112 void re_input();
    113 void my_exit();
    114 
    115 /* Apply a set of diffs as appropriate. */
    116 
    117 main(argc,argv)
    118 int argc;
    119 char **argv;
    120 {
    121     LINENUM where;
    122     LINENUM newwhere;
    123     LINENUM fuzz;
    124     LINENUM mymaxfuzz;
    125     int hunk = 0;
    126     int failed = 0;
    127     int i;
    128 
    129     setbuf(stderr, serrbuf);
    130     for (i = 0; i<MAXFILEC; i++)
    131 	filearg[i] = Nullch;
    132     Mktemp(TMPOUTNAME);
    133     Mktemp(TMPINNAME);
    134     Mktemp(TMPREJNAME);
    135     Mktemp(TMPPATNAME);
    136 
    137     /* parse switches */
    138     Argc = argc;
    139     Argv = argv;
    140     get_some_switches();
    141 
    142     /* make sure we clean up /tmp in case of disaster */
    143     set_signals();
    144 
    145     for (
    146 	open_patch_file(filearg[1]);
    147 	there_is_another_patch();
    148 	reinitialize_almost_everything()
    149     ) {					/* for each patch in patch file */
    150 
    151 	if (outname == Nullch)
    152 	    outname = savestr(filearg[0]);
    153 
    154 	/* initialize the patched file */
    155 	if (!skip_rest_of_patch)
    156 	    init_output(TMPOUTNAME);
    157 
    158 	/* for ed script just up and do it and exit */
    159 	if (diff_type == ED_DIFF) {
    160 	    do_ed_script();
    161 	    continue;
    162 	}
    163 
    164 	/* initialize reject file */
    165 	init_reject(TMPREJNAME);
    166 
    167 	/* find out where all the lines are */
    168 	if (!skip_rest_of_patch)
    169 	    scan_input(filearg[0]);
    170 
    171 	/* from here on, open no standard i/o files, because malloc */
    172 	/* might misfire and we can't catch it easily */
    173 
    174 	/* apply each hunk of patch */
    175 	hunk = 0;
    176 	failed = 0;
    177 	out_of_mem = FALSE;
    178 	while (another_hunk()) {
    179 	    hunk++;
    180 	    fuzz = Nulline;
    181 	    mymaxfuzz = pch_context();
    182 	    if (maxfuzz < mymaxfuzz)
    183 		mymaxfuzz = maxfuzz;
    184 	    if (!skip_rest_of_patch) {
    185 		do {
    186 		    where = locate_hunk(fuzz);
    187 		    if (hunk == 1 && where == Nulline && !force) {
    188 						/* dwim for reversed patch? */
    189 			if (!pch_swap()) {
    190 			    if (fuzz == Nulline)
    191 				say1("\
    192 Not enough memory to try swapped hunk!  Assuming unswapped.\n");
    193 			    continue;
    194 			}
    195 			reverse = !reverse;
    196 			where = locate_hunk(fuzz);  /* try again */
    197 			if (where == Nulline) {	    /* didn't find it swapped */
    198 			    if (!pch_swap())         /* put it back to normal */
    199 				fatal1("Lost hunk on alloc error!\n");
    200 			    reverse = !reverse;
    201 			}
    202 			else if (noreverse) {
    203 			    if (!pch_swap())         /* put it back to normal */
    204 				fatal1("Lost hunk on alloc error!\n");
    205 			    reverse = !reverse;
    206 			    say1("\
    207 Ignoring previously applied (or reversed) patch.\n");
    208 			    skip_rest_of_patch = TRUE;
    209 			}
    210 			else {
    211 			    ask3("\
    212 %seversed (or previously applied) patch detected!  %s -R? [y] ",
    213 				reverse ? "R" : "Unr",
    214 				reverse ? "Assume" : "Ignore");
    215 			    if (*buf == 'n') {
    216 				ask1("Apply anyway? [n] ");
    217 				if (*buf != 'y')
    218 				    skip_rest_of_patch = TRUE;
    219 				where = Nulline;
    220 				reverse = !reverse;
    221 				if (!pch_swap())  /* put it back to normal */
    222 				    fatal1("Lost hunk on alloc error!\n");
    223 			    }
    224 			}
    225 		    }
    226 		} while (!skip_rest_of_patch && where == Nulline &&
    227 		    ++fuzz <= mymaxfuzz);
    228 
    229 		if (skip_rest_of_patch) {		/* just got decided */
    230 		    Fclose(ofp);
    231 		    ofp = Nullfp;
    232 		}
    233 	    }
    234 
    235 	    newwhere = pch_newfirst() + last_offset;
    236 	    if (skip_rest_of_patch) {
    237 		abort_hunk();
    238 		failed++;
    239 		if (verbose)
    240 		    say3("Hunk #%d ignored at %ld.\n", hunk, newwhere);
    241 	    }
    242 	    else if (where == Nulline) {
    243 		abort_hunk();
    244 		failed++;
    245 		if (verbose)
    246 		    say3("Hunk #%d failed at %ld.\n", hunk, newwhere);
    247 	    }
    248 	    else {
    249 		apply_hunk(where);
    250 		if (verbose) {
    251 		    say3("Hunk #%d succeeded at %ld", hunk, newwhere);
    252 		    if (fuzz)
    253 			say2(" with fuzz %ld", fuzz);
    254 		    if (last_offset)
    255 			say3(" (offset %ld line%s)",
    256 			    last_offset, last_offset==1L?"":"s");
    257 		    say1(".\n");
    258 		}
    259 	    }
    260 	}
    261 
    262 	if (out_of_mem && using_plan_a) {
    263 	    Argc = Argc_last;
    264 	    Argv = Argv_last;
    265 	    say1("\n\nRan out of memory using Plan A--trying again...\n\n");
    266 	    continue;
    267 	}
    268 
    269 	assert(hunk);
    270 
    271 	/* finish spewing out the new file */
    272 	if (!skip_rest_of_patch)
    273 	    spew_output();
    274 
    275 	/* and put the output where desired */
    276 	ignore_signals();
    277 	if (!skip_rest_of_patch) {
    278 	    if (move_file(TMPOUTNAME, outname) < 0) {
    279 		toutkeep = TRUE;
    280 		chmod(TMPOUTNAME, filemode);
    281 	    }
    282 	    else
    283 		chmod(outname, filemode);
    284 	}
    285 	Fclose(rejfp);
    286 	rejfp = Nullfp;
    287 	if (failed) {
    288 	    if (!*rejname) {
    289 		Strcpy(rejname, outname);
    290 		Strcat(rejname, ".rej");
    291 	    }
    292 	    if (skip_rest_of_patch) {
    293 		say4("%d out of %d hunks ignored--saving rejects to %s\n",
    294 		    failed, hunk, rejname);
    295 	    }
    296 	    else {
    297 		say4("%d out of %d hunks failed--saving rejects to %s\n",
    298 		    failed, hunk, rejname);
    299 	    }
    300 	    if (move_file(TMPREJNAME, rejname) < 0)
    301 		trejkeep = TRUE;
    302 	}
    303 	set_signals();
    304     }
    305     my_exit(0);
    306 }
    307 
    308 /* Prepare to find the next patch to do in the patch file. */
    309 
    310 void
    311 reinitialize_almost_everything()
    312 {
    313     re_patch();
    314     re_input();
    315 
    316     input_lines = 0;
    317     last_frozen_line = 0;
    318 
    319     filec = 0;
    320     if (filearg[0] != Nullch && !out_of_mem) {
    321 	free(filearg[0]);
    322 	filearg[0] = Nullch;
    323     }
    324 
    325     if (outname != Nullch) {
    326 	free(outname);
    327 	outname = Nullch;
    328     }
    329 
    330     last_offset = 0;
    331 
    332     diff_type = 0;
    333 
    334     if (revision != Nullch) {
    335 	free(revision);
    336 	revision = Nullch;
    337     }
    338 
    339     reverse = FALSE;
    340     skip_rest_of_patch = FALSE;
    341 
    342     get_some_switches();
    343 
    344     if (filec >= 2)
    345 	fatal1("You may not change to a different patch file.\n");
    346 }
    347 
    348 /* Process switches and filenames up to next '+' or end of list. */
    349 
    350 void
    351 get_some_switches()
    352 {
    353     Reg1 char *s;
    354 
    355     rejname[0] = '\0';
    356     Argc_last = Argc;
    357     Argv_last = Argv;
    358     if (!Argc)
    359 	return;
    360     for (Argc--,Argv++; Argc; Argc--,Argv++) {
    361 	s = Argv[0];
    362 	if (strEQ(s, "+")) {
    363 	    return;			/* + will be skipped by for loop */
    364 	}
    365 	if (*s != '-' || !s[1]) {
    366 	    if (filec == MAXFILEC)
    367 		fatal1("Too many file arguments.\n");
    368 	    filearg[filec++] = savestr(s);
    369 	}
    370 	else {
    371 	    switch (*++s) {
    372 	    case 'b':
    373 		origext = savestr(Argv[1]);
    374 		Argc--,Argv++;
    375 		break;
    376 	    case 'c':
    377 		diff_type = CONTEXT_DIFF;
    378 		break;
    379 	    case 'd':
    380 		if (!*++s) {
    381 		    Argc--,Argv++;
    382 		    s = Argv[0];
    383 		}
    384 		if (chdir(s) < 0)
    385 		    fatal2("Can't cd to %s.\n", s);
    386 		break;
    387 	    case 'D':
    388 	    	do_defines = TRUE;
    389 		if (!*++s) {
    390 		    Argc--,Argv++;
    391 		    s = Argv[0];
    392 		}
    393 		Sprintf(if_defined, "#ifdef %s\n", s);
    394 		Sprintf(not_defined, "#ifndef %s\n", s);
    395 		Sprintf(end_defined, "#endif /* %s */\n", s);
    396 		break;
    397 	    case 'e':
    398 		diff_type = ED_DIFF;
    399 		break;
    400 	    case 'f':
    401 		force = TRUE;
    402 		break;
    403 	    case 'F':
    404 		if (*++s == '=')
    405 		    s++;
    406 		maxfuzz = atoi(s);
    407 		break;
    408 	    case 'l':
    409 		canonicalize = TRUE;
    410 		break;
    411 	    case 'n':
    412 		diff_type = NORMAL_DIFF;
    413 		break;
    414 	    case 'N':
    415 		noreverse = TRUE;
    416 		break;
    417 	    case 'o':
    418 		outname = savestr(Argv[1]);
    419 		Argc--,Argv++;
    420 		break;
    421 	    case 'p':
    422 		if (*++s == '=')
    423 		    s++;
    424 		strippath = atoi(s);
    425 		break;
    426 	    case 'r':
    427 		Strcpy(rejname, Argv[1]);
    428 		Argc--,Argv++;
    429 		break;
    430 	    case 'R':
    431 		reverse = TRUE;
    432 		break;
    433 	    case 's':
    434 		verbose = FALSE;
    435 		break;
    436 	    case 'S':
    437 		skip_rest_of_patch = TRUE;
    438 		break;
    439 	    case 'v':
    440 		version();
    441 		break;
    442 #ifdef DEBUGGING
    443 	    case 'x':
    444 		debug = atoi(s+1);
    445 		break;
    446 #endif
    447 	    default:
    448 		fatal2("Unrecognized switch: %s\n", Argv[0]);
    449 	    }
    450 	}
    451     }
    452 }
    453 
    454 /* Attempt to find the right place to apply this hunk of patch. */
    455 
    456 LINENUM
    457 locate_hunk(fuzz)
    458 LINENUM fuzz;
    459 {
    460     Reg1 LINENUM first_guess = pch_first() + last_offset;
    461     Reg2 LINENUM offset;
    462     LINENUM pat_lines = pch_ptrn_lines();
    463     Reg3 LINENUM max_pos_offset = input_lines - first_guess
    464 				- pat_lines + 1;
    465     Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1
    466 				+ pch_context();
    467 
    468     if (!pat_lines)			/* null range matches always */
    469 	return first_guess;
    470     if (max_neg_offset >= first_guess)	/* do not try lines < 0 */
    471 	max_neg_offset = first_guess - 1;
    472     if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz))
    473 	return first_guess;
    474     for (offset = 1; ; offset++) {
    475 	Reg5 bool check_after = (offset <= max_pos_offset);
    476 	Reg6 bool check_before = (offset <= max_neg_offset);
    477 
    478 	if (check_after && patch_match(first_guess, offset, fuzz)) {
    479 #ifdef DEBUGGING
    480 	    if (debug & 1)
    481 		say3("Offset changing from %ld to %ld\n", last_offset, offset);
    482 #endif
    483 	    last_offset = offset;
    484 	    return first_guess+offset;
    485 	}
    486 	else if (check_before && patch_match(first_guess, -offset, fuzz)) {
    487 #ifdef DEBUGGING
    488 	    if (debug & 1)
    489 		say3("Offset changing from %ld to %ld\n", last_offset, -offset);
    490 #endif
    491 	    last_offset = -offset;
    492 	    return first_guess-offset;
    493 	}
    494 	else if (!check_before && !check_after)
    495 	    return Nulline;
    496     }
    497 }
    498 
    499 /* We did not find the pattern, dump out the hunk so they can handle it. */
    500 
    501 void
    502 abort_hunk()
    503 {
    504     Reg1 LINENUM i;
    505     Reg2 LINENUM pat_end = pch_end();
    506     /* add in last_offset to guess the same as the previous successful hunk */
    507     LINENUM oldfirst = pch_first() + last_offset;
    508     LINENUM newfirst = pch_newfirst() + last_offset;
    509     LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
    510     LINENUM newlast = newfirst + pch_repl_lines() - 1;
    511     char *stars = (diff_type == NEW_CONTEXT_DIFF ? " ****" : "");
    512     char *minuses = (diff_type == NEW_CONTEXT_DIFF ? " ----" : " -----");
    513 
    514     fprintf(rejfp, "***************\n");
    515     for (i=0; i<=pat_end; i++) {
    516 	switch (pch_char(i)) {
    517 	case '*':
    518 	    if (oldlast < oldfirst)
    519 		fprintf(rejfp, "*** 0%s\n", stars);
    520 	    else if (oldlast == oldfirst)
    521 		fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
    522 	    else
    523 		fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
    524 	    break;
    525 	case '=':
    526 	    if (newlast < newfirst)
    527 		fprintf(rejfp, "--- 0%s\n", minuses);
    528 	    else if (newlast == newfirst)
    529 		fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
    530 	    else
    531 		fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
    532 	    break;
    533 	case '\n':
    534 	    fprintf(rejfp, "%s", pfetch(i));
    535 	    break;
    536 	case ' ': case '-': case '+': case '!':
    537 	    fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
    538 	    break;
    539 	default:
    540 	    say1("Fatal internal error in abort_hunk().\n");
    541 	    abort();
    542 	}
    543     }
    544 }
    545 
    546 /* We found where to apply it (we hope), so do it. */
    547 
    548 void
    549 apply_hunk(where)
    550 LINENUM where;
    551 {
    552     Reg1 LINENUM old = 1;
    553     Reg2 LINENUM lastline = pch_ptrn_lines();
    554     Reg3 LINENUM new = lastline+1;
    555 #define OUTSIDE 0
    556 #define IN_IFNDEF 1
    557 #define IN_IFDEF 2
    558 #define IN_ELSE 3
    559     Reg4 int def_state = OUTSIDE;
    560     Reg5 bool R_do_defines = do_defines;
    561     Reg6 LINENUM pat_end = pch_end();
    562 
    563     where--;
    564     while (pch_char(new) == '=' || pch_char(new) == '\n')
    565 	new++;
    566 
    567     while (old <= lastline) {
    568 	if (pch_char(old) == '-') {
    569 	    copy_till(where + old - 1);
    570 	    if (R_do_defines) {
    571 		if (def_state == OUTSIDE) {
    572 		    fputs(not_defined, ofp);
    573 		    def_state = IN_IFNDEF;
    574 		}
    575 		else if (def_state == IN_IFDEF) {
    576 		    fputs(else_defined, ofp);
    577 		    def_state = IN_ELSE;
    578 		}
    579 		fputs(pfetch(old), ofp);
    580 	    }
    581 	    last_frozen_line++;
    582 	    old++;
    583 	}
    584 	else if (new > pat_end)
    585 	    break;
    586 	else if (pch_char(new) == '+') {
    587 	    copy_till(where + old - 1);
    588 	    if (R_do_defines) {
    589 		if (def_state == IN_IFNDEF) {
    590 		    fputs(else_defined, ofp);
    591 		    def_state = IN_ELSE;
    592 		}
    593 		else if (def_state == OUTSIDE) {
    594 		    fputs(if_defined, ofp);
    595 		    def_state = IN_IFDEF;
    596 		}
    597 	    }
    598 	    fputs(pfetch(new), ofp);
    599 	    new++;
    600 	}
    601 	else {
    602 	    if (pch_char(new) != pch_char(old)) {
    603 		say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
    604 		    pch_hunk_beg() + old,
    605 		    pch_hunk_beg() + new);
    606 #ifdef DEBUGGING
    607 		say3("oldchar = '%c', newchar = '%c'\n",
    608 		    pch_char(old), pch_char(new));
    609 #endif
    610 		my_exit(1);
    611 	    }
    612 	    if (pch_char(new) == '!') {
    613 		copy_till(where + old - 1);
    614 		if (R_do_defines) {
    615 		   fputs(not_defined, ofp);
    616 		   def_state = IN_IFNDEF;
    617 		}
    618 		while (pch_char(old) == '!') {
    619 		    if (R_do_defines) {
    620 			fputs(pfetch(old), ofp);
    621 		    }
    622 		    last_frozen_line++;
    623 		    old++;
    624 		}
    625 		if (R_do_defines) {
    626 		    fputs(else_defined, ofp);
    627 		    def_state = IN_ELSE;
    628 		}
    629 		while (pch_char(new) == '!') {
    630 		    fputs(pfetch(new), ofp);
    631 		    new++;
    632 		}
    633 		if (R_do_defines) {
    634 		    fputs(end_defined, ofp);
    635 		    def_state = OUTSIDE;
    636 		}
    637 	    }
    638 	    else {
    639 		assert(pch_char(new) == ' ');
    640 		old++;
    641 		new++;
    642 	    }
    643 	}
    644     }
    645     if (new <= pat_end && pch_char(new) == '+') {
    646 	copy_till(where + old - 1);
    647 	if (R_do_defines) {
    648 	    if (def_state == OUTSIDE) {
    649 	    	fputs(if_defined, ofp);
    650 		def_state = IN_IFDEF;
    651 	    }
    652 	    else if (def_state == IN_IFNDEF) {
    653 		fputs(else_defined, ofp);
    654 		def_state = IN_ELSE;
    655 	    }
    656 	}
    657 	while (new <= pat_end && pch_char(new) == '+') {
    658 	    fputs(pfetch(new), ofp);
    659 	    new++;
    660 	}
    661     }
    662     if (R_do_defines && def_state != OUTSIDE) {
    663 	fputs(end_defined, ofp);
    664     }
    665 }
    666 
    667 /* Open the new file. */
    668 
    669 void
    670 init_output(name)
    671 char *name;
    672 {
    673     ofp = fopen(name, "w");
    674     if (ofp == Nullfp)
    675 	fatal2("patch: can't create %s.\n", name);
    676 }
    677 
    678 /* Open a file to put hunks we can't locate. */
    679 
    680 void
    681 init_reject(name)
    682 char *name;
    683 {
    684     rejfp = fopen(name, "w");
    685     if (rejfp == Nullfp)
    686 	fatal2("patch: can't create %s.\n", name);
    687 }
    688 
    689 /* Copy input file to output, up to wherever hunk is to be applied. */
    690 
    691 void
    692 copy_till(lastline)
    693 Reg1 LINENUM lastline;
    694 {
    695     Reg2 LINENUM R_last_frozen_line = last_frozen_line;
    696 
    697     if (R_last_frozen_line > lastline)
    698 	say1("patch: misordered hunks! output will be garbled.\n");
    699     while (R_last_frozen_line < lastline) {
    700 	dump_line(++R_last_frozen_line);
    701     }
    702     last_frozen_line = R_last_frozen_line;
    703 }
    704 
    705 /* Finish copying the input file to the output file. */
    706 
    707 void
    708 spew_output()
    709 {
    710 #ifdef DEBUGGING
    711     if (debug & 256)
    712 	say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line);
    713 #endif
    714     if (input_lines)
    715 	copy_till(input_lines);		/* dump remainder of file */
    716     Fclose(ofp);
    717     ofp = Nullfp;
    718 }
    719 
    720 /* Copy one line from input to output. */
    721 
    722 void
    723 dump_line(line)
    724 LINENUM line;
    725 {
    726     Reg1 char *s;
    727     Reg2 char R_newline = '\n';
    728 
    729     /* Note: string is not null terminated. */
    730     for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ;
    731 }
    732 
    733 /* Does the patch pattern match at line base+offset? */
    734 
    735 bool
    736 patch_match(base, offset, fuzz)
    737 LINENUM base;
    738 LINENUM offset;
    739 LINENUM fuzz;
    740 {
    741     Reg1 LINENUM pline = 1 + fuzz;
    742     Reg2 LINENUM iline;
    743     Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz;
    744 
    745     for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) {
    746 	if (canonicalize) {
    747 	    if (!similar(ifetch(iline, (offset >= 0)),
    748 			 pfetch(pline),
    749 			 pch_line_len(pline) ))
    750 		return FALSE;
    751 	}
    752 	else if (strnNE(ifetch(iline, (offset >= 0)),
    753 		   pfetch(pline),
    754 		   pch_line_len(pline) ))
    755 	    return FALSE;
    756     }
    757     return TRUE;
    758 }
    759 
    760 /* Do two lines match with canonicalized white space? */
    761 
    762 bool
    763 similar(a,b,len)
    764 Reg1 char *a;
    765 Reg2 char *b;
    766 Reg3 int len;
    767 {
    768     while (len) {
    769 	if (isspace(*b)) {		/* whitespace (or \n) to match? */
    770 	    if (!isspace(*a))		/* no corresponding whitespace? */
    771 		return FALSE;
    772 	    while (len && isspace(*b) && *b != '\n')
    773 		b++,len--;		/* skip pattern whitespace */
    774 	    while (isspace(*a) && *a != '\n')
    775 		a++;			/* skip target whitespace */
    776 	    if (*a == '\n' || *b == '\n')
    777 		return (*a == *b);	/* should end in sync */
    778 	}
    779 	else if (*a++ != *b++)		/* match non-whitespace chars */
    780 	    return FALSE;
    781 	else
    782 	    len--;			/* probably not necessary */
    783     }
    784     return TRUE;			/* actually, this is not reached */
    785 					/* since there is always a \n */
    786 }
    787 
    788 /* Exit with cleanup. */
    789 
    790 void
    791 my_exit(status)
    792 int status;
    793 {
    794     Unlink(TMPINNAME);
    795     if (!toutkeep) {
    796 	Unlink(TMPOUTNAME);
    797     }
    798     if (!trejkeep) {
    799 	Unlink(TMPREJNAME);
    800     }
    801     Unlink(TMPPATNAME);
    802     exit(status);
    803 }
    804