xref: /openbsd-src/usr.bin/diff/diffreg.c (revision 90f56ad89a9a2865b738e2fb968a31f24e4f9aa7)
1*90f56ad8Smillert /*	$OpenBSD: diffreg.c,v 1.22 2003/06/26 22:04:45 millert 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 
122ae8d569bSderaadt struct cand {
123ae8d569bSderaadt 	int x;
124ae8d569bSderaadt 	int y;
125ae8d569bSderaadt 	int pred;
126ae8d569bSderaadt } cand;
12726da422aStedu 
128ae8d569bSderaadt struct line {
129ae8d569bSderaadt 	int serial;
130ae8d569bSderaadt 	int value;
13192af4c2dStedu } *file[2];
13226da422aStedu 
133ae8d569bSderaadt int len[2];
1340c9f493eStedu struct line *sfile[2];	/* shortened by pruning common prefix and suffix */
135ae8d569bSderaadt int slen[2];
136ae8d569bSderaadt int pref, suff;			/* length of prefix and suffix */
137ae8d569bSderaadt int *class;			/* will be overlaid on file[0] */
138ae8d569bSderaadt int *member;			/* will be overlaid on file[1] */
139ae8d569bSderaadt int *klist;			/* will be overlaid on file[0] after class */
140ae8d569bSderaadt struct cand *clist;		/* merely a free storage pot for candidates */
141ae8d569bSderaadt int clen = 0;
142ae8d569bSderaadt int *J;				/* will be overlaid on class */
143ae8d569bSderaadt long *ixold;			/* will be overlaid on klist */
144ae8d569bSderaadt long *ixnew;			/* will be overlaid on file[1] */
1458a15d8deSderaadt u_char *chrtran;		/* translation table for case-folding */
146ae8d569bSderaadt 
14726da422aStedu static void fetch(long *, int, int, FILE *, char *, int);
14826da422aStedu static void output(void);
14926da422aStedu static void check(void);
15026da422aStedu static void range(int, int, char *);
15126da422aStedu static void dump_context_vec(void);
1529de32c1bSmillert static void dump_unified_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  */
1718a15d8deSderaadt u_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 
1988a15d8deSderaadt u_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 {
228ae8d569bSderaadt 	char buf1[BUFSIZ], buf2[BUFSIZ];
22948b8c3e3Sderaadt 	FILE *f1, *f2;
23048b8c3e3Sderaadt 	int i, j;
231ae8d569bSderaadt 
232ae8d569bSderaadt 	if (hflag) {
233ae8d569bSderaadt 		diffargv[0] = "diffh";
234ae8d569bSderaadt 		execv(diffh, diffargv);
23566e5764eSmillert 		error("%s", diffh);
236ae8d569bSderaadt 	}
237ae8d569bSderaadt 	chrtran = (iflag ? cup2low : clow2low);
23866e5764eSmillert 	if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0)
23966e5764eSmillert 		errorx("can't specify - -");
24048b947b7Smillert 	if (S_ISDIR(stb1.st_mode)) {
241ae8d569bSderaadt 		file1 = splice(file1, file2);
24266e5764eSmillert 		if (stat(file1, &stb1) < 0)
24366e5764eSmillert 			error("%s", file1);
24448b947b7Smillert 	} else if (!S_ISREG(stb1.st_mode) || strcmp(file1, "-") == 0) {
24548b947b7Smillert 		file1 = copytemp(file1, 1);
24666e5764eSmillert 		if (stat(file1, &stb1) < 0)
24766e5764eSmillert 			error("%s", file1);
24848b947b7Smillert 	}
24948b947b7Smillert 	if (S_ISDIR(stb2.st_mode)) {
250ae8d569bSderaadt 		file2 = splice(file2, file1);
25166e5764eSmillert 		if (stat(file2, &stb2) < 0)
25266e5764eSmillert 			error("%s", file2);
25348b947b7Smillert 	} else if (!S_ISREG(stb2.st_mode) || strcmp(file2, "-") == 0) {
25448b947b7Smillert 		file2 = copytemp(file2, 2);
25566e5764eSmillert 		if (stat(file2, &stb2) < 0)
25666e5764eSmillert 			error("%s", file2);
257ae8d569bSderaadt 	}
25866e5764eSmillert 	if ((f1 = fopen(file1, "r")) == NULL)
25966e5764eSmillert 		error("%s", file1);
26066e5764eSmillert 	if ((f2 = fopen(file2, "r")) == NULL)
26166e5764eSmillert 		error("%s", file2);
26248b947b7Smillert 	if (S_ISREG(stb1.st_mode) && S_ISREG(stb2.st_mode) &&
26348b947b7Smillert 	    stb1.st_size != stb2.st_size)
264ae8d569bSderaadt 		goto notsame;
265ae8d569bSderaadt 	for (;;) {
266ae8d569bSderaadt 		i = fread(buf1, 1, BUFSIZ, f1);
267ae8d569bSderaadt 		j = fread(buf2, 1, BUFSIZ, f2);
268ae8d569bSderaadt 		if (i < 0 || j < 0 || i != j)
269ae8d569bSderaadt 			goto notsame;
270ae8d569bSderaadt 		if (i == 0 && j == 0) {
271ae8d569bSderaadt 			fclose(f1);
272ae8d569bSderaadt 			fclose(f2);
273ae8d569bSderaadt 			status = 0;	/* files don't differ */
274ae8d569bSderaadt 			goto same;
275ae8d569bSderaadt 		}
276ae8d569bSderaadt 		for (j = 0; j < i; j++)
277ae8d569bSderaadt 			if (buf1[j] != buf2[j])
278ae8d569bSderaadt 				goto notsame;
279ae8d569bSderaadt 	}
280ae8d569bSderaadt notsame:
281ae8d569bSderaadt 	/*
282ae8d569bSderaadt 	 * Files certainly differ at this point; set status accordingly
283ae8d569bSderaadt 	 */
284ae8d569bSderaadt 	status = 1;
285ae8d569bSderaadt 	if (!asciifile(f1) || !asciifile(f2)) {
286ae8d569bSderaadt 		printf("Binary files %s and %s differ\n", file1, file2);
28766e5764eSmillert 		exit(status);
288ae8d569bSderaadt 	}
289ae8d569bSderaadt 	prepare(0, f1);
290ae8d569bSderaadt 	prepare(1, f2);
291ae8d569bSderaadt 	fclose(f1);
292ae8d569bSderaadt 	fclose(f2);
293ae8d569bSderaadt 	prune();
294ae8d569bSderaadt 	sort(sfile[0], slen[0]);
295ae8d569bSderaadt 	sort(sfile[1], slen[1]);
296ae8d569bSderaadt 
297ae8d569bSderaadt 	member = (int *)file[1];
298ae8d569bSderaadt 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
29949dffe13Smillert 	member = erealloc(member, (slen[1] + 2) * sizeof(int));
300ae8d569bSderaadt 
301ae8d569bSderaadt 	class = (int *)file[0];
302ae8d569bSderaadt 	unsort(sfile[0], slen[0], class);
30349dffe13Smillert 	class = erealloc(class, (slen[0] + 2) * sizeof(int));
304ae8d569bSderaadt 
30549dffe13Smillert 	klist = emalloc((slen[0] + 2) * sizeof(int));
30649dffe13Smillert 	clist = emalloc(sizeof(cand));
307ae8d569bSderaadt 	i = stone(class, slen[0], member, klist);
30826da422aStedu 	free(member);
30926da422aStedu 	free(class);
310ae8d569bSderaadt 
31149dffe13Smillert 	J = emalloc((len[0] + 2) * sizeof(int));
312ae8d569bSderaadt 	unravel(klist[i]);
31326da422aStedu 	free(clist);
31426da422aStedu 	free(klist);
315ae8d569bSderaadt 
31649dffe13Smillert 	ixold = emalloc((len[0] + 2) * sizeof(long));
31749dffe13Smillert 	ixnew = emalloc((len[1] + 2) * sizeof(long));
318ae8d569bSderaadt 	check();
319ae8d569bSderaadt 	output();
320ae8d569bSderaadt 	status = anychange;
321ae8d569bSderaadt same:
3229de32c1bSmillert 	if (anychange == 0 && (opt == D_CONTEXT || opt == D_UNIFIED))
323ae8d569bSderaadt 		printf("No differences encountered\n");
324ae8d569bSderaadt }
325ae8d569bSderaadt 
32648b947b7Smillert char *tempfiles[2];
327ae8d569bSderaadt 
328ae8d569bSderaadt char *
32948b947b7Smillert copytemp(const char *file, int n)
330ae8d569bSderaadt {
33148b947b7Smillert 	char buf[BUFSIZ], *tempdir, *tempfile;
33248b947b7Smillert 	int i, ifd, ofd;
33348b947b7Smillert 
33448b947b7Smillert 	if (n != 1 && n != 2)
33548b947b7Smillert 		return (NULL);
33648b947b7Smillert 
33748b947b7Smillert 	if (strcmp(file, "-") == 0)
33848b947b7Smillert 		ifd = STDIN_FILENO;
33966e5764eSmillert 	else if ((ifd = open(file, O_RDONLY, 0644)) < 0)
34066e5764eSmillert 		error("%s", file);
34148b947b7Smillert 
34248b947b7Smillert 	if ((tempdir = getenv("TMPDIR")) == NULL)
34348b947b7Smillert 		tempdir = _PATH_TMP;
34466e5764eSmillert 	if (asprintf(&tempfile, "%s/diff%d.XXXXXXXX", tempdir, n) == -1)
34566e5764eSmillert 		error(NULL);
34648b947b7Smillert 	tempfiles[n - 1] = tempfile;
347ae8d569bSderaadt 
3487d9f164dStedu 	signal(SIGHUP, done);
3497d9f164dStedu 	signal(SIGINT, done);
3507d9f164dStedu 	signal(SIGPIPE, done);
3517d9f164dStedu 	signal(SIGTERM, done);
35248b947b7Smillert 	ofd = mkstemp(tempfile);
35366e5764eSmillert 	if (ofd < 0)
35466e5764eSmillert 		error("%s", tempfile);
35566e5764eSmillert 	while ((i = read(ifd, buf, BUFSIZ)) > 0) {
35666e5764eSmillert 		if (write(ofd, buf, i) != i)
35766e5764eSmillert 			error("%s", tempfile);
358ae8d569bSderaadt 	}
35948b947b7Smillert 	close(ifd);
36048b947b7Smillert 	close(ofd);
361ae8d569bSderaadt 	return (tempfile);
362ae8d569bSderaadt }
363ae8d569bSderaadt 
364ae8d569bSderaadt char *
36526da422aStedu splice(char *dir, char *file)
366ae8d569bSderaadt {
36749dffe13Smillert 	char *tail, *buf;
36849dffe13Smillert 	size_t len;
369ae8d569bSderaadt 
37066e5764eSmillert 	if (!strcmp(file, "-"))
37166e5764eSmillert 		errorx("can't specify - with other arg directory");
372d226b3cdSderaadt 	tail = strrchr(file, '/');
37349dffe13Smillert 	if (tail == NULL)
374ae8d569bSderaadt 		tail = file;
375ae8d569bSderaadt 	else
376ae8d569bSderaadt 		tail++;
3777f136ccbSvincent 	len = strlen(dir) + 1 + strlen(tail) + 1;
37849dffe13Smillert 	buf = emalloc(len);
37949dffe13Smillert 	snprintf(buf, len, "%s/%s", dir, tail);
38049dffe13Smillert 	return (buf);
381ae8d569bSderaadt }
382ae8d569bSderaadt 
38326da422aStedu static void
38426da422aStedu prepare(int i, FILE *fd)
385ae8d569bSderaadt {
38626da422aStedu 	struct line *p;
38726da422aStedu 	int j, h;
388ae8d569bSderaadt 
3898ae1ab09Sderaadt 	fseek(fd, 0L, SEEK_SET);
39049dffe13Smillert 	p = emalloc(3 * sizeof(struct line));
39126da422aStedu 	for (j = 0; (h = readhash(fd));) {
39249dffe13Smillert 		p = erealloc(p, (++j + 3) * sizeof(struct line));
393ae8d569bSderaadt 		p[j].value = h;
394ae8d569bSderaadt 	}
395ae8d569bSderaadt 	len[i] = j;
396ae8d569bSderaadt 	file[i] = p;
397ae8d569bSderaadt }
398ae8d569bSderaadt 
39926da422aStedu static void
40026da422aStedu prune(void)
401ae8d569bSderaadt {
40226da422aStedu 	int i, j;
40348b8c3e3Sderaadt 
404ae8d569bSderaadt 	for (pref = 0; pref < len[0] && pref < len[1] &&
405ae8d569bSderaadt 	    file[0][pref + 1].value == file[1][pref + 1].value;
4067d2b2b91Sderaadt 	    pref++)
4077d2b2b91Sderaadt 		;
408ae8d569bSderaadt 	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
409ae8d569bSderaadt 	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
4107d2b2b91Sderaadt 	    suff++)
4117d2b2b91Sderaadt 		;
412ae8d569bSderaadt 	for (j = 0; j < 2; j++) {
413ae8d569bSderaadt 		sfile[j] = file[j] + pref;
414ae8d569bSderaadt 		slen[j] = len[j] - pref - suff;
415ae8d569bSderaadt 		for (i = 0; i <= slen[j]; i++)
416ae8d569bSderaadt 			sfile[j][i].serial = i;
417ae8d569bSderaadt 	}
418ae8d569bSderaadt }
419ae8d569bSderaadt 
42026da422aStedu static void
42126da422aStedu equiv(struct line *a, int n, struct line *b, int m, int *c)
422ae8d569bSderaadt {
42326da422aStedu 	int i, j;
42448b8c3e3Sderaadt 
425ae8d569bSderaadt 	i = j = 1;
426ae8d569bSderaadt 	while (i <= n && j <= m) {
427ae8d569bSderaadt 		if (a[i].value < b[j].value)
428ae8d569bSderaadt 			a[i++].value = 0;
429ae8d569bSderaadt 		else if (a[i].value == b[j].value)
430ae8d569bSderaadt 			a[i++].value = j;
431ae8d569bSderaadt 		else
432ae8d569bSderaadt 			j++;
433ae8d569bSderaadt 	}
434ae8d569bSderaadt 	while (i <= n)
435ae8d569bSderaadt 		a[i++].value = 0;
436ae8d569bSderaadt 	b[m + 1].value = 0;
437ae8d569bSderaadt 	j = 0;
438ae8d569bSderaadt 	while (++j <= m) {
439ae8d569bSderaadt 		c[j] = -b[j].serial;
440ae8d569bSderaadt 		while (b[j + 1].value == b[j].value) {
441ae8d569bSderaadt 			j++;
442ae8d569bSderaadt 			c[j] = b[j].serial;
443ae8d569bSderaadt 		}
444ae8d569bSderaadt 	}
445ae8d569bSderaadt 	c[j] = -1;
446ae8d569bSderaadt }
447ae8d569bSderaadt 
44826da422aStedu static int
44926da422aStedu stone(int *a, int n, int *b, int *c)
450ae8d569bSderaadt {
45148b8c3e3Sderaadt 	int i, k, y, j, l;
45248b8c3e3Sderaadt 	int oldc, tc, oldl;
45348b8c3e3Sderaadt 
454ae8d569bSderaadt 	k = 0;
455ae8d569bSderaadt 	c[0] = newcand(0, 0, 0);
456ae8d569bSderaadt 	for (i = 1; i <= n; i++) {
457ae8d569bSderaadt 		j = a[i];
458ae8d569bSderaadt 		if (j == 0)
459ae8d569bSderaadt 			continue;
460ae8d569bSderaadt 		y = -b[j];
461ae8d569bSderaadt 		oldl = 0;
462ae8d569bSderaadt 		oldc = c[0];
463ae8d569bSderaadt 		do {
464ae8d569bSderaadt 			if (y <= clist[oldc].y)
465ae8d569bSderaadt 				continue;
466ae8d569bSderaadt 			l = search(c, k, y);
467ae8d569bSderaadt 			if (l != oldl + 1)
468ae8d569bSderaadt 				oldc = c[l - 1];
469ae8d569bSderaadt 			if (l <= k) {
470ae8d569bSderaadt 				if (clist[c[l]].y <= y)
471ae8d569bSderaadt 					continue;
472ae8d569bSderaadt 				tc = c[l];
473ae8d569bSderaadt 				c[l] = newcand(i, y, oldc);
474ae8d569bSderaadt 				oldc = tc;
475ae8d569bSderaadt 				oldl = l;
476ae8d569bSderaadt 			} else {
477ae8d569bSderaadt 				c[l] = newcand(i, y, oldc);
478ae8d569bSderaadt 				k++;
479ae8d569bSderaadt 				break;
480ae8d569bSderaadt 			}
481ae8d569bSderaadt 		} while ((y = b[++j]) > 0);
482ae8d569bSderaadt 	}
483ae8d569bSderaadt 	return (k);
484ae8d569bSderaadt }
485ae8d569bSderaadt 
48626da422aStedu static int
48726da422aStedu newcand(int x, int y, int pred)
488ae8d569bSderaadt {
48926da422aStedu 	struct cand *q;
49026da422aStedu 
49149dffe13Smillert 	clist = erealloc(clist, ++clen * sizeof(cand));
492ae8d569bSderaadt 	q = clist + clen - 1;
493ae8d569bSderaadt 	q->x = x;
494ae8d569bSderaadt 	q->y = y;
495ae8d569bSderaadt 	q->pred = pred;
496ae8d569bSderaadt 	return (clen - 1);
497ae8d569bSderaadt }
498ae8d569bSderaadt 
49926da422aStedu static int
50026da422aStedu search(int *c, int k, int y)
501ae8d569bSderaadt {
50248b8c3e3Sderaadt 	int i, j, l, t;
50348b8c3e3Sderaadt 
504ae8d569bSderaadt 	if (clist[c[k]].y < y)	/* quick look for typical case */
505ae8d569bSderaadt 		return (k + 1);
506ae8d569bSderaadt 	i = 0;
507ae8d569bSderaadt 	j = k + 1;
508ae8d569bSderaadt 	while (1) {
509ae8d569bSderaadt 		l = i + j;
510ae8d569bSderaadt 		if ((l >>= 1) <= i)
511ae8d569bSderaadt 			break;
512ae8d569bSderaadt 		t = clist[c[l]].y;
513ae8d569bSderaadt 		if (t > y)
514ae8d569bSderaadt 			j = l;
515ae8d569bSderaadt 		else if (t < y)
516ae8d569bSderaadt 			i = l;
517ae8d569bSderaadt 		else
518ae8d569bSderaadt 			return (l);
519ae8d569bSderaadt 	}
520ae8d569bSderaadt 	return (l + 1);
521ae8d569bSderaadt }
522ae8d569bSderaadt 
52326da422aStedu static void
52426da422aStedu unravel(int p)
525ae8d569bSderaadt {
52626da422aStedu 	struct cand *q;
52748b8c3e3Sderaadt 	int i;
52848b8c3e3Sderaadt 
529ae8d569bSderaadt 	for (i = 0; i <= len[0]; i++)
530ae8d569bSderaadt 		J[i] = i <= pref ? i :
5317d2b2b91Sderaadt 		    i > len[0] - suff ? i + len[1] - len[0] : 0;
532ae8d569bSderaadt 	for (q = clist + p; q->y != 0; q = clist + q->pred)
533ae8d569bSderaadt 		J[q->x + pref] = q->y + pref;
534ae8d569bSderaadt }
535ae8d569bSderaadt 
53626da422aStedu /*
53749dffe13Smillert  * Check does double duty:
53849dffe13Smillert  *  1.	ferret out any fortuitous correspondences due
53949dffe13Smillert  *	to confounding by hashing (which result in "jackpot")
54049dffe13Smillert  *  2.  collect random access indexes to the two files
54126da422aStedu  */
54226da422aStedu static void
54326da422aStedu check(void)
544ae8d569bSderaadt {
54548b8c3e3Sderaadt 	int i, j, jackpot, c, d;
546ae8d569bSderaadt 	long ctold, ctnew;
547ae8d569bSderaadt 
54866e5764eSmillert 	if ((input[0] = fopen(file1, "r")) == NULL)
54966e5764eSmillert 		error("%s", file1);
55066e5764eSmillert 	if ((input[1] = fopen(file2, "r")) == NULL)
55166e5764eSmillert 		error("%s", file2);
552ae8d569bSderaadt 	j = 1;
553ae8d569bSderaadt 	ixold[0] = ixnew[0] = 0;
554ae8d569bSderaadt 	jackpot = 0;
555ae8d569bSderaadt 	ctold = ctnew = 0;
556ae8d569bSderaadt 	for (i = 1; i <= len[0]; i++) {
557ae8d569bSderaadt 		if (J[i] == 0) {
558ae8d569bSderaadt 			ixold[i] = ctold += skipline(0);
559ae8d569bSderaadt 			continue;
560ae8d569bSderaadt 		}
561ae8d569bSderaadt 		while (j < J[i]) {
562ae8d569bSderaadt 			ixnew[j] = ctnew += skipline(1);
563ae8d569bSderaadt 			j++;
564ae8d569bSderaadt 		}
565ae8d569bSderaadt 		if (bflag || wflag || iflag) {
566ae8d569bSderaadt 			for (;;) {
567ae8d569bSderaadt 				c = getc(input[0]);
568ae8d569bSderaadt 				d = getc(input[1]);
569ae8d569bSderaadt 				ctold++;
570ae8d569bSderaadt 				ctnew++;
571ae8d569bSderaadt 				if (bflag && isspace(c) && isspace(d)) {
572ae8d569bSderaadt 					do {
573ae8d569bSderaadt 						if (c == '\n')
574ae8d569bSderaadt 							break;
575ae8d569bSderaadt 						ctold++;
576ae8d569bSderaadt 					} while (isspace(c = getc(input[0])));
577ae8d569bSderaadt 					do {
578ae8d569bSderaadt 						if (d == '\n')
579ae8d569bSderaadt 							break;
580ae8d569bSderaadt 						ctnew++;
581ae8d569bSderaadt 					} while (isspace(d = getc(input[1])));
582ae8d569bSderaadt 				} else if (wflag) {
583ae8d569bSderaadt 					while (isspace(c) && c != '\n') {
584ae8d569bSderaadt 						c = getc(input[0]);
585ae8d569bSderaadt 						ctold++;
586ae8d569bSderaadt 					}
587ae8d569bSderaadt 					while (isspace(d) && d != '\n') {
588ae8d569bSderaadt 						d = getc(input[1]);
589ae8d569bSderaadt 						ctnew++;
590ae8d569bSderaadt 					}
591ae8d569bSderaadt 				}
592ae8d569bSderaadt 				if (chrtran[c] != chrtran[d]) {
593ae8d569bSderaadt 					jackpot++;
594ae8d569bSderaadt 					J[i] = 0;
595ae8d569bSderaadt 					if (c != '\n')
596ae8d569bSderaadt 						ctold += skipline(0);
597ae8d569bSderaadt 					if (d != '\n')
598ae8d569bSderaadt 						ctnew += skipline(1);
599ae8d569bSderaadt 					break;
600ae8d569bSderaadt 				}
601ae8d569bSderaadt 				if (c == '\n')
602ae8d569bSderaadt 					break;
603ae8d569bSderaadt 			}
604ae8d569bSderaadt 		} else {
605ae8d569bSderaadt 			for (;;) {
606ae8d569bSderaadt 				ctold++;
607ae8d569bSderaadt 				ctnew++;
608ae8d569bSderaadt 				if ((c = getc(input[0])) != (d = getc(input[1]))) {
609ae8d569bSderaadt 					/* jackpot++; */
610ae8d569bSderaadt 					J[i] = 0;
611ae8d569bSderaadt 					if (c != '\n')
612ae8d569bSderaadt 						ctold += skipline(0);
613ae8d569bSderaadt 					if (d != '\n')
614ae8d569bSderaadt 						ctnew += skipline(1);
615ae8d569bSderaadt 					break;
616ae8d569bSderaadt 				}
617ae8d569bSderaadt 				if (c == '\n')
618ae8d569bSderaadt 					break;
619ae8d569bSderaadt 			}
620ae8d569bSderaadt 		}
621ae8d569bSderaadt 		ixold[i] = ctold;
622ae8d569bSderaadt 		ixnew[j] = ctnew;
623ae8d569bSderaadt 		j++;
624ae8d569bSderaadt 	}
625ae8d569bSderaadt 	for (; j <= len[1]; j++) {
626ae8d569bSderaadt 		ixnew[j] = ctnew += skipline(1);
627ae8d569bSderaadt 	}
628ae8d569bSderaadt 	fclose(input[0]);
629ae8d569bSderaadt 	fclose(input[1]);
630ae8d569bSderaadt 	/*
63148b8c3e3Sderaadt 	 * if (jackpot)
63248b8c3e3Sderaadt 	 *	fprintf(stderr, "jackpot\n");
633ae8d569bSderaadt 	 */
634ae8d569bSderaadt }
635ae8d569bSderaadt 
63648b8c3e3Sderaadt /* shellsort CACM #201 */
63726da422aStedu static void
63826da422aStedu sort(struct line *a, int n)
63948b8c3e3Sderaadt {
64048b8c3e3Sderaadt 	struct line *ai, *aim, w;
64148b8c3e3Sderaadt 	int j, m = 0, k;
642ae8d569bSderaadt 
643ae8d569bSderaadt 	if (n == 0)
644ae8d569bSderaadt 		return;
645ae8d569bSderaadt 	for (j = 1; j <= n; j *= 2)
646ae8d569bSderaadt 		m = 2 * j - 1;
647ae8d569bSderaadt 	for (m /= 2; m != 0; m /= 2) {
648ae8d569bSderaadt 		k = n - m;
649ae8d569bSderaadt 		for (j = 1; j <= k; j++) {
650ae8d569bSderaadt 			for (ai = &a[j]; ai > a; ai -= m) {
651ae8d569bSderaadt 				aim = &ai[m];
652ae8d569bSderaadt 				if (aim < ai)
653ae8d569bSderaadt 					break;	/* wraparound */
654ae8d569bSderaadt 				if (aim->value > ai[0].value ||
65526da422aStedu 				    (aim->value == ai[0].value &&
65626da422aStedu 					aim->serial > ai[0].serial))
657ae8d569bSderaadt 					break;
658ae8d569bSderaadt 				w.value = ai[0].value;
659ae8d569bSderaadt 				ai[0].value = aim->value;
660ae8d569bSderaadt 				aim->value = w.value;
661ae8d569bSderaadt 				w.serial = ai[0].serial;
662ae8d569bSderaadt 				ai[0].serial = aim->serial;
663ae8d569bSderaadt 				aim->serial = w.serial;
664ae8d569bSderaadt 			}
665ae8d569bSderaadt 		}
666ae8d569bSderaadt 	}
667ae8d569bSderaadt }
668ae8d569bSderaadt 
66926da422aStedu static void
67026da422aStedu unsort(struct line *f, int l, int *b)
671ae8d569bSderaadt {
67248b8c3e3Sderaadt 	int *a, i;
67326da422aStedu 
67449dffe13Smillert 	a = emalloc((l + 1) * sizeof(int));
675ae8d569bSderaadt 	for (i = 1; i <= l; i++)
676ae8d569bSderaadt 		a[f[i].serial] = f[i].value;
677ae8d569bSderaadt 	for (i = 1; i <= l; i++)
678ae8d569bSderaadt 		b[i] = a[i];
67926da422aStedu 	free(a);
680ae8d569bSderaadt }
681ae8d569bSderaadt 
68226da422aStedu static int
68326da422aStedu skipline(int f)
684ae8d569bSderaadt {
68526da422aStedu 	int i, c;
686ae8d569bSderaadt 
687ae8d569bSderaadt 	for (i = 1; (c = getc(input[f])) != '\n'; i++)
688ae8d569bSderaadt 		if (c < 0)
689ae8d569bSderaadt 			return (i);
690ae8d569bSderaadt 	return (i);
691ae8d569bSderaadt }
692ae8d569bSderaadt 
69326da422aStedu static void
69426da422aStedu output(void)
695ae8d569bSderaadt {
69648b8c3e3Sderaadt 	int m, i0, i1, j0, j1;
69748b8c3e3Sderaadt 
698ae8d569bSderaadt 	input[0] = fopen(file1, "r");
699ae8d569bSderaadt 	input[1] = fopen(file2, "r");
700ae8d569bSderaadt 	m = len[0];
701ae8d569bSderaadt 	J[0] = 0;
702ae8d569bSderaadt 	J[m + 1] = len[1] + 1;
70326da422aStedu 	if (opt != D_EDIT) {
70426da422aStedu 		for (i0 = 1; i0 <= m; i0 = i1 + 1) {
70526da422aStedu 			while (i0 <= m && J[i0] == J[i0 - 1] + 1)
70626da422aStedu 				i0++;
707ae8d569bSderaadt 			j0 = J[i0 - 1] + 1;
708ae8d569bSderaadt 			i1 = i0 - 1;
70926da422aStedu 			while (i1 < m && J[i1 + 1] == 0)
71026da422aStedu 				i1++;
711ae8d569bSderaadt 			j1 = J[i1 + 1] - 1;
712ae8d569bSderaadt 			J[i1] = j1;
713ae8d569bSderaadt 			change(i0, i1, j0, j1);
71426da422aStedu 		}
71526da422aStedu 	} else {
71626da422aStedu 		for (i0 = m; i0 >= 1; i0 = i1 - 1) {
71726da422aStedu 			while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
71826da422aStedu 				i0--;
719ae8d569bSderaadt 			j0 = J[i0 + 1] - 1;
720ae8d569bSderaadt 			i1 = i0 + 1;
72126da422aStedu 			while (i1 > 1 && J[i1 - 1] == 0)
72226da422aStedu 				i1--;
723ae8d569bSderaadt 			j1 = J[i1 - 1] + 1;
724ae8d569bSderaadt 			J[i1] = j1;
725ae8d569bSderaadt 			change(i1, i0, j1, j0);
726ae8d569bSderaadt 		}
72726da422aStedu 	}
728ae8d569bSderaadt 	if (m == 0)
729ae8d569bSderaadt 		change(1, 0, 1, len[1]);
730ae8d569bSderaadt 	if (opt == D_IFDEF) {
731ae8d569bSderaadt 		for (;;) {
732ae8d569bSderaadt #define	c i0
733ae8d569bSderaadt 			c = getc(input[0]);
734ae8d569bSderaadt 			if (c < 0)
735ae8d569bSderaadt 				return;
736ae8d569bSderaadt 			putchar(c);
737ae8d569bSderaadt 		}
738ae8d569bSderaadt #undef c
739ae8d569bSderaadt 	}
7409de32c1bSmillert 	if (anychange != 0) {
7419de32c1bSmillert 		if (opt == D_CONTEXT)
742ae8d569bSderaadt 			dump_context_vec();
7439de32c1bSmillert 		else if (opt == D_UNIFIED)
7449de32c1bSmillert 			dump_unified_vec();
7459de32c1bSmillert 	}
746ae8d569bSderaadt }
747ae8d569bSderaadt 
748ae8d569bSderaadt /*
749ae8d569bSderaadt  * The following struct is used to record change information when
750ae8d569bSderaadt  * doing a "context" diff.  (see routine "change" to understand the
751ae8d569bSderaadt  * highly mneumonic field names)
752ae8d569bSderaadt  */
753ae8d569bSderaadt struct context_vec {
754ae8d569bSderaadt 	int a;			/* start line in old file */
755ae8d569bSderaadt 	int b;			/* end line in old file */
756ae8d569bSderaadt 	int c;			/* start line in new file */
757ae8d569bSderaadt 	int d;			/* end line in new file */
758ae8d569bSderaadt };
759ae8d569bSderaadt 
76026da422aStedu struct context_vec *context_vec_start, *context_vec_end, *context_vec_ptr;
761ae8d569bSderaadt 
762ae8d569bSderaadt #define	MAX_CONTEXT	128
763ae8d569bSderaadt 
76426da422aStedu /*
76526da422aStedu  * indicate that there is a difference between lines a and b of the from file
76626da422aStedu  * to get to lines c to d of the to file. If a is greater then b then there
76726da422aStedu  * are no lines in the from file involved and this means that there were
76826da422aStedu  * lines appended (beginning at b). If c is greater than d then there are
76926da422aStedu  * lines missing from the to file.
770ae8d569bSderaadt  */
77126da422aStedu static void
77226da422aStedu change(int a, int b, int c, int d)
773ae8d569bSderaadt {
774ae8d569bSderaadt 	struct stat stbuf;
775ae8d569bSderaadt 
776ae8d569bSderaadt 	if (opt != D_IFDEF && a > b && c > d)
777ae8d569bSderaadt 		return;
778ae8d569bSderaadt 	if (anychange == 0) {
779ae8d569bSderaadt 		anychange = 1;
7809de32c1bSmillert 		if (opt == D_CONTEXT || opt == D_UNIFIED) {
781ae8d569bSderaadt 			stat(file1, &stbuf);
7829de32c1bSmillert 			printf("%s %s	%s", opt == D_CONTEXT ? "***" : "---",
7839de32c1bSmillert 			   file1, ctime(&stbuf.st_mtime));
784ae8d569bSderaadt 			stat(file2, &stbuf);
7859de32c1bSmillert 			printf("%s %s	%s", opt == D_CONTEXT ? "---" : "+++",
7869de32c1bSmillert 			    file2, ctime(&stbuf.st_mtime));
78749dffe13Smillert 			context_vec_start = emalloc(MAX_CONTEXT *
788ae8d569bSderaadt 			    sizeof(struct context_vec));
789ae8d569bSderaadt 			context_vec_end = context_vec_start + MAX_CONTEXT;
790ae8d569bSderaadt 			context_vec_ptr = context_vec_start - 1;
791ae8d569bSderaadt 		}
792ae8d569bSderaadt 	}
7939de32c1bSmillert 	if (opt == D_CONTEXT || opt == D_UNIFIED) {
794ae8d569bSderaadt 		/*
795*90f56ad8Smillert 		 * If this new change is within 'context' lines of
796ae8d569bSderaadt 		 * the previous change, just add it to the change
797ae8d569bSderaadt 		 * record.  If the record is full or if this
798ae8d569bSderaadt 		 * change is more than 'context' lines from the previous
799ae8d569bSderaadt 		 * change, dump the record, reset it & add the new change.
800ae8d569bSderaadt 		 */
801ae8d569bSderaadt 		if (context_vec_ptr >= context_vec_end ||
802ae8d569bSderaadt 		    (context_vec_ptr >= context_vec_start &&
803ae8d569bSderaadt 		    a > (context_vec_ptr->b + 2 * context) &&
8049de32c1bSmillert 		    c > (context_vec_ptr->d + 2 * context))) {
8059de32c1bSmillert 			if (opt == D_CONTEXT)
806ae8d569bSderaadt 				dump_context_vec();
8079de32c1bSmillert 			else
8089de32c1bSmillert 				dump_unified_vec();
8099de32c1bSmillert 		}
810ae8d569bSderaadt 		context_vec_ptr++;
811ae8d569bSderaadt 		context_vec_ptr->a = a;
812ae8d569bSderaadt 		context_vec_ptr->b = b;
813ae8d569bSderaadt 		context_vec_ptr->c = c;
814ae8d569bSderaadt 		context_vec_ptr->d = d;
815ae8d569bSderaadt 		return;
816ae8d569bSderaadt 	}
817ae8d569bSderaadt 	switch (opt) {
818ae8d569bSderaadt 
819ae8d569bSderaadt 	case D_NORMAL:
820ae8d569bSderaadt 	case D_EDIT:
821ae8d569bSderaadt 		range(a, b, ",");
822ae8d569bSderaadt 		putchar(a > b ? 'a' : c > d ? 'd' : 'c');
823ae8d569bSderaadt 		if (opt == D_NORMAL)
824ae8d569bSderaadt 			range(c, d, ",");
825ae8d569bSderaadt 		putchar('\n');
826ae8d569bSderaadt 		break;
827ae8d569bSderaadt 	case D_REVERSE:
828ae8d569bSderaadt 		putchar(a > b ? 'a' : c > d ? 'd' : 'c');
829ae8d569bSderaadt 		range(a, b, " ");
830ae8d569bSderaadt 		putchar('\n');
831ae8d569bSderaadt 		break;
832ae8d569bSderaadt 	case D_NREVERSE:
833ae8d569bSderaadt 		if (a > b)
834ae8d569bSderaadt 			printf("a%d %d\n", b, d - c + 1);
835ae8d569bSderaadt 		else {
836ae8d569bSderaadt 			printf("d%d %d\n", a, b - a + 1);
837ae8d569bSderaadt 			if (!(c > d))
838ae8d569bSderaadt 				/* add changed lines */
839ae8d569bSderaadt 				printf("a%d %d\n", b, d - c + 1);
840ae8d569bSderaadt 		}
841ae8d569bSderaadt 		break;
842ae8d569bSderaadt 	}
843ae8d569bSderaadt 	if (opt == D_NORMAL || opt == D_IFDEF) {
844ae8d569bSderaadt 		fetch(ixold, a, b, input[0], "< ", 1);
845ae8d569bSderaadt 		if (a <= b && c <= d && opt == D_NORMAL)
846ae8d569bSderaadt 			prints("---\n");
847ae8d569bSderaadt 	}
848ae8d569bSderaadt 	fetch(ixnew, c, d, input[1], opt == D_NORMAL ? "> " : "", 0);
849ae8d569bSderaadt 	if ((opt == D_EDIT || opt == D_REVERSE) && c <= d)
850ae8d569bSderaadt 		prints(".\n");
851ae8d569bSderaadt 	if (inifdef) {
852*90f56ad8Smillert 		fprintf(stdout, "#endif /* %s */\n", ifdefname);
853ae8d569bSderaadt 		inifdef = 0;
854ae8d569bSderaadt 	}
855ae8d569bSderaadt }
856ae8d569bSderaadt 
85726da422aStedu static void
85826da422aStedu range(int a, int b, char *separator)
859ae8d569bSderaadt {
860ae8d569bSderaadt 	printf("%d", a > b ? b : a);
86148b8c3e3Sderaadt 	if (a < b)
862ae8d569bSderaadt 		printf("%s%d", separator, b);
863ae8d569bSderaadt }
864ae8d569bSderaadt 
86526da422aStedu static void
86626da422aStedu fetch(long *f, int a, int b, FILE *lb, char *s, int oldfile)
867ae8d569bSderaadt {
86848b8c3e3Sderaadt 	int i, j, c, col, nc;
869ae8d569bSderaadt 
870ae8d569bSderaadt 	/*
871ae8d569bSderaadt 	 * When doing #ifdef's, copy down to current line
872ae8d569bSderaadt 	 * if this is the first file, so that stuff makes it to output.
873ae8d569bSderaadt 	 */
874ae8d569bSderaadt 	if (opt == D_IFDEF && oldfile) {
875ae8d569bSderaadt 		long curpos = ftell(lb);
876ae8d569bSderaadt 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
877ae8d569bSderaadt 		nc = f[a > b ? b : a - 1] - curpos;
878ae8d569bSderaadt 		for (i = 0; i < nc; i++)
879ae8d569bSderaadt 			putchar(getc(lb));
880ae8d569bSderaadt 	}
881ae8d569bSderaadt 	if (a > b)
882ae8d569bSderaadt 		return;
883ae8d569bSderaadt 	if (opt == D_IFDEF) {
884*90f56ad8Smillert 		if (inifdef) {
88548b8c3e3Sderaadt 			fprintf(stdout, "#else /* %s%s */\n",
886*90f56ad8Smillert 			    oldfile == 1 ? "!" : "", ifdefname);
88726da422aStedu 		} else {
888*90f56ad8Smillert 			if (oldfile)
889*90f56ad8Smillert 				fprintf(stdout, "#ifndef %s\n", ifdefname);
890*90f56ad8Smillert 			else
891*90f56ad8Smillert 				fprintf(stdout, "#ifdef %s\n", ifdefname);
892ae8d569bSderaadt 		}
893ae8d569bSderaadt 		inifdef = 1 + oldfile;
894ae8d569bSderaadt 	}
895ae8d569bSderaadt 	for (i = a; i <= b; i++) {
89691cf91eeSderaadt 		fseek(lb, f[i - 1], SEEK_SET);
897ae8d569bSderaadt 		nc = f[i] - f[i - 1];
898ae8d569bSderaadt 		if (opt != D_IFDEF)
899ae8d569bSderaadt 			prints(s);
900ae8d569bSderaadt 		col = 0;
901ae8d569bSderaadt 		for (j = 0; j < nc; j++) {
902ae8d569bSderaadt 			c = getc(lb);
903ae8d569bSderaadt 			if (c == '\t' && tflag)
904ae8d569bSderaadt 				do
905ae8d569bSderaadt 					putchar(' ');
9067d9f164dStedu 				while (++col & 7);
907ae8d569bSderaadt 			else {
908ae8d569bSderaadt 				putchar(c);
909ae8d569bSderaadt 				col++;
910ae8d569bSderaadt 			}
911ae8d569bSderaadt 		}
912ae8d569bSderaadt 	}
913ae8d569bSderaadt }
914ae8d569bSderaadt 
915ae8d569bSderaadt #define POW2			/* define only if HALFLONG is 2**n */
916ae8d569bSderaadt #define HALFLONG 16
917ae8d569bSderaadt #define low(x)	(x&((1L<<HALFLONG)-1))
918ae8d569bSderaadt #define high(x)	(x>>HALFLONG)
919ae8d569bSderaadt 
920ae8d569bSderaadt /*
921ae8d569bSderaadt  * hashing has the effect of
922ae8d569bSderaadt  * arranging line in 7-bit bytes and then
923ae8d569bSderaadt  * summing 1-s complement in 16-bit hunks
924ae8d569bSderaadt  */
92526da422aStedu static int
92626da422aStedu readhash(FILE *f)
927ae8d569bSderaadt {
92848b8c3e3Sderaadt 	unsigned int shift;
92948b8c3e3Sderaadt 	int t, space;
93026da422aStedu 	long sum;
931ae8d569bSderaadt 
932ae8d569bSderaadt 	sum = 1;
933ae8d569bSderaadt 	space = 0;
934ae8d569bSderaadt 	if (!bflag && !wflag) {
935ae8d569bSderaadt 		if (iflag)
936ae8d569bSderaadt 			for (shift = 0; (t = getc(f)) != '\n'; shift += 7) {
937ae8d569bSderaadt 				if (t == -1)
938ae8d569bSderaadt 					return (0);
939ae8d569bSderaadt 				sum += (long)chrtran[t] << (shift
940ae8d569bSderaadt #ifdef POW2
941ae8d569bSderaadt 				    &= HALFLONG - 1);
942ae8d569bSderaadt #else
943ae8d569bSderaadt 				    %= HALFLONG);
944ae8d569bSderaadt #endif
945ae8d569bSderaadt 			}
946ae8d569bSderaadt 		else
947ae8d569bSderaadt 			for (shift = 0; (t = getc(f)) != '\n'; shift += 7) {
948ae8d569bSderaadt 				if (t == -1)
949ae8d569bSderaadt 					return (0);
950ae8d569bSderaadt 				sum += (long)t << (shift
951ae8d569bSderaadt #ifdef POW2
952ae8d569bSderaadt 				    &= HALFLONG - 1);
953ae8d569bSderaadt #else
954ae8d569bSderaadt 				    %= HALFLONG);
955ae8d569bSderaadt #endif
956ae8d569bSderaadt 			}
957ae8d569bSderaadt 	} else {
958ae8d569bSderaadt 		for (shift = 0;;) {
959ae8d569bSderaadt 			switch (t = getc(f)) {
960ae8d569bSderaadt 			case -1:
961ae8d569bSderaadt 				return (0);
962ae8d569bSderaadt 			case '\t':
963ae8d569bSderaadt 			case ' ':
964ae8d569bSderaadt 				space++;
965ae8d569bSderaadt 				continue;
966ae8d569bSderaadt 			default:
967ae8d569bSderaadt 				if (space && !wflag) {
968ae8d569bSderaadt 					shift += 7;
969ae8d569bSderaadt 					space = 0;
970ae8d569bSderaadt 				}
971ae8d569bSderaadt 				sum += (long)chrtran[t] << (shift
972ae8d569bSderaadt #ifdef POW2
973ae8d569bSderaadt 				    &= HALFLONG - 1);
974ae8d569bSderaadt #else
975ae8d569bSderaadt 				    %= HALFLONG);
976ae8d569bSderaadt #endif
977ae8d569bSderaadt 				shift += 7;
978ae8d569bSderaadt 				continue;
979ae8d569bSderaadt 			case '\n':
980ae8d569bSderaadt 				break;
981ae8d569bSderaadt 			}
982ae8d569bSderaadt 			break;
983ae8d569bSderaadt 		}
984ae8d569bSderaadt 	}
985ae8d569bSderaadt 	sum = low(sum) + high(sum);
986ae8d569bSderaadt 	return ((short) low(sum) + (short) high(sum));
987ae8d569bSderaadt }
988ae8d569bSderaadt 
98926da422aStedu static int
99026da422aStedu asciifile(FILE *f)
991ae8d569bSderaadt {
99248b8c3e3Sderaadt 	char buf[BUFSIZ], *cp;
99326da422aStedu 	int cnt;
994ae8d569bSderaadt 
9958ae1ab09Sderaadt 	fseek(f, 0L, SEEK_SET);
996ae8d569bSderaadt 	cnt = fread(buf, 1, BUFSIZ, f);
997ae8d569bSderaadt 	cp = buf;
998ae8d569bSderaadt 	while (--cnt >= 0)
999ae8d569bSderaadt 		if (*cp++ & 0200)
1000ae8d569bSderaadt 			return (0);
1001ae8d569bSderaadt 	return (1);
1002ae8d569bSderaadt }
1003ae8d569bSderaadt 
1004ae8d569bSderaadt /* dump accumulated "context" diff changes */
100526da422aStedu static void
100626da422aStedu dump_context_vec(void)
1007ae8d569bSderaadt {
100848b8c3e3Sderaadt 	struct context_vec *cvp = context_vec_start;
100948b8c3e3Sderaadt 	int lowa, upb, lowc, upd, do_output;
101026da422aStedu 	int a, b, c, d;
101126da422aStedu 	char ch;
1012ae8d569bSderaadt 
1013*90f56ad8Smillert 	if (context_vec_start > context_vec_ptr)
1014ae8d569bSderaadt 		return;
1015ae8d569bSderaadt 
101626da422aStedu 	b = d = 0;		/* gcc */
1017ae8d569bSderaadt 	lowa = max(1, cvp->a - context);
1018ae8d569bSderaadt 	upb = min(len[0], context_vec_ptr->b + context);
1019ae8d569bSderaadt 	lowc = max(1, cvp->c - context);
1020ae8d569bSderaadt 	upd = min(len[1], context_vec_ptr->d + context);
1021ae8d569bSderaadt 
1022ae8d569bSderaadt 	printf("***************\n*** ");
1023ae8d569bSderaadt 	range(lowa, upb, ",");
1024ae8d569bSderaadt 	printf(" ****\n");
1025ae8d569bSderaadt 
1026ae8d569bSderaadt 	/*
1027ae8d569bSderaadt 	 * output changes to the "old" file.  The first loop suppresses
1028ae8d569bSderaadt 	 * output if there were no changes to the "old" file (we'll see
1029ae8d569bSderaadt 	 * the "old" lines as context in the "new" list).
1030ae8d569bSderaadt 	 */
1031ae8d569bSderaadt 	do_output = 0;
1032ae8d569bSderaadt 	for (; cvp <= context_vec_ptr; cvp++)
1033ae8d569bSderaadt 		if (cvp->a <= cvp->b) {
1034ae8d569bSderaadt 			cvp = context_vec_start;
1035ae8d569bSderaadt 			do_output++;
1036ae8d569bSderaadt 			break;
1037ae8d569bSderaadt 		}
1038ae8d569bSderaadt 	if (do_output) {
1039ae8d569bSderaadt 		while (cvp <= context_vec_ptr) {
104026da422aStedu 			a = cvp->a;
104126da422aStedu 			b = cvp->b;
104226da422aStedu 			c = cvp->c;
104326da422aStedu 			d = cvp->d;
1044ae8d569bSderaadt 
1045ae8d569bSderaadt 			if (a <= b && c <= d)
1046ae8d569bSderaadt 				ch = 'c';
1047ae8d569bSderaadt 			else
1048ae8d569bSderaadt 				ch = (a <= b) ? 'd' : 'a';
1049ae8d569bSderaadt 
1050ae8d569bSderaadt 			if (ch == 'a')
105126da422aStedu 				fetch(ixold, lowa, b, input[0], "  ", 0);
1052ae8d569bSderaadt 			else {
105326da422aStedu 				fetch(ixold, lowa, a - 1, input[0], "  ", 0);
105448b8c3e3Sderaadt 				fetch(ixold, a, b, input[0],
105548b8c3e3Sderaadt 				    ch == 'c' ? "! " : "- ", 0);
1056ae8d569bSderaadt 			}
1057ae8d569bSderaadt 			lowa = b + 1;
1058ae8d569bSderaadt 			cvp++;
1059ae8d569bSderaadt 		}
106026da422aStedu 		fetch(ixold, b + 1, upb, input[0], "  ", 0);
1061ae8d569bSderaadt 	}
1062ae8d569bSderaadt 	/* output changes to the "new" file */
1063ae8d569bSderaadt 	printf("--- ");
1064ae8d569bSderaadt 	range(lowc, upd, ",");
1065ae8d569bSderaadt 	printf(" ----\n");
1066ae8d569bSderaadt 
1067ae8d569bSderaadt 	do_output = 0;
1068ae8d569bSderaadt 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
1069ae8d569bSderaadt 		if (cvp->c <= cvp->d) {
1070ae8d569bSderaadt 			cvp = context_vec_start;
1071ae8d569bSderaadt 			do_output++;
1072ae8d569bSderaadt 			break;
1073ae8d569bSderaadt 		}
1074ae8d569bSderaadt 	if (do_output) {
1075ae8d569bSderaadt 		while (cvp <= context_vec_ptr) {
107626da422aStedu 			a = cvp->a;
107726da422aStedu 			b = cvp->b;
107826da422aStedu 			c = cvp->c;
107926da422aStedu 			d = cvp->d;
1080ae8d569bSderaadt 
1081ae8d569bSderaadt 			if (a <= b && c <= d)
1082ae8d569bSderaadt 				ch = 'c';
1083ae8d569bSderaadt 			else
1084ae8d569bSderaadt 				ch = (a <= b) ? 'd' : 'a';
1085ae8d569bSderaadt 
1086ae8d569bSderaadt 			if (ch == 'd')
108726da422aStedu 				fetch(ixnew, lowc, d, input[1], "  ", 0);
1088ae8d569bSderaadt 			else {
108926da422aStedu 				fetch(ixnew, lowc, c - 1, input[1], "  ", 0);
109048b8c3e3Sderaadt 				fetch(ixnew, c, d, input[1],
109148b8c3e3Sderaadt 				    ch == 'c' ? "! " : "+ ", 0);
1092ae8d569bSderaadt 			}
1093ae8d569bSderaadt 			lowc = d + 1;
1094ae8d569bSderaadt 			cvp++;
1095ae8d569bSderaadt 		}
109626da422aStedu 		fetch(ixnew, d + 1, upd, input[1], "  ", 0);
1097ae8d569bSderaadt 	}
1098ae8d569bSderaadt 	context_vec_ptr = context_vec_start - 1;
1099ae8d569bSderaadt }
11009de32c1bSmillert 
11019de32c1bSmillert /* dump accumulated "unified" diff changes */
11029de32c1bSmillert static void
11039de32c1bSmillert dump_unified_vec(void)
11049de32c1bSmillert {
11059de32c1bSmillert 	struct context_vec *cvp = context_vec_start;
11069de32c1bSmillert 	int lowa, upb, lowc, upd;
11079de32c1bSmillert 	int a, b, c, d;
11089de32c1bSmillert 	char ch;
11099de32c1bSmillert 
1110*90f56ad8Smillert 	if (context_vec_start > context_vec_ptr)
11119de32c1bSmillert 		return;
11129de32c1bSmillert 
11139de32c1bSmillert 	b = d = 0;		/* gcc */
11149de32c1bSmillert 	lowa = max(1, cvp->a - context);
11159de32c1bSmillert 	upb = min(len[0], context_vec_ptr->b + context);
11169de32c1bSmillert 	lowc = max(1, cvp->c - context);
11179de32c1bSmillert 	upd = min(len[1], context_vec_ptr->d + context);
11189de32c1bSmillert 
11199de32c1bSmillert 	printf("@@ -%d,%d +%d,%d @@\n", lowa, upb - lowa + 1,
11209de32c1bSmillert 	    lowc, upd - lowc + 1);
11219de32c1bSmillert 
11229de32c1bSmillert 	/*
11239de32c1bSmillert 	 * Output changes in "unified" diff format--the old and new lines
11249de32c1bSmillert 	 * are printed together.
11259de32c1bSmillert 	 */
11269de32c1bSmillert 	for (; cvp <= context_vec_ptr; cvp++) {
11279de32c1bSmillert 		a = cvp->a;
11289de32c1bSmillert 		b = cvp->b;
11299de32c1bSmillert 		c = cvp->c;
11309de32c1bSmillert 		d = cvp->d;
11319de32c1bSmillert 
11329de32c1bSmillert 		/*
11339de32c1bSmillert 		 * c: both new and old changes
11349de32c1bSmillert 		 * d: only changes in the old file
11359de32c1bSmillert 		 * a: only changes in the new file
11369de32c1bSmillert 		 */
11379de32c1bSmillert 		if (a <= b && c <= d)
11389de32c1bSmillert 			ch = 'c';
11399de32c1bSmillert 		else
11409de32c1bSmillert 			ch = (a <= b) ? 'd' : 'a';
11419de32c1bSmillert 
11429de32c1bSmillert 		switch (ch) {
11439de32c1bSmillert 		case 'c':
11449de32c1bSmillert 			fetch(ixold, lowa, a - 1, input[0], " ", 0);
11459de32c1bSmillert 			fetch(ixold, a, b, input[0], "-", 0);
11469de32c1bSmillert 			fetch(ixnew, c, d, input[1], "+", 0);
11479de32c1bSmillert 			break;
11489de32c1bSmillert 		case 'd':
11499de32c1bSmillert 			fetch(ixold, lowa, a - 1, input[0], " ", 0);
11509de32c1bSmillert 			fetch(ixold, a, b, input[0], "-", 0);
11519de32c1bSmillert 			break;
11529de32c1bSmillert 		case 'a':
11539de32c1bSmillert 			fetch(ixnew, lowc, c - 1, input[1], " ", 0);
11549de32c1bSmillert 			fetch(ixnew, c, d, input[1], "+", 0);
11559de32c1bSmillert 			break;
11569de32c1bSmillert 		}
11579de32c1bSmillert 		lowa = b + 1;
11589de32c1bSmillert 		lowc = d + 1;
11599de32c1bSmillert 	}
11609de32c1bSmillert 	fetch(ixnew, d + 1, upd, input[1], " ", 0);
11619de32c1bSmillert 
11629de32c1bSmillert 	context_vec_ptr = context_vec_start - 1;
11639de32c1bSmillert }
1164