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