xref: /plan9/sys/src/ape/cmd/patch/patch.c (revision 0b459c2cb92b7c9d88818e9a2f72e678e5bc4553)
1*0b459c2cSDavid du Colombier /* patch - a program to apply diffs to original files */
2*0b459c2cSDavid du Colombier 
3*0b459c2cSDavid du Colombier /* $Id: patch.c,v 1.23 1997/07/05 10:32:23 eggert Exp $ */
4*0b459c2cSDavid du Colombier 
5*0b459c2cSDavid du Colombier /*
6*0b459c2cSDavid du Colombier Copyright 1984, 1985, 1986, 1987, 1988 Larry Wall
7*0b459c2cSDavid du Colombier Copyright 1989, 1990, 1991, 1992, 1993, 1997 Free Software Foundation, Inc.
8*0b459c2cSDavid du Colombier 
9*0b459c2cSDavid du Colombier This program is free software; you can redistribute it and/or modify
10*0b459c2cSDavid du Colombier it under the terms of the GNU General Public License as published by
11*0b459c2cSDavid du Colombier the Free Software Foundation; either version 2, or (at your option)
12*0b459c2cSDavid du Colombier any later version.
13*0b459c2cSDavid du Colombier 
14*0b459c2cSDavid du Colombier This program is distributed in the hope that it will be useful,
15*0b459c2cSDavid du Colombier but WITHOUT ANY WARRANTY; without even the implied warranty of
16*0b459c2cSDavid du Colombier MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17*0b459c2cSDavid du Colombier GNU General Public License for more details.
18*0b459c2cSDavid du Colombier 
19*0b459c2cSDavid du Colombier You should have received a copy of the GNU General Public License
20*0b459c2cSDavid du Colombier along with this program; see the file COPYING.
21*0b459c2cSDavid du Colombier If not, write to the Free Software Foundation,
22*0b459c2cSDavid du Colombier 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23*0b459c2cSDavid du Colombier */
24*0b459c2cSDavid du Colombier 
25*0b459c2cSDavid du Colombier #define XTERN
26*0b459c2cSDavid du Colombier #include <common.h>
27*0b459c2cSDavid du Colombier #undef XTERN
28*0b459c2cSDavid du Colombier #define XTERN extern
29*0b459c2cSDavid du Colombier #include <argmatch.h>
30*0b459c2cSDavid du Colombier #include <backupfile.h>
31*0b459c2cSDavid du Colombier #include <getopt.h>
32*0b459c2cSDavid du Colombier #include <inp.h>
33*0b459c2cSDavid du Colombier #include <pch.h>
34*0b459c2cSDavid du Colombier #include <util.h>
35*0b459c2cSDavid du Colombier #include <version.h>
36*0b459c2cSDavid du Colombier 
37*0b459c2cSDavid du Colombier #if HAVE_UTIME_H
38*0b459c2cSDavid du Colombier # include <utime.h>
39*0b459c2cSDavid du Colombier #endif
40*0b459c2cSDavid du Colombier /* Some nonstandard hosts don't declare this structure even in <utime.h>.  */
41*0b459c2cSDavid du Colombier #if ! HAVE_STRUCT_UTIMBUF
42*0b459c2cSDavid du Colombier struct utimbuf
43*0b459c2cSDavid du Colombier {
44*0b459c2cSDavid du Colombier   time_t actime;
45*0b459c2cSDavid du Colombier   time_t modtime;
46*0b459c2cSDavid du Colombier };
47*0b459c2cSDavid du Colombier #endif
48*0b459c2cSDavid du Colombier 
49*0b459c2cSDavid du Colombier /* Output stream state.  */
50*0b459c2cSDavid du Colombier struct outstate
51*0b459c2cSDavid du Colombier {
52*0b459c2cSDavid du Colombier   FILE *ofp;
53*0b459c2cSDavid du Colombier   int after_newline;
54*0b459c2cSDavid du Colombier   int zero_output;
55*0b459c2cSDavid du Colombier };
56*0b459c2cSDavid du Colombier 
57*0b459c2cSDavid du Colombier /* procedures */
58*0b459c2cSDavid du Colombier 
59*0b459c2cSDavid du Colombier static FILE *create_output_file PARAMS ((char const *));
60*0b459c2cSDavid du Colombier static LINENUM locate_hunk PARAMS ((LINENUM));
61*0b459c2cSDavid du Colombier static bool apply_hunk PARAMS ((struct outstate *, LINENUM));
62*0b459c2cSDavid du Colombier static bool copy_till PARAMS ((struct outstate *, LINENUM));
63*0b459c2cSDavid du Colombier static bool patch_match PARAMS ((LINENUM, LINENUM, LINENUM, LINENUM));
64*0b459c2cSDavid du Colombier static bool similar PARAMS ((char const *, size_t, char const *, size_t));
65*0b459c2cSDavid du Colombier static bool spew_output PARAMS ((struct outstate *));
66*0b459c2cSDavid du Colombier static char const *make_temp PARAMS ((int));
67*0b459c2cSDavid du Colombier static int numeric_string PARAMS ((char const *, int, char const *));
68*0b459c2cSDavid du Colombier static void abort_hunk PARAMS ((void));
69*0b459c2cSDavid du Colombier static void cleanup PARAMS ((void));
70*0b459c2cSDavid du Colombier static void get_some_switches PARAMS ((void));
71*0b459c2cSDavid du Colombier static void init_output PARAMS ((char const *, struct outstate *));
72*0b459c2cSDavid du Colombier static void init_reject PARAMS ((char const *));
73*0b459c2cSDavid du Colombier static void reinitialize_almost_everything PARAMS ((void));
74*0b459c2cSDavid du Colombier static void usage PARAMS ((FILE *, int)) __attribute__((noreturn));
75*0b459c2cSDavid du Colombier 
76*0b459c2cSDavid du Colombier static int make_backups;
77*0b459c2cSDavid du Colombier static int backup_if_mismatch;
78*0b459c2cSDavid du Colombier static char const *version_control;
79*0b459c2cSDavid du Colombier static int remove_empty_files;
80*0b459c2cSDavid du Colombier 
81*0b459c2cSDavid du Colombier /* TRUE if -R was specified on command line.  */
82*0b459c2cSDavid du Colombier static int reverse_flag_specified;
83*0b459c2cSDavid du Colombier 
84*0b459c2cSDavid du Colombier /* how many input lines have been irretractably output */
85*0b459c2cSDavid du Colombier static LINENUM last_frozen_line;
86*0b459c2cSDavid du Colombier 
87*0b459c2cSDavid du Colombier static char const *do_defines; /* symbol to patch using ifdef, ifndef, etc. */
88*0b459c2cSDavid du Colombier static char const if_defined[] = "\n#ifdef %s\n";
89*0b459c2cSDavid du Colombier static char const not_defined[] = "#ifndef %s\n";
90*0b459c2cSDavid du Colombier static char const else_defined[] = "\n#else\n";
91*0b459c2cSDavid du Colombier static char const end_defined[] = "\n#endif /* %s */\n";
92*0b459c2cSDavid du Colombier 
93*0b459c2cSDavid du Colombier static int Argc;
94*0b459c2cSDavid du Colombier static char * const *Argv;
95*0b459c2cSDavid du Colombier 
96*0b459c2cSDavid du Colombier static FILE *rejfp;  /* reject file pointer */
97*0b459c2cSDavid du Colombier 
98*0b459c2cSDavid du Colombier static char const *patchname;
99*0b459c2cSDavid du Colombier static char *rejname;
100*0b459c2cSDavid du Colombier static char const * volatile TMPREJNAME;
101*0b459c2cSDavid du Colombier 
102*0b459c2cSDavid du Colombier static LINENUM last_offset;
103*0b459c2cSDavid du Colombier static LINENUM maxfuzz = 2;
104*0b459c2cSDavid du Colombier 
105*0b459c2cSDavid du Colombier static char serrbuf[BUFSIZ];
106*0b459c2cSDavid du Colombier 
107*0b459c2cSDavid du Colombier char const program_name[] = "patch";
108*0b459c2cSDavid du Colombier 
109*0b459c2cSDavid du Colombier /* Apply a set of diffs as appropriate. */
110*0b459c2cSDavid du Colombier 
111*0b459c2cSDavid du Colombier int main PARAMS ((int, char **));
112*0b459c2cSDavid du Colombier 
113*0b459c2cSDavid du Colombier int
main(argc,argv)114*0b459c2cSDavid du Colombier main(argc,argv)
115*0b459c2cSDavid du Colombier int argc;
116*0b459c2cSDavid du Colombier char **argv;
117*0b459c2cSDavid du Colombier {
118*0b459c2cSDavid du Colombier     char const *val;
119*0b459c2cSDavid du Colombier     bool somefailed = FALSE;
120*0b459c2cSDavid du Colombier     struct outstate outstate;
121*0b459c2cSDavid du Colombier 
122*0b459c2cSDavid du Colombier     init_time ();
123*0b459c2cSDavid du Colombier 
124*0b459c2cSDavid du Colombier     setbuf(stderr, serrbuf);
125*0b459c2cSDavid du Colombier 
126*0b459c2cSDavid du Colombier     bufsize = 8 * 1024;
127*0b459c2cSDavid du Colombier     buf = xmalloc (bufsize);
128*0b459c2cSDavid du Colombier 
129*0b459c2cSDavid du Colombier     strippath = INT_MAX;
130*0b459c2cSDavid du Colombier 
131*0b459c2cSDavid du Colombier     posixly_correct = getenv ("POSIXLY_CORRECT") != 0;
132*0b459c2cSDavid du Colombier     backup_if_mismatch = ! posixly_correct;
133*0b459c2cSDavid du Colombier     patch_get = ((val = getenv ("PATCH_GET"))
134*0b459c2cSDavid du Colombier 		 ? numeric_string (val, 1, "PATCH_GET value")
135*0b459c2cSDavid du Colombier 		 : posixly_correct - 1);
136*0b459c2cSDavid du Colombier 
137*0b459c2cSDavid du Colombier     {
138*0b459c2cSDavid du Colombier       char const *v = getenv ("SIMPLE_BACKUP_SUFFIX");
139*0b459c2cSDavid du Colombier       if (v && *v)
140*0b459c2cSDavid du Colombier 	simple_backup_suffix = v;
141*0b459c2cSDavid du Colombier     }
142*0b459c2cSDavid du Colombier 
143*0b459c2cSDavid du Colombier     version_control = getenv ("PATCH_VERSION_CONTROL");
144*0b459c2cSDavid du Colombier     if (! version_control)
145*0b459c2cSDavid du Colombier       version_control = getenv ("VERSION_CONTROL");
146*0b459c2cSDavid du Colombier 
147*0b459c2cSDavid du Colombier     /* Cons up the names of the global temporary files.
148*0b459c2cSDavid du Colombier        Do this before `cleanup' can possibly be called (e.g. by `pfatal').  */
149*0b459c2cSDavid du Colombier     TMPOUTNAME = make_temp ('o');
150*0b459c2cSDavid du Colombier     TMPINNAME = make_temp ('i');
151*0b459c2cSDavid du Colombier     TMPREJNAME = make_temp ('r');
152*0b459c2cSDavid du Colombier     TMPPATNAME = make_temp ('p');
153*0b459c2cSDavid du Colombier 
154*0b459c2cSDavid du Colombier     /* parse switches */
155*0b459c2cSDavid du Colombier     Argc = argc;
156*0b459c2cSDavid du Colombier     Argv = argv;
157*0b459c2cSDavid du Colombier     get_some_switches();
158*0b459c2cSDavid du Colombier 
159*0b459c2cSDavid du Colombier     if (make_backups | backup_if_mismatch)
160*0b459c2cSDavid du Colombier       backup_type = get_version (version_control);
161*0b459c2cSDavid du Colombier 
162*0b459c2cSDavid du Colombier     init_output (outfile, &outstate);
163*0b459c2cSDavid du Colombier 
164*0b459c2cSDavid du Colombier     /* Make sure we clean up in case of disaster.  */
165*0b459c2cSDavid du Colombier     set_signals(0);
166*0b459c2cSDavid du Colombier 
167*0b459c2cSDavid du Colombier     for (
168*0b459c2cSDavid du Colombier 	open_patch_file (patchname);
169*0b459c2cSDavid du Colombier 	there_is_another_patch();
170*0b459c2cSDavid du Colombier 	reinitialize_almost_everything()
171*0b459c2cSDavid du Colombier     ) {					/* for each patch in patch file */
172*0b459c2cSDavid du Colombier       int hunk = 0;
173*0b459c2cSDavid du Colombier       int failed = 0;
174*0b459c2cSDavid du Colombier       int mismatch = 0;
175*0b459c2cSDavid du Colombier       char *outname = outfile ? outfile : inname;
176*0b459c2cSDavid du Colombier 
177*0b459c2cSDavid du Colombier       if (!skip_rest_of_patch)
178*0b459c2cSDavid du Colombier 	get_input_file (inname, outname);
179*0b459c2cSDavid du Colombier 
180*0b459c2cSDavid du Colombier       if (diff_type == ED_DIFF) {
181*0b459c2cSDavid du Colombier 	outstate.zero_output = 0;
182*0b459c2cSDavid du Colombier 	if (! dry_run)
183*0b459c2cSDavid du Colombier 	  {
184*0b459c2cSDavid du Colombier 	    do_ed_script (outstate.ofp);
185*0b459c2cSDavid du Colombier 	    if (! outfile)
186*0b459c2cSDavid du Colombier 	      {
187*0b459c2cSDavid du Colombier 		struct stat statbuf;
188*0b459c2cSDavid du Colombier 		if (stat (TMPOUTNAME, &statbuf) != 0)
189*0b459c2cSDavid du Colombier 		  pfatal ("%s", TMPOUTNAME);
190*0b459c2cSDavid du Colombier 		outstate.zero_output = statbuf.st_size == 0;
191*0b459c2cSDavid du Colombier 	      }
192*0b459c2cSDavid du Colombier 	  }
193*0b459c2cSDavid du Colombier       } else {
194*0b459c2cSDavid du Colombier 	int got_hunk;
195*0b459c2cSDavid du Colombier 	int apply_anyway = 0;
196*0b459c2cSDavid du Colombier 
197*0b459c2cSDavid du Colombier 	/* initialize the patched file */
198*0b459c2cSDavid du Colombier 	if (! skip_rest_of_patch && ! outfile)
199*0b459c2cSDavid du Colombier 	  init_output (TMPOUTNAME, &outstate);
200*0b459c2cSDavid du Colombier 
201*0b459c2cSDavid du Colombier 	/* initialize reject file */
202*0b459c2cSDavid du Colombier 	init_reject(TMPREJNAME);
203*0b459c2cSDavid du Colombier 
204*0b459c2cSDavid du Colombier 	/* find out where all the lines are */
205*0b459c2cSDavid du Colombier 	if (!skip_rest_of_patch)
206*0b459c2cSDavid du Colombier 	    scan_input (inname);
207*0b459c2cSDavid du Colombier 
208*0b459c2cSDavid du Colombier 	/* from here on, open no standard i/o files, because malloc */
209*0b459c2cSDavid du Colombier 	/* might misfire and we can't catch it easily */
210*0b459c2cSDavid du Colombier 
211*0b459c2cSDavid du Colombier 	/* apply each hunk of patch */
212*0b459c2cSDavid du Colombier 	while (0 < (got_hunk = another_hunk (diff_type, reverse))) {
213*0b459c2cSDavid du Colombier 	    LINENUM where = 0; /* Pacify `gcc -Wall'.  */
214*0b459c2cSDavid du Colombier 	    LINENUM newwhere;
215*0b459c2cSDavid du Colombier 	    LINENUM fuzz = 0;
216*0b459c2cSDavid du Colombier 	    LINENUM prefix_context = pch_prefix_context ();
217*0b459c2cSDavid du Colombier 	    LINENUM suffix_context = pch_suffix_context ();
218*0b459c2cSDavid du Colombier 	    LINENUM context = (prefix_context < suffix_context
219*0b459c2cSDavid du Colombier 			       ? suffix_context : prefix_context);
220*0b459c2cSDavid du Colombier 	    LINENUM mymaxfuzz = (maxfuzz < context ? maxfuzz : context);
221*0b459c2cSDavid du Colombier 	    hunk++;
222*0b459c2cSDavid du Colombier 	    if (!skip_rest_of_patch) {
223*0b459c2cSDavid du Colombier 		do {
224*0b459c2cSDavid du Colombier 		    where = locate_hunk(fuzz);
225*0b459c2cSDavid du Colombier 		    if (! where || fuzz || last_offset)
226*0b459c2cSDavid du Colombier 		      mismatch = 1;
227*0b459c2cSDavid du Colombier 		    if (hunk == 1 && ! where && ! (force | apply_anyway)
228*0b459c2cSDavid du Colombier 			&& reverse == reverse_flag_specified) {
229*0b459c2cSDavid du Colombier 						/* dwim for reversed patch? */
230*0b459c2cSDavid du Colombier 			if (!pch_swap()) {
231*0b459c2cSDavid du Colombier 			    say (
232*0b459c2cSDavid du Colombier "Not enough memory to try swapped hunk!  Assuming unswapped.\n");
233*0b459c2cSDavid du Colombier 			    continue;
234*0b459c2cSDavid du Colombier 			}
235*0b459c2cSDavid du Colombier 			/* Try again.  */
236*0b459c2cSDavid du Colombier 			where = locate_hunk (fuzz);
237*0b459c2cSDavid du Colombier 			if (where
238*0b459c2cSDavid du Colombier 			    && (ok_to_reverse
239*0b459c2cSDavid du Colombier 				("%s patch detected!",
240*0b459c2cSDavid du Colombier 				 (reverse
241*0b459c2cSDavid du Colombier 				  ? "Unreversed"
242*0b459c2cSDavid du Colombier 				  : "Reversed (or previously applied)"))))
243*0b459c2cSDavid du Colombier 			  reverse ^= 1;
244*0b459c2cSDavid du Colombier 			else
245*0b459c2cSDavid du Colombier 			  {
246*0b459c2cSDavid du Colombier 			    /* Put it back to normal.  */
247*0b459c2cSDavid du Colombier 			    if (! pch_swap ())
248*0b459c2cSDavid du Colombier 			      fatal ("lost hunk on alloc error!");
249*0b459c2cSDavid du Colombier 			    if (where)
250*0b459c2cSDavid du Colombier 			      {
251*0b459c2cSDavid du Colombier 				apply_anyway = 1;
252*0b459c2cSDavid du Colombier 				fuzz--; /* Undo `++fuzz' below.  */
253*0b459c2cSDavid du Colombier 				where = 0;
254*0b459c2cSDavid du Colombier 			      }
255*0b459c2cSDavid du Colombier 			  }
256*0b459c2cSDavid du Colombier 		    }
257*0b459c2cSDavid du Colombier 		} while (!skip_rest_of_patch && !where
258*0b459c2cSDavid du Colombier 			 && ++fuzz <= mymaxfuzz);
259*0b459c2cSDavid du Colombier 
260*0b459c2cSDavid du Colombier 		if (skip_rest_of_patch) {		/* just got decided */
261*0b459c2cSDavid du Colombier 		  if (outstate.ofp && ! outfile)
262*0b459c2cSDavid du Colombier 		    {
263*0b459c2cSDavid du Colombier 		      fclose (outstate.ofp);
264*0b459c2cSDavid du Colombier 		      outstate.ofp = 0;
265*0b459c2cSDavid du Colombier 		    }
266*0b459c2cSDavid du Colombier 		}
267*0b459c2cSDavid du Colombier 	    }
268*0b459c2cSDavid du Colombier 
269*0b459c2cSDavid du Colombier 	    newwhere = pch_newfirst() + last_offset;
270*0b459c2cSDavid du Colombier 	    if (skip_rest_of_patch) {
271*0b459c2cSDavid du Colombier 		abort_hunk();
272*0b459c2cSDavid du Colombier 		failed++;
273*0b459c2cSDavid du Colombier 		if (verbosity == VERBOSE)
274*0b459c2cSDavid du Colombier 		    say ("Hunk #%d ignored at %ld.\n", hunk, newwhere);
275*0b459c2cSDavid du Colombier 	    }
276*0b459c2cSDavid du Colombier 	    else if (!where
277*0b459c2cSDavid du Colombier 		     || (where == 1 && pch_says_nonexistent (reverse)
278*0b459c2cSDavid du Colombier 			 && instat.st_size)) {
279*0b459c2cSDavid du Colombier 		if (where)
280*0b459c2cSDavid du Colombier 		  say ("Patch attempted to create file `%s', which already exists.\n", inname);
281*0b459c2cSDavid du Colombier 		abort_hunk();
282*0b459c2cSDavid du Colombier 		failed++;
283*0b459c2cSDavid du Colombier 		if (verbosity != SILENT)
284*0b459c2cSDavid du Colombier 		    say ("Hunk #%d FAILED at %ld.\n", hunk, newwhere);
285*0b459c2cSDavid du Colombier 	    }
286*0b459c2cSDavid du Colombier 	    else if (! apply_hunk (&outstate, where)) {
287*0b459c2cSDavid du Colombier 		abort_hunk ();
288*0b459c2cSDavid du Colombier 		failed++;
289*0b459c2cSDavid du Colombier 		if (verbosity != SILENT)
290*0b459c2cSDavid du Colombier 		    say ("Hunk #%d FAILED at %ld.\n", hunk, newwhere);
291*0b459c2cSDavid du Colombier 	    } else {
292*0b459c2cSDavid du Colombier 		if (verbosity == VERBOSE
293*0b459c2cSDavid du Colombier 		    || (verbosity != SILENT && (fuzz || last_offset))) {
294*0b459c2cSDavid du Colombier 		    say ("Hunk #%d succeeded at %ld", hunk, newwhere);
295*0b459c2cSDavid du Colombier 		    if (fuzz)
296*0b459c2cSDavid du Colombier 			say (" with fuzz %ld", fuzz);
297*0b459c2cSDavid du Colombier 		    if (last_offset)
298*0b459c2cSDavid du Colombier 			say (" (offset %ld line%s)",
299*0b459c2cSDavid du Colombier 			    last_offset, last_offset==1?"":"s");
300*0b459c2cSDavid du Colombier 		    say (".\n");
301*0b459c2cSDavid du Colombier 		}
302*0b459c2cSDavid du Colombier 	    }
303*0b459c2cSDavid du Colombier 	}
304*0b459c2cSDavid du Colombier 
305*0b459c2cSDavid du Colombier 	if (got_hunk < 0  &&  using_plan_a) {
306*0b459c2cSDavid du Colombier 	    if (outfile)
307*0b459c2cSDavid du Colombier 	      fatal ("out of memory using Plan A");
308*0b459c2cSDavid du Colombier 	    say ("\n\nRan out of memory using Plan A -- trying again...\n\n");
309*0b459c2cSDavid du Colombier 	    if (outstate.ofp)
310*0b459c2cSDavid du Colombier 	      {
311*0b459c2cSDavid du Colombier 		fclose (outstate.ofp);
312*0b459c2cSDavid du Colombier 		outstate.ofp = 0;
313*0b459c2cSDavid du Colombier 	      }
314*0b459c2cSDavid du Colombier 	    fclose (rejfp);
315*0b459c2cSDavid du Colombier 	    continue;
316*0b459c2cSDavid du Colombier 	}
317*0b459c2cSDavid du Colombier 
318*0b459c2cSDavid du Colombier 	/* finish spewing out the new file */
319*0b459c2cSDavid du Colombier 	if (!skip_rest_of_patch)
320*0b459c2cSDavid du Colombier 	  {
321*0b459c2cSDavid du Colombier 	    assert (hunk);
322*0b459c2cSDavid du Colombier 	    if (! spew_output (&outstate))
323*0b459c2cSDavid du Colombier 	      {
324*0b459c2cSDavid du Colombier 		say ("Skipping patch.\n");
325*0b459c2cSDavid du Colombier 		skip_rest_of_patch = TRUE;
326*0b459c2cSDavid du Colombier 	      }
327*0b459c2cSDavid du Colombier 	  }
328*0b459c2cSDavid du Colombier       }
329*0b459c2cSDavid du Colombier 
330*0b459c2cSDavid du Colombier       /* and put the output where desired */
331*0b459c2cSDavid du Colombier       ignore_signals ();
332*0b459c2cSDavid du Colombier       if (! skip_rest_of_patch && ! outfile) {
333*0b459c2cSDavid du Colombier 	  if (outstate.zero_output
334*0b459c2cSDavid du Colombier 	      && (remove_empty_files
335*0b459c2cSDavid du Colombier 		  || (pch_says_nonexistent (reverse ^ 1) == 2
336*0b459c2cSDavid du Colombier 		      && ! posixly_correct)))
337*0b459c2cSDavid du Colombier 	    {
338*0b459c2cSDavid du Colombier 	      if (verbosity == VERBOSE)
339*0b459c2cSDavid du Colombier 		say ("Removing file `%s'%s.\n", outname,
340*0b459c2cSDavid du Colombier 		     dry_run ? " and any empty ancestor directories" : "");
341*0b459c2cSDavid du Colombier 	      if (! dry_run)
342*0b459c2cSDavid du Colombier 		{
343*0b459c2cSDavid du Colombier 		  move_file ((char *) 0, outname, (mode_t) 0,
344*0b459c2cSDavid du Colombier 			     (make_backups
345*0b459c2cSDavid du Colombier 			      || (backup_if_mismatch && (mismatch | failed))));
346*0b459c2cSDavid du Colombier 		  removedirs (outname);
347*0b459c2cSDavid du Colombier 		}
348*0b459c2cSDavid du Colombier 	    }
349*0b459c2cSDavid du Colombier 	  else
350*0b459c2cSDavid du Colombier 	    {
351*0b459c2cSDavid du Colombier 	      if (! outstate.zero_output
352*0b459c2cSDavid du Colombier 		  && pch_says_nonexistent (reverse ^ 1))
353*0b459c2cSDavid du Colombier 		{
354*0b459c2cSDavid du Colombier 		  mismatch = 1;
355*0b459c2cSDavid du Colombier 		  if (verbosity != SILENT)
356*0b459c2cSDavid du Colombier 		    say ("File `%s' is not empty after patch, as expected.\n",
357*0b459c2cSDavid du Colombier 			 outname);
358*0b459c2cSDavid du Colombier 		}
359*0b459c2cSDavid du Colombier 
360*0b459c2cSDavid du Colombier 	      if (! dry_run)
361*0b459c2cSDavid du Colombier 		{
362*0b459c2cSDavid du Colombier 		  time_t t;
363*0b459c2cSDavid du Colombier 
364*0b459c2cSDavid du Colombier 		  move_file (TMPOUTNAME, outname, instat.st_mode,
365*0b459c2cSDavid du Colombier 			     (make_backups
366*0b459c2cSDavid du Colombier 			      || (backup_if_mismatch && (mismatch | failed))));
367*0b459c2cSDavid du Colombier 
368*0b459c2cSDavid du Colombier 		  if ((set_time | set_utc)
369*0b459c2cSDavid du Colombier 		      && (t = pch_timestamp (reverse ^ 1)) != (time_t) -1)
370*0b459c2cSDavid du Colombier 		    {
371*0b459c2cSDavid du Colombier 		      struct utimbuf utimbuf;
372*0b459c2cSDavid du Colombier 		      utimbuf.actime = utimbuf.modtime = t;
373*0b459c2cSDavid du Colombier 
374*0b459c2cSDavid du Colombier 		      if (! force && ! inerrno
375*0b459c2cSDavid du Colombier 			  && ! pch_says_nonexistent (reverse)
376*0b459c2cSDavid du Colombier 			  && (t = pch_timestamp (reverse)) != (time_t) -1
377*0b459c2cSDavid du Colombier 			  && t != instat.st_mtime)
378*0b459c2cSDavid du Colombier 			say ("not setting time of file `%s' (time mismatch)\n",
379*0b459c2cSDavid du Colombier 			     outname);
380*0b459c2cSDavid du Colombier 		      else if (! force && (mismatch | failed))
381*0b459c2cSDavid du Colombier 			say ("not setting time of file `%s' (contents mismatch)\n",
382*0b459c2cSDavid du Colombier 			     outname);
383*0b459c2cSDavid du Colombier 		      else if (utime (outname, &utimbuf) != 0)
384*0b459c2cSDavid du Colombier 			pfatal ("can't set timestamp on file `%s'", outname);
385*0b459c2cSDavid du Colombier 		    }
386*0b459c2cSDavid du Colombier 
387*0b459c2cSDavid du Colombier 		  if (! inerrno && chmod (outname, instat.st_mode) != 0)
388*0b459c2cSDavid du Colombier 		    pfatal ("can't set permissions on file `%s'", outname);
389*0b459c2cSDavid du Colombier 		}
390*0b459c2cSDavid du Colombier 	    }
391*0b459c2cSDavid du Colombier       }
392*0b459c2cSDavid du Colombier       if (diff_type != ED_DIFF) {
393*0b459c2cSDavid du Colombier 	if (fclose (rejfp) != 0)
394*0b459c2cSDavid du Colombier 	    write_fatal ();
395*0b459c2cSDavid du Colombier 	if (failed) {
396*0b459c2cSDavid du Colombier 	    somefailed = TRUE;
397*0b459c2cSDavid du Colombier 	    say ("%d out of %d hunk%s %s", failed, hunk, "s" + (hunk == 1),
398*0b459c2cSDavid du Colombier 		 skip_rest_of_patch ? "ignored" : "FAILED");
399*0b459c2cSDavid du Colombier 	    if (outname) {
400*0b459c2cSDavid du Colombier 		char *rej = rejname;
401*0b459c2cSDavid du Colombier 		if (!rejname) {
402*0b459c2cSDavid du Colombier 		    rej = xmalloc (strlen (outname) + 5);
403*0b459c2cSDavid du Colombier 		    strcpy (rej, outname);
404*0b459c2cSDavid du Colombier 		    addext (rej, ".rej", '#');
405*0b459c2cSDavid du Colombier 		}
406*0b459c2cSDavid du Colombier 		say (" -- saving rejects to %s", rej);
407*0b459c2cSDavid du Colombier 		if (! dry_run)
408*0b459c2cSDavid du Colombier 		  {
409*0b459c2cSDavid du Colombier 		    move_file (TMPREJNAME, rej, instat.st_mode, FALSE);
410*0b459c2cSDavid du Colombier 		    if (! inerrno
411*0b459c2cSDavid du Colombier 			&& (chmod (rej, (instat.st_mode
412*0b459c2cSDavid du Colombier 					 & ~(S_IXUSR|S_IXGRP|S_IXOTH)))
413*0b459c2cSDavid du Colombier 			    != 0))
414*0b459c2cSDavid du Colombier 		      pfatal ("can't set permissions on file `%s'", rej);
415*0b459c2cSDavid du Colombier 		  }
416*0b459c2cSDavid du Colombier 		if (!rejname)
417*0b459c2cSDavid du Colombier 		    free (rej);
418*0b459c2cSDavid du Colombier 	    }
419*0b459c2cSDavid du Colombier 	    say ("\n");
420*0b459c2cSDavid du Colombier 	}
421*0b459c2cSDavid du Colombier       }
422*0b459c2cSDavid du Colombier       set_signals (1);
423*0b459c2cSDavid du Colombier     }
424*0b459c2cSDavid du Colombier     if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0))
425*0b459c2cSDavid du Colombier       write_fatal ();
426*0b459c2cSDavid du Colombier     cleanup ();
427*0b459c2cSDavid du Colombier     if (somefailed)
428*0b459c2cSDavid du Colombier       exit (1);
429*0b459c2cSDavid du Colombier     return 0;
430*0b459c2cSDavid du Colombier }
431*0b459c2cSDavid du Colombier 
432*0b459c2cSDavid du Colombier /* Prepare to find the next patch to do in the patch file. */
433*0b459c2cSDavid du Colombier 
434*0b459c2cSDavid du Colombier static void
reinitialize_almost_everything()435*0b459c2cSDavid du Colombier reinitialize_almost_everything()
436*0b459c2cSDavid du Colombier {
437*0b459c2cSDavid du Colombier     re_patch();
438*0b459c2cSDavid du Colombier     re_input();
439*0b459c2cSDavid du Colombier 
440*0b459c2cSDavid du Colombier     input_lines = 0;
441*0b459c2cSDavid du Colombier     last_frozen_line = 0;
442*0b459c2cSDavid du Colombier 
443*0b459c2cSDavid du Colombier     if (inname) {
444*0b459c2cSDavid du Colombier 	free (inname);
445*0b459c2cSDavid du Colombier 	inname = 0;
446*0b459c2cSDavid du Colombier     }
447*0b459c2cSDavid du Colombier 
448*0b459c2cSDavid du Colombier     last_offset = 0;
449*0b459c2cSDavid du Colombier 
450*0b459c2cSDavid du Colombier     diff_type = NO_DIFF;
451*0b459c2cSDavid du Colombier 
452*0b459c2cSDavid du Colombier     if (revision) {
453*0b459c2cSDavid du Colombier 	free(revision);
454*0b459c2cSDavid du Colombier 	revision = 0;
455*0b459c2cSDavid du Colombier     }
456*0b459c2cSDavid du Colombier 
457*0b459c2cSDavid du Colombier     reverse = reverse_flag_specified;
458*0b459c2cSDavid du Colombier     skip_rest_of_patch = FALSE;
459*0b459c2cSDavid du Colombier }
460*0b459c2cSDavid du Colombier 
461*0b459c2cSDavid du Colombier static char const shortopts[] = "bB:cd:D:eEfF:g:i:lnNo:p:r:RstTuvV:x:Y:z:Z";
462*0b459c2cSDavid du Colombier static struct option const longopts[] =
463*0b459c2cSDavid du Colombier {
464*0b459c2cSDavid du Colombier   {"backup", no_argument, NULL, 'b'},
465*0b459c2cSDavid du Colombier   {"prefix", required_argument, NULL, 'B'},
466*0b459c2cSDavid du Colombier   {"context", no_argument, NULL, 'c'},
467*0b459c2cSDavid du Colombier   {"directory", required_argument, NULL, 'd'},
468*0b459c2cSDavid du Colombier   {"ifdef", required_argument, NULL, 'D'},
469*0b459c2cSDavid du Colombier   {"ed", no_argument, NULL, 'e'},
470*0b459c2cSDavid du Colombier   {"remove-empty-files", no_argument, NULL, 'E'},
471*0b459c2cSDavid du Colombier   {"force", no_argument, NULL, 'f'},
472*0b459c2cSDavid du Colombier   {"fuzz", required_argument, NULL, 'F'},
473*0b459c2cSDavid du Colombier   {"get", no_argument, NULL, 'g'},
474*0b459c2cSDavid du Colombier   {"input", required_argument, NULL, 'i'},
475*0b459c2cSDavid du Colombier   {"ignore-whitespace", no_argument, NULL, 'l'},
476*0b459c2cSDavid du Colombier   {"normal", no_argument, NULL, 'n'},
477*0b459c2cSDavid du Colombier   {"forward", no_argument, NULL, 'N'},
478*0b459c2cSDavid du Colombier   {"output", required_argument, NULL, 'o'},
479*0b459c2cSDavid du Colombier   {"strip", required_argument, NULL, 'p'},
480*0b459c2cSDavid du Colombier   {"reject-file", required_argument, NULL, 'r'},
481*0b459c2cSDavid du Colombier   {"reverse", no_argument, NULL, 'R'},
482*0b459c2cSDavid du Colombier   {"quiet", no_argument, NULL, 's'},
483*0b459c2cSDavid du Colombier   {"silent", no_argument, NULL, 's'},
484*0b459c2cSDavid du Colombier   {"batch", no_argument, NULL, 't'},
485*0b459c2cSDavid du Colombier   {"set-time", no_argument, NULL, 'T'},
486*0b459c2cSDavid du Colombier   {"unified", no_argument, NULL, 'u'},
487*0b459c2cSDavid du Colombier   {"version", no_argument, NULL, 'v'},
488*0b459c2cSDavid du Colombier   {"version-control", required_argument, NULL, 'V'},
489*0b459c2cSDavid du Colombier   {"debug", required_argument, NULL, 'x'},
490*0b459c2cSDavid du Colombier   {"basename-prefix", required_argument, NULL, 'Y'},
491*0b459c2cSDavid du Colombier   {"suffix", required_argument, NULL, 'z'},
492*0b459c2cSDavid du Colombier   {"set-utc", no_argument, NULL, 'Z'},
493*0b459c2cSDavid du Colombier   {"dry-run", no_argument, NULL, 129},
494*0b459c2cSDavid du Colombier   {"verbose", no_argument, NULL, 130},
495*0b459c2cSDavid du Colombier   {"binary", no_argument, NULL, 131},
496*0b459c2cSDavid du Colombier   {"help", no_argument, NULL, 132},
497*0b459c2cSDavid du Colombier   {"backup-if-mismatch", no_argument, NULL, 133},
498*0b459c2cSDavid du Colombier   {"no-backup-if-mismatch", no_argument, NULL, 134},
499*0b459c2cSDavid du Colombier   {NULL, no_argument, NULL, 0}
500*0b459c2cSDavid du Colombier };
501*0b459c2cSDavid du Colombier 
502*0b459c2cSDavid du Colombier static char const *const option_help[] =
503*0b459c2cSDavid du Colombier {
504*0b459c2cSDavid du Colombier "Input options:",
505*0b459c2cSDavid du Colombier "",
506*0b459c2cSDavid du Colombier "  -p NUM  --strip=NUM  Strip NUM leading components from file names.",
507*0b459c2cSDavid du Colombier "  -F LINES  --fuzz LINES  Set the fuzz factor to LINES for inexact matching.",
508*0b459c2cSDavid du Colombier "  -l  --ignore-whitespace  Ignore white space changes between patch and input.",
509*0b459c2cSDavid du Colombier "",
510*0b459c2cSDavid du Colombier "  -c  --context  Interpret the patch as a context difference.",
511*0b459c2cSDavid du Colombier "  -e  --ed  Interpret the patch as an ed script.",
512*0b459c2cSDavid du Colombier "  -n  --normal  Interpret the patch as a normal difference.",
513*0b459c2cSDavid du Colombier "  -u  --unified  Interpret the patch as a unified difference.",
514*0b459c2cSDavid du Colombier "",
515*0b459c2cSDavid du Colombier "  -N  --forward  Ignore patches that appear to be reversed or already applied.",
516*0b459c2cSDavid du Colombier "  -R  --reverse  Assume patches were created with old and new files swapped.",
517*0b459c2cSDavid du Colombier "",
518*0b459c2cSDavid du Colombier "  -i PATCHFILE  --input=PATCHFILE  Read patch from PATCHFILE instead of stdin.",
519*0b459c2cSDavid du Colombier "",
520*0b459c2cSDavid du Colombier "Output options:",
521*0b459c2cSDavid du Colombier "",
522*0b459c2cSDavid du Colombier "  -o FILE  --output=FILE  Output patched files to FILE.",
523*0b459c2cSDavid du Colombier "  -r FILE  --reject-file=FILE  Output rejects to FILE.",
524*0b459c2cSDavid du Colombier "",
525*0b459c2cSDavid du Colombier "  -D NAME  --ifdef=NAME  Make merged if-then-else output using NAME.",
526*0b459c2cSDavid du Colombier "  -E  --remove-empty-files  Remove output files that are empty after patching.",
527*0b459c2cSDavid du Colombier "",
528*0b459c2cSDavid du Colombier "  -Z  --set-utc  Set times of patched files, assuming diff uses UTC (GMT).",
529*0b459c2cSDavid du Colombier "  -T  --set-time  Likewise, assuming local time.",
530*0b459c2cSDavid du Colombier "",
531*0b459c2cSDavid du Colombier "Backup and version control options:",
532*0b459c2cSDavid du Colombier "",
533*0b459c2cSDavid du Colombier "  -b  --backup  Back up the original contents of each file.",
534*0b459c2cSDavid du Colombier "  --backup-if-mismatch  Back up if the patch does not match exactly.",
535*0b459c2cSDavid du Colombier "  --no-backup-if-mismatch  Back up mismatches only if otherwise requested.",
536*0b459c2cSDavid du Colombier "",
537*0b459c2cSDavid du Colombier "  -V STYLE  --version-control=STYLE  Use STYLE version control.",
538*0b459c2cSDavid du Colombier "	STYLE is either 'simple', 'numbered', or 'existing'.",
539*0b459c2cSDavid du Colombier "  -B PREFIX  --prefix=PREFIX  Prepend PREFIX to backup file names.",
540*0b459c2cSDavid du Colombier "  -Y PREFIX  --basename-prefix=PREFIX  Prepend PREFIX to backup file basenames.",
541*0b459c2cSDavid du Colombier "  -z SUFFIX  --suffix=SUFFIX  Append SUFFIX to backup file names.",
542*0b459c2cSDavid du Colombier "",
543*0b459c2cSDavid du Colombier "  -g NUM  --get=NUM  Get files from RCS or SCCS if positive; ask if negative.",
544*0b459c2cSDavid du Colombier "",
545*0b459c2cSDavid du Colombier "Miscellaneous options:",
546*0b459c2cSDavid du Colombier "",
547*0b459c2cSDavid du Colombier "  -t  --batch  Ask no questions; skip bad-Prereq patches; assume reversed.",
548*0b459c2cSDavid du Colombier "  -f  --force  Like -t, but ignore bad-Prereq patches, and assume unreversed.",
549*0b459c2cSDavid du Colombier "  -s  --quiet  --silent  Work silently unless an error occurs.",
550*0b459c2cSDavid du Colombier "  --verbose  Output extra information about the work being done.",
551*0b459c2cSDavid du Colombier "  --dry-run  Do not actually change any files; just print what would happen.",
552*0b459c2cSDavid du Colombier "",
553*0b459c2cSDavid du Colombier "  -d DIR  --directory=DIR  Change the working directory to DIR first.",
554*0b459c2cSDavid du Colombier #if HAVE_SETMODE
555*0b459c2cSDavid du Colombier "  --binary  Read and write data in binary mode.",
556*0b459c2cSDavid du Colombier #else
557*0b459c2cSDavid du Colombier "  --binary  Read and write data in binary mode (no effect on this platform).",
558*0b459c2cSDavid du Colombier #endif
559*0b459c2cSDavid du Colombier "",
560*0b459c2cSDavid du Colombier "  -v  --version  Output version info.",
561*0b459c2cSDavid du Colombier "  --help  Output this help.",
562*0b459c2cSDavid du Colombier "",
563*0b459c2cSDavid du Colombier "Report bugs to <bug-gnu-utils@prep.ai.mit.edu>.",
564*0b459c2cSDavid du Colombier 0
565*0b459c2cSDavid du Colombier };
566*0b459c2cSDavid du Colombier 
567*0b459c2cSDavid du Colombier static void
usage(stream,status)568*0b459c2cSDavid du Colombier usage (stream, status)
569*0b459c2cSDavid du Colombier      FILE *stream;
570*0b459c2cSDavid du Colombier      int status;
571*0b459c2cSDavid du Colombier {
572*0b459c2cSDavid du Colombier   char const * const *p;
573*0b459c2cSDavid du Colombier 
574*0b459c2cSDavid du Colombier   if (status != 0)
575*0b459c2cSDavid du Colombier     {
576*0b459c2cSDavid du Colombier       fprintf (stream, "%s: Try `%s --help' for more information.\n",
577*0b459c2cSDavid du Colombier 	       program_name, Argv[0]);
578*0b459c2cSDavid du Colombier     }
579*0b459c2cSDavid du Colombier   else
580*0b459c2cSDavid du Colombier     {
581*0b459c2cSDavid du Colombier       fprintf (stream, "Usage: %s [OPTION]... [ORIGFILE [PATCHFILE]]\n\n",
582*0b459c2cSDavid du Colombier 	       Argv[0]);
583*0b459c2cSDavid du Colombier       for (p = option_help;  *p;  p++)
584*0b459c2cSDavid du Colombier 	fprintf (stream, "%s\n", *p);
585*0b459c2cSDavid du Colombier     }
586*0b459c2cSDavid du Colombier 
587*0b459c2cSDavid du Colombier   exit (status);
588*0b459c2cSDavid du Colombier }
589*0b459c2cSDavid du Colombier 
590*0b459c2cSDavid du Colombier /* Process switches and filenames.  */
591*0b459c2cSDavid du Colombier 
592*0b459c2cSDavid du Colombier static void
get_some_switches()593*0b459c2cSDavid du Colombier get_some_switches()
594*0b459c2cSDavid du Colombier {
595*0b459c2cSDavid du Colombier     register int optc;
596*0b459c2cSDavid du Colombier 
597*0b459c2cSDavid du Colombier     if (rejname)
598*0b459c2cSDavid du Colombier 	free (rejname);
599*0b459c2cSDavid du Colombier     rejname = 0;
600*0b459c2cSDavid du Colombier     if (optind == Argc)
601*0b459c2cSDavid du Colombier 	return;
602*0b459c2cSDavid du Colombier     while ((optc = getopt_long (Argc, Argv, shortopts, longopts, (int *) 0))
603*0b459c2cSDavid du Colombier 	   != -1) {
604*0b459c2cSDavid du Colombier 	switch (optc) {
605*0b459c2cSDavid du Colombier 	    case 'b':
606*0b459c2cSDavid du Colombier 		make_backups = 1;
607*0b459c2cSDavid du Colombier 		 /* Special hack for backward compatibility with CVS 1.9.
608*0b459c2cSDavid du Colombier 		    If the last 4 args are `-b SUFFIX ORIGFILE PATCHFILE',
609*0b459c2cSDavid du Colombier 		    treat `-b' as if it were `-b -z'.  */
610*0b459c2cSDavid du Colombier 		if (Argc - optind == 3
611*0b459c2cSDavid du Colombier 		    && strcmp (Argv[optind - 1], "-b") == 0
612*0b459c2cSDavid du Colombier 		    && ! (Argv[optind + 0][0] == '-' && Argv[optind + 0][1])
613*0b459c2cSDavid du Colombier 		    && ! (Argv[optind + 1][0] == '-' && Argv[optind + 1][1])
614*0b459c2cSDavid du Colombier 		    && ! (Argv[optind + 2][0] == '-' && Argv[optind + 2][1]))
615*0b459c2cSDavid du Colombier 		  {
616*0b459c2cSDavid du Colombier 		    optarg = Argv[optind++];
617*0b459c2cSDavid du Colombier 		    if (verbosity != SILENT)
618*0b459c2cSDavid du Colombier 		      say ("warning: the `-b %s' option is obsolete; use `-b -z %s' instead\n",
619*0b459c2cSDavid du Colombier 			   optarg, optarg);
620*0b459c2cSDavid du Colombier 		    goto case_z;
621*0b459c2cSDavid du Colombier 		  }
622*0b459c2cSDavid du Colombier 		break;
623*0b459c2cSDavid du Colombier 	    case 'B':
624*0b459c2cSDavid du Colombier 		if (!*optarg)
625*0b459c2cSDavid du Colombier 		  fatal ("backup prefix is empty");
626*0b459c2cSDavid du Colombier 		origprae = savestr (optarg);
627*0b459c2cSDavid du Colombier 		break;
628*0b459c2cSDavid du Colombier 	    case 'c':
629*0b459c2cSDavid du Colombier 		diff_type = CONTEXT_DIFF;
630*0b459c2cSDavid du Colombier 		break;
631*0b459c2cSDavid du Colombier 	    case 'd':
632*0b459c2cSDavid du Colombier 		if (chdir(optarg) < 0)
633*0b459c2cSDavid du Colombier 		  pfatal ("can't change directory to `%s'", optarg);
634*0b459c2cSDavid du Colombier 		break;
635*0b459c2cSDavid du Colombier 	    case 'D':
636*0b459c2cSDavid du Colombier 		do_defines = savestr (optarg);
637*0b459c2cSDavid du Colombier 		break;
638*0b459c2cSDavid du Colombier 	    case 'e':
639*0b459c2cSDavid du Colombier 		diff_type = ED_DIFF;
640*0b459c2cSDavid du Colombier 		break;
641*0b459c2cSDavid du Colombier 	    case 'E':
642*0b459c2cSDavid du Colombier 		remove_empty_files = TRUE;
643*0b459c2cSDavid du Colombier 		break;
644*0b459c2cSDavid du Colombier 	    case 'f':
645*0b459c2cSDavid du Colombier 		force = TRUE;
646*0b459c2cSDavid du Colombier 		break;
647*0b459c2cSDavid du Colombier 	    case 'F':
648*0b459c2cSDavid du Colombier 		maxfuzz = numeric_string (optarg, 0, "fuzz factor");
649*0b459c2cSDavid du Colombier 		break;
650*0b459c2cSDavid du Colombier 	    case 'g':
651*0b459c2cSDavid du Colombier 		patch_get = numeric_string (optarg, 1, "get option value");
652*0b459c2cSDavid du Colombier 		break;
653*0b459c2cSDavid du Colombier 	    case 'i':
654*0b459c2cSDavid du Colombier 		patchname = savestr (optarg);
655*0b459c2cSDavid du Colombier 		break;
656*0b459c2cSDavid du Colombier 	    case 'l':
657*0b459c2cSDavid du Colombier 		canonicalize = TRUE;
658*0b459c2cSDavid du Colombier 		break;
659*0b459c2cSDavid du Colombier 	    case 'n':
660*0b459c2cSDavid du Colombier 		diff_type = NORMAL_DIFF;
661*0b459c2cSDavid du Colombier 		break;
662*0b459c2cSDavid du Colombier 	    case 'N':
663*0b459c2cSDavid du Colombier 		noreverse = TRUE;
664*0b459c2cSDavid du Colombier 		break;
665*0b459c2cSDavid du Colombier 	    case 'o':
666*0b459c2cSDavid du Colombier 		if (strcmp (optarg, "-") == 0)
667*0b459c2cSDavid du Colombier 		  fatal ("can't output patches to standard output");
668*0b459c2cSDavid du Colombier 		outfile = savestr (optarg);
669*0b459c2cSDavid du Colombier 		break;
670*0b459c2cSDavid du Colombier 	    case 'p':
671*0b459c2cSDavid du Colombier 		strippath = numeric_string (optarg, 0, "strip count");
672*0b459c2cSDavid du Colombier 		break;
673*0b459c2cSDavid du Colombier 	    case 'r':
674*0b459c2cSDavid du Colombier 		rejname = savestr (optarg);
675*0b459c2cSDavid du Colombier 		break;
676*0b459c2cSDavid du Colombier 	    case 'R':
677*0b459c2cSDavid du Colombier 		reverse = 1;
678*0b459c2cSDavid du Colombier 		reverse_flag_specified = 1;
679*0b459c2cSDavid du Colombier 		break;
680*0b459c2cSDavid du Colombier 	    case 's':
681*0b459c2cSDavid du Colombier 		verbosity = SILENT;
682*0b459c2cSDavid du Colombier 		break;
683*0b459c2cSDavid du Colombier 	    case 't':
684*0b459c2cSDavid du Colombier 		batch = TRUE;
685*0b459c2cSDavid du Colombier 		break;
686*0b459c2cSDavid du Colombier 	    case 'T':
687*0b459c2cSDavid du Colombier 		set_time = 1;
688*0b459c2cSDavid du Colombier 		break;
689*0b459c2cSDavid du Colombier 	    case 'u':
690*0b459c2cSDavid du Colombier 		diff_type = UNI_DIFF;
691*0b459c2cSDavid du Colombier 		break;
692*0b459c2cSDavid du Colombier 	    case 'v':
693*0b459c2cSDavid du Colombier 		version();
694*0b459c2cSDavid du Colombier 		exit (0);
695*0b459c2cSDavid du Colombier 		break;
696*0b459c2cSDavid du Colombier 	    case 'V':
697*0b459c2cSDavid du Colombier 		version_control = optarg;
698*0b459c2cSDavid du Colombier 		break;
699*0b459c2cSDavid du Colombier #if DEBUGGING
700*0b459c2cSDavid du Colombier 	    case 'x':
701*0b459c2cSDavid du Colombier 		debug = numeric_string (optarg, 1, "debugging option");
702*0b459c2cSDavid du Colombier 		break;
703*0b459c2cSDavid du Colombier #endif
704*0b459c2cSDavid du Colombier 	    case 'Y':
705*0b459c2cSDavid du Colombier 		if (!*optarg)
706*0b459c2cSDavid du Colombier 		  fatal ("backup basename prefix is empty");
707*0b459c2cSDavid du Colombier 		origbase = savestr (optarg);
708*0b459c2cSDavid du Colombier 		break;
709*0b459c2cSDavid du Colombier 	    case 'z':
710*0b459c2cSDavid du Colombier 	    case_z:
711*0b459c2cSDavid du Colombier 		if (!*optarg)
712*0b459c2cSDavid du Colombier 		  fatal ("backup suffix is empty");
713*0b459c2cSDavid du Colombier 		simple_backup_suffix = savestr (optarg);
714*0b459c2cSDavid du Colombier 		break;
715*0b459c2cSDavid du Colombier 	    case 'Z':
716*0b459c2cSDavid du Colombier 		set_utc = 1;
717*0b459c2cSDavid du Colombier 		break;
718*0b459c2cSDavid du Colombier 	    case 129:
719*0b459c2cSDavid du Colombier 		dry_run = TRUE;
720*0b459c2cSDavid du Colombier 		break;
721*0b459c2cSDavid du Colombier 	    case 130:
722*0b459c2cSDavid du Colombier 		verbosity = VERBOSE;
723*0b459c2cSDavid du Colombier 		break;
724*0b459c2cSDavid du Colombier 	    case 131:
725*0b459c2cSDavid du Colombier #if HAVE_SETMODE
726*0b459c2cSDavid du Colombier 		binary_transput = O_BINARY;
727*0b459c2cSDavid du Colombier #endif
728*0b459c2cSDavid du Colombier 		break;
729*0b459c2cSDavid du Colombier 	    case 132:
730*0b459c2cSDavid du Colombier 		usage (stdout, 0);
731*0b459c2cSDavid du Colombier 	    case 133:
732*0b459c2cSDavid du Colombier 		backup_if_mismatch = 1;
733*0b459c2cSDavid du Colombier 		break;
734*0b459c2cSDavid du Colombier 	    case 134:
735*0b459c2cSDavid du Colombier 		backup_if_mismatch = 0;
736*0b459c2cSDavid du Colombier 		break;
737*0b459c2cSDavid du Colombier 	    default:
738*0b459c2cSDavid du Colombier 		usage (stderr, 2);
739*0b459c2cSDavid du Colombier 	}
740*0b459c2cSDavid du Colombier     }
741*0b459c2cSDavid du Colombier 
742*0b459c2cSDavid du Colombier     /* Process any filename args.  */
743*0b459c2cSDavid du Colombier     if (optind < Argc)
744*0b459c2cSDavid du Colombier       {
745*0b459c2cSDavid du Colombier 	inname = savestr (Argv[optind++]);
746*0b459c2cSDavid du Colombier 	invc = -1;
747*0b459c2cSDavid du Colombier 	if (optind < Argc)
748*0b459c2cSDavid du Colombier 	  {
749*0b459c2cSDavid du Colombier 	    patchname = savestr (Argv[optind++]);
750*0b459c2cSDavid du Colombier 	    if (optind < Argc)
751*0b459c2cSDavid du Colombier 	      {
752*0b459c2cSDavid du Colombier 		fprintf (stderr, "%s: extra operand `%s'\n",
753*0b459c2cSDavid du Colombier 			 program_name, Argv[optind]);
754*0b459c2cSDavid du Colombier 		usage (stderr, 2);
755*0b459c2cSDavid du Colombier 	      }
756*0b459c2cSDavid du Colombier 	  }
757*0b459c2cSDavid du Colombier       }
758*0b459c2cSDavid du Colombier }
759*0b459c2cSDavid du Colombier 
760*0b459c2cSDavid du Colombier /* Handle STRING (possibly negative if NEGATIVE_ALLOWED is nonzero)
761*0b459c2cSDavid du Colombier    of type ARGTYPE_MSGID by converting it to an integer,
762*0b459c2cSDavid du Colombier    returning the result.  */
763*0b459c2cSDavid du Colombier static int
numeric_string(string,negative_allowed,argtype_msgid)764*0b459c2cSDavid du Colombier numeric_string (string, negative_allowed, argtype_msgid)
765*0b459c2cSDavid du Colombier      char const *string;
766*0b459c2cSDavid du Colombier      int negative_allowed;
767*0b459c2cSDavid du Colombier      char const *argtype_msgid;
768*0b459c2cSDavid du Colombier {
769*0b459c2cSDavid du Colombier   int value = 0;
770*0b459c2cSDavid du Colombier   char const *p = string;
771*0b459c2cSDavid du Colombier   int sign = *p == '-' ? -1 : 1;
772*0b459c2cSDavid du Colombier 
773*0b459c2cSDavid du Colombier   p += *p == '-' || *p == '+';
774*0b459c2cSDavid du Colombier 
775*0b459c2cSDavid du Colombier   do
776*0b459c2cSDavid du Colombier     {
777*0b459c2cSDavid du Colombier       int v10 = value * 10;
778*0b459c2cSDavid du Colombier       int digit = *p - '0';
779*0b459c2cSDavid du Colombier       int signed_digit = sign * digit;
780*0b459c2cSDavid du Colombier       int next_value = v10 + signed_digit;
781*0b459c2cSDavid du Colombier 
782*0b459c2cSDavid du Colombier       if (9 < (unsigned) digit)
783*0b459c2cSDavid du Colombier 	fatal ("%s `%s' is not a number", argtype_msgid, string);
784*0b459c2cSDavid du Colombier 
785*0b459c2cSDavid du Colombier       if (v10 / 10 != value || (next_value < v10) != (signed_digit < 0))
786*0b459c2cSDavid du Colombier 	fatal ("%s `%s' is too large", argtype_msgid, string);
787*0b459c2cSDavid du Colombier 
788*0b459c2cSDavid du Colombier       value = next_value;
789*0b459c2cSDavid du Colombier     }
790*0b459c2cSDavid du Colombier   while (*++p);
791*0b459c2cSDavid du Colombier 
792*0b459c2cSDavid du Colombier   if (value < 0 && ! negative_allowed)
793*0b459c2cSDavid du Colombier     fatal ("%s `%s' is negative", argtype_msgid, string);
794*0b459c2cSDavid du Colombier 
795*0b459c2cSDavid du Colombier   return value;
796*0b459c2cSDavid du Colombier }
797*0b459c2cSDavid du Colombier 
798*0b459c2cSDavid du Colombier /* Attempt to find the right place to apply this hunk of patch. */
799*0b459c2cSDavid du Colombier 
800*0b459c2cSDavid du Colombier static LINENUM
locate_hunk(fuzz)801*0b459c2cSDavid du Colombier locate_hunk(fuzz)
802*0b459c2cSDavid du Colombier LINENUM fuzz;
803*0b459c2cSDavid du Colombier {
804*0b459c2cSDavid du Colombier     register LINENUM first_guess = pch_first () + last_offset;
805*0b459c2cSDavid du Colombier     register LINENUM offset;
806*0b459c2cSDavid du Colombier     LINENUM pat_lines = pch_ptrn_lines();
807*0b459c2cSDavid du Colombier     LINENUM prefix_context = pch_prefix_context ();
808*0b459c2cSDavid du Colombier     LINENUM suffix_context = pch_suffix_context ();
809*0b459c2cSDavid du Colombier     LINENUM context = (prefix_context < suffix_context
810*0b459c2cSDavid du Colombier 		       ? suffix_context : prefix_context);
811*0b459c2cSDavid du Colombier     LINENUM prefix_fuzz = fuzz + prefix_context - context;
812*0b459c2cSDavid du Colombier     LINENUM suffix_fuzz = fuzz + suffix_context - context;
813*0b459c2cSDavid du Colombier     LINENUM max_where = input_lines - (pat_lines - suffix_fuzz) + 1;
814*0b459c2cSDavid du Colombier     LINENUM min_where = last_frozen_line + 1 - (prefix_context - prefix_fuzz);
815*0b459c2cSDavid du Colombier     LINENUM max_pos_offset = max_where - first_guess;
816*0b459c2cSDavid du Colombier     LINENUM max_neg_offset = first_guess - min_where;
817*0b459c2cSDavid du Colombier     LINENUM max_offset = (max_pos_offset < max_neg_offset
818*0b459c2cSDavid du Colombier 			  ? max_neg_offset : max_pos_offset);
819*0b459c2cSDavid du Colombier 
820*0b459c2cSDavid du Colombier     if (!pat_lines)			/* null range matches always */
821*0b459c2cSDavid du Colombier 	return first_guess;
822*0b459c2cSDavid du Colombier 
823*0b459c2cSDavid du Colombier     /* Do not try lines <= 0.  */
824*0b459c2cSDavid du Colombier     if (first_guess <= max_neg_offset)
825*0b459c2cSDavid du Colombier 	max_neg_offset = first_guess - 1;
826*0b459c2cSDavid du Colombier 
827*0b459c2cSDavid du Colombier     if (prefix_fuzz < 0)
828*0b459c2cSDavid du Colombier       {
829*0b459c2cSDavid du Colombier 	/* Can only match start of file.  */
830*0b459c2cSDavid du Colombier 
831*0b459c2cSDavid du Colombier 	if (suffix_fuzz < 0)
832*0b459c2cSDavid du Colombier 	  /* Can only match entire file.  */
833*0b459c2cSDavid du Colombier 	  if (pat_lines != input_lines || prefix_context < last_frozen_line)
834*0b459c2cSDavid du Colombier 	    return 0;
835*0b459c2cSDavid du Colombier 
836*0b459c2cSDavid du Colombier 	offset = 1 - first_guess;
837*0b459c2cSDavid du Colombier 	if (last_frozen_line <= prefix_context
838*0b459c2cSDavid du Colombier 	    && offset <= max_pos_offset
839*0b459c2cSDavid du Colombier 	    && patch_match (first_guess, offset, (LINENUM) 0, suffix_fuzz))
840*0b459c2cSDavid du Colombier 	  {
841*0b459c2cSDavid du Colombier 	    last_offset = offset;
842*0b459c2cSDavid du Colombier 	    return first_guess + offset;
843*0b459c2cSDavid du Colombier 	  }
844*0b459c2cSDavid du Colombier 	else
845*0b459c2cSDavid du Colombier 	  return 0;
846*0b459c2cSDavid du Colombier       }
847*0b459c2cSDavid du Colombier 
848*0b459c2cSDavid du Colombier     if (suffix_fuzz < 0)
849*0b459c2cSDavid du Colombier       {
850*0b459c2cSDavid du Colombier 	/* Can only match end of file.  */
851*0b459c2cSDavid du Colombier 	offset = first_guess - (input_lines - pat_lines + 1);
852*0b459c2cSDavid du Colombier 	if (offset <= max_neg_offset
853*0b459c2cSDavid du Colombier 	    && patch_match (first_guess, -offset, prefix_fuzz, (LINENUM) 0))
854*0b459c2cSDavid du Colombier 	  {
855*0b459c2cSDavid du Colombier 	    last_offset = - offset;
856*0b459c2cSDavid du Colombier 	    return first_guess - offset;
857*0b459c2cSDavid du Colombier 	  }
858*0b459c2cSDavid du Colombier 	else
859*0b459c2cSDavid du Colombier 	  return 0;
860*0b459c2cSDavid du Colombier       }
861*0b459c2cSDavid du Colombier 
862*0b459c2cSDavid du Colombier     for (offset = 0;  offset <= max_offset;  offset++) {
863*0b459c2cSDavid du Colombier 	if (offset <= max_pos_offset
864*0b459c2cSDavid du Colombier 	    && patch_match (first_guess, offset, prefix_fuzz, suffix_fuzz)) {
865*0b459c2cSDavid du Colombier 	    if (debug & 1)
866*0b459c2cSDavid du Colombier 		say ("Offset changing from %ld to %ld\n", last_offset, offset);
867*0b459c2cSDavid du Colombier 	    last_offset = offset;
868*0b459c2cSDavid du Colombier 	    return first_guess+offset;
869*0b459c2cSDavid du Colombier 	}
870*0b459c2cSDavid du Colombier 	if (0 < offset && offset <= max_neg_offset
871*0b459c2cSDavid du Colombier 	    && patch_match (first_guess, -offset, prefix_fuzz, suffix_fuzz)) {
872*0b459c2cSDavid du Colombier 	    if (debug & 1)
873*0b459c2cSDavid du Colombier 		say ("Offset changing from %ld to %ld\n", last_offset, -offset);
874*0b459c2cSDavid du Colombier 	    last_offset = -offset;
875*0b459c2cSDavid du Colombier 	    return first_guess-offset;
876*0b459c2cSDavid du Colombier 	}
877*0b459c2cSDavid du Colombier     }
878*0b459c2cSDavid du Colombier     return 0;
879*0b459c2cSDavid du Colombier }
880*0b459c2cSDavid du Colombier 
881*0b459c2cSDavid du Colombier /* We did not find the pattern, dump out the hunk so they can handle it. */
882*0b459c2cSDavid du Colombier 
883*0b459c2cSDavid du Colombier static void
abort_hunk()884*0b459c2cSDavid du Colombier abort_hunk()
885*0b459c2cSDavid du Colombier {
886*0b459c2cSDavid du Colombier     register LINENUM i;
887*0b459c2cSDavid du Colombier     register LINENUM pat_end = pch_end ();
888*0b459c2cSDavid du Colombier     /* add in last_offset to guess the same as the previous successful hunk */
889*0b459c2cSDavid du Colombier     LINENUM oldfirst = pch_first() + last_offset;
890*0b459c2cSDavid du Colombier     LINENUM newfirst = pch_newfirst() + last_offset;
891*0b459c2cSDavid du Colombier     LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
892*0b459c2cSDavid du Colombier     LINENUM newlast = newfirst + pch_repl_lines() - 1;
893*0b459c2cSDavid du Colombier     char const *stars =
894*0b459c2cSDavid du Colombier       (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ****" : "";
895*0b459c2cSDavid du Colombier     char const *minuses =
896*0b459c2cSDavid du Colombier       (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----";
897*0b459c2cSDavid du Colombier 
898*0b459c2cSDavid du Colombier     fprintf(rejfp, "***************\n");
899*0b459c2cSDavid du Colombier     for (i=0; i<=pat_end; i++) {
900*0b459c2cSDavid du Colombier 	switch (pch_char(i)) {
901*0b459c2cSDavid du Colombier 	case '*':
902*0b459c2cSDavid du Colombier 	    if (oldlast < oldfirst)
903*0b459c2cSDavid du Colombier 		fprintf(rejfp, "*** 0%s\n", stars);
904*0b459c2cSDavid du Colombier 	    else if (oldlast == oldfirst)
905*0b459c2cSDavid du Colombier 		fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
906*0b459c2cSDavid du Colombier 	    else
907*0b459c2cSDavid du Colombier 		fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
908*0b459c2cSDavid du Colombier 	    break;
909*0b459c2cSDavid du Colombier 	case '=':
910*0b459c2cSDavid du Colombier 	    if (newlast < newfirst)
911*0b459c2cSDavid du Colombier 		fprintf(rejfp, "--- 0%s\n", minuses);
912*0b459c2cSDavid du Colombier 	    else if (newlast == newfirst)
913*0b459c2cSDavid du Colombier 		fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
914*0b459c2cSDavid du Colombier 	    else
915*0b459c2cSDavid du Colombier 		fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
916*0b459c2cSDavid du Colombier 	    break;
917*0b459c2cSDavid du Colombier 	case ' ': case '-': case '+': case '!':
918*0b459c2cSDavid du Colombier 	    fprintf (rejfp, "%c ", pch_char (i));
919*0b459c2cSDavid du Colombier 	    /* fall into */
920*0b459c2cSDavid du Colombier 	case '\n':
921*0b459c2cSDavid du Colombier 	    pch_write_line (i, rejfp);
922*0b459c2cSDavid du Colombier 	    break;
923*0b459c2cSDavid du Colombier 	default:
924*0b459c2cSDavid du Colombier 	    fatal ("fatal internal error in abort_hunk");
925*0b459c2cSDavid du Colombier 	}
926*0b459c2cSDavid du Colombier 	if (ferror (rejfp))
927*0b459c2cSDavid du Colombier 	  write_fatal ();
928*0b459c2cSDavid du Colombier     }
929*0b459c2cSDavid du Colombier }
930*0b459c2cSDavid du Colombier 
931*0b459c2cSDavid du Colombier /* We found where to apply it (we hope), so do it. */
932*0b459c2cSDavid du Colombier 
933*0b459c2cSDavid du Colombier static bool
apply_hunk(outstate,where)934*0b459c2cSDavid du Colombier apply_hunk (outstate, where)
935*0b459c2cSDavid du Colombier      struct outstate *outstate;
936*0b459c2cSDavid du Colombier      LINENUM where;
937*0b459c2cSDavid du Colombier {
938*0b459c2cSDavid du Colombier     register LINENUM old = 1;
939*0b459c2cSDavid du Colombier     register LINENUM lastline = pch_ptrn_lines ();
940*0b459c2cSDavid du Colombier     register LINENUM new = lastline+1;
941*0b459c2cSDavid du Colombier     register enum {OUTSIDE, IN_IFNDEF, IN_IFDEF, IN_ELSE} def_state = OUTSIDE;
942*0b459c2cSDavid du Colombier     register char const *R_do_defines = do_defines;
943*0b459c2cSDavid du Colombier     register LINENUM pat_end = pch_end ();
944*0b459c2cSDavid du Colombier     register FILE *fp = outstate->ofp;
945*0b459c2cSDavid du Colombier 
946*0b459c2cSDavid du Colombier     where--;
947*0b459c2cSDavid du Colombier     while (pch_char(new) == '=' || pch_char(new) == '\n')
948*0b459c2cSDavid du Colombier 	new++;
949*0b459c2cSDavid du Colombier 
950*0b459c2cSDavid du Colombier     while (old <= lastline) {
951*0b459c2cSDavid du Colombier 	if (pch_char(old) == '-') {
952*0b459c2cSDavid du Colombier 	    assert (outstate->after_newline);
953*0b459c2cSDavid du Colombier 	    if (! copy_till (outstate, where + old - 1))
954*0b459c2cSDavid du Colombier 		return FALSE;
955*0b459c2cSDavid du Colombier 	    if (R_do_defines) {
956*0b459c2cSDavid du Colombier 		if (def_state == OUTSIDE) {
957*0b459c2cSDavid du Colombier 		    fprintf (fp, outstate->after_newline + if_defined,
958*0b459c2cSDavid du Colombier 			     R_do_defines);
959*0b459c2cSDavid du Colombier 		    def_state = IN_IFNDEF;
960*0b459c2cSDavid du Colombier 		}
961*0b459c2cSDavid du Colombier 		else if (def_state == IN_IFDEF) {
962*0b459c2cSDavid du Colombier 		    fprintf (fp, outstate->after_newline + else_defined);
963*0b459c2cSDavid du Colombier 		    def_state = IN_ELSE;
964*0b459c2cSDavid du Colombier 		}
965*0b459c2cSDavid du Colombier 		if (ferror (fp))
966*0b459c2cSDavid du Colombier 		  write_fatal ();
967*0b459c2cSDavid du Colombier 		outstate->after_newline = pch_write_line (old, fp);
968*0b459c2cSDavid du Colombier 		outstate->zero_output = 0;
969*0b459c2cSDavid du Colombier 	    }
970*0b459c2cSDavid du Colombier 	    last_frozen_line++;
971*0b459c2cSDavid du Colombier 	    old++;
972*0b459c2cSDavid du Colombier 	}
973*0b459c2cSDavid du Colombier 	else if (new > pat_end) {
974*0b459c2cSDavid du Colombier 	    break;
975*0b459c2cSDavid du Colombier 	}
976*0b459c2cSDavid du Colombier 	else if (pch_char(new) == '+') {
977*0b459c2cSDavid du Colombier 	    if (! copy_till (outstate, where + old - 1))
978*0b459c2cSDavid du Colombier 		return FALSE;
979*0b459c2cSDavid du Colombier 	    if (R_do_defines) {
980*0b459c2cSDavid du Colombier 		if (def_state == IN_IFNDEF) {
981*0b459c2cSDavid du Colombier 		    fprintf (fp, outstate->after_newline + else_defined);
982*0b459c2cSDavid du Colombier 		    def_state = IN_ELSE;
983*0b459c2cSDavid du Colombier 		}
984*0b459c2cSDavid du Colombier 		else if (def_state == OUTSIDE) {
985*0b459c2cSDavid du Colombier 		    fprintf (fp, outstate->after_newline + if_defined,
986*0b459c2cSDavid du Colombier 			     R_do_defines);
987*0b459c2cSDavid du Colombier 		    def_state = IN_IFDEF;
988*0b459c2cSDavid du Colombier 		}
989*0b459c2cSDavid du Colombier 		if (ferror (fp))
990*0b459c2cSDavid du Colombier 		  write_fatal ();
991*0b459c2cSDavid du Colombier 	    }
992*0b459c2cSDavid du Colombier 	    outstate->after_newline = pch_write_line (new, fp);
993*0b459c2cSDavid du Colombier 	    outstate->zero_output = 0;
994*0b459c2cSDavid du Colombier 	    new++;
995*0b459c2cSDavid du Colombier 	}
996*0b459c2cSDavid du Colombier 	else if (pch_char(new) != pch_char(old)) {
997*0b459c2cSDavid du Colombier 	    if (debug & 1)
998*0b459c2cSDavid du Colombier 	      say ("oldchar = '%c', newchar = '%c'\n",
999*0b459c2cSDavid du Colombier 		   pch_char (old), pch_char (new));
1000*0b459c2cSDavid du Colombier 	    fatal ("Out-of-sync patch, lines %ld,%ld -- mangled text or line numbers, maybe?",
1001*0b459c2cSDavid du Colombier 		pch_hunk_beg() + old,
1002*0b459c2cSDavid du Colombier 		pch_hunk_beg() + new);
1003*0b459c2cSDavid du Colombier 	}
1004*0b459c2cSDavid du Colombier 	else if (pch_char(new) == '!') {
1005*0b459c2cSDavid du Colombier 	    assert (outstate->after_newline);
1006*0b459c2cSDavid du Colombier 	    if (! copy_till (outstate, where + old - 1))
1007*0b459c2cSDavid du Colombier 		return FALSE;
1008*0b459c2cSDavid du Colombier 	    assert (outstate->after_newline);
1009*0b459c2cSDavid du Colombier 	    if (R_do_defines) {
1010*0b459c2cSDavid du Colombier 	       fprintf (fp, not_defined, R_do_defines);
1011*0b459c2cSDavid du Colombier 	       if (ferror (fp))
1012*0b459c2cSDavid du Colombier 		write_fatal ();
1013*0b459c2cSDavid du Colombier 	       def_state = IN_IFNDEF;
1014*0b459c2cSDavid du Colombier 	    }
1015*0b459c2cSDavid du Colombier 
1016*0b459c2cSDavid du Colombier 	    do
1017*0b459c2cSDavid du Colombier 	      {
1018*0b459c2cSDavid du Colombier 		if (R_do_defines) {
1019*0b459c2cSDavid du Colombier 		    outstate->after_newline = pch_write_line (old, fp);
1020*0b459c2cSDavid du Colombier 		}
1021*0b459c2cSDavid du Colombier 		last_frozen_line++;
1022*0b459c2cSDavid du Colombier 		old++;
1023*0b459c2cSDavid du Colombier 	      }
1024*0b459c2cSDavid du Colombier 	    while (pch_char (old) == '!');
1025*0b459c2cSDavid du Colombier 
1026*0b459c2cSDavid du Colombier 	    if (R_do_defines) {
1027*0b459c2cSDavid du Colombier 		fprintf (fp, outstate->after_newline + else_defined);
1028*0b459c2cSDavid du Colombier 		if (ferror (fp))
1029*0b459c2cSDavid du Colombier 		  write_fatal ();
1030*0b459c2cSDavid du Colombier 		def_state = IN_ELSE;
1031*0b459c2cSDavid du Colombier 	    }
1032*0b459c2cSDavid du Colombier 
1033*0b459c2cSDavid du Colombier 	    do
1034*0b459c2cSDavid du Colombier 	      {
1035*0b459c2cSDavid du Colombier 		outstate->after_newline = pch_write_line (new, fp);
1036*0b459c2cSDavid du Colombier 		new++;
1037*0b459c2cSDavid du Colombier 	      }
1038*0b459c2cSDavid du Colombier 	    while (pch_char (new) == '!');
1039*0b459c2cSDavid du Colombier 	    outstate->zero_output = 0;
1040*0b459c2cSDavid du Colombier 	}
1041*0b459c2cSDavid du Colombier 	else {
1042*0b459c2cSDavid du Colombier 	    assert(pch_char(new) == ' ');
1043*0b459c2cSDavid du Colombier 	    old++;
1044*0b459c2cSDavid du Colombier 	    new++;
1045*0b459c2cSDavid du Colombier 	    if (R_do_defines && def_state != OUTSIDE) {
1046*0b459c2cSDavid du Colombier 		fprintf (fp, outstate->after_newline + end_defined,
1047*0b459c2cSDavid du Colombier 			 R_do_defines);
1048*0b459c2cSDavid du Colombier 		if (ferror (fp))
1049*0b459c2cSDavid du Colombier 		  write_fatal ();
1050*0b459c2cSDavid du Colombier 		outstate->after_newline = 1;
1051*0b459c2cSDavid du Colombier 		def_state = OUTSIDE;
1052*0b459c2cSDavid du Colombier 	    }
1053*0b459c2cSDavid du Colombier 	}
1054*0b459c2cSDavid du Colombier     }
1055*0b459c2cSDavid du Colombier     if (new <= pat_end && pch_char(new) == '+') {
1056*0b459c2cSDavid du Colombier 	if (! copy_till (outstate, where + old - 1))
1057*0b459c2cSDavid du Colombier 	    return FALSE;
1058*0b459c2cSDavid du Colombier 	if (R_do_defines) {
1059*0b459c2cSDavid du Colombier 	    if (def_state == OUTSIDE) {
1060*0b459c2cSDavid du Colombier 		fprintf (fp, outstate->after_newline + if_defined,
1061*0b459c2cSDavid du Colombier 			 R_do_defines);
1062*0b459c2cSDavid du Colombier 		def_state = IN_IFDEF;
1063*0b459c2cSDavid du Colombier 	    }
1064*0b459c2cSDavid du Colombier 	    else if (def_state == IN_IFNDEF) {
1065*0b459c2cSDavid du Colombier 		fprintf (fp, outstate->after_newline + else_defined);
1066*0b459c2cSDavid du Colombier 		def_state = IN_ELSE;
1067*0b459c2cSDavid du Colombier 	    }
1068*0b459c2cSDavid du Colombier 	    if (ferror (fp))
1069*0b459c2cSDavid du Colombier 	      write_fatal ();
1070*0b459c2cSDavid du Colombier 	    outstate->zero_output = 0;
1071*0b459c2cSDavid du Colombier 	}
1072*0b459c2cSDavid du Colombier 
1073*0b459c2cSDavid du Colombier 	do
1074*0b459c2cSDavid du Colombier 	  {
1075*0b459c2cSDavid du Colombier 	    if (! outstate->after_newline  &&  putc ('\n', fp) == EOF)
1076*0b459c2cSDavid du Colombier 	      write_fatal ();
1077*0b459c2cSDavid du Colombier 	    outstate->after_newline = pch_write_line (new, fp);
1078*0b459c2cSDavid du Colombier 	    outstate->zero_output = 0;
1079*0b459c2cSDavid du Colombier 	    new++;
1080*0b459c2cSDavid du Colombier 	  }
1081*0b459c2cSDavid du Colombier 	while (new <= pat_end && pch_char (new) == '+');
1082*0b459c2cSDavid du Colombier     }
1083*0b459c2cSDavid du Colombier     if (R_do_defines && def_state != OUTSIDE) {
1084*0b459c2cSDavid du Colombier 	fprintf (fp, outstate->after_newline + end_defined, R_do_defines);
1085*0b459c2cSDavid du Colombier 	if (ferror (fp))
1086*0b459c2cSDavid du Colombier 	  write_fatal ();
1087*0b459c2cSDavid du Colombier 	outstate->after_newline = 1;
1088*0b459c2cSDavid du Colombier     }
1089*0b459c2cSDavid du Colombier     return TRUE;
1090*0b459c2cSDavid du Colombier }
1091*0b459c2cSDavid du Colombier 
1092*0b459c2cSDavid du Colombier /* Create an output file.  */
1093*0b459c2cSDavid du Colombier 
1094*0b459c2cSDavid du Colombier static FILE *
create_output_file(name)1095*0b459c2cSDavid du Colombier create_output_file (name)
1096*0b459c2cSDavid du Colombier      char const *name;
1097*0b459c2cSDavid du Colombier {
1098*0b459c2cSDavid du Colombier   int fd = create_file (name, O_WRONLY | binary_transput, instat.st_mode);
1099*0b459c2cSDavid du Colombier   FILE *f = fdopen (fd, binary_transput ? "wb" : "w");
1100*0b459c2cSDavid du Colombier   if (! f)
1101*0b459c2cSDavid du Colombier     pfatal ("can't create `%s'", name);
1102*0b459c2cSDavid du Colombier   return f;
1103*0b459c2cSDavid du Colombier }
1104*0b459c2cSDavid du Colombier 
1105*0b459c2cSDavid du Colombier /* Open the new file. */
1106*0b459c2cSDavid du Colombier 
1107*0b459c2cSDavid du Colombier static void
init_output(name,outstate)1108*0b459c2cSDavid du Colombier init_output (name, outstate)
1109*0b459c2cSDavid du Colombier      char const *name;
1110*0b459c2cSDavid du Colombier      struct outstate *outstate;
1111*0b459c2cSDavid du Colombier {
1112*0b459c2cSDavid du Colombier   outstate->ofp = name ? create_output_file (name) : (FILE *) 0;
1113*0b459c2cSDavid du Colombier   outstate->after_newline = 1;
1114*0b459c2cSDavid du Colombier   outstate->zero_output = 1;
1115*0b459c2cSDavid du Colombier }
1116*0b459c2cSDavid du Colombier 
1117*0b459c2cSDavid du Colombier /* Open a file to put hunks we can't locate. */
1118*0b459c2cSDavid du Colombier 
1119*0b459c2cSDavid du Colombier static void
init_reject(name)1120*0b459c2cSDavid du Colombier init_reject(name)
1121*0b459c2cSDavid du Colombier      char const *name;
1122*0b459c2cSDavid du Colombier {
1123*0b459c2cSDavid du Colombier   rejfp = create_output_file (name);
1124*0b459c2cSDavid du Colombier }
1125*0b459c2cSDavid du Colombier 
1126*0b459c2cSDavid du Colombier /* Copy input file to output, up to wherever hunk is to be applied. */
1127*0b459c2cSDavid du Colombier 
1128*0b459c2cSDavid du Colombier static bool
copy_till(outstate,lastline)1129*0b459c2cSDavid du Colombier copy_till (outstate, lastline)
1130*0b459c2cSDavid du Colombier      register struct outstate *outstate;
1131*0b459c2cSDavid du Colombier      register LINENUM lastline;
1132*0b459c2cSDavid du Colombier {
1133*0b459c2cSDavid du Colombier     register LINENUM R_last_frozen_line = last_frozen_line;
1134*0b459c2cSDavid du Colombier     register FILE *fp = outstate->ofp;
1135*0b459c2cSDavid du Colombier     register char const *s;
1136*0b459c2cSDavid du Colombier     size_t size;
1137*0b459c2cSDavid du Colombier 
1138*0b459c2cSDavid du Colombier     if (R_last_frozen_line > lastline)
1139*0b459c2cSDavid du Colombier       {
1140*0b459c2cSDavid du Colombier 	say ("misordered hunks! output would be garbled\n");
1141*0b459c2cSDavid du Colombier 	return FALSE;
1142*0b459c2cSDavid du Colombier       }
1143*0b459c2cSDavid du Colombier     while (R_last_frozen_line < lastline)
1144*0b459c2cSDavid du Colombier       {
1145*0b459c2cSDavid du Colombier 	s = ifetch (++R_last_frozen_line, 0, &size);
1146*0b459c2cSDavid du Colombier 	if (size)
1147*0b459c2cSDavid du Colombier 	  {
1148*0b459c2cSDavid du Colombier 	    if ((! outstate->after_newline  &&  putc ('\n', fp) == EOF)
1149*0b459c2cSDavid du Colombier 		|| ! fwrite (s, sizeof *s, size, fp))
1150*0b459c2cSDavid du Colombier 	      write_fatal ();
1151*0b459c2cSDavid du Colombier 	    outstate->after_newline = s[size - 1] == '\n';
1152*0b459c2cSDavid du Colombier 	    outstate->zero_output = 0;
1153*0b459c2cSDavid du Colombier 	  }
1154*0b459c2cSDavid du Colombier       }
1155*0b459c2cSDavid du Colombier     last_frozen_line = R_last_frozen_line;
1156*0b459c2cSDavid du Colombier     return TRUE;
1157*0b459c2cSDavid du Colombier }
1158*0b459c2cSDavid du Colombier 
1159*0b459c2cSDavid du Colombier /* Finish copying the input file to the output file. */
1160*0b459c2cSDavid du Colombier 
1161*0b459c2cSDavid du Colombier static bool
spew_output(outstate)1162*0b459c2cSDavid du Colombier spew_output (outstate)
1163*0b459c2cSDavid du Colombier      struct outstate *outstate;
1164*0b459c2cSDavid du Colombier {
1165*0b459c2cSDavid du Colombier     if (debug & 256)
1166*0b459c2cSDavid du Colombier       say ("il=%ld lfl=%ld\n", input_lines, last_frozen_line);
1167*0b459c2cSDavid du Colombier 
1168*0b459c2cSDavid du Colombier     if (last_frozen_line < input_lines)
1169*0b459c2cSDavid du Colombier       if (! copy_till (outstate, input_lines))
1170*0b459c2cSDavid du Colombier 	return FALSE;
1171*0b459c2cSDavid du Colombier 
1172*0b459c2cSDavid du Colombier     if (outstate->ofp && ! outfile)
1173*0b459c2cSDavid du Colombier       {
1174*0b459c2cSDavid du Colombier 	if (fclose (outstate->ofp) != 0)
1175*0b459c2cSDavid du Colombier 	  write_fatal ();
1176*0b459c2cSDavid du Colombier 	outstate->ofp = 0;
1177*0b459c2cSDavid du Colombier       }
1178*0b459c2cSDavid du Colombier 
1179*0b459c2cSDavid du Colombier     return TRUE;
1180*0b459c2cSDavid du Colombier }
1181*0b459c2cSDavid du Colombier 
1182*0b459c2cSDavid du Colombier /* Does the patch pattern match at line base+offset? */
1183*0b459c2cSDavid du Colombier 
1184*0b459c2cSDavid du Colombier static bool
patch_match(base,offset,prefix_fuzz,suffix_fuzz)1185*0b459c2cSDavid du Colombier patch_match (base, offset, prefix_fuzz, suffix_fuzz)
1186*0b459c2cSDavid du Colombier LINENUM base;
1187*0b459c2cSDavid du Colombier LINENUM offset;
1188*0b459c2cSDavid du Colombier LINENUM prefix_fuzz;
1189*0b459c2cSDavid du Colombier LINENUM suffix_fuzz;
1190*0b459c2cSDavid du Colombier {
1191*0b459c2cSDavid du Colombier     register LINENUM pline = 1 + prefix_fuzz;
1192*0b459c2cSDavid du Colombier     register LINENUM iline;
1193*0b459c2cSDavid du Colombier     register LINENUM pat_lines = pch_ptrn_lines () - suffix_fuzz;
1194*0b459c2cSDavid du Colombier     size_t size;
1195*0b459c2cSDavid du Colombier     register char const *p;
1196*0b459c2cSDavid du Colombier 
1197*0b459c2cSDavid du Colombier     for (iline=base+offset+prefix_fuzz; pline <= pat_lines; pline++,iline++) {
1198*0b459c2cSDavid du Colombier 	p = ifetch (iline, offset >= 0, &size);
1199*0b459c2cSDavid du Colombier 	if (canonicalize) {
1200*0b459c2cSDavid du Colombier 	    if (!similar(p, size,
1201*0b459c2cSDavid du Colombier 			 pfetch(pline),
1202*0b459c2cSDavid du Colombier 			 pch_line_len(pline) ))
1203*0b459c2cSDavid du Colombier 		return FALSE;
1204*0b459c2cSDavid du Colombier 	}
1205*0b459c2cSDavid du Colombier 	else if (size != pch_line_len (pline)
1206*0b459c2cSDavid du Colombier 		 || memcmp (p, pfetch (pline), size) != 0)
1207*0b459c2cSDavid du Colombier 	    return FALSE;
1208*0b459c2cSDavid du Colombier     }
1209*0b459c2cSDavid du Colombier     return TRUE;
1210*0b459c2cSDavid du Colombier }
1211*0b459c2cSDavid du Colombier 
1212*0b459c2cSDavid du Colombier /* Do two lines match with canonicalized white space? */
1213*0b459c2cSDavid du Colombier 
1214*0b459c2cSDavid du Colombier static bool
similar(a,alen,b,blen)1215*0b459c2cSDavid du Colombier similar (a, alen, b, blen)
1216*0b459c2cSDavid du Colombier      register char const *a;
1217*0b459c2cSDavid du Colombier      register size_t alen;
1218*0b459c2cSDavid du Colombier      register char const *b;
1219*0b459c2cSDavid du Colombier      register size_t blen;
1220*0b459c2cSDavid du Colombier {
1221*0b459c2cSDavid du Colombier   /* Ignore presence or absence of trailing newlines.  */
1222*0b459c2cSDavid du Colombier   alen  -=  alen && a[alen - 1] == '\n';
1223*0b459c2cSDavid du Colombier   blen  -=  blen && b[blen - 1] == '\n';
1224*0b459c2cSDavid du Colombier 
1225*0b459c2cSDavid du Colombier   for (;;)
1226*0b459c2cSDavid du Colombier     {
1227*0b459c2cSDavid du Colombier       if (!blen || (*b == ' ' || *b == '\t'))
1228*0b459c2cSDavid du Colombier 	{
1229*0b459c2cSDavid du Colombier 	  while (blen && (*b == ' ' || *b == '\t'))
1230*0b459c2cSDavid du Colombier 	    b++, blen--;
1231*0b459c2cSDavid du Colombier 	  if (alen)
1232*0b459c2cSDavid du Colombier 	    {
1233*0b459c2cSDavid du Colombier 	      if (!(*a == ' ' || *a == '\t'))
1234*0b459c2cSDavid du Colombier 		return FALSE;
1235*0b459c2cSDavid du Colombier 	      do a++, alen--;
1236*0b459c2cSDavid du Colombier 	      while (alen && (*a == ' ' || *a == '\t'));
1237*0b459c2cSDavid du Colombier 	    }
1238*0b459c2cSDavid du Colombier 	  if (!alen || !blen)
1239*0b459c2cSDavid du Colombier 	    return alen == blen;
1240*0b459c2cSDavid du Colombier 	}
1241*0b459c2cSDavid du Colombier       else if (!alen || *a++ != *b++)
1242*0b459c2cSDavid du Colombier 	return FALSE;
1243*0b459c2cSDavid du Colombier       else
1244*0b459c2cSDavid du Colombier 	alen--, blen--;
1245*0b459c2cSDavid du Colombier     }
1246*0b459c2cSDavid du Colombier }
1247*0b459c2cSDavid du Colombier 
1248*0b459c2cSDavid du Colombier /* Make a temporary file.  */
1249*0b459c2cSDavid du Colombier 
1250*0b459c2cSDavid du Colombier #if HAVE_MKTEMP
1251*0b459c2cSDavid du Colombier char *mktemp PARAMS ((char *));
1252*0b459c2cSDavid du Colombier #endif
1253*0b459c2cSDavid du Colombier 
1254*0b459c2cSDavid du Colombier #ifndef TMPDIR
1255*0b459c2cSDavid du Colombier #define TMPDIR "/tmp"
1256*0b459c2cSDavid du Colombier #endif
1257*0b459c2cSDavid du Colombier 
1258*0b459c2cSDavid du Colombier static char const *
make_temp(letter)1259*0b459c2cSDavid du Colombier make_temp (letter)
1260*0b459c2cSDavid du Colombier      int letter;
1261*0b459c2cSDavid du Colombier {
1262*0b459c2cSDavid du Colombier   char *r;
1263*0b459c2cSDavid du Colombier #if HAVE_MKTEMP
1264*0b459c2cSDavid du Colombier   char const *tmpdir = getenv ("TMPDIR");	/* Unix tradition */
1265*0b459c2cSDavid du Colombier   if (!tmpdir) tmpdir = getenv ("TMP");		/* DOS tradition */
1266*0b459c2cSDavid du Colombier   if (!tmpdir) tmpdir = getenv ("TEMP");	/* another DOS tradition */
1267*0b459c2cSDavid du Colombier   if (!tmpdir) tmpdir = TMPDIR;
1268*0b459c2cSDavid du Colombier   r = xmalloc (strlen (tmpdir) + 10);
1269*0b459c2cSDavid du Colombier   sprintf (r, "%s/p%cXXXXXX", tmpdir, letter);
1270*0b459c2cSDavid du Colombier   mktemp (r);
1271*0b459c2cSDavid du Colombier   if (!*r)
1272*0b459c2cSDavid du Colombier     pfatal ("mktemp");
1273*0b459c2cSDavid du Colombier #else
1274*0b459c2cSDavid du Colombier   r = xmalloc (L_tmpnam);
1275*0b459c2cSDavid du Colombier   if (! (tmpnam (r) == r && *r))
1276*0b459c2cSDavid du Colombier     pfatal ("tmpnam");
1277*0b459c2cSDavid du Colombier #endif
1278*0b459c2cSDavid du Colombier   return r;
1279*0b459c2cSDavid du Colombier }
1280*0b459c2cSDavid du Colombier 
1281*0b459c2cSDavid du Colombier /* Fatal exit with cleanup. */
1282*0b459c2cSDavid du Colombier 
1283*0b459c2cSDavid du Colombier void
fatal_exit(sig)1284*0b459c2cSDavid du Colombier fatal_exit (sig)
1285*0b459c2cSDavid du Colombier      int sig;
1286*0b459c2cSDavid du Colombier {
1287*0b459c2cSDavid du Colombier   cleanup ();
1288*0b459c2cSDavid du Colombier 
1289*0b459c2cSDavid du Colombier   if (sig)
1290*0b459c2cSDavid du Colombier     exit_with_signal (sig);
1291*0b459c2cSDavid du Colombier 
1292*0b459c2cSDavid du Colombier   exit (2);
1293*0b459c2cSDavid du Colombier }
1294*0b459c2cSDavid du Colombier 
1295*0b459c2cSDavid du Colombier static void
cleanup()1296*0b459c2cSDavid du Colombier cleanup ()
1297*0b459c2cSDavid du Colombier {
1298*0b459c2cSDavid du Colombier   unlink (TMPINNAME);
1299*0b459c2cSDavid du Colombier   unlink (TMPOUTNAME);
1300*0b459c2cSDavid du Colombier   unlink (TMPPATNAME);
1301*0b459c2cSDavid du Colombier   unlink (TMPREJNAME);
1302*0b459c2cSDavid du Colombier }
1303