xref: /freebsd/usr.bin/patch/patch.c (revision 5e64d66ca4365c2a3ebc22a9fd4a333876d8e29a)
12dd076b8SGabor Kovesdan /*-
22dd076b8SGabor Kovesdan  * Copyright 1986, Larry Wall
32dd076b8SGabor Kovesdan  *
42dd076b8SGabor Kovesdan  * Redistribution and use in source and binary forms, with or without
52dd076b8SGabor Kovesdan  * modification, are permitted provided that the following condition is met:
62dd076b8SGabor Kovesdan  * 1. Redistributions of source code must retain the above copyright notice,
72dd076b8SGabor Kovesdan  * this condition and the following disclaimer.
82dd076b8SGabor Kovesdan  *
92dd076b8SGabor Kovesdan  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
102dd076b8SGabor Kovesdan  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
112dd076b8SGabor Kovesdan  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
122dd076b8SGabor Kovesdan  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
132dd076b8SGabor Kovesdan  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
142dd076b8SGabor Kovesdan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
152dd076b8SGabor Kovesdan  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
162dd076b8SGabor Kovesdan  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
172dd076b8SGabor Kovesdan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
182dd076b8SGabor Kovesdan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
192dd076b8SGabor Kovesdan  * SUCH DAMAGE.
202dd076b8SGabor Kovesdan  *
212dd076b8SGabor Kovesdan  * patch - a program to apply diffs to original files
222dd076b8SGabor Kovesdan  *
232dd076b8SGabor Kovesdan  * -C option added in 1998, original code by Marc Espie, based on FreeBSD
242dd076b8SGabor Kovesdan  * behaviour
252dd076b8SGabor Kovesdan  *
26e56ef7d3SXin LI  * $OpenBSD: patch.c,v 1.50 2012/05/15 19:32:02 millert Exp $
27e56ef7d3SXin LI  * $FreeBSD$
282dd076b8SGabor Kovesdan  *
292dd076b8SGabor Kovesdan  */
302dd076b8SGabor Kovesdan 
312dd076b8SGabor Kovesdan #include <sys/types.h>
322dd076b8SGabor Kovesdan #include <sys/stat.h>
332dd076b8SGabor Kovesdan 
342dd076b8SGabor Kovesdan #include <ctype.h>
352dd076b8SGabor Kovesdan #include <getopt.h>
362dd076b8SGabor Kovesdan #include <limits.h>
372dd076b8SGabor Kovesdan #include <stdio.h>
382dd076b8SGabor Kovesdan #include <string.h>
392dd076b8SGabor Kovesdan #include <stdlib.h>
402dd076b8SGabor Kovesdan #include <unistd.h>
412dd076b8SGabor Kovesdan 
422dd076b8SGabor Kovesdan #include "common.h"
432dd076b8SGabor Kovesdan #include "util.h"
442dd076b8SGabor Kovesdan #include "pch.h"
452dd076b8SGabor Kovesdan #include "inp.h"
462dd076b8SGabor Kovesdan #include "backupfile.h"
472dd076b8SGabor Kovesdan #include "pathnames.h"
482dd076b8SGabor Kovesdan 
492dd076b8SGabor Kovesdan mode_t		filemode = 0644;
502dd076b8SGabor Kovesdan 
512dd076b8SGabor Kovesdan char		*buf;			/* general purpose buffer */
522dd076b8SGabor Kovesdan size_t		buf_size;		/* size of the general purpose buffer */
532dd076b8SGabor Kovesdan 
542dd076b8SGabor Kovesdan bool		using_plan_a = true;	/* try to keep everything in memory */
552dd076b8SGabor Kovesdan bool		out_of_mem = false;	/* ran out of memory in plan a */
562dd076b8SGabor Kovesdan 
572dd076b8SGabor Kovesdan #define MAXFILEC 2
582dd076b8SGabor Kovesdan 
592dd076b8SGabor Kovesdan char		*filearg[MAXFILEC];
602dd076b8SGabor Kovesdan bool		ok_to_create_file = false;
612dd076b8SGabor Kovesdan char		*outname = NULL;
622dd076b8SGabor Kovesdan char		*origprae = NULL;
632dd076b8SGabor Kovesdan char		*TMPOUTNAME;
642dd076b8SGabor Kovesdan char		*TMPINNAME;
652dd076b8SGabor Kovesdan char		*TMPREJNAME;
662dd076b8SGabor Kovesdan char		*TMPPATNAME;
672dd076b8SGabor Kovesdan bool		toutkeep = false;
682dd076b8SGabor Kovesdan bool		trejkeep = false;
692dd076b8SGabor Kovesdan bool		warn_on_invalid_line;
702dd076b8SGabor Kovesdan bool		last_line_missing_eol;
712dd076b8SGabor Kovesdan 
722dd076b8SGabor Kovesdan #ifdef DEBUGGING
732dd076b8SGabor Kovesdan int		debug = 0;
742dd076b8SGabor Kovesdan #endif
752dd076b8SGabor Kovesdan 
762dd076b8SGabor Kovesdan bool		force = false;
772dd076b8SGabor Kovesdan bool		batch = false;
782dd076b8SGabor Kovesdan bool		verbose = true;
792dd076b8SGabor Kovesdan bool		reverse = false;
802dd076b8SGabor Kovesdan bool		noreverse = false;
812dd076b8SGabor Kovesdan bool		skip_rest_of_patch = false;
822dd076b8SGabor Kovesdan int		strippath = 957;
832dd076b8SGabor Kovesdan bool		canonicalize = false;
842dd076b8SGabor Kovesdan bool		check_only = false;
852dd076b8SGabor Kovesdan int		diff_type = 0;
862dd076b8SGabor Kovesdan char		*revision = NULL;	/* prerequisite revision, if any */
872dd076b8SGabor Kovesdan LINENUM		input_lines = 0;	/* how long is input file in lines */
882dd076b8SGabor Kovesdan int		posix = 0;		/* strict POSIX mode? */
892dd076b8SGabor Kovesdan 
902dd076b8SGabor Kovesdan static void	reinitialize_almost_everything(void);
912dd076b8SGabor Kovesdan static void	get_some_switches(void);
922dd076b8SGabor Kovesdan static LINENUM	locate_hunk(LINENUM);
932dd076b8SGabor Kovesdan static void	abort_context_hunk(void);
942dd076b8SGabor Kovesdan static void	rej_line(int, LINENUM);
952dd076b8SGabor Kovesdan static void	abort_hunk(void);
962dd076b8SGabor Kovesdan static void	apply_hunk(LINENUM);
972dd076b8SGabor Kovesdan static void	init_output(const char *);
982dd076b8SGabor Kovesdan static void	init_reject(const char *);
992dd076b8SGabor Kovesdan static void	copy_till(LINENUM, bool);
1002dd076b8SGabor Kovesdan static bool	spew_output(void);
1012dd076b8SGabor Kovesdan static void	dump_line(LINENUM, bool);
1022dd076b8SGabor Kovesdan static bool	patch_match(LINENUM, LINENUM, LINENUM);
1032dd076b8SGabor Kovesdan static bool	similar(const char *, const char *, int);
1042dd076b8SGabor Kovesdan static void	usage(void);
1052dd076b8SGabor Kovesdan 
1062dd076b8SGabor Kovesdan /* true if -E was specified on command line.  */
1072dd076b8SGabor Kovesdan static bool	remove_empty_files = false;
1082dd076b8SGabor Kovesdan 
1092dd076b8SGabor Kovesdan /* true if -R was specified on command line.  */
1102dd076b8SGabor Kovesdan static bool	reverse_flag_specified = false;
1112dd076b8SGabor Kovesdan 
1122dd076b8SGabor Kovesdan /* buffer holding the name of the rejected patch file. */
1132dd076b8SGabor Kovesdan static char	rejname[NAME_MAX + 1];
1142dd076b8SGabor Kovesdan 
1152dd076b8SGabor Kovesdan /* how many input lines have been irretractibly output */
1162dd076b8SGabor Kovesdan static LINENUM	last_frozen_line = 0;
1172dd076b8SGabor Kovesdan 
1182dd076b8SGabor Kovesdan static int	Argc;		/* guess */
1192dd076b8SGabor Kovesdan static char	**Argv;
1202dd076b8SGabor Kovesdan static int	Argc_last;	/* for restarting plan_b */
1212dd076b8SGabor Kovesdan static char	**Argv_last;
1222dd076b8SGabor Kovesdan 
1232dd076b8SGabor Kovesdan static FILE	*ofp = NULL;	/* output file pointer */
1242dd076b8SGabor Kovesdan static FILE	*rejfp = NULL;	/* reject file pointer */
1252dd076b8SGabor Kovesdan 
1262dd076b8SGabor Kovesdan static int	filec = 0;	/* how many file arguments? */
1272dd076b8SGabor Kovesdan static LINENUM	last_offset = 0;
1282dd076b8SGabor Kovesdan static LINENUM	maxfuzz = 2;
1292dd076b8SGabor Kovesdan 
1302dd076b8SGabor Kovesdan /* patch using ifdef, ifndef, etc. */
1312dd076b8SGabor Kovesdan static bool		do_defines = false;
1322dd076b8SGabor Kovesdan /* #ifdef xyzzy */
1332dd076b8SGabor Kovesdan static char		if_defined[128];
1342dd076b8SGabor Kovesdan /* #ifndef xyzzy */
1352dd076b8SGabor Kovesdan static char		not_defined[128];
1362dd076b8SGabor Kovesdan /* #else */
1372dd076b8SGabor Kovesdan static const char	else_defined[] = "#else\n";
1382dd076b8SGabor Kovesdan /* #endif xyzzy */
1392dd076b8SGabor Kovesdan static char		end_defined[128];
1402dd076b8SGabor Kovesdan 
1412dd076b8SGabor Kovesdan 
1422dd076b8SGabor Kovesdan /* Apply a set of diffs as appropriate. */
1432dd076b8SGabor Kovesdan 
1442dd076b8SGabor Kovesdan int
1452dd076b8SGabor Kovesdan main(int argc, char *argv[])
1462dd076b8SGabor Kovesdan {
1472dd076b8SGabor Kovesdan 	int	error = 0, hunk, failed, i, fd;
148e11cd3bcSXin LI 	bool	patch_seen, reverse_seen;
1492dd076b8SGabor Kovesdan 	LINENUM	where = 0, newwhere, fuzz, mymaxfuzz;
1502dd076b8SGabor Kovesdan 	const	char *tmpdir;
1512dd076b8SGabor Kovesdan 	char	*v;
1522dd076b8SGabor Kovesdan 
153e56ef7d3SXin LI 	setlinebuf(stdout);
154e56ef7d3SXin LI 	setlinebuf(stderr);
1552dd076b8SGabor Kovesdan 	for (i = 0; i < MAXFILEC; i++)
1562dd076b8SGabor Kovesdan 		filearg[i] = NULL;
1572dd076b8SGabor Kovesdan 
1582dd076b8SGabor Kovesdan 	buf_size = INITLINELEN;
1592dd076b8SGabor Kovesdan 	buf = malloc((unsigned)(buf_size));
1602dd076b8SGabor Kovesdan 	if (buf == NULL)
1612dd076b8SGabor Kovesdan 		fatal("out of memory\n");
1622dd076b8SGabor Kovesdan 
1632dd076b8SGabor Kovesdan 	/* Cons up the names of the temporary files.  */
1642dd076b8SGabor Kovesdan 	if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
1652dd076b8SGabor Kovesdan 		tmpdir = _PATH_TMP;
1662dd076b8SGabor Kovesdan 	for (i = strlen(tmpdir) - 1; i > 0 && tmpdir[i] == '/'; i--)
1672dd076b8SGabor Kovesdan 		;
1682dd076b8SGabor Kovesdan 	i++;
1692dd076b8SGabor Kovesdan 	if (asprintf(&TMPOUTNAME, "%.*s/patchoXXXXXXXXXX", i, tmpdir) == -1)
1702dd076b8SGabor Kovesdan 		fatal("cannot allocate memory");
1712dd076b8SGabor Kovesdan 	if ((fd = mkstemp(TMPOUTNAME)) < 0)
1722dd076b8SGabor Kovesdan 		pfatal("can't create %s", TMPOUTNAME);
1732dd076b8SGabor Kovesdan 	close(fd);
1742dd076b8SGabor Kovesdan 
1752dd076b8SGabor Kovesdan 	if (asprintf(&TMPINNAME, "%.*s/patchiXXXXXXXXXX", i, tmpdir) == -1)
1762dd076b8SGabor Kovesdan 		fatal("cannot allocate memory");
1772dd076b8SGabor Kovesdan 	if ((fd = mkstemp(TMPINNAME)) < 0)
1782dd076b8SGabor Kovesdan 		pfatal("can't create %s", TMPINNAME);
1792dd076b8SGabor Kovesdan 	close(fd);
1802dd076b8SGabor Kovesdan 
1812dd076b8SGabor Kovesdan 	if (asprintf(&TMPREJNAME, "%.*s/patchrXXXXXXXXXX", i, tmpdir) == -1)
1822dd076b8SGabor Kovesdan 		fatal("cannot allocate memory");
1832dd076b8SGabor Kovesdan 	if ((fd = mkstemp(TMPREJNAME)) < 0)
1842dd076b8SGabor Kovesdan 		pfatal("can't create %s", TMPREJNAME);
1852dd076b8SGabor Kovesdan 	close(fd);
1862dd076b8SGabor Kovesdan 
1872dd076b8SGabor Kovesdan 	if (asprintf(&TMPPATNAME, "%.*s/patchpXXXXXXXXXX", i, tmpdir) == -1)
1882dd076b8SGabor Kovesdan 		fatal("cannot allocate memory");
1892dd076b8SGabor Kovesdan 	if ((fd = mkstemp(TMPPATNAME)) < 0)
1902dd076b8SGabor Kovesdan 		pfatal("can't create %s", TMPPATNAME);
1912dd076b8SGabor Kovesdan 	close(fd);
1922dd076b8SGabor Kovesdan 
1932dd076b8SGabor Kovesdan 	v = getenv("SIMPLE_BACKUP_SUFFIX");
1942dd076b8SGabor Kovesdan 	if (v)
1952dd076b8SGabor Kovesdan 		simple_backup_suffix = v;
1962dd076b8SGabor Kovesdan 	else
1972dd076b8SGabor Kovesdan 		simple_backup_suffix = ORIGEXT;
1982dd076b8SGabor Kovesdan 
1992dd076b8SGabor Kovesdan 	/* parse switches */
2002dd076b8SGabor Kovesdan 	Argc = argc;
2012dd076b8SGabor Kovesdan 	Argv = argv;
2022dd076b8SGabor Kovesdan 	get_some_switches();
2032dd076b8SGabor Kovesdan 
2042dd076b8SGabor Kovesdan 	if (backup_type == none) {
2052dd076b8SGabor Kovesdan 		if ((v = getenv("PATCH_VERSION_CONTROL")) == NULL)
2062dd076b8SGabor Kovesdan 			v = getenv("VERSION_CONTROL");
2072dd076b8SGabor Kovesdan 		if (v != NULL || !posix)
2082dd076b8SGabor Kovesdan 			backup_type = get_version(v);	/* OK to pass NULL. */
2092dd076b8SGabor Kovesdan 	}
2102dd076b8SGabor Kovesdan 
2112dd076b8SGabor Kovesdan 	/* make sure we clean up /tmp in case of disaster */
2122dd076b8SGabor Kovesdan 	set_signals(0);
2132dd076b8SGabor Kovesdan 
214e56ef7d3SXin LI 	patch_seen = false;
2152dd076b8SGabor Kovesdan 	for (open_patch_file(filearg[1]); there_is_another_patch();
2162dd076b8SGabor Kovesdan 	    reinitialize_almost_everything()) {
2172dd076b8SGabor Kovesdan 		/* for each patch in patch file */
2182dd076b8SGabor Kovesdan 
219e56ef7d3SXin LI 		patch_seen = true;
220e56ef7d3SXin LI 
2212dd076b8SGabor Kovesdan 		warn_on_invalid_line = true;
2222dd076b8SGabor Kovesdan 
2232dd076b8SGabor Kovesdan 		if (outname == NULL)
2242dd076b8SGabor Kovesdan 			outname = savestr(filearg[0]);
2252dd076b8SGabor Kovesdan 
2262dd076b8SGabor Kovesdan 		/* for ed script just up and do it and exit */
2272dd076b8SGabor Kovesdan 		if (diff_type == ED_DIFF) {
2282dd076b8SGabor Kovesdan 			do_ed_script();
2292dd076b8SGabor Kovesdan 			continue;
2302dd076b8SGabor Kovesdan 		}
2312dd076b8SGabor Kovesdan 		/* initialize the patched file */
2322dd076b8SGabor Kovesdan 		if (!skip_rest_of_patch)
2332dd076b8SGabor Kovesdan 			init_output(TMPOUTNAME);
2342dd076b8SGabor Kovesdan 
2352dd076b8SGabor Kovesdan 		/* initialize reject file */
2362dd076b8SGabor Kovesdan 		init_reject(TMPREJNAME);
2372dd076b8SGabor Kovesdan 
2382dd076b8SGabor Kovesdan 		/* find out where all the lines are */
2392dd076b8SGabor Kovesdan 		if (!skip_rest_of_patch)
2402dd076b8SGabor Kovesdan 			scan_input(filearg[0]);
2412dd076b8SGabor Kovesdan 
242b9740ba1SPedro F. Giffuni 		/*
243b9740ba1SPedro F. Giffuni 		 * from here on, open no standard i/o files, because
244b9740ba1SPedro F. Giffuni 		 * malloc might misfire and we can't catch it easily
245b9740ba1SPedro F. Giffuni 		 */
2462dd076b8SGabor Kovesdan 
2472dd076b8SGabor Kovesdan 		/* apply each hunk of patch */
2482dd076b8SGabor Kovesdan 		hunk = 0;
2492dd076b8SGabor Kovesdan 		failed = 0;
250e11cd3bcSXin LI 		reverse_seen = false;
2512dd076b8SGabor Kovesdan 		out_of_mem = false;
2522dd076b8SGabor Kovesdan 		while (another_hunk()) {
2532dd076b8SGabor Kovesdan 			hunk++;
2542dd076b8SGabor Kovesdan 			fuzz = 0;
2552dd076b8SGabor Kovesdan 			mymaxfuzz = pch_context();
2562dd076b8SGabor Kovesdan 			if (maxfuzz < mymaxfuzz)
2572dd076b8SGabor Kovesdan 				mymaxfuzz = maxfuzz;
2582dd076b8SGabor Kovesdan 			if (!skip_rest_of_patch) {
2592dd076b8SGabor Kovesdan 				do {
2602dd076b8SGabor Kovesdan 					where = locate_hunk(fuzz);
261e11cd3bcSXin LI 					if (hunk == 1 && where == 0 && !force && !reverse_seen) {
2622dd076b8SGabor Kovesdan 						/* dwim for reversed patch? */
2632dd076b8SGabor Kovesdan 						if (!pch_swap()) {
2642dd076b8SGabor Kovesdan 							if (fuzz == 0)
2652dd076b8SGabor Kovesdan 								say("Not enough memory to try swapped hunk!  Assuming unswapped.\n");
2662dd076b8SGabor Kovesdan 							continue;
2672dd076b8SGabor Kovesdan 						}
2682dd076b8SGabor Kovesdan 						reverse = !reverse;
2692dd076b8SGabor Kovesdan 						/* try again */
2702dd076b8SGabor Kovesdan 						where = locate_hunk(fuzz);
2712dd076b8SGabor Kovesdan 						if (where == 0) {
2722dd076b8SGabor Kovesdan 							/* didn't find it swapped */
2732dd076b8SGabor Kovesdan 							if (!pch_swap())
2742dd076b8SGabor Kovesdan 								/* put it back to normal */
2752dd076b8SGabor Kovesdan 								fatal("lost hunk on alloc error!\n");
2762dd076b8SGabor Kovesdan 							reverse = !reverse;
2772dd076b8SGabor Kovesdan 						} else if (noreverse) {
2782dd076b8SGabor Kovesdan 							if (!pch_swap())
2792dd076b8SGabor Kovesdan 								/* put it back to normal */
2802dd076b8SGabor Kovesdan 								fatal("lost hunk on alloc error!\n");
2812dd076b8SGabor Kovesdan 							reverse = !reverse;
2822dd076b8SGabor Kovesdan 							say("Ignoring previously applied (or reversed) patch.\n");
2832dd076b8SGabor Kovesdan 							skip_rest_of_patch = true;
2842dd076b8SGabor Kovesdan 						} else if (batch) {
2852dd076b8SGabor Kovesdan 							if (verbose)
2862dd076b8SGabor Kovesdan 								say("%seversed (or previously applied) patch detected!  %s -R.",
2872dd076b8SGabor Kovesdan 								    reverse ? "R" : "Unr",
2882dd076b8SGabor Kovesdan 								    reverse ? "Assuming" : "Ignoring");
2892dd076b8SGabor Kovesdan 						} else {
2902dd076b8SGabor Kovesdan 							ask("%seversed (or previously applied) patch detected!  %s -R? [y] ",
2912dd076b8SGabor Kovesdan 							    reverse ? "R" : "Unr",
2922dd076b8SGabor Kovesdan 							    reverse ? "Assume" : "Ignore");
2932dd076b8SGabor Kovesdan 							if (*buf == 'n') {
2942dd076b8SGabor Kovesdan 								ask("Apply anyway? [n] ");
2952dd076b8SGabor Kovesdan 								if (*buf != 'y')
2962dd076b8SGabor Kovesdan 									skip_rest_of_patch = true;
297e11cd3bcSXin LI 								else
298e11cd3bcSXin LI 									reverse_seen = true;
2992dd076b8SGabor Kovesdan 								where = 0;
3002dd076b8SGabor Kovesdan 								reverse = !reverse;
3012dd076b8SGabor Kovesdan 								if (!pch_swap())
3022dd076b8SGabor Kovesdan 									/* put it back to normal */
3032dd076b8SGabor Kovesdan 									fatal("lost hunk on alloc error!\n");
3042dd076b8SGabor Kovesdan 							}
3052dd076b8SGabor Kovesdan 						}
3062dd076b8SGabor Kovesdan 					}
3072dd076b8SGabor Kovesdan 				} while (!skip_rest_of_patch && where == 0 &&
3082dd076b8SGabor Kovesdan 				    ++fuzz <= mymaxfuzz);
3092dd076b8SGabor Kovesdan 
3102dd076b8SGabor Kovesdan 				if (skip_rest_of_patch) {	/* just got decided */
311ffca5883SGlen Barber 					if (ferror(ofp) || fclose(ofp)) {
3122dd076b8SGabor Kovesdan 						say("Error writing %s\n",
3132dd076b8SGabor Kovesdan 						    TMPOUTNAME);
3142dd076b8SGabor Kovesdan 						error = 1;
3152dd076b8SGabor Kovesdan 					}
3162dd076b8SGabor Kovesdan 					ofp = NULL;
3172dd076b8SGabor Kovesdan 				}
3182dd076b8SGabor Kovesdan 			}
3192dd076b8SGabor Kovesdan 			newwhere = pch_newfirst() + last_offset;
3202dd076b8SGabor Kovesdan 			if (skip_rest_of_patch) {
3212dd076b8SGabor Kovesdan 				abort_hunk();
3222dd076b8SGabor Kovesdan 				failed++;
3232dd076b8SGabor Kovesdan 				if (verbose)
3242dd076b8SGabor Kovesdan 					say("Hunk #%d ignored at %ld.\n",
3252dd076b8SGabor Kovesdan 					    hunk, newwhere);
3262dd076b8SGabor Kovesdan 			} else if (where == 0) {
3272dd076b8SGabor Kovesdan 				abort_hunk();
3282dd076b8SGabor Kovesdan 				failed++;
3292dd076b8SGabor Kovesdan 				if (verbose)
3302dd076b8SGabor Kovesdan 					say("Hunk #%d failed at %ld.\n",
3312dd076b8SGabor Kovesdan 					    hunk, newwhere);
3322dd076b8SGabor Kovesdan 			} else {
3332dd076b8SGabor Kovesdan 				apply_hunk(where);
3342dd076b8SGabor Kovesdan 				if (verbose) {
3352dd076b8SGabor Kovesdan 					say("Hunk #%d succeeded at %ld",
3362dd076b8SGabor Kovesdan 					    hunk, newwhere);
3372dd076b8SGabor Kovesdan 					if (fuzz != 0)
3382dd076b8SGabor Kovesdan 						say(" with fuzz %ld", fuzz);
3392dd076b8SGabor Kovesdan 					if (last_offset)
3402dd076b8SGabor Kovesdan 						say(" (offset %ld line%s)",
3412dd076b8SGabor Kovesdan 						    last_offset,
3422dd076b8SGabor Kovesdan 						    last_offset == 1L ? "" : "s");
3432dd076b8SGabor Kovesdan 					say(".\n");
3442dd076b8SGabor Kovesdan 				}
3452dd076b8SGabor Kovesdan 			}
3462dd076b8SGabor Kovesdan 		}
3472dd076b8SGabor Kovesdan 
3482dd076b8SGabor Kovesdan 		if (out_of_mem && using_plan_a) {
3492dd076b8SGabor Kovesdan 			Argc = Argc_last;
3502dd076b8SGabor Kovesdan 			Argv = Argv_last;
3512dd076b8SGabor Kovesdan 			say("\n\nRan out of memory using Plan A--trying again...\n\n");
3522dd076b8SGabor Kovesdan 			if (ofp)
3532dd076b8SGabor Kovesdan 				fclose(ofp);
3542dd076b8SGabor Kovesdan 			ofp = NULL;
3552dd076b8SGabor Kovesdan 			if (rejfp)
3562dd076b8SGabor Kovesdan 				fclose(rejfp);
3572dd076b8SGabor Kovesdan 			rejfp = NULL;
3582dd076b8SGabor Kovesdan 			continue;
3592dd076b8SGabor Kovesdan 		}
3602dd076b8SGabor Kovesdan 		if (hunk == 0)
3612dd076b8SGabor Kovesdan 			fatal("Internal error: hunk should not be 0\n");
3622dd076b8SGabor Kovesdan 
3632dd076b8SGabor Kovesdan 		/* finish spewing out the new file */
3642dd076b8SGabor Kovesdan 		if (!skip_rest_of_patch && !spew_output()) {
3652dd076b8SGabor Kovesdan 			say("Can't write %s\n", TMPOUTNAME);
3662dd076b8SGabor Kovesdan 			error = 1;
3672dd076b8SGabor Kovesdan 		}
3682dd076b8SGabor Kovesdan 
3692dd076b8SGabor Kovesdan 		/* and put the output where desired */
3702dd076b8SGabor Kovesdan 		ignore_signals();
3712dd076b8SGabor Kovesdan 		if (!skip_rest_of_patch) {
3722dd076b8SGabor Kovesdan 			struct stat	statbuf;
3732dd076b8SGabor Kovesdan 			char	*realout = outname;
3742dd076b8SGabor Kovesdan 
3752dd076b8SGabor Kovesdan 			if (!check_only) {
3762dd076b8SGabor Kovesdan 				if (move_file(TMPOUTNAME, outname) < 0) {
3772dd076b8SGabor Kovesdan 					toutkeep = true;
3782dd076b8SGabor Kovesdan 					realout = TMPOUTNAME;
3792dd076b8SGabor Kovesdan 					chmod(TMPOUTNAME, filemode);
3802dd076b8SGabor Kovesdan 				} else
3812dd076b8SGabor Kovesdan 					chmod(outname, filemode);
3822dd076b8SGabor Kovesdan 
3832dd076b8SGabor Kovesdan 				if (remove_empty_files &&
3842dd076b8SGabor Kovesdan 				    stat(realout, &statbuf) == 0 &&
3852dd076b8SGabor Kovesdan 				    statbuf.st_size == 0) {
3862dd076b8SGabor Kovesdan 					if (verbose)
3872dd076b8SGabor Kovesdan 						say("Removing %s (empty after patching).\n",
3882dd076b8SGabor Kovesdan 						    realout);
3892dd076b8SGabor Kovesdan 					unlink(realout);
3902dd076b8SGabor Kovesdan 				}
3912dd076b8SGabor Kovesdan 			}
3922dd076b8SGabor Kovesdan 		}
393ffca5883SGlen Barber 		if (ferror(rejfp) || fclose(rejfp)) {
3942dd076b8SGabor Kovesdan 			say("Error writing %s\n", rejname);
3952dd076b8SGabor Kovesdan 			error = 1;
3962dd076b8SGabor Kovesdan 		}
3972dd076b8SGabor Kovesdan 		rejfp = NULL;
3982dd076b8SGabor Kovesdan 		if (failed) {
3992dd076b8SGabor Kovesdan 			error = 1;
4002dd076b8SGabor Kovesdan 			if (*rejname == '\0') {
4012dd076b8SGabor Kovesdan 				if (strlcpy(rejname, outname,
4022dd076b8SGabor Kovesdan 				    sizeof(rejname)) >= sizeof(rejname))
4032dd076b8SGabor Kovesdan 					fatal("filename %s is too long\n", outname);
4042dd076b8SGabor Kovesdan 				if (strlcat(rejname, REJEXT,
4052dd076b8SGabor Kovesdan 				    sizeof(rejname)) >= sizeof(rejname))
4062dd076b8SGabor Kovesdan 					fatal("filename %s is too long\n", outname);
4072dd076b8SGabor Kovesdan 			}
408e56ef7d3SXin LI 			if (!check_only)
409e56ef7d3SXin LI 				say("%d out of %d hunks %s--saving rejects to %s\n",
410e56ef7d3SXin LI 				    failed, hunk, skip_rest_of_patch ? "ignored" : "failed", rejname);
411e56ef7d3SXin LI 			else
412e11cd3bcSXin LI 				say("%d out of %d hunks %s while patching %s\n",
413e11cd3bcSXin LI 				    failed, hunk, skip_rest_of_patch ? "ignored" : "failed", filearg[0]);
4142dd076b8SGabor Kovesdan 			if (!check_only && move_file(TMPREJNAME, rejname) < 0)
4152dd076b8SGabor Kovesdan 				trejkeep = true;
4162dd076b8SGabor Kovesdan 		}
4172dd076b8SGabor Kovesdan 		set_signals(1);
4182dd076b8SGabor Kovesdan 	}
419e56ef7d3SXin LI 
420e56ef7d3SXin LI 	if (!patch_seen)
421e56ef7d3SXin LI 		error = 2;
422e56ef7d3SXin LI 
4232dd076b8SGabor Kovesdan 	my_exit(error);
4242dd076b8SGabor Kovesdan 	/* NOTREACHED */
4252dd076b8SGabor Kovesdan }
4262dd076b8SGabor Kovesdan 
4272dd076b8SGabor Kovesdan /* Prepare to find the next patch to do in the patch file. */
4282dd076b8SGabor Kovesdan 
4292dd076b8SGabor Kovesdan static void
4302dd076b8SGabor Kovesdan reinitialize_almost_everything(void)
4312dd076b8SGabor Kovesdan {
4322dd076b8SGabor Kovesdan 	re_patch();
4332dd076b8SGabor Kovesdan 	re_input();
4342dd076b8SGabor Kovesdan 
4352dd076b8SGabor Kovesdan 	input_lines = 0;
4362dd076b8SGabor Kovesdan 	last_frozen_line = 0;
4372dd076b8SGabor Kovesdan 
4382dd076b8SGabor Kovesdan 	filec = 0;
4392dd076b8SGabor Kovesdan 	if (!out_of_mem) {
4402dd076b8SGabor Kovesdan 		free(filearg[0]);
4412dd076b8SGabor Kovesdan 		filearg[0] = NULL;
4422dd076b8SGabor Kovesdan 	}
4432dd076b8SGabor Kovesdan 
4442dd076b8SGabor Kovesdan 	free(outname);
4452dd076b8SGabor Kovesdan 	outname = NULL;
4462dd076b8SGabor Kovesdan 
4472dd076b8SGabor Kovesdan 	last_offset = 0;
4482dd076b8SGabor Kovesdan 	diff_type = 0;
4492dd076b8SGabor Kovesdan 
4502dd076b8SGabor Kovesdan 	free(revision);
4512dd076b8SGabor Kovesdan 	revision = NULL;
4522dd076b8SGabor Kovesdan 
4532dd076b8SGabor Kovesdan 	reverse = reverse_flag_specified;
4542dd076b8SGabor Kovesdan 	skip_rest_of_patch = false;
4552dd076b8SGabor Kovesdan 
4562dd076b8SGabor Kovesdan 	get_some_switches();
4572dd076b8SGabor Kovesdan }
4582dd076b8SGabor Kovesdan 
4592dd076b8SGabor Kovesdan /* Process switches and filenames. */
4602dd076b8SGabor Kovesdan 
4612dd076b8SGabor Kovesdan static void
4622dd076b8SGabor Kovesdan get_some_switches(void)
4632dd076b8SGabor Kovesdan {
4642dd076b8SGabor Kovesdan 	const char *options = "b::B:cCd:D:eEfF:i:lnNo:p:r:RstuvV:x:z:";
4652dd076b8SGabor Kovesdan 	static struct option longopts[] = {
4662dd076b8SGabor Kovesdan 		{"backup",		no_argument,		0,	'b'},
4672dd076b8SGabor Kovesdan 		{"batch",		no_argument,		0,	't'},
4682dd076b8SGabor Kovesdan 		{"check",		no_argument,		0,	'C'},
4692dd076b8SGabor Kovesdan 		{"context",		no_argument,		0,	'c'},
4702dd076b8SGabor Kovesdan 		{"debug",		required_argument,	0,	'x'},
4712dd076b8SGabor Kovesdan 		{"directory",		required_argument,	0,	'd'},
472*5e64d66cSPedro F. Giffuni 		{"dry-run",		no_argument,		0,	'C'},
4732dd076b8SGabor Kovesdan 		{"ed",			no_argument,		0,	'e'},
4742dd076b8SGabor Kovesdan 		{"force",		no_argument,		0,	'f'},
4752dd076b8SGabor Kovesdan 		{"forward",		no_argument,		0,	'N'},
4762dd076b8SGabor Kovesdan 		{"fuzz",		required_argument,	0,	'F'},
4772dd076b8SGabor Kovesdan 		{"ifdef",		required_argument,	0,	'D'},
4782dd076b8SGabor Kovesdan 		{"input",		required_argument,	0,	'i'},
4792dd076b8SGabor Kovesdan 		{"ignore-whitespace",	no_argument,		0,	'l'},
4802dd076b8SGabor Kovesdan 		{"normal",		no_argument,		0,	'n'},
4812dd076b8SGabor Kovesdan 		{"output",		required_argument,	0,	'o'},
4822dd076b8SGabor Kovesdan 		{"prefix",		required_argument,	0,	'B'},
4832dd076b8SGabor Kovesdan 		{"quiet",		no_argument,		0,	's'},
4842dd076b8SGabor Kovesdan 		{"reject-file",		required_argument,	0,	'r'},
4852dd076b8SGabor Kovesdan 		{"remove-empty-files",	no_argument,		0,	'E'},
4862dd076b8SGabor Kovesdan 		{"reverse",		no_argument,		0,	'R'},
4872dd076b8SGabor Kovesdan 		{"silent",		no_argument,		0,	's'},
4882dd076b8SGabor Kovesdan 		{"strip",		required_argument,	0,	'p'},
4892dd076b8SGabor Kovesdan 		{"suffix",		required_argument,	0,	'z'},
4902dd076b8SGabor Kovesdan 		{"unified",		no_argument,		0,	'u'},
4912dd076b8SGabor Kovesdan 		{"version",		no_argument,		0,	'v'},
4922dd076b8SGabor Kovesdan 		{"version-control",	required_argument,	0,	'V'},
4932dd076b8SGabor Kovesdan 		{"posix",		no_argument,		&posix,	1},
4942dd076b8SGabor Kovesdan 		{NULL,			0,			0,	0}
4952dd076b8SGabor Kovesdan 	};
4962dd076b8SGabor Kovesdan 	int ch;
4972dd076b8SGabor Kovesdan 
4982dd076b8SGabor Kovesdan 	rejname[0] = '\0';
4992dd076b8SGabor Kovesdan 	Argc_last = Argc;
5002dd076b8SGabor Kovesdan 	Argv_last = Argv;
5012dd076b8SGabor Kovesdan 	if (!Argc)
5022dd076b8SGabor Kovesdan 		return;
5032dd076b8SGabor Kovesdan 	optreset = optind = 1;
5042dd076b8SGabor Kovesdan 	while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) {
5052dd076b8SGabor Kovesdan 		switch (ch) {
5062dd076b8SGabor Kovesdan 		case 'b':
5072dd076b8SGabor Kovesdan 			if (backup_type == none)
5082dd076b8SGabor Kovesdan 				backup_type = numbered_existing;
5092dd076b8SGabor Kovesdan 			if (optarg == NULL)
5102dd076b8SGabor Kovesdan 				break;
5112dd076b8SGabor Kovesdan 			if (verbose)
5122dd076b8SGabor Kovesdan 				say("Warning, the ``-b suffix'' option has been"
5132dd076b8SGabor Kovesdan 				    " obsoleted by the -z option.\n");
5142dd076b8SGabor Kovesdan 			/* FALLTHROUGH */
5152dd076b8SGabor Kovesdan 		case 'z':
5162dd076b8SGabor Kovesdan 			/* must directly follow 'b' case for backwards compat */
5172dd076b8SGabor Kovesdan 			simple_backup_suffix = savestr(optarg);
5182dd076b8SGabor Kovesdan 			break;
5192dd076b8SGabor Kovesdan 		case 'B':
5202dd076b8SGabor Kovesdan 			origprae = savestr(optarg);
5212dd076b8SGabor Kovesdan 			break;
5222dd076b8SGabor Kovesdan 		case 'c':
5232dd076b8SGabor Kovesdan 			diff_type = CONTEXT_DIFF;
5242dd076b8SGabor Kovesdan 			break;
5252dd076b8SGabor Kovesdan 		case 'C':
5262dd076b8SGabor Kovesdan 			check_only = true;
5272dd076b8SGabor Kovesdan 			break;
5282dd076b8SGabor Kovesdan 		case 'd':
5292dd076b8SGabor Kovesdan 			if (chdir(optarg) < 0)
5302dd076b8SGabor Kovesdan 				pfatal("can't cd to %s", optarg);
5312dd076b8SGabor Kovesdan 			break;
5322dd076b8SGabor Kovesdan 		case 'D':
5332dd076b8SGabor Kovesdan 			do_defines = true;
5342dd076b8SGabor Kovesdan 			if (!isalpha((unsigned char)*optarg) && *optarg != '_')
5352dd076b8SGabor Kovesdan 				fatal("argument to -D is not an identifier\n");
5362dd076b8SGabor Kovesdan 			snprintf(if_defined, sizeof if_defined,
5372dd076b8SGabor Kovesdan 			    "#ifdef %s\n", optarg);
5382dd076b8SGabor Kovesdan 			snprintf(not_defined, sizeof not_defined,
5392dd076b8SGabor Kovesdan 			    "#ifndef %s\n", optarg);
5402dd076b8SGabor Kovesdan 			snprintf(end_defined, sizeof end_defined,
5412dd076b8SGabor Kovesdan 			    "#endif /* %s */\n", optarg);
5422dd076b8SGabor Kovesdan 			break;
5432dd076b8SGabor Kovesdan 		case 'e':
5442dd076b8SGabor Kovesdan 			diff_type = ED_DIFF;
5452dd076b8SGabor Kovesdan 			break;
5462dd076b8SGabor Kovesdan 		case 'E':
5472dd076b8SGabor Kovesdan 			remove_empty_files = true;
5482dd076b8SGabor Kovesdan 			break;
5492dd076b8SGabor Kovesdan 		case 'f':
5502dd076b8SGabor Kovesdan 			force = true;
5512dd076b8SGabor Kovesdan 			break;
5522dd076b8SGabor Kovesdan 		case 'F':
5532dd076b8SGabor Kovesdan 			maxfuzz = atoi(optarg);
5542dd076b8SGabor Kovesdan 			break;
5552dd076b8SGabor Kovesdan 		case 'i':
5562dd076b8SGabor Kovesdan 			if (++filec == MAXFILEC)
5572dd076b8SGabor Kovesdan 				fatal("too many file arguments\n");
5582dd076b8SGabor Kovesdan 			filearg[filec] = savestr(optarg);
5592dd076b8SGabor Kovesdan 			break;
5602dd076b8SGabor Kovesdan 		case 'l':
5612dd076b8SGabor Kovesdan 			canonicalize = true;
5622dd076b8SGabor Kovesdan 			break;
5632dd076b8SGabor Kovesdan 		case 'n':
5642dd076b8SGabor Kovesdan 			diff_type = NORMAL_DIFF;
5652dd076b8SGabor Kovesdan 			break;
5662dd076b8SGabor Kovesdan 		case 'N':
5672dd076b8SGabor Kovesdan 			noreverse = true;
5682dd076b8SGabor Kovesdan 			break;
5692dd076b8SGabor Kovesdan 		case 'o':
5702dd076b8SGabor Kovesdan 			outname = savestr(optarg);
5712dd076b8SGabor Kovesdan 			break;
5722dd076b8SGabor Kovesdan 		case 'p':
5732dd076b8SGabor Kovesdan 			strippath = atoi(optarg);
5742dd076b8SGabor Kovesdan 			break;
5752dd076b8SGabor Kovesdan 		case 'r':
5762dd076b8SGabor Kovesdan 			if (strlcpy(rejname, optarg,
5772dd076b8SGabor Kovesdan 			    sizeof(rejname)) >= sizeof(rejname))
5782dd076b8SGabor Kovesdan 				fatal("argument for -r is too long\n");
5792dd076b8SGabor Kovesdan 			break;
5802dd076b8SGabor Kovesdan 		case 'R':
5812dd076b8SGabor Kovesdan 			reverse = true;
5822dd076b8SGabor Kovesdan 			reverse_flag_specified = true;
5832dd076b8SGabor Kovesdan 			break;
5842dd076b8SGabor Kovesdan 		case 's':
5852dd076b8SGabor Kovesdan 			verbose = false;
5862dd076b8SGabor Kovesdan 			break;
5872dd076b8SGabor Kovesdan 		case 't':
5882dd076b8SGabor Kovesdan 			batch = true;
5892dd076b8SGabor Kovesdan 			break;
5902dd076b8SGabor Kovesdan 		case 'u':
5912dd076b8SGabor Kovesdan 			diff_type = UNI_DIFF;
5922dd076b8SGabor Kovesdan 			break;
5932dd076b8SGabor Kovesdan 		case 'v':
5942dd076b8SGabor Kovesdan 			version();
5952dd076b8SGabor Kovesdan 			break;
5962dd076b8SGabor Kovesdan 		case 'V':
5972dd076b8SGabor Kovesdan 			backup_type = get_version(optarg);
5982dd076b8SGabor Kovesdan 			break;
5992dd076b8SGabor Kovesdan #ifdef DEBUGGING
6002dd076b8SGabor Kovesdan 		case 'x':
6012dd076b8SGabor Kovesdan 			debug = atoi(optarg);
6022dd076b8SGabor Kovesdan 			break;
6032dd076b8SGabor Kovesdan #endif
6042dd076b8SGabor Kovesdan 		default:
6052dd076b8SGabor Kovesdan 			if (ch != '\0')
6062dd076b8SGabor Kovesdan 				usage();
6072dd076b8SGabor Kovesdan 			break;
6082dd076b8SGabor Kovesdan 		}
6092dd076b8SGabor Kovesdan 	}
6102dd076b8SGabor Kovesdan 	Argc -= optind;
6112dd076b8SGabor Kovesdan 	Argv += optind;
6122dd076b8SGabor Kovesdan 
6132dd076b8SGabor Kovesdan 	if (Argc > 0) {
6142dd076b8SGabor Kovesdan 		filearg[0] = savestr(*Argv++);
6152dd076b8SGabor Kovesdan 		Argc--;
6162dd076b8SGabor Kovesdan 		while (Argc > 0) {
6172dd076b8SGabor Kovesdan 			if (++filec == MAXFILEC)
6182dd076b8SGabor Kovesdan 				fatal("too many file arguments\n");
6192dd076b8SGabor Kovesdan 			filearg[filec] = savestr(*Argv++);
6202dd076b8SGabor Kovesdan 			Argc--;
6212dd076b8SGabor Kovesdan 		}
6222dd076b8SGabor Kovesdan 	}
6232dd076b8SGabor Kovesdan 
6242dd076b8SGabor Kovesdan 	if (getenv("POSIXLY_CORRECT") != NULL)
6252dd076b8SGabor Kovesdan 		posix = 1;
6262dd076b8SGabor Kovesdan }
6272dd076b8SGabor Kovesdan 
6282dd076b8SGabor Kovesdan static void
6292dd076b8SGabor Kovesdan usage(void)
6302dd076b8SGabor Kovesdan {
6312dd076b8SGabor Kovesdan 	fprintf(stderr,
6322dd076b8SGabor Kovesdan "usage: patch [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] [-d directory]\n"
6332dd076b8SGabor Kovesdan "             [-F max-fuzz] [-i patchfile] [-o out-file] [-p strip-count]\n"
6342dd076b8SGabor Kovesdan "             [-r rej-name] [-V t | nil | never] [-x number] [-z backup-ext]\n"
6352dd076b8SGabor Kovesdan "             [--posix] [origfile [patchfile]]\n"
6362dd076b8SGabor Kovesdan "       patch <patchfile\n");
6372dd076b8SGabor Kovesdan 	my_exit(EXIT_SUCCESS);
6382dd076b8SGabor Kovesdan }
6392dd076b8SGabor Kovesdan 
6402dd076b8SGabor Kovesdan /*
6412dd076b8SGabor Kovesdan  * Attempt to find the right place to apply this hunk of patch.
6422dd076b8SGabor Kovesdan  */
6432dd076b8SGabor Kovesdan static LINENUM
6442dd076b8SGabor Kovesdan locate_hunk(LINENUM fuzz)
6452dd076b8SGabor Kovesdan {
6462dd076b8SGabor Kovesdan 	LINENUM	first_guess = pch_first() + last_offset;
6472dd076b8SGabor Kovesdan 	LINENUM	offset;
6482dd076b8SGabor Kovesdan 	LINENUM	pat_lines = pch_ptrn_lines();
6492dd076b8SGabor Kovesdan 	LINENUM	max_pos_offset = input_lines - first_guess - pat_lines + 1;
6502dd076b8SGabor Kovesdan 	LINENUM	max_neg_offset = first_guess - last_frozen_line - 1 + pch_context();
6512dd076b8SGabor Kovesdan 
6522dd076b8SGabor Kovesdan 	if (pat_lines == 0) {		/* null range matches always */
6532dd076b8SGabor Kovesdan 		if (verbose && fuzz == 0 && (diff_type == CONTEXT_DIFF
6542dd076b8SGabor Kovesdan 		    || diff_type == NEW_CONTEXT_DIFF
6552dd076b8SGabor Kovesdan 		    || diff_type == UNI_DIFF)) {
6562dd076b8SGabor Kovesdan 			say("Empty context always matches.\n");
6572dd076b8SGabor Kovesdan 		}
6582dd076b8SGabor Kovesdan 		return (first_guess);
6592dd076b8SGabor Kovesdan 	}
6602dd076b8SGabor Kovesdan 	if (max_neg_offset >= first_guess)	/* do not try lines < 0 */
6612dd076b8SGabor Kovesdan 		max_neg_offset = first_guess - 1;
6622dd076b8SGabor Kovesdan 	if (first_guess <= input_lines && patch_match(first_guess, 0, fuzz))
6632dd076b8SGabor Kovesdan 		return first_guess;
6642dd076b8SGabor Kovesdan 	for (offset = 1; ; offset++) {
6652dd076b8SGabor Kovesdan 		bool	check_after = (offset <= max_pos_offset);
6662dd076b8SGabor Kovesdan 		bool	check_before = (offset <= max_neg_offset);
6672dd076b8SGabor Kovesdan 
6682dd076b8SGabor Kovesdan 		if (check_after && patch_match(first_guess, offset, fuzz)) {
6692dd076b8SGabor Kovesdan #ifdef DEBUGGING
6702dd076b8SGabor Kovesdan 			if (debug & 1)
6712dd076b8SGabor Kovesdan 				say("Offset changing from %ld to %ld\n",
6722dd076b8SGabor Kovesdan 				    last_offset, offset);
6732dd076b8SGabor Kovesdan #endif
6742dd076b8SGabor Kovesdan 			last_offset = offset;
6752dd076b8SGabor Kovesdan 			return first_guess + offset;
6762dd076b8SGabor Kovesdan 		} else if (check_before && patch_match(first_guess, -offset, fuzz)) {
6772dd076b8SGabor Kovesdan #ifdef DEBUGGING
6782dd076b8SGabor Kovesdan 			if (debug & 1)
6792dd076b8SGabor Kovesdan 				say("Offset changing from %ld to %ld\n",
6802dd076b8SGabor Kovesdan 				    last_offset, -offset);
6812dd076b8SGabor Kovesdan #endif
6822dd076b8SGabor Kovesdan 			last_offset = -offset;
6832dd076b8SGabor Kovesdan 			return first_guess - offset;
6842dd076b8SGabor Kovesdan 		} else if (!check_before && !check_after)
6852dd076b8SGabor Kovesdan 			return 0;
6862dd076b8SGabor Kovesdan 	}
6872dd076b8SGabor Kovesdan }
6882dd076b8SGabor Kovesdan 
6892dd076b8SGabor Kovesdan /* We did not find the pattern, dump out the hunk so they can handle it. */
6902dd076b8SGabor Kovesdan 
6912dd076b8SGabor Kovesdan static void
6922dd076b8SGabor Kovesdan abort_context_hunk(void)
6932dd076b8SGabor Kovesdan {
6942dd076b8SGabor Kovesdan 	LINENUM	i;
6952dd076b8SGabor Kovesdan 	const LINENUM	pat_end = pch_end();
6962dd076b8SGabor Kovesdan 	/*
6972dd076b8SGabor Kovesdan 	 * add in last_offset to guess the same as the previous successful
6982dd076b8SGabor Kovesdan 	 * hunk
6992dd076b8SGabor Kovesdan 	 */
7002dd076b8SGabor Kovesdan 	const LINENUM	oldfirst = pch_first() + last_offset;
7012dd076b8SGabor Kovesdan 	const LINENUM	newfirst = pch_newfirst() + last_offset;
7022dd076b8SGabor Kovesdan 	const LINENUM	oldlast = oldfirst + pch_ptrn_lines() - 1;
7032dd076b8SGabor Kovesdan 	const LINENUM	newlast = newfirst + pch_repl_lines() - 1;
7042dd076b8SGabor Kovesdan 	const char	*stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
7052dd076b8SGabor Kovesdan 	const char	*minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
7062dd076b8SGabor Kovesdan 
7072dd076b8SGabor Kovesdan 	fprintf(rejfp, "***************\n");
7082dd076b8SGabor Kovesdan 	for (i = 0; i <= pat_end; i++) {
7092dd076b8SGabor Kovesdan 		switch (pch_char(i)) {
7102dd076b8SGabor Kovesdan 		case '*':
7112dd076b8SGabor Kovesdan 			if (oldlast < oldfirst)
7122dd076b8SGabor Kovesdan 				fprintf(rejfp, "*** 0%s\n", stars);
7132dd076b8SGabor Kovesdan 			else if (oldlast == oldfirst)
7142dd076b8SGabor Kovesdan 				fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
7152dd076b8SGabor Kovesdan 			else
7162dd076b8SGabor Kovesdan 				fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst,
7172dd076b8SGabor Kovesdan 				    oldlast, stars);
7182dd076b8SGabor Kovesdan 			break;
7192dd076b8SGabor Kovesdan 		case '=':
7202dd076b8SGabor Kovesdan 			if (newlast < newfirst)
7212dd076b8SGabor Kovesdan 				fprintf(rejfp, "--- 0%s\n", minuses);
7222dd076b8SGabor Kovesdan 			else if (newlast == newfirst)
7232dd076b8SGabor Kovesdan 				fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
7242dd076b8SGabor Kovesdan 			else
7252dd076b8SGabor Kovesdan 				fprintf(rejfp, "--- %ld,%ld%s\n", newfirst,
7262dd076b8SGabor Kovesdan 				    newlast, minuses);
7272dd076b8SGabor Kovesdan 			break;
7282dd076b8SGabor Kovesdan 		case '\n':
7292dd076b8SGabor Kovesdan 			fprintf(rejfp, "%s", pfetch(i));
7302dd076b8SGabor Kovesdan 			break;
7312dd076b8SGabor Kovesdan 		case ' ':
7322dd076b8SGabor Kovesdan 		case '-':
7332dd076b8SGabor Kovesdan 		case '+':
7342dd076b8SGabor Kovesdan 		case '!':
7352dd076b8SGabor Kovesdan 			fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
7362dd076b8SGabor Kovesdan 			break;
7372dd076b8SGabor Kovesdan 		default:
7382dd076b8SGabor Kovesdan 			fatal("fatal internal error in abort_context_hunk\n");
7392dd076b8SGabor Kovesdan 		}
7402dd076b8SGabor Kovesdan 	}
7412dd076b8SGabor Kovesdan }
7422dd076b8SGabor Kovesdan 
7432dd076b8SGabor Kovesdan static void
7442dd076b8SGabor Kovesdan rej_line(int ch, LINENUM i)
7452dd076b8SGabor Kovesdan {
746c1a08643SPedro F. Giffuni 	unsigned short len;
7472dd076b8SGabor Kovesdan 	const char *line = pfetch(i);
7482dd076b8SGabor Kovesdan 
749c1a08643SPedro F. Giffuni 	len = strnlen(line, USHRT_MAX);
7502dd076b8SGabor Kovesdan 
7512dd076b8SGabor Kovesdan 	fprintf(rejfp, "%c%s", ch, line);
752c1a08643SPedro F. Giffuni 	if (len == 0 || line[len-1] != '\n') {
753c1a08643SPedro F. Giffuni 		if (len >= USHRT_MAX)
754c1a08643SPedro F. Giffuni 			fprintf(rejfp, "\n\\ Line too long\n");
755c1a08643SPedro F. Giffuni 		else
756c1a08643SPedro F. Giffuni 			fprintf(rejfp, "\n\\ No newline at end of line\n");
757c1a08643SPedro F. Giffuni 	}
7582dd076b8SGabor Kovesdan }
7592dd076b8SGabor Kovesdan 
7602dd076b8SGabor Kovesdan static void
7612dd076b8SGabor Kovesdan abort_hunk(void)
7622dd076b8SGabor Kovesdan {
7632dd076b8SGabor Kovesdan 	LINENUM		i, j, split;
7642dd076b8SGabor Kovesdan 	int		ch1, ch2;
7652dd076b8SGabor Kovesdan 	const LINENUM	pat_end = pch_end();
7662dd076b8SGabor Kovesdan 	const LINENUM	oldfirst = pch_first() + last_offset;
7672dd076b8SGabor Kovesdan 	const LINENUM	newfirst = pch_newfirst() + last_offset;
7682dd076b8SGabor Kovesdan 
7692dd076b8SGabor Kovesdan 	if (diff_type != UNI_DIFF) {
7702dd076b8SGabor Kovesdan 		abort_context_hunk();
7712dd076b8SGabor Kovesdan 		return;
7722dd076b8SGabor Kovesdan 	}
7732dd076b8SGabor Kovesdan 	split = -1;
7742dd076b8SGabor Kovesdan 	for (i = 0; i <= pat_end; i++) {
7752dd076b8SGabor Kovesdan 		if (pch_char(i) == '=') {
7762dd076b8SGabor Kovesdan 			split = i;
7772dd076b8SGabor Kovesdan 			break;
7782dd076b8SGabor Kovesdan 		}
7792dd076b8SGabor Kovesdan 	}
7802dd076b8SGabor Kovesdan 	if (split == -1) {
7812dd076b8SGabor Kovesdan 		fprintf(rejfp, "malformed hunk: no split found\n");
7822dd076b8SGabor Kovesdan 		return;
7832dd076b8SGabor Kovesdan 	}
7842dd076b8SGabor Kovesdan 	i = 0;
7852dd076b8SGabor Kovesdan 	j = split + 1;
7862dd076b8SGabor Kovesdan 	fprintf(rejfp, "@@ -%ld,%ld +%ld,%ld @@\n",
7872dd076b8SGabor Kovesdan 	    pch_ptrn_lines() ? oldfirst : 0,
7882dd076b8SGabor Kovesdan 	    pch_ptrn_lines(), newfirst, pch_repl_lines());
7892dd076b8SGabor Kovesdan 	while (i < split || j <= pat_end) {
7902dd076b8SGabor Kovesdan 		ch1 = i < split ? pch_char(i) : -1;
7912dd076b8SGabor Kovesdan 		ch2 = j <= pat_end ? pch_char(j) : -1;
7922dd076b8SGabor Kovesdan 		if (ch1 == '-') {
7932dd076b8SGabor Kovesdan 			rej_line('-', i);
7942dd076b8SGabor Kovesdan 			i++;
7952dd076b8SGabor Kovesdan 		} else if (ch1 == ' ' && ch2 == ' ') {
7962dd076b8SGabor Kovesdan 			rej_line(' ', i);
7972dd076b8SGabor Kovesdan 			i++;
7982dd076b8SGabor Kovesdan 			j++;
7992dd076b8SGabor Kovesdan 		} else if (ch1 == '!' && ch2 == '!') {
8002dd076b8SGabor Kovesdan 			while (i < split && ch1 == '!') {
8012dd076b8SGabor Kovesdan 				rej_line('-', i);
8022dd076b8SGabor Kovesdan 				i++;
8032dd076b8SGabor Kovesdan 				ch1 = i < split ? pch_char(i) : -1;
8042dd076b8SGabor Kovesdan 			}
8052dd076b8SGabor Kovesdan 			while (j <= pat_end && ch2 == '!') {
8062dd076b8SGabor Kovesdan 				rej_line('+', j);
8072dd076b8SGabor Kovesdan 				j++;
8082dd076b8SGabor Kovesdan 				ch2 = j <= pat_end ? pch_char(j) : -1;
8092dd076b8SGabor Kovesdan 			}
8102dd076b8SGabor Kovesdan 		} else if (ch1 == '*') {
8112dd076b8SGabor Kovesdan 			i++;
8122dd076b8SGabor Kovesdan 		} else if (ch2 == '+' || ch2 == ' ') {
8132dd076b8SGabor Kovesdan 			rej_line(ch2, j);
8142dd076b8SGabor Kovesdan 			j++;
8152dd076b8SGabor Kovesdan 		} else {
8162dd076b8SGabor Kovesdan 			fprintf(rejfp, "internal error on (%ld %ld %ld)\n",
8172dd076b8SGabor Kovesdan 			    i, split, j);
8182dd076b8SGabor Kovesdan 			rej_line(ch1, i);
8192dd076b8SGabor Kovesdan 			rej_line(ch2, j);
8202dd076b8SGabor Kovesdan 			return;
8212dd076b8SGabor Kovesdan 		}
8222dd076b8SGabor Kovesdan 	}
8232dd076b8SGabor Kovesdan }
8242dd076b8SGabor Kovesdan 
8252dd076b8SGabor Kovesdan /* We found where to apply it (we hope), so do it. */
8262dd076b8SGabor Kovesdan 
8272dd076b8SGabor Kovesdan static void
8282dd076b8SGabor Kovesdan apply_hunk(LINENUM where)
8292dd076b8SGabor Kovesdan {
8302dd076b8SGabor Kovesdan 	LINENUM		old = 1;
8312dd076b8SGabor Kovesdan 	const LINENUM	lastline = pch_ptrn_lines();
8322dd076b8SGabor Kovesdan 	LINENUM		new = lastline + 1;
8332dd076b8SGabor Kovesdan #define OUTSIDE 0
8342dd076b8SGabor Kovesdan #define IN_IFNDEF 1
8352dd076b8SGabor Kovesdan #define IN_IFDEF 2
8362dd076b8SGabor Kovesdan #define IN_ELSE 3
8372dd076b8SGabor Kovesdan 	int		def_state = OUTSIDE;
8382dd076b8SGabor Kovesdan 	const LINENUM	pat_end = pch_end();
8392dd076b8SGabor Kovesdan 
8402dd076b8SGabor Kovesdan 	where--;
8412dd076b8SGabor Kovesdan 	while (pch_char(new) == '=' || pch_char(new) == '\n')
8422dd076b8SGabor Kovesdan 		new++;
8432dd076b8SGabor Kovesdan 
8442dd076b8SGabor Kovesdan 	while (old <= lastline) {
8452dd076b8SGabor Kovesdan 		if (pch_char(old) == '-') {
8462dd076b8SGabor Kovesdan 			copy_till(where + old - 1, false);
8472dd076b8SGabor Kovesdan 			if (do_defines) {
8482dd076b8SGabor Kovesdan 				if (def_state == OUTSIDE) {
8492dd076b8SGabor Kovesdan 					fputs(not_defined, ofp);
8502dd076b8SGabor Kovesdan 					def_state = IN_IFNDEF;
8512dd076b8SGabor Kovesdan 				} else if (def_state == IN_IFDEF) {
8522dd076b8SGabor Kovesdan 					fputs(else_defined, ofp);
8532dd076b8SGabor Kovesdan 					def_state = IN_ELSE;
8542dd076b8SGabor Kovesdan 				}
8552dd076b8SGabor Kovesdan 				fputs(pfetch(old), ofp);
8562dd076b8SGabor Kovesdan 			}
8572dd076b8SGabor Kovesdan 			last_frozen_line++;
8582dd076b8SGabor Kovesdan 			old++;
8592dd076b8SGabor Kovesdan 		} else if (new > pat_end) {
8602dd076b8SGabor Kovesdan 			break;
8612dd076b8SGabor Kovesdan 		} else if (pch_char(new) == '+') {
8622dd076b8SGabor Kovesdan 			copy_till(where + old - 1, false);
8632dd076b8SGabor Kovesdan 			if (do_defines) {
8642dd076b8SGabor Kovesdan 				if (def_state == IN_IFNDEF) {
8652dd076b8SGabor Kovesdan 					fputs(else_defined, ofp);
8662dd076b8SGabor Kovesdan 					def_state = IN_ELSE;
8672dd076b8SGabor Kovesdan 				} else if (def_state == OUTSIDE) {
8682dd076b8SGabor Kovesdan 					fputs(if_defined, ofp);
8692dd076b8SGabor Kovesdan 					def_state = IN_IFDEF;
8702dd076b8SGabor Kovesdan 				}
8712dd076b8SGabor Kovesdan 			}
8722dd076b8SGabor Kovesdan 			fputs(pfetch(new), ofp);
8732dd076b8SGabor Kovesdan 			new++;
8742dd076b8SGabor Kovesdan 		} else if (pch_char(new) != pch_char(old)) {
8752dd076b8SGabor Kovesdan 			say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
8762dd076b8SGabor Kovesdan 			    pch_hunk_beg() + old,
8772dd076b8SGabor Kovesdan 			    pch_hunk_beg() + new);
8782dd076b8SGabor Kovesdan #ifdef DEBUGGING
8792dd076b8SGabor Kovesdan 			say("oldchar = '%c', newchar = '%c'\n",
8802dd076b8SGabor Kovesdan 			    pch_char(old), pch_char(new));
8812dd076b8SGabor Kovesdan #endif
8822dd076b8SGabor Kovesdan 			my_exit(2);
8832dd076b8SGabor Kovesdan 		} else if (pch_char(new) == '!') {
8842dd076b8SGabor Kovesdan 			copy_till(where + old - 1, false);
8852dd076b8SGabor Kovesdan 			if (do_defines) {
8862dd076b8SGabor Kovesdan 				fputs(not_defined, ofp);
8872dd076b8SGabor Kovesdan 				def_state = IN_IFNDEF;
8882dd076b8SGabor Kovesdan 			}
8892dd076b8SGabor Kovesdan 			while (pch_char(old) == '!') {
8902dd076b8SGabor Kovesdan 				if (do_defines) {
8912dd076b8SGabor Kovesdan 					fputs(pfetch(old), ofp);
8922dd076b8SGabor Kovesdan 				}
8932dd076b8SGabor Kovesdan 				last_frozen_line++;
8942dd076b8SGabor Kovesdan 				old++;
8952dd076b8SGabor Kovesdan 			}
8962dd076b8SGabor Kovesdan 			if (do_defines) {
8972dd076b8SGabor Kovesdan 				fputs(else_defined, ofp);
8982dd076b8SGabor Kovesdan 				def_state = IN_ELSE;
8992dd076b8SGabor Kovesdan 			}
9002dd076b8SGabor Kovesdan 			while (pch_char(new) == '!') {
9012dd076b8SGabor Kovesdan 				fputs(pfetch(new), ofp);
9022dd076b8SGabor Kovesdan 				new++;
9032dd076b8SGabor Kovesdan 			}
9042dd076b8SGabor Kovesdan 		} else {
9052dd076b8SGabor Kovesdan 			if (pch_char(new) != ' ')
9062dd076b8SGabor Kovesdan 				fatal("Internal error: expected ' '\n");
9072dd076b8SGabor Kovesdan 			old++;
9082dd076b8SGabor Kovesdan 			new++;
9092dd076b8SGabor Kovesdan 			if (do_defines && def_state != OUTSIDE) {
9102dd076b8SGabor Kovesdan 				fputs(end_defined, ofp);
9112dd076b8SGabor Kovesdan 				def_state = OUTSIDE;
9122dd076b8SGabor Kovesdan 			}
9132dd076b8SGabor Kovesdan 		}
9142dd076b8SGabor Kovesdan 	}
9152dd076b8SGabor Kovesdan 	if (new <= pat_end && pch_char(new) == '+') {
9162dd076b8SGabor Kovesdan 		copy_till(where + old - 1, false);
9172dd076b8SGabor Kovesdan 		if (do_defines) {
9182dd076b8SGabor Kovesdan 			if (def_state == OUTSIDE) {
9192dd076b8SGabor Kovesdan 				fputs(if_defined, ofp);
9202dd076b8SGabor Kovesdan 				def_state = IN_IFDEF;
9212dd076b8SGabor Kovesdan 			} else if (def_state == IN_IFNDEF) {
9222dd076b8SGabor Kovesdan 				fputs(else_defined, ofp);
9232dd076b8SGabor Kovesdan 				def_state = IN_ELSE;
9242dd076b8SGabor Kovesdan 			}
9252dd076b8SGabor Kovesdan 		}
9262dd076b8SGabor Kovesdan 		while (new <= pat_end && pch_char(new) == '+') {
9272dd076b8SGabor Kovesdan 			fputs(pfetch(new), ofp);
9282dd076b8SGabor Kovesdan 			new++;
9292dd076b8SGabor Kovesdan 		}
9302dd076b8SGabor Kovesdan 	}
9312dd076b8SGabor Kovesdan 	if (do_defines && def_state != OUTSIDE) {
9322dd076b8SGabor Kovesdan 		fputs(end_defined, ofp);
9332dd076b8SGabor Kovesdan 	}
9342dd076b8SGabor Kovesdan }
9352dd076b8SGabor Kovesdan 
9362dd076b8SGabor Kovesdan /*
9372dd076b8SGabor Kovesdan  * Open the new file.
9382dd076b8SGabor Kovesdan  */
9392dd076b8SGabor Kovesdan static void
9402dd076b8SGabor Kovesdan init_output(const char *name)
9412dd076b8SGabor Kovesdan {
9422dd076b8SGabor Kovesdan 	ofp = fopen(name, "w");
9432dd076b8SGabor Kovesdan 	if (ofp == NULL)
9442dd076b8SGabor Kovesdan 		pfatal("can't create %s", name);
9452dd076b8SGabor Kovesdan }
9462dd076b8SGabor Kovesdan 
9472dd076b8SGabor Kovesdan /*
9482dd076b8SGabor Kovesdan  * Open a file to put hunks we can't locate.
9492dd076b8SGabor Kovesdan  */
9502dd076b8SGabor Kovesdan static void
9512dd076b8SGabor Kovesdan init_reject(const char *name)
9522dd076b8SGabor Kovesdan {
9532dd076b8SGabor Kovesdan 	rejfp = fopen(name, "w");
9542dd076b8SGabor Kovesdan 	if (rejfp == NULL)
9552dd076b8SGabor Kovesdan 		pfatal("can't create %s", name);
9562dd076b8SGabor Kovesdan }
9572dd076b8SGabor Kovesdan 
9582dd076b8SGabor Kovesdan /*
9592dd076b8SGabor Kovesdan  * Copy input file to output, up to wherever hunk is to be applied.
9602dd076b8SGabor Kovesdan  * If endoffile is true, treat the last line specially since it may
9612dd076b8SGabor Kovesdan  * lack a newline.
9622dd076b8SGabor Kovesdan  */
9632dd076b8SGabor Kovesdan static void
9642dd076b8SGabor Kovesdan copy_till(LINENUM lastline, bool endoffile)
9652dd076b8SGabor Kovesdan {
9662dd076b8SGabor Kovesdan 	if (last_frozen_line > lastline)
9672dd076b8SGabor Kovesdan 		fatal("misordered hunks! output would be garbled\n");
9682dd076b8SGabor Kovesdan 	while (last_frozen_line < lastline) {
9692dd076b8SGabor Kovesdan 		if (++last_frozen_line == lastline && endoffile)
9702dd076b8SGabor Kovesdan 			dump_line(last_frozen_line, !last_line_missing_eol);
9712dd076b8SGabor Kovesdan 		else
9722dd076b8SGabor Kovesdan 			dump_line(last_frozen_line, true);
9732dd076b8SGabor Kovesdan 	}
9742dd076b8SGabor Kovesdan }
9752dd076b8SGabor Kovesdan 
9762dd076b8SGabor Kovesdan /*
9772dd076b8SGabor Kovesdan  * Finish copying the input file to the output file.
9782dd076b8SGabor Kovesdan  */
9792dd076b8SGabor Kovesdan static bool
9802dd076b8SGabor Kovesdan spew_output(void)
9812dd076b8SGabor Kovesdan {
9822dd076b8SGabor Kovesdan 	int rv;
9832dd076b8SGabor Kovesdan 
9842dd076b8SGabor Kovesdan #ifdef DEBUGGING
9852dd076b8SGabor Kovesdan 	if (debug & 256)
9862dd076b8SGabor Kovesdan 		say("il=%ld lfl=%ld\n", input_lines, last_frozen_line);
9872dd076b8SGabor Kovesdan #endif
9882dd076b8SGabor Kovesdan 	if (input_lines)
9892dd076b8SGabor Kovesdan 		copy_till(input_lines, true);	/* dump remainder of file */
990ffca5883SGlen Barber 	rv = ferror(ofp) == 0 && fclose(ofp) == 0;
9912dd076b8SGabor Kovesdan 	ofp = NULL;
9922dd076b8SGabor Kovesdan 	return rv;
9932dd076b8SGabor Kovesdan }
9942dd076b8SGabor Kovesdan 
9952dd076b8SGabor Kovesdan /*
9962dd076b8SGabor Kovesdan  * Copy one line from input to output.
9972dd076b8SGabor Kovesdan  */
9982dd076b8SGabor Kovesdan static void
9992dd076b8SGabor Kovesdan dump_line(LINENUM line, bool write_newline)
10002dd076b8SGabor Kovesdan {
10012dd076b8SGabor Kovesdan 	char	*s;
10022dd076b8SGabor Kovesdan 
10032dd076b8SGabor Kovesdan 	s = ifetch(line, 0);
10042dd076b8SGabor Kovesdan 	if (s == NULL)
10052dd076b8SGabor Kovesdan 		return;
10062dd076b8SGabor Kovesdan 	/* Note: string is not NUL terminated. */
10072dd076b8SGabor Kovesdan 	for (; *s != '\n'; s++)
10082dd076b8SGabor Kovesdan 		putc(*s, ofp);
10092dd076b8SGabor Kovesdan 	if (write_newline)
10102dd076b8SGabor Kovesdan 		putc('\n', ofp);
10112dd076b8SGabor Kovesdan }
10122dd076b8SGabor Kovesdan 
10132dd076b8SGabor Kovesdan /*
10142dd076b8SGabor Kovesdan  * Does the patch pattern match at line base+offset?
10152dd076b8SGabor Kovesdan  */
10162dd076b8SGabor Kovesdan static bool
10172dd076b8SGabor Kovesdan patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
10182dd076b8SGabor Kovesdan {
10192dd076b8SGabor Kovesdan 	LINENUM		pline = 1 + fuzz;
10202dd076b8SGabor Kovesdan 	LINENUM		iline;
10212dd076b8SGabor Kovesdan 	LINENUM		pat_lines = pch_ptrn_lines() - fuzz;
10222dd076b8SGabor Kovesdan 	const char	*ilineptr;
10232dd076b8SGabor Kovesdan 	const char	*plineptr;
1024c1a08643SPedro F. Giffuni 	unsigned short	plinelen;
10252dd076b8SGabor Kovesdan 
10262dd076b8SGabor Kovesdan 	for (iline = base + offset + fuzz; pline <= pat_lines; pline++, iline++) {
10272dd076b8SGabor Kovesdan 		ilineptr = ifetch(iline, offset >= 0);
10282dd076b8SGabor Kovesdan 		if (ilineptr == NULL)
10292dd076b8SGabor Kovesdan 			return false;
10302dd076b8SGabor Kovesdan 		plineptr = pfetch(pline);
10312dd076b8SGabor Kovesdan 		plinelen = pch_line_len(pline);
10322dd076b8SGabor Kovesdan 		if (canonicalize) {
10332dd076b8SGabor Kovesdan 			if (!similar(ilineptr, plineptr, plinelen))
10342dd076b8SGabor Kovesdan 				return false;
10352dd076b8SGabor Kovesdan 		} else if (strnNE(ilineptr, plineptr, plinelen))
10362dd076b8SGabor Kovesdan 			return false;
10372dd076b8SGabor Kovesdan 		if (iline == input_lines) {
10382dd076b8SGabor Kovesdan 			/*
10392dd076b8SGabor Kovesdan 			 * We are looking at the last line of the file.
10402dd076b8SGabor Kovesdan 			 * If the file has no eol, the patch line should
10412dd076b8SGabor Kovesdan 			 * not have one either and vice-versa. Note that
10422dd076b8SGabor Kovesdan 			 * plinelen > 0.
10432dd076b8SGabor Kovesdan 			 */
10442dd076b8SGabor Kovesdan 			if (last_line_missing_eol) {
10452dd076b8SGabor Kovesdan 				if (plineptr[plinelen - 1] == '\n')
10462dd076b8SGabor Kovesdan 					return false;
10472dd076b8SGabor Kovesdan 			} else {
10482dd076b8SGabor Kovesdan 				if (plineptr[plinelen - 1] != '\n')
10492dd076b8SGabor Kovesdan 					return false;
10502dd076b8SGabor Kovesdan 			}
10512dd076b8SGabor Kovesdan 		}
10522dd076b8SGabor Kovesdan 	}
10532dd076b8SGabor Kovesdan 	return true;
10542dd076b8SGabor Kovesdan }
10552dd076b8SGabor Kovesdan 
10562dd076b8SGabor Kovesdan /*
10572dd076b8SGabor Kovesdan  * Do two lines match with canonicalized white space?
10582dd076b8SGabor Kovesdan  */
10592dd076b8SGabor Kovesdan static bool
10602dd076b8SGabor Kovesdan similar(const char *a, const char *b, int len)
10612dd076b8SGabor Kovesdan {
10622dd076b8SGabor Kovesdan 	while (len) {
10632dd076b8SGabor Kovesdan 		if (isspace((unsigned char)*b)) {	/* whitespace (or \n) to match? */
10642dd076b8SGabor Kovesdan 			if (!isspace((unsigned char)*a))	/* no corresponding whitespace? */
10652dd076b8SGabor Kovesdan 				return false;
10662dd076b8SGabor Kovesdan 			while (len && isspace((unsigned char)*b) && *b != '\n')
10672dd076b8SGabor Kovesdan 				b++, len--;	/* skip pattern whitespace */
10682dd076b8SGabor Kovesdan 			while (isspace((unsigned char)*a) && *a != '\n')
10692dd076b8SGabor Kovesdan 				a++;	/* skip target whitespace */
10702dd076b8SGabor Kovesdan 			if (*a == '\n' || *b == '\n')
10712dd076b8SGabor Kovesdan 				return (*a == *b);	/* should end in sync */
10722dd076b8SGabor Kovesdan 		} else if (*a++ != *b++)	/* match non-whitespace chars */
10732dd076b8SGabor Kovesdan 			return false;
10742dd076b8SGabor Kovesdan 		else
10752dd076b8SGabor Kovesdan 			len--;	/* probably not necessary */
10762dd076b8SGabor Kovesdan 	}
10772dd076b8SGabor Kovesdan 	return true;		/* actually, this is not reached */
10782dd076b8SGabor Kovesdan 	/* since there is always a \n */
10792dd076b8SGabor Kovesdan }
1080