xref: /csrg-svn/usr.bin/patch/patch.c (revision 24295)
124294Smckusick #ifndef lint
2*24295Smckusick static char sccsid[] = "@(#)patch.c	5.2 (Berkeley) 08/16/85";
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 $
15*24295Smckusick  * 85/08/15 van%ucbmonet@berkeley
16*24295Smckusick  * Changes for 4.3bsd diff -c.
17*24295Smckusick  *
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;
15524294Smckusick bool usepath = FALSE;
15624294Smckusick bool canonicalize = FALSE;
15724294Smckusick 
15824294Smckusick #define CONTEXT_DIFF 1
15924294Smckusick #define NORMAL_DIFF 2
16024294Smckusick #define ED_DIFF 3
161*24295Smckusick #define NEW_CONTEXT_DIFF 4
16224294Smckusick int diff_type = 0;
16324294Smckusick 
16424294Smckusick int do_defines = 0;			/* patch using ifdef, ifndef, etc. */
16524294Smckusick char if_defined[128];			/* #ifdef xyzzy */
16624294Smckusick char not_defined[128];			/* #ifndef xyzzy */
16724294Smckusick char else_defined[] = "#else\n";	/* #else */
16824294Smckusick char end_defined[128];			/* #endif xyzzy */
16924294Smckusick 
17024294Smckusick char *revision = Nullch;		/* prerequisite revision, if any */
17124294Smckusick 
17224294Smckusick /* procedures */
17324294Smckusick 
17424294Smckusick LINENUM locate_hunk();
17524294Smckusick bool patch_match();
17624294Smckusick bool similar();
17724294Smckusick char *malloc();
17824294Smckusick char *savestr();
17924294Smckusick char *strcpy();
18024294Smckusick char *strcat();
18124294Smckusick char *sprintf();		/* usually */
18224294Smckusick int my_exit();
18324294Smckusick bool rev_in_string();
18424294Smckusick char *fetchname();
18524294Smckusick long atol();
18624294Smckusick long lseek();
18724294Smckusick char *mktemp();
18824294Smckusick 
18924294Smckusick /* patch type */
19024294Smckusick 
19124294Smckusick bool there_is_another_patch();
19224294Smckusick bool another_hunk();
19324294Smckusick char *pfetch();
19424294Smckusick int pch_line_len();
19524294Smckusick LINENUM pch_first();
19624294Smckusick LINENUM pch_ptrn_lines();
19724294Smckusick LINENUM pch_newfirst();
19824294Smckusick LINENUM pch_repl_lines();
19924294Smckusick LINENUM pch_end();
20024294Smckusick LINENUM pch_context();
20124294Smckusick LINENUM pch_hunk_beg();
20224294Smckusick char pch_char();
20324294Smckusick char *pfetch();
20424294Smckusick char *pgets();
20524294Smckusick 
20624294Smckusick /* input file type */
20724294Smckusick 
20824294Smckusick char *ifetch();
20924294Smckusick 
21024294Smckusick /* apply a context patch to a named file */
21124294Smckusick 
21224294Smckusick main(argc,argv)
21324294Smckusick int argc;
21424294Smckusick char **argv;
21524294Smckusick {
21624294Smckusick     LINENUM where;
21724294Smckusick     int hunk = 0;
21824294Smckusick     int failed = 0;
21924294Smckusick     int i;
22024294Smckusick 
22124294Smckusick     setbuf(stderr,serrbuf);
22224294Smckusick     for (i = 0; i<MAXFILEC; i++)
22324294Smckusick 	filearg[i] = Nullch;
22424294Smckusick     Mktemp(TMPOUTNAME);
22524294Smckusick     Mktemp(TMPINNAME);
22624294Smckusick     Mktemp(TMPREJNAME);
22724294Smckusick     Mktemp(TMPPATNAME);
22824294Smckusick 
22924294Smckusick     /* parse switches */
23024294Smckusick     Argc = argc;
23124294Smckusick     Argv = argv;
23224294Smckusick     get_some_switches();
23324294Smckusick 
23424294Smckusick     /* make sure we clean up /tmp in case of disaster */
23524294Smckusick     set_signals();
23624294Smckusick 
23724294Smckusick     for (
23824294Smckusick 	open_patch_file(filearg[1]);
23924294Smckusick 	there_is_another_patch();
24024294Smckusick 	reinitialize_almost_everything()
24124294Smckusick     ) {					/* for each patch in patch file */
24224294Smckusick 
24324294Smckusick 	if (outname == Nullch)
24424294Smckusick 	    outname = savestr(filearg[0]);
24524294Smckusick 
24624294Smckusick 	/* initialize the patched file */
24724294Smckusick 	init_output(TMPOUTNAME);
24824294Smckusick 
24924294Smckusick 	/* for ed script just up and do it and exit */
25024294Smckusick 	if (diff_type == ED_DIFF) {
25124294Smckusick 	    do_ed_script();
25224294Smckusick 	    continue;
25324294Smckusick 	}
25424294Smckusick 
25524294Smckusick 	/* initialize reject file */
25624294Smckusick 	init_reject(TMPREJNAME);
25724294Smckusick 
25824294Smckusick 	/* find out where all the lines are */
25924294Smckusick 	scan_input(filearg[0]);
26024294Smckusick 
26124294Smckusick 	/* from here on, open no standard i/o files, because malloc */
26224294Smckusick 	/* might misfire */
26324294Smckusick 
26424294Smckusick 	/* apply each hunk of patch */
26524294Smckusick 	hunk = 0;
26624294Smckusick 	failed = 0;
26724294Smckusick 	while (another_hunk()) {
26824294Smckusick 	    hunk++;
26924294Smckusick 	    where = locate_hunk();
27024294Smckusick 	    if (hunk == 1 && where == Null(LINENUM)) {
27124294Smckusick 					/* dwim for reversed patch? */
27224294Smckusick 		pch_swap();
27324294Smckusick 		reverse = !reverse;
27424294Smckusick 		where = locate_hunk();	/* try again */
27524294Smckusick 		if (where == Null(LINENUM)) {
27624294Smckusick 		    pch_swap();		/* no, put it back to normal */
27724294Smckusick 		    reverse = !reverse;
27824294Smckusick 		}
27924294Smckusick 		else {
28024294Smckusick 		    say("%seversed (or previously applied) patch detected!  %s -R.\n",
28124294Smckusick 			reverse ? "R" : "Unr",
28224294Smckusick 			reverse ? "Assuming" : "Ignoring");
28324294Smckusick 		}
28424294Smckusick 	    }
28524294Smckusick 	    if (where == Null(LINENUM)) {
28624294Smckusick 		abort_hunk();
28724294Smckusick 		failed++;
28824294Smckusick 		if (verbose)
28924294Smckusick 		    say("Hunk #%d failed.\n",hunk);
29024294Smckusick 	    }
29124294Smckusick 	    else {
29224294Smckusick 		apply_hunk(where);
29324294Smckusick 		if (verbose)
29424294Smckusick 		    if (last_offset)
29524294Smckusick 			say("Hunk #%d succeeded (offset %d line%s).\n",
29624294Smckusick 			  hunk,last_offset,last_offset==1?"":"s");
29724294Smckusick 		    else
29824294Smckusick 			say("Hunk #%d succeeded.\n", hunk);
29924294Smckusick 	    }
30024294Smckusick 	}
30124294Smckusick 
30224294Smckusick 	assert(hunk);
30324294Smckusick 
30424294Smckusick 	/* finish spewing out the new file */
30524294Smckusick 	spew_output();
30624294Smckusick 
30724294Smckusick 	/* and put the output where desired */
30824294Smckusick 	ignore_signals();
30924294Smckusick 	move_file(TMPOUTNAME,outname);
31024294Smckusick 	Fclose(rejfp);
31124294Smckusick 	rejfp = Nullfp;
31224294Smckusick 	if (failed) {
31324294Smckusick 	    if (!*rejname) {
31424294Smckusick 		Strcpy(rejname, outname);
31524294Smckusick 		Strcat(rejname, ".rej");
31624294Smckusick 	    }
31724294Smckusick 	    say("%d out of %d hunks failed--saving rejects to %s\n",
31824294Smckusick 		failed, hunk, rejname);
31924294Smckusick 	    move_file(TMPREJNAME,rejname);
32024294Smckusick 	}
32124294Smckusick 	set_signals();
32224294Smckusick     }
32324294Smckusick     my_exit(0);
32424294Smckusick }
32524294Smckusick 
32624294Smckusick reinitialize_almost_everything()
32724294Smckusick {
32824294Smckusick     re_patch();
32924294Smckusick     re_input();
33024294Smckusick 
33124294Smckusick     input_lines = 0;
33224294Smckusick     last_frozen_line = 0;
33324294Smckusick 
33424294Smckusick     filec = 0;
33524294Smckusick     if (filearg[0] != Nullch) {
33624294Smckusick 	free(filearg[0]);
33724294Smckusick 	filearg[0] = Nullch;
33824294Smckusick     }
33924294Smckusick 
34024294Smckusick     if (outname != Nullch) {
34124294Smckusick 	free(outname);
34224294Smckusick 	outname = Nullch;
34324294Smckusick     }
34424294Smckusick 
34524294Smckusick     last_offset = 0;
34624294Smckusick 
34724294Smckusick     diff_type = 0;
34824294Smckusick 
34924294Smckusick     if (revision != Nullch) {
35024294Smckusick 	free(revision);
35124294Smckusick 	revision = Nullch;
35224294Smckusick     }
35324294Smckusick 
35424294Smckusick     reverse = FALSE;
35524294Smckusick 
35624294Smckusick     get_some_switches();
35724294Smckusick 
35824294Smckusick     if (filec >= 2)
35924294Smckusick 	fatal("You may not change to a different patch file.\n");
36024294Smckusick }
36124294Smckusick 
36224294Smckusick get_some_switches()
36324294Smckusick {
36424294Smckusick     register char *s;
36524294Smckusick 
36624294Smckusick     rejname[0] = '\0';
36724294Smckusick     if (!Argc)
36824294Smckusick 	return;
36924294Smckusick     for (Argc--,Argv++; Argc; Argc--,Argv++) {
37024294Smckusick 	s = Argv[0];
37124294Smckusick 	if (strEQ(s,"+")) {
37224294Smckusick 	    return;			/* + will be skipped by for loop */
37324294Smckusick 	}
37424294Smckusick 	if (*s != '-' || !s[1]) {
37524294Smckusick 	    if (filec == MAXFILEC)
37624294Smckusick 		fatal("Too many file arguments.\n");
37724294Smckusick 	    filearg[filec++] = savestr(s);
37824294Smckusick 	}
37924294Smckusick 	else {
38024294Smckusick 	    switch (*++s) {
38124294Smckusick 	    case 'b':
38224294Smckusick 		origext = savestr(Argv[1]);
38324294Smckusick 		Argc--,Argv++;
38424294Smckusick 		break;
38524294Smckusick 	    case 'c':
38624294Smckusick 		diff_type = CONTEXT_DIFF;
38724294Smckusick 		break;
38824294Smckusick 	    case 'd':
38924294Smckusick 		if (chdir(Argv[1]) < 0)
39024294Smckusick 		    fatal("Can't cd to %s.\n",Argv[1]);
39124294Smckusick 		Argc--,Argv++;
39224294Smckusick 		break;
39324294Smckusick 	    case 'D':
39424294Smckusick 	    	do_defines++;
39524294Smckusick 		Sprintf(if_defined, "#ifdef %s\n", Argv[1]);
39624294Smckusick 		Sprintf(not_defined, "#ifndef %s\n", Argv[1]);
39724294Smckusick 		Sprintf(end_defined, "#endif %s\n", Argv[1]);
39824294Smckusick 		Argc--,Argv++;
39924294Smckusick 		break;
40024294Smckusick 	    case 'e':
40124294Smckusick 		diff_type = ED_DIFF;
40224294Smckusick 		break;
40324294Smckusick 	    case 'l':
40424294Smckusick 		canonicalize = TRUE;
40524294Smckusick 		break;
40624294Smckusick 	    case 'n':
40724294Smckusick 		diff_type = NORMAL_DIFF;
40824294Smckusick 		break;
40924294Smckusick 	    case 'o':
41024294Smckusick 		outname = savestr(Argv[1]);
41124294Smckusick 		Argc--,Argv++;
41224294Smckusick 		break;
41324294Smckusick 	    case 'p':
41424294Smckusick 		usepath = TRUE;	/* do not strip path names */
41524294Smckusick 		break;
41624294Smckusick 	    case 'r':
41724294Smckusick 		Strcpy(rejname,Argv[1]);
41824294Smckusick 		Argc--,Argv++;
41924294Smckusick 		break;
42024294Smckusick 	    case 'R':
42124294Smckusick 		reverse = TRUE;
42224294Smckusick 		break;
42324294Smckusick 	    case 's':
42424294Smckusick 		verbose = FALSE;
42524294Smckusick 		break;
42624294Smckusick #ifdef DEBUGGING
42724294Smckusick 	    case 'x':
42824294Smckusick 		debug = atoi(s+1);
42924294Smckusick 		break;
43024294Smckusick #endif
43124294Smckusick 	    default:
43224294Smckusick 		fatal("Unrecognized switch: %s\n",Argv[0]);
43324294Smckusick 	    }
43424294Smckusick 	}
43524294Smckusick     }
43624294Smckusick }
43724294Smckusick 
43824294Smckusick LINENUM
43924294Smckusick locate_hunk()
44024294Smckusick {
44124294Smckusick     register LINENUM first_guess = pch_first() + last_offset;
44224294Smckusick     register LINENUM offset;
44324294Smckusick     LINENUM pat_lines = pch_ptrn_lines();
44424294Smckusick     register LINENUM max_pos_offset = input_lines - first_guess
44524294Smckusick 				- pat_lines + 1;
44624294Smckusick     register LINENUM max_neg_offset = first_guess - last_frozen_line - 1
44724294Smckusick 				- pch_context();
44824294Smckusick 
44924294Smckusick     if (!pat_lines)			/* null range matches always */
45024294Smckusick 	return first_guess;
45124294Smckusick     if (max_neg_offset >= first_guess)	/* do not try lines < 0 */
45224294Smckusick 	max_neg_offset = first_guess - 1;
45324294Smckusick     if (first_guess <= input_lines && patch_match(first_guess,(LINENUM)0))
45424294Smckusick 	return first_guess;
45524294Smckusick     for (offset = 1; ; offset++) {
45624294Smckusick 	bool check_after = (offset <= max_pos_offset);
45724294Smckusick 	bool check_before = (offset <= max_pos_offset);
45824294Smckusick 
45924294Smckusick 	if (check_after && patch_match(first_guess,offset)) {
46024294Smckusick #ifdef DEBUGGING
46124294Smckusick 	    if (debug & 1)
46224294Smckusick 		printf("Offset changing from %d to %d\n",last_offset,offset);
46324294Smckusick #endif
46424294Smckusick 	    last_offset = offset;
46524294Smckusick 	    return first_guess+offset;
46624294Smckusick 	}
46724294Smckusick 	else if (check_before && patch_match(first_guess,-offset)) {
46824294Smckusick #ifdef DEBUGGING
46924294Smckusick 	    if (debug & 1)
47024294Smckusick 		printf("Offset changing from %d to %d\n",last_offset,-offset);
47124294Smckusick #endif
47224294Smckusick 	    last_offset = -offset;
47324294Smckusick 	    return first_guess-offset;
47424294Smckusick 	}
47524294Smckusick 	else if (!check_before && !check_after)
47624294Smckusick 	    return Null(LINENUM);
47724294Smckusick     }
47824294Smckusick }
47924294Smckusick 
48024294Smckusick /* we did not find the pattern, dump out the hunk so they can handle it */
48124294Smckusick 
48224294Smckusick abort_hunk()
48324294Smckusick {
48424294Smckusick     register LINENUM i;
48524294Smckusick     register LINENUM pat_end = pch_end();
48624294Smckusick     /* add in last_offset to guess the same as the previous successful hunk */
48724294Smckusick     int oldfirst = pch_first() + last_offset;
48824294Smckusick     int newfirst = pch_newfirst() + last_offset;
48924294Smckusick     int oldlast = oldfirst + pch_ptrn_lines() - 1;
49024294Smckusick     int newlast = newfirst + pch_repl_lines() - 1;
49124294Smckusick 
49224294Smckusick     fprintf(rejfp,"***************\n");
49324294Smckusick     for (i=0; i<=pat_end; i++) {
49424294Smckusick 	switch (pch_char(i)) {
49524294Smckusick 	case '*':
49624294Smckusick 	    fprintf(rejfp,"*** %d,%d\n", oldfirst, oldlast);
49724294Smckusick 	    break;
49824294Smckusick 	case '=':
49924294Smckusick 	    fprintf(rejfp,"--- %d,%d -----\n", newfirst, newlast);
50024294Smckusick 	    break;
50124294Smckusick 	case '\n':
50224294Smckusick 	    fprintf(rejfp,"%s", pfetch(i));
50324294Smckusick 	    break;
50424294Smckusick 	case ' ': case '-': case '+': case '!':
50524294Smckusick 	    fprintf(rejfp,"%c %s", pch_char(i), pfetch(i));
50624294Smckusick 	    break;
50724294Smckusick 	default:
50824294Smckusick 	    say("Fatal internal error in abort_hunk().\n");
50924294Smckusick 	    abort();
51024294Smckusick 	}
51124294Smckusick     }
51224294Smckusick }
51324294Smckusick 
51424294Smckusick /* we found where to apply it (we hope), so do it */
51524294Smckusick 
51624294Smckusick apply_hunk(where)
51724294Smckusick LINENUM where;
51824294Smckusick {
51924294Smckusick     register LINENUM old = 1;
52024294Smckusick     register LINENUM lastline = pch_ptrn_lines();
52124294Smckusick     register LINENUM new = lastline+1;
52224294Smckusick     register int def_state = 0;	/* -1 = ifndef, 1 = ifdef */
52324294Smckusick 
52424294Smckusick     where--;
52524294Smckusick     while (pch_char(new) == '=' || pch_char(new) == '\n')
52624294Smckusick 	new++;
52724294Smckusick 
52824294Smckusick     while (old <= lastline) {
52924294Smckusick 	if (pch_char(old) == '-') {
53024294Smckusick 	    copy_till(where + old - 1);
53124294Smckusick 	    if (do_defines) {
53224294Smckusick 		if (def_state == 0) {
53324294Smckusick 		    fputs(not_defined, ofp);
53424294Smckusick 		    def_state = -1;
53524294Smckusick 		} else
53624294Smckusick 		if (def_state == 1) {
53724294Smckusick 		    fputs(else_defined, ofp);
53824294Smckusick 		    def_state = 2;
53924294Smckusick 		}
54024294Smckusick 		fputs(pfetch(old), ofp);
54124294Smckusick 	    }
54224294Smckusick 	    last_frozen_line++;
54324294Smckusick 	    old++;
54424294Smckusick 	}
54524294Smckusick 	else if (pch_char(new) == '+') {
54624294Smckusick 	    copy_till(where + old - 1);
54724294Smckusick 	    if (do_defines) {
54824294Smckusick 		if (def_state == -1) {
54924294Smckusick 		    fputs(else_defined, ofp);
55024294Smckusick 		    def_state = 2;
55124294Smckusick 		} else
55224294Smckusick 		if (def_state == 0) {
55324294Smckusick 		    fputs(if_defined, ofp);
55424294Smckusick 		    def_state = 1;
55524294Smckusick 		}
55624294Smckusick 	    }
55724294Smckusick 	    fputs(pfetch(new),ofp);
55824294Smckusick 	    new++;
55924294Smckusick 	}
56024294Smckusick 	else {
56124294Smckusick 	    if (pch_char(new) != pch_char(old)) {
56224294Smckusick 		say("Out-of-sync patch, lines %d,%d\n",
56324294Smckusick 		    pch_hunk_beg() + old - 1,
56424294Smckusick 		    pch_hunk_beg() + new - 1);
56524294Smckusick #ifdef DEBUGGING
56624294Smckusick 		printf("oldchar = '%c', newchar = '%c'\n",
56724294Smckusick 		    pch_char(old), pch_char(new));
56824294Smckusick #endif
56924294Smckusick 		my_exit(1);
57024294Smckusick 	    }
57124294Smckusick 	    if (pch_char(new) == '!') {
57224294Smckusick 		copy_till(where + old - 1);
57324294Smckusick 		if (do_defines) {
57424294Smckusick 		   fputs(not_defined,ofp);
57524294Smckusick 		   def_state = -1;
57624294Smckusick 		}
57724294Smckusick 		while (pch_char(old) == '!') {
57824294Smckusick 		    if (do_defines) {
57924294Smckusick 			fputs(pfetch(old),ofp);
58024294Smckusick 		    }
58124294Smckusick 		    last_frozen_line++;
58224294Smckusick 		    old++;
58324294Smckusick 		}
58424294Smckusick 		if (do_defines) {
58524294Smckusick 		    fputs(else_defined, ofp);
58624294Smckusick 		    def_state = 2;
58724294Smckusick 		}
58824294Smckusick 		while (pch_char(new) == '!') {
58924294Smckusick 		    fputs(pfetch(new),ofp);
59024294Smckusick 		    new++;
59124294Smckusick 		}
59224294Smckusick 		if (do_defines) {
59324294Smckusick 		    fputs(end_defined, ofp);
59424294Smckusick 		    def_state = 0;
59524294Smckusick 		}
59624294Smckusick 	    }
59724294Smckusick 	    else {
59824294Smckusick 		assert(pch_char(new) == ' ');
59924294Smckusick 		old++;
60024294Smckusick 		new++;
60124294Smckusick 	    }
60224294Smckusick 	}
60324294Smckusick     }
60424294Smckusick     if (new <= pch_end() && pch_char(new) == '+') {
60524294Smckusick 	copy_till(where + old - 1);
60624294Smckusick 	if (do_defines) {
60724294Smckusick 	    if (def_state == 0) {
60824294Smckusick 	    	fputs(if_defined, ofp);
60924294Smckusick 		def_state = 1;
61024294Smckusick 	    } else
61124294Smckusick 	    if (def_state == -1) {
61224294Smckusick 		fputs(else_defined, ofp);
61324294Smckusick 		def_state = 2;
61424294Smckusick 	    }
61524294Smckusick 	}
61624294Smckusick 	while (new <= pch_end() && pch_char(new) == '+') {
61724294Smckusick 	    fputs(pfetch(new),ofp);
61824294Smckusick 	    new++;
61924294Smckusick 	}
62024294Smckusick     }
62124294Smckusick     if (do_defines && def_state) {
62224294Smckusick 	fputs(end_defined, ofp);
62324294Smckusick     }
62424294Smckusick }
62524294Smckusick 
62624294Smckusick do_ed_script()
62724294Smckusick {
62824294Smckusick     FILE *pipefp, *popen();
62924294Smckusick     bool this_line_is_command = FALSE;
63024294Smckusick     register char *t;
63124294Smckusick     long beginning_of_this_line;
63224294Smckusick 
63324294Smckusick     Unlink(TMPOUTNAME);
63424294Smckusick     copy_file(filearg[0],TMPOUTNAME);
63524294Smckusick     if (verbose)
63624294Smckusick 	Sprintf(buf,"/bin/ed %s",TMPOUTNAME);
63724294Smckusick     else
63824294Smckusick 	Sprintf(buf,"/bin/ed - %s",TMPOUTNAME);
63924294Smckusick     pipefp = popen(buf,"w");
64024294Smckusick     for (;;) {
64124294Smckusick 	beginning_of_this_line = ftell(pfp);
64224294Smckusick 	if (pgets(buf,sizeof buf,pfp) == Nullch) {
64324294Smckusick 	    next_intuit_at(beginning_of_this_line);
64424294Smckusick 	    break;
64524294Smckusick 	}
64624294Smckusick 	for (t=buf; isdigit(*t) || *t == ','; t++) ;
64724294Smckusick 	this_line_is_command = (isdigit(*buf) &&
64824294Smckusick 	  (*t == 'd' || *t == 'c' || *t == 'a') );
64924294Smckusick 	if (this_line_is_command) {
65024294Smckusick 	    fputs(buf,pipefp);
65124294Smckusick 	    if (*t != 'd') {
65224294Smckusick 		while (pgets(buf,sizeof buf,pfp) != Nullch) {
65324294Smckusick 		    fputs(buf,pipefp);
65424294Smckusick 		    if (strEQ(buf,".\n"))
65524294Smckusick 			break;
65624294Smckusick 		}
65724294Smckusick 	    }
65824294Smckusick 	}
65924294Smckusick 	else {
66024294Smckusick 	    next_intuit_at(beginning_of_this_line);
66124294Smckusick 	    break;
66224294Smckusick 	}
66324294Smckusick     }
66424294Smckusick     fprintf(pipefp,"w\n");
66524294Smckusick     fprintf(pipefp,"q\n");
66624294Smckusick     Fflush(pipefp);
66724294Smckusick     Pclose(pipefp);
66824294Smckusick     ignore_signals();
66924294Smckusick     move_file(TMPOUTNAME,outname);
67024294Smckusick     set_signals();
67124294Smckusick }
67224294Smckusick 
67324294Smckusick init_output(name)
67424294Smckusick char *name;
67524294Smckusick {
67624294Smckusick     ofp = fopen(name,"w");
67724294Smckusick     if (ofp == Nullfp)
67824294Smckusick 	fatal("patch: can't create %s.\n",name);
67924294Smckusick }
68024294Smckusick 
68124294Smckusick init_reject(name)
68224294Smckusick char *name;
68324294Smckusick {
68424294Smckusick     rejfp = fopen(name,"w");
68524294Smckusick     if (rejfp == Nullfp)
68624294Smckusick 	fatal("patch: can't create %s.\n",name);
68724294Smckusick }
68824294Smckusick 
68924294Smckusick move_file(from,to)
69024294Smckusick char *from, *to;
69124294Smckusick {
69224294Smckusick     char bakname[512];
69324294Smckusick     register char *s;
69424294Smckusick     int fromfd;
69524294Smckusick     register int i;
69624294Smckusick 
69724294Smckusick     /* to stdout? */
69824294Smckusick 
69924294Smckusick     if (strEQ(to,"-")) {
70024294Smckusick #ifdef DEBUGGING
70124294Smckusick 	if (debug & 4)
70224294Smckusick 	    say("Moving %s to stdout.\n",from);
70324294Smckusick #endif
70424294Smckusick 	fromfd = open(from,0);
70524294Smckusick 	if (fromfd < 0)
70624294Smckusick 	    fatal("patch: internal error, can't reopen %s\n",from);
70724294Smckusick 	while ((i=read(fromfd,buf,sizeof buf)) > 0)
70824294Smckusick 	    if (write(1,buf,i) != 1)
70924294Smckusick 		fatal("patch: write failed\n");
71024294Smckusick 	Close(fromfd);
71124294Smckusick 	return;
71224294Smckusick     }
71324294Smckusick 
71424294Smckusick     Strcpy(bakname,to);
71524294Smckusick     Strcat(bakname,origext?origext:ORIGEXT);
71624294Smckusick     if (stat(to,&filestat) >= 0) {	/* output file exists */
71724294Smckusick 	dev_t to_device = filestat.st_dev;
71824294Smckusick 	ino_t to_inode  = filestat.st_ino;
71924294Smckusick 	char *simplename = bakname;
72024294Smckusick 
72124294Smckusick 	for (s=bakname; *s; s++) {
72224294Smckusick 	    if (*s == '/')
72324294Smckusick 		simplename = s+1;
72424294Smckusick 	}
72524294Smckusick 	/* find a backup name that is not the same file */
72624294Smckusick 	while (stat(bakname,&filestat) >= 0 &&
72724294Smckusick 		to_device == filestat.st_dev && to_inode == filestat.st_ino) {
72824294Smckusick 	    for (s=simplename; *s && !islower(*s); s++) ;
72924294Smckusick 	    if (*s)
73024294Smckusick 		*s = toupper(*s);
73124294Smckusick 	    else
73224294Smckusick 		Strcpy(simplename, simplename+1);
73324294Smckusick 	}
73424294Smckusick 	while (unlink(bakname) >= 0) ;	/* while() is for benefit of Eunice */
73524294Smckusick #ifdef DEBUGGING
73624294Smckusick 	if (debug & 4)
73724294Smckusick 	    say("Moving %s to %s.\n",to,bakname);
73824294Smckusick #endif
73924294Smckusick 	if (link(to,bakname) < 0) {
74024294Smckusick 	    say("patch: can't backup %s, output is in %s\n",
74124294Smckusick 		to,from);
74224294Smckusick 	    return;
74324294Smckusick 	}
74424294Smckusick 	while (unlink(to) >= 0) ;
74524294Smckusick     }
74624294Smckusick #ifdef DEBUGGING
74724294Smckusick     if (debug & 4)
74824294Smckusick 	say("Moving %s to %s.\n",from,to);
74924294Smckusick #endif
75024294Smckusick     if (link(from,to) < 0) {		/* different file system? */
75124294Smckusick 	int tofd;
75224294Smckusick 
75324294Smckusick 	tofd = creat(to,0666);
75424294Smckusick 	if (tofd < 0) {
75524294Smckusick 	    say("patch: can't create %s, output is in %s.\n",
75624294Smckusick 	      to, from);
75724294Smckusick 	    return;
75824294Smckusick 	}
75924294Smckusick 	fromfd = open(from,0);
76024294Smckusick 	if (fromfd < 0)
76124294Smckusick 	    fatal("patch: internal error, can't reopen %s\n",from);
76224294Smckusick 	while ((i=read(fromfd,buf,sizeof buf)) > 0)
76324294Smckusick 	    if (write(tofd,buf,i) != i)
76424294Smckusick 		fatal("patch: write failed\n");
76524294Smckusick 	Close(fromfd);
76624294Smckusick 	Close(tofd);
76724294Smckusick     }
76824294Smckusick     Unlink(from);
76924294Smckusick }
77024294Smckusick 
77124294Smckusick copy_file(from,to)
77224294Smckusick char *from, *to;
77324294Smckusick {
77424294Smckusick     int tofd;
77524294Smckusick     int fromfd;
77624294Smckusick     register int i;
77724294Smckusick 
77824294Smckusick     tofd = creat(to,0666);
77924294Smckusick     if (tofd < 0)
78024294Smckusick 	fatal("patch: can't create %s.\n", to);
78124294Smckusick     fromfd = open(from,0);
78224294Smckusick     if (fromfd < 0)
78324294Smckusick 	fatal("patch: internal error, can't reopen %s\n",from);
78424294Smckusick     while ((i=read(fromfd,buf,sizeof buf)) > 0)
78524294Smckusick 	if (write(tofd,buf,i) != i)
78624294Smckusick 	    fatal("patch: write (%s) failed\n", to);
78724294Smckusick     Close(fromfd);
78824294Smckusick     Close(tofd);
78924294Smckusick }
79024294Smckusick 
79124294Smckusick copy_till(lastline)
79224294Smckusick register LINENUM lastline;
79324294Smckusick {
79424294Smckusick     if (last_frozen_line > lastline)
79524294Smckusick 	say("patch: misordered hunks! output will be garbled.\n");
79624294Smckusick     while (last_frozen_line < lastline) {
79724294Smckusick 	dump_line(++last_frozen_line);
79824294Smckusick     }
79924294Smckusick }
80024294Smckusick 
80124294Smckusick spew_output()
80224294Smckusick {
80324294Smckusick     copy_till(input_lines);		/* dump remainder of file */
80424294Smckusick     Fclose(ofp);
80524294Smckusick     ofp = Nullfp;
80624294Smckusick }
80724294Smckusick 
80824294Smckusick dump_line(line)
80924294Smckusick LINENUM line;
81024294Smckusick {
81124294Smckusick     register char *s;
81224294Smckusick 
81324294Smckusick     for (s=ifetch(line,0); putc(*s,ofp) != '\n'; s++) ;
81424294Smckusick }
81524294Smckusick 
81624294Smckusick /* does the patch pattern match at line base+offset? */
81724294Smckusick 
81824294Smckusick bool
81924294Smckusick patch_match(base,offset)
82024294Smckusick LINENUM base;
82124294Smckusick LINENUM offset;
82224294Smckusick {
82324294Smckusick     register LINENUM pline;
82424294Smckusick     register LINENUM iline;
82524294Smckusick     register LINENUM pat_lines = pch_ptrn_lines();
82624294Smckusick 
82724294Smckusick     for (pline = 1, iline=base+offset; pline <= pat_lines; pline++,iline++) {
82824294Smckusick 	if (canonicalize) {
82924294Smckusick 	    if (!similar(ifetch(iline,(offset >= 0)),
83024294Smckusick 			 pfetch(pline),
83124294Smckusick 			 pch_line_len(pline) ))
83224294Smckusick 		return FALSE;
83324294Smckusick 	}
83424294Smckusick 	else if (strnNE(ifetch(iline,(offset >= 0)),
83524294Smckusick 		   pfetch(pline),
83624294Smckusick 		   pch_line_len(pline) ))
83724294Smckusick 	    return FALSE;
83824294Smckusick     }
83924294Smckusick     return TRUE;
84024294Smckusick }
84124294Smckusick 
84224294Smckusick /* match two lines with canonicalized white space */
84324294Smckusick 
84424294Smckusick bool
84524294Smckusick similar(a,b,len)
84624294Smckusick register char *a, *b;
84724294Smckusick register int len;
84824294Smckusick {
84924294Smckusick     while (len) {
85024294Smckusick 	if (isspace(*b)) {		/* whitespace (or \n) to match? */
85124294Smckusick 	    if (!isspace(*a))		/* no corresponding whitespace? */
85224294Smckusick 		return FALSE;
85324294Smckusick 	    while (len && isspace(*b) && *b != '\n')
85424294Smckusick 		b++,len--;		/* skip pattern whitespace */
85524294Smckusick 	    while (isspace(*a) && *a != '\n')
85624294Smckusick 		a++;			/* skip target whitespace */
85724294Smckusick 	    if (*a == '\n' || *b == '\n')
85824294Smckusick 		return (*a == *b);	/* should end in sync */
85924294Smckusick 	}
86024294Smckusick 	else if (*a++ != *b++)		/* match non-whitespace chars */
86124294Smckusick 	    return FALSE;
86224294Smckusick 	else
86324294Smckusick 	    len--;			/* probably not necessary */
86424294Smckusick     }
86524294Smckusick     return TRUE;			/* actually, this is not reached */
86624294Smckusick 					/* since there is always a \n */
86724294Smckusick }
86824294Smckusick 
86924294Smckusick /* input file with indexable lines abstract type */
87024294Smckusick 
87124294Smckusick bool using_plan_a = TRUE;
87224294Smckusick static long i_size;			/* size of the input file */
87324294Smckusick static char *i_womp;			/* plan a buffer for entire file */
87424294Smckusick static char **i_ptr;			/* pointers to lines in i_womp */
87524294Smckusick 
87624294Smckusick static int tifd = -1;			/* plan b virtual string array */
87724294Smckusick static char *tibuf[2];			/* plan b buffers */
87824294Smckusick static LINENUM tiline[2] = {-1,-1};	/* 1st line in each buffer */
87924294Smckusick static LINENUM lines_per_buf;		/* how many lines per buffer */
88024294Smckusick static int tireclen;			/* length of records in tmp file */
88124294Smckusick 
88224294Smckusick re_input()
88324294Smckusick {
88424294Smckusick     if (using_plan_a) {
88524294Smckusick 	i_size = 0;
88624294Smckusick 	/*NOSTRICT*/
88724294Smckusick 	if (i_ptr != Null(char**))
88824294Smckusick 	    free((char *)i_ptr);
88924294Smckusick 	if (i_womp != Nullch)
89024294Smckusick 	    free(i_womp);
89124294Smckusick 	i_womp = Nullch;
89224294Smckusick 	i_ptr = Null(char **);
89324294Smckusick     }
89424294Smckusick     else {
89524294Smckusick 	using_plan_a = TRUE;		/* maybe the next one is smaller */
89624294Smckusick 	Close(tifd);
89724294Smckusick 	tifd = -1;
89824294Smckusick 	free(tibuf[0]);
89924294Smckusick 	free(tibuf[1]);
90024294Smckusick 	tibuf[0] = tibuf[1] = Nullch;
90124294Smckusick 	tiline[0] = tiline[1] = -1;
90224294Smckusick 	tireclen = 0;
90324294Smckusick     }
90424294Smckusick }
90524294Smckusick 
90624294Smckusick scan_input(filename)
90724294Smckusick char *filename;
90824294Smckusick {
90924294Smckusick     bool plan_a();
91024294Smckusick 
91124294Smckusick     if (!plan_a(filename))
91224294Smckusick 	plan_b(filename);
91324294Smckusick }
91424294Smckusick 
91524294Smckusick /* try keeping everything in memory */
91624294Smckusick 
91724294Smckusick bool
91824294Smckusick plan_a(filename)
91924294Smckusick char *filename;
92024294Smckusick {
92124294Smckusick     int ifd;
92224294Smckusick     register char *s;
92324294Smckusick     register LINENUM iline;
92424294Smckusick 
92524294Smckusick     if (stat(filename,&filestat) < 0) {
92624294Smckusick 	Sprintf(buf,"RCS/%s%s",filename,RCSSUFFIX);
92724294Smckusick 	if (stat(buf,&filestat) >= 0 || stat(buf+4,&filestat) >= 0) {
92824294Smckusick 	    Sprintf(buf,CHECKOUT,filename);
92924294Smckusick 	    if (verbose)
93024294Smckusick 		say("Can't find %s--attempting to check it out from RCS.\n",
93124294Smckusick 		    filename);
93224294Smckusick 	    if (system(buf) || stat(filename,&filestat))
93324294Smckusick 		fatal("Can't check out %s.\n",filename);
93424294Smckusick 	}
93524294Smckusick 	else {
93624294Smckusick 	    Sprintf(buf,"SCCS/%s%s",SCCSPREFIX,filename);
93724294Smckusick 	    if (stat(buf,&filestat) >= 0 || stat(buf+5,&filestat) >= 0) {
93824294Smckusick 		Sprintf(buf,GET,filename);
93924294Smckusick 		if (verbose)
94024294Smckusick 		    say("Can't find %s--attempting to get it from SCCS.\n",
94124294Smckusick 			filename);
94224294Smckusick 		if (system(buf) || stat(filename,&filestat))
94324294Smckusick 		    fatal("Can't get %s.\n",filename);
94424294Smckusick 	    }
94524294Smckusick 	    else
94624294Smckusick 		fatal("Can't find %s.\n",filename);
94724294Smckusick 	}
94824294Smckusick     }
94924294Smckusick     if ((filestat.st_mode & S_IFMT) & ~S_IFREG)
95024294Smckusick 	fatal("%s is not a normal file--can't patch.\n",filename);
95124294Smckusick     i_size = filestat.st_size;
95224294Smckusick     /*NOSTRICT*/
95324294Smckusick     i_womp = malloc((MEM)(i_size+2));
95424294Smckusick     if (i_womp == Nullch)
95524294Smckusick 	return FALSE;
95624294Smckusick     if ((ifd = open(filename,0)) < 0)
95724294Smckusick 	fatal("Can't open file %s\n",filename);
95824294Smckusick     /*NOSTRICT*/
95924294Smckusick     if (read(ifd,i_womp,(int)i_size) != i_size) {
96024294Smckusick 	Close(ifd);
96124294Smckusick 	free(i_womp);
96224294Smckusick 	return FALSE;
96324294Smckusick     }
96424294Smckusick     Close(ifd);
96524294Smckusick     if (i_womp[i_size-1] != '\n')
96624294Smckusick 	i_womp[i_size++] = '\n';
96724294Smckusick     i_womp[i_size] = '\0';
96824294Smckusick 
96924294Smckusick     /* count the lines in the buffer so we know how many pointers we need */
97024294Smckusick 
97124294Smckusick     iline = 0;
97224294Smckusick     for (s=i_womp; *s; s++) {
97324294Smckusick 	if (*s == '\n')
97424294Smckusick 	    iline++;
97524294Smckusick     }
97624294Smckusick     /*NOSTRICT*/
97724294Smckusick     i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *)));
97824294Smckusick     if (i_ptr == Null(char **)) {	/* shucks, it was a near thing */
97924294Smckusick 	free((char *)i_womp);
98024294Smckusick 	return FALSE;
98124294Smckusick     }
98224294Smckusick 
98324294Smckusick     /* now scan the buffer and build pointer array */
98424294Smckusick 
98524294Smckusick     iline = 1;
98624294Smckusick     i_ptr[iline] = i_womp;
98724294Smckusick     for (s=i_womp; *s; s++) {
98824294Smckusick 	if (*s == '\n')
98924294Smckusick 	    i_ptr[++iline] = s+1;	/* these are NOT null terminated */
99024294Smckusick     }
99124294Smckusick     input_lines = iline - 1;
99224294Smckusick 
99324294Smckusick     /* now check for revision, if any */
99424294Smckusick 
99524294Smckusick     if (revision != Nullch) {
99624294Smckusick 	if (!rev_in_string(i_womp)) {
99724294Smckusick 	    ask("This file doesn't appear to be the %s version--patch anyway? [n] ",
99824294Smckusick 		revision);
99924294Smckusick 	    if (*buf != 'y')
100024294Smckusick 		fatal("Aborted.\n");
100124294Smckusick 	}
100224294Smckusick 	else if (verbose)
100324294Smckusick 	    say("Good.  This file appears to be the %s version.\n",
100424294Smckusick 		revision);
100524294Smckusick     }
100624294Smckusick     return TRUE;			/* plan a will work */
100724294Smckusick }
100824294Smckusick 
100924294Smckusick /* keep (virtually) nothing in memory */
101024294Smckusick 
101124294Smckusick plan_b(filename)
101224294Smckusick char *filename;
101324294Smckusick {
101424294Smckusick     FILE *ifp;
101524294Smckusick     register int i = 0;
101624294Smckusick     register int maxlen = 1;
101724294Smckusick     bool found_revision = (revision == Nullch);
101824294Smckusick 
101924294Smckusick     using_plan_a = FALSE;
102024294Smckusick     if ((ifp = fopen(filename,"r")) == Nullfp)
102124294Smckusick 	fatal("Can't open file %s\n",filename);
102224294Smckusick     if ((tifd = creat(TMPINNAME,0666)) < 0)
102324294Smckusick 	fatal("Can't open file %s\n",TMPINNAME);
102424294Smckusick     while (fgets(buf,sizeof buf, ifp) != Nullch) {
102524294Smckusick 	if (revision != Nullch && !found_revision && rev_in_string(buf))
102624294Smckusick 	    found_revision = TRUE;
102724294Smckusick 	if ((i = strlen(buf)) > maxlen)
102824294Smckusick 	    maxlen = i;			/* find longest line */
102924294Smckusick     }
103024294Smckusick     if (revision != Nullch) {
103124294Smckusick 	if (!found_revision) {
103224294Smckusick 	    ask("This file doesn't appear to be the %s version--patch anyway? [n] ",
103324294Smckusick 		revision);
103424294Smckusick 	    if (*buf != 'y')
103524294Smckusick 		fatal("Aborted.\n");
103624294Smckusick 	}
103724294Smckusick 	else if (verbose)
103824294Smckusick 	    say("Good.  This file appears to be the %s version.\n",
103924294Smckusick 		revision);
104024294Smckusick     }
104124294Smckusick     Fseek(ifp,0L,0);		/* rewind file */
104224294Smckusick     lines_per_buf = BUFFERSIZE / maxlen;
104324294Smckusick     tireclen = maxlen;
104424294Smckusick     tibuf[0] = malloc((MEM)(BUFFERSIZE + 1));
104524294Smckusick     tibuf[1] = malloc((MEM)(BUFFERSIZE + 1));
104624294Smckusick     if (tibuf[1] == Nullch)
104724294Smckusick 	fatal("Can't seem to get enough memory.\n");
104824294Smckusick     for (i=1; ; i++) {
104924294Smckusick 	if (! (i % lines_per_buf))	/* new block */
105024294Smckusick 	    if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE)
105124294Smckusick 		fatal("patch: can't write temp file.\n");
105224294Smckusick 	if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp)
105324294Smckusick 	  == Nullch) {
105424294Smckusick 	    input_lines = i - 1;
105524294Smckusick 	    if (i % lines_per_buf)
105624294Smckusick 		if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE)
105724294Smckusick 		    fatal("patch: can't write temp file.\n");
105824294Smckusick 	    break;
105924294Smckusick 	}
106024294Smckusick     }
106124294Smckusick     Fclose(ifp);
106224294Smckusick     Close(tifd);
106324294Smckusick     if ((tifd = open(TMPINNAME,0)) < 0) {
106424294Smckusick 	fatal("Can't reopen file %s\n",TMPINNAME);
106524294Smckusick     }
106624294Smckusick }
106724294Smckusick 
106824294Smckusick /* fetch a line from the input file, \n terminated, not necessarily \0 */
106924294Smckusick char *
107024294Smckusick ifetch(line,whichbuf)
107124294Smckusick register LINENUM line;
107224294Smckusick int whichbuf;				/* ignored when file in memory */
107324294Smckusick {
107424294Smckusick     if (line < 1 || line > input_lines)
107524294Smckusick 	return "";
107624294Smckusick     if (using_plan_a)
107724294Smckusick 	return i_ptr[line];
107824294Smckusick     else {
107924294Smckusick 	LINENUM offline = line % lines_per_buf;
108024294Smckusick 	LINENUM baseline = line - offline;
108124294Smckusick 
108224294Smckusick 	if (tiline[0] == baseline)
108324294Smckusick 	    whichbuf = 0;
108424294Smckusick 	else if (tiline[1] == baseline)
108524294Smckusick 	    whichbuf = 1;
108624294Smckusick 	else {
108724294Smckusick 	    tiline[whichbuf] = baseline;
108824294Smckusick 	    Lseek(tifd,(long)baseline / lines_per_buf * BUFFERSIZE,0);
108924294Smckusick 	    if (read(tifd,tibuf[whichbuf],BUFFERSIZE) < 0)
109024294Smckusick 		fatal("Error reading tmp file %s.\n",TMPINNAME);
109124294Smckusick 	}
109224294Smckusick 	return tibuf[whichbuf] + (tireclen*offline);
109324294Smckusick     }
109424294Smckusick }
109524294Smckusick 
109624294Smckusick /* patch abstract type */
109724294Smckusick 
109824294Smckusick static long p_filesize;			/* size of the patch file */
109924294Smckusick static LINENUM p_first;			/* 1st line number */
110024294Smckusick static LINENUM p_newfirst;		/* 1st line number of replacement */
110124294Smckusick static LINENUM p_ptrn_lines;		/* # lines in pattern */
110224294Smckusick static LINENUM p_repl_lines;		/* # lines in replacement text */
110324294Smckusick static LINENUM p_end = -1;		/* last line in hunk */
110424294Smckusick static LINENUM p_max;			/* max allowed value of p_end */
110524294Smckusick static LINENUM p_context = 3;		/* # of context lines */
110624294Smckusick static LINENUM p_input_line = 0;	/* current line # from patch file */
110724294Smckusick static char *p_line[MAXHUNKSIZE];	/* the text of the hunk */
110824294Smckusick static char p_char[MAXHUNKSIZE];	/* +, -, and ! */
110924294Smckusick static int p_len[MAXHUNKSIZE];		/* length of each line */
111024294Smckusick static int p_indent;			/* indent to patch */
111124294Smckusick static long p_base;			/* where to intuit this time */
111224294Smckusick static long p_start;			/* where intuit found a patch */
111324294Smckusick 
111424294Smckusick re_patch()
111524294Smckusick {
111624294Smckusick     p_first = (LINENUM)0;
111724294Smckusick     p_newfirst = (LINENUM)0;
111824294Smckusick     p_ptrn_lines = (LINENUM)0;
111924294Smckusick     p_repl_lines = (LINENUM)0;
112024294Smckusick     p_end = (LINENUM)-1;
112124294Smckusick     p_max = (LINENUM)0;
112224294Smckusick     p_indent = 0;
112324294Smckusick }
112424294Smckusick 
112524294Smckusick open_patch_file(filename)
112624294Smckusick char *filename;
112724294Smckusick {
112824294Smckusick     if (filename == Nullch || !*filename || strEQ(filename,"-")) {
112924294Smckusick 	pfp = fopen(TMPPATNAME,"w");
113024294Smckusick 	if (pfp == Nullfp)
113124294Smckusick 	    fatal("patch: can't create %s.\n",TMPPATNAME);
113224294Smckusick 	while (fgets(buf,sizeof buf,stdin) != NULL)
113324294Smckusick 	    fputs(buf,pfp);
113424294Smckusick 	Fclose(pfp);
113524294Smckusick 	filename = TMPPATNAME;
113624294Smckusick     }
113724294Smckusick     pfp = fopen(filename,"r");
113824294Smckusick     if (pfp == Nullfp)
113924294Smckusick 	fatal("patch file %s not found\n",filename);
114024294Smckusick     Fstat(fileno(pfp), &filestat);
114124294Smckusick     p_filesize = filestat.st_size;
114224294Smckusick     next_intuit_at(0L);			/* start at the beginning */
114324294Smckusick }
114424294Smckusick 
114524294Smckusick bool
114624294Smckusick there_is_another_patch()
114724294Smckusick {
114824294Smckusick     bool no_input_file = (filearg[0] == Nullch);
114924294Smckusick 
115024294Smckusick     if (p_base != 0L && p_base >= p_filesize) {
115124294Smckusick 	if (verbose)
115224294Smckusick 	    say("done\n");
115324294Smckusick 	return FALSE;
115424294Smckusick     }
115524294Smckusick     if (verbose)
115624294Smckusick 	say("Hmm...");
115724294Smckusick     diff_type = intuit_diff_type();
115824294Smckusick     if (!diff_type) {
115924294Smckusick 	if (p_base != 0L) {
116024294Smckusick 	    if (verbose)
116124294Smckusick 		say("  Ignoring the trailing garbage.\ndone\n");
116224294Smckusick 	}
116324294Smckusick 	else
116424294Smckusick 	    say("  I can't seem to find a patch in there anywhere.\n");
116524294Smckusick 	return FALSE;
116624294Smckusick     }
116724294Smckusick     if (verbose)
116824294Smckusick 	say("  %sooks like %s to me...\n",
116924294Smckusick 	    (p_base == 0L ? "L" : "The next patch l"),
117024294Smckusick 	    diff_type == CONTEXT_DIFF ? "a context diff" :
1171*24295Smckusick 	    diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
117224294Smckusick 	    diff_type == NORMAL_DIFF ? "a normal diff" :
117324294Smckusick 	    "an ed script" );
117424294Smckusick     if (p_indent && verbose)
117524294Smckusick 	say("(Patch is indented %d space%s.)\n",p_indent,p_indent==1?"":"s");
117624294Smckusick     skip_to(p_start);
117724294Smckusick     if (no_input_file) {
117824294Smckusick 	if (filearg[0] == Nullch) {
117924294Smckusick 	    ask("File to patch: ");
118024294Smckusick 	    filearg[0] = fetchname(buf);
118124294Smckusick 	}
118224294Smckusick 	else if (verbose) {
118324294Smckusick 	    say("Patching file %s...\n",filearg[0]);
118424294Smckusick 	}
118524294Smckusick     }
118624294Smckusick     return TRUE;
118724294Smckusick }
118824294Smckusick 
118924294Smckusick intuit_diff_type()
119024294Smckusick {
119124294Smckusick     long this_line = 0;
119224294Smckusick     long previous_line;
119324294Smckusick     long first_command_line = -1;
119424294Smckusick     bool last_line_was_command = FALSE;
119524294Smckusick     bool this_line_is_command = FALSE;
1196*24295Smckusick     bool last_line_was_stars = FALSE;
1197*24295Smckusick     bool this_line_is_stars = FALSE;
119824294Smckusick     register int indent;
119924294Smckusick     register char *s, *t;
120024294Smckusick     char *oldname = Nullch;
120124294Smckusick     char *newname = Nullch;
120224294Smckusick     bool no_filearg = (filearg[0] == Nullch);
120324294Smckusick 
120424294Smckusick     Fseek(pfp,p_base,0);
120524294Smckusick     for (;;) {
120624294Smckusick 	previous_line = this_line;
120724294Smckusick 	last_line_was_command = this_line_is_command;
1208*24295Smckusick 	last_line_was_stars = this_line_is_stars;
120924294Smckusick 	this_line = ftell(pfp);
121024294Smckusick 	indent = 0;
121124294Smckusick 	if (fgets(buf,sizeof buf,pfp) == Nullch) {
121224294Smckusick 	    if (first_command_line >= 0L) {
121324294Smckusick 					/* nothing but deletes!? */
121424294Smckusick 		p_start = first_command_line;
121524294Smckusick 		return ED_DIFF;
121624294Smckusick 	    }
121724294Smckusick 	    else {
121824294Smckusick 		p_start = this_line;
121924294Smckusick 		return 0;
122024294Smckusick 	    }
122124294Smckusick 	}
122224294Smckusick 	for (s = buf; *s == ' ' || *s == '\t'; s++) {
122324294Smckusick 	    if (*s == '\t')
122424294Smckusick 		indent += 8 - (indent % 8);
122524294Smckusick 	    else
122624294Smckusick 		indent++;
122724294Smckusick 	}
122824294Smckusick 	for (t=s; isdigit(*t) || *t == ','; t++) ;
122924294Smckusick 	this_line_is_command = (isdigit(*s) &&
123024294Smckusick 	  (*t == 'd' || *t == 'c' || *t == 'a') );
123124294Smckusick 	if (first_command_line < 0L && this_line_is_command) {
123224294Smckusick 	    first_command_line = this_line;
123324294Smckusick 	    p_indent = indent;		/* assume this for now */
123424294Smckusick 	}
123524294Smckusick 	if (strnEQ(s,"*** ",4))
123624294Smckusick 	    oldname = fetchname(s+4);
123724294Smckusick 	else if (strnEQ(s,"--- ",4)) {
123824294Smckusick 	    newname = fetchname(s+4);
123924294Smckusick 	    if (no_filearg) {
124024294Smckusick 		if (oldname && newname) {
124124294Smckusick 		    if (strlen(oldname) < strlen(newname))
124224294Smckusick 			filearg[0] = oldname;
124324294Smckusick 		    else
124424294Smckusick 			filearg[0] = newname;
124524294Smckusick 		}
124624294Smckusick 		else if (oldname)
124724294Smckusick 		    filearg[0] = oldname;
124824294Smckusick 		else if (newname)
124924294Smckusick 		    filearg[0] = newname;
125024294Smckusick 	    }
125124294Smckusick 	}
125224294Smckusick 	else if (strnEQ(s,"Index:",6)) {
125324294Smckusick 	    if (no_filearg)
125424294Smckusick 		filearg[0] = fetchname(s+6);
125524294Smckusick 					/* this filearg might get limboed */
125624294Smckusick 	}
125724294Smckusick 	else if (strnEQ(s,"Prereq:",7)) {
125824294Smckusick 	    for (t=s+7; isspace(*t); t++) ;
125924294Smckusick 	    revision = savestr(t);
126024294Smckusick 	    for (t=revision; *t && !isspace(*t); t++) ;
126124294Smckusick 	    *t = '\0';
126224294Smckusick 	    if (!*revision) {
126324294Smckusick 		free(revision);
126424294Smckusick 		revision = Nullch;
126524294Smckusick 	    }
126624294Smckusick 	}
126724294Smckusick 	if ((!diff_type || diff_type == ED_DIFF) &&
126824294Smckusick 	  first_command_line >= 0L &&
126924294Smckusick 	  strEQ(s,".\n") ) {
127024294Smckusick 	    p_indent = indent;
127124294Smckusick 	    p_start = first_command_line;
127224294Smckusick 	    return ED_DIFF;
127324294Smckusick 	}
1274*24295Smckusick 	this_line_is_stars = strnEQ(s,"********",8);
1275*24295Smckusick 	if ((!diff_type || diff_type == CONTEXT_DIFF) && last_line_was_stars &&
1276*24295Smckusick 		 strnEQ(s,"*** ",4)) {
1277*24295Smckusick 	    /* if this is a new context diff the character just before */
1278*24295Smckusick 	    /* the newline is a '*'. */
1279*24295Smckusick 	    while (*s != '\n')
1280*24295Smckusick 		s++;
128124294Smckusick 	    p_indent = indent;
1282*24295Smckusick 	    p_start = previous_line;
1283*24295Smckusick 	    return (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
128424294Smckusick 	}
128524294Smckusick 	if ((!diff_type || diff_type == NORMAL_DIFF) &&
128624294Smckusick 	  last_line_was_command &&
128724294Smckusick 	  (strnEQ(s,"< ",2) || strnEQ(s,"> ",2)) ) {
128824294Smckusick 	    p_start = previous_line;
128924294Smckusick 	    p_indent = indent;
129024294Smckusick 	    return NORMAL_DIFF;
129124294Smckusick 	}
129224294Smckusick     }
129324294Smckusick }
129424294Smckusick 
129524294Smckusick char *
129624294Smckusick fetchname(at)
129724294Smckusick char *at;
129824294Smckusick {
129924294Smckusick     char *s = savestr(at);
130024294Smckusick     char *name;
130124294Smckusick     register char *t;
130224294Smckusick     char tmpbuf[200];
130324294Smckusick 
130424294Smckusick     for (t=s; isspace(*t); t++) ;
130524294Smckusick     name = t;
130624294Smckusick     for (; *t && !isspace(*t); t++)
130724294Smckusick 	if (!usepath)
130824294Smckusick 	    if (*t == '/')
130924294Smckusick 		name = t+1;
131024294Smckusick     *t = '\0';
131124294Smckusick     name = savestr(name);
131224294Smckusick     Sprintf(tmpbuf,"RCS/%s",name);
131324294Smckusick     free(s);
131424294Smckusick     if (stat(name,&filestat) < 0) {
131524294Smckusick 	Strcat(tmpbuf,RCSSUFFIX);
131624294Smckusick 	if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+4,&filestat) < 0) {
131724294Smckusick 	    Sprintf(tmpbuf,"SCCS/%s%s",SCCSPREFIX,name);
131824294Smckusick 	    if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+5,&filestat) < 0) {
131924294Smckusick 		free(name);
132024294Smckusick 		name = Nullch;
132124294Smckusick 	    }
132224294Smckusick 	}
132324294Smckusick     }
132424294Smckusick     return name;
132524294Smckusick }
132624294Smckusick 
132724294Smckusick next_intuit_at(file_pos)
132824294Smckusick long file_pos;
132924294Smckusick {
133024294Smckusick     p_base = file_pos;
133124294Smckusick }
133224294Smckusick 
133324294Smckusick skip_to(file_pos)
133424294Smckusick long file_pos;
133524294Smckusick {
133624294Smckusick     char *ret;
133724294Smckusick 
133824294Smckusick     assert(p_base <= file_pos);
133924294Smckusick     if (verbose && p_base < file_pos) {
134024294Smckusick 	Fseek(pfp,p_base,0);
134124294Smckusick 	say("The text leading up to this was:\n--------------------------\n");
134224294Smckusick 	while (ftell(pfp) < file_pos) {
134324294Smckusick 	    ret = fgets(buf,sizeof buf,pfp);
134424294Smckusick 	    assert(ret != Nullch);
134524294Smckusick 	    say("|%s",buf);
134624294Smckusick 	}
134724294Smckusick 	say("--------------------------\n");
134824294Smckusick     }
134924294Smckusick     else
135024294Smckusick 	Fseek(pfp,file_pos,0);
135124294Smckusick }
135224294Smckusick 
135324294Smckusick bool
135424294Smckusick another_hunk()
135524294Smckusick {
135624294Smckusick     register char *s;
135724294Smckusick     char *ret;
1358*24295Smckusick     register int context = 0;
135924294Smckusick 
136024294Smckusick     while (p_end >= 0) {
136124294Smckusick 	free(p_line[p_end--]);
136224294Smckusick     }
136324294Smckusick     assert(p_end == -1);
136424294Smckusick 
136524294Smckusick     p_max = MAXHUNKSIZE;		/* gets reduced when --- found */
136624294Smckusick     if (diff_type == CONTEXT_DIFF) {
136724294Smckusick 	long line_beginning = ftell(pfp);
136824294Smckusick 	LINENUM repl_beginning = 0;
136924294Smckusick 
137024294Smckusick 	ret = pgets(buf,sizeof buf, pfp);
137124294Smckusick 	if (ret == Nullch || strnNE(buf,"********",8)) {
137224294Smckusick 	    next_intuit_at(line_beginning);
137324294Smckusick 	    return FALSE;
137424294Smckusick 	}
137524294Smckusick 	p_context = 100;
137624294Smckusick 	while (p_end < p_max) {
137724294Smckusick 	    ret = pgets(buf,sizeof buf, pfp);
137824294Smckusick 	    if (ret == Nullch) {
137924294Smckusick 		if (p_max - p_end < 4)
138024294Smckusick 		    Strcpy(buf,"  \n");	/* assume blank lines got chopped */
138124294Smckusick 		else
138224294Smckusick 		    fatal("Unexpected end of file in patch.\n");
138324294Smckusick 	    }
138424294Smckusick 	    p_input_line++;
138524294Smckusick 	    if (strnEQ(buf,"********",8))
138624294Smckusick 		fatal("Unexpected end of hunk at line %d.\n",
138724294Smckusick 		    p_input_line);
138824294Smckusick 	    p_char[++p_end] = *buf;
138924294Smckusick 	    switch (*buf) {
139024294Smckusick 	    case '*':
139124294Smckusick 		if (p_end != 0)
139224294Smckusick 		    fatal("Unexpected *** at line %d: %s", p_input_line, buf);
139324294Smckusick 		context = 0;
139424294Smckusick 		p_line[p_end] = savestr(buf);
139524294Smckusick 		for (s=buf; *s && !isdigit(*s); s++) ;
139624294Smckusick 		p_first = (LINENUM) atol(s);
139724294Smckusick 		while (isdigit(*s)) s++;
139824294Smckusick 		for (; *s && !isdigit(*s); s++) ;
139924294Smckusick 		p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1;
140024294Smckusick 		break;
140124294Smckusick 	    case '-':
140224294Smckusick 		if (buf[1] == '-') {
140324294Smckusick 		    if (p_end != p_ptrn_lines + 1 &&
140424294Smckusick 			p_end != p_ptrn_lines + 2)
140524294Smckusick 			fatal("Unexpected --- at line %d: %s",
140624294Smckusick 			    p_input_line,buf);
140724294Smckusick 		    repl_beginning = p_end;
140824294Smckusick 		    context = 0;
140924294Smckusick 		    p_line[p_end] = savestr(buf);
141024294Smckusick 		    p_char[p_end] = '=';
141124294Smckusick 		    for (s=buf; *s && !isdigit(*s); s++) ;
141224294Smckusick 		    p_newfirst = (LINENUM) atol(s);
141324294Smckusick 		    while (isdigit(*s)) s++;
141424294Smckusick 		    for (; *s && !isdigit(*s); s++) ;
141524294Smckusick 		    p_max = ((LINENUM)atol(s)) - p_newfirst + 1 + p_end;
141624294Smckusick 		    break;
141724294Smckusick 		}
141824294Smckusick 		/* FALL THROUGH */
141924294Smckusick 	    case '+': case '!':
142024294Smckusick 		if (context > 0) {
142124294Smckusick 		    if (context < p_context)
142224294Smckusick 			p_context = context;
142324294Smckusick 		    context = -100;
142424294Smckusick 		}
142524294Smckusick 		p_line[p_end] = savestr(buf+2);
142624294Smckusick 		break;
142724294Smckusick 	    case '\t': case '\n':	/* assume the 2 spaces got eaten */
142824294Smckusick 		p_line[p_end] = savestr(buf);
142924294Smckusick 		if (p_end != p_ptrn_lines + 1) {
143024294Smckusick 		    context++;
143124294Smckusick 		    p_char[p_end] = ' ';
143224294Smckusick 		}
143324294Smckusick 		break;
143424294Smckusick 	    case ' ':
143524294Smckusick 		context++;
143624294Smckusick 		p_line[p_end] = savestr(buf+2);
143724294Smckusick 		break;
143824294Smckusick 	    default:
143924294Smckusick 		fatal("Malformed patch at line %d: %s",p_input_line,buf);
144024294Smckusick 	    }
144124294Smckusick 	    p_len[p_end] = strlen(p_line[p_end]);
144224294Smckusick 					/* for strncmp() so we do not have */
144324294Smckusick 					/* to assume null termination */
144424294Smckusick 	}
144524294Smckusick 	if (p_end >=0 && !p_ptrn_lines)
144624294Smckusick 	    fatal("No --- found in patch at line %d\n", pch_hunk_beg());
144724294Smckusick 	p_repl_lines = p_end - repl_beginning;
144824294Smckusick     }
1449*24295Smckusick     else if (diff_type == NEW_CONTEXT_DIFF) {
1450*24295Smckusick 	long line_beginning = ftell(pfp);
1451*24295Smckusick 	LINENUM repl_beginning = 0;
1452*24295Smckusick 	LINENUM fillcnt = 0;
1453*24295Smckusick 	LINENUM fillsrc;
1454*24295Smckusick 	LINENUM filldst;
1455*24295Smckusick 
1456*24295Smckusick 	ret = pgets(buf,sizeof buf, pfp);
1457*24295Smckusick 	if (ret == Nullch || strnNE(buf,"********",8)) {
1458*24295Smckusick 	    next_intuit_at(line_beginning);
1459*24295Smckusick 	    return FALSE;
1460*24295Smckusick 	}
1461*24295Smckusick 	p_context = 0;
1462*24295Smckusick 	while (p_end < p_max) {
1463*24295Smckusick 	    ret = pgets(buf,sizeof buf, pfp);
1464*24295Smckusick 	    if (ret == Nullch) {
1465*24295Smckusick 		if (p_max - p_end < 4)
1466*24295Smckusick 		    Strcpy(buf,"  \n");	/* assume blank lines got chopped */
1467*24295Smckusick 		else
1468*24295Smckusick 		    fatal("Unexpected end of file in patch.\n");
1469*24295Smckusick 	    }
1470*24295Smckusick 	    p_input_line++;
1471*24295Smckusick 	    p_char[++p_end] = *buf;
1472*24295Smckusick 	    switch (*buf) {
1473*24295Smckusick 	    case '*':
1474*24295Smckusick 		if (strnEQ(buf,"********",8)) {
1475*24295Smckusick 		    if (p_end != repl_beginning + 1)
1476*24295Smckusick 			fatal("Unexpected end of hunk at line %d.\n",
1477*24295Smckusick 				p_input_line);
1478*24295Smckusick 		    /* redundant 'new' context lines were omitted - set up */
1479*24295Smckusick 		    /* to fill them in from the the old file's context */
1480*24295Smckusick 		    fillsrc = 1;
1481*24295Smckusick 		    filldst = p_end;
1482*24295Smckusick 		    fillcnt = p_max - p_end;
1483*24295Smckusick 		    p_end = p_max;
1484*24295Smckusick 		    break;
1485*24295Smckusick 		}
1486*24295Smckusick 		if (p_end != 0)
1487*24295Smckusick 		    fatal("Unexpected *** at line %d: %s", p_input_line, buf);
1488*24295Smckusick 		context = 0;
1489*24295Smckusick 		p_line[p_end] = savestr(buf);
1490*24295Smckusick 		for (s=buf; *s && !isdigit(*s); s++) ;
1491*24295Smckusick 		p_first = (LINENUM) atol(s);
1492*24295Smckusick 		while (isdigit(*s)) s++;
1493*24295Smckusick 		for (; *s && !isdigit(*s); s++) ;
1494*24295Smckusick 		p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1;
1495*24295Smckusick 		break;
1496*24295Smckusick 	    case '-':
1497*24295Smckusick 		if (buf[1] == '-') {
1498*24295Smckusick 		    if (p_end != p_ptrn_lines + 1) {
1499*24295Smckusick 			if (p_end == 1) {
1500*24295Smckusick 			    /* `old' lines were omitted - set up to fill them */
1501*24295Smckusick 			    /* in from 'new' context lines. */
1502*24295Smckusick 			    p_end = p_ptrn_lines + 1;
1503*24295Smckusick 			    fillsrc = p_end + 1;
1504*24295Smckusick 			    filldst = 1;
1505*24295Smckusick 			    fillcnt = p_ptrn_lines;
1506*24295Smckusick 			} else
1507*24295Smckusick 			    fatal("Unexpected --- at line %d: %s",
1508*24295Smckusick 				p_input_line,buf);
1509*24295Smckusick 		    }
1510*24295Smckusick 		    repl_beginning = p_end;
1511*24295Smckusick 		    p_line[p_end] = savestr(buf);
1512*24295Smckusick 		    p_char[p_end] = '=';
1513*24295Smckusick 		    for (s=buf; *s && !isdigit(*s); s++) ;
1514*24295Smckusick 		    p_newfirst = (LINENUM) atol(s);
1515*24295Smckusick 		    while (isdigit(*s)) s++;
1516*24295Smckusick 		    for (; *s && !isdigit(*s); s++) ;
1517*24295Smckusick 		    p_max = ((LINENUM)atol(s)) - p_newfirst + 1 + p_end;
1518*24295Smckusick 		    break;
1519*24295Smckusick 		}
1520*24295Smckusick 		/* FALL THROUGH */
1521*24295Smckusick 	    case '+': case '!':
1522*24295Smckusick 		if (context > 0 && p_context == 0) {
1523*24295Smckusick 		    p_context = context;
1524*24295Smckusick 		}
1525*24295Smckusick 		p_line[p_end] = savestr(buf+2);
1526*24295Smckusick 		break;
1527*24295Smckusick 	    case '\t': case '\n':	/* assume the 2 spaces got eaten */
1528*24295Smckusick 		p_line[p_end] = savestr(buf);
1529*24295Smckusick 		if (p_end != p_ptrn_lines + 1) {
1530*24295Smckusick 		    context++;
1531*24295Smckusick 		    p_char[p_end] = ' ';
1532*24295Smckusick 		}
1533*24295Smckusick 		break;
1534*24295Smckusick 	    case ' ':
1535*24295Smckusick 		context++;
1536*24295Smckusick 		p_line[p_end] = savestr(buf+2);
1537*24295Smckusick 		break;
1538*24295Smckusick 	    default:
1539*24295Smckusick 		fatal("Malformed patch at line %d: %s",p_input_line,buf);
1540*24295Smckusick 	    }
1541*24295Smckusick 	    p_len[p_end] = strlen(p_line[p_end]);
1542*24295Smckusick 					/* for strncmp() so we do not have */
1543*24295Smckusick 					/* to assume null termination */
1544*24295Smckusick 	}
1545*24295Smckusick 	if (p_end >=0 && !p_ptrn_lines)
1546*24295Smckusick 	    fatal("No --- found in patch at line %d\n", pch_hunk_beg());
1547*24295Smckusick 
1548*24295Smckusick 	/* if there were omitted context lines, fill them in */
1549*24295Smckusick 	if (fillcnt) {
1550*24295Smckusick 	    while (fillcnt-- > 0) {
1551*24295Smckusick 		while (p_char[fillsrc] != ' ')
1552*24295Smckusick 		    fillsrc++;
1553*24295Smckusick 		p_line[filldst] = p_line[fillsrc];
1554*24295Smckusick 		p_char[filldst] = p_char[fillsrc];
1555*24295Smckusick 		p_len[filldst] = p_len[fillsrc];
1556*24295Smckusick 		fillsrc++; filldst++;
1557*24295Smckusick 	    }
1558*24295Smckusick 	    assert(fillsrc==p_end+1 || fillsrc==repl_beginning);
1559*24295Smckusick 	    assert(filldst==p_end+1 || filldst==repl_beginning);
1560*24295Smckusick 	}
1561*24295Smckusick 	p_repl_lines = p_end - repl_beginning;
1562*24295Smckusick     }
156324294Smckusick     else {				/* normal diff--fake it up */
156424294Smckusick 	char hunk_type;
156524294Smckusick 	register int i;
156624294Smckusick 	LINENUM min, max;
156724294Smckusick 	long line_beginning = ftell(pfp);
156824294Smckusick 
156924294Smckusick 	p_context = 0;
157024294Smckusick 	ret = pgets(buf,sizeof buf, pfp);
157124294Smckusick 	p_input_line++;
157224294Smckusick 	if (ret == Nullch || !isdigit(*buf)) {
157324294Smckusick 	    next_intuit_at(line_beginning);
157424294Smckusick 	    return FALSE;
157524294Smckusick 	}
157624294Smckusick 	p_first = (LINENUM)atol(buf);
157724294Smckusick 	for (s=buf; isdigit(*s); s++) ;
157824294Smckusick 	if (*s == ',') {
157924294Smckusick 	    p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1;
158024294Smckusick 	    while (isdigit(*s)) s++;
158124294Smckusick 	}
158224294Smckusick 	else
158324294Smckusick 	    p_ptrn_lines = (*s != 'a');
158424294Smckusick 	hunk_type = *s;
158524294Smckusick 	if (hunk_type == 'a')
158624294Smckusick 	    p_first++;			/* do append rather than insert */
158724294Smckusick 	min = (LINENUM)atol(++s);
158824294Smckusick 	for (; isdigit(*s); s++) ;
158924294Smckusick 	if (*s == ',')
159024294Smckusick 	    max = (LINENUM)atol(++s);
159124294Smckusick 	else
159224294Smckusick 	    max = min;
159324294Smckusick 	if (hunk_type == 'd')
159424294Smckusick 	    min++;
159524294Smckusick 	p_end = p_ptrn_lines + 1 + max - min + 1;
159624294Smckusick 	p_newfirst = min;
159724294Smckusick 	p_repl_lines = max - min + 1;
159824294Smckusick 	Sprintf(buf,"*** %d,%d\n", p_first, p_first + p_ptrn_lines - 1);
159924294Smckusick 	p_line[0] = savestr(buf);
160024294Smckusick 	p_char[0] = '*';
160124294Smckusick 	for (i=1; i<=p_ptrn_lines; i++) {
160224294Smckusick 	    ret = pgets(buf,sizeof buf, pfp);
160324294Smckusick 	    p_input_line++;
160424294Smckusick 	    if (ret == Nullch)
160524294Smckusick 		fatal("Unexpected end of file in patch at line %d.\n",
160624294Smckusick 		  p_input_line);
160724294Smckusick 	    if (*buf != '<')
160824294Smckusick 		fatal("< expected at line %d of patch.\n", p_input_line);
160924294Smckusick 	    p_line[i] = savestr(buf+2);
161024294Smckusick 	    p_len[i] = strlen(p_line[i]);
161124294Smckusick 	    p_char[i] = '-';
161224294Smckusick 	}
161324294Smckusick 	if (hunk_type == 'c') {
161424294Smckusick 	    ret = pgets(buf,sizeof buf, pfp);
161524294Smckusick 	    p_input_line++;
161624294Smckusick 	    if (ret == Nullch)
161724294Smckusick 		fatal("Unexpected end of file in patch at line %d.\n",
161824294Smckusick 		    p_input_line);
161924294Smckusick 	    if (*buf != '-')
162024294Smckusick 		fatal("--- expected at line %d of patch.\n", p_input_line);
162124294Smckusick 	}
162224294Smckusick 	Sprintf(buf,"--- %d,%d\n",min,max);
162324294Smckusick 	p_line[i] = savestr(buf);
162424294Smckusick 	p_char[i] = '=';
162524294Smckusick 	for (i++; i<=p_end; i++) {
162624294Smckusick 	    ret = pgets(buf,sizeof buf, pfp);
162724294Smckusick 	    p_input_line++;
162824294Smckusick 	    if (ret == Nullch)
162924294Smckusick 		fatal("Unexpected end of file in patch at line %d.\n",
163024294Smckusick 		    p_input_line);
163124294Smckusick 	    if (*buf != '>')
163224294Smckusick 		fatal("> expected at line %d of patch.\n", p_input_line);
163324294Smckusick 	    p_line[i] = savestr(buf+2);
163424294Smckusick 	    p_len[i] = strlen(p_line[i]);
163524294Smckusick 	    p_char[i] = '+';
163624294Smckusick 	}
163724294Smckusick     }
163824294Smckusick     if (reverse)			/* backwards patch? */
163924294Smckusick 	pch_swap();
164024294Smckusick #ifdef DEBUGGING
164124294Smckusick     if (debug & 2) {
164224294Smckusick 	int i;
164324294Smckusick 	char special;
164424294Smckusick 
164524294Smckusick 	for (i=0; i <= p_end; i++) {
164624294Smckusick 	    if (i == p_ptrn_lines)
164724294Smckusick 		special = '^';
164824294Smckusick 	    else
164924294Smckusick 		special = ' ';
165024294Smckusick 	    printf("%3d %c %c %s",i,p_char[i],special,p_line[i]);
165124294Smckusick 	}
165224294Smckusick     }
165324294Smckusick #endif
165424294Smckusick     return TRUE;
165524294Smckusick }
165624294Smckusick 
165724294Smckusick char *
165824294Smckusick pgets(bf,sz,fp)
165924294Smckusick char *bf;
166024294Smckusick int sz;
166124294Smckusick FILE *fp;
166224294Smckusick {
166324294Smckusick     char *ret = fgets(bf,sz,fp);
166424294Smckusick     register char *s;
166524294Smckusick     register int indent = 0;
166624294Smckusick 
166724294Smckusick     if (p_indent && ret != Nullch) {
166824294Smckusick 	for (s=buf; indent < p_indent && (*s == ' ' || *s == '\t'); s++) {
166924294Smckusick 	    if (*s == '\t')
167024294Smckusick 		indent += 8 - (indent % 7);
167124294Smckusick 	    else
167224294Smckusick 		indent++;
167324294Smckusick 	}
167424294Smckusick 	if (buf != s)
167524294Smckusick 	    Strcpy(buf,s);
167624294Smckusick     }
167724294Smckusick     return ret;
167824294Smckusick }
167924294Smckusick 
168024294Smckusick pch_swap()
168124294Smckusick {
168224294Smckusick     char *tp_line[MAXHUNKSIZE];		/* the text of the hunk */
168324294Smckusick     char tp_char[MAXHUNKSIZE];		/* +, -, and ! */
168424294Smckusick     int tp_len[MAXHUNKSIZE];		/* length of each line */
168524294Smckusick     register LINENUM i, n;
168624294Smckusick     bool blankline = FALSE;
168724294Smckusick     register char *s;
168824294Smckusick 
168924294Smckusick     i = p_first;
169024294Smckusick     p_first = p_newfirst;
169124294Smckusick     p_newfirst = i;
169224294Smckusick 
169324294Smckusick     /* make a scratch copy */
169424294Smckusick 
169524294Smckusick     for (i=0; i<=p_end; i++) {
169624294Smckusick 	tp_line[i] = p_line[i];
169724294Smckusick 	tp_char[i] = p_char[i];
169824294Smckusick 	tp_len[i] = p_len[i];
169924294Smckusick     }
170024294Smckusick 
170124294Smckusick     /* now turn the new into the old */
170224294Smckusick 
170324294Smckusick     i = p_ptrn_lines + 1;
170424294Smckusick     if (tp_char[i] == '\n') {		/* account for possible blank line */
170524294Smckusick 	blankline = TRUE;
170624294Smckusick 	i++;
170724294Smckusick     }
170824294Smckusick     for (n=0; i <= p_end; i++,n++) {
170924294Smckusick 	p_line[n] = tp_line[i];
171024294Smckusick 	p_char[n] = tp_char[i];
171124294Smckusick 	if (p_char[n] == '+')
171224294Smckusick 	    p_char[n] = '-';
171324294Smckusick 	p_len[n] = tp_len[i];
171424294Smckusick     }
171524294Smckusick     if (blankline) {
171624294Smckusick 	i = p_ptrn_lines + 1;
171724294Smckusick 	p_line[n] = tp_line[i];
171824294Smckusick 	p_char[n] = tp_char[i];
171924294Smckusick 	p_len[n] = tp_len[i];
172024294Smckusick 	n++;
172124294Smckusick     }
172224294Smckusick     assert(p_char[0] == '=');
172324294Smckusick     p_char[0] = '*';
172424294Smckusick     for (s=p_line[0]; *s; s++)
172524294Smckusick 	if (*s == '-')
172624294Smckusick 	    *s = '*';
172724294Smckusick 
172824294Smckusick     /* now turn the old into the new */
172924294Smckusick 
173024294Smckusick     assert(tp_char[0] == '*');
173124294Smckusick     tp_char[0] = '=';
173224294Smckusick     for (s=tp_line[0]; *s; s++)
173324294Smckusick 	if (*s == '*')
173424294Smckusick 	    *s = '-';
173524294Smckusick     for (i=0; n <= p_end; i++,n++) {
173624294Smckusick 	p_line[n] = tp_line[i];
173724294Smckusick 	p_char[n] = tp_char[i];
173824294Smckusick 	if (p_char[n] == '-')
173924294Smckusick 	    p_char[n] = '+';
174024294Smckusick 	p_len[n] = tp_len[i];
174124294Smckusick     }
174224294Smckusick     assert(i == p_ptrn_lines + 1);
174324294Smckusick     i = p_ptrn_lines;
174424294Smckusick     p_ptrn_lines = p_repl_lines;
174524294Smckusick     p_repl_lines = i;
174624294Smckusick }
174724294Smckusick 
174824294Smckusick LINENUM
174924294Smckusick pch_first()
175024294Smckusick {
175124294Smckusick     return p_first;
175224294Smckusick }
175324294Smckusick 
175424294Smckusick LINENUM
175524294Smckusick pch_ptrn_lines()
175624294Smckusick {
175724294Smckusick     return p_ptrn_lines;
175824294Smckusick }
175924294Smckusick 
176024294Smckusick LINENUM
176124294Smckusick pch_newfirst()
176224294Smckusick {
176324294Smckusick     return p_newfirst;
176424294Smckusick }
176524294Smckusick 
176624294Smckusick LINENUM
176724294Smckusick pch_repl_lines()
176824294Smckusick {
176924294Smckusick     return p_repl_lines;
177024294Smckusick }
177124294Smckusick 
177224294Smckusick LINENUM
177324294Smckusick pch_end()
177424294Smckusick {
177524294Smckusick     return p_end;
177624294Smckusick }
177724294Smckusick 
177824294Smckusick LINENUM
177924294Smckusick pch_context()
178024294Smckusick {
178124294Smckusick     return p_context;
178224294Smckusick }
178324294Smckusick 
178424294Smckusick pch_line_len(line)
178524294Smckusick LINENUM line;
178624294Smckusick {
178724294Smckusick     return p_len[line];
178824294Smckusick }
178924294Smckusick 
179024294Smckusick char
179124294Smckusick pch_char(line)
179224294Smckusick LINENUM line;
179324294Smckusick {
179424294Smckusick     return p_char[line];
179524294Smckusick }
179624294Smckusick 
179724294Smckusick char *
179824294Smckusick pfetch(line)
179924294Smckusick LINENUM line;
180024294Smckusick {
180124294Smckusick     return p_line[line];
180224294Smckusick }
180324294Smckusick 
180424294Smckusick LINENUM
180524294Smckusick pch_hunk_beg()
180624294Smckusick {
180724294Smckusick     return p_input_line - p_end - 1;
180824294Smckusick }
180924294Smckusick 
181024294Smckusick char *
181124294Smckusick savestr(s)
181224294Smckusick register char *s;
181324294Smckusick {
181424294Smckusick     register char  *rv,
181524294Smckusick                    *t;
181624294Smckusick 
181724294Smckusick     t = s;
181824294Smckusick     while (*t++);
181924294Smckusick     rv = malloc((MEM) (t - s));
182024294Smckusick     if (rv == NULL)
182124294Smckusick 	fatal ("patch: out of memory (savestr)\n");
182224294Smckusick     t = rv;
182324294Smckusick     while (*t++ = *s++);
182424294Smckusick     return rv;
182524294Smckusick }
182624294Smckusick 
182724294Smckusick my_exit(status)
182824294Smckusick int status;
182924294Smckusick {
183024294Smckusick     Unlink(TMPINNAME);
183124294Smckusick     Unlink(TMPOUTNAME);
183224294Smckusick     Unlink(TMPREJNAME);
183324294Smckusick     Unlink(TMPPATNAME);
183424294Smckusick     exit(status);
183524294Smckusick }
183624294Smckusick 
183724294Smckusick #ifdef lint
183824294Smckusick 
183924294Smckusick /*VARARGS ARGSUSED*/
184024294Smckusick say(pat) char *pat; { ; }
184124294Smckusick /*VARARGS ARGSUSED*/
184224294Smckusick fatal(pat) char *pat; { ; }
184324294Smckusick /*VARARGS ARGSUSED*/
184424294Smckusick ask(pat) char *pat; { ; }
184524294Smckusick 
184624294Smckusick #else lint
184724294Smckusick 
184824294Smckusick say(pat,arg1,arg2,arg3)
184924294Smckusick char *pat;
185024294Smckusick int arg1,arg2,arg3;
185124294Smckusick {
185224294Smckusick     fprintf(stderr,pat,arg1,arg2,arg3);
185324294Smckusick     Fflush(stderr);
185424294Smckusick }
185524294Smckusick 
185624294Smckusick fatal(pat,arg1,arg2,arg3)
185724294Smckusick char *pat;
185824294Smckusick int arg1,arg2,arg3;
185924294Smckusick {
186024294Smckusick     say(pat,arg1,arg2,arg3);
186124294Smckusick     my_exit(1);
186224294Smckusick }
186324294Smckusick 
186424294Smckusick ask(pat,arg1,arg2,arg3)
186524294Smckusick char *pat;
186624294Smckusick int arg1,arg2,arg3;
186724294Smckusick {
186824294Smckusick     int ttyfd = open("/dev/tty",2);
186924294Smckusick     int r;
187024294Smckusick 
187124294Smckusick     say(pat,arg1,arg2,arg3);
187224294Smckusick     if (ttyfd >= 0) {
187324294Smckusick 	r = read(ttyfd, buf, sizeof buf);
187424294Smckusick 	Close(ttyfd);
187524294Smckusick     }
187624294Smckusick     else
187724294Smckusick 	r = read(2, buf, sizeof buf);
187824294Smckusick     if (r <= 0)
187924294Smckusick 	buf[0] = 0;
188024294Smckusick }
188124294Smckusick #endif lint
188224294Smckusick 
188324294Smckusick bool
188424294Smckusick rev_in_string(string)
188524294Smckusick char *string;
188624294Smckusick {
188724294Smckusick     register char *s;
188824294Smckusick     register int patlen;
188924294Smckusick 
189024294Smckusick     if (revision == Nullch)
189124294Smckusick 	return TRUE;
189224294Smckusick     patlen = strlen(revision);
189324294Smckusick     for (s = string; *s; s++) {
189424294Smckusick 	if (isspace(*s) && strnEQ(s+1,revision,patlen) &&
189524294Smckusick 		isspace(s[patlen+1] )) {
189624294Smckusick 	    return TRUE;
189724294Smckusick 	}
189824294Smckusick     }
189924294Smckusick     return FALSE;
190024294Smckusick }
190124294Smckusick 
190224294Smckusick set_signals()
190324294Smckusick {
190424294Smckusick     /*NOSTRICT*/
190524294Smckusick     if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
190624294Smckusick 	Signal(SIGHUP, my_exit);
190724294Smckusick     /*NOSTRICT*/
190824294Smckusick     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
190924294Smckusick 	Signal(SIGINT, my_exit);
191024294Smckusick }
191124294Smckusick 
191224294Smckusick ignore_signals()
191324294Smckusick {
191424294Smckusick     /*NOSTRICT*/
191524294Smckusick     Signal(SIGHUP, SIG_IGN);
191624294Smckusick     /*NOSTRICT*/
191724294Smckusick     Signal(SIGINT, SIG_IGN);
191824294Smckusick }
1919