Home | History | Annotate | Line # | Download | only in patch
patch.c revision 1.12.2.1
      1  1.12.2.1       jmc /*	$NetBSD: patch.c,v 1.12.2.1 2003/01/26 09:57:23 jmc Exp $	*/
      2       1.4   thorpej 
      3       1.1       cgd /* patch - a program to apply diffs to original files
      4       1.1       cgd  *
      5       1.1       cgd  * Copyright 1986, Larry Wall
      6       1.1       cgd  *
      7       1.9     ragge  * Redistribution and use in source and binary forms, with or without
      8       1.9     ragge  * modification, are permitted provided that the following condition
      9       1.9     ragge  * is met:
     10       1.9     ragge  *  1. Redistributions of source code must retain the above copyright
     11       1.9     ragge  *     notice, this condition and the following disclaimer.
     12       1.9     ragge  *
     13       1.9     ragge  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     14       1.9     ragge  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15       1.9     ragge  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     16       1.9     ragge  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     17       1.9     ragge  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     18       1.9     ragge  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     19       1.9     ragge  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     20       1.9     ragge  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     21       1.9     ragge  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     22       1.9     ragge  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     23       1.9     ragge  * SUCH DAMAGE.
     24       1.1       cgd  */
     25       1.2   mycroft 
     26       1.6  christos #include <sys/cdefs.h>
     27       1.2   mycroft #ifndef lint
     28  1.12.2.1       jmc __RCSID("$NetBSD: patch.c,v 1.12.2.1 2003/01/26 09:57:23 jmc Exp $");
     29       1.2   mycroft #endif /* not lint */
     30       1.1       cgd 
     31       1.1       cgd #include "INTERN.h"
     32       1.1       cgd #include "common.h"
     33       1.1       cgd #include "EXTERN.h"
     34       1.1       cgd #include "version.h"
     35       1.1       cgd #include "util.h"
     36       1.1       cgd #include "pch.h"
     37       1.1       cgd #include "inp.h"
     38       1.1       cgd #include "backupfile.h"
     39       1.1       cgd 
     40       1.3   thorpej #include <stdlib.h>
     41       1.6  christos #include <unistd.h>
     42       1.6  christos 
     43       1.1       cgd /* procedures */
     44      1.10  kristerw static void reinitialize_almost_everything(void);
     45      1.10  kristerw static char *nextarg(void);
     46      1.10  kristerw static int optcmp(const void *, const void *);
     47      1.10  kristerw static char decode_long_option(char *);
     48      1.10  kristerw static void get_some_switches(void);
     49      1.10  kristerw static LINENUM locate_hunk(LINENUM);
     50      1.10  kristerw static void abort_hunk(void);
     51      1.10  kristerw static void apply_hunk(LINENUM);
     52      1.10  kristerw static void init_output(char *);
     53      1.10  kristerw static void init_reject(char *);
     54      1.10  kristerw static void copy_till(LINENUM);
     55      1.10  kristerw static void spew_output(void);
     56      1.10  kristerw static void dump_line(LINENUM);
     57      1.10  kristerw static bool patch_match(LINENUM, LINENUM, LINENUM);
     58      1.10  kristerw static bool similar(char *, char *, int);
     59      1.10  kristerw int main(int, char *[]);
     60       1.1       cgd 
     61       1.1       cgd /* TRUE if -E was specified on command line.  */
     62       1.1       cgd static int remove_empty_files = FALSE;
     63       1.1       cgd 
     64       1.1       cgd /* TRUE if -R was specified on command line.  */
     65       1.1       cgd static int reverse_flag_specified = FALSE;
     66       1.1       cgd 
     67       1.1       cgd /* Apply a set of diffs as appropriate. */
     68       1.1       cgd 
     69       1.1       cgd int
     70      1.10  kristerw main(int argc, char *argv[])
     71       1.1       cgd {
     72       1.6  christos     LINENUM where = 0;
     73       1.1       cgd     LINENUM newwhere;
     74       1.1       cgd     LINENUM fuzz;
     75       1.1       cgd     LINENUM mymaxfuzz;
     76       1.1       cgd     int hunk = 0;
     77       1.1       cgd     int failed = 0;
     78       1.1       cgd     int failtotal = 0;
     79       1.1       cgd     int i;
     80       1.1       cgd 
     81       1.1       cgd     for (i = 0; i<MAXFILEC; i++)
     82      1.11  kristerw 	filearg[i] = NULL;
     83       1.1       cgd 
     84       1.1       cgd     myuid = getuid();
     85       1.1       cgd 
     86       1.1       cgd     /* Cons up the names of the temporary files.  */
     87       1.1       cgd     {
     88       1.1       cgd       /* Directory for temporary files.  */
     89       1.1       cgd       char *tmpdir;
     90      1.11  kristerw       size_t tmpname_len;
     91       1.1       cgd 
     92       1.1       cgd       tmpdir = getenv ("TMPDIR");
     93       1.1       cgd       if (tmpdir == NULL) {
     94       1.1       cgd 	tmpdir = "/tmp";
     95       1.1       cgd       }
     96       1.1       cgd       tmpname_len = strlen (tmpdir) + 20;
     97       1.1       cgd 
     98      1.12  kristerw       TMPOUTNAME = xmalloc(tmpname_len);
     99       1.1       cgd       strcpy (TMPOUTNAME, tmpdir);
    100       1.1       cgd       strcat (TMPOUTNAME, "/patchoXXXXXX");
    101       1.5     lukem       if ((i = mkstemp(TMPOUTNAME)) < 0)
    102      1.11  kristerw         pfatal("can't create %s", TMPOUTNAME);
    103       1.5     lukem       Close(i);
    104       1.1       cgd 
    105      1.12  kristerw       TMPINNAME = xmalloc(tmpname_len);
    106       1.1       cgd       strcpy (TMPINNAME, tmpdir);
    107       1.1       cgd       strcat (TMPINNAME, "/patchiXXXXXX");
    108       1.5     lukem       if ((i = mkstemp(TMPINNAME)) < 0)
    109      1.11  kristerw         pfatal("can't create %s", TMPINNAME);
    110       1.5     lukem       Close(i);
    111       1.1       cgd 
    112      1.12  kristerw       TMPREJNAME = xmalloc(tmpname_len);
    113       1.1       cgd       strcpy (TMPREJNAME, tmpdir);
    114       1.1       cgd       strcat (TMPREJNAME, "/patchrXXXXXX");
    115       1.5     lukem       if ((i = mkstemp(TMPREJNAME)) < 0)
    116      1.11  kristerw         pfatal("can't create %s", TMPREJNAME);
    117       1.5     lukem       Close(i);
    118       1.1       cgd 
    119      1.12  kristerw       TMPPATNAME = xmalloc(tmpname_len);
    120       1.1       cgd       strcpy (TMPPATNAME, tmpdir);
    121       1.1       cgd       strcat (TMPPATNAME, "/patchpXXXXXX");
    122       1.5     lukem       if ((i = mkstemp(TMPPATNAME)) < 0)
    123      1.11  kristerw         pfatal("can't create %s", TMPPATNAME);
    124       1.5     lukem       Close(i);
    125       1.1       cgd     }
    126       1.1       cgd 
    127       1.1       cgd     {
    128       1.1       cgd       char *v;
    129       1.1       cgd 
    130       1.1       cgd       v = getenv ("SIMPLE_BACKUP_SUFFIX");
    131       1.1       cgd       if (v)
    132       1.1       cgd 	simple_backup_suffix = v;
    133       1.1       cgd       else
    134       1.1       cgd 	simple_backup_suffix = ORIGEXT;
    135       1.1       cgd #ifndef NODIR
    136       1.1       cgd       v = getenv ("VERSION_CONTROL");
    137       1.1       cgd       backup_type = get_version (v); /* OK to pass NULL. */
    138       1.1       cgd #endif
    139       1.1       cgd     }
    140       1.1       cgd 
    141       1.1       cgd     /* parse switches */
    142       1.1       cgd     Argc = argc;
    143       1.1       cgd     Argv = argv;
    144       1.1       cgd     get_some_switches();
    145       1.1       cgd 
    146       1.1       cgd     /* make sure we clean up /tmp in case of disaster */
    147       1.1       cgd     set_signals(0);
    148       1.1       cgd 
    149       1.1       cgd     for (
    150       1.1       cgd 	open_patch_file(filearg[1]);
    151       1.1       cgd 	there_is_another_patch();
    152       1.1       cgd 	reinitialize_almost_everything()
    153       1.1       cgd     ) {					/* for each patch in patch file */
    154       1.1       cgd 
    155  1.12.2.1       jmc 	if (!skip_rest_of_patch && outname == NULL)
    156      1.12  kristerw 	    outname = xstrdup(filearg[0]);
    157       1.1       cgd 
    158       1.1       cgd 	/* for ed script just up and do it and exit */
    159       1.1       cgd 	if (diff_type == ED_DIFF) {
    160       1.1       cgd 	    do_ed_script();
    161       1.1       cgd 	    continue;
    162       1.1       cgd 	}
    163       1.1       cgd 
    164       1.1       cgd 	/* initialize the patched file */
    165       1.1       cgd 	if (!skip_rest_of_patch)
    166       1.1       cgd 	    init_output(TMPOUTNAME);
    167       1.1       cgd 
    168       1.1       cgd 	/* initialize reject file */
    169       1.1       cgd 	init_reject(TMPREJNAME);
    170       1.1       cgd 
    171       1.1       cgd 	/* find out where all the lines are */
    172       1.1       cgd 	if (!skip_rest_of_patch)
    173       1.1       cgd 	    scan_input(filearg[0]);
    174       1.1       cgd 
    175       1.1       cgd 	/* from here on, open no standard i/o files, because malloc */
    176       1.1       cgd 	/* might misfire and we can't catch it easily */
    177       1.1       cgd 
    178       1.1       cgd 	/* apply each hunk of patch */
    179       1.1       cgd 	hunk = 0;
    180       1.1       cgd 	failed = 0;
    181       1.1       cgd 	out_of_mem = FALSE;
    182       1.1       cgd 	while (another_hunk()) {
    183       1.1       cgd 	    hunk++;
    184       1.1       cgd 	    fuzz = Nulline;
    185       1.1       cgd 	    mymaxfuzz = pch_context();
    186       1.1       cgd 	    if (maxfuzz < mymaxfuzz)
    187       1.1       cgd 		mymaxfuzz = maxfuzz;
    188       1.1       cgd 	    if (!skip_rest_of_patch) {
    189       1.1       cgd 		do {
    190       1.1       cgd 		    where = locate_hunk(fuzz);
    191       1.1       cgd 		    if (hunk == 1 && where == Nulline && !force) {
    192       1.1       cgd 						/* dwim for reversed patch? */
    193       1.1       cgd 			if (!pch_swap()) {
    194       1.1       cgd 			    if (fuzz == Nulline)
    195      1.11  kristerw 				say(
    196       1.1       cgd "Not enough memory to try swapped hunk!  Assuming unswapped.\n");
    197       1.1       cgd 			    continue;
    198       1.1       cgd 			}
    199       1.1       cgd 			reverse = !reverse;
    200       1.1       cgd 			where = locate_hunk(fuzz);  /* try again */
    201       1.1       cgd 			if (where == Nulline) {	    /* didn't find it swapped */
    202       1.1       cgd 			    if (!pch_swap())         /* put it back to normal */
    203      1.11  kristerw 				fatal("lost hunk on alloc error!\n");
    204       1.1       cgd 			    reverse = !reverse;
    205       1.1       cgd 			}
    206       1.1       cgd 			else if (noreverse) {
    207       1.1       cgd 			    if (!pch_swap())         /* put it back to normal */
    208      1.11  kristerw 				fatal("lost hunk on alloc error!\n");
    209       1.1       cgd 			    reverse = !reverse;
    210      1.11  kristerw 			    say(
    211       1.1       cgd "Ignoring previously applied (or reversed) patch.\n");
    212       1.1       cgd 			    skip_rest_of_patch = TRUE;
    213       1.1       cgd 			}
    214       1.1       cgd 			else if (batch) {
    215       1.1       cgd 			    if (verbose)
    216      1.11  kristerw 				say(
    217       1.1       cgd "%seversed (or previously applied) patch detected!  %s -R.",
    218       1.1       cgd 				reverse ? "R" : "Unr",
    219       1.1       cgd 				reverse ? "Assuming" : "Ignoring");
    220       1.1       cgd 			}
    221       1.1       cgd 			else {
    222      1.11  kristerw 			    ask(
    223       1.1       cgd "%seversed (or previously applied) patch detected!  %s -R? [y] ",
    224       1.1       cgd 				reverse ? "R" : "Unr",
    225       1.1       cgd 				reverse ? "Assume" : "Ignore");
    226       1.1       cgd 			    if (*buf == 'n') {
    227      1.11  kristerw 				ask("Apply anyway? [n] ");
    228       1.1       cgd 				if (*buf != 'y')
    229       1.1       cgd 				    skip_rest_of_patch = TRUE;
    230       1.1       cgd 				where = Nulline;
    231       1.1       cgd 				reverse = !reverse;
    232       1.1       cgd 				if (!pch_swap())  /* put it back to normal */
    233      1.11  kristerw 				    fatal("lost hunk on alloc error!\n");
    234       1.1       cgd 			    }
    235       1.1       cgd 			}
    236       1.1       cgd 		    }
    237       1.1       cgd 		} while (!skip_rest_of_patch && where == Nulline &&
    238       1.1       cgd 		    ++fuzz <= mymaxfuzz);
    239       1.1       cgd 
    240       1.1       cgd 		if (skip_rest_of_patch) {		/* just got decided */
    241       1.1       cgd 		    Fclose(ofp);
    242      1.11  kristerw 		    ofp = NULL;
    243       1.1       cgd 		}
    244       1.1       cgd 	    }
    245       1.1       cgd 
    246       1.1       cgd 	    newwhere = pch_newfirst() + last_offset;
    247       1.1       cgd 	    if (skip_rest_of_patch) {
    248       1.1       cgd 		abort_hunk();
    249       1.1       cgd 		failed++;
    250       1.1       cgd 		if (verbose)
    251      1.11  kristerw 		    say("Hunk #%d ignored at %ld.\n", hunk, newwhere);
    252       1.1       cgd 	    }
    253       1.1       cgd 	    else if (where == Nulline) {
    254       1.1       cgd 		abort_hunk();
    255       1.1       cgd 		failed++;
    256       1.1       cgd 		if (verbose)
    257      1.11  kristerw 		    say("Hunk #%d failed at %ld.\n", hunk, newwhere);
    258       1.1       cgd 	    }
    259       1.1       cgd 	    else {
    260       1.1       cgd 		apply_hunk(where);
    261       1.1       cgd 		if (verbose) {
    262      1.11  kristerw 		    say("Hunk #%d succeeded at %ld", hunk, newwhere);
    263       1.1       cgd 		    if (fuzz)
    264      1.11  kristerw 			say(" with fuzz %ld", fuzz);
    265       1.1       cgd 		    if (last_offset)
    266      1.11  kristerw 			say(" (offset %ld line%s)",
    267       1.1       cgd 			    last_offset, last_offset==1L?"":"s");
    268      1.11  kristerw 		    say(".\n");
    269       1.1       cgd 		}
    270       1.1       cgd 	    }
    271       1.1       cgd 	}
    272       1.1       cgd 
    273       1.1       cgd 	if (out_of_mem && using_plan_a) {
    274       1.1       cgd 	    Argc = Argc_last;
    275       1.1       cgd 	    Argv = Argv_last;
    276      1.11  kristerw 	    say("\n\nRan out of memory using Plan A--trying again...\n\n");
    277       1.1       cgd 	    if (ofp)
    278       1.1       cgd 	        Fclose(ofp);
    279      1.11  kristerw 	    ofp = NULL;
    280       1.1       cgd 	    if (rejfp)
    281       1.1       cgd 	        Fclose(rejfp);
    282      1.11  kristerw 	    rejfp = NULL;
    283       1.1       cgd 	    continue;
    284       1.1       cgd 	}
    285       1.1       cgd 
    286       1.1       cgd 	assert(hunk);
    287       1.1       cgd 
    288       1.1       cgd 	/* finish spewing out the new file */
    289       1.1       cgd 	if (!skip_rest_of_patch)
    290       1.1       cgd 	    spew_output();
    291       1.1       cgd 
    292       1.1       cgd 	/* and put the output where desired */
    293       1.1       cgd 	ignore_signals();
    294       1.1       cgd 	if (!skip_rest_of_patch) {
    295       1.1       cgd 	    struct stat statbuf;
    296       1.1       cgd 	    char *realout = outname;
    297       1.1       cgd 
    298       1.1       cgd 	    if (move_file(TMPOUTNAME, outname) < 0) {
    299       1.1       cgd 		toutkeep = TRUE;
    300       1.1       cgd 		realout = TMPOUTNAME;
    301       1.1       cgd 		chmod(TMPOUTNAME, filemode);
    302       1.1       cgd 	    }
    303       1.1       cgd 	    else
    304       1.1       cgd 		chmod(outname, filemode);
    305       1.1       cgd 
    306       1.1       cgd 	    if (remove_empty_files && stat(realout, &statbuf) == 0
    307       1.1       cgd 		&& statbuf.st_size == 0) {
    308       1.1       cgd 		if (verbose)
    309      1.11  kristerw 		    say("Removing %s (empty after patching).\n", realout);
    310       1.1       cgd 	        while (unlink(realout) >= 0) ; /* while is for Eunice.  */
    311       1.1       cgd 	    }
    312       1.1       cgd 	}
    313       1.1       cgd 	Fclose(rejfp);
    314      1.11  kristerw 	rejfp = NULL;
    315       1.1       cgd 	if (failed) {
    316       1.1       cgd 	    failtotal += failed;
    317  1.12.2.1       jmc 	    if (outname != NULL) {
    318  1.12.2.1       jmc 		    if (!*rejname) {
    319  1.12.2.1       jmc 			    Strcpy(rejname, outname);
    320  1.12.2.1       jmc 			    Strcat(rejname, REJEXT);
    321  1.12.2.1       jmc 		    }
    322  1.12.2.1       jmc 		    if (skip_rest_of_patch)
    323  1.12.2.1       jmc 			    say("%d out of %d hunks ignored"
    324  1.12.2.1       jmc 				"--saving rejects to %s\n",
    325  1.12.2.1       jmc 				failed, hunk, rejname);
    326  1.12.2.1       jmc 		    else
    327  1.12.2.1       jmc 			    say("%d out of %d hunks failed"
    328  1.12.2.1       jmc 				"--saving rejects to %s\n",
    329  1.12.2.1       jmc 				failed, hunk, rejname);
    330  1.12.2.1       jmc 		    if (move_file(TMPREJNAME, rejname) < 0)
    331  1.12.2.1       jmc 			    trejkeep = TRUE;
    332  1.12.2.1       jmc 	    } else
    333  1.12.2.1       jmc 		    say("%d out of %d hunks ignored\n", failed, hunk);
    334       1.1       cgd 	}
    335       1.1       cgd 	set_signals(1);
    336       1.1       cgd     }
    337       1.1       cgd     my_exit(failtotal);
    338       1.1       cgd }
    339       1.1       cgd 
    340       1.1       cgd /* Prepare to find the next patch to do in the patch file. */
    341       1.1       cgd 
    342       1.6  christos static void
    343      1.10  kristerw reinitialize_almost_everything(void)
    344       1.1       cgd {
    345       1.1       cgd     re_patch();
    346       1.1       cgd     re_input();
    347       1.1       cgd 
    348       1.1       cgd     input_lines = 0;
    349       1.1       cgd     last_frozen_line = 0;
    350       1.1       cgd 
    351       1.1       cgd     filec = 0;
    352      1.11  kristerw     if (filearg[0] != NULL && !out_of_mem) {
    353       1.1       cgd 	free(filearg[0]);
    354      1.11  kristerw 	filearg[0] = NULL;
    355       1.1       cgd     }
    356       1.1       cgd 
    357      1.11  kristerw     if (outname != NULL) {
    358       1.1       cgd 	free(outname);
    359      1.11  kristerw 	outname = NULL;
    360       1.1       cgd     }
    361       1.1       cgd 
    362       1.1       cgd     last_offset = 0;
    363       1.1       cgd 
    364       1.1       cgd     diff_type = 0;
    365       1.1       cgd 
    366      1.11  kristerw     if (revision != NULL) {
    367       1.1       cgd 	free(revision);
    368      1.11  kristerw 	revision = NULL;
    369       1.1       cgd     }
    370       1.1       cgd 
    371       1.1       cgd     reverse = reverse_flag_specified;
    372       1.1       cgd     skip_rest_of_patch = FALSE;
    373       1.1       cgd 
    374       1.1       cgd     get_some_switches();
    375       1.1       cgd 
    376       1.1       cgd     if (filec >= 2)
    377      1.11  kristerw 	fatal("you may not change to a different patch file\n");
    378       1.1       cgd }
    379       1.1       cgd 
    380       1.1       cgd static char *
    381      1.10  kristerw nextarg(void)
    382       1.1       cgd {
    383       1.1       cgd     if (!--Argc)
    384      1.11  kristerw 	fatal("missing argument after `%s'\n", *Argv);
    385       1.1       cgd     return *++Argv;
    386       1.1       cgd }
    387       1.1       cgd 
    388       1.3   thorpej /* Module for handling of long options. */
    389       1.3   thorpej 
    390       1.3   thorpej struct option {
    391       1.3   thorpej     char *long_opt;
    392       1.3   thorpej     char short_opt;
    393       1.3   thorpej };
    394       1.3   thorpej 
    395       1.6  christos static int
    396      1.10  kristerw optcmp(const void *va, const void *vb)
    397       1.3   thorpej {
    398       1.6  christos     const struct option *a = va, *b = vb;
    399       1.3   thorpej     return strcmp (a->long_opt, b->long_opt);
    400       1.3   thorpej }
    401       1.3   thorpej 
    402       1.3   thorpej /* Decode Long options beginning with "--" to their short equivalents. */
    403       1.3   thorpej 
    404       1.6  christos static char
    405      1.10  kristerw decode_long_option(char *opt)
    406       1.3   thorpej {
    407       1.3   thorpej     /*
    408       1.3   thorpej      * This table must be sorted on the first field.  We also decode
    409       1.3   thorpej      * unimplemented options as those will probably be dealt with
    410       1.3   thorpej      * later, anyhow.
    411       1.3   thorpej      */
    412       1.3   thorpej     static struct option options[] = {
    413       1.3   thorpej       { "batch",		't' },
    414       1.3   thorpej       { "check",		'C' },
    415       1.3   thorpej       { "context",		'c' },
    416       1.3   thorpej       { "debug",		'x' },
    417       1.3   thorpej       { "directory",		'd' },
    418       1.3   thorpej       { "ed",			'e' },
    419       1.3   thorpej       { "force",		'f' },
    420       1.3   thorpej       { "forward",		'N' },
    421       1.3   thorpej       { "fuzz",			'F' },
    422       1.3   thorpej       { "ifdef",		'D' },
    423       1.3   thorpej       { "ignore-whitespace",	'l' },
    424       1.3   thorpej       { "normal",		'n' },
    425       1.3   thorpej       { "output",		'o' },
    426       1.8    kleink       { "patchfile",		'i' },
    427       1.3   thorpej       { "prefix",		'B' },
    428       1.3   thorpej       { "quiet",		's' },
    429       1.3   thorpej       { "reject-file",		'r' },
    430       1.3   thorpej       { "remove-empty-files",	'E' },
    431       1.3   thorpej       { "reverse",		'R' },
    432       1.3   thorpej       { "silent",		's' },
    433       1.3   thorpej       { "skip",			'S' },
    434       1.3   thorpej       { "strip",		'p' },
    435       1.3   thorpej       { "suffix",		'b' },
    436       1.3   thorpej       { "unified",		'u' },
    437       1.3   thorpej       { "version",		'v' },
    438       1.3   thorpej       { "version-control",	'V' },
    439       1.3   thorpej     };
    440       1.3   thorpej     struct option key, *found;
    441       1.3   thorpej 
    442       1.3   thorpej     key.long_opt = opt;
    443      1.11  kristerw     found = bsearch(&key, options,
    444       1.3   thorpej 	sizeof(options) / sizeof(options[0]), sizeof(options[0]), optcmp);
    445       1.3   thorpej 
    446       1.3   thorpej     return found ? found->short_opt : '\0';
    447       1.3   thorpej }
    448       1.3   thorpej 
    449       1.1       cgd /* Process switches and filenames up to next '+' or end of list. */
    450       1.1       cgd 
    451       1.6  christos static void
    452      1.10  kristerw get_some_switches(void)
    453       1.1       cgd {
    454      1.10  kristerw     char *s;
    455       1.1       cgd 
    456       1.1       cgd     rejname[0] = '\0';
    457       1.1       cgd     Argc_last = Argc;
    458       1.1       cgd     Argv_last = Argv;
    459       1.1       cgd     if (!Argc)
    460       1.1       cgd 	return;
    461       1.1       cgd     for (Argc--,Argv++; Argc; Argc--,Argv++) {
    462       1.1       cgd 	s = Argv[0];
    463       1.1       cgd 	if (strEQ(s, "+")) {
    464       1.1       cgd 	    return;			/* + will be skipped by for loop */
    465       1.1       cgd 	}
    466       1.1       cgd 	if (*s != '-' || !s[1]) {
    467       1.1       cgd 	    if (filec == MAXFILEC)
    468      1.11  kristerw 		fatal("too many file arguments\n");
    469      1.11  kristerw 	    if (filec == 1 && filearg[filec] != NULL)
    470      1.11  kristerw 		fatal("-i option and patchfile argument are mutually\
    471       1.8    kleink exclusive\n");
    472      1.12  kristerw 	    filearg[filec++] = xstrdup(s);
    473       1.1       cgd 	}
    474       1.1       cgd 	else {
    475       1.3   thorpej 	    char opt;
    476       1.3   thorpej 
    477       1.3   thorpej 	    if (*(s + 1) == '-') {
    478       1.3   thorpej 		opt = decode_long_option(s + 2);
    479       1.3   thorpej 		s += strlen(s) - 1;
    480       1.3   thorpej 	    }
    481       1.3   thorpej 	    else
    482       1.3   thorpej 		opt = *++s;
    483       1.3   thorpej 
    484       1.3   thorpej 	    switch (opt) {
    485       1.1       cgd 	    case 'b':
    486      1.12  kristerw 		simple_backup_suffix = xstrdup(nextarg());
    487       1.1       cgd 		break;
    488       1.1       cgd 	    case 'B':
    489      1.12  kristerw 		origprae = xstrdup(nextarg());
    490       1.1       cgd 		break;
    491       1.1       cgd 	    case 'c':
    492       1.1       cgd 		diff_type = CONTEXT_DIFF;
    493       1.1       cgd 		break;
    494       1.1       cgd 	    case 'd':
    495       1.1       cgd 		if (!*++s)
    496       1.1       cgd 		    s = nextarg();
    497       1.1       cgd 		if (chdir(s) < 0)
    498      1.11  kristerw 		    pfatal("can't cd to %s", s);
    499       1.1       cgd 		break;
    500       1.1       cgd 	    case 'D':
    501       1.1       cgd 	    	do_defines = TRUE;
    502       1.1       cgd 		if (!*++s)
    503       1.1       cgd 		    s = nextarg();
    504       1.7  christos 		if (!isalpha((unsigned char)*s) && '_' != *s)
    505      1.11  kristerw 		    fatal("argument to -D is not an identifier\n");
    506       1.1       cgd 		Sprintf(if_defined, "#ifdef %s\n", s);
    507       1.1       cgd 		Sprintf(not_defined, "#ifndef %s\n", s);
    508       1.1       cgd 		Sprintf(end_defined, "#endif /* %s */\n", s);
    509       1.1       cgd 		break;
    510       1.1       cgd 	    case 'e':
    511       1.1       cgd 		diff_type = ED_DIFF;
    512       1.1       cgd 		break;
    513       1.1       cgd 	    case 'E':
    514       1.1       cgd 		remove_empty_files = TRUE;
    515       1.1       cgd 		break;
    516       1.1       cgd 	    case 'f':
    517       1.1       cgd 		force = TRUE;
    518       1.1       cgd 		break;
    519       1.1       cgd 	    case 'F':
    520       1.1       cgd 		if (*++s == '=')
    521       1.1       cgd 		    s++;
    522       1.1       cgd 		maxfuzz = atoi(s);
    523       1.8    kleink 		break;
    524       1.8    kleink 	    case 'i':
    525      1.11  kristerw 		if (filearg[1] != NULL)
    526       1.8    kleink 		    free(filearg[1]);
    527      1.12  kristerw 		filearg[1] = xstrdup(nextarg());
    528       1.1       cgd 		break;
    529       1.1       cgd 	    case 'l':
    530       1.1       cgd 		canonicalize = TRUE;
    531       1.1       cgd 		break;
    532       1.1       cgd 	    case 'n':
    533       1.1       cgd 		diff_type = NORMAL_DIFF;
    534       1.1       cgd 		break;
    535       1.1       cgd 	    case 'N':
    536       1.1       cgd 		noreverse = TRUE;
    537       1.1       cgd 		break;
    538       1.1       cgd 	    case 'o':
    539      1.12  kristerw 		outname = xstrdup(nextarg());
    540       1.1       cgd 		break;
    541       1.1       cgd 	    case 'p':
    542       1.1       cgd 		if (*++s == '=')
    543       1.1       cgd 		    s++;
    544       1.1       cgd 		strippath = atoi(s);
    545       1.1       cgd 		break;
    546       1.1       cgd 	    case 'r':
    547       1.1       cgd 		Strcpy(rejname, nextarg());
    548       1.1       cgd 		break;
    549       1.1       cgd 	    case 'R':
    550       1.1       cgd 		reverse = TRUE;
    551       1.1       cgd 		reverse_flag_specified = TRUE;
    552       1.1       cgd 		break;
    553       1.1       cgd 	    case 's':
    554       1.1       cgd 		verbose = FALSE;
    555       1.1       cgd 		break;
    556       1.1       cgd 	    case 'S':
    557       1.1       cgd 		skip_rest_of_patch = TRUE;
    558       1.1       cgd 		break;
    559       1.1       cgd 	    case 't':
    560       1.1       cgd 		batch = TRUE;
    561       1.1       cgd 		break;
    562       1.1       cgd 	    case 'u':
    563       1.1       cgd 		diff_type = UNI_DIFF;
    564       1.1       cgd 		break;
    565       1.1       cgd 	    case 'v':
    566       1.1       cgd 		version();
    567       1.1       cgd 		break;
    568       1.1       cgd 	    case 'V':
    569       1.1       cgd #ifndef NODIR
    570       1.1       cgd 		backup_type = get_version (nextarg ());
    571       1.1       cgd #endif
    572       1.1       cgd 		break;
    573       1.1       cgd #ifdef DEBUGGING
    574       1.1       cgd 	    case 'x':
    575       1.1       cgd 		debug = atoi(s+1);
    576       1.1       cgd 		break;
    577       1.1       cgd #endif
    578       1.1       cgd 	    default:
    579       1.1       cgd 		fprintf(stderr, "patch: unrecognized option `%s'\n", Argv[0]);
    580       1.1       cgd 		fprintf(stderr, "\
    581       1.1       cgd Usage: patch [options] [origfile [patchfile]] [+ [options] [origfile]]...\n\
    582       1.1       cgd Options:\n\
    583       1.1       cgd        [-ceEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\
    584       1.1       cgd        [-D symbol] [-Fmax-fuzz] [-o out-file] [-p[strip-count]]\n\
    585       1.1       cgd        [-r rej-name] [-V {numbered,existing,simple}]\n");
    586       1.1       cgd 		my_exit(1);
    587       1.1       cgd 	    }
    588       1.1       cgd 	}
    589       1.1       cgd     }
    590       1.1       cgd }
    591       1.1       cgd 
    592       1.1       cgd /* Attempt to find the right place to apply this hunk of patch. */
    593       1.1       cgd 
    594       1.6  christos static LINENUM
    595      1.10  kristerw locate_hunk(LINENUM fuzz)
    596       1.1       cgd {
    597      1.10  kristerw     LINENUM first_guess = pch_first() + last_offset;
    598      1.10  kristerw     LINENUM offset;
    599       1.1       cgd     LINENUM pat_lines = pch_ptrn_lines();
    600      1.10  kristerw     LINENUM max_pos_offset = input_lines - first_guess
    601       1.1       cgd 				- pat_lines + 1;
    602      1.10  kristerw     LINENUM max_neg_offset = first_guess - last_frozen_line - 1
    603       1.1       cgd 				+ pch_context();
    604       1.1       cgd 
    605       1.1       cgd     if (!pat_lines)			/* null range matches always */
    606       1.1       cgd 	return first_guess;
    607       1.1       cgd     if (max_neg_offset >= first_guess)	/* do not try lines < 0 */
    608       1.1       cgd 	max_neg_offset = first_guess - 1;
    609       1.1       cgd     if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz))
    610       1.1       cgd 	return first_guess;
    611       1.1       cgd     for (offset = 1; ; offset++) {
    612      1.10  kristerw 	bool check_after = (offset <= max_pos_offset);
    613      1.10  kristerw 	bool check_before = (offset <= max_neg_offset);
    614       1.1       cgd 
    615       1.1       cgd 	if (check_after && patch_match(first_guess, offset, fuzz)) {
    616       1.1       cgd #ifdef DEBUGGING
    617       1.1       cgd 	    if (debug & 1)
    618      1.11  kristerw 		say("Offset changing from %ld to %ld\n", last_offset, offset);
    619       1.1       cgd #endif
    620       1.1       cgd 	    last_offset = offset;
    621       1.1       cgd 	    return first_guess+offset;
    622       1.1       cgd 	}
    623       1.1       cgd 	else if (check_before && patch_match(first_guess, -offset, fuzz)) {
    624       1.1       cgd #ifdef DEBUGGING
    625       1.1       cgd 	    if (debug & 1)
    626      1.11  kristerw 		say("Offset changing from %ld to %ld\n", last_offset, -offset);
    627       1.1       cgd #endif
    628       1.1       cgd 	    last_offset = -offset;
    629       1.1       cgd 	    return first_guess-offset;
    630       1.1       cgd 	}
    631       1.1       cgd 	else if (!check_before && !check_after)
    632       1.1       cgd 	    return Nulline;
    633       1.1       cgd     }
    634       1.1       cgd }
    635       1.1       cgd 
    636       1.1       cgd /* We did not find the pattern, dump out the hunk so they can handle it. */
    637       1.1       cgd 
    638       1.6  christos static void
    639      1.10  kristerw abort_hunk(void)
    640       1.1       cgd {
    641      1.10  kristerw     LINENUM i;
    642      1.10  kristerw     LINENUM pat_end = pch_end();
    643       1.1       cgd     /* add in last_offset to guess the same as the previous successful hunk */
    644       1.1       cgd     LINENUM oldfirst = pch_first() + last_offset;
    645       1.1       cgd     LINENUM newfirst = pch_newfirst() + last_offset;
    646       1.1       cgd     LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
    647       1.1       cgd     LINENUM newlast = newfirst + pch_repl_lines() - 1;
    648       1.1       cgd     char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
    649       1.1       cgd     char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
    650       1.1       cgd 
    651       1.1       cgd     fprintf(rejfp, "***************\n");
    652       1.1       cgd     for (i=0; i<=pat_end; i++) {
    653       1.1       cgd 	switch (pch_char(i)) {
    654       1.1       cgd 	case '*':
    655       1.1       cgd 	    if (oldlast < oldfirst)
    656       1.1       cgd 		fprintf(rejfp, "*** 0%s\n", stars);
    657       1.1       cgd 	    else if (oldlast == oldfirst)
    658       1.1       cgd 		fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
    659       1.1       cgd 	    else
    660       1.1       cgd 		fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
    661       1.1       cgd 	    break;
    662       1.1       cgd 	case '=':
    663       1.1       cgd 	    if (newlast < newfirst)
    664       1.1       cgd 		fprintf(rejfp, "--- 0%s\n", minuses);
    665       1.1       cgd 	    else if (newlast == newfirst)
    666       1.1       cgd 		fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
    667       1.1       cgd 	    else
    668       1.1       cgd 		fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
    669       1.1       cgd 	    break;
    670       1.1       cgd 	case '\n':
    671       1.1       cgd 	    fprintf(rejfp, "%s", pfetch(i));
    672       1.1       cgd 	    break;
    673       1.1       cgd 	case ' ': case '-': case '+': case '!':
    674       1.1       cgd 	    fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
    675       1.1       cgd 	    break;
    676       1.1       cgd 	default:
    677      1.11  kristerw 	    fatal("fatal internal error in abort_hunk\n");
    678       1.1       cgd 	}
    679       1.1       cgd     }
    680       1.1       cgd }
    681       1.1       cgd 
    682       1.1       cgd /* We found where to apply it (we hope), so do it. */
    683       1.1       cgd 
    684       1.6  christos static void
    685      1.10  kristerw apply_hunk(LINENUM where)
    686       1.1       cgd {
    687      1.10  kristerw     LINENUM old = 1;
    688      1.10  kristerw     LINENUM lastline = pch_ptrn_lines();
    689      1.10  kristerw     LINENUM new = lastline+1;
    690       1.1       cgd #define OUTSIDE 0
    691       1.1       cgd #define IN_IFNDEF 1
    692       1.1       cgd #define IN_IFDEF 2
    693       1.1       cgd #define IN_ELSE 3
    694      1.10  kristerw     int def_state = OUTSIDE;
    695      1.10  kristerw     bool R_do_defines = do_defines;
    696      1.10  kristerw     LINENUM pat_end = pch_end();
    697       1.1       cgd 
    698       1.1       cgd     where--;
    699       1.1       cgd     while (pch_char(new) == '=' || pch_char(new) == '\n')
    700       1.1       cgd 	new++;
    701       1.1       cgd 
    702       1.1       cgd     while (old <= lastline) {
    703       1.1       cgd 	if (pch_char(old) == '-') {
    704       1.1       cgd 	    copy_till(where + old - 1);
    705       1.1       cgd 	    if (R_do_defines) {
    706       1.1       cgd 		if (def_state == OUTSIDE) {
    707       1.1       cgd 		    fputs(not_defined, ofp);
    708       1.1       cgd 		    def_state = IN_IFNDEF;
    709       1.1       cgd 		}
    710       1.1       cgd 		else if (def_state == IN_IFDEF) {
    711       1.1       cgd 		    fputs(else_defined, ofp);
    712       1.1       cgd 		    def_state = IN_ELSE;
    713       1.1       cgd 		}
    714       1.1       cgd 		fputs(pfetch(old), ofp);
    715       1.1       cgd 	    }
    716       1.1       cgd 	    last_frozen_line++;
    717       1.1       cgd 	    old++;
    718       1.1       cgd 	}
    719       1.1       cgd 	else if (new > pat_end) {
    720       1.1       cgd 	    break;
    721       1.1       cgd 	}
    722       1.1       cgd 	else if (pch_char(new) == '+') {
    723       1.1       cgd 	    copy_till(where + old - 1);
    724       1.1       cgd 	    if (R_do_defines) {
    725       1.1       cgd 		if (def_state == IN_IFNDEF) {
    726       1.1       cgd 		    fputs(else_defined, ofp);
    727       1.1       cgd 		    def_state = IN_ELSE;
    728       1.1       cgd 		}
    729       1.1       cgd 		else if (def_state == OUTSIDE) {
    730       1.1       cgd 		    fputs(if_defined, ofp);
    731       1.1       cgd 		    def_state = IN_IFDEF;
    732       1.1       cgd 		}
    733       1.1       cgd 	    }
    734       1.1       cgd 	    fputs(pfetch(new), ofp);
    735       1.1       cgd 	    new++;
    736       1.1       cgd 	}
    737       1.1       cgd 	else if (pch_char(new) != pch_char(old)) {
    738      1.11  kristerw 	    say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
    739       1.1       cgd 		pch_hunk_beg() + old,
    740       1.1       cgd 		pch_hunk_beg() + new);
    741       1.1       cgd #ifdef DEBUGGING
    742      1.11  kristerw 	    say("oldchar = '%c', newchar = '%c'\n",
    743       1.1       cgd 		pch_char(old), pch_char(new));
    744       1.1       cgd #endif
    745       1.1       cgd 	    my_exit(1);
    746       1.1       cgd 	}
    747       1.1       cgd 	else if (pch_char(new) == '!') {
    748       1.1       cgd 	    copy_till(where + old - 1);
    749       1.1       cgd 	    if (R_do_defines) {
    750       1.1       cgd 	       fputs(not_defined, ofp);
    751       1.1       cgd 	       def_state = IN_IFNDEF;
    752       1.1       cgd 	    }
    753       1.1       cgd 	    while (pch_char(old) == '!') {
    754       1.1       cgd 		if (R_do_defines) {
    755       1.1       cgd 		    fputs(pfetch(old), ofp);
    756       1.1       cgd 		}
    757       1.1       cgd 		last_frozen_line++;
    758       1.1       cgd 		old++;
    759       1.1       cgd 	    }
    760       1.1       cgd 	    if (R_do_defines) {
    761       1.1       cgd 		fputs(else_defined, ofp);
    762       1.1       cgd 		def_state = IN_ELSE;
    763       1.1       cgd 	    }
    764       1.1       cgd 	    while (pch_char(new) == '!') {
    765       1.1       cgd 		fputs(pfetch(new), ofp);
    766       1.1       cgd 		new++;
    767       1.1       cgd 	    }
    768       1.1       cgd 	}
    769       1.1       cgd 	else {
    770       1.1       cgd 	    assert(pch_char(new) == ' ');
    771       1.1       cgd 	    old++;
    772       1.1       cgd 	    new++;
    773       1.1       cgd 	    if (R_do_defines && def_state != OUTSIDE) {
    774       1.1       cgd 		fputs(end_defined, ofp);
    775       1.1       cgd 		def_state = OUTSIDE;
    776       1.1       cgd 	    }
    777       1.1       cgd 	}
    778       1.1       cgd     }
    779       1.1       cgd     if (new <= pat_end && pch_char(new) == '+') {
    780       1.1       cgd 	copy_till(where + old - 1);
    781       1.1       cgd 	if (R_do_defines) {
    782       1.1       cgd 	    if (def_state == OUTSIDE) {
    783       1.1       cgd 	    	fputs(if_defined, ofp);
    784       1.1       cgd 		def_state = IN_IFDEF;
    785       1.1       cgd 	    }
    786       1.1       cgd 	    else if (def_state == IN_IFNDEF) {
    787       1.1       cgd 		fputs(else_defined, ofp);
    788       1.1       cgd 		def_state = IN_ELSE;
    789       1.1       cgd 	    }
    790       1.1       cgd 	}
    791       1.1       cgd 	while (new <= pat_end && pch_char(new) == '+') {
    792       1.1       cgd 	    fputs(pfetch(new), ofp);
    793       1.1       cgd 	    new++;
    794       1.1       cgd 	}
    795       1.1       cgd     }
    796       1.1       cgd     if (R_do_defines && def_state != OUTSIDE) {
    797       1.1       cgd 	fputs(end_defined, ofp);
    798       1.1       cgd     }
    799       1.1       cgd }
    800       1.1       cgd 
    801       1.1       cgd /* Open the new file. */
    802       1.1       cgd 
    803       1.6  christos static void
    804      1.10  kristerw init_output(char *name)
    805       1.1       cgd {
    806       1.1       cgd     ofp = fopen(name, "w");
    807      1.11  kristerw     if (ofp == NULL)
    808      1.11  kristerw 	pfatal("can't create %s", name);
    809       1.1       cgd }
    810       1.1       cgd 
    811       1.1       cgd /* Open a file to put hunks we can't locate. */
    812       1.1       cgd 
    813       1.6  christos static void
    814      1.10  kristerw init_reject(char *name)
    815       1.1       cgd {
    816       1.1       cgd     rejfp = fopen(name, "w");
    817      1.11  kristerw     if (rejfp == NULL)
    818      1.11  kristerw 	pfatal("can't create %s", name);
    819       1.1       cgd }
    820       1.1       cgd 
    821       1.1       cgd /* Copy input file to output, up to wherever hunk is to be applied. */
    822       1.1       cgd 
    823       1.6  christos static void
    824      1.10  kristerw copy_till(LINENUM lastline)
    825       1.1       cgd {
    826      1.10  kristerw     LINENUM R_last_frozen_line = last_frozen_line;
    827       1.1       cgd 
    828       1.1       cgd     if (R_last_frozen_line > lastline)
    829      1.11  kristerw 	fatal("misordered hunks! output would be garbled\n");
    830       1.1       cgd     while (R_last_frozen_line < lastline) {
    831       1.1       cgd 	dump_line(++R_last_frozen_line);
    832       1.1       cgd     }
    833       1.1       cgd     last_frozen_line = R_last_frozen_line;
    834       1.1       cgd }
    835       1.1       cgd 
    836       1.1       cgd /* Finish copying the input file to the output file. */
    837       1.1       cgd 
    838       1.6  christos static void
    839      1.10  kristerw spew_output(void)
    840       1.1       cgd {
    841       1.1       cgd #ifdef DEBUGGING
    842       1.1       cgd     if (debug & 256)
    843      1.11  kristerw 	say("il=%ld lfl=%ld\n",input_lines,last_frozen_line);
    844       1.1       cgd #endif
    845       1.1       cgd     if (input_lines)
    846       1.1       cgd 	copy_till(input_lines);		/* dump remainder of file */
    847       1.1       cgd     Fclose(ofp);
    848      1.11  kristerw     ofp = NULL;
    849       1.1       cgd }
    850       1.1       cgd 
    851       1.1       cgd /* Copy one line from input to output. */
    852       1.1       cgd 
    853       1.6  christos static void
    854      1.10  kristerw dump_line(LINENUM line)
    855       1.1       cgd {
    856      1.10  kristerw     char *s;
    857      1.10  kristerw     char R_newline = '\n';
    858       1.1       cgd 
    859       1.1       cgd     /* Note: string is not null terminated. */
    860       1.1       cgd     for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ;
    861       1.1       cgd }
    862       1.1       cgd 
    863       1.1       cgd /* Does the patch pattern match at line base+offset? */
    864       1.1       cgd 
    865       1.6  christos static bool
    866      1.10  kristerw patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
    867      1.10  kristerw {
    868      1.10  kristerw     LINENUM pline = 1 + fuzz;
    869      1.10  kristerw     LINENUM iline;
    870      1.10  kristerw     LINENUM pat_lines = pch_ptrn_lines() - fuzz;
    871       1.1       cgd 
    872       1.1       cgd     for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) {
    873       1.1       cgd 	if (canonicalize) {
    874       1.1       cgd 	    if (!similar(ifetch(iline, (offset >= 0)),
    875       1.1       cgd 			 pfetch(pline),
    876       1.1       cgd 			 pch_line_len(pline) ))
    877       1.1       cgd 		return FALSE;
    878       1.1       cgd 	}
    879       1.1       cgd 	else if (strnNE(ifetch(iline, (offset >= 0)),
    880       1.1       cgd 		   pfetch(pline),
    881       1.1       cgd 		   pch_line_len(pline) ))
    882       1.1       cgd 	    return FALSE;
    883       1.1       cgd     }
    884       1.1       cgd     return TRUE;
    885       1.1       cgd }
    886       1.1       cgd 
    887       1.1       cgd /* Do two lines match with canonicalized white space? */
    888       1.1       cgd 
    889       1.6  christos static bool
    890      1.10  kristerw similar(char *a, char *b, int len)
    891       1.1       cgd {
    892       1.1       cgd     while (len) {
    893       1.7  christos 	if (isspace((unsigned char)*b)) {/* whitespace (or \n) to match? */
    894       1.7  christos 	    if (!isspace((unsigned char)*a))/* no corresponding whitespace? */
    895       1.1       cgd 		return FALSE;
    896       1.7  christos 	    while (len && isspace((unsigned char)*b) && *b != '\n')
    897       1.1       cgd 		b++,len--;		/* skip pattern whitespace */
    898       1.7  christos 	    while (isspace((unsigned char)*a) && *a != '\n')
    899       1.1       cgd 		a++;			/* skip target whitespace */
    900       1.1       cgd 	    if (*a == '\n' || *b == '\n')
    901       1.1       cgd 		return (*a == *b);	/* should end in sync */
    902       1.1       cgd 	}
    903       1.1       cgd 	else if (*a++ != *b++)		/* match non-whitespace chars */
    904       1.1       cgd 	    return FALSE;
    905       1.1       cgd 	else
    906       1.1       cgd 	    len--;			/* probably not necessary */
    907       1.1       cgd     }
    908       1.1       cgd     return TRUE;			/* actually, this is not reached */
    909       1.1       cgd 					/* since there is always a \n */
    910       1.1       cgd }
    911       1.1       cgd 
    912       1.1       cgd /* Exit with cleanup. */
    913       1.1       cgd 
    914       1.1       cgd void
    915      1.10  kristerw my_exit(int status)
    916       1.1       cgd {
    917       1.1       cgd     Unlink(TMPINNAME);
    918       1.1       cgd     if (!toutkeep) {
    919       1.1       cgd 	Unlink(TMPOUTNAME);
    920       1.1       cgd     }
    921       1.1       cgd     if (!trejkeep) {
    922       1.1       cgd 	Unlink(TMPREJNAME);
    923       1.1       cgd     }
    924       1.1       cgd     Unlink(TMPPATNAME);
    925       1.1       cgd     exit(status);
    926       1.1       cgd }
    927