xref: /csrg-svn/usr.bin/patch/patch.c (revision 26490)
124294Smckusick #ifndef lint
2*26490Sbloom static char sccsid[] = "@(#)patch.c	5.6 (Berkeley) 03/07/86";
324294Smckusick #endif not lint
424294Smckusick 
524294Smckusick /* patch - a program to apply diffs to original files
624294Smckusick  *
724294Smckusick  * $Header: patch.c,v 1.3 85/03/26 15:07:43 lwall Exp $
824294Smckusick  *
924294Smckusick  * Copyright 1984, Larry Wall
1024294Smckusick  *
1124294Smckusick  * This program may be copied as long as you don't try to make any
1224294Smckusick  * money off of it, or pretend that you wrote it.
1324294Smckusick  *
1424294Smckusick  * $Log:	patch.c,v $
1524295Smckusick  * 85/08/15 van%ucbmonet@berkeley
1624295Smckusick  * Changes for 4.3bsd diff -c.
1724295Smckusick  *
1824294Smckusick  * Revision 1.3  85/03/26  15:07:43  lwall
1924294Smckusick  * Frozen.
2024294Smckusick  *
2124294Smckusick  * Revision 1.2.1.9  85/03/12  17:03:35  lwall
2224294Smckusick  * Changed pfp->_file to fileno(pfp).
2324294Smckusick  *
2424294Smckusick  * Revision 1.2.1.8  85/03/12  16:30:43  lwall
2524294Smckusick  * Check i_ptr and i_womp to make sure they aren't null before freeing.
2624294Smckusick  * Also allow ed output to be suppressed.
2724294Smckusick  *
2824294Smckusick  * Revision 1.2.1.7  85/03/12  15:56:13  lwall
2924294Smckusick  * Added -p option from jromine@uci-750a.
3024294Smckusick  *
3124294Smckusick  * Revision 1.2.1.6  85/03/12  12:12:51  lwall
3224294Smckusick  * Now checks for normalness of file to patch.
3324294Smckusick  *
3424294Smckusick  * Revision 1.2.1.5  85/03/12  11:52:12  lwall
3524294Smckusick  * Added -D (#ifdef) option from joe@fluke.
3624294Smckusick  *
3724294Smckusick  * Revision 1.2.1.4  84/12/06  11:14:15  lwall
3824294Smckusick  * Made smarter about SCCS subdirectories.
3924294Smckusick  *
4024294Smckusick  * Revision 1.2.1.3  84/12/05  11:18:43  lwall
4124294Smckusick  * Added -l switch to do loose string comparison.
4224294Smckusick  *
4324294Smckusick  * Revision 1.2.1.2  84/12/04  09:47:13  lwall
4424294Smckusick  * Failed hunk count not reset on multiple patch file.
4524294Smckusick  *
4624294Smckusick  * Revision 1.2.1.1  84/12/04  09:42:37  lwall
4724294Smckusick  * Branch for sdcrdcf changes.
4824294Smckusick  *
4924294Smckusick  * Revision 1.2  84/11/29  13:29:51  lwall
5024294Smckusick  * Linted.  Identifiers uniqified.  Fixed i_ptr malloc() bug.  Fixed
5124294Smckusick  * multiple calls to mktemp().  Will now work on machines that can only
5224294Smckusick  * read 32767 chars.  Added -R option for diffs with new and old swapped.
5324294Smckusick  * Various cosmetic changes.
5424294Smckusick  *
5524294Smckusick  * Revision 1.1  84/11/09  17:03:58  lwall
5624294Smckusick  * Initial revision
5724294Smckusick  *
5824294Smckusick  */
5924294Smckusick 
6024294Smckusick #define DEBUGGING
6124294Smckusick 
6224294Smckusick /* shut lint up about the following when return value ignored */
6324294Smckusick 
6424294Smckusick #define Signal (void)signal
6524294Smckusick #define Unlink (void)unlink
6624294Smckusick #define Lseek (void)lseek
6724294Smckusick #define Fseek (void)fseek
6824294Smckusick #define Fstat (void)fstat
6924294Smckusick #define Pclose (void)pclose
7024294Smckusick #define Close (void)close
7124294Smckusick #define Fclose (void)fclose
7224294Smckusick #define Fflush (void)fflush
7324294Smckusick #define Sprintf (void)sprintf
7424294Smckusick #define Mktemp (void)mktemp
7524294Smckusick #define Strcpy (void)strcpy
7624294Smckusick #define Strcat (void)strcat
7724294Smckusick 
7824294Smckusick #include <stdio.h>
7924294Smckusick #include <assert.h>
8024294Smckusick #include <sys/types.h>
8124294Smckusick #include <sys/stat.h>
8224294Smckusick #include <ctype.h>
8324294Smckusick #include <signal.h>
8424294Smckusick 
8524294Smckusick /* constants */
8624294Smckusick 
8724294Smckusick #define TRUE (1)
8824294Smckusick #define FALSE (0)
8924294Smckusick 
9024294Smckusick #define MAXHUNKSIZE 500
9124294Smckusick #define MAXLINELEN 1024
9224294Smckusick #define BUFFERSIZE 1024
9324294Smckusick #define ORIGEXT ".orig"
9424294Smckusick #define SCCSPREFIX "s."
9524294Smckusick #define GET "get -e %s"
9624294Smckusick #define RCSSUFFIX ",v"
9724294Smckusick #define CHECKOUT "co -l %s"
9824294Smckusick 
9924294Smckusick /* handy definitions */
10024294Smckusick 
10124294Smckusick #define Null(t) ((t)0)
10224294Smckusick #define Nullch Null(char *)
10324294Smckusick #define Nullfp Null(FILE *)
10424294Smckusick 
10524294Smckusick #define Ctl(ch) (ch & 037)
10624294Smckusick 
10724294Smckusick #define strNE(s1,s2) (strcmp(s1,s2))
10824294Smckusick #define strEQ(s1,s2) (!strcmp(s1,s2))
10924294Smckusick #define strnNE(s1,s2,l) (strncmp(s1,s2,l))
11024294Smckusick #define strnEQ(s1,s2,l) (!strncmp(s1,s2,l))
11124294Smckusick 
11224294Smckusick /* typedefs */
11324294Smckusick 
11424294Smckusick typedef char bool;
11524294Smckusick typedef long LINENUM;			/* must be signed */
11624294Smckusick typedef unsigned MEM;			/* what to feed malloc */
11724294Smckusick 
11824294Smckusick /* globals */
11924294Smckusick 
12024294Smckusick int Argc;				/* guess */
12124294Smckusick char **Argv;
12224294Smckusick 
12324294Smckusick struct stat filestat;			/* file statistics area */
12424294Smckusick 
12524294Smckusick char serrbuf[BUFSIZ];			/* buffer for stderr */
12624294Smckusick char buf[MAXLINELEN];			/* general purpose buffer */
12724294Smckusick FILE *pfp = Nullfp;			/* patch file pointer */
12824294Smckusick FILE *ofp = Nullfp;			/* output file pointer */
12924294Smckusick FILE *rejfp = Nullfp;			/* reject file pointer */
13024294Smckusick 
13124294Smckusick LINENUM input_lines = 0;		/* how long is input file in lines */
13224294Smckusick LINENUM last_frozen_line = 0;		/* how many input lines have been */
13324294Smckusick 					/* irretractibly output */
13424294Smckusick 
13524294Smckusick #define MAXFILEC 2
13624294Smckusick int filec = 0;				/* how many file arguments? */
13724294Smckusick char *filearg[MAXFILEC];
13824294Smckusick 
13924294Smckusick char *outname = Nullch;
14024294Smckusick char rejname[128];
14124294Smckusick 
14224294Smckusick char *origext = Nullch;
14324294Smckusick 
14424294Smckusick char TMPOUTNAME[] = "/tmp/patchoXXXXXX";
14524294Smckusick char TMPINNAME[] = "/tmp/patchiXXXXXX";	/* you might want /usr/tmp here */
14624294Smckusick char TMPREJNAME[] = "/tmp/patchrXXXXXX";
14724294Smckusick char TMPPATNAME[] = "/tmp/patchpXXXXXX";
14824294Smckusick 
14924294Smckusick LINENUM last_offset = 0;
15024294Smckusick #ifdef DEBUGGING
15124294Smckusick int debug = 0;
15224294Smckusick #endif
15324294Smckusick bool verbose = TRUE;
15424294Smckusick bool reverse = FALSE;
155*26490Sbloom bool noreverse = FALSE;
156*26490Sbloom bool skip_this_patch = FALSE;
15724294Smckusick bool usepath = FALSE;
15824294Smckusick bool canonicalize = FALSE;
15924294Smckusick 
16024294Smckusick #define CONTEXT_DIFF 1
16124294Smckusick #define NORMAL_DIFF 2
16224294Smckusick #define ED_DIFF 3
16324295Smckusick #define NEW_CONTEXT_DIFF 4
16424294Smckusick int diff_type = 0;
16524294Smckusick 
16624294Smckusick int do_defines = 0;			/* patch using ifdef, ifndef, etc. */
16724294Smckusick char if_defined[128];			/* #ifdef xyzzy */
16824294Smckusick char not_defined[128];			/* #ifndef xyzzy */
16924294Smckusick char else_defined[] = "#else\n";	/* #else */
17024294Smckusick char end_defined[128];			/* #endif xyzzy */
17124294Smckusick 
17224294Smckusick char *revision = Nullch;		/* prerequisite revision, if any */
17324294Smckusick 
17424294Smckusick /* procedures */
17524294Smckusick 
17624294Smckusick LINENUM locate_hunk();
17724294Smckusick bool patch_match();
17824294Smckusick bool similar();
17924294Smckusick char *malloc();
18024294Smckusick char *savestr();
18124294Smckusick char *strcpy();
18224294Smckusick char *strcat();
18324294Smckusick char *sprintf();		/* usually */
18424294Smckusick int my_exit();
18524294Smckusick bool rev_in_string();
18624294Smckusick char *fetchname();
18724294Smckusick long atol();
18824294Smckusick long lseek();
18924294Smckusick char *mktemp();
19024294Smckusick 
19124294Smckusick /* patch type */
19224294Smckusick 
19324294Smckusick bool there_is_another_patch();
19424294Smckusick bool another_hunk();
19524294Smckusick char *pfetch();
19624294Smckusick int pch_line_len();
19724294Smckusick LINENUM pch_first();
19824294Smckusick LINENUM pch_ptrn_lines();
19924294Smckusick LINENUM pch_newfirst();
20024294Smckusick LINENUM pch_repl_lines();
20124294Smckusick LINENUM pch_end();
20224294Smckusick LINENUM pch_context();
20324294Smckusick LINENUM pch_hunk_beg();
20424294Smckusick char pch_char();
20524294Smckusick char *pfetch();
20624294Smckusick char *pgets();
20724294Smckusick 
20824294Smckusick /* input file type */
20924294Smckusick 
21024294Smckusick char *ifetch();
21124294Smckusick 
21224294Smckusick /* apply a context patch to a named file */
21324294Smckusick 
21424294Smckusick main(argc,argv)
21524294Smckusick int argc;
21624294Smckusick char **argv;
21724294Smckusick {
21824294Smckusick     LINENUM where;
21924294Smckusick     int hunk = 0;
22024294Smckusick     int failed = 0;
22124294Smckusick     int i;
22224294Smckusick 
22324294Smckusick     setbuf(stderr,serrbuf);
22424294Smckusick     for (i = 0; i<MAXFILEC; i++)
22524294Smckusick 	filearg[i] = Nullch;
22624294Smckusick     Mktemp(TMPOUTNAME);
22724294Smckusick     Mktemp(TMPINNAME);
22824294Smckusick     Mktemp(TMPREJNAME);
22924294Smckusick     Mktemp(TMPPATNAME);
23024294Smckusick 
23124294Smckusick     /* parse switches */
23224294Smckusick     Argc = argc;
23324294Smckusick     Argv = argv;
23424294Smckusick     get_some_switches();
23524294Smckusick 
23624294Smckusick     /* make sure we clean up /tmp in case of disaster */
23724294Smckusick     set_signals();
23824294Smckusick 
23924294Smckusick     for (
24024294Smckusick 	open_patch_file(filearg[1]);
24124294Smckusick 	there_is_another_patch();
24224294Smckusick 	reinitialize_almost_everything()
24324294Smckusick     ) {					/* for each patch in patch file */
24424294Smckusick 
24524294Smckusick 	if (outname == Nullch)
24624294Smckusick 	    outname = savestr(filearg[0]);
24724294Smckusick 
24824294Smckusick 	/* initialize the patched file */
24924294Smckusick 	init_output(TMPOUTNAME);
25024294Smckusick 
25124294Smckusick 	/* for ed script just up and do it and exit */
25224294Smckusick 	if (diff_type == ED_DIFF) {
25324294Smckusick 	    do_ed_script();
25424294Smckusick 	    continue;
25524294Smckusick 	}
25624294Smckusick 
25724294Smckusick 	/* initialize reject file */
25824294Smckusick 	init_reject(TMPREJNAME);
25924294Smckusick 
26024294Smckusick 	/* find out where all the lines are */
26124294Smckusick 	scan_input(filearg[0]);
26224294Smckusick 
26324294Smckusick 	/* from here on, open no standard i/o files, because malloc */
26424294Smckusick 	/* might misfire */
26524294Smckusick 
26624294Smckusick 	/* apply each hunk of patch */
26724294Smckusick 	hunk = 0;
26824294Smckusick 	failed = 0;
26924294Smckusick 	while (another_hunk()) {
27024294Smckusick 	    hunk++;
27124294Smckusick 	    where = locate_hunk();
27224294Smckusick 	    if (hunk == 1 && where == Null(LINENUM)) {
27324294Smckusick 					/* dwim for reversed patch? */
27424294Smckusick 		pch_swap();
27524294Smckusick 		reverse = !reverse;
27624294Smckusick 		where = locate_hunk();	/* try again */
27724294Smckusick 		if (where == Null(LINENUM)) {
27824294Smckusick 		    pch_swap();		/* no, put it back to normal */
27924294Smckusick 		    reverse = !reverse;
280*26490Sbloom 		} else if (noreverse) {
281*26490Sbloom 		    pch_swap();		/* put it back to normal */
282*26490Sbloom 		    reverse = !reverse;
283*26490Sbloom 		    say("Ignoring previously applied (or reversed) patch.\n");
284*26490Sbloom 		    skip_this_patch = TRUE;
28524294Smckusick 		}
28624294Smckusick 		else {
28724294Smckusick 		    say("%seversed (or previously applied) patch detected!  %s -R.\n",
28824294Smckusick 			reverse ? "R" : "Unr",
28924294Smckusick 			reverse ? "Assuming" : "Ignoring");
29024294Smckusick 		}
29124294Smckusick 	    }
292*26490Sbloom 	    if (where == Null(LINENUM) || skip_this_patch) {
29324294Smckusick 		abort_hunk();
29424294Smckusick 		failed++;
29524294Smckusick 		if (verbose)
29624294Smckusick 		    say("Hunk #%d failed.\n",hunk);
29724294Smckusick 	    }
29824294Smckusick 	    else {
29924294Smckusick 		apply_hunk(where);
30024294Smckusick 		if (verbose)
30124294Smckusick 		    if (last_offset)
30224294Smckusick 			say("Hunk #%d succeeded (offset %d line%s).\n",
30324294Smckusick 			  hunk,last_offset,last_offset==1?"":"s");
30424294Smckusick 		    else
30524294Smckusick 			say("Hunk #%d succeeded.\n", hunk);
30624294Smckusick 	    }
30724294Smckusick 	}
30824294Smckusick 
30924294Smckusick 	assert(hunk);
31024294Smckusick 
31124294Smckusick 	/* finish spewing out the new file */
31224294Smckusick 	spew_output();
31324294Smckusick 
31424294Smckusick 	/* and put the output where desired */
31524294Smckusick 	ignore_signals();
31624294Smckusick 	move_file(TMPOUTNAME,outname);
31724294Smckusick 	Fclose(rejfp);
31824294Smckusick 	rejfp = Nullfp;
31924294Smckusick 	if (failed) {
32024294Smckusick 	    if (!*rejname) {
32124294Smckusick 		Strcpy(rejname, outname);
32224294Smckusick 		Strcat(rejname, ".rej");
32324294Smckusick 	    }
32424294Smckusick 	    say("%d out of %d hunks failed--saving rejects to %s\n",
32524294Smckusick 		failed, hunk, rejname);
32624294Smckusick 	    move_file(TMPREJNAME,rejname);
32724294Smckusick 	}
32824294Smckusick 	set_signals();
32924294Smckusick     }
33024294Smckusick     my_exit(0);
33124294Smckusick }
33224294Smckusick 
33324294Smckusick reinitialize_almost_everything()
33424294Smckusick {
33524294Smckusick     re_patch();
33624294Smckusick     re_input();
33724294Smckusick 
33824294Smckusick     input_lines = 0;
33924294Smckusick     last_frozen_line = 0;
34024294Smckusick 
34124294Smckusick     filec = 0;
34224294Smckusick     if (filearg[0] != Nullch) {
34324294Smckusick 	free(filearg[0]);
34424294Smckusick 	filearg[0] = Nullch;
34524294Smckusick     }
34624294Smckusick 
34724294Smckusick     if (outname != Nullch) {
34824294Smckusick 	free(outname);
34924294Smckusick 	outname = Nullch;
35024294Smckusick     }
35124294Smckusick 
35224294Smckusick     last_offset = 0;
35324294Smckusick 
35424294Smckusick     diff_type = 0;
35524294Smckusick 
35624294Smckusick     if (revision != Nullch) {
35724294Smckusick 	free(revision);
35824294Smckusick 	revision = Nullch;
35924294Smckusick     }
36024294Smckusick 
36124294Smckusick     reverse = FALSE;
362*26490Sbloom     skip_this_patch = FALSE;
36324294Smckusick 
36424294Smckusick     get_some_switches();
36524294Smckusick 
36624294Smckusick     if (filec >= 2)
36724294Smckusick 	fatal("You may not change to a different patch file.\n");
36824294Smckusick }
36924294Smckusick 
37024294Smckusick get_some_switches()
37124294Smckusick {
37224294Smckusick     register char *s;
37324294Smckusick 
37424294Smckusick     rejname[0] = '\0';
37524294Smckusick     if (!Argc)
37624294Smckusick 	return;
37724294Smckusick     for (Argc--,Argv++; Argc; Argc--,Argv++) {
37824294Smckusick 	s = Argv[0];
37924294Smckusick 	if (strEQ(s,"+")) {
38024294Smckusick 	    return;			/* + will be skipped by for loop */
38124294Smckusick 	}
38224294Smckusick 	if (*s != '-' || !s[1]) {
38324294Smckusick 	    if (filec == MAXFILEC)
38424294Smckusick 		fatal("Too many file arguments.\n");
38524294Smckusick 	    filearg[filec++] = savestr(s);
38624294Smckusick 	}
38724294Smckusick 	else {
38824294Smckusick 	    switch (*++s) {
38924294Smckusick 	    case 'b':
39024294Smckusick 		origext = savestr(Argv[1]);
39124294Smckusick 		Argc--,Argv++;
39224294Smckusick 		break;
39324294Smckusick 	    case 'c':
39424294Smckusick 		diff_type = CONTEXT_DIFF;
39524294Smckusick 		break;
39624294Smckusick 	    case 'd':
39724294Smckusick 		if (chdir(Argv[1]) < 0)
39824294Smckusick 		    fatal("Can't cd to %s.\n",Argv[1]);
39924294Smckusick 		Argc--,Argv++;
40024294Smckusick 		break;
40124294Smckusick 	    case 'D':
40224294Smckusick 	    	do_defines++;
40324294Smckusick 		Sprintf(if_defined, "#ifdef %s\n", Argv[1]);
40424294Smckusick 		Sprintf(not_defined, "#ifndef %s\n", Argv[1]);
40524294Smckusick 		Sprintf(end_defined, "#endif %s\n", Argv[1]);
40624294Smckusick 		Argc--,Argv++;
40724294Smckusick 		break;
40824294Smckusick 	    case 'e':
40924294Smckusick 		diff_type = ED_DIFF;
41024294Smckusick 		break;
41124294Smckusick 	    case 'l':
41224294Smckusick 		canonicalize = TRUE;
41324294Smckusick 		break;
41424294Smckusick 	    case 'n':
41524294Smckusick 		diff_type = NORMAL_DIFF;
41624294Smckusick 		break;
41724294Smckusick 	    case 'o':
41824294Smckusick 		outname = savestr(Argv[1]);
41924294Smckusick 		Argc--,Argv++;
42024294Smckusick 		break;
42124294Smckusick 	    case 'p':
42224294Smckusick 		usepath = TRUE;	/* do not strip path names */
42324294Smckusick 		break;
42424294Smckusick 	    case 'r':
42524294Smckusick 		Strcpy(rejname,Argv[1]);
42624294Smckusick 		Argc--,Argv++;
42724294Smckusick 		break;
42824294Smckusick 	    case 'R':
42924294Smckusick 		reverse = TRUE;
43024294Smckusick 		break;
431*26490Sbloom 	    case 'N':
432*26490Sbloom 		noreverse = TRUE;
433*26490Sbloom 		break;
43424294Smckusick 	    case 's':
43524294Smckusick 		verbose = FALSE;
43624294Smckusick 		break;
43724294Smckusick #ifdef DEBUGGING
43824294Smckusick 	    case 'x':
43924294Smckusick 		debug = atoi(s+1);
44024294Smckusick 		break;
44124294Smckusick #endif
44224294Smckusick 	    default:
44324294Smckusick 		fatal("Unrecognized switch: %s\n",Argv[0]);
44424294Smckusick 	    }
44524294Smckusick 	}
44624294Smckusick     }
44724294Smckusick }
44824294Smckusick 
44924294Smckusick LINENUM
45024294Smckusick locate_hunk()
45124294Smckusick {
45224294Smckusick     register LINENUM first_guess = pch_first() + last_offset;
45324294Smckusick     register LINENUM offset;
45424294Smckusick     LINENUM pat_lines = pch_ptrn_lines();
45524294Smckusick     register LINENUM max_pos_offset = input_lines - first_guess
45624294Smckusick 				- pat_lines + 1;
45724294Smckusick     register LINENUM max_neg_offset = first_guess - last_frozen_line - 1
45824294Smckusick 				- pch_context();
45924294Smckusick 
46024294Smckusick     if (!pat_lines)			/* null range matches always */
46124294Smckusick 	return first_guess;
46224294Smckusick     if (max_neg_offset >= first_guess)	/* do not try lines < 0 */
46324294Smckusick 	max_neg_offset = first_guess - 1;
46424294Smckusick     if (first_guess <= input_lines && patch_match(first_guess,(LINENUM)0))
46524294Smckusick 	return first_guess;
46624294Smckusick     for (offset = 1; ; offset++) {
46724294Smckusick 	bool check_after = (offset <= max_pos_offset);
46824294Smckusick 	bool check_before = (offset <= max_pos_offset);
46924294Smckusick 
47024294Smckusick 	if (check_after && patch_match(first_guess,offset)) {
47124294Smckusick #ifdef DEBUGGING
47224294Smckusick 	    if (debug & 1)
47324294Smckusick 		printf("Offset changing from %d to %d\n",last_offset,offset);
47424294Smckusick #endif
47524294Smckusick 	    last_offset = offset;
47624294Smckusick 	    return first_guess+offset;
47724294Smckusick 	}
47824294Smckusick 	else if (check_before && patch_match(first_guess,-offset)) {
47924294Smckusick #ifdef DEBUGGING
48024294Smckusick 	    if (debug & 1)
48124294Smckusick 		printf("Offset changing from %d to %d\n",last_offset,-offset);
48224294Smckusick #endif
48324294Smckusick 	    last_offset = -offset;
48424294Smckusick 	    return first_guess-offset;
48524294Smckusick 	}
48624294Smckusick 	else if (!check_before && !check_after)
48724294Smckusick 	    return Null(LINENUM);
48824294Smckusick     }
48924294Smckusick }
49024294Smckusick 
49124294Smckusick /* we did not find the pattern, dump out the hunk so they can handle it */
49224294Smckusick 
49324294Smckusick abort_hunk()
49424294Smckusick {
49524294Smckusick     register LINENUM i;
49624294Smckusick     register LINENUM pat_end = pch_end();
49724294Smckusick     /* add in last_offset to guess the same as the previous successful hunk */
49824294Smckusick     int oldfirst = pch_first() + last_offset;
49924294Smckusick     int newfirst = pch_newfirst() + last_offset;
50024294Smckusick     int oldlast = oldfirst + pch_ptrn_lines() - 1;
50124294Smckusick     int newlast = newfirst + pch_repl_lines() - 1;
50224294Smckusick 
50324294Smckusick     fprintf(rejfp,"***************\n");
50424294Smckusick     for (i=0; i<=pat_end; i++) {
50524294Smckusick 	switch (pch_char(i)) {
50624294Smckusick 	case '*':
50724294Smckusick 	    fprintf(rejfp,"*** %d,%d\n", oldfirst, oldlast);
50824294Smckusick 	    break;
50924294Smckusick 	case '=':
51024294Smckusick 	    fprintf(rejfp,"--- %d,%d -----\n", newfirst, newlast);
51124294Smckusick 	    break;
51224294Smckusick 	case '\n':
51324294Smckusick 	    fprintf(rejfp,"%s", pfetch(i));
51424294Smckusick 	    break;
51524294Smckusick 	case ' ': case '-': case '+': case '!':
51624294Smckusick 	    fprintf(rejfp,"%c %s", pch_char(i), pfetch(i));
51724294Smckusick 	    break;
51824294Smckusick 	default:
51924294Smckusick 	    say("Fatal internal error in abort_hunk().\n");
52024294Smckusick 	    abort();
52124294Smckusick 	}
52224294Smckusick     }
52324294Smckusick }
52424294Smckusick 
52524294Smckusick /* we found where to apply it (we hope), so do it */
52624294Smckusick 
52724294Smckusick apply_hunk(where)
52824294Smckusick LINENUM where;
52924294Smckusick {
53024294Smckusick     register LINENUM old = 1;
53124294Smckusick     register LINENUM lastline = pch_ptrn_lines();
53224294Smckusick     register LINENUM new = lastline+1;
53324294Smckusick     register int def_state = 0;	/* -1 = ifndef, 1 = ifdef */
53424294Smckusick 
53524294Smckusick     where--;
53624294Smckusick     while (pch_char(new) == '=' || pch_char(new) == '\n')
53724294Smckusick 	new++;
53824294Smckusick 
53924294Smckusick     while (old <= lastline) {
54024294Smckusick 	if (pch_char(old) == '-') {
54124294Smckusick 	    copy_till(where + old - 1);
54224294Smckusick 	    if (do_defines) {
54324294Smckusick 		if (def_state == 0) {
54424294Smckusick 		    fputs(not_defined, ofp);
54524294Smckusick 		    def_state = -1;
54624294Smckusick 		} else
54724294Smckusick 		if (def_state == 1) {
54824294Smckusick 		    fputs(else_defined, ofp);
54924294Smckusick 		    def_state = 2;
55024294Smckusick 		}
55124294Smckusick 		fputs(pfetch(old), ofp);
55224294Smckusick 	    }
55324294Smckusick 	    last_frozen_line++;
55424294Smckusick 	    old++;
55524294Smckusick 	}
55624294Smckusick 	else if (pch_char(new) == '+') {
55724294Smckusick 	    copy_till(where + old - 1);
55824294Smckusick 	    if (do_defines) {
55924294Smckusick 		if (def_state == -1) {
56024294Smckusick 		    fputs(else_defined, ofp);
56124294Smckusick 		    def_state = 2;
56224294Smckusick 		} else
56324294Smckusick 		if (def_state == 0) {
56424294Smckusick 		    fputs(if_defined, ofp);
56524294Smckusick 		    def_state = 1;
56624294Smckusick 		}
56724294Smckusick 	    }
56824294Smckusick 	    fputs(pfetch(new),ofp);
56924294Smckusick 	    new++;
57024294Smckusick 	}
57124294Smckusick 	else {
57224294Smckusick 	    if (pch_char(new) != pch_char(old)) {
57324294Smckusick 		say("Out-of-sync patch, lines %d,%d\n",
57424294Smckusick 		    pch_hunk_beg() + old - 1,
57524294Smckusick 		    pch_hunk_beg() + new - 1);
57624294Smckusick #ifdef DEBUGGING
57724294Smckusick 		printf("oldchar = '%c', newchar = '%c'\n",
57824294Smckusick 		    pch_char(old), pch_char(new));
57924294Smckusick #endif
58024294Smckusick 		my_exit(1);
58124294Smckusick 	    }
58224294Smckusick 	    if (pch_char(new) == '!') {
58324294Smckusick 		copy_till(where + old - 1);
58424294Smckusick 		if (do_defines) {
58524294Smckusick 		   fputs(not_defined,ofp);
58624294Smckusick 		   def_state = -1;
58724294Smckusick 		}
58824294Smckusick 		while (pch_char(old) == '!') {
58924294Smckusick 		    if (do_defines) {
59024294Smckusick 			fputs(pfetch(old),ofp);
59124294Smckusick 		    }
59224294Smckusick 		    last_frozen_line++;
59324294Smckusick 		    old++;
59424294Smckusick 		}
59524294Smckusick 		if (do_defines) {
59624294Smckusick 		    fputs(else_defined, ofp);
59724294Smckusick 		    def_state = 2;
59824294Smckusick 		}
59924294Smckusick 		while (pch_char(new) == '!') {
60024294Smckusick 		    fputs(pfetch(new),ofp);
60124294Smckusick 		    new++;
60224294Smckusick 		}
60324294Smckusick 		if (do_defines) {
60424294Smckusick 		    fputs(end_defined, ofp);
60524294Smckusick 		    def_state = 0;
60624294Smckusick 		}
60724294Smckusick 	    }
60824294Smckusick 	    else {
60924294Smckusick 		assert(pch_char(new) == ' ');
61024294Smckusick 		old++;
61124294Smckusick 		new++;
61224294Smckusick 	    }
61324294Smckusick 	}
61424294Smckusick     }
61524294Smckusick     if (new <= pch_end() && pch_char(new) == '+') {
61624294Smckusick 	copy_till(where + old - 1);
61724294Smckusick 	if (do_defines) {
61824294Smckusick 	    if (def_state == 0) {
61924294Smckusick 	    	fputs(if_defined, ofp);
62024294Smckusick 		def_state = 1;
62124294Smckusick 	    } else
62224294Smckusick 	    if (def_state == -1) {
62324294Smckusick 		fputs(else_defined, ofp);
62424294Smckusick 		def_state = 2;
62524294Smckusick 	    }
62624294Smckusick 	}
62724294Smckusick 	while (new <= pch_end() && pch_char(new) == '+') {
62824294Smckusick 	    fputs(pfetch(new),ofp);
62924294Smckusick 	    new++;
63024294Smckusick 	}
63124294Smckusick     }
63224294Smckusick     if (do_defines && def_state) {
63324294Smckusick 	fputs(end_defined, ofp);
63424294Smckusick     }
63524294Smckusick }
63624294Smckusick 
63724294Smckusick do_ed_script()
63824294Smckusick {
63924294Smckusick     FILE *pipefp, *popen();
64024294Smckusick     bool this_line_is_command = FALSE;
64124294Smckusick     register char *t;
64224294Smckusick     long beginning_of_this_line;
64324294Smckusick 
64424294Smckusick     Unlink(TMPOUTNAME);
64524294Smckusick     copy_file(filearg[0],TMPOUTNAME);
64624294Smckusick     if (verbose)
64724294Smckusick 	Sprintf(buf,"/bin/ed %s",TMPOUTNAME);
64824294Smckusick     else
64924294Smckusick 	Sprintf(buf,"/bin/ed - %s",TMPOUTNAME);
65024294Smckusick     pipefp = popen(buf,"w");
65124294Smckusick     for (;;) {
65224294Smckusick 	beginning_of_this_line = ftell(pfp);
65324294Smckusick 	if (pgets(buf,sizeof buf,pfp) == Nullch) {
65424294Smckusick 	    next_intuit_at(beginning_of_this_line);
65524294Smckusick 	    break;
65624294Smckusick 	}
65724294Smckusick 	for (t=buf; isdigit(*t) || *t == ','; t++) ;
65824294Smckusick 	this_line_is_command = (isdigit(*buf) &&
65924294Smckusick 	  (*t == 'd' || *t == 'c' || *t == 'a') );
66024294Smckusick 	if (this_line_is_command) {
66124294Smckusick 	    fputs(buf,pipefp);
66224294Smckusick 	    if (*t != 'd') {
66324294Smckusick 		while (pgets(buf,sizeof buf,pfp) != Nullch) {
66424294Smckusick 		    fputs(buf,pipefp);
66524294Smckusick 		    if (strEQ(buf,".\n"))
66624294Smckusick 			break;
66724294Smckusick 		}
66824294Smckusick 	    }
66924294Smckusick 	}
67024294Smckusick 	else {
67124294Smckusick 	    next_intuit_at(beginning_of_this_line);
67224294Smckusick 	    break;
67324294Smckusick 	}
67424294Smckusick     }
67524294Smckusick     fprintf(pipefp,"w\n");
67624294Smckusick     fprintf(pipefp,"q\n");
67724294Smckusick     Fflush(pipefp);
67824294Smckusick     Pclose(pipefp);
67924294Smckusick     ignore_signals();
68024294Smckusick     move_file(TMPOUTNAME,outname);
68124294Smckusick     set_signals();
68224294Smckusick }
68324294Smckusick 
68424294Smckusick init_output(name)
68524294Smckusick char *name;
68624294Smckusick {
68724294Smckusick     ofp = fopen(name,"w");
68824294Smckusick     if (ofp == Nullfp)
68924294Smckusick 	fatal("patch: can't create %s.\n",name);
69024294Smckusick }
69124294Smckusick 
69224294Smckusick init_reject(name)
69324294Smckusick char *name;
69424294Smckusick {
69524294Smckusick     rejfp = fopen(name,"w");
69624294Smckusick     if (rejfp == Nullfp)
69724294Smckusick 	fatal("patch: can't create %s.\n",name);
69824294Smckusick }
69924294Smckusick 
70024294Smckusick move_file(from,to)
70124294Smckusick char *from, *to;
70224294Smckusick {
70324294Smckusick     char bakname[512];
70424294Smckusick     register char *s;
70524294Smckusick     int fromfd;
70624294Smckusick     register int i;
70724294Smckusick 
70824294Smckusick     /* to stdout? */
70924294Smckusick 
71024294Smckusick     if (strEQ(to,"-")) {
71124294Smckusick #ifdef DEBUGGING
71224294Smckusick 	if (debug & 4)
71324294Smckusick 	    say("Moving %s to stdout.\n",from);
71424294Smckusick #endif
71524294Smckusick 	fromfd = open(from,0);
71624294Smckusick 	if (fromfd < 0)
71724294Smckusick 	    fatal("patch: internal error, can't reopen %s\n",from);
71824294Smckusick 	while ((i=read(fromfd,buf,sizeof buf)) > 0)
71924294Smckusick 	    if (write(1,buf,i) != 1)
72024294Smckusick 		fatal("patch: write failed\n");
72124294Smckusick 	Close(fromfd);
72224294Smckusick 	return;
72324294Smckusick     }
72424294Smckusick 
72524294Smckusick     Strcpy(bakname,to);
72624294Smckusick     Strcat(bakname,origext?origext:ORIGEXT);
72724294Smckusick     if (stat(to,&filestat) >= 0) {	/* output file exists */
72824294Smckusick 	dev_t to_device = filestat.st_dev;
72924294Smckusick 	ino_t to_inode  = filestat.st_ino;
73024294Smckusick 	char *simplename = bakname;
73124294Smckusick 
73224294Smckusick 	for (s=bakname; *s; s++) {
73324294Smckusick 	    if (*s == '/')
73424294Smckusick 		simplename = s+1;
73524294Smckusick 	}
73624294Smckusick 	/* find a backup name that is not the same file */
73724294Smckusick 	while (stat(bakname,&filestat) >= 0 &&
73824294Smckusick 		to_device == filestat.st_dev && to_inode == filestat.st_ino) {
73924294Smckusick 	    for (s=simplename; *s && !islower(*s); s++) ;
74024294Smckusick 	    if (*s)
74124294Smckusick 		*s = toupper(*s);
74224294Smckusick 	    else
74324294Smckusick 		Strcpy(simplename, simplename+1);
74424294Smckusick 	}
74524294Smckusick 	while (unlink(bakname) >= 0) ;	/* while() is for benefit of Eunice */
74624294Smckusick #ifdef DEBUGGING
74724294Smckusick 	if (debug & 4)
74824294Smckusick 	    say("Moving %s to %s.\n",to,bakname);
74924294Smckusick #endif
75024294Smckusick 	if (link(to,bakname) < 0) {
75124294Smckusick 	    say("patch: can't backup %s, output is in %s\n",
75224294Smckusick 		to,from);
75324294Smckusick 	    return;
75424294Smckusick 	}
75524294Smckusick 	while (unlink(to) >= 0) ;
75624294Smckusick     }
75724294Smckusick #ifdef DEBUGGING
75824294Smckusick     if (debug & 4)
75924294Smckusick 	say("Moving %s to %s.\n",from,to);
76024294Smckusick #endif
76124294Smckusick     if (link(from,to) < 0) {		/* different file system? */
76224294Smckusick 	int tofd;
76324294Smckusick 
76424294Smckusick 	tofd = creat(to,0666);
76524294Smckusick 	if (tofd < 0) {
76624294Smckusick 	    say("patch: can't create %s, output is in %s.\n",
76724294Smckusick 	      to, from);
76824294Smckusick 	    return;
76924294Smckusick 	}
77024294Smckusick 	fromfd = open(from,0);
77124294Smckusick 	if (fromfd < 0)
77224294Smckusick 	    fatal("patch: internal error, can't reopen %s\n",from);
77324294Smckusick 	while ((i=read(fromfd,buf,sizeof buf)) > 0)
77424294Smckusick 	    if (write(tofd,buf,i) != i)
77524294Smckusick 		fatal("patch: write failed\n");
77624294Smckusick 	Close(fromfd);
77724294Smckusick 	Close(tofd);
77824294Smckusick     }
77924294Smckusick     Unlink(from);
78024294Smckusick }
78124294Smckusick 
78224294Smckusick copy_file(from,to)
78324294Smckusick char *from, *to;
78424294Smckusick {
78524294Smckusick     int tofd;
78624294Smckusick     int fromfd;
78724294Smckusick     register int i;
78824294Smckusick 
78924294Smckusick     tofd = creat(to,0666);
79024294Smckusick     if (tofd < 0)
79124294Smckusick 	fatal("patch: can't create %s.\n", to);
79224294Smckusick     fromfd = open(from,0);
79324294Smckusick     if (fromfd < 0)
79424294Smckusick 	fatal("patch: internal error, can't reopen %s\n",from);
79524294Smckusick     while ((i=read(fromfd,buf,sizeof buf)) > 0)
79624294Smckusick 	if (write(tofd,buf,i) != i)
79724294Smckusick 	    fatal("patch: write (%s) failed\n", to);
79824294Smckusick     Close(fromfd);
79924294Smckusick     Close(tofd);
80024294Smckusick }
80124294Smckusick 
80224294Smckusick copy_till(lastline)
80324294Smckusick register LINENUM lastline;
80424294Smckusick {
80524294Smckusick     if (last_frozen_line > lastline)
80624294Smckusick 	say("patch: misordered hunks! output will be garbled.\n");
80724294Smckusick     while (last_frozen_line < lastline) {
80824294Smckusick 	dump_line(++last_frozen_line);
80924294Smckusick     }
81024294Smckusick }
81124294Smckusick 
81224294Smckusick spew_output()
81324294Smckusick {
81424294Smckusick     copy_till(input_lines);		/* dump remainder of file */
81524294Smckusick     Fclose(ofp);
81624294Smckusick     ofp = Nullfp;
81724294Smckusick }
81824294Smckusick 
81924294Smckusick dump_line(line)
82024294Smckusick LINENUM line;
82124294Smckusick {
82224294Smckusick     register char *s;
82324294Smckusick 
82424294Smckusick     for (s=ifetch(line,0); putc(*s,ofp) != '\n'; s++) ;
82524294Smckusick }
82624294Smckusick 
82724294Smckusick /* does the patch pattern match at line base+offset? */
82824294Smckusick 
82924294Smckusick bool
83024294Smckusick patch_match(base,offset)
83124294Smckusick LINENUM base;
83224294Smckusick LINENUM offset;
83324294Smckusick {
83424294Smckusick     register LINENUM pline;
83524294Smckusick     register LINENUM iline;
83624294Smckusick     register LINENUM pat_lines = pch_ptrn_lines();
83724294Smckusick 
83824294Smckusick     for (pline = 1, iline=base+offset; pline <= pat_lines; pline++,iline++) {
83924294Smckusick 	if (canonicalize) {
84024294Smckusick 	    if (!similar(ifetch(iline,(offset >= 0)),
84124294Smckusick 			 pfetch(pline),
84224294Smckusick 			 pch_line_len(pline) ))
84324294Smckusick 		return FALSE;
84424294Smckusick 	}
84524294Smckusick 	else if (strnNE(ifetch(iline,(offset >= 0)),
84624294Smckusick 		   pfetch(pline),
84724294Smckusick 		   pch_line_len(pline) ))
84824294Smckusick 	    return FALSE;
84924294Smckusick     }
85024294Smckusick     return TRUE;
85124294Smckusick }
85224294Smckusick 
85324294Smckusick /* match two lines with canonicalized white space */
85424294Smckusick 
85524294Smckusick bool
85624294Smckusick similar(a,b,len)
85724294Smckusick register char *a, *b;
85824294Smckusick register int len;
85924294Smckusick {
86024294Smckusick     while (len) {
86124294Smckusick 	if (isspace(*b)) {		/* whitespace (or \n) to match? */
86224294Smckusick 	    if (!isspace(*a))		/* no corresponding whitespace? */
86324294Smckusick 		return FALSE;
86424294Smckusick 	    while (len && isspace(*b) && *b != '\n')
86524294Smckusick 		b++,len--;		/* skip pattern whitespace */
86624294Smckusick 	    while (isspace(*a) && *a != '\n')
86724294Smckusick 		a++;			/* skip target whitespace */
86824294Smckusick 	    if (*a == '\n' || *b == '\n')
86924294Smckusick 		return (*a == *b);	/* should end in sync */
87024294Smckusick 	}
87124294Smckusick 	else if (*a++ != *b++)		/* match non-whitespace chars */
87224294Smckusick 	    return FALSE;
87324294Smckusick 	else
87424294Smckusick 	    len--;			/* probably not necessary */
87524294Smckusick     }
87624294Smckusick     return TRUE;			/* actually, this is not reached */
87724294Smckusick 					/* since there is always a \n */
87824294Smckusick }
87924294Smckusick 
88024294Smckusick /* input file with indexable lines abstract type */
88124294Smckusick 
88224294Smckusick bool using_plan_a = TRUE;
88324294Smckusick static long i_size;			/* size of the input file */
88424294Smckusick static char *i_womp;			/* plan a buffer for entire file */
88524294Smckusick static char **i_ptr;			/* pointers to lines in i_womp */
88624294Smckusick 
88724294Smckusick static int tifd = -1;			/* plan b virtual string array */
88824294Smckusick static char *tibuf[2];			/* plan b buffers */
88924294Smckusick static LINENUM tiline[2] = {-1,-1};	/* 1st line in each buffer */
89024294Smckusick static LINENUM lines_per_buf;		/* how many lines per buffer */
89124294Smckusick static int tireclen;			/* length of records in tmp file */
89224294Smckusick 
89324294Smckusick re_input()
89424294Smckusick {
89524294Smckusick     if (using_plan_a) {
89624294Smckusick 	i_size = 0;
89724294Smckusick 	/*NOSTRICT*/
89824294Smckusick 	if (i_ptr != Null(char**))
89924294Smckusick 	    free((char *)i_ptr);
90024294Smckusick 	if (i_womp != Nullch)
90124294Smckusick 	    free(i_womp);
90224294Smckusick 	i_womp = Nullch;
90324294Smckusick 	i_ptr = Null(char **);
90424294Smckusick     }
90524294Smckusick     else {
90624294Smckusick 	using_plan_a = TRUE;		/* maybe the next one is smaller */
90724294Smckusick 	Close(tifd);
90824294Smckusick 	tifd = -1;
90924294Smckusick 	free(tibuf[0]);
91024294Smckusick 	free(tibuf[1]);
91124294Smckusick 	tibuf[0] = tibuf[1] = Nullch;
91224294Smckusick 	tiline[0] = tiline[1] = -1;
91324294Smckusick 	tireclen = 0;
91424294Smckusick     }
91524294Smckusick }
91624294Smckusick 
91724294Smckusick scan_input(filename)
91824294Smckusick char *filename;
91924294Smckusick {
92024294Smckusick     bool plan_a();
92124294Smckusick 
92224294Smckusick     if (!plan_a(filename))
92324294Smckusick 	plan_b(filename);
92424294Smckusick }
92524294Smckusick 
92624294Smckusick /* try keeping everything in memory */
92724294Smckusick 
92824294Smckusick bool
92924294Smckusick plan_a(filename)
93024294Smckusick char *filename;
93124294Smckusick {
93224294Smckusick     int ifd;
93324294Smckusick     register char *s;
93424294Smckusick     register LINENUM iline;
93524294Smckusick 
93624294Smckusick     if (stat(filename,&filestat) < 0) {
93724294Smckusick 	Sprintf(buf,"RCS/%s%s",filename,RCSSUFFIX);
93824294Smckusick 	if (stat(buf,&filestat) >= 0 || stat(buf+4,&filestat) >= 0) {
93924294Smckusick 	    Sprintf(buf,CHECKOUT,filename);
94024294Smckusick 	    if (verbose)
94124294Smckusick 		say("Can't find %s--attempting to check it out from RCS.\n",
94224294Smckusick 		    filename);
94324294Smckusick 	    if (system(buf) || stat(filename,&filestat))
94424294Smckusick 		fatal("Can't check out %s.\n",filename);
94524294Smckusick 	}
94624294Smckusick 	else {
94724294Smckusick 	    Sprintf(buf,"SCCS/%s%s",SCCSPREFIX,filename);
94824294Smckusick 	    if (stat(buf,&filestat) >= 0 || stat(buf+5,&filestat) >= 0) {
94924294Smckusick 		Sprintf(buf,GET,filename);
95024294Smckusick 		if (verbose)
95124294Smckusick 		    say("Can't find %s--attempting to get it from SCCS.\n",
95224294Smckusick 			filename);
95324294Smckusick 		if (system(buf) || stat(filename,&filestat))
95424294Smckusick 		    fatal("Can't get %s.\n",filename);
95524294Smckusick 	    }
95624294Smckusick 	    else
95724294Smckusick 		fatal("Can't find %s.\n",filename);
95824294Smckusick 	}
95924294Smckusick     }
96024294Smckusick     if ((filestat.st_mode & S_IFMT) & ~S_IFREG)
96124294Smckusick 	fatal("%s is not a normal file--can't patch.\n",filename);
96224294Smckusick     i_size = filestat.st_size;
96324294Smckusick     /*NOSTRICT*/
96424294Smckusick     i_womp = malloc((MEM)(i_size+2));
96524294Smckusick     if (i_womp == Nullch)
96624294Smckusick 	return FALSE;
96724294Smckusick     if ((ifd = open(filename,0)) < 0)
96824294Smckusick 	fatal("Can't open file %s\n",filename);
96924294Smckusick     /*NOSTRICT*/
97024294Smckusick     if (read(ifd,i_womp,(int)i_size) != i_size) {
97124294Smckusick 	Close(ifd);
97224294Smckusick 	free(i_womp);
97324294Smckusick 	return FALSE;
97424294Smckusick     }
97524294Smckusick     Close(ifd);
97624294Smckusick     if (i_womp[i_size-1] != '\n')
97724294Smckusick 	i_womp[i_size++] = '\n';
97824294Smckusick     i_womp[i_size] = '\0';
97924294Smckusick 
98024294Smckusick     /* count the lines in the buffer so we know how many pointers we need */
98124294Smckusick 
98224294Smckusick     iline = 0;
98324294Smckusick     for (s=i_womp; *s; s++) {
98424294Smckusick 	if (*s == '\n')
98524294Smckusick 	    iline++;
98624294Smckusick     }
98724294Smckusick     /*NOSTRICT*/
98824294Smckusick     i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *)));
98924294Smckusick     if (i_ptr == Null(char **)) {	/* shucks, it was a near thing */
99024294Smckusick 	free((char *)i_womp);
99124294Smckusick 	return FALSE;
99224294Smckusick     }
99324294Smckusick 
99424294Smckusick     /* now scan the buffer and build pointer array */
99524294Smckusick 
99624294Smckusick     iline = 1;
99724294Smckusick     i_ptr[iline] = i_womp;
99824294Smckusick     for (s=i_womp; *s; s++) {
99924294Smckusick 	if (*s == '\n')
100024294Smckusick 	    i_ptr[++iline] = s+1;	/* these are NOT null terminated */
100124294Smckusick     }
100224294Smckusick     input_lines = iline - 1;
100324294Smckusick 
100424294Smckusick     /* now check for revision, if any */
100524294Smckusick 
100624294Smckusick     if (revision != Nullch) {
100724294Smckusick 	if (!rev_in_string(i_womp)) {
100824294Smckusick 	    ask("This file doesn't appear to be the %s version--patch anyway? [n] ",
100924294Smckusick 		revision);
101024294Smckusick 	    if (*buf != 'y')
101124294Smckusick 		fatal("Aborted.\n");
101224294Smckusick 	}
101324294Smckusick 	else if (verbose)
101424294Smckusick 	    say("Good.  This file appears to be the %s version.\n",
101524294Smckusick 		revision);
101624294Smckusick     }
101724294Smckusick     return TRUE;			/* plan a will work */
101824294Smckusick }
101924294Smckusick 
102024294Smckusick /* keep (virtually) nothing in memory */
102124294Smckusick 
102224294Smckusick plan_b(filename)
102324294Smckusick char *filename;
102424294Smckusick {
102524294Smckusick     FILE *ifp;
102624294Smckusick     register int i = 0;
102724294Smckusick     register int maxlen = 1;
102824294Smckusick     bool found_revision = (revision == Nullch);
102924294Smckusick 
103024294Smckusick     using_plan_a = FALSE;
103124294Smckusick     if ((ifp = fopen(filename,"r")) == Nullfp)
103224294Smckusick 	fatal("Can't open file %s\n",filename);
103324294Smckusick     if ((tifd = creat(TMPINNAME,0666)) < 0)
103424294Smckusick 	fatal("Can't open file %s\n",TMPINNAME);
103524294Smckusick     while (fgets(buf,sizeof buf, ifp) != Nullch) {
103624294Smckusick 	if (revision != Nullch && !found_revision && rev_in_string(buf))
103724294Smckusick 	    found_revision = TRUE;
103824294Smckusick 	if ((i = strlen(buf)) > maxlen)
103924294Smckusick 	    maxlen = i;			/* find longest line */
104024294Smckusick     }
104124294Smckusick     if (revision != Nullch) {
104224294Smckusick 	if (!found_revision) {
104324294Smckusick 	    ask("This file doesn't appear to be the %s version--patch anyway? [n] ",
104424294Smckusick 		revision);
104524294Smckusick 	    if (*buf != 'y')
104624294Smckusick 		fatal("Aborted.\n");
104724294Smckusick 	}
104824294Smckusick 	else if (verbose)
104924294Smckusick 	    say("Good.  This file appears to be the %s version.\n",
105024294Smckusick 		revision);
105124294Smckusick     }
105224294Smckusick     Fseek(ifp,0L,0);		/* rewind file */
105324294Smckusick     lines_per_buf = BUFFERSIZE / maxlen;
105424294Smckusick     tireclen = maxlen;
105524294Smckusick     tibuf[0] = malloc((MEM)(BUFFERSIZE + 1));
105624294Smckusick     tibuf[1] = malloc((MEM)(BUFFERSIZE + 1));
105724294Smckusick     if (tibuf[1] == Nullch)
105824294Smckusick 	fatal("Can't seem to get enough memory.\n");
105924294Smckusick     for (i=1; ; i++) {
106024294Smckusick 	if (! (i % lines_per_buf))	/* new block */
106124294Smckusick 	    if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE)
106224294Smckusick 		fatal("patch: can't write temp file.\n");
106324294Smckusick 	if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp)
106424294Smckusick 	  == Nullch) {
106524294Smckusick 	    input_lines = i - 1;
106624294Smckusick 	    if (i % lines_per_buf)
106724294Smckusick 		if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE)
106824294Smckusick 		    fatal("patch: can't write temp file.\n");
106924294Smckusick 	    break;
107024294Smckusick 	}
107124294Smckusick     }
107224294Smckusick     Fclose(ifp);
107324294Smckusick     Close(tifd);
107424294Smckusick     if ((tifd = open(TMPINNAME,0)) < 0) {
107524294Smckusick 	fatal("Can't reopen file %s\n",TMPINNAME);
107624294Smckusick     }
107724294Smckusick }
107824294Smckusick 
107924294Smckusick /* fetch a line from the input file, \n terminated, not necessarily \0 */
108024294Smckusick char *
108124294Smckusick ifetch(line,whichbuf)
108224294Smckusick register LINENUM line;
108324294Smckusick int whichbuf;				/* ignored when file in memory */
108424294Smckusick {
108524294Smckusick     if (line < 1 || line > input_lines)
108624294Smckusick 	return "";
108724294Smckusick     if (using_plan_a)
108824294Smckusick 	return i_ptr[line];
108924294Smckusick     else {
109024294Smckusick 	LINENUM offline = line % lines_per_buf;
109124294Smckusick 	LINENUM baseline = line - offline;
109224294Smckusick 
109324294Smckusick 	if (tiline[0] == baseline)
109424294Smckusick 	    whichbuf = 0;
109524294Smckusick 	else if (tiline[1] == baseline)
109624294Smckusick 	    whichbuf = 1;
109724294Smckusick 	else {
109824294Smckusick 	    tiline[whichbuf] = baseline;
109924294Smckusick 	    Lseek(tifd,(long)baseline / lines_per_buf * BUFFERSIZE,0);
110024294Smckusick 	    if (read(tifd,tibuf[whichbuf],BUFFERSIZE) < 0)
110124294Smckusick 		fatal("Error reading tmp file %s.\n",TMPINNAME);
110224294Smckusick 	}
110324294Smckusick 	return tibuf[whichbuf] + (tireclen*offline);
110424294Smckusick     }
110524294Smckusick }
110624294Smckusick 
110724294Smckusick /* patch abstract type */
110824294Smckusick 
110924294Smckusick static long p_filesize;			/* size of the patch file */
111024294Smckusick static LINENUM p_first;			/* 1st line number */
111124294Smckusick static LINENUM p_newfirst;		/* 1st line number of replacement */
111224294Smckusick static LINENUM p_ptrn_lines;		/* # lines in pattern */
111324294Smckusick static LINENUM p_repl_lines;		/* # lines in replacement text */
111424294Smckusick static LINENUM p_end = -1;		/* last line in hunk */
111524294Smckusick static LINENUM p_max;			/* max allowed value of p_end */
111624294Smckusick static LINENUM p_context = 3;		/* # of context lines */
111724294Smckusick static LINENUM p_input_line = 0;	/* current line # from patch file */
111824294Smckusick static char *p_line[MAXHUNKSIZE];	/* the text of the hunk */
111924294Smckusick static char p_char[MAXHUNKSIZE];	/* +, -, and ! */
112024294Smckusick static int p_len[MAXHUNKSIZE];		/* length of each line */
112124294Smckusick static int p_indent;			/* indent to patch */
112224294Smckusick static long p_base;			/* where to intuit this time */
112324294Smckusick static long p_start;			/* where intuit found a patch */
112424294Smckusick 
112524294Smckusick re_patch()
112624294Smckusick {
112724294Smckusick     p_first = (LINENUM)0;
112824294Smckusick     p_newfirst = (LINENUM)0;
112924294Smckusick     p_ptrn_lines = (LINENUM)0;
113024294Smckusick     p_repl_lines = (LINENUM)0;
113124294Smckusick     p_end = (LINENUM)-1;
113224294Smckusick     p_max = (LINENUM)0;
113324294Smckusick     p_indent = 0;
113424294Smckusick }
113524294Smckusick 
113624294Smckusick open_patch_file(filename)
113724294Smckusick char *filename;
113824294Smckusick {
113924294Smckusick     if (filename == Nullch || !*filename || strEQ(filename,"-")) {
114024294Smckusick 	pfp = fopen(TMPPATNAME,"w");
114124294Smckusick 	if (pfp == Nullfp)
114224294Smckusick 	    fatal("patch: can't create %s.\n",TMPPATNAME);
114324294Smckusick 	while (fgets(buf,sizeof buf,stdin) != NULL)
114424294Smckusick 	    fputs(buf,pfp);
114524294Smckusick 	Fclose(pfp);
114624294Smckusick 	filename = TMPPATNAME;
114724294Smckusick     }
114824294Smckusick     pfp = fopen(filename,"r");
114924294Smckusick     if (pfp == Nullfp)
115024294Smckusick 	fatal("patch file %s not found\n",filename);
115124294Smckusick     Fstat(fileno(pfp), &filestat);
115224294Smckusick     p_filesize = filestat.st_size;
115324294Smckusick     next_intuit_at(0L);			/* start at the beginning */
115424294Smckusick }
115524294Smckusick 
115624294Smckusick bool
115724294Smckusick there_is_another_patch()
115824294Smckusick {
115924294Smckusick     bool no_input_file = (filearg[0] == Nullch);
116024294Smckusick 
116124294Smckusick     if (p_base != 0L && p_base >= p_filesize) {
116224294Smckusick 	if (verbose)
116324294Smckusick 	    say("done\n");
116424294Smckusick 	return FALSE;
116524294Smckusick     }
116624294Smckusick     if (verbose)
116724294Smckusick 	say("Hmm...");
116824294Smckusick     diff_type = intuit_diff_type();
116924294Smckusick     if (!diff_type) {
117024294Smckusick 	if (p_base != 0L) {
117124294Smckusick 	    if (verbose)
117224294Smckusick 		say("  Ignoring the trailing garbage.\ndone\n");
117324294Smckusick 	}
117424294Smckusick 	else
117524294Smckusick 	    say("  I can't seem to find a patch in there anywhere.\n");
117624294Smckusick 	return FALSE;
117724294Smckusick     }
117824294Smckusick     if (verbose)
117924294Smckusick 	say("  %sooks like %s to me...\n",
118024294Smckusick 	    (p_base == 0L ? "L" : "The next patch l"),
118124294Smckusick 	    diff_type == CONTEXT_DIFF ? "a context diff" :
118224295Smckusick 	    diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
118324294Smckusick 	    diff_type == NORMAL_DIFF ? "a normal diff" :
118424294Smckusick 	    "an ed script" );
118524294Smckusick     if (p_indent && verbose)
118624294Smckusick 	say("(Patch is indented %d space%s.)\n",p_indent,p_indent==1?"":"s");
118724294Smckusick     skip_to(p_start);
118824294Smckusick     if (no_input_file) {
1189*26490Sbloom 	while (filearg[0] == Nullch) {
119024294Smckusick 	    ask("File to patch: ");
119124294Smckusick 	    filearg[0] = fetchname(buf);
119224294Smckusick 	}
1193*26490Sbloom 	if (verbose) {
119424294Smckusick 	    say("Patching file %s...\n",filearg[0]);
119524294Smckusick 	}
119624294Smckusick     }
119724294Smckusick     return TRUE;
119824294Smckusick }
119924294Smckusick 
120024294Smckusick intuit_diff_type()
120124294Smckusick {
120224294Smckusick     long this_line = 0;
120324294Smckusick     long previous_line;
120424294Smckusick     long first_command_line = -1;
120524294Smckusick     bool last_line_was_command = FALSE;
120624294Smckusick     bool this_line_is_command = FALSE;
120724295Smckusick     bool last_line_was_stars = FALSE;
120824295Smckusick     bool this_line_is_stars = FALSE;
120924294Smckusick     register int indent;
121024294Smckusick     register char *s, *t;
121124294Smckusick     char *oldname = Nullch;
121224294Smckusick     char *newname = Nullch;
121324294Smckusick     bool no_filearg = (filearg[0] == Nullch);
121424294Smckusick 
121524294Smckusick     Fseek(pfp,p_base,0);
121624294Smckusick     for (;;) {
121724294Smckusick 	previous_line = this_line;
121824294Smckusick 	last_line_was_command = this_line_is_command;
121924295Smckusick 	last_line_was_stars = this_line_is_stars;
122024294Smckusick 	this_line = ftell(pfp);
122124294Smckusick 	indent = 0;
122224294Smckusick 	if (fgets(buf,sizeof buf,pfp) == Nullch) {
122324294Smckusick 	    if (first_command_line >= 0L) {
122424294Smckusick 					/* nothing but deletes!? */
122524294Smckusick 		p_start = first_command_line;
122624294Smckusick 		return ED_DIFF;
122724294Smckusick 	    }
122824294Smckusick 	    else {
122924294Smckusick 		p_start = this_line;
123024294Smckusick 		return 0;
123124294Smckusick 	    }
123224294Smckusick 	}
123324294Smckusick 	for (s = buf; *s == ' ' || *s == '\t'; s++) {
123424294Smckusick 	    if (*s == '\t')
123524294Smckusick 		indent += 8 - (indent % 8);
123624294Smckusick 	    else
123724294Smckusick 		indent++;
123824294Smckusick 	}
123924294Smckusick 	for (t=s; isdigit(*t) || *t == ','; t++) ;
124024294Smckusick 	this_line_is_command = (isdigit(*s) &&
124124294Smckusick 	  (*t == 'd' || *t == 'c' || *t == 'a') );
124224294Smckusick 	if (first_command_line < 0L && this_line_is_command) {
124324294Smckusick 	    first_command_line = this_line;
124424294Smckusick 	    p_indent = indent;		/* assume this for now */
124524294Smckusick 	}
124624294Smckusick 	if (strnEQ(s,"*** ",4))
124724294Smckusick 	    oldname = fetchname(s+4);
124824294Smckusick 	else if (strnEQ(s,"--- ",4)) {
124924294Smckusick 	    newname = fetchname(s+4);
125024294Smckusick 	    if (no_filearg) {
125124294Smckusick 		if (oldname && newname) {
125224294Smckusick 		    if (strlen(oldname) < strlen(newname))
125324294Smckusick 			filearg[0] = oldname;
125424294Smckusick 		    else
125524294Smckusick 			filearg[0] = newname;
125624294Smckusick 		}
125724294Smckusick 		else if (oldname)
125824294Smckusick 		    filearg[0] = oldname;
125924294Smckusick 		else if (newname)
126024294Smckusick 		    filearg[0] = newname;
126124294Smckusick 	    }
126224294Smckusick 	}
126324294Smckusick 	else if (strnEQ(s,"Index:",6)) {
126424294Smckusick 	    if (no_filearg)
126524294Smckusick 		filearg[0] = fetchname(s+6);
126624294Smckusick 					/* this filearg might get limboed */
126724294Smckusick 	}
126824294Smckusick 	else if (strnEQ(s,"Prereq:",7)) {
126924294Smckusick 	    for (t=s+7; isspace(*t); t++) ;
127024294Smckusick 	    revision = savestr(t);
127124294Smckusick 	    for (t=revision; *t && !isspace(*t); t++) ;
127224294Smckusick 	    *t = '\0';
127324294Smckusick 	    if (!*revision) {
127424294Smckusick 		free(revision);
127524294Smckusick 		revision = Nullch;
127624294Smckusick 	    }
127724294Smckusick 	}
127824294Smckusick 	if ((!diff_type || diff_type == ED_DIFF) &&
127924294Smckusick 	  first_command_line >= 0L &&
128024294Smckusick 	  strEQ(s,".\n") ) {
128124294Smckusick 	    p_indent = indent;
128224294Smckusick 	    p_start = first_command_line;
128324294Smckusick 	    return ED_DIFF;
128424294Smckusick 	}
128524295Smckusick 	this_line_is_stars = strnEQ(s,"********",8);
128624295Smckusick 	if ((!diff_type || diff_type == CONTEXT_DIFF) && last_line_was_stars &&
128724295Smckusick 		 strnEQ(s,"*** ",4)) {
128824295Smckusick 	    /* if this is a new context diff the character just before */
128924295Smckusick 	    /* the newline is a '*'. */
129024295Smckusick 	    while (*s != '\n')
129124295Smckusick 		s++;
129224294Smckusick 	    p_indent = indent;
129324295Smckusick 	    p_start = previous_line;
129424295Smckusick 	    return (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
129524294Smckusick 	}
129624294Smckusick 	if ((!diff_type || diff_type == NORMAL_DIFF) &&
129724294Smckusick 	  last_line_was_command &&
129824294Smckusick 	  (strnEQ(s,"< ",2) || strnEQ(s,"> ",2)) ) {
129924294Smckusick 	    p_start = previous_line;
130024294Smckusick 	    p_indent = indent;
130124294Smckusick 	    return NORMAL_DIFF;
130224294Smckusick 	}
130324294Smckusick     }
130424294Smckusick }
130524294Smckusick 
130624294Smckusick char *
130724294Smckusick fetchname(at)
130824294Smckusick char *at;
130924294Smckusick {
131024294Smckusick     char *s = savestr(at);
131124294Smckusick     char *name;
131224294Smckusick     register char *t;
131324294Smckusick     char tmpbuf[200];
131424294Smckusick 
131524294Smckusick     for (t=s; isspace(*t); t++) ;
131624294Smckusick     name = t;
131724294Smckusick     for (; *t && !isspace(*t); t++)
131824294Smckusick 	if (!usepath)
131924294Smckusick 	    if (*t == '/')
132024294Smckusick 		name = t+1;
132124294Smckusick     *t = '\0';
132224294Smckusick     name = savestr(name);
132324294Smckusick     Sprintf(tmpbuf,"RCS/%s",name);
132424294Smckusick     free(s);
132524294Smckusick     if (stat(name,&filestat) < 0) {
132624294Smckusick 	Strcat(tmpbuf,RCSSUFFIX);
132724294Smckusick 	if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+4,&filestat) < 0) {
132824294Smckusick 	    Sprintf(tmpbuf,"SCCS/%s%s",SCCSPREFIX,name);
132924294Smckusick 	    if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+5,&filestat) < 0) {
133024294Smckusick 		free(name);
133124294Smckusick 		name = Nullch;
133224294Smckusick 	    }
133324294Smckusick 	}
133424294Smckusick     }
133524294Smckusick     return name;
133624294Smckusick }
133724294Smckusick 
133824294Smckusick next_intuit_at(file_pos)
133924294Smckusick long file_pos;
134024294Smckusick {
134124294Smckusick     p_base = file_pos;
134224294Smckusick }
134324294Smckusick 
134424294Smckusick skip_to(file_pos)
134524294Smckusick long file_pos;
134624294Smckusick {
134724294Smckusick     char *ret;
134824294Smckusick 
134924294Smckusick     assert(p_base <= file_pos);
135024294Smckusick     if (verbose && p_base < file_pos) {
135124294Smckusick 	Fseek(pfp,p_base,0);
135224294Smckusick 	say("The text leading up to this was:\n--------------------------\n");
135324294Smckusick 	while (ftell(pfp) < file_pos) {
135424294Smckusick 	    ret = fgets(buf,sizeof buf,pfp);
135524294Smckusick 	    assert(ret != Nullch);
135624294Smckusick 	    say("|%s",buf);
135724294Smckusick 	}
135824294Smckusick 	say("--------------------------\n");
135924294Smckusick     }
136024294Smckusick     else
136124294Smckusick 	Fseek(pfp,file_pos,0);
136224294Smckusick }
136324294Smckusick 
136424294Smckusick bool
136524294Smckusick another_hunk()
136624294Smckusick {
136724294Smckusick     register char *s;
136824294Smckusick     char *ret;
136924295Smckusick     register int context = 0;
137024294Smckusick 
137124294Smckusick     while (p_end >= 0) {
137224294Smckusick 	free(p_line[p_end--]);
137324294Smckusick     }
137424294Smckusick     assert(p_end == -1);
137524294Smckusick 
137624294Smckusick     p_max = MAXHUNKSIZE;		/* gets reduced when --- found */
137724294Smckusick     if (diff_type == CONTEXT_DIFF) {
137824294Smckusick 	long line_beginning = ftell(pfp);
137924294Smckusick 	LINENUM repl_beginning = 0;
138024294Smckusick 
138124294Smckusick 	ret = pgets(buf,sizeof buf, pfp);
138224294Smckusick 	if (ret == Nullch || strnNE(buf,"********",8)) {
138324294Smckusick 	    next_intuit_at(line_beginning);
138424294Smckusick 	    return FALSE;
138524294Smckusick 	}
138624294Smckusick 	p_context = 100;
138724294Smckusick 	while (p_end < p_max) {
138824294Smckusick 	    ret = pgets(buf,sizeof buf, pfp);
138924294Smckusick 	    if (ret == Nullch) {
139024294Smckusick 		if (p_max - p_end < 4)
139124294Smckusick 		    Strcpy(buf,"  \n");	/* assume blank lines got chopped */
139224294Smckusick 		else
139324294Smckusick 		    fatal("Unexpected end of file in patch.\n");
139424294Smckusick 	    }
139524294Smckusick 	    p_input_line++;
139624294Smckusick 	    if (strnEQ(buf,"********",8))
139724294Smckusick 		fatal("Unexpected end of hunk at line %d.\n",
139824294Smckusick 		    p_input_line);
139924294Smckusick 	    p_char[++p_end] = *buf;
140024294Smckusick 	    switch (*buf) {
140124294Smckusick 	    case '*':
140224294Smckusick 		if (p_end != 0)
140324294Smckusick 		    fatal("Unexpected *** at line %d: %s", p_input_line, buf);
140424294Smckusick 		context = 0;
140524294Smckusick 		p_line[p_end] = savestr(buf);
140624294Smckusick 		for (s=buf; *s && !isdigit(*s); s++) ;
140724294Smckusick 		p_first = (LINENUM) atol(s);
140824294Smckusick 		while (isdigit(*s)) s++;
140924294Smckusick 		for (; *s && !isdigit(*s); s++) ;
141024294Smckusick 		p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1;
141124294Smckusick 		break;
141224294Smckusick 	    case '-':
141324294Smckusick 		if (buf[1] == '-') {
141424294Smckusick 		    if (p_end != p_ptrn_lines + 1 &&
141524294Smckusick 			p_end != p_ptrn_lines + 2)
141624294Smckusick 			fatal("Unexpected --- at line %d: %s",
141724294Smckusick 			    p_input_line,buf);
141824294Smckusick 		    repl_beginning = p_end;
141924294Smckusick 		    context = 0;
142024294Smckusick 		    p_line[p_end] = savestr(buf);
142124294Smckusick 		    p_char[p_end] = '=';
142224294Smckusick 		    for (s=buf; *s && !isdigit(*s); s++) ;
142324294Smckusick 		    p_newfirst = (LINENUM) atol(s);
142424294Smckusick 		    while (isdigit(*s)) s++;
142524294Smckusick 		    for (; *s && !isdigit(*s); s++) ;
142624294Smckusick 		    p_max = ((LINENUM)atol(s)) - p_newfirst + 1 + p_end;
142724294Smckusick 		    break;
142824294Smckusick 		}
142924294Smckusick 		/* FALL THROUGH */
143024294Smckusick 	    case '+': case '!':
143124294Smckusick 		if (context > 0) {
143224294Smckusick 		    if (context < p_context)
143324294Smckusick 			p_context = context;
143424294Smckusick 		    context = -100;
143524294Smckusick 		}
143624294Smckusick 		p_line[p_end] = savestr(buf+2);
143724294Smckusick 		break;
143824294Smckusick 	    case '\t': case '\n':	/* assume the 2 spaces got eaten */
143924294Smckusick 		p_line[p_end] = savestr(buf);
144024294Smckusick 		if (p_end != p_ptrn_lines + 1) {
144124294Smckusick 		    context++;
144224294Smckusick 		    p_char[p_end] = ' ';
144324294Smckusick 		}
144424294Smckusick 		break;
144524294Smckusick 	    case ' ':
144624294Smckusick 		context++;
144724294Smckusick 		p_line[p_end] = savestr(buf+2);
144824294Smckusick 		break;
144924294Smckusick 	    default:
145024294Smckusick 		fatal("Malformed patch at line %d: %s",p_input_line,buf);
145124294Smckusick 	    }
145225795Smckusick 	    p_len[p_end] = 0;
145325795Smckusick 	    if (p_line[p_end] != 0)
145425795Smckusick 		p_len[p_end] = strlen(p_line[p_end]);
145524294Smckusick 					/* for strncmp() so we do not have */
145624294Smckusick 					/* to assume null termination */
145724294Smckusick 	}
145824294Smckusick 	if (p_end >=0 && !p_ptrn_lines)
145924294Smckusick 	    fatal("No --- found in patch at line %d\n", pch_hunk_beg());
146024294Smckusick 	p_repl_lines = p_end - repl_beginning;
146124294Smckusick     }
146224295Smckusick     else if (diff_type == NEW_CONTEXT_DIFF) {
146324295Smckusick 	long line_beginning = ftell(pfp);
146424295Smckusick 	LINENUM repl_beginning = 0;
146524295Smckusick 	LINENUM fillcnt = 0;
146624295Smckusick 	LINENUM fillsrc;
146724295Smckusick 	LINENUM filldst;
146824295Smckusick 
146924295Smckusick 	ret = pgets(buf,sizeof buf, pfp);
147024295Smckusick 	if (ret == Nullch || strnNE(buf,"********",8)) {
147124295Smckusick 	    next_intuit_at(line_beginning);
147224295Smckusick 	    return FALSE;
147324295Smckusick 	}
147424295Smckusick 	p_context = 0;
147524295Smckusick 	while (p_end < p_max) {
147624302Smckusick 	    line_beginning = ftell(pfp);
147724295Smckusick 	    ret = pgets(buf,sizeof buf, pfp);
147824295Smckusick 	    if (ret == Nullch) {
147924295Smckusick 		if (p_max - p_end < 4)
148024295Smckusick 		    Strcpy(buf,"  \n");	/* assume blank lines got chopped */
148125531Svan 		else if (p_end == repl_beginning) {
148225531Svan 		    /* redundant 'new' context lines were omitted - set up */
148325531Svan 		    /* to fill them in from the the old file's context */
148425531Svan 		    fillsrc = 1;
148525531Svan 		    filldst = p_end + 1;
148625531Svan 		    fillcnt = p_max - repl_beginning;
148725531Svan 		    p_end = p_max;
148825531Svan 		    break;
148925531Svan 		} else
149024295Smckusick 		    fatal("Unexpected end of file in patch.\n");
149124295Smckusick 	    }
149224295Smckusick 	    p_input_line++;
149324295Smckusick 	    p_char[++p_end] = *buf;
149424295Smckusick 	    switch (*buf) {
1495*26490Sbloom 	    case '*':	/* another hunk */
1496*26490Sbloom 	    case 'd':	/* another hunk in a different file */
1497*26490Sbloom 	    case 'B':	/* ditto */
1498*26490Sbloom 	    case 'C':	/* ditto */
1499*26490Sbloom 	    case 'F':	/* ditto */
1500*26490Sbloom 	    case 'O':	/* ditto */
1501*26490Sbloom 		if (strnEQ(buf,"********",8) ||
1502*26490Sbloom 		    strnEQ(buf,"diff",4) ||
1503*26490Sbloom 	    	    strnEQ(buf,"Binary files ",13) ||
1504*26490Sbloom 	    	    strnEQ(buf,"Files ",6) ||
1505*26490Sbloom 		    strnEQ(buf,"Common subdirectories: ",23) ||
1506*26490Sbloom 		    strnEQ(buf,"Only in ",8)) {
150724295Smckusick 		    if (p_end != repl_beginning + 1)
150824295Smckusick 			fatal("Unexpected end of hunk at line %d.\n",
150924295Smckusick 				p_input_line);
151024295Smckusick 		    /* redundant 'new' context lines were omitted - set up */
151124295Smckusick 		    /* to fill them in from the the old file's context */
151224295Smckusick 		    fillsrc = 1;
151324295Smckusick 		    filldst = p_end;
151424302Smckusick 		    fillcnt = p_max - repl_beginning;
151524295Smckusick 		    p_end = p_max;
151624302Smckusick 		    Fseek(pfp, line_beginning, 0); /* backup the diff input */
151724295Smckusick 		    break;
151824295Smckusick 		}
151924295Smckusick 		if (p_end != 0)
152024295Smckusick 		    fatal("Unexpected *** at line %d: %s", p_input_line, buf);
152124295Smckusick 		context = 0;
152224295Smckusick 		p_line[p_end] = savestr(buf);
152324295Smckusick 		for (s=buf; *s && !isdigit(*s); s++) ;
152424295Smckusick 		p_first = (LINENUM) atol(s);
152524295Smckusick 		while (isdigit(*s)) s++;
152624295Smckusick 		for (; *s && !isdigit(*s); s++) ;
152724295Smckusick 		p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1;
152824295Smckusick 		break;
152924295Smckusick 	    case '-':
153024295Smckusick 		if (buf[1] == '-') {
153124295Smckusick 		    if (p_end != p_ptrn_lines + 1) {
153224295Smckusick 			if (p_end == 1) {
153324295Smckusick 			    /* `old' lines were omitted - set up to fill them */
153424295Smckusick 			    /* in from 'new' context lines. */
153524295Smckusick 			    p_end = p_ptrn_lines + 1;
153624295Smckusick 			    fillsrc = p_end + 1;
153724295Smckusick 			    filldst = 1;
153824295Smckusick 			    fillcnt = p_ptrn_lines;
153924295Smckusick 			} else
154024295Smckusick 			    fatal("Unexpected --- at line %d: %s",
154124295Smckusick 				p_input_line,buf);
154224295Smckusick 		    }
154324295Smckusick 		    repl_beginning = p_end;
154424295Smckusick 		    p_line[p_end] = savestr(buf);
154524295Smckusick 		    p_char[p_end] = '=';
154624295Smckusick 		    for (s=buf; *s && !isdigit(*s); s++) ;
154724295Smckusick 		    p_newfirst = (LINENUM) atol(s);
154824295Smckusick 		    while (isdigit(*s)) s++;
154924295Smckusick 		    for (; *s && !isdigit(*s); s++) ;
155024295Smckusick 		    p_max = ((LINENUM)atol(s)) - p_newfirst + 1 + p_end;
155124295Smckusick 		    break;
155224295Smckusick 		}
155324295Smckusick 		/* FALL THROUGH */
155424295Smckusick 	    case '+': case '!':
155524295Smckusick 		if (context > 0 && p_context == 0) {
155624295Smckusick 		    p_context = context;
155724295Smckusick 		}
155824295Smckusick 		p_line[p_end] = savestr(buf+2);
155924295Smckusick 		break;
156024295Smckusick 	    case '\t': case '\n':	/* assume the 2 spaces got eaten */
156124295Smckusick 		p_line[p_end] = savestr(buf);
156224295Smckusick 		if (p_end != p_ptrn_lines + 1) {
156324295Smckusick 		    context++;
156424295Smckusick 		    p_char[p_end] = ' ';
156524295Smckusick 		}
156624295Smckusick 		break;
156724295Smckusick 	    case ' ':
156824295Smckusick 		context++;
156924295Smckusick 		p_line[p_end] = savestr(buf+2);
157024295Smckusick 		break;
157124295Smckusick 	    default:
157224295Smckusick 		fatal("Malformed patch at line %d: %s",p_input_line,buf);
157324295Smckusick 	    }
157425795Smckusick 	    p_len[p_end] = 0;
157525795Smckusick 	    if (p_line[p_end] != 0)
157625795Smckusick 		p_len[p_end] = strlen(p_line[p_end]);
157724295Smckusick 					/* for strncmp() so we do not have */
157824295Smckusick 					/* to assume null termination */
157924295Smckusick 	}
158024295Smckusick 	if (p_end >=0 && !p_ptrn_lines)
158124295Smckusick 	    fatal("No --- found in patch at line %d\n", pch_hunk_beg());
158224295Smckusick 
158324295Smckusick 	/* if there were omitted context lines, fill them in */
158424295Smckusick 	if (fillcnt) {
158524295Smckusick 	    while (fillcnt-- > 0) {
158624295Smckusick 		while (p_char[fillsrc] != ' ')
158724295Smckusick 		    fillsrc++;
158824295Smckusick 		p_line[filldst] = p_line[fillsrc];
158924295Smckusick 		p_char[filldst] = p_char[fillsrc];
159024295Smckusick 		p_len[filldst] = p_len[fillsrc];
159124295Smckusick 		fillsrc++; filldst++;
159224295Smckusick 	    }
159324295Smckusick 	}
159424295Smckusick 	p_repl_lines = p_end - repl_beginning;
159524295Smckusick     }
159624294Smckusick     else {				/* normal diff--fake it up */
159724294Smckusick 	char hunk_type;
159824294Smckusick 	register int i;
159924294Smckusick 	LINENUM min, max;
160024294Smckusick 	long line_beginning = ftell(pfp);
160124294Smckusick 
160224294Smckusick 	p_context = 0;
160324294Smckusick 	ret = pgets(buf,sizeof buf, pfp);
160424294Smckusick 	p_input_line++;
160524294Smckusick 	if (ret == Nullch || !isdigit(*buf)) {
160624294Smckusick 	    next_intuit_at(line_beginning);
160724294Smckusick 	    return FALSE;
160824294Smckusick 	}
160924294Smckusick 	p_first = (LINENUM)atol(buf);
161024294Smckusick 	for (s=buf; isdigit(*s); s++) ;
161124294Smckusick 	if (*s == ',') {
161224294Smckusick 	    p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1;
161324294Smckusick 	    while (isdigit(*s)) s++;
161424294Smckusick 	}
161524294Smckusick 	else
161624294Smckusick 	    p_ptrn_lines = (*s != 'a');
161724294Smckusick 	hunk_type = *s;
161824294Smckusick 	if (hunk_type == 'a')
161924294Smckusick 	    p_first++;			/* do append rather than insert */
162024294Smckusick 	min = (LINENUM)atol(++s);
162124294Smckusick 	for (; isdigit(*s); s++) ;
162224294Smckusick 	if (*s == ',')
162324294Smckusick 	    max = (LINENUM)atol(++s);
162424294Smckusick 	else
162524294Smckusick 	    max = min;
162624294Smckusick 	if (hunk_type == 'd')
162724294Smckusick 	    min++;
162824294Smckusick 	p_end = p_ptrn_lines + 1 + max - min + 1;
162924294Smckusick 	p_newfirst = min;
163024294Smckusick 	p_repl_lines = max - min + 1;
163124294Smckusick 	Sprintf(buf,"*** %d,%d\n", p_first, p_first + p_ptrn_lines - 1);
163224294Smckusick 	p_line[0] = savestr(buf);
163324294Smckusick 	p_char[0] = '*';
163424294Smckusick 	for (i=1; i<=p_ptrn_lines; i++) {
163524294Smckusick 	    ret = pgets(buf,sizeof buf, pfp);
163624294Smckusick 	    p_input_line++;
163724294Smckusick 	    if (ret == Nullch)
163824294Smckusick 		fatal("Unexpected end of file in patch at line %d.\n",
163924294Smckusick 		  p_input_line);
164024294Smckusick 	    if (*buf != '<')
164124294Smckusick 		fatal("< expected at line %d of patch.\n", p_input_line);
164224294Smckusick 	    p_line[i] = savestr(buf+2);
164325795Smckusick 	    p_len[i] = 0;
164425795Smckusick 	    if (p_line[i] != 0)
164525795Smckusick 		p_len[i] = strlen(p_line[i]);
164624294Smckusick 	    p_char[i] = '-';
164724294Smckusick 	}
164824294Smckusick 	if (hunk_type == 'c') {
164924294Smckusick 	    ret = pgets(buf,sizeof buf, pfp);
165024294Smckusick 	    p_input_line++;
165124294Smckusick 	    if (ret == Nullch)
165224294Smckusick 		fatal("Unexpected end of file in patch at line %d.\n",
165324294Smckusick 		    p_input_line);
165424294Smckusick 	    if (*buf != '-')
165524294Smckusick 		fatal("--- expected at line %d of patch.\n", p_input_line);
165624294Smckusick 	}
165724294Smckusick 	Sprintf(buf,"--- %d,%d\n",min,max);
165824294Smckusick 	p_line[i] = savestr(buf);
165924294Smckusick 	p_char[i] = '=';
166024294Smckusick 	for (i++; i<=p_end; i++) {
166124294Smckusick 	    ret = pgets(buf,sizeof buf, pfp);
166224294Smckusick 	    p_input_line++;
166324294Smckusick 	    if (ret == Nullch)
166424294Smckusick 		fatal("Unexpected end of file in patch at line %d.\n",
166524294Smckusick 		    p_input_line);
166624294Smckusick 	    if (*buf != '>')
166724294Smckusick 		fatal("> expected at line %d of patch.\n", p_input_line);
166824294Smckusick 	    p_line[i] = savestr(buf+2);
166925795Smckusick 	    p_len[i] = 0;
167025795Smckusick 	    if (p_line[i] != 0)
167125795Smckusick 		p_len[i] = strlen(p_line[i]);
167224294Smckusick 	    p_char[i] = '+';
167324294Smckusick 	}
167424294Smckusick     }
167524294Smckusick     if (reverse)			/* backwards patch? */
167624294Smckusick 	pch_swap();
167724294Smckusick #ifdef DEBUGGING
167824294Smckusick     if (debug & 2) {
167924294Smckusick 	int i;
168024294Smckusick 	char special;
168124294Smckusick 
168224294Smckusick 	for (i=0; i <= p_end; i++) {
168324294Smckusick 	    if (i == p_ptrn_lines)
168424294Smckusick 		special = '^';
168524294Smckusick 	    else
168624294Smckusick 		special = ' ';
168724294Smckusick 	    printf("%3d %c %c %s",i,p_char[i],special,p_line[i]);
168824294Smckusick 	}
168924294Smckusick     }
169024294Smckusick #endif
169124294Smckusick     return TRUE;
169224294Smckusick }
169324294Smckusick 
169424294Smckusick char *
169524294Smckusick pgets(bf,sz,fp)
169624294Smckusick char *bf;
169724294Smckusick int sz;
169824294Smckusick FILE *fp;
169924294Smckusick {
170024294Smckusick     char *ret = fgets(bf,sz,fp);
170124294Smckusick     register char *s;
170224294Smckusick     register int indent = 0;
170324294Smckusick 
170424294Smckusick     if (p_indent && ret != Nullch) {
170524294Smckusick 	for (s=buf; indent < p_indent && (*s == ' ' || *s == '\t'); s++) {
170624294Smckusick 	    if (*s == '\t')
170724294Smckusick 		indent += 8 - (indent % 7);
170824294Smckusick 	    else
170924294Smckusick 		indent++;
171024294Smckusick 	}
171124294Smckusick 	if (buf != s)
171224294Smckusick 	    Strcpy(buf,s);
171324294Smckusick     }
171424294Smckusick     return ret;
171524294Smckusick }
171624294Smckusick 
171724294Smckusick pch_swap()
171824294Smckusick {
171924294Smckusick     char *tp_line[MAXHUNKSIZE];		/* the text of the hunk */
172024294Smckusick     char tp_char[MAXHUNKSIZE];		/* +, -, and ! */
172124294Smckusick     int tp_len[MAXHUNKSIZE];		/* length of each line */
172224294Smckusick     register LINENUM i, n;
172324294Smckusick     bool blankline = FALSE;
172424294Smckusick     register char *s;
172524294Smckusick 
172624294Smckusick     i = p_first;
172724294Smckusick     p_first = p_newfirst;
172824294Smckusick     p_newfirst = i;
172924294Smckusick 
173024294Smckusick     /* make a scratch copy */
173124294Smckusick 
173224294Smckusick     for (i=0; i<=p_end; i++) {
173324294Smckusick 	tp_line[i] = p_line[i];
173424294Smckusick 	tp_char[i] = p_char[i];
173524294Smckusick 	tp_len[i] = p_len[i];
173624294Smckusick     }
173724294Smckusick 
173824294Smckusick     /* now turn the new into the old */
173924294Smckusick 
174024294Smckusick     i = p_ptrn_lines + 1;
174124294Smckusick     if (tp_char[i] == '\n') {		/* account for possible blank line */
174224294Smckusick 	blankline = TRUE;
174324294Smckusick 	i++;
174424294Smckusick     }
174524294Smckusick     for (n=0; i <= p_end; i++,n++) {
174624294Smckusick 	p_line[n] = tp_line[i];
174724294Smckusick 	p_char[n] = tp_char[i];
174824294Smckusick 	if (p_char[n] == '+')
174924294Smckusick 	    p_char[n] = '-';
175024294Smckusick 	p_len[n] = tp_len[i];
175124294Smckusick     }
175224294Smckusick     if (blankline) {
175324294Smckusick 	i = p_ptrn_lines + 1;
175424294Smckusick 	p_line[n] = tp_line[i];
175524294Smckusick 	p_char[n] = tp_char[i];
175624294Smckusick 	p_len[n] = tp_len[i];
175724294Smckusick 	n++;
175824294Smckusick     }
175924294Smckusick     assert(p_char[0] == '=');
176024294Smckusick     p_char[0] = '*';
176124294Smckusick     for (s=p_line[0]; *s; s++)
176224294Smckusick 	if (*s == '-')
176324294Smckusick 	    *s = '*';
176424294Smckusick 
176524294Smckusick     /* now turn the old into the new */
176624294Smckusick 
176724294Smckusick     assert(tp_char[0] == '*');
176824294Smckusick     tp_char[0] = '=';
176924294Smckusick     for (s=tp_line[0]; *s; s++)
177024294Smckusick 	if (*s == '*')
177124294Smckusick 	    *s = '-';
177224294Smckusick     for (i=0; n <= p_end; i++,n++) {
177324294Smckusick 	p_line[n] = tp_line[i];
177424294Smckusick 	p_char[n] = tp_char[i];
177524294Smckusick 	if (p_char[n] == '-')
177624294Smckusick 	    p_char[n] = '+';
177724294Smckusick 	p_len[n] = tp_len[i];
177824294Smckusick     }
177924294Smckusick     assert(i == p_ptrn_lines + 1);
178024294Smckusick     i = p_ptrn_lines;
178124294Smckusick     p_ptrn_lines = p_repl_lines;
178224294Smckusick     p_repl_lines = i;
178324294Smckusick }
178424294Smckusick 
178524294Smckusick LINENUM
178624294Smckusick pch_first()
178724294Smckusick {
178824294Smckusick     return p_first;
178924294Smckusick }
179024294Smckusick 
179124294Smckusick LINENUM
179224294Smckusick pch_ptrn_lines()
179324294Smckusick {
179424294Smckusick     return p_ptrn_lines;
179524294Smckusick }
179624294Smckusick 
179724294Smckusick LINENUM
179824294Smckusick pch_newfirst()
179924294Smckusick {
180024294Smckusick     return p_newfirst;
180124294Smckusick }
180224294Smckusick 
180324294Smckusick LINENUM
180424294Smckusick pch_repl_lines()
180524294Smckusick {
180624294Smckusick     return p_repl_lines;
180724294Smckusick }
180824294Smckusick 
180924294Smckusick LINENUM
181024294Smckusick pch_end()
181124294Smckusick {
181224294Smckusick     return p_end;
181324294Smckusick }
181424294Smckusick 
181524294Smckusick LINENUM
181624294Smckusick pch_context()
181724294Smckusick {
181824294Smckusick     return p_context;
181924294Smckusick }
182024294Smckusick 
182124294Smckusick pch_line_len(line)
182224294Smckusick LINENUM line;
182324294Smckusick {
182424294Smckusick     return p_len[line];
182524294Smckusick }
182624294Smckusick 
182724294Smckusick char
182824294Smckusick pch_char(line)
182924294Smckusick LINENUM line;
183024294Smckusick {
183124294Smckusick     return p_char[line];
183224294Smckusick }
183324294Smckusick 
183424294Smckusick char *
183524294Smckusick pfetch(line)
183624294Smckusick LINENUM line;
183724294Smckusick {
183824294Smckusick     return p_line[line];
183924294Smckusick }
184024294Smckusick 
184124294Smckusick LINENUM
184224294Smckusick pch_hunk_beg()
184324294Smckusick {
184424294Smckusick     return p_input_line - p_end - 1;
184524294Smckusick }
184624294Smckusick 
184724294Smckusick char *
184824294Smckusick savestr(s)
184924294Smckusick register char *s;
185024294Smckusick {
185124294Smckusick     register char  *rv,
185224294Smckusick                    *t;
185324294Smckusick 
185424294Smckusick     t = s;
185524294Smckusick     while (*t++);
185624294Smckusick     rv = malloc((MEM) (t - s));
185724294Smckusick     if (rv == NULL)
185824294Smckusick 	fatal ("patch: out of memory (savestr)\n");
185924294Smckusick     t = rv;
186024294Smckusick     while (*t++ = *s++);
186124294Smckusick     return rv;
186224294Smckusick }
186324294Smckusick 
186424294Smckusick my_exit(status)
186524294Smckusick int status;
186624294Smckusick {
186724294Smckusick     Unlink(TMPINNAME);
186824294Smckusick     Unlink(TMPOUTNAME);
186924294Smckusick     Unlink(TMPREJNAME);
187024294Smckusick     Unlink(TMPPATNAME);
187124294Smckusick     exit(status);
187224294Smckusick }
187324294Smckusick 
187424294Smckusick #ifdef lint
187524294Smckusick 
187624294Smckusick /*VARARGS ARGSUSED*/
187724294Smckusick say(pat) char *pat; { ; }
187824294Smckusick /*VARARGS ARGSUSED*/
187924294Smckusick fatal(pat) char *pat; { ; }
188024294Smckusick /*VARARGS ARGSUSED*/
188124294Smckusick ask(pat) char *pat; { ; }
188224294Smckusick 
188324294Smckusick #else lint
188424294Smckusick 
188524294Smckusick say(pat,arg1,arg2,arg3)
188624294Smckusick char *pat;
188724294Smckusick int arg1,arg2,arg3;
188824294Smckusick {
188924294Smckusick     fprintf(stderr,pat,arg1,arg2,arg3);
189024294Smckusick     Fflush(stderr);
189124294Smckusick }
189224294Smckusick 
189324294Smckusick fatal(pat,arg1,arg2,arg3)
189424294Smckusick char *pat;
189524294Smckusick int arg1,arg2,arg3;
189624294Smckusick {
189724294Smckusick     say(pat,arg1,arg2,arg3);
189824294Smckusick     my_exit(1);
189924294Smckusick }
190024294Smckusick 
190124294Smckusick ask(pat,arg1,arg2,arg3)
190224294Smckusick char *pat;
190324294Smckusick int arg1,arg2,arg3;
190424294Smckusick {
190524294Smckusick     int ttyfd = open("/dev/tty",2);
190624294Smckusick     int r;
190724294Smckusick 
190824294Smckusick     say(pat,arg1,arg2,arg3);
190924294Smckusick     if (ttyfd >= 0) {
191024294Smckusick 	r = read(ttyfd, buf, sizeof buf);
191124294Smckusick 	Close(ttyfd);
191224294Smckusick     }
191324294Smckusick     else
191424294Smckusick 	r = read(2, buf, sizeof buf);
191524294Smckusick     if (r <= 0)
191624294Smckusick 	buf[0] = 0;
191724294Smckusick }
191824294Smckusick #endif lint
191924294Smckusick 
192024294Smckusick bool
192124294Smckusick rev_in_string(string)
192224294Smckusick char *string;
192324294Smckusick {
192424294Smckusick     register char *s;
192524294Smckusick     register int patlen;
192624294Smckusick 
192724294Smckusick     if (revision == Nullch)
192824294Smckusick 	return TRUE;
192924294Smckusick     patlen = strlen(revision);
193024294Smckusick     for (s = string; *s; s++) {
193124294Smckusick 	if (isspace(*s) && strnEQ(s+1,revision,patlen) &&
193224294Smckusick 		isspace(s[patlen+1] )) {
193324294Smckusick 	    return TRUE;
193424294Smckusick 	}
193524294Smckusick     }
193624294Smckusick     return FALSE;
193724294Smckusick }
193824294Smckusick 
193924294Smckusick set_signals()
194024294Smckusick {
194124294Smckusick     /*NOSTRICT*/
194224294Smckusick     if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
194324294Smckusick 	Signal(SIGHUP, my_exit);
194424294Smckusick     /*NOSTRICT*/
194524294Smckusick     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
194624294Smckusick 	Signal(SIGINT, my_exit);
194724294Smckusick }
194824294Smckusick 
194924294Smckusick ignore_signals()
195024294Smckusick {
195124294Smckusick     /*NOSTRICT*/
195224294Smckusick     Signal(SIGHUP, SIG_IGN);
195324294Smckusick     /*NOSTRICT*/
195424294Smckusick     Signal(SIGINT, SIG_IGN);
195524294Smckusick }
1956