xref: /openbsd-src/usr.bin/diff/diffreg.c (revision c72ea322cca2646b80fb359cc8dba57816c9a50e)
1*c72ea322Smillert /*	$OpenBSD: diffreg.c,v 1.33 2003/07/15 23:17:56 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  */
364ec4b3d5Smillert /*-
374ec4b3d5Smillert  * Copyright (c) 1991, 1993
384ec4b3d5Smillert  *	The Regents of the University of California.  All rights reserved.
394ec4b3d5Smillert  *
404ec4b3d5Smillert  * Redistribution and use in source and binary forms, with or without
414ec4b3d5Smillert  * modification, are permitted provided that the following conditions
424ec4b3d5Smillert  * are met:
434ec4b3d5Smillert  * 1. Redistributions of source code must retain the above copyright
444ec4b3d5Smillert  *    notice, this list of conditions and the following disclaimer.
454ec4b3d5Smillert  * 2. Redistributions in binary form must reproduce the above copyright
464ec4b3d5Smillert  *    notice, this list of conditions and the following disclaimer in the
474ec4b3d5Smillert  *    documentation and/or other materials provided with the distribution.
484ec4b3d5Smillert  * 3. Neither the name of the University nor the names of its contributors
494ec4b3d5Smillert  *    may be used to endorse or promote products derived from this software
504ec4b3d5Smillert  *    without specific prior written permission.
514ec4b3d5Smillert  *
524ec4b3d5Smillert  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
534ec4b3d5Smillert  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
544ec4b3d5Smillert  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
554ec4b3d5Smillert  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
564ec4b3d5Smillert  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
574ec4b3d5Smillert  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
584ec4b3d5Smillert  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
594ec4b3d5Smillert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
604ec4b3d5Smillert  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
614ec4b3d5Smillert  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
624ec4b3d5Smillert  * SUCH DAMAGE.
634ec4b3d5Smillert  *
644ec4b3d5Smillert  *	@(#)diffreg.c   8.1 (Berkeley) 6/6/93
654ec4b3d5Smillert  */
664ec4b3d5Smillert 
674ec4b3d5Smillert #ifndef lint
68*c72ea322Smillert static const char rcsid[] = "$OpenBSD: diffreg.c,v 1.33 2003/07/15 23:17:56 millert Exp $";
694ec4b3d5Smillert #endif /* not lint */
70d0c3f575Sderaadt 
717b6ec9e4Smillert #include <sys/param.h>
724ec4b3d5Smillert #include <sys/stat.h>
73b4bca33fSmillert #include <sys/wait.h>
7426da422aStedu 
754ec4b3d5Smillert #include <ctype.h>
764ec4b3d5Smillert #include <err.h>
777b6ec9e4Smillert #include <errno.h>
7826da422aStedu #include <fcntl.h>
794ec4b3d5Smillert #include <libgen.h>
804ec4b3d5Smillert #include <stdio.h>
814ec4b3d5Smillert #include <stdlib.h>
8226da422aStedu #include <string.h>
834ec4b3d5Smillert #include <unistd.h>
84ae8d569bSderaadt 
85ae8d569bSderaadt #include "diff.h"
86b4bca33fSmillert #include "pathnames.h"
8726da422aStedu 
88ae8d569bSderaadt /*
89ae8d569bSderaadt  * diff - compare two files.
90ae8d569bSderaadt  */
91ae8d569bSderaadt 
92ae8d569bSderaadt /*
93ae8d569bSderaadt  *	Uses an algorithm due to Harold Stone, which finds
94ae8d569bSderaadt  *	a pair of longest identical subsequences in the two
95ae8d569bSderaadt  *	files.
96ae8d569bSderaadt  *
97ae8d569bSderaadt  *	The major goal is to generate the match vector J.
98ae8d569bSderaadt  *	J[i] is the index of the line in file1 corresponding
99ae8d569bSderaadt  *	to line i file0. J[i] = 0 if there is no
100ae8d569bSderaadt  *	such line in file1.
101ae8d569bSderaadt  *
102ae8d569bSderaadt  *	Lines are hashed so as to work in core. All potential
103ae8d569bSderaadt  *	matches are located by sorting the lines of each file
104ae8d569bSderaadt  *	on the hash (called ``value''). In particular, this
105ae8d569bSderaadt  *	collects the equivalence classes in file1 together.
106ae8d569bSderaadt  *	Subroutine equiv replaces the value of each line in
107ae8d569bSderaadt  *	file0 by the index of the first element of its
108ae8d569bSderaadt  *	matching equivalence in (the reordered) file1.
109ae8d569bSderaadt  *	To save space equiv squeezes file1 into a single
110ae8d569bSderaadt  *	array member in which the equivalence classes
111ae8d569bSderaadt  *	are simply concatenated, except that their first
112ae8d569bSderaadt  *	members are flagged by changing sign.
113ae8d569bSderaadt  *
114ae8d569bSderaadt  *	Next the indices that point into member are unsorted into
115ae8d569bSderaadt  *	array class according to the original order of file0.
116ae8d569bSderaadt  *
117ae8d569bSderaadt  *	The cleverness lies in routine stone. This marches
118ae8d569bSderaadt  *	through the lines of file0, developing a vector klist
119ae8d569bSderaadt  *	of "k-candidates". At step i a k-candidate is a matched
120ae8d569bSderaadt  *	pair of lines x,y (x in file0 y in file1) such that
121ae8d569bSderaadt  *	there is a common subsequence of length k
122ae8d569bSderaadt  *	between the first i lines of file0 and the first y
123ae8d569bSderaadt  *	lines of file1, but there is no such subsequence for
124ae8d569bSderaadt  *	any smaller y. x is the earliest possible mate to y
125ae8d569bSderaadt  *	that occurs in such a subsequence.
126ae8d569bSderaadt  *
127ae8d569bSderaadt  *	Whenever any of the members of the equivalence class of
128ae8d569bSderaadt  *	lines in file1 matable to a line in file0 has serial number
129ae8d569bSderaadt  *	less than the y of some k-candidate, that k-candidate
130ae8d569bSderaadt  *	with the smallest such y is replaced. The new
131ae8d569bSderaadt  *	k-candidate is chained (via pred) to the current
132ae8d569bSderaadt  *	k-1 candidate so that the actual subsequence can
133ae8d569bSderaadt  *	be recovered. When a member has serial number greater
134ae8d569bSderaadt  *	that the y of all k-candidates, the klist is extended.
135ae8d569bSderaadt  *	At the end, the longest subsequence is pulled out
136ae8d569bSderaadt  *	and placed in the array J by unravel
137ae8d569bSderaadt  *
138ae8d569bSderaadt  *	With J in hand, the matches there recorded are
139ae8d569bSderaadt  *	check'ed against reality to assure that no spurious
140ae8d569bSderaadt  *	matches have crept in due to hashing. If they have,
141ae8d569bSderaadt  *	they are broken, and "jackpot" is recorded--a harmless
142ae8d569bSderaadt  *	matter except that a true match for a spuriously
143ae8d569bSderaadt  *	mated line may now be unnecessarily reported as a change.
144ae8d569bSderaadt  *
145ae8d569bSderaadt  *	Much of the complexity of the program comes simply
146ae8d569bSderaadt  *	from trying to minimize core utilization and
147ae8d569bSderaadt  *	maximize the range of doable problems by dynamically
148ae8d569bSderaadt  *	allocating what is needed and reusing what is not.
149ae8d569bSderaadt  *	The core requirements for problems larger than somewhat
150ae8d569bSderaadt  *	are (in words) 2*length(file0) + length(file1) +
151ae8d569bSderaadt  *	3*(number of k-candidates installed),  typically about
152ae8d569bSderaadt  *	6n words for files of length n.
153ae8d569bSderaadt  */
154ae8d569bSderaadt 
155ae8d569bSderaadt struct cand {
156ae8d569bSderaadt 	int x;
157ae8d569bSderaadt 	int y;
158ae8d569bSderaadt 	int pred;
159ae8d569bSderaadt } cand;
16026da422aStedu 
161ae8d569bSderaadt struct line {
162ae8d569bSderaadt 	int serial;
163ae8d569bSderaadt 	int value;
16492af4c2dStedu } *file[2];
16526da422aStedu 
1664ec4b3d5Smillert static int  *J;			/* will be overlaid on class */
1674ec4b3d5Smillert static int  *class;		/* will be overlaid on file[0] */
1684ec4b3d5Smillert static int  *klist;		/* will be overlaid on file[0] after class */
1694ec4b3d5Smillert static int  *member;		/* will be overlaid on file[1] */
1704ec4b3d5Smillert static int   clen;
1714ec4b3d5Smillert static int   inifdef;		/* whether or not we are in a #ifdef block */
1724ec4b3d5Smillert static int   len[2];
1734ec4b3d5Smillert static int   pref, suff;	/* length of prefix and suffix */
1744ec4b3d5Smillert static int   slen[2];
1754ec4b3d5Smillert static int   anychange;
1764ec4b3d5Smillert static long *ixnew;		/* will be overlaid on file[1] */
1774ec4b3d5Smillert static long *ixold;		/* will be overlaid on klist */
1784ec4b3d5Smillert static struct cand *clist;	/* merely a free storage pot for candidates */
1794ec4b3d5Smillert static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
1804ec4b3d5Smillert static u_char *chrtran;		/* translation table for case-folding */
181ae8d569bSderaadt 
1827b6ec9e4Smillert static FILE *opentemp(const char *);
18326da422aStedu static void fetch(long *, int, int, FILE *, char *, int);
1844ec4b3d5Smillert static void output(char *, FILE *, char *, FILE *);
1854ec4b3d5Smillert static void check(char *, FILE *, char *, FILE *);
18626da422aStedu static void range(int, int, char *);
187*c72ea322Smillert static void uni_range(int, int);
1884ec4b3d5Smillert static void dump_context_vec(FILE *, FILE *);
1894ec4b3d5Smillert static void dump_unified_vec(FILE *, FILE *);
19026da422aStedu static void prepare(int, FILE *);
19126da422aStedu static void prune(void);
19226da422aStedu static void equiv(struct line *, int, struct line *, int, int *);
19326da422aStedu static void unravel(int);
19426da422aStedu static void unsort(struct line *, int, int *);
1954ec4b3d5Smillert static void change(char *, FILE *, char *, FILE *, int, int, int, int);
19626da422aStedu static void sort(struct line *, int);
1974ec4b3d5Smillert static int  asciifile(FILE *);
19826da422aStedu static int  newcand(int, int, int);
19926da422aStedu static int  search(int *, int, int);
2004ec4b3d5Smillert static int  skipline(FILE *);
20126da422aStedu static int  stone(int *, int, int *, int *);
20226da422aStedu static int  readhash(FILE *);
2034ec4b3d5Smillert static int  files_differ(FILE *, FILE *, int);
20426da422aStedu 
20526da422aStedu /*
20626da422aStedu  * chrtran points to one of 2 translation tables: cup2low if folding upper to
20726da422aStedu  * lower case clow2low if not folding case
208ae8d569bSderaadt  */
2098a15d8deSderaadt u_char clow2low[256] = {
21026da422aStedu 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
21126da422aStedu 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
21226da422aStedu 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
21326da422aStedu 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
21426da422aStedu 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
21526da422aStedu 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
21626da422aStedu 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
21726da422aStedu 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
21826da422aStedu 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
21926da422aStedu 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
22026da422aStedu 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
22126da422aStedu 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
22226da422aStedu 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
22326da422aStedu 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
22426da422aStedu 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
22526da422aStedu 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
22626da422aStedu 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
22726da422aStedu 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
22826da422aStedu 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
22926da422aStedu 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
23026da422aStedu 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
23126da422aStedu 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
23226da422aStedu 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
23326da422aStedu 	0xfd, 0xfe, 0xff
234ae8d569bSderaadt };
235ae8d569bSderaadt 
2368a15d8deSderaadt u_char cup2low[256] = {
23726da422aStedu 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
23826da422aStedu 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
23926da422aStedu 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
24026da422aStedu 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
24126da422aStedu 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
24226da422aStedu 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
24326da422aStedu 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
24426da422aStedu 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
24526da422aStedu 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
24626da422aStedu 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
24726da422aStedu 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
24826da422aStedu 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
24926da422aStedu 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
25026da422aStedu 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
25126da422aStedu 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
25226da422aStedu 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
25326da422aStedu 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
25426da422aStedu 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
25526da422aStedu 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
25626da422aStedu 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
25726da422aStedu 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
25826da422aStedu 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
25926da422aStedu 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
26026da422aStedu 	0xfd, 0xfe, 0xff
261ae8d569bSderaadt };
262ae8d569bSderaadt 
263b4bca33fSmillert int
2644ec4b3d5Smillert diffreg(char *ofile1, char *ofile2, int flags)
265ae8d569bSderaadt {
2664ec4b3d5Smillert 	char *file1 = ofile1;
2674ec4b3d5Smillert 	char *file2 = ofile2;
2684ec4b3d5Smillert 	FILE *f1 = NULL;
2694ec4b3d5Smillert 	FILE *f2 = NULL;
270b4bca33fSmillert 	int rval = D_SAME;
271b4bca33fSmillert 	int i, ostdout = -1;
272b4bca33fSmillert 	pid_t pid = -1;
273ae8d569bSderaadt 
2744ec4b3d5Smillert 	anychange = 0;
275ae8d569bSderaadt 	chrtran = (iflag ? cup2low : clow2low);
2767b6ec9e4Smillert 	if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
2777b6ec9e4Smillert 		return (D_MISMATCH);
27866e5764eSmillert 	if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0)
2794ec4b3d5Smillert 		goto notsame;
2804ec4b3d5Smillert 
2814ec4b3d5Smillert 	if (flags & D_EMPTY1)
2824ec4b3d5Smillert 		f1 = fopen(_PATH_DEVNULL, "r");
2834ec4b3d5Smillert 	else {
2847b6ec9e4Smillert 		if (!S_ISREG(stb1.st_mode)) {
2857b6ec9e4Smillert 			if ((f1 = opentemp(file1)) == NULL ||
2867b6ec9e4Smillert 			    fstat(fileno(f1), &stb1) < 0) {
2874ec4b3d5Smillert 				warn("%s", file1);
2884ec4b3d5Smillert 				status |= 2;
2894ec4b3d5Smillert 				goto closem;
29048b947b7Smillert 			}
2917b6ec9e4Smillert 		} else if (strcmp(file1, "-") == 0)
292b1a26502Smillert 			f1 = stdin;
293b1a26502Smillert 		else
2944ec4b3d5Smillert 			f1 = fopen(file1, "r");
2954ec4b3d5Smillert 	}
2964ec4b3d5Smillert 	if (f1 == NULL) {
2974ec4b3d5Smillert 		warn("%s", file1);
2984ec4b3d5Smillert 		status |= 2;
2994ec4b3d5Smillert 		goto closem;
3004ec4b3d5Smillert 	}
3014ec4b3d5Smillert 
3024ec4b3d5Smillert 	if (flags & D_EMPTY2)
3034ec4b3d5Smillert 		f2 = fopen(_PATH_DEVNULL, "r");
3044ec4b3d5Smillert 	else {
3057b6ec9e4Smillert 		if (!S_ISREG(stb2.st_mode)) {
3067b6ec9e4Smillert 			if ((f2 = opentemp(file2)) == NULL ||
3077b6ec9e4Smillert 			    fstat(fileno(f2), &stb2) < 0) {
3084ec4b3d5Smillert 				warn("%s", file2);
3094ec4b3d5Smillert 				status |= 2;
3104ec4b3d5Smillert 				goto closem;
3114ec4b3d5Smillert 			}
3127b6ec9e4Smillert 		} else if (strcmp(file2, "-") == 0)
313b1a26502Smillert 			f2 = stdin;
314b1a26502Smillert 		else
3154ec4b3d5Smillert 			f2 = fopen(file2, "r");
3164ec4b3d5Smillert 	}
3174ec4b3d5Smillert 	if (f2 == NULL) {
3184ec4b3d5Smillert 		warn("%s", file2);
3194ec4b3d5Smillert 		status |= 2;
3204ec4b3d5Smillert 		goto closem;
3214ec4b3d5Smillert 	}
3224ec4b3d5Smillert 
3234ec4b3d5Smillert 	switch (files_differ(f1, f2, flags)) {
3244ec4b3d5Smillert 	case 0:
325b4bca33fSmillert 		goto closem;
3264ec4b3d5Smillert 	case 1:
3274ec4b3d5Smillert 		break;
3284ec4b3d5Smillert 	default:
3294ec4b3d5Smillert 		/* error */
3304ec4b3d5Smillert 		status |= 2;
3314ec4b3d5Smillert 		goto closem;
332ae8d569bSderaadt 	}
3334ec4b3d5Smillert 
334ae8d569bSderaadt notsame:
335ae8d569bSderaadt 	/*
336ae8d569bSderaadt 	 * Files certainly differ at this point; set status accordingly
337ae8d569bSderaadt 	 */
3384ec4b3d5Smillert 	status |= 1;
339b4bca33fSmillert 	rval = D_DIFFER;
340b4bca33fSmillert 	if (!asciifile(f1) || !asciifile(f2)) {
341b4bca33fSmillert 		rval = D_BINARY;
342cab5d83cSmillert 		goto closem;
343cab5d83cSmillert 	}
344b4bca33fSmillert 	if (format == D_BRIEF)
345b4bca33fSmillert 		goto closem;
346b4bca33fSmillert 	if (lflag) {
347b4bca33fSmillert 		/* redirect stdout to pr */
348b4bca33fSmillert 		int pfd[2];
349b4bca33fSmillert 		char *header;
350b4bca33fSmillert 		char *prargv[] = { "pr", "-h", NULL, "-f", NULL };
351b4bca33fSmillert 
352b4bca33fSmillert 		easprintf(&header, "%s %s %s", diffargs, file1, file2);
353b4bca33fSmillert 		prargv[2] = header;
354b4bca33fSmillert 		fflush(stdout);
355b4bca33fSmillert 		rewind(stdout);
356b4bca33fSmillert 		pipe(pfd);
357b4bca33fSmillert 		switch ((pid = fork())) {
358b4bca33fSmillert 		case -1:
359b4bca33fSmillert 			warnx("No more processes");
360b4bca33fSmillert 			status |= 2;
361b4bca33fSmillert 			free(header);
362b4bca33fSmillert 			return (D_ERROR);
363b4bca33fSmillert 		case 0:
364b4bca33fSmillert 			/* child */
365b4bca33fSmillert 			if (pfd[0] != STDIN_FILENO) {
366b4bca33fSmillert 				dup2(pfd[0], STDIN_FILENO);
367b4bca33fSmillert 				close(pfd[0]);
368b4bca33fSmillert 			}
369b4bca33fSmillert 			close(pfd[1]);
370b4bca33fSmillert 			execv(_PATH_PR, prargv);
371b4bca33fSmillert 			_exit(127);
372b4bca33fSmillert 		default:
373b4bca33fSmillert 			/* parent */
374b4bca33fSmillert 			if (pfd[1] != STDOUT_FILENO) {
375b4bca33fSmillert 				ostdout = dup(STDOUT_FILENO);
376b4bca33fSmillert 				dup2(pfd[1], STDOUT_FILENO);
377b4bca33fSmillert 				close(pfd[1]);
378b4bca33fSmillert 			}
379b4bca33fSmillert 			close(pfd[0]);
380b4bca33fSmillert 			rewind(stdout);
381b4bca33fSmillert 			free(header);
382b4bca33fSmillert 		}
383b4bca33fSmillert 	} else {
3844ec4b3d5Smillert 		if (flags & D_HEADER) {
3854ec4b3d5Smillert 			if (format == D_EDIT)
386b4bca33fSmillert 				printf("ed - %s << '-*-END-*-'\n",
387b4bca33fSmillert 				    basename(file1));
3884ec4b3d5Smillert 			else
3894ec4b3d5Smillert 				printf("%s %s %s\n", diffargs, file1, file2);
3904ec4b3d5Smillert 		}
391ae8d569bSderaadt 	}
392ae8d569bSderaadt 	prepare(0, f1);
393ae8d569bSderaadt 	prepare(1, f2);
394ae8d569bSderaadt 	prune();
395ae8d569bSderaadt 	sort(sfile[0], slen[0]);
396ae8d569bSderaadt 	sort(sfile[1], slen[1]);
397ae8d569bSderaadt 
398ae8d569bSderaadt 	member = (int *)file[1];
399ae8d569bSderaadt 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
40049dffe13Smillert 	member = erealloc(member, (slen[1] + 2) * sizeof(int));
401ae8d569bSderaadt 
402ae8d569bSderaadt 	class = (int *)file[0];
403ae8d569bSderaadt 	unsort(sfile[0], slen[0], class);
40449dffe13Smillert 	class = erealloc(class, (slen[0] + 2) * sizeof(int));
405ae8d569bSderaadt 
40649dffe13Smillert 	klist = emalloc((slen[0] + 2) * sizeof(int));
40749dffe13Smillert 	clist = emalloc(sizeof(cand));
408ae8d569bSderaadt 	i = stone(class, slen[0], member, klist);
40926da422aStedu 	free(member);
41026da422aStedu 	free(class);
411ae8d569bSderaadt 
4124ec4b3d5Smillert 	J = erealloc(J, (len[0] + 2) * sizeof(int));
413ae8d569bSderaadt 	unravel(klist[i]);
41426da422aStedu 	free(clist);
41526da422aStedu 	free(klist);
416ae8d569bSderaadt 
4174ec4b3d5Smillert 	ixold = erealloc(ixold, (len[0] + 2) * sizeof(long));
4184ec4b3d5Smillert 	ixnew = erealloc(ixnew, (len[1] + 2) * sizeof(long));
4194ec4b3d5Smillert 	check(file1, f1, file2, f2);
4204ec4b3d5Smillert 	output(file1, f1, file2, f2);
421b4bca33fSmillert 	if (ostdout != -1) {
422b4bca33fSmillert 		int wstatus;
4234ec4b3d5Smillert 
424b4bca33fSmillert 		/* close the pipe to pr and restore stdout */
425b4bca33fSmillert 		fflush(stdout);
426b4bca33fSmillert 		rewind(stdout);
427b4bca33fSmillert 		if (ostdout != STDOUT_FILENO) {
428b4bca33fSmillert 			close(STDOUT_FILENO);
429b4bca33fSmillert 			dup2(ostdout, STDOUT_FILENO);
430b4bca33fSmillert 			close(ostdout);
431b4bca33fSmillert 		}
432b4bca33fSmillert 		waitpid(pid, &wstatus, 0);
433b4bca33fSmillert 	} else if ((flags & D_HEADER) && format == D_EDIT)
434b4bca33fSmillert 		printf("w\nq\n-*-END-*-\n");
4354ec4b3d5Smillert closem:
4364ec4b3d5Smillert 	if (f1 != NULL)
4374ec4b3d5Smillert 		fclose(f1);
4384ec4b3d5Smillert 	if (f2 != NULL)
4394ec4b3d5Smillert 		fclose(f2);
4407b6ec9e4Smillert 	if (file1 != ofile1)
441b1a26502Smillert 		free(file1);
4427b6ec9e4Smillert 	if (file2 != ofile2)
4434ec4b3d5Smillert 		free(file2);
444b4bca33fSmillert 	return (rval);
4454ec4b3d5Smillert }
4464ec4b3d5Smillert 
4474ec4b3d5Smillert /*
4484ec4b3d5Smillert  * Check to see if the given files differ.
4494ec4b3d5Smillert  * Returns 0 if they are the same, 1 if different, and -1 on error.
4504ec4b3d5Smillert  * XXX - could use code from cmp(1) [faster]
4514ec4b3d5Smillert  */
4524ec4b3d5Smillert static int
4534ec4b3d5Smillert files_differ(FILE *f1, FILE *f2, int flags)
4544ec4b3d5Smillert {
4554ec4b3d5Smillert 	char buf1[BUFSIZ], buf2[BUFSIZ];
4564ec4b3d5Smillert 	size_t i, j;
4574ec4b3d5Smillert 
4584ec4b3d5Smillert 	if ((flags & (D_EMPTY1|D_EMPTY2)) || stb1.st_size != stb2.st_size ||
4594ec4b3d5Smillert 	    (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT))
4604ec4b3d5Smillert 		return (1);
4614ec4b3d5Smillert 	for (;;) {
4624ec4b3d5Smillert 		i = fread(buf1, 1, sizeof(buf1), f1);
4634ec4b3d5Smillert 		j = fread(buf2, 1, sizeof(buf2), f2);
4644ec4b3d5Smillert 		if (i != j)
4654ec4b3d5Smillert 			return (1);
4664ec4b3d5Smillert 		if (i == 0 && j == 0) {
4674ec4b3d5Smillert 			if (ferror(f1) || ferror(f2))
4684ec4b3d5Smillert 				return (1);
4694ec4b3d5Smillert 			return (0);
4704ec4b3d5Smillert 		}
4714ec4b3d5Smillert 		if (memcmp(buf1, buf2, i) != 0)
4724ec4b3d5Smillert 			return (1);
4734ec4b3d5Smillert 	}
474ae8d569bSderaadt }
475ae8d569bSderaadt 
4767b6ec9e4Smillert static FILE *
4777b6ec9e4Smillert opentemp(const char *file)
478ae8d569bSderaadt {
4797b6ec9e4Smillert 	char buf[BUFSIZ], *tempdir, tempfile[MAXPATHLEN];
4807b6ec9e4Smillert 	ssize_t nread;
4817b6ec9e4Smillert 	int ifd, ofd;
48248b947b7Smillert 
48348b947b7Smillert 	if (strcmp(file, "-") == 0)
48448b947b7Smillert 		ifd = STDIN_FILENO;
48566e5764eSmillert 	else if ((ifd = open(file, O_RDONLY, 0644)) < 0)
4864ec4b3d5Smillert 		return (NULL);
48748b947b7Smillert 
48848b947b7Smillert 	if ((tempdir = getenv("TMPDIR")) == NULL)
48948b947b7Smillert 		tempdir = _PATH_TMP;
4907b6ec9e4Smillert 	if (snprintf(tempfile, sizeof(tempfile), "%s/diff.XXXXXXXX",
4917b6ec9e4Smillert 	    tempdir) >= sizeof(tempfile)) {
4927b6ec9e4Smillert 		close(ifd);
4937b6ec9e4Smillert 		errno = ENAMETOOLONG;
4944ec4b3d5Smillert 		return (NULL);
495ae8d569bSderaadt 	}
4967b6ec9e4Smillert 
4977b6ec9e4Smillert 	if ((ofd = mkstemp(tempfile)) < 0)
4987b6ec9e4Smillert 		return (NULL);
4997b6ec9e4Smillert 	unlink(tempfile);
5007b6ec9e4Smillert 	while ((nread = read(ifd, buf, BUFSIZ)) > 0) {
5017b6ec9e4Smillert 		if (write(ofd, buf, nread) != nread) {
50248b947b7Smillert 			close(ifd);
50348b947b7Smillert 			close(ofd);
5047b6ec9e4Smillert 			return (NULL);
5057b6ec9e4Smillert 		}
5067b6ec9e4Smillert 	}
5077b6ec9e4Smillert 	close(ifd);
5087b6ec9e4Smillert 	return (fdopen(ofd, "r"));
509ae8d569bSderaadt }
510ae8d569bSderaadt 
511ae8d569bSderaadt char *
51226da422aStedu splice(char *dir, char *file)
513ae8d569bSderaadt {
51449dffe13Smillert 	char *tail, *buf;
515ae8d569bSderaadt 
5167b6ec9e4Smillert 	if ((tail = strrchr(file, '/')) == NULL)
517ae8d569bSderaadt 		tail = file;
518ae8d569bSderaadt 	else
519ae8d569bSderaadt 		tail++;
520b4bca33fSmillert 	easprintf(&buf, "%s/%s", dir, tail);
52149dffe13Smillert 	return (buf);
522ae8d569bSderaadt }
523ae8d569bSderaadt 
52426da422aStedu static void
52526da422aStedu prepare(int i, FILE *fd)
526ae8d569bSderaadt {
52726da422aStedu 	struct line *p;
52826da422aStedu 	int j, h;
529ae8d569bSderaadt 
5304ec4b3d5Smillert 	rewind(fd);
53149dffe13Smillert 	p = emalloc(3 * sizeof(struct line));
53226da422aStedu 	for (j = 0; (h = readhash(fd));) {
53349dffe13Smillert 		p = erealloc(p, (++j + 3) * sizeof(struct line));
534ae8d569bSderaadt 		p[j].value = h;
535ae8d569bSderaadt 	}
536ae8d569bSderaadt 	len[i] = j;
537ae8d569bSderaadt 	file[i] = p;
538ae8d569bSderaadt }
539ae8d569bSderaadt 
54026da422aStedu static void
54126da422aStedu prune(void)
542ae8d569bSderaadt {
54326da422aStedu 	int i, j;
54448b8c3e3Sderaadt 
545ae8d569bSderaadt 	for (pref = 0; pref < len[0] && pref < len[1] &&
546ae8d569bSderaadt 	    file[0][pref + 1].value == file[1][pref + 1].value;
5477d2b2b91Sderaadt 	    pref++)
5487d2b2b91Sderaadt 		;
549ae8d569bSderaadt 	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
550ae8d569bSderaadt 	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
5517d2b2b91Sderaadt 	    suff++)
5527d2b2b91Sderaadt 		;
553ae8d569bSderaadt 	for (j = 0; j < 2; j++) {
554ae8d569bSderaadt 		sfile[j] = file[j] + pref;
555ae8d569bSderaadt 		slen[j] = len[j] - pref - suff;
556ae8d569bSderaadt 		for (i = 0; i <= slen[j]; i++)
557ae8d569bSderaadt 			sfile[j][i].serial = i;
558ae8d569bSderaadt 	}
559ae8d569bSderaadt }
560ae8d569bSderaadt 
56126da422aStedu static void
56226da422aStedu equiv(struct line *a, int n, struct line *b, int m, int *c)
563ae8d569bSderaadt {
56426da422aStedu 	int i, j;
56548b8c3e3Sderaadt 
566ae8d569bSderaadt 	i = j = 1;
567ae8d569bSderaadt 	while (i <= n && j <= m) {
568ae8d569bSderaadt 		if (a[i].value < b[j].value)
569ae8d569bSderaadt 			a[i++].value = 0;
570ae8d569bSderaadt 		else if (a[i].value == b[j].value)
571ae8d569bSderaadt 			a[i++].value = j;
572ae8d569bSderaadt 		else
573ae8d569bSderaadt 			j++;
574ae8d569bSderaadt 	}
575ae8d569bSderaadt 	while (i <= n)
576ae8d569bSderaadt 		a[i++].value = 0;
577ae8d569bSderaadt 	b[m + 1].value = 0;
578ae8d569bSderaadt 	j = 0;
579ae8d569bSderaadt 	while (++j <= m) {
580ae8d569bSderaadt 		c[j] = -b[j].serial;
581ae8d569bSderaadt 		while (b[j + 1].value == b[j].value) {
582ae8d569bSderaadt 			j++;
583ae8d569bSderaadt 			c[j] = b[j].serial;
584ae8d569bSderaadt 		}
585ae8d569bSderaadt 	}
586ae8d569bSderaadt 	c[j] = -1;
587ae8d569bSderaadt }
588ae8d569bSderaadt 
58926da422aStedu static int
59026da422aStedu stone(int *a, int n, int *b, int *c)
591ae8d569bSderaadt {
59248b8c3e3Sderaadt 	int i, k, y, j, l;
59348b8c3e3Sderaadt 	int oldc, tc, oldl;
59448b8c3e3Sderaadt 
595ae8d569bSderaadt 	k = 0;
596ae8d569bSderaadt 	c[0] = newcand(0, 0, 0);
597ae8d569bSderaadt 	for (i = 1; i <= n; i++) {
598ae8d569bSderaadt 		j = a[i];
599ae8d569bSderaadt 		if (j == 0)
600ae8d569bSderaadt 			continue;
601ae8d569bSderaadt 		y = -b[j];
602ae8d569bSderaadt 		oldl = 0;
603ae8d569bSderaadt 		oldc = c[0];
604ae8d569bSderaadt 		do {
605ae8d569bSderaadt 			if (y <= clist[oldc].y)
606ae8d569bSderaadt 				continue;
607ae8d569bSderaadt 			l = search(c, k, y);
608ae8d569bSderaadt 			if (l != oldl + 1)
609ae8d569bSderaadt 				oldc = c[l - 1];
610ae8d569bSderaadt 			if (l <= k) {
611ae8d569bSderaadt 				if (clist[c[l]].y <= y)
612ae8d569bSderaadt 					continue;
613ae8d569bSderaadt 				tc = c[l];
614ae8d569bSderaadt 				c[l] = newcand(i, y, oldc);
615ae8d569bSderaadt 				oldc = tc;
616ae8d569bSderaadt 				oldl = l;
617ae8d569bSderaadt 			} else {
618ae8d569bSderaadt 				c[l] = newcand(i, y, oldc);
619ae8d569bSderaadt 				k++;
620ae8d569bSderaadt 				break;
621ae8d569bSderaadt 			}
622ae8d569bSderaadt 		} while ((y = b[++j]) > 0);
623ae8d569bSderaadt 	}
624ae8d569bSderaadt 	return (k);
625ae8d569bSderaadt }
626ae8d569bSderaadt 
62726da422aStedu static int
62826da422aStedu newcand(int x, int y, int pred)
629ae8d569bSderaadt {
63026da422aStedu 	struct cand *q;
63126da422aStedu 
63249dffe13Smillert 	clist = erealloc(clist, ++clen * sizeof(cand));
633ae8d569bSderaadt 	q = clist + clen - 1;
634ae8d569bSderaadt 	q->x = x;
635ae8d569bSderaadt 	q->y = y;
636ae8d569bSderaadt 	q->pred = pred;
637ae8d569bSderaadt 	return (clen - 1);
638ae8d569bSderaadt }
639ae8d569bSderaadt 
64026da422aStedu static int
64126da422aStedu search(int *c, int k, int y)
642ae8d569bSderaadt {
64348b8c3e3Sderaadt 	int i, j, l, t;
64448b8c3e3Sderaadt 
645ae8d569bSderaadt 	if (clist[c[k]].y < y)	/* quick look for typical case */
646ae8d569bSderaadt 		return (k + 1);
647ae8d569bSderaadt 	i = 0;
648ae8d569bSderaadt 	j = k + 1;
649ae8d569bSderaadt 	while (1) {
650ae8d569bSderaadt 		l = i + j;
651ae8d569bSderaadt 		if ((l >>= 1) <= i)
652ae8d569bSderaadt 			break;
653ae8d569bSderaadt 		t = clist[c[l]].y;
654ae8d569bSderaadt 		if (t > y)
655ae8d569bSderaadt 			j = l;
656ae8d569bSderaadt 		else if (t < y)
657ae8d569bSderaadt 			i = l;
658ae8d569bSderaadt 		else
659ae8d569bSderaadt 			return (l);
660ae8d569bSderaadt 	}
661ae8d569bSderaadt 	return (l + 1);
662ae8d569bSderaadt }
663ae8d569bSderaadt 
66426da422aStedu static void
66526da422aStedu unravel(int p)
666ae8d569bSderaadt {
66726da422aStedu 	struct cand *q;
66848b8c3e3Sderaadt 	int i;
66948b8c3e3Sderaadt 
670ae8d569bSderaadt 	for (i = 0; i <= len[0]; i++)
671ae8d569bSderaadt 		J[i] = i <= pref ? i :
6727d2b2b91Sderaadt 		    i > len[0] - suff ? i + len[1] - len[0] : 0;
673ae8d569bSderaadt 	for (q = clist + p; q->y != 0; q = clist + q->pred)
674ae8d569bSderaadt 		J[q->x + pref] = q->y + pref;
675ae8d569bSderaadt }
676ae8d569bSderaadt 
67726da422aStedu /*
67849dffe13Smillert  * Check does double duty:
67949dffe13Smillert  *  1.	ferret out any fortuitous correspondences due
68049dffe13Smillert  *	to confounding by hashing (which result in "jackpot")
68149dffe13Smillert  *  2.  collect random access indexes to the two files
68226da422aStedu  */
68326da422aStedu static void
6844ec4b3d5Smillert check(char *file1, FILE *f1, char *file2, FILE *f2)
685ae8d569bSderaadt {
68648b8c3e3Sderaadt 	int i, j, jackpot, c, d;
687ae8d569bSderaadt 	long ctold, ctnew;
688ae8d569bSderaadt 
6894ec4b3d5Smillert 	rewind(f1);
6904ec4b3d5Smillert 	rewind(f2);
691ae8d569bSderaadt 	j = 1;
692ae8d569bSderaadt 	ixold[0] = ixnew[0] = 0;
693ae8d569bSderaadt 	jackpot = 0;
694ae8d569bSderaadt 	ctold = ctnew = 0;
695ae8d569bSderaadt 	for (i = 1; i <= len[0]; i++) {
696ae8d569bSderaadt 		if (J[i] == 0) {
6974ec4b3d5Smillert 			ixold[i] = ctold += skipline(f1);
698ae8d569bSderaadt 			continue;
699ae8d569bSderaadt 		}
700ae8d569bSderaadt 		while (j < J[i]) {
7014ec4b3d5Smillert 			ixnew[j] = ctnew += skipline(f2);
702ae8d569bSderaadt 			j++;
703ae8d569bSderaadt 		}
704ae8d569bSderaadt 		if (bflag || wflag || iflag) {
705ae8d569bSderaadt 			for (;;) {
7064ec4b3d5Smillert 				c = getc(f1);
7074ec4b3d5Smillert 				d = getc(f2);
708ae8d569bSderaadt 				ctold++;
709ae8d569bSderaadt 				ctnew++;
710ae8d569bSderaadt 				if (bflag && isspace(c) && isspace(d)) {
711ae8d569bSderaadt 					do {
712ae8d569bSderaadt 						if (c == '\n')
713ae8d569bSderaadt 							break;
714ae8d569bSderaadt 						ctold++;
7154ec4b3d5Smillert 					} while (isspace(c = getc(f1)));
716ae8d569bSderaadt 					do {
717ae8d569bSderaadt 						if (d == '\n')
718ae8d569bSderaadt 							break;
719ae8d569bSderaadt 						ctnew++;
7204ec4b3d5Smillert 					} while (isspace(d = getc(f2)));
721ae8d569bSderaadt 				} else if (wflag) {
722ae8d569bSderaadt 					while (isspace(c) && c != '\n') {
7234ec4b3d5Smillert 						c = getc(f1);
724ae8d569bSderaadt 						ctold++;
725ae8d569bSderaadt 					}
726ae8d569bSderaadt 					while (isspace(d) && d != '\n') {
7274ec4b3d5Smillert 						d = getc(f2);
728ae8d569bSderaadt 						ctnew++;
729ae8d569bSderaadt 					}
730ae8d569bSderaadt 				}
731ae8d569bSderaadt 				if (chrtran[c] != chrtran[d]) {
732ae8d569bSderaadt 					jackpot++;
733ae8d569bSderaadt 					J[i] = 0;
734ae8d569bSderaadt 					if (c != '\n')
7354ec4b3d5Smillert 						ctold += skipline(f1);
736ae8d569bSderaadt 					if (d != '\n')
7374ec4b3d5Smillert 						ctnew += skipline(f2);
738ae8d569bSderaadt 					break;
739ae8d569bSderaadt 				}
740ae8d569bSderaadt 				if (c == '\n')
741ae8d569bSderaadt 					break;
742ae8d569bSderaadt 			}
743ae8d569bSderaadt 		} else {
744ae8d569bSderaadt 			for (;;) {
745ae8d569bSderaadt 				ctold++;
746ae8d569bSderaadt 				ctnew++;
7474ec4b3d5Smillert 				if ((c = getc(f1)) != (d = getc(f2))) {
748ae8d569bSderaadt 					/* jackpot++; */
749ae8d569bSderaadt 					J[i] = 0;
750ae8d569bSderaadt 					if (c != '\n')
7514ec4b3d5Smillert 						ctold += skipline(f1);
752ae8d569bSderaadt 					if (d != '\n')
7534ec4b3d5Smillert 						ctnew += skipline(f2);
754ae8d569bSderaadt 					break;
755ae8d569bSderaadt 				}
756ae8d569bSderaadt 				if (c == '\n')
757ae8d569bSderaadt 					break;
758ae8d569bSderaadt 			}
759ae8d569bSderaadt 		}
760ae8d569bSderaadt 		ixold[i] = ctold;
761ae8d569bSderaadt 		ixnew[j] = ctnew;
762ae8d569bSderaadt 		j++;
763ae8d569bSderaadt 	}
7644ec4b3d5Smillert 	for (; j <= len[1]; j++)
7654ec4b3d5Smillert 		ixnew[j] = ctnew += skipline(f2);
766ae8d569bSderaadt 	/*
76748b8c3e3Sderaadt 	 * if (jackpot)
76848b8c3e3Sderaadt 	 *	fprintf(stderr, "jackpot\n");
769ae8d569bSderaadt 	 */
770ae8d569bSderaadt }
771ae8d569bSderaadt 
77248b8c3e3Sderaadt /* shellsort CACM #201 */
77326da422aStedu static void
77426da422aStedu sort(struct line *a, int n)
77548b8c3e3Sderaadt {
77648b8c3e3Sderaadt 	struct line *ai, *aim, w;
77748b8c3e3Sderaadt 	int j, m = 0, k;
778ae8d569bSderaadt 
779ae8d569bSderaadt 	if (n == 0)
780ae8d569bSderaadt 		return;
781ae8d569bSderaadt 	for (j = 1; j <= n; j *= 2)
782ae8d569bSderaadt 		m = 2 * j - 1;
783ae8d569bSderaadt 	for (m /= 2; m != 0; m /= 2) {
784ae8d569bSderaadt 		k = n - m;
785ae8d569bSderaadt 		for (j = 1; j <= k; j++) {
786ae8d569bSderaadt 			for (ai = &a[j]; ai > a; ai -= m) {
787ae8d569bSderaadt 				aim = &ai[m];
788ae8d569bSderaadt 				if (aim < ai)
789ae8d569bSderaadt 					break;	/* wraparound */
790ae8d569bSderaadt 				if (aim->value > ai[0].value ||
79126da422aStedu 				    (aim->value == ai[0].value &&
79226da422aStedu 					aim->serial > ai[0].serial))
793ae8d569bSderaadt 					break;
794ae8d569bSderaadt 				w.value = ai[0].value;
795ae8d569bSderaadt 				ai[0].value = aim->value;
796ae8d569bSderaadt 				aim->value = w.value;
797ae8d569bSderaadt 				w.serial = ai[0].serial;
798ae8d569bSderaadt 				ai[0].serial = aim->serial;
799ae8d569bSderaadt 				aim->serial = w.serial;
800ae8d569bSderaadt 			}
801ae8d569bSderaadt 		}
802ae8d569bSderaadt 	}
803ae8d569bSderaadt }
804ae8d569bSderaadt 
80526da422aStedu static void
80626da422aStedu unsort(struct line *f, int l, int *b)
807ae8d569bSderaadt {
80848b8c3e3Sderaadt 	int *a, i;
80926da422aStedu 
81049dffe13Smillert 	a = emalloc((l + 1) * sizeof(int));
811ae8d569bSderaadt 	for (i = 1; i <= l; i++)
812ae8d569bSderaadt 		a[f[i].serial] = f[i].value;
813ae8d569bSderaadt 	for (i = 1; i <= l; i++)
814ae8d569bSderaadt 		b[i] = a[i];
81526da422aStedu 	free(a);
816ae8d569bSderaadt }
817ae8d569bSderaadt 
81826da422aStedu static int
8194ec4b3d5Smillert skipline(FILE *f)
820ae8d569bSderaadt {
82126da422aStedu 	int i, c;
822ae8d569bSderaadt 
8234ec4b3d5Smillert 	for (i = 1; (c = getc(f)) != '\n'; i++)
824ae8d569bSderaadt 		if (c < 0)
825ae8d569bSderaadt 			return (i);
826ae8d569bSderaadt 	return (i);
827ae8d569bSderaadt }
828ae8d569bSderaadt 
82926da422aStedu static void
8304ec4b3d5Smillert output(char *file1, FILE *f1, char *file2, FILE *f2)
831ae8d569bSderaadt {
83248b8c3e3Sderaadt 	int m, i0, i1, j0, j1;
83348b8c3e3Sderaadt 
8344ec4b3d5Smillert 	rewind(f1);
8354ec4b3d5Smillert 	rewind(f2);
836ae8d569bSderaadt 	m = len[0];
837ae8d569bSderaadt 	J[0] = 0;
838ae8d569bSderaadt 	J[m + 1] = len[1] + 1;
8394ec4b3d5Smillert 	if (format != D_EDIT) {
84026da422aStedu 		for (i0 = 1; i0 <= m; i0 = i1 + 1) {
84126da422aStedu 			while (i0 <= m && J[i0] == J[i0 - 1] + 1)
84226da422aStedu 				i0++;
843ae8d569bSderaadt 			j0 = J[i0 - 1] + 1;
844ae8d569bSderaadt 			i1 = i0 - 1;
84526da422aStedu 			while (i1 < m && J[i1 + 1] == 0)
84626da422aStedu 				i1++;
847ae8d569bSderaadt 			j1 = J[i1 + 1] - 1;
848ae8d569bSderaadt 			J[i1] = j1;
8494ec4b3d5Smillert 			change(file1, f1, file2, f2, i0, i1, j0, j1);
85026da422aStedu 		}
85126da422aStedu 	} else {
85226da422aStedu 		for (i0 = m; i0 >= 1; i0 = i1 - 1) {
85326da422aStedu 			while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
85426da422aStedu 				i0--;
855ae8d569bSderaadt 			j0 = J[i0 + 1] - 1;
856ae8d569bSderaadt 			i1 = i0 + 1;
85726da422aStedu 			while (i1 > 1 && J[i1 - 1] == 0)
85826da422aStedu 				i1--;
859ae8d569bSderaadt 			j1 = J[i1 - 1] + 1;
860ae8d569bSderaadt 			J[i1] = j1;
8614ec4b3d5Smillert 			change(file1, f1, file2, f2, i1, i0, j1, j0);
862ae8d569bSderaadt 		}
86326da422aStedu 	}
864ae8d569bSderaadt 	if (m == 0)
8654ec4b3d5Smillert 		change(file1, f1, file2, f2, 1, 0, 1, len[1]);
8664ec4b3d5Smillert 	if (format == D_IFDEF) {
867ae8d569bSderaadt 		for (;;) {
868ae8d569bSderaadt #define	c i0
8694ec4b3d5Smillert 			c = getc(f1);
870ae8d569bSderaadt 			if (c < 0)
871ae8d569bSderaadt 				return;
872ae8d569bSderaadt 			putchar(c);
873ae8d569bSderaadt 		}
874ae8d569bSderaadt #undef c
875ae8d569bSderaadt 	}
8769de32c1bSmillert 	if (anychange != 0) {
8774ec4b3d5Smillert 		if (format == D_CONTEXT)
8784ec4b3d5Smillert 			dump_context_vec(f1, f2);
8794ec4b3d5Smillert 		else if (format == D_UNIFIED)
8804ec4b3d5Smillert 			dump_unified_vec(f1, f2);
8819de32c1bSmillert 	}
882ae8d569bSderaadt }
883ae8d569bSderaadt 
884*c72ea322Smillert static __inline void
885*c72ea322Smillert range(int a, int b, char *separator)
886*c72ea322Smillert {
887*c72ea322Smillert 	printf("%d", a > b ? b : a);
888*c72ea322Smillert 	if (a < b)
889*c72ea322Smillert 		printf("%s%d", separator, b);
890*c72ea322Smillert }
891*c72ea322Smillert 
892*c72ea322Smillert static __inline void
893*c72ea322Smillert uni_range(int a, int b)
894*c72ea322Smillert {
895*c72ea322Smillert 	if (a < b)
896*c72ea322Smillert 		printf("%d,%d", a, b - a + 1);
897*c72ea322Smillert 	else if (a == b)
898*c72ea322Smillert 		printf("%d", b);
899*c72ea322Smillert 	else
900*c72ea322Smillert 		printf("%d,0", b);
901*c72ea322Smillert }
902*c72ea322Smillert 
903ae8d569bSderaadt /*
904ae8d569bSderaadt  * The following struct is used to record change information when
9054ec4b3d5Smillert  * doing a "context" or "unified" diff.  (see routine "change" to
9064ec4b3d5Smillert  * understand the highly mnemonic field names)
907ae8d569bSderaadt  */
908ae8d569bSderaadt struct context_vec {
909ae8d569bSderaadt 	int a;			/* start line in old file */
910ae8d569bSderaadt 	int b;			/* end line in old file */
911ae8d569bSderaadt 	int c;			/* start line in new file */
912ae8d569bSderaadt 	int d;			/* end line in new file */
913ae8d569bSderaadt };
914ae8d569bSderaadt 
91526da422aStedu struct context_vec *context_vec_start, *context_vec_end, *context_vec_ptr;
916ae8d569bSderaadt 
917ae8d569bSderaadt #define	MAX_CONTEXT	128
918ae8d569bSderaadt 
91926da422aStedu /*
9204ec4b3d5Smillert  * Indicate that there is a difference between lines a and b of the from file
92126da422aStedu  * to get to lines c to d of the to file.  If a is greater then b then there
92226da422aStedu  * are no lines in the from file involved and this means that there were
92326da422aStedu  * lines appended (beginning at b).  If c is greater than d then there are
92426da422aStedu  * lines missing from the to file.
925ae8d569bSderaadt  */
92626da422aStedu static void
9274ec4b3d5Smillert change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d)
928ae8d569bSderaadt {
9294ec4b3d5Smillert 	if (format != D_IFDEF && a > b && c > d)
930ae8d569bSderaadt 		return;
931ae8d569bSderaadt 	if (anychange == 0) {
932ae8d569bSderaadt 		anychange = 1;
9334ec4b3d5Smillert 		if (format == D_CONTEXT || format == D_UNIFIED) {
9344ec4b3d5Smillert 			printf("%s %s	%s", format == D_CONTEXT ? "***" : "---",
9354ec4b3d5Smillert 			   file1, ctime(&stb1.st_mtime));
9364ec4b3d5Smillert 			printf("%s %s	%s", format == D_CONTEXT ? "---" : "+++",
9374ec4b3d5Smillert 			    file2, ctime(&stb2.st_mtime));
9384ec4b3d5Smillert 			if (context_vec_start == NULL)
93949dffe13Smillert 				context_vec_start = emalloc(MAX_CONTEXT *
940ae8d569bSderaadt 				    sizeof(struct context_vec));
941ae8d569bSderaadt 			context_vec_end = context_vec_start + MAX_CONTEXT;
942ae8d569bSderaadt 			context_vec_ptr = context_vec_start - 1;
943ae8d569bSderaadt 		}
944ae8d569bSderaadt 	}
9454ec4b3d5Smillert 	if (format == D_CONTEXT || format == D_UNIFIED) {
946ae8d569bSderaadt 		/*
94790f56ad8Smillert 		 * If this new change is within 'context' lines of
948ae8d569bSderaadt 		 * the previous change, just add it to the change
949ae8d569bSderaadt 		 * record.  If the record is full or if this
950ae8d569bSderaadt 		 * change is more than 'context' lines from the previous
951ae8d569bSderaadt 		 * change, dump the record, reset it & add the new change.
952ae8d569bSderaadt 		 */
953ae8d569bSderaadt 		if (context_vec_ptr >= context_vec_end ||
954ae8d569bSderaadt 		    (context_vec_ptr >= context_vec_start &&
955ae8d569bSderaadt 		    a > (context_vec_ptr->b + 2 * context) &&
9569de32c1bSmillert 		    c > (context_vec_ptr->d + 2 * context))) {
9574ec4b3d5Smillert 			if (format == D_CONTEXT)
9584ec4b3d5Smillert 				dump_context_vec(f1, f2);
9599de32c1bSmillert 			else
9604ec4b3d5Smillert 				dump_unified_vec(f1, f2);
9619de32c1bSmillert 		}
962ae8d569bSderaadt 		context_vec_ptr++;
963ae8d569bSderaadt 		context_vec_ptr->a = a;
964ae8d569bSderaadt 		context_vec_ptr->b = b;
965ae8d569bSderaadt 		context_vec_ptr->c = c;
966ae8d569bSderaadt 		context_vec_ptr->d = d;
967ae8d569bSderaadt 		return;
968ae8d569bSderaadt 	}
9694ec4b3d5Smillert 	switch (format) {
970ae8d569bSderaadt 
971ae8d569bSderaadt 	case D_NORMAL:
972ae8d569bSderaadt 	case D_EDIT:
973ae8d569bSderaadt 		range(a, b, ",");
974ae8d569bSderaadt 		putchar(a > b ? 'a' : c > d ? 'd' : 'c');
9754ec4b3d5Smillert 		if (format == D_NORMAL)
976ae8d569bSderaadt 			range(c, d, ",");
977ae8d569bSderaadt 		putchar('\n');
978ae8d569bSderaadt 		break;
979ae8d569bSderaadt 	case D_REVERSE:
980ae8d569bSderaadt 		putchar(a > b ? 'a' : c > d ? 'd' : 'c');
981ae8d569bSderaadt 		range(a, b, " ");
982ae8d569bSderaadt 		putchar('\n');
983ae8d569bSderaadt 		break;
984ae8d569bSderaadt 	case D_NREVERSE:
985ae8d569bSderaadt 		if (a > b)
986ae8d569bSderaadt 			printf("a%d %d\n", b, d - c + 1);
987ae8d569bSderaadt 		else {
988ae8d569bSderaadt 			printf("d%d %d\n", a, b - a + 1);
989ae8d569bSderaadt 			if (!(c > d))
990ae8d569bSderaadt 				/* add changed lines */
991ae8d569bSderaadt 				printf("a%d %d\n", b, d - c + 1);
992ae8d569bSderaadt 		}
993ae8d569bSderaadt 		break;
994ae8d569bSderaadt 	}
9954ec4b3d5Smillert 	if (format == D_NORMAL || format == D_IFDEF) {
9964ec4b3d5Smillert 		fetch(ixold, a, b, f1, "< ", 1);
9974ec4b3d5Smillert 		if (a <= b && c <= d && format == D_NORMAL)
9984ec4b3d5Smillert 			puts("---");
999ae8d569bSderaadt 	}
10004ec4b3d5Smillert 	fetch(ixnew, c, d, f2, format == D_NORMAL ? "> " : "", 0);
10014ec4b3d5Smillert 	if ((format == D_EDIT || format == D_REVERSE) && c <= d)
10024ec4b3d5Smillert 		puts(".");
1003ae8d569bSderaadt 	if (inifdef) {
1004b4bca33fSmillert 		printf("#endif /* %s */\n", ifdefname);
1005ae8d569bSderaadt 		inifdef = 0;
1006ae8d569bSderaadt 	}
1007ae8d569bSderaadt }
1008ae8d569bSderaadt 
100926da422aStedu static void
101026da422aStedu fetch(long *f, int a, int b, FILE *lb, char *s, int oldfile)
1011ae8d569bSderaadt {
101248b8c3e3Sderaadt 	int i, j, c, col, nc;
1013ae8d569bSderaadt 
1014ae8d569bSderaadt 	/*
1015ae8d569bSderaadt 	 * When doing #ifdef's, copy down to current line
1016ae8d569bSderaadt 	 * if this is the first file, so that stuff makes it to output.
1017ae8d569bSderaadt 	 */
10184ec4b3d5Smillert 	if (format == D_IFDEF && oldfile) {
1019ae8d569bSderaadt 		long curpos = ftell(lb);
1020ae8d569bSderaadt 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
1021ae8d569bSderaadt 		nc = f[a > b ? b : a - 1] - curpos;
1022ae8d569bSderaadt 		for (i = 0; i < nc; i++)
1023ae8d569bSderaadt 			putchar(getc(lb));
1024ae8d569bSderaadt 	}
1025ae8d569bSderaadt 	if (a > b)
1026ae8d569bSderaadt 		return;
10274ec4b3d5Smillert 	if (format == D_IFDEF) {
102890f56ad8Smillert 		if (inifdef) {
1029b4bca33fSmillert 			printf("#else /* %s%s */\n",
103090f56ad8Smillert 			    oldfile == 1 ? "!" : "", ifdefname);
103126da422aStedu 		} else {
103290f56ad8Smillert 			if (oldfile)
1033b4bca33fSmillert 				printf("#ifndef %s\n", ifdefname);
103490f56ad8Smillert 			else
1035b4bca33fSmillert 				printf("#ifdef %s\n", ifdefname);
1036ae8d569bSderaadt 		}
1037ae8d569bSderaadt 		inifdef = 1 + oldfile;
1038ae8d569bSderaadt 	}
1039ae8d569bSderaadt 	for (i = a; i <= b; i++) {
104091cf91eeSderaadt 		fseek(lb, f[i - 1], SEEK_SET);
1041ae8d569bSderaadt 		nc = f[i] - f[i - 1];
10424ec4b3d5Smillert 		if (format != D_IFDEF)
10434ec4b3d5Smillert 			fputs(s, stdout);
1044ae8d569bSderaadt 		col = 0;
1045ae8d569bSderaadt 		for (j = 0; j < nc; j++) {
1046ae8d569bSderaadt 			c = getc(lb);
1047ae8d569bSderaadt 			if (c == '\t' && tflag)
1048ae8d569bSderaadt 				do
1049ae8d569bSderaadt 					putchar(' ');
10507d9f164dStedu 				while (++col & 7);
1051ae8d569bSderaadt 			else {
1052ae8d569bSderaadt 				putchar(c);
1053ae8d569bSderaadt 				col++;
1054ae8d569bSderaadt 			}
1055ae8d569bSderaadt 		}
1056ae8d569bSderaadt 	}
1057ae8d569bSderaadt }
1058ae8d569bSderaadt 
1059ae8d569bSderaadt #define POW2			/* define only if HALFLONG is 2**n */
1060ae8d569bSderaadt #define HALFLONG 16
1061ae8d569bSderaadt #define low(x)	(x&((1L<<HALFLONG)-1))
1062ae8d569bSderaadt #define high(x)	(x>>HALFLONG)
1063ae8d569bSderaadt 
1064ae8d569bSderaadt /*
1065ae8d569bSderaadt  * hashing has the effect of
1066ae8d569bSderaadt  * arranging line in 7-bit bytes and then
1067ae8d569bSderaadt  * summing 1-s complement in 16-bit hunks
1068ae8d569bSderaadt  */
106926da422aStedu static int
107026da422aStedu readhash(FILE *f)
1071ae8d569bSderaadt {
107248b8c3e3Sderaadt 	unsigned int shift;
107348b8c3e3Sderaadt 	int t, space;
107426da422aStedu 	long sum;
1075ae8d569bSderaadt 
1076ae8d569bSderaadt 	sum = 1;
1077ae8d569bSderaadt 	space = 0;
1078ae8d569bSderaadt 	if (!bflag && !wflag) {
1079ae8d569bSderaadt 		if (iflag)
1080ae8d569bSderaadt 			for (shift = 0; (t = getc(f)) != '\n'; shift += 7) {
1081ae8d569bSderaadt 				if (t == -1)
1082ae8d569bSderaadt 					return (0);
1083ae8d569bSderaadt 				sum += (long)chrtran[t] << (shift
1084ae8d569bSderaadt #ifdef POW2
1085ae8d569bSderaadt 				    &= HALFLONG - 1);
1086ae8d569bSderaadt #else
1087ae8d569bSderaadt 				    %= HALFLONG);
1088ae8d569bSderaadt #endif
1089ae8d569bSderaadt 			}
1090ae8d569bSderaadt 		else
1091ae8d569bSderaadt 			for (shift = 0; (t = getc(f)) != '\n'; shift += 7) {
1092ae8d569bSderaadt 				if (t == -1)
1093ae8d569bSderaadt 					return (0);
1094ae8d569bSderaadt 				sum += (long)t << (shift
1095ae8d569bSderaadt #ifdef POW2
1096ae8d569bSderaadt 				    &= HALFLONG - 1);
1097ae8d569bSderaadt #else
1098ae8d569bSderaadt 				    %= HALFLONG);
1099ae8d569bSderaadt #endif
1100ae8d569bSderaadt 			}
1101ae8d569bSderaadt 	} else {
1102ae8d569bSderaadt 		for (shift = 0;;) {
1103ae8d569bSderaadt 			switch (t = getc(f)) {
1104ae8d569bSderaadt 			case -1:
1105ae8d569bSderaadt 				return (0);
1106ae8d569bSderaadt 			case '\t':
1107ae8d569bSderaadt 			case ' ':
1108ae8d569bSderaadt 				space++;
1109ae8d569bSderaadt 				continue;
1110ae8d569bSderaadt 			default:
1111ae8d569bSderaadt 				if (space && !wflag) {
1112ae8d569bSderaadt 					shift += 7;
1113ae8d569bSderaadt 					space = 0;
1114ae8d569bSderaadt 				}
1115ae8d569bSderaadt 				sum += (long)chrtran[t] << (shift
1116ae8d569bSderaadt #ifdef POW2
1117ae8d569bSderaadt 				    &= HALFLONG - 1);
1118ae8d569bSderaadt #else
1119ae8d569bSderaadt 				    %= HALFLONG);
1120ae8d569bSderaadt #endif
1121ae8d569bSderaadt 				shift += 7;
1122ae8d569bSderaadt 				continue;
1123ae8d569bSderaadt 			case '\n':
1124ae8d569bSderaadt 				break;
1125ae8d569bSderaadt 			}
1126ae8d569bSderaadt 			break;
1127ae8d569bSderaadt 		}
1128ae8d569bSderaadt 	}
1129ae8d569bSderaadt 	sum = low(sum) + high(sum);
1130ae8d569bSderaadt 	return ((short) low(sum) + (short) high(sum));
1131ae8d569bSderaadt }
1132ae8d569bSderaadt 
11334ec4b3d5Smillert int
113426da422aStedu asciifile(FILE *f)
1135ae8d569bSderaadt {
113648b8c3e3Sderaadt 	char buf[BUFSIZ], *cp;
113726da422aStedu 	int cnt;
1138ae8d569bSderaadt 
11394ec4b3d5Smillert 	if (aflag || f == NULL)
11402a1593dfStedu 		return (1);
11412a1593dfStedu 
11424ec4b3d5Smillert 	rewind(f);
11434ec4b3d5Smillert 	cnt = fread(buf, 1, sizeof(buf), f);
1144ae8d569bSderaadt 	cp = buf;
1145ae8d569bSderaadt 	while (--cnt >= 0)
1146ae8d569bSderaadt 		if (*cp++ & 0200)
1147ae8d569bSderaadt 			return (0);
1148ae8d569bSderaadt 	return (1);
1149ae8d569bSderaadt }
1150ae8d569bSderaadt 
11514ec4b3d5Smillert static __inline int min(int a, int b)
11524ec4b3d5Smillert {
11534ec4b3d5Smillert 	return (a < b ? a : b);
11544ec4b3d5Smillert }
11554ec4b3d5Smillert 
11564ec4b3d5Smillert static __inline int max(int a, int b)
11574ec4b3d5Smillert {
11584ec4b3d5Smillert 	return (a > b ? a : b);
11594ec4b3d5Smillert }
11604ec4b3d5Smillert 
1161ae8d569bSderaadt /* dump accumulated "context" diff changes */
116226da422aStedu static void
11634ec4b3d5Smillert dump_context_vec(FILE *f1, FILE *f2)
1164ae8d569bSderaadt {
116548b8c3e3Sderaadt 	struct context_vec *cvp = context_vec_start;
116648b8c3e3Sderaadt 	int lowa, upb, lowc, upd, do_output;
116726da422aStedu 	int a, b, c, d;
116826da422aStedu 	char ch;
1169ae8d569bSderaadt 
117090f56ad8Smillert 	if (context_vec_start > context_vec_ptr)
1171ae8d569bSderaadt 		return;
1172ae8d569bSderaadt 
117326da422aStedu 	b = d = 0;		/* gcc */
1174ae8d569bSderaadt 	lowa = max(1, cvp->a - context);
1175ae8d569bSderaadt 	upb = min(len[0], context_vec_ptr->b + context);
1176ae8d569bSderaadt 	lowc = max(1, cvp->c - context);
1177ae8d569bSderaadt 	upd = min(len[1], context_vec_ptr->d + context);
1178ae8d569bSderaadt 
1179ae8d569bSderaadt 	printf("***************\n*** ");
1180ae8d569bSderaadt 	range(lowa, upb, ",");
1181ae8d569bSderaadt 	printf(" ****\n");
1182ae8d569bSderaadt 
1183ae8d569bSderaadt 	/*
11844ec4b3d5Smillert 	 * Output changes to the "old" file.  The first loop suppresses
1185ae8d569bSderaadt 	 * output if there were no changes to the "old" file (we'll see
1186ae8d569bSderaadt 	 * the "old" lines as context in the "new" list).
1187ae8d569bSderaadt 	 */
1188ae8d569bSderaadt 	do_output = 0;
1189ae8d569bSderaadt 	for (; cvp <= context_vec_ptr; cvp++)
1190ae8d569bSderaadt 		if (cvp->a <= cvp->b) {
1191ae8d569bSderaadt 			cvp = context_vec_start;
1192ae8d569bSderaadt 			do_output++;
1193ae8d569bSderaadt 			break;
1194ae8d569bSderaadt 		}
1195ae8d569bSderaadt 	if (do_output) {
1196ae8d569bSderaadt 		while (cvp <= context_vec_ptr) {
119726da422aStedu 			a = cvp->a;
119826da422aStedu 			b = cvp->b;
119926da422aStedu 			c = cvp->c;
120026da422aStedu 			d = cvp->d;
1201ae8d569bSderaadt 
1202ae8d569bSderaadt 			if (a <= b && c <= d)
1203ae8d569bSderaadt 				ch = 'c';
1204ae8d569bSderaadt 			else
1205ae8d569bSderaadt 				ch = (a <= b) ? 'd' : 'a';
1206ae8d569bSderaadt 
1207ae8d569bSderaadt 			if (ch == 'a')
12084ec4b3d5Smillert 				fetch(ixold, lowa, b, f1, "  ", 0);
1209ae8d569bSderaadt 			else {
12104ec4b3d5Smillert 				fetch(ixold, lowa, a - 1, f1, "  ", 0);
12114ec4b3d5Smillert 				fetch(ixold, a, b, f1,
121248b8c3e3Sderaadt 				    ch == 'c' ? "! " : "- ", 0);
1213ae8d569bSderaadt 			}
1214ae8d569bSderaadt 			lowa = b + 1;
1215ae8d569bSderaadt 			cvp++;
1216ae8d569bSderaadt 		}
12174ec4b3d5Smillert 		fetch(ixold, b + 1, upb, f1, "  ", 0);
1218ae8d569bSderaadt 	}
1219ae8d569bSderaadt 	/* output changes to the "new" file */
1220ae8d569bSderaadt 	printf("--- ");
1221ae8d569bSderaadt 	range(lowc, upd, ",");
1222ae8d569bSderaadt 	printf(" ----\n");
1223ae8d569bSderaadt 
1224ae8d569bSderaadt 	do_output = 0;
1225ae8d569bSderaadt 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
1226ae8d569bSderaadt 		if (cvp->c <= cvp->d) {
1227ae8d569bSderaadt 			cvp = context_vec_start;
1228ae8d569bSderaadt 			do_output++;
1229ae8d569bSderaadt 			break;
1230ae8d569bSderaadt 		}
1231ae8d569bSderaadt 	if (do_output) {
1232ae8d569bSderaadt 		while (cvp <= context_vec_ptr) {
123326da422aStedu 			a = cvp->a;
123426da422aStedu 			b = cvp->b;
123526da422aStedu 			c = cvp->c;
123626da422aStedu 			d = cvp->d;
1237ae8d569bSderaadt 
1238ae8d569bSderaadt 			if (a <= b && c <= d)
1239ae8d569bSderaadt 				ch = 'c';
1240ae8d569bSderaadt 			else
1241ae8d569bSderaadt 				ch = (a <= b) ? 'd' : 'a';
1242ae8d569bSderaadt 
1243ae8d569bSderaadt 			if (ch == 'd')
12444ec4b3d5Smillert 				fetch(ixnew, lowc, d, f2, "  ", 0);
1245ae8d569bSderaadt 			else {
12464ec4b3d5Smillert 				fetch(ixnew, lowc, c - 1, f2, "  ", 0);
12474ec4b3d5Smillert 				fetch(ixnew, c, d, f2,
124848b8c3e3Sderaadt 				    ch == 'c' ? "! " : "+ ", 0);
1249ae8d569bSderaadt 			}
1250ae8d569bSderaadt 			lowc = d + 1;
1251ae8d569bSderaadt 			cvp++;
1252ae8d569bSderaadt 		}
12534ec4b3d5Smillert 		fetch(ixnew, d + 1, upd, f2, "  ", 0);
1254ae8d569bSderaadt 	}
1255ae8d569bSderaadt 	context_vec_ptr = context_vec_start - 1;
1256ae8d569bSderaadt }
12579de32c1bSmillert 
12589de32c1bSmillert /* dump accumulated "unified" diff changes */
12599de32c1bSmillert static void
12604ec4b3d5Smillert dump_unified_vec(FILE *f1, FILE *f2)
12619de32c1bSmillert {
12629de32c1bSmillert 	struct context_vec *cvp = context_vec_start;
12639de32c1bSmillert 	int lowa, upb, lowc, upd;
12649de32c1bSmillert 	int a, b, c, d;
12659de32c1bSmillert 	char ch;
12669de32c1bSmillert 
126790f56ad8Smillert 	if (context_vec_start > context_vec_ptr)
12689de32c1bSmillert 		return;
12699de32c1bSmillert 
12709de32c1bSmillert 	b = d = 0;		/* gcc */
12719de32c1bSmillert 	lowa = max(1, cvp->a - context);
12729de32c1bSmillert 	upb = min(len[0], context_vec_ptr->b + context);
12739de32c1bSmillert 	lowc = max(1, cvp->c - context);
12749de32c1bSmillert 	upd = min(len[1], context_vec_ptr->d + context);
12759de32c1bSmillert 
1276*c72ea322Smillert 	fputs("@@ -", stdout);
1277*c72ea322Smillert 	uni_range(lowa, upb);
1278*c72ea322Smillert 	fputs(" +", stdout);
1279*c72ea322Smillert 	uni_range(lowc, upd);
1280*c72ea322Smillert 	fputs(" @@\n", stdout);
12819de32c1bSmillert 
12829de32c1bSmillert 	/*
12839de32c1bSmillert 	 * Output changes in "unified" diff format--the old and new lines
12849de32c1bSmillert 	 * are printed together.
12859de32c1bSmillert 	 */
12869de32c1bSmillert 	for (; cvp <= context_vec_ptr; cvp++) {
12879de32c1bSmillert 		a = cvp->a;
12889de32c1bSmillert 		b = cvp->b;
12899de32c1bSmillert 		c = cvp->c;
12909de32c1bSmillert 		d = cvp->d;
12919de32c1bSmillert 
12929de32c1bSmillert 		/*
12939de32c1bSmillert 		 * c: both new and old changes
12949de32c1bSmillert 		 * d: only changes in the old file
12959de32c1bSmillert 		 * a: only changes in the new file
12969de32c1bSmillert 		 */
12979de32c1bSmillert 		if (a <= b && c <= d)
12989de32c1bSmillert 			ch = 'c';
12999de32c1bSmillert 		else
13009de32c1bSmillert 			ch = (a <= b) ? 'd' : 'a';
13019de32c1bSmillert 
13029de32c1bSmillert 		switch (ch) {
13039de32c1bSmillert 		case 'c':
13044ec4b3d5Smillert 			fetch(ixold, lowa, a - 1, f1, " ", 0);
13054ec4b3d5Smillert 			fetch(ixold, a, b, f1, "-", 0);
13064ec4b3d5Smillert 			fetch(ixnew, c, d, f2, "+", 0);
13079de32c1bSmillert 			break;
13089de32c1bSmillert 		case 'd':
13094ec4b3d5Smillert 			fetch(ixold, lowa, a - 1, f1, " ", 0);
13104ec4b3d5Smillert 			fetch(ixold, a, b, f1, "-", 0);
13119de32c1bSmillert 			break;
13129de32c1bSmillert 		case 'a':
13134ec4b3d5Smillert 			fetch(ixnew, lowc, c - 1, f2, " ", 0);
13144ec4b3d5Smillert 			fetch(ixnew, c, d, f2, "+", 0);
13159de32c1bSmillert 			break;
13169de32c1bSmillert 		}
13179de32c1bSmillert 		lowa = b + 1;
13189de32c1bSmillert 		lowc = d + 1;
13199de32c1bSmillert 	}
13204ec4b3d5Smillert 	fetch(ixnew, d + 1, upd, f2, " ", 0);
13219de32c1bSmillert 
13229de32c1bSmillert 	context_vec_ptr = context_vec_start - 1;
13239de32c1bSmillert }
1324