xref: /openbsd-src/usr.bin/diff/diffreg.c (revision 0c9f493eda33a1be8b85995ddecb0196ef2f949c)
1*0c9f493eStedu /*	$OpenBSD: diffreg.c,v 1.4 2003/06/25 03:25:29 tedu Exp $	*/
2d0c3f575Sderaadt 
3d0c3f575Sderaadt /*
4d0c3f575Sderaadt  * Copyright (C) Caldera International Inc.  2001-2002.
5d0c3f575Sderaadt  * All rights reserved.
6d0c3f575Sderaadt  *
7d0c3f575Sderaadt  * Redistribution and use in source and binary forms, with or without
8d0c3f575Sderaadt  * modification, are permitted provided that the following conditions
9d0c3f575Sderaadt  * are met:
10d0c3f575Sderaadt  * 1. Redistributions of source code and documentation must retain the above
11d0c3f575Sderaadt  *    copyright notice, this list of conditions and the following disclaimer.
12d0c3f575Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
13d0c3f575Sderaadt  *    notice, this list of conditions and the following disclaimer in the
14d0c3f575Sderaadt  *    documentation and/or other materials provided with the distribution.
15d0c3f575Sderaadt  * 3. All advertising materials mentioning features or use of this software
16d0c3f575Sderaadt  *    must display the following acknowledgement:
17d0c3f575Sderaadt  *	This product includes software developed or owned by Caldera
18d0c3f575Sderaadt  *	International, Inc.
19d0c3f575Sderaadt  * 4. Neither the name of Caldera International, Inc. nor the names of other
20d0c3f575Sderaadt  *    contributors may be used to endorse or promote products derived from
21d0c3f575Sderaadt  *    this software without specific prior written permission.
22d0c3f575Sderaadt  *
23d0c3f575Sderaadt  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
24d0c3f575Sderaadt  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
25d0c3f575Sderaadt  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26d0c3f575Sderaadt  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27d0c3f575Sderaadt  * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
28d0c3f575Sderaadt  * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29d0c3f575Sderaadt  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30d0c3f575Sderaadt  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31d0c3f575Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32d0c3f575Sderaadt  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33d0c3f575Sderaadt  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34d0c3f575Sderaadt  * POSSIBILITY OF SUCH DAMAGE.
35d0c3f575Sderaadt  */
36d0c3f575Sderaadt 
3726da422aStedu #include <sys/types.h>
3826da422aStedu 
3926da422aStedu #include <stdlib.h>
4026da422aStedu #include <unistd.h>
4126da422aStedu #include <fcntl.h>
4226da422aStedu #include <string.h>
43ae8d569bSderaadt 
44ae8d569bSderaadt #include "diff.h"
45ae8d569bSderaadt #include "pathnames.h"
4626da422aStedu 
4726da422aStedu #if 0
4826da422aStedu static char const sccsid[] = "@(#)diffreg.c 4.21 4/6/90";
4926da422aStedu #endif
5026da422aStedu 
51ae8d569bSderaadt /*
52ae8d569bSderaadt  * diff - compare two files.
53ae8d569bSderaadt  */
54ae8d569bSderaadt 
55ae8d569bSderaadt /*
56ae8d569bSderaadt  *	Uses an algorithm due to Harold Stone, which finds
57ae8d569bSderaadt  *	a pair of longest identical subsequences in the two
58ae8d569bSderaadt  *	files.
59ae8d569bSderaadt  *
60ae8d569bSderaadt  *	The major goal is to generate the match vector J.
61ae8d569bSderaadt  *	J[i] is the index of the line in file1 corresponding
62ae8d569bSderaadt  *	to line i file0. J[i] = 0 if there is no
63ae8d569bSderaadt  *	such line in file1.
64ae8d569bSderaadt  *
65ae8d569bSderaadt  *	Lines are hashed so as to work in core. All potential
66ae8d569bSderaadt  *	matches are located by sorting the lines of each file
67ae8d569bSderaadt  *	on the hash (called ``value''). In particular, this
68ae8d569bSderaadt  *	collects the equivalence classes in file1 together.
69ae8d569bSderaadt  *	Subroutine equiv replaces the value of each line in
70ae8d569bSderaadt  *	file0 by the index of the first element of its
71ae8d569bSderaadt  *	matching equivalence in (the reordered) file1.
72ae8d569bSderaadt  *	To save space equiv squeezes file1 into a single
73ae8d569bSderaadt  *	array member in which the equivalence classes
74ae8d569bSderaadt  *	are simply concatenated, except that their first
75ae8d569bSderaadt  *	members are flagged by changing sign.
76ae8d569bSderaadt  *
77ae8d569bSderaadt  *	Next the indices that point into member are unsorted into
78ae8d569bSderaadt  *	array class according to the original order of file0.
79ae8d569bSderaadt  *
80ae8d569bSderaadt  *	The cleverness lies in routine stone. This marches
81ae8d569bSderaadt  *	through the lines of file0, developing a vector klist
82ae8d569bSderaadt  *	of "k-candidates". At step i a k-candidate is a matched
83ae8d569bSderaadt  *	pair of lines x,y (x in file0 y in file1) such that
84ae8d569bSderaadt  *	there is a common subsequence of length k
85ae8d569bSderaadt  *	between the first i lines of file0 and the first y
86ae8d569bSderaadt  *	lines of file1, but there is no such subsequence for
87ae8d569bSderaadt  *	any smaller y. x is the earliest possible mate to y
88ae8d569bSderaadt  *	that occurs in such a subsequence.
89ae8d569bSderaadt  *
90ae8d569bSderaadt  *	Whenever any of the members of the equivalence class of
91ae8d569bSderaadt  *	lines in file1 matable to a line in file0 has serial number
92ae8d569bSderaadt  *	less than the y of some k-candidate, that k-candidate
93ae8d569bSderaadt  *	with the smallest such y is replaced. The new
94ae8d569bSderaadt  *	k-candidate is chained (via pred) to the current
95ae8d569bSderaadt  *	k-1 candidate so that the actual subsequence can
96ae8d569bSderaadt  *	be recovered. When a member has serial number greater
97ae8d569bSderaadt  *	that the y of all k-candidates, the klist is extended.
98ae8d569bSderaadt  *	At the end, the longest subsequence is pulled out
99ae8d569bSderaadt  *	and placed in the array J by unravel
100ae8d569bSderaadt  *
101ae8d569bSderaadt  *	With J in hand, the matches there recorded are
102ae8d569bSderaadt  *	check'ed against reality to assure that no spurious
103ae8d569bSderaadt  *	matches have crept in due to hashing. If they have,
104ae8d569bSderaadt  *	they are broken, and "jackpot" is recorded--a harmless
105ae8d569bSderaadt  *	matter except that a true match for a spuriously
106ae8d569bSderaadt  *	mated line may now be unnecessarily reported as a change.
107ae8d569bSderaadt  *
108ae8d569bSderaadt  *	Much of the complexity of the program comes simply
109ae8d569bSderaadt  *	from trying to minimize core utilization and
110ae8d569bSderaadt  *	maximize the range of doable problems by dynamically
111ae8d569bSderaadt  *	allocating what is needed and reusing what is not.
112ae8d569bSderaadt  *	The core requirements for problems larger than somewhat
113ae8d569bSderaadt  *	are (in words) 2*length(file0) + length(file1) +
114ae8d569bSderaadt  *	3*(number of k-candidates installed),  typically about
115ae8d569bSderaadt  *	6n words for files of length n.
116ae8d569bSderaadt  */
117ae8d569bSderaadt 
118ae8d569bSderaadt #define	prints(s)	fputs(s,stdout)
119ae8d569bSderaadt 
120ae8d569bSderaadt FILE *input[2];
121ae8d569bSderaadt FILE *fopen();
122ae8d569bSderaadt 
123ae8d569bSderaadt struct cand {
124ae8d569bSderaadt 	int x;
125ae8d569bSderaadt 	int y;
126ae8d569bSderaadt 	int pred;
127ae8d569bSderaadt } cand;
12826da422aStedu 
129ae8d569bSderaadt struct line {
130ae8d569bSderaadt 	int serial;
131ae8d569bSderaadt 	int value;
132ae8d569bSderaadt } *file[2], line;
13326da422aStedu 
134ae8d569bSderaadt int len[2];
135*0c9f493eStedu struct line *sfile[2];	/* shortened by pruning common prefix and suffix */
136ae8d569bSderaadt int slen[2];
137ae8d569bSderaadt int pref, suff;			/* length of prefix and suffix */
138ae8d569bSderaadt int *class;			/* will be overlaid on file[0] */
139ae8d569bSderaadt int *member;			/* will be overlaid on file[1] */
140ae8d569bSderaadt int *klist;			/* will be overlaid on file[0] after class */
141ae8d569bSderaadt struct cand *clist;		/* merely a free storage pot for candidates */
142ae8d569bSderaadt int clen = 0;
143ae8d569bSderaadt int *J;				/* will be overlaid on class */
144ae8d569bSderaadt long *ixold;			/* will be overlaid on klist */
145ae8d569bSderaadt long *ixnew;			/* will be overlaid on file[1] */
146ae8d569bSderaadt char *chrtran;			/* translation table for case-folding */
147ae8d569bSderaadt 
14826da422aStedu static void fetch(long *, int, int, FILE *, char *, int);
14926da422aStedu static void output(void);
15026da422aStedu static void check(void);
15126da422aStedu static void range(int, int, char *);
15226da422aStedu static void dump_context_vec(void);
15326da422aStedu static void prepare(int, FILE *);
15426da422aStedu static void prune(void);
15526da422aStedu static void equiv(struct line *, int, struct line *, int, int *);
15626da422aStedu static void unravel(int);
15726da422aStedu static void unsort(struct line *, int, int *);
15826da422aStedu static void change(int, int, int, int);
15926da422aStedu static void sort(struct line *, int);
16026da422aStedu static int newcand(int, int, int);
16126da422aStedu static int search(int *, int, int);
16226da422aStedu static int skipline(int);
16326da422aStedu static int asciifile(FILE *);
16426da422aStedu static int stone(int *, int, int *, int *);
16526da422aStedu static int readhash(FILE *);
16626da422aStedu 
16726da422aStedu /*
16826da422aStedu  * chrtran points to one of 2 translation tables: cup2low if folding upper to
16926da422aStedu  * lower case clow2low if not folding case
170ae8d569bSderaadt  */
171ae8d569bSderaadt char clow2low[256] = {
17226da422aStedu 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
17326da422aStedu 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
17426da422aStedu 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
17526da422aStedu 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
17626da422aStedu 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
17726da422aStedu 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
17826da422aStedu 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
17926da422aStedu 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
18026da422aStedu 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
18126da422aStedu 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
18226da422aStedu 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
18326da422aStedu 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
18426da422aStedu 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
18526da422aStedu 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
18626da422aStedu 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
18726da422aStedu 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
18826da422aStedu 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
18926da422aStedu 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
19026da422aStedu 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
19126da422aStedu 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
19226da422aStedu 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
19326da422aStedu 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
19426da422aStedu 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
19526da422aStedu 	0xfd, 0xfe, 0xff
196ae8d569bSderaadt };
197ae8d569bSderaadt 
198ae8d569bSderaadt char cup2low[256] = {
19926da422aStedu 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
20026da422aStedu 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
20126da422aStedu 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
20226da422aStedu 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
20326da422aStedu 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
20426da422aStedu 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
20526da422aStedu 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
20626da422aStedu 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
20726da422aStedu 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
20826da422aStedu 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
20926da422aStedu 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
21026da422aStedu 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
21126da422aStedu 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
21226da422aStedu 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
21326da422aStedu 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
21426da422aStedu 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
21526da422aStedu 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
21626da422aStedu 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
21726da422aStedu 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
21826da422aStedu 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
21926da422aStedu 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
22026da422aStedu 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
22126da422aStedu 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
22226da422aStedu 	0xfd, 0xfe, 0xff
223ae8d569bSderaadt };
224ae8d569bSderaadt 
22526da422aStedu void
22626da422aStedu diffreg(void)
227ae8d569bSderaadt {
22826da422aStedu 	int i, j;
229ae8d569bSderaadt 	FILE *f1, *f2;
230ae8d569bSderaadt 	char buf1[BUFSIZ], buf2[BUFSIZ];
231ae8d569bSderaadt 
232ae8d569bSderaadt 	if (hflag) {
233ae8d569bSderaadt 		diffargv[0] = "diffh";
234ae8d569bSderaadt 		execv(diffh, diffargv);
235ae8d569bSderaadt 		fprintf(stderr, "diff: ");
236ae8d569bSderaadt 		perror(diffh);
237ae8d569bSderaadt 		done();
238ae8d569bSderaadt 	}
239ae8d569bSderaadt 	chrtran = (iflag ? cup2low : clow2low);
240ae8d569bSderaadt 	if ((stb1.st_mode & S_IFMT) == S_IFDIR) {
241ae8d569bSderaadt 		file1 = splice(file1, file2);
242ae8d569bSderaadt 		if (stat(file1, &stb1) < 0) {
243ae8d569bSderaadt 			fprintf(stderr, "diff: ");
244ae8d569bSderaadt 			perror(file1);
245ae8d569bSderaadt 			done();
246ae8d569bSderaadt 		}
247ae8d569bSderaadt 	} else if ((stb2.st_mode & S_IFMT) == S_IFDIR) {
248ae8d569bSderaadt 		file2 = splice(file2, file1);
249ae8d569bSderaadt 		if (stat(file2, &stb2) < 0) {
250ae8d569bSderaadt 			fprintf(stderr, "diff: ");
251ae8d569bSderaadt 			perror(file2);
252ae8d569bSderaadt 			done();
253ae8d569bSderaadt 		}
254ae8d569bSderaadt 	} else if ((stb1.st_mode & S_IFMT) != S_IFREG || !strcmp(file1, "-")) {
255ae8d569bSderaadt 		if (!strcmp(file2, "-")) {
256ae8d569bSderaadt 			fprintf(stderr, "diff: can't specify - -\n");
257ae8d569bSderaadt 			done();
258ae8d569bSderaadt 		}
259ae8d569bSderaadt 		file1 = copytemp();
260ae8d569bSderaadt 		if (stat(file1, &stb1) < 0) {
261ae8d569bSderaadt 			fprintf(stderr, "diff: ");
262ae8d569bSderaadt 			perror(file1);
263ae8d569bSderaadt 			done();
264ae8d569bSderaadt 		}
265ae8d569bSderaadt 	} else if ((stb2.st_mode & S_IFMT) != S_IFREG || !strcmp(file2, "-")) {
266ae8d569bSderaadt 		file2 = copytemp();
267ae8d569bSderaadt 		if (stat(file2, &stb2) < 0) {
268ae8d569bSderaadt 			fprintf(stderr, "diff: ");
269ae8d569bSderaadt 			perror(file2);
270ae8d569bSderaadt 			done();
271ae8d569bSderaadt 		}
272ae8d569bSderaadt 	}
273ae8d569bSderaadt 	if ((f1 = fopen(file1, "r")) == NULL) {
274ae8d569bSderaadt 		fprintf(stderr, "diff: ");
275ae8d569bSderaadt 		perror(file1);
276ae8d569bSderaadt 		done();
277ae8d569bSderaadt 	}
278ae8d569bSderaadt 	if ((f2 = fopen(file2, "r")) == NULL) {
279ae8d569bSderaadt 		fprintf(stderr, "diff: ");
280ae8d569bSderaadt 		perror(file2);
281ae8d569bSderaadt 		fclose(f1);
282ae8d569bSderaadt 		done();
283ae8d569bSderaadt 	}
284ae8d569bSderaadt 	if (stb1.st_size != stb2.st_size)
285ae8d569bSderaadt 		goto notsame;
286ae8d569bSderaadt 	for (;;) {
287ae8d569bSderaadt 		i = fread(buf1, 1, BUFSIZ, f1);
288ae8d569bSderaadt 		j = fread(buf2, 1, BUFSIZ, f2);
289ae8d569bSderaadt 		if (i < 0 || j < 0 || i != j)
290ae8d569bSderaadt 			goto notsame;
291ae8d569bSderaadt 		if (i == 0 && j == 0) {
292ae8d569bSderaadt 			fclose(f1);
293ae8d569bSderaadt 			fclose(f2);
294ae8d569bSderaadt 			status = 0;	/* files don't differ */
295ae8d569bSderaadt 			goto same;
296ae8d569bSderaadt 		}
297ae8d569bSderaadt 		for (j = 0; j < i; j++)
298ae8d569bSderaadt 			if (buf1[j] != buf2[j])
299ae8d569bSderaadt 				goto notsame;
300ae8d569bSderaadt 	}
301ae8d569bSderaadt notsame:
302ae8d569bSderaadt 	/*
303ae8d569bSderaadt 	 *	Files certainly differ at this point; set status accordingly
304ae8d569bSderaadt 	 */
305ae8d569bSderaadt 	status = 1;
306ae8d569bSderaadt 	if (!asciifile(f1) || !asciifile(f2)) {
307ae8d569bSderaadt 		printf("Binary files %s and %s differ\n", file1, file2);
308ae8d569bSderaadt 		fclose(f1);
309ae8d569bSderaadt 		fclose(f2);
310ae8d569bSderaadt 		done();
311ae8d569bSderaadt 	}
312ae8d569bSderaadt 	prepare(0, f1);
313ae8d569bSderaadt 	prepare(1, f2);
314ae8d569bSderaadt 	fclose(f1);
315ae8d569bSderaadt 	fclose(f2);
316ae8d569bSderaadt 	prune();
317ae8d569bSderaadt 	sort(sfile[0], slen[0]);
318ae8d569bSderaadt 	sort(sfile[1], slen[1]);
319ae8d569bSderaadt 
320ae8d569bSderaadt 	member = (int *)file[1];
321ae8d569bSderaadt 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
322*0c9f493eStedu 	member = ralloc(member, (slen[1] + 2) * sizeof(int));
323ae8d569bSderaadt 
324ae8d569bSderaadt 	class = (int *)file[0];
325ae8d569bSderaadt 	unsort(sfile[0], slen[0], class);
326*0c9f493eStedu 	class = ralloc(class, (slen[0] + 2) * sizeof(int));
327ae8d569bSderaadt 
32826da422aStedu 	klist = talloc((slen[0] + 2) * sizeof(int));
32926da422aStedu 	clist = talloc(sizeof(cand));
330ae8d569bSderaadt 	i = stone(class, slen[0], member, klist);
33126da422aStedu 	free(member);
33226da422aStedu 	free(class);
333ae8d569bSderaadt 
33426da422aStedu 	J = talloc((len[0] + 2) * sizeof(int));
335ae8d569bSderaadt 	unravel(klist[i]);
33626da422aStedu 	free(clist);
33726da422aStedu 	free(klist);
338ae8d569bSderaadt 
33926da422aStedu 	ixold = talloc((len[0] + 2) * sizeof(long));
34026da422aStedu 	ixnew = talloc((len[1] + 2) * sizeof(long));
341ae8d569bSderaadt 	check();
342ae8d569bSderaadt 	output();
343ae8d569bSderaadt 	status = anychange;
344ae8d569bSderaadt same:
345ae8d569bSderaadt 	if (opt == D_CONTEXT && anychange == 0)
346ae8d569bSderaadt 		printf("No differences encountered\n");
347ae8d569bSderaadt 	done();
348ae8d569bSderaadt }
349ae8d569bSderaadt 
350ae8d569bSderaadt char *tempfile = _PATH_TMP;
351ae8d569bSderaadt 
352ae8d569bSderaadt char *
35326da422aStedu copytemp(void)
354ae8d569bSderaadt {
355ae8d569bSderaadt 	char buf[BUFSIZ];
35626da422aStedu 	int i, f;
357ae8d569bSderaadt 
35826da422aStedu 	signal(SIGHUP, catchsig);
35926da422aStedu 	signal(SIGINT, catchsig);
36026da422aStedu 	signal(SIGPIPE, catchsig);
36126da422aStedu 	signal(SIGTERM, catchsig);
36226da422aStedu 	f = mkstemp(tempfile);
363ae8d569bSderaadt 	if (f < 0) {
364ae8d569bSderaadt 		fprintf(stderr, "diff: ");
365ae8d569bSderaadt 		perror(tempfile);
366ae8d569bSderaadt 		done();
367ae8d569bSderaadt 	}
368ae8d569bSderaadt 	while ((i = read(0, buf, BUFSIZ)) > 0)
369ae8d569bSderaadt 		if (write(f, buf, i) != i) {
370ae8d569bSderaadt 			fprintf(stderr, "diff: ");
371ae8d569bSderaadt 			perror(tempfile);
372ae8d569bSderaadt 			done();
373ae8d569bSderaadt 		}
374ae8d569bSderaadt 	close(f);
375ae8d569bSderaadt 	return (tempfile);
376ae8d569bSderaadt }
377ae8d569bSderaadt 
378ae8d569bSderaadt char *
37926da422aStedu splice(char *dir, char *file)
380ae8d569bSderaadt {
381ae8d569bSderaadt 	char *tail;
382ae8d569bSderaadt 	char buf[BUFSIZ];
383ae8d569bSderaadt 
384ae8d569bSderaadt 	if (!strcmp(file, "-")) {
385ae8d569bSderaadt 		fprintf(stderr, "diff: can't specify - with other arg directory\n");
386ae8d569bSderaadt 		done();
387ae8d569bSderaadt 	}
388ae8d569bSderaadt 	tail = rindex(file, '/');
389ae8d569bSderaadt 	if (tail == 0)
390ae8d569bSderaadt 		tail = file;
391ae8d569bSderaadt 	else
392ae8d569bSderaadt 		tail++;
39326da422aStedu 	snprintf(buf, sizeof buf, "%s/%s", dir, tail);
39426da422aStedu 	return (strdup(buf));
395ae8d569bSderaadt }
396ae8d569bSderaadt 
39726da422aStedu static void
39826da422aStedu prepare(int i, FILE *fd)
399ae8d569bSderaadt {
40026da422aStedu 	struct line *p;
40126da422aStedu 	int j, h;
402ae8d569bSderaadt 
40326da422aStedu 	fseek(fd, 0, 0);
40426da422aStedu 	p = talloc(3 * sizeof(line));
40526da422aStedu 	for (j = 0; (h = readhash(fd));) {
406*0c9f493eStedu 		p = ralloc(p, (++j + 3) * sizeof(line));
407ae8d569bSderaadt 		p[j].value = h;
408ae8d569bSderaadt 	}
409ae8d569bSderaadt 	len[i] = j;
410ae8d569bSderaadt 	file[i] = p;
411ae8d569bSderaadt }
412ae8d569bSderaadt 
41326da422aStedu static void
41426da422aStedu prune(void)
415ae8d569bSderaadt {
41626da422aStedu 	int i, j;
417ae8d569bSderaadt 	for (pref = 0; pref < len[0] && pref < len[1] &&
418ae8d569bSderaadt 	    file[0][pref + 1].value == file[1][pref + 1].value;
419ae8d569bSderaadt 	    pref++);
420ae8d569bSderaadt 	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
421ae8d569bSderaadt 	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
422ae8d569bSderaadt 	    suff++);
423ae8d569bSderaadt 	for (j = 0; j < 2; j++) {
424ae8d569bSderaadt 		sfile[j] = file[j] + pref;
425ae8d569bSderaadt 		slen[j] = len[j] - pref - suff;
426ae8d569bSderaadt 		for (i = 0; i <= slen[j]; i++)
427ae8d569bSderaadt 			sfile[j][i].serial = i;
428ae8d569bSderaadt 	}
429ae8d569bSderaadt }
430ae8d569bSderaadt 
43126da422aStedu static void
43226da422aStedu equiv(struct line *a, int n, struct line *b, int m, int *c)
433ae8d569bSderaadt {
43426da422aStedu 	int i, j;
435ae8d569bSderaadt 	i = j = 1;
436ae8d569bSderaadt 	while (i <= n && j <= m) {
437ae8d569bSderaadt 		if (a[i].value < b[j].value)
438ae8d569bSderaadt 			a[i++].value = 0;
439ae8d569bSderaadt 		else if (a[i].value == b[j].value)
440ae8d569bSderaadt 			a[i++].value = j;
441ae8d569bSderaadt 		else
442ae8d569bSderaadt 			j++;
443ae8d569bSderaadt 	}
444ae8d569bSderaadt 	while (i <= n)
445ae8d569bSderaadt 		a[i++].value = 0;
446ae8d569bSderaadt 	b[m + 1].value = 0;
447ae8d569bSderaadt 	j = 0;
448ae8d569bSderaadt 	while (++j <= m) {
449ae8d569bSderaadt 		c[j] = -b[j].serial;
450ae8d569bSderaadt 		while (b[j + 1].value == b[j].value) {
451ae8d569bSderaadt 			j++;
452ae8d569bSderaadt 			c[j] = b[j].serial;
453ae8d569bSderaadt 		}
454ae8d569bSderaadt 	}
455ae8d569bSderaadt 	c[j] = -1;
456ae8d569bSderaadt }
457ae8d569bSderaadt 
45826da422aStedu static int
45926da422aStedu stone(int *a, int n, int *b, int *c)
460ae8d569bSderaadt {
46126da422aStedu 	int i, k, y;
462ae8d569bSderaadt 	int j, l;
463ae8d569bSderaadt 	int oldc, tc;
464ae8d569bSderaadt 	int oldl;
465ae8d569bSderaadt 	k = 0;
466ae8d569bSderaadt 	c[0] = newcand(0, 0, 0);
467ae8d569bSderaadt 	for (i = 1; i <= n; i++) {
468ae8d569bSderaadt 		j = a[i];
469ae8d569bSderaadt 		if (j == 0)
470ae8d569bSderaadt 			continue;
471ae8d569bSderaadt 		y = -b[j];
472ae8d569bSderaadt 		oldl = 0;
473ae8d569bSderaadt 		oldc = c[0];
474ae8d569bSderaadt 		do {
475ae8d569bSderaadt 			if (y <= clist[oldc].y)
476ae8d569bSderaadt 				continue;
477ae8d569bSderaadt 			l = search(c, k, y);
478ae8d569bSderaadt 			if (l != oldl + 1)
479ae8d569bSderaadt 				oldc = c[l - 1];
480ae8d569bSderaadt 			if (l <= k) {
481ae8d569bSderaadt 				if (clist[c[l]].y <= y)
482ae8d569bSderaadt 					continue;
483ae8d569bSderaadt 				tc = c[l];
484ae8d569bSderaadt 				c[l] = newcand(i, y, oldc);
485ae8d569bSderaadt 				oldc = tc;
486ae8d569bSderaadt 				oldl = l;
487ae8d569bSderaadt 			} else {
488ae8d569bSderaadt 				c[l] = newcand(i, y, oldc);
489ae8d569bSderaadt 				k++;
490ae8d569bSderaadt 				break;
491ae8d569bSderaadt 			}
492ae8d569bSderaadt 		} while ((y = b[++j]) > 0);
493ae8d569bSderaadt 	}
494ae8d569bSderaadt 	return (k);
495ae8d569bSderaadt }
496ae8d569bSderaadt 
49726da422aStedu static int
49826da422aStedu newcand(int x, int y, int pred)
499ae8d569bSderaadt {
50026da422aStedu 	struct cand *q;
50126da422aStedu 
502*0c9f493eStedu 	clist = ralloc(clist, ++clen * sizeof(cand));
503ae8d569bSderaadt 	q = clist + clen - 1;
504ae8d569bSderaadt 	q->x = x;
505ae8d569bSderaadt 	q->y = y;
506ae8d569bSderaadt 	q->pred = pred;
507ae8d569bSderaadt 	return (clen - 1);
508ae8d569bSderaadt }
509ae8d569bSderaadt 
51026da422aStedu static int
51126da422aStedu search(int *c, int k, int y)
512ae8d569bSderaadt {
51326da422aStedu 	int i, j, l;
514ae8d569bSderaadt 	int t;
515ae8d569bSderaadt 	if (clist[c[k]].y < y)	/* quick look for typical case */
516ae8d569bSderaadt 		return (k + 1);
517ae8d569bSderaadt 	i = 0;
518ae8d569bSderaadt 	j = k + 1;
519ae8d569bSderaadt 	while (1) {
520ae8d569bSderaadt 		l = i + j;
521ae8d569bSderaadt 		if ((l >>= 1) <= i)
522ae8d569bSderaadt 			break;
523ae8d569bSderaadt 		t = clist[c[l]].y;
524ae8d569bSderaadt 		if (t > y)
525ae8d569bSderaadt 			j = l;
526ae8d569bSderaadt 		else if (t < y)
527ae8d569bSderaadt 			i = l;
528ae8d569bSderaadt 		else
529ae8d569bSderaadt 			return (l);
530ae8d569bSderaadt 	}
531ae8d569bSderaadt 	return (l + 1);
532ae8d569bSderaadt }
533ae8d569bSderaadt 
53426da422aStedu static void
53526da422aStedu unravel(int p)
536ae8d569bSderaadt {
53726da422aStedu 	int i;
53826da422aStedu 	struct cand *q;
539ae8d569bSderaadt 	for (i = 0; i <= len[0]; i++)
540ae8d569bSderaadt 		J[i] = i <= pref ? i :
541ae8d569bSderaadt 		    i > len[0] - suff ? i + len[1] - len[0] :
542ae8d569bSderaadt 		    0;
543ae8d569bSderaadt 	for (q = clist + p; q->y != 0; q = clist + q->pred)
544ae8d569bSderaadt 		J[q->x + pref] = q->y + pref;
545ae8d569bSderaadt }
546ae8d569bSderaadt 
54726da422aStedu /*
54826da422aStedu  * check does double duty: 1.  ferret out any fortuitous correspondences due
54926da422aStedu  * to confounding by hashing (which result in "jackpot") 2.  collect random
55026da422aStedu  * access indexes to the two files
55126da422aStedu  */
552ae8d569bSderaadt 
55326da422aStedu static void
55426da422aStedu check(void)
555ae8d569bSderaadt {
55626da422aStedu 	int i, j;
557ae8d569bSderaadt 	int jackpot;
558ae8d569bSderaadt 	long ctold, ctnew;
55926da422aStedu 	int c, d;
560ae8d569bSderaadt 
561ae8d569bSderaadt 	if ((input[0] = fopen(file1, "r")) == NULL) {
562ae8d569bSderaadt 		perror(file1);
563ae8d569bSderaadt 		done();
564ae8d569bSderaadt 	}
565ae8d569bSderaadt 	if ((input[1] = fopen(file2, "r")) == NULL) {
566ae8d569bSderaadt 		perror(file2);
567ae8d569bSderaadt 		done();
568ae8d569bSderaadt 	}
569ae8d569bSderaadt 	j = 1;
570ae8d569bSderaadt 	ixold[0] = ixnew[0] = 0;
571ae8d569bSderaadt 	jackpot = 0;
572ae8d569bSderaadt 	ctold = ctnew = 0;
573ae8d569bSderaadt 	for (i = 1; i <= len[0]; i++) {
574ae8d569bSderaadt 		if (J[i] == 0) {
575ae8d569bSderaadt 			ixold[i] = ctold += skipline(0);
576ae8d569bSderaadt 			continue;
577ae8d569bSderaadt 		}
578ae8d569bSderaadt 		while (j < J[i]) {
579ae8d569bSderaadt 			ixnew[j] = ctnew += skipline(1);
580ae8d569bSderaadt 			j++;
581ae8d569bSderaadt 		}
582ae8d569bSderaadt 		if (bflag || wflag || iflag) {
583ae8d569bSderaadt 			for (;;) {
584ae8d569bSderaadt 				c = getc(input[0]);
585ae8d569bSderaadt 				d = getc(input[1]);
586ae8d569bSderaadt 				ctold++;
587ae8d569bSderaadt 				ctnew++;
588ae8d569bSderaadt 				if (bflag && isspace(c) && isspace(d)) {
589ae8d569bSderaadt 					do {
590ae8d569bSderaadt 						if (c == '\n')
591ae8d569bSderaadt 							break;
592ae8d569bSderaadt 						ctold++;
593ae8d569bSderaadt 					} while (isspace(c = getc(input[0])));
594ae8d569bSderaadt 					do {
595ae8d569bSderaadt 						if (d == '\n')
596ae8d569bSderaadt 							break;
597ae8d569bSderaadt 						ctnew++;
598ae8d569bSderaadt 					} while (isspace(d = getc(input[1])));
599ae8d569bSderaadt 				} else if (wflag) {
600ae8d569bSderaadt 					while (isspace(c) && c != '\n') {
601ae8d569bSderaadt 						c = getc(input[0]);
602ae8d569bSderaadt 						ctold++;
603ae8d569bSderaadt 					}
604ae8d569bSderaadt 					while (isspace(d) && d != '\n') {
605ae8d569bSderaadt 						d = getc(input[1]);
606ae8d569bSderaadt 						ctnew++;
607ae8d569bSderaadt 					}
608ae8d569bSderaadt 				}
609ae8d569bSderaadt 				if (chrtran[c] != chrtran[d]) {
610ae8d569bSderaadt 					jackpot++;
611ae8d569bSderaadt 					J[i] = 0;
612ae8d569bSderaadt 					if (c != '\n')
613ae8d569bSderaadt 						ctold += skipline(0);
614ae8d569bSderaadt 					if (d != '\n')
615ae8d569bSderaadt 						ctnew += skipline(1);
616ae8d569bSderaadt 					break;
617ae8d569bSderaadt 				}
618ae8d569bSderaadt 				if (c == '\n')
619ae8d569bSderaadt 					break;
620ae8d569bSderaadt 			}
621ae8d569bSderaadt 		} else {
622ae8d569bSderaadt 			for (;;) {
623ae8d569bSderaadt 				ctold++;
624ae8d569bSderaadt 				ctnew++;
625ae8d569bSderaadt 				if ((c = getc(input[0])) != (d = getc(input[1]))) {
626ae8d569bSderaadt 					/* jackpot++; */
627ae8d569bSderaadt 					J[i] = 0;
628ae8d569bSderaadt 					if (c != '\n')
629ae8d569bSderaadt 						ctold += skipline(0);
630ae8d569bSderaadt 					if (d != '\n')
631ae8d569bSderaadt 						ctnew += skipline(1);
632ae8d569bSderaadt 					break;
633ae8d569bSderaadt 				}
634ae8d569bSderaadt 				if (c == '\n')
635ae8d569bSderaadt 					break;
636ae8d569bSderaadt 			}
637ae8d569bSderaadt 		}
638ae8d569bSderaadt 		ixold[i] = ctold;
639ae8d569bSderaadt 		ixnew[j] = ctnew;
640ae8d569bSderaadt 		j++;
641ae8d569bSderaadt 	}
642ae8d569bSderaadt 	for (; j <= len[1]; j++) {
643ae8d569bSderaadt 		ixnew[j] = ctnew += skipline(1);
644ae8d569bSderaadt 	}
645ae8d569bSderaadt 	fclose(input[0]);
646ae8d569bSderaadt 	fclose(input[1]);
647ae8d569bSderaadt 	/*
648ae8d569bSderaadt 		if(jackpot)
649ae8d569bSderaadt 			fprintf(stderr, "jackpot\n");
650ae8d569bSderaadt 	*/
651ae8d569bSderaadt }
652ae8d569bSderaadt 
65326da422aStedu static void
65426da422aStedu sort(struct line *a, int n)
65526da422aStedu {				/* shellsort CACM #201 */
656ae8d569bSderaadt 	struct line w;
65726da422aStedu 	int j, m = 0;		/* gcc */
658ae8d569bSderaadt 	struct line *ai;
65926da422aStedu 	struct line *aim;
660ae8d569bSderaadt 	int k;
661ae8d569bSderaadt 
662ae8d569bSderaadt 	if (n == 0)
663ae8d569bSderaadt 		return;
664ae8d569bSderaadt 	for (j = 1; j <= n; j *= 2)
665ae8d569bSderaadt 		m = 2 * j - 1;
666ae8d569bSderaadt 	for (m /= 2; m != 0; m /= 2) {
667ae8d569bSderaadt 		k = n - m;
668ae8d569bSderaadt 		for (j = 1; j <= k; j++) {
669ae8d569bSderaadt 			for (ai = &a[j]; ai > a; ai -= m) {
670ae8d569bSderaadt 				aim = &ai[m];
671ae8d569bSderaadt 				if (aim < ai)
672ae8d569bSderaadt 					break;	/* wraparound */
673ae8d569bSderaadt 				if (aim->value > ai[0].value ||
67426da422aStedu 				    (aim->value == ai[0].value &&
67526da422aStedu 					aim->serial > ai[0].serial))
676ae8d569bSderaadt 					break;
677ae8d569bSderaadt 				w.value = ai[0].value;
678ae8d569bSderaadt 				ai[0].value = aim->value;
679ae8d569bSderaadt 				aim->value = w.value;
680ae8d569bSderaadt 				w.serial = ai[0].serial;
681ae8d569bSderaadt 				ai[0].serial = aim->serial;
682ae8d569bSderaadt 				aim->serial = w.serial;
683ae8d569bSderaadt 			}
684ae8d569bSderaadt 		}
685ae8d569bSderaadt 	}
686ae8d569bSderaadt }
687ae8d569bSderaadt 
68826da422aStedu static void
68926da422aStedu unsort(struct line *f, int l, int *b)
690ae8d569bSderaadt {
69126da422aStedu 	int *a;
69226da422aStedu 	int i;
69326da422aStedu 
69426da422aStedu 	a = talloc((l + 1) * sizeof(int));
695ae8d569bSderaadt 	for (i = 1; i <= l; i++)
696ae8d569bSderaadt 		a[f[i].serial] = f[i].value;
697ae8d569bSderaadt 	for (i = 1; i <= l; i++)
698ae8d569bSderaadt 		b[i] = a[i];
69926da422aStedu 	free(a);
700ae8d569bSderaadt }
701ae8d569bSderaadt 
70226da422aStedu static int
70326da422aStedu skipline(int f)
704ae8d569bSderaadt {
70526da422aStedu 	int i, c;
706ae8d569bSderaadt 
707ae8d569bSderaadt 	for (i = 1; (c = getc(input[f])) != '\n'; i++)
708ae8d569bSderaadt 		if (c < 0)
709ae8d569bSderaadt 			return (i);
710ae8d569bSderaadt 	return (i);
711ae8d569bSderaadt }
712ae8d569bSderaadt 
71326da422aStedu static void
71426da422aStedu output(void)
715ae8d569bSderaadt {
716ae8d569bSderaadt 	int m;
71726da422aStedu 	int i0, i1, j1;
718ae8d569bSderaadt 	int j0;
719ae8d569bSderaadt 	input[0] = fopen(file1, "r");
720ae8d569bSderaadt 	input[1] = fopen(file2, "r");
721ae8d569bSderaadt 	m = len[0];
722ae8d569bSderaadt 	J[0] = 0;
723ae8d569bSderaadt 	J[m + 1] = len[1] + 1;
72426da422aStedu 	if (opt != D_EDIT) {
72526da422aStedu 		for (i0 = 1; i0 <= m; i0 = i1 + 1) {
72626da422aStedu 			while (i0 <= m && J[i0] == J[i0 - 1] + 1)
72726da422aStedu 				i0++;
728ae8d569bSderaadt 			j0 = J[i0 - 1] + 1;
729ae8d569bSderaadt 			i1 = i0 - 1;
73026da422aStedu 			while (i1 < m && J[i1 + 1] == 0)
73126da422aStedu 				i1++;
732ae8d569bSderaadt 			j1 = J[i1 + 1] - 1;
733ae8d569bSderaadt 			J[i1] = j1;
734ae8d569bSderaadt 			change(i0, i1, j0, j1);
73526da422aStedu 		}
73626da422aStedu 	} else {
73726da422aStedu 		for (i0 = m; i0 >= 1; i0 = i1 - 1) {
73826da422aStedu 			while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
73926da422aStedu 				i0--;
740ae8d569bSderaadt 			j0 = J[i0 + 1] - 1;
741ae8d569bSderaadt 			i1 = i0 + 1;
74226da422aStedu 			while (i1 > 1 && J[i1 - 1] == 0)
74326da422aStedu 				i1--;
744ae8d569bSderaadt 			j1 = J[i1 - 1] + 1;
745ae8d569bSderaadt 			J[i1] = j1;
746ae8d569bSderaadt 			change(i1, i0, j1, j0);
747ae8d569bSderaadt 		}
74826da422aStedu 	}
749ae8d569bSderaadt 	if (m == 0)
750ae8d569bSderaadt 		change(1, 0, 1, len[1]);
751ae8d569bSderaadt 	if (opt == D_IFDEF) {
752ae8d569bSderaadt 		for (;;) {
753ae8d569bSderaadt #define	c i0
754ae8d569bSderaadt 			c = getc(input[0]);
755ae8d569bSderaadt 			if (c < 0)
756ae8d569bSderaadt 				return;
757ae8d569bSderaadt 			putchar(c);
758ae8d569bSderaadt 		}
759ae8d569bSderaadt #undef c
760ae8d569bSderaadt 	}
761ae8d569bSderaadt 	if (anychange && opt == D_CONTEXT)
762ae8d569bSderaadt 		dump_context_vec();
763ae8d569bSderaadt }
764ae8d569bSderaadt 
765ae8d569bSderaadt /*
766ae8d569bSderaadt  * The following struct is used to record change information when
767ae8d569bSderaadt  * doing a "context" diff.  (see routine "change" to understand the
768ae8d569bSderaadt  * highly mneumonic field names)
769ae8d569bSderaadt  */
770ae8d569bSderaadt struct context_vec {
771ae8d569bSderaadt 	int a;			/* start line in old file */
772ae8d569bSderaadt 	int b;			/* end line in old file */
773ae8d569bSderaadt 	int c;			/* start line in new file */
774ae8d569bSderaadt 	int d;			/* end line in new file */
775ae8d569bSderaadt };
776ae8d569bSderaadt 
77726da422aStedu struct context_vec *context_vec_start, *context_vec_end, *context_vec_ptr;
778ae8d569bSderaadt 
779ae8d569bSderaadt #define	MAX_CONTEXT	128
780ae8d569bSderaadt 
78126da422aStedu /*
78226da422aStedu  * indicate that there is a difference between lines a and b of the from file
78326da422aStedu  * to get to lines c to d of the to file. If a is greater then b then there
78426da422aStedu  * are no lines in the from file involved and this means that there were
78526da422aStedu  * lines appended (beginning at b). If c is greater than d then there are
78626da422aStedu  * lines missing from the to file.
787ae8d569bSderaadt  */
78826da422aStedu static void
78926da422aStedu change(int a, int b, int c, int d)
790ae8d569bSderaadt {
791ae8d569bSderaadt 	struct stat stbuf;
792ae8d569bSderaadt 
793ae8d569bSderaadt 	if (opt != D_IFDEF && a > b && c > d)
794ae8d569bSderaadt 		return;
795ae8d569bSderaadt 	if (anychange == 0) {
796ae8d569bSderaadt 		anychange = 1;
797ae8d569bSderaadt 		if (opt == D_CONTEXT) {
798ae8d569bSderaadt 			printf("*** %s	", file1);
799ae8d569bSderaadt 			stat(file1, &stbuf);
800ae8d569bSderaadt 			printf("%s--- %s	",
801ae8d569bSderaadt 			    ctime(&stbuf.st_mtime), file2);
802ae8d569bSderaadt 			stat(file2, &stbuf);
803ae8d569bSderaadt 			printf("%s", ctime(&stbuf.st_mtime));
804ae8d569bSderaadt 
80526da422aStedu 			context_vec_start = talloc(MAX_CONTEXT *
806ae8d569bSderaadt 			    sizeof(struct context_vec));
807ae8d569bSderaadt 			context_vec_end = context_vec_start + MAX_CONTEXT;
808ae8d569bSderaadt 			context_vec_ptr = context_vec_start - 1;
809ae8d569bSderaadt 		}
810ae8d569bSderaadt 	}
811ae8d569bSderaadt 	if (opt == D_CONTEXT) {
812ae8d569bSderaadt 		/*
813ae8d569bSderaadt 		 * if this new change is within 'context' lines of
814ae8d569bSderaadt 		 * the previous change, just add it to the change
815ae8d569bSderaadt 		 * record.  If the record is full or if this
816ae8d569bSderaadt 		 * change is more than 'context' lines from the previous
817ae8d569bSderaadt 		 * change, dump the record, reset it & add the new change.
818ae8d569bSderaadt 		 */
819ae8d569bSderaadt 		if (context_vec_ptr >= context_vec_end ||
820ae8d569bSderaadt 		    (context_vec_ptr >= context_vec_start &&
821ae8d569bSderaadt 			a > (context_vec_ptr->b + 2 * context) &&
822ae8d569bSderaadt 			c > (context_vec_ptr->d + 2 * context)))
823ae8d569bSderaadt 			dump_context_vec();
824ae8d569bSderaadt 
825ae8d569bSderaadt 		context_vec_ptr++;
826ae8d569bSderaadt 		context_vec_ptr->a = a;
827ae8d569bSderaadt 		context_vec_ptr->b = b;
828ae8d569bSderaadt 		context_vec_ptr->c = c;
829ae8d569bSderaadt 		context_vec_ptr->d = d;
830ae8d569bSderaadt 		return;
831ae8d569bSderaadt 	}
832ae8d569bSderaadt 	switch (opt) {
833ae8d569bSderaadt 
834ae8d569bSderaadt 	case D_NORMAL:
835ae8d569bSderaadt 	case D_EDIT:
836ae8d569bSderaadt 		range(a, b, ",");
837ae8d569bSderaadt 		putchar(a > b ? 'a' : c > d ? 'd' : 'c');
838ae8d569bSderaadt 		if (opt == D_NORMAL)
839ae8d569bSderaadt 			range(c, d, ",");
840ae8d569bSderaadt 		putchar('\n');
841ae8d569bSderaadt 		break;
842ae8d569bSderaadt 	case D_REVERSE:
843ae8d569bSderaadt 		putchar(a > b ? 'a' : c > d ? 'd' : 'c');
844ae8d569bSderaadt 		range(a, b, " ");
845ae8d569bSderaadt 		putchar('\n');
846ae8d569bSderaadt 		break;
847ae8d569bSderaadt 	case D_NREVERSE:
848ae8d569bSderaadt 		if (a > b)
849ae8d569bSderaadt 			printf("a%d %d\n", b, d - c + 1);
850ae8d569bSderaadt 		else {
851ae8d569bSderaadt 			printf("d%d %d\n", a, b - a + 1);
852ae8d569bSderaadt 			if (!(c > d))
853ae8d569bSderaadt 				/* add changed lines */
854ae8d569bSderaadt 				printf("a%d %d\n", b, d - c + 1);
855ae8d569bSderaadt 		}
856ae8d569bSderaadt 		break;
857ae8d569bSderaadt 	}
858ae8d569bSderaadt 	if (opt == D_NORMAL || opt == D_IFDEF) {
859ae8d569bSderaadt 		fetch(ixold, a, b, input[0], "< ", 1);
860ae8d569bSderaadt 		if (a <= b && c <= d && opt == D_NORMAL)
861ae8d569bSderaadt 			prints("---\n");
862ae8d569bSderaadt 	}
863ae8d569bSderaadt 	fetch(ixnew, c, d, input[1], opt == D_NORMAL ? "> " : "", 0);
864ae8d569bSderaadt 	if ((opt == D_EDIT || opt == D_REVERSE) && c <= d)
865ae8d569bSderaadt 		prints(".\n");
866ae8d569bSderaadt 	if (inifdef) {
867ae8d569bSderaadt 		fprintf(stdout, "#endif /* %s */\n", endifname);
868ae8d569bSderaadt 		inifdef = 0;
869ae8d569bSderaadt 	}
870ae8d569bSderaadt }
871ae8d569bSderaadt 
87226da422aStedu static void
87326da422aStedu range(int a, int b, char *separator)
874ae8d569bSderaadt {
875ae8d569bSderaadt 	printf("%d", a > b ? b : a);
876ae8d569bSderaadt 	if (a < b) {
877ae8d569bSderaadt 		printf("%s%d", separator, b);
878ae8d569bSderaadt 	}
879ae8d569bSderaadt }
880ae8d569bSderaadt 
88126da422aStedu static void
88226da422aStedu fetch(long *f, int a, int b, FILE *lb, char *s, int oldfile)
883ae8d569bSderaadt {
88426da422aStedu 	int i, j;
88526da422aStedu 	int c;
88626da422aStedu 	int col;
88726da422aStedu 	int nc;
888ae8d569bSderaadt 	int oneflag = (*ifdef1 != '\0') != (*ifdef2 != '\0');
889ae8d569bSderaadt 
890ae8d569bSderaadt 	/*
891ae8d569bSderaadt 	 * When doing #ifdef's, copy down to current line
892ae8d569bSderaadt 	 * if this is the first file, so that stuff makes it to output.
893ae8d569bSderaadt 	 */
894ae8d569bSderaadt 	if (opt == D_IFDEF && oldfile) {
895ae8d569bSderaadt 		long curpos = ftell(lb);
896ae8d569bSderaadt 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
897ae8d569bSderaadt 		nc = f[a > b ? b : a - 1] - curpos;
898ae8d569bSderaadt 		for (i = 0; i < nc; i++)
899ae8d569bSderaadt 			putchar(getc(lb));
900ae8d569bSderaadt 	}
901ae8d569bSderaadt 	if (a > b)
902ae8d569bSderaadt 		return;
903ae8d569bSderaadt 	if (opt == D_IFDEF) {
904ae8d569bSderaadt 		if (inifdef)
905ae8d569bSderaadt 			fprintf(stdout, "#else /* %s%s */\n", oneflag && oldfile == 1 ? "!" : "", ifdef2);
906ae8d569bSderaadt 		else {
907ae8d569bSderaadt 			if (oneflag) {
908ae8d569bSderaadt 				/* There was only one ifdef given */
909ae8d569bSderaadt 				endifname = ifdef2;
910ae8d569bSderaadt 				if (oldfile)
911ae8d569bSderaadt 					fprintf(stdout, "#ifndef %s\n", endifname);
912ae8d569bSderaadt 				else
913ae8d569bSderaadt 					fprintf(stdout, "#ifdef %s\n", endifname);
91426da422aStedu 			} else {
915ae8d569bSderaadt 				endifname = oldfile ? ifdef1 : ifdef2;
916ae8d569bSderaadt 				fprintf(stdout, "#ifdef %s\n", endifname);
917ae8d569bSderaadt 			}
918ae8d569bSderaadt 		}
919ae8d569bSderaadt 		inifdef = 1 + oldfile;
920ae8d569bSderaadt 	}
921ae8d569bSderaadt 	for (i = a; i <= b; i++) {
922ae8d569bSderaadt 		fseek(lb, f[i - 1], 0);
923ae8d569bSderaadt 		nc = f[i] - f[i - 1];
924ae8d569bSderaadt 		if (opt != D_IFDEF)
925ae8d569bSderaadt 			prints(s);
926ae8d569bSderaadt 		col = 0;
927ae8d569bSderaadt 		for (j = 0; j < nc; j++) {
928ae8d569bSderaadt 			c = getc(lb);
929ae8d569bSderaadt 			if (c == '\t' && tflag)
930ae8d569bSderaadt 				do
931ae8d569bSderaadt 					putchar(' ');
932ae8d569bSderaadt 				while (++col & 7);
933ae8d569bSderaadt 			else {
934ae8d569bSderaadt 				putchar(c);
935ae8d569bSderaadt 				col++;
936ae8d569bSderaadt 			}
937ae8d569bSderaadt 		}
938ae8d569bSderaadt 	}
939ae8d569bSderaadt 
940ae8d569bSderaadt 	if (inifdef && !wantelses) {
941ae8d569bSderaadt 		fprintf(stdout, "#endif /* %s */\n", endifname);
942ae8d569bSderaadt 		inifdef = 0;
943ae8d569bSderaadt 	}
944ae8d569bSderaadt }
945ae8d569bSderaadt 
946ae8d569bSderaadt #define POW2			/* define only if HALFLONG is 2**n */
947ae8d569bSderaadt #define HALFLONG 16
948ae8d569bSderaadt #define low(x)	(x&((1L<<HALFLONG)-1))
949ae8d569bSderaadt #define high(x)	(x>>HALFLONG)
950ae8d569bSderaadt 
951ae8d569bSderaadt /*
952ae8d569bSderaadt  * hashing has the effect of
953ae8d569bSderaadt  * arranging line in 7-bit bytes and then
954ae8d569bSderaadt  * summing 1-s complement in 16-bit hunks
955ae8d569bSderaadt  */
95626da422aStedu static int
95726da422aStedu readhash(FILE *f)
958ae8d569bSderaadt {
95926da422aStedu 	long sum;
96026da422aStedu 	unsigned shift;
96126da422aStedu 	int t;
96226da422aStedu 	int space;
963ae8d569bSderaadt 
964ae8d569bSderaadt 	sum = 1;
965ae8d569bSderaadt 	space = 0;
966ae8d569bSderaadt 	if (!bflag && !wflag) {
967ae8d569bSderaadt 		if (iflag)
968ae8d569bSderaadt 			for (shift = 0; (t = getc(f)) != '\n'; shift += 7) {
969ae8d569bSderaadt 				if (t == -1)
970ae8d569bSderaadt 					return (0);
971ae8d569bSderaadt 				sum += (long)chrtran[t] << (shift
972ae8d569bSderaadt #ifdef POW2
973ae8d569bSderaadt 				    &= HALFLONG - 1);
974ae8d569bSderaadt #else
975ae8d569bSderaadt 				    %= HALFLONG);
976ae8d569bSderaadt #endif
977ae8d569bSderaadt 			}
978ae8d569bSderaadt 		else
979ae8d569bSderaadt 			for (shift = 0; (t = getc(f)) != '\n'; shift += 7) {
980ae8d569bSderaadt 				if (t == -1)
981ae8d569bSderaadt 					return (0);
982ae8d569bSderaadt 				sum += (long)t << (shift
983ae8d569bSderaadt #ifdef POW2
984ae8d569bSderaadt 				    &= HALFLONG - 1);
985ae8d569bSderaadt #else
986ae8d569bSderaadt 				    %= HALFLONG);
987ae8d569bSderaadt #endif
988ae8d569bSderaadt 			}
989ae8d569bSderaadt 	} else {
990ae8d569bSderaadt 		for (shift = 0;;) {
991ae8d569bSderaadt 			switch (t = getc(f)) {
992ae8d569bSderaadt 			case -1:
993ae8d569bSderaadt 				return (0);
994ae8d569bSderaadt 			case '\t':
995ae8d569bSderaadt 			case ' ':
996ae8d569bSderaadt 				space++;
997ae8d569bSderaadt 				continue;
998ae8d569bSderaadt 			default:
999ae8d569bSderaadt 				if (space && !wflag) {
1000ae8d569bSderaadt 					shift += 7;
1001ae8d569bSderaadt 					space = 0;
1002ae8d569bSderaadt 				}
1003ae8d569bSderaadt 				sum += (long)chrtran[t] << (shift
1004ae8d569bSderaadt #ifdef POW2
1005ae8d569bSderaadt 				    &= HALFLONG - 1);
1006ae8d569bSderaadt #else
1007ae8d569bSderaadt 				    %= HALFLONG);
1008ae8d569bSderaadt #endif
1009ae8d569bSderaadt 				shift += 7;
1010ae8d569bSderaadt 				continue;
1011ae8d569bSderaadt 			case '\n':
1012ae8d569bSderaadt 				break;
1013ae8d569bSderaadt 			}
1014ae8d569bSderaadt 			break;
1015ae8d569bSderaadt 		}
1016ae8d569bSderaadt 	}
1017ae8d569bSderaadt 	sum = low(sum) + high(sum);
1018ae8d569bSderaadt 	return ((short) low(sum) + (short) high(sum));
1019ae8d569bSderaadt }
1020ae8d569bSderaadt 
102126da422aStedu static int
102226da422aStedu asciifile(FILE *f)
1023ae8d569bSderaadt {
1024ae8d569bSderaadt 	char buf[BUFSIZ];
102526da422aStedu 	int cnt;
102626da422aStedu 	char *cp;
1027ae8d569bSderaadt 
102826da422aStedu 	fseek(f, 0, 0);
1029ae8d569bSderaadt 	cnt = fread(buf, 1, BUFSIZ, f);
1030ae8d569bSderaadt 	cp = buf;
1031ae8d569bSderaadt 	while (--cnt >= 0)
1032ae8d569bSderaadt 		if (*cp++ & 0200)
1033ae8d569bSderaadt 			return (0);
1034ae8d569bSderaadt 	return (1);
1035ae8d569bSderaadt }
1036ae8d569bSderaadt 
1037ae8d569bSderaadt 
1038ae8d569bSderaadt /* dump accumulated "context" diff changes */
103926da422aStedu static void
104026da422aStedu dump_context_vec(void)
1041ae8d569bSderaadt {
104226da422aStedu 	int a, b, c, d;
104326da422aStedu 	char ch;
104426da422aStedu 	struct context_vec *cvp = context_vec_start;
104526da422aStedu 	int lowa, upb, lowc, upd;
104626da422aStedu 	int do_output;
1047ae8d569bSderaadt 
1048ae8d569bSderaadt 	if (cvp > context_vec_ptr)
1049ae8d569bSderaadt 		return;
1050ae8d569bSderaadt 
105126da422aStedu 	b = d = 0;		/* gcc */
1052ae8d569bSderaadt 	lowa = max(1, cvp->a - context);
1053ae8d569bSderaadt 	upb = min(len[0], context_vec_ptr->b + context);
1054ae8d569bSderaadt 	lowc = max(1, cvp->c - context);
1055ae8d569bSderaadt 	upd = min(len[1], context_vec_ptr->d + context);
1056ae8d569bSderaadt 
1057ae8d569bSderaadt 	printf("***************\n*** ");
1058ae8d569bSderaadt 	range(lowa, upb, ",");
1059ae8d569bSderaadt 	printf(" ****\n");
1060ae8d569bSderaadt 
1061ae8d569bSderaadt 	/*
1062ae8d569bSderaadt 	 * output changes to the "old" file.  The first loop suppresses
1063ae8d569bSderaadt 	 * output if there were no changes to the "old" file (we'll see
1064ae8d569bSderaadt 	 * the "old" lines as context in the "new" list).
1065ae8d569bSderaadt 	 */
1066ae8d569bSderaadt 	do_output = 0;
1067ae8d569bSderaadt 	for (; cvp <= context_vec_ptr; cvp++)
1068ae8d569bSderaadt 		if (cvp->a <= cvp->b) {
1069ae8d569bSderaadt 			cvp = context_vec_start;
1070ae8d569bSderaadt 			do_output++;
1071ae8d569bSderaadt 			break;
1072ae8d569bSderaadt 		}
1073ae8d569bSderaadt 	if (do_output) {
1074ae8d569bSderaadt 		while (cvp <= context_vec_ptr) {
107526da422aStedu 			a = cvp->a;
107626da422aStedu 			b = cvp->b;
107726da422aStedu 			c = cvp->c;
107826da422aStedu 			d = cvp->d;
1079ae8d569bSderaadt 
1080ae8d569bSderaadt 			if (a <= b && c <= d)
1081ae8d569bSderaadt 				ch = 'c';
1082ae8d569bSderaadt 			else
1083ae8d569bSderaadt 				ch = (a <= b) ? 'd' : 'a';
1084ae8d569bSderaadt 
1085ae8d569bSderaadt 			if (ch == 'a')
108626da422aStedu 				fetch(ixold, lowa, b, input[0], "  ", 0);
1087ae8d569bSderaadt 			else {
108826da422aStedu 				fetch(ixold, lowa, a - 1, input[0], "  ", 0);
108926da422aStedu 				fetch(ixold, a, b, input[0], ch == 'c' ? "! " : "- ", 0);
1090ae8d569bSderaadt 			}
1091ae8d569bSderaadt 			lowa = b + 1;
1092ae8d569bSderaadt 			cvp++;
1093ae8d569bSderaadt 		}
109426da422aStedu 		fetch(ixold, b + 1, upb, input[0], "  ", 0);
1095ae8d569bSderaadt 	}
1096ae8d569bSderaadt 	/* output changes to the "new" file */
1097ae8d569bSderaadt 	printf("--- ");
1098ae8d569bSderaadt 	range(lowc, upd, ",");
1099ae8d569bSderaadt 	printf(" ----\n");
1100ae8d569bSderaadt 
1101ae8d569bSderaadt 	do_output = 0;
1102ae8d569bSderaadt 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
1103ae8d569bSderaadt 		if (cvp->c <= cvp->d) {
1104ae8d569bSderaadt 			cvp = context_vec_start;
1105ae8d569bSderaadt 			do_output++;
1106ae8d569bSderaadt 			break;
1107ae8d569bSderaadt 		}
1108ae8d569bSderaadt 	if (do_output) {
1109ae8d569bSderaadt 		while (cvp <= context_vec_ptr) {
111026da422aStedu 			a = cvp->a;
111126da422aStedu 			b = cvp->b;
111226da422aStedu 			c = cvp->c;
111326da422aStedu 			d = cvp->d;
1114ae8d569bSderaadt 
1115ae8d569bSderaadt 			if (a <= b && c <= d)
1116ae8d569bSderaadt 				ch = 'c';
1117ae8d569bSderaadt 			else
1118ae8d569bSderaadt 				ch = (a <= b) ? 'd' : 'a';
1119ae8d569bSderaadt 
1120ae8d569bSderaadt 			if (ch == 'd')
112126da422aStedu 				fetch(ixnew, lowc, d, input[1], "  ", 0);
1122ae8d569bSderaadt 			else {
112326da422aStedu 				fetch(ixnew, lowc, c - 1, input[1], "  ", 0);
112426da422aStedu 				fetch(ixnew, c, d, input[1], ch == 'c' ? "! " : "+ ", 0);
1125ae8d569bSderaadt 			}
1126ae8d569bSderaadt 			lowc = d + 1;
1127ae8d569bSderaadt 			cvp++;
1128ae8d569bSderaadt 		}
112926da422aStedu 		fetch(ixnew, d + 1, upd, input[1], "  ", 0);
1130ae8d569bSderaadt 	}
1131ae8d569bSderaadt 	context_vec_ptr = context_vec_start - 1;
1132ae8d569bSderaadt }
1133