xref: /openbsd-src/usr.bin/cvs/diff_internals.c (revision 3ad3fb451433a2cb29008ed8ecedd399bde46da1)
1*3ad3fb45Sjoris /*	$OpenBSD: diff_internals.c,v 1.1 2006/05/27 03:30:30 joris Exp $	*/
2*3ad3fb45Sjoris /*
3*3ad3fb45Sjoris  * Copyright (C) Caldera International Inc.  2001-2002.
4*3ad3fb45Sjoris  * All rights reserved.
5*3ad3fb45Sjoris  *
6*3ad3fb45Sjoris  * Redistribution and use in source and binary forms, with or without
7*3ad3fb45Sjoris  * modification, are permitted provided that the following conditions
8*3ad3fb45Sjoris  * are met:
9*3ad3fb45Sjoris  * 1. Redistributions of source code and documentation must retain the above
10*3ad3fb45Sjoris  *    copyright notice, this list of conditions and the following disclaimer.
11*3ad3fb45Sjoris  * 2. Redistributions in binary form must reproduce the above copyright
12*3ad3fb45Sjoris  *    notice, this list of conditions and the following disclaimer in the
13*3ad3fb45Sjoris  *    documentation and/or other materials provided with the distribution.
14*3ad3fb45Sjoris  * 3. All advertising materials mentioning features or use of this software
15*3ad3fb45Sjoris  *    must display the following acknowledgement:
16*3ad3fb45Sjoris  *	This product includes software developed or owned by Caldera
17*3ad3fb45Sjoris  *	International, Inc.
18*3ad3fb45Sjoris  * 4. Neither the name of Caldera International, Inc. nor the names of other
19*3ad3fb45Sjoris  *    contributors may be used to endorse or promote products derived from
20*3ad3fb45Sjoris  *    this software without specific prior written permission.
21*3ad3fb45Sjoris  *
22*3ad3fb45Sjoris  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
23*3ad3fb45Sjoris  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
24*3ad3fb45Sjoris  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25*3ad3fb45Sjoris  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26*3ad3fb45Sjoris  * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
27*3ad3fb45Sjoris  * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28*3ad3fb45Sjoris  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29*3ad3fb45Sjoris  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30*3ad3fb45Sjoris  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31*3ad3fb45Sjoris  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32*3ad3fb45Sjoris  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33*3ad3fb45Sjoris  * POSSIBILITY OF SUCH DAMAGE.
34*3ad3fb45Sjoris  */
35*3ad3fb45Sjoris /*-
36*3ad3fb45Sjoris  * Copyright (c) 1991, 1993
37*3ad3fb45Sjoris  *	The Regents of the University of California.  All rights reserved.
38*3ad3fb45Sjoris  * Copyright (c) 2004 Jean-Francois Brousseau.  All rights reserved.
39*3ad3fb45Sjoris  *
40*3ad3fb45Sjoris  * Redistribution and use in source and binary forms, with or without
41*3ad3fb45Sjoris  * modification, are permitted provided that the following conditions
42*3ad3fb45Sjoris  * are met:
43*3ad3fb45Sjoris  * 1. Redistributions of source code must retain the above copyright
44*3ad3fb45Sjoris  *    notice, this list of conditions and the following disclaimer.
45*3ad3fb45Sjoris  * 2. Redistributions in binary form must reproduce the above copyright
46*3ad3fb45Sjoris  *    notice, this list of conditions and the following disclaimer in the
47*3ad3fb45Sjoris  *    documentation and/or other materials provided with the distribution.
48*3ad3fb45Sjoris  * 3. Neither the name of the University nor the names of its contributors
49*3ad3fb45Sjoris  *    may be used to endorse or promote products derived from this software
50*3ad3fb45Sjoris  *    without specific prior written permission.
51*3ad3fb45Sjoris  *
52*3ad3fb45Sjoris  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53*3ad3fb45Sjoris  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54*3ad3fb45Sjoris  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55*3ad3fb45Sjoris  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56*3ad3fb45Sjoris  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57*3ad3fb45Sjoris  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58*3ad3fb45Sjoris  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59*3ad3fb45Sjoris  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60*3ad3fb45Sjoris  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61*3ad3fb45Sjoris  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62*3ad3fb45Sjoris  * SUCH DAMAGE.
63*3ad3fb45Sjoris  *
64*3ad3fb45Sjoris  *	@(#)diffreg.c   8.1 (Berkeley) 6/6/93
65*3ad3fb45Sjoris  */
66*3ad3fb45Sjoris /*
67*3ad3fb45Sjoris  *	Uses an algorithm due to Harold Stone, which finds
68*3ad3fb45Sjoris  *	a pair of longest identical subsequences in the two
69*3ad3fb45Sjoris  *	files.
70*3ad3fb45Sjoris  *
71*3ad3fb45Sjoris  *	The major goal is to generate the match vector J.
72*3ad3fb45Sjoris  *	J[i] is the index of the line in file1 corresponding
73*3ad3fb45Sjoris  *	to line i file0. J[i] = 0 if there is no
74*3ad3fb45Sjoris  *	such line in file1.
75*3ad3fb45Sjoris  *
76*3ad3fb45Sjoris  *	Lines are hashed so as to work in core. All potential
77*3ad3fb45Sjoris  *	matches are located by sorting the lines of each file
78*3ad3fb45Sjoris  *	on the hash (called ``value''). In particular, this
79*3ad3fb45Sjoris  *	collects the equivalence classes in file1 together.
80*3ad3fb45Sjoris  *	Subroutine equiv replaces the value of each line in
81*3ad3fb45Sjoris  *	file0 by the index of the first element of its
82*3ad3fb45Sjoris  *	matching equivalence in (the reordered) file1.
83*3ad3fb45Sjoris  *	To save space equiv squeezes file1 into a single
84*3ad3fb45Sjoris  *	array member in which the equivalence classes
85*3ad3fb45Sjoris  *	are simply concatenated, except that their first
86*3ad3fb45Sjoris  *	members are flagged by changing sign.
87*3ad3fb45Sjoris  *
88*3ad3fb45Sjoris  *	Next the indices that point into member are unsorted into
89*3ad3fb45Sjoris  *	array class according to the original order of file0.
90*3ad3fb45Sjoris  *
91*3ad3fb45Sjoris  *	The cleverness lies in routine stone. This marches
92*3ad3fb45Sjoris  *	through the lines of file0, developing a vector klist
93*3ad3fb45Sjoris  *	of "k-candidates". At step i a k-candidate is a matched
94*3ad3fb45Sjoris  *	pair of lines x,y (x in file0 y in file1) such that
95*3ad3fb45Sjoris  *	there is a common subsequence of length k
96*3ad3fb45Sjoris  *	between the first i lines of file0 and the first y
97*3ad3fb45Sjoris  *	lines of file1, but there is no such subsequence for
98*3ad3fb45Sjoris  *	any smaller y. x is the earliest possible mate to y
99*3ad3fb45Sjoris  *	that occurs in such a subsequence.
100*3ad3fb45Sjoris  *
101*3ad3fb45Sjoris  *	Whenever any of the members of the equivalence class of
102*3ad3fb45Sjoris  *	lines in file1 matable to a line in file0 has serial number
103*3ad3fb45Sjoris  *	less than the y of some k-candidate, that k-candidate
104*3ad3fb45Sjoris  *	with the smallest such y is replaced. The new
105*3ad3fb45Sjoris  *	k-candidate is chained (via pred) to the current
106*3ad3fb45Sjoris  *	k-1 candidate so that the actual subsequence can
107*3ad3fb45Sjoris  *	be recovered. When a member has serial number greater
108*3ad3fb45Sjoris  *	that the y of all k-candidates, the klist is extended.
109*3ad3fb45Sjoris  *	At the end, the longest subsequence is pulled out
110*3ad3fb45Sjoris  *	and placed in the array J by unravel
111*3ad3fb45Sjoris  *
112*3ad3fb45Sjoris  *	With J in hand, the matches there recorded are
113*3ad3fb45Sjoris  *	check'ed against reality to assure that no spurious
114*3ad3fb45Sjoris  *	matches have crept in due to hashing. If they have,
115*3ad3fb45Sjoris  *	they are broken, and "jackpot" is recorded--a harmless
116*3ad3fb45Sjoris  *	matter except that a true match for a spuriously
117*3ad3fb45Sjoris  *	mated line may now be unnecessarily reported as a change.
118*3ad3fb45Sjoris  *
119*3ad3fb45Sjoris  *	Much of the complexity of the program comes simply
120*3ad3fb45Sjoris  *	from trying to minimize core utilization and
121*3ad3fb45Sjoris  *	maximize the range of doable problems by dynamically
122*3ad3fb45Sjoris  *	allocating what is needed and reusing what is not.
123*3ad3fb45Sjoris  *	The core requirements for problems larger than somewhat
124*3ad3fb45Sjoris  *	are (in words) 2*length(file0) + length(file1) +
125*3ad3fb45Sjoris  *	3*(number of k-candidates installed),  typically about
126*3ad3fb45Sjoris  *	6n words for files of length n.
127*3ad3fb45Sjoris  */
128*3ad3fb45Sjoris 
129*3ad3fb45Sjoris #include "includes.h"
130*3ad3fb45Sjoris 
131*3ad3fb45Sjoris #include "buf.h"
132*3ad3fb45Sjoris #include "cvs.h"
133*3ad3fb45Sjoris #include "diff.h"
134*3ad3fb45Sjoris #include "log.h"
135*3ad3fb45Sjoris 
136*3ad3fb45Sjoris #include "xmalloc.h"
137*3ad3fb45Sjoris 
138*3ad3fb45Sjoris struct cand {
139*3ad3fb45Sjoris 	int	x;
140*3ad3fb45Sjoris 	int	y;
141*3ad3fb45Sjoris 	int	pred;
142*3ad3fb45Sjoris } cand;
143*3ad3fb45Sjoris 
144*3ad3fb45Sjoris struct line {
145*3ad3fb45Sjoris 	int	serial;
146*3ad3fb45Sjoris 	int	value;
147*3ad3fb45Sjoris } *file[2];
148*3ad3fb45Sjoris 
149*3ad3fb45Sjoris /*
150*3ad3fb45Sjoris  * The following struct is used to record change in formation when
151*3ad3fb45Sjoris  * doing a "context" or "unified" diff.  (see routine "change" to
152*3ad3fb45Sjoris  * understand the highly mnemonic field names)
153*3ad3fb45Sjoris  */
154*3ad3fb45Sjoris struct context_vec {
155*3ad3fb45Sjoris 	int	a;	/* start line in old file */
156*3ad3fb45Sjoris 	int	b;	/* end line in old file */
157*3ad3fb45Sjoris 	int	c;	/* start line in new file */
158*3ad3fb45Sjoris 	int	d;	/* end line in new file */
159*3ad3fb45Sjoris };
160*3ad3fb45Sjoris 
161*3ad3fb45Sjoris struct diff_arg {
162*3ad3fb45Sjoris 	char	*rev1;
163*3ad3fb45Sjoris 	char	*rev2;
164*3ad3fb45Sjoris 	char	*date1;
165*3ad3fb45Sjoris 	char	*date2;
166*3ad3fb45Sjoris };
167*3ad3fb45Sjoris 
168*3ad3fb45Sjoris static void	 output(FILE *, FILE *);
169*3ad3fb45Sjoris static void	 check(FILE *, FILE *);
170*3ad3fb45Sjoris static void	 range(int, int, char *);
171*3ad3fb45Sjoris static void	 uni_range(int, int);
172*3ad3fb45Sjoris static void	 dump_context_vec(FILE *, FILE *);
173*3ad3fb45Sjoris static void	 dump_unified_vec(FILE *, FILE *);
174*3ad3fb45Sjoris static int	 prepare(int, FILE *, off_t);
175*3ad3fb45Sjoris static void	 prune(void);
176*3ad3fb45Sjoris static void	 equiv(struct line *, int, struct line *, int, int *);
177*3ad3fb45Sjoris static void	 unravel(int);
178*3ad3fb45Sjoris static void	 unsort(struct line *, int, int *);
179*3ad3fb45Sjoris static void	 change(FILE *, FILE *, int, int, int, int);
180*3ad3fb45Sjoris static void	 sort(struct line *, int);
181*3ad3fb45Sjoris static int	 ignoreline(char *);
182*3ad3fb45Sjoris static int	 asciifile(FILE *);
183*3ad3fb45Sjoris static void	 fetch(long *, int, int, FILE *, int, int);
184*3ad3fb45Sjoris static int	 newcand(int, int, int);
185*3ad3fb45Sjoris static int	 search(int *, int, int);
186*3ad3fb45Sjoris static int	 skipline(FILE *);
187*3ad3fb45Sjoris static int	 isqrt(int);
188*3ad3fb45Sjoris static int	 stone(int *, int, int *, int *);
189*3ad3fb45Sjoris static int	 readhash(FILE *);
190*3ad3fb45Sjoris static int	 files_differ(FILE *, FILE *);
191*3ad3fb45Sjoris static char	*match_function(const long *, int, FILE *);
192*3ad3fb45Sjoris static char	*preadline(int, size_t, off_t);
193*3ad3fb45Sjoris 
194*3ad3fb45Sjoris static int aflag, bflag, dflag, iflag, pflag, tflag, Tflag, wflag;
195*3ad3fb45Sjoris static int context = 3;
196*3ad3fb45Sjoris int diff_format = D_NORMAL;
197*3ad3fb45Sjoris char *diff_file = NULL;
198*3ad3fb45Sjoris RCSNUM *diff_rev1 = NULL;
199*3ad3fb45Sjoris RCSNUM *diff_rev2 = NULL;
200*3ad3fb45Sjoris char diffargs[128];
201*3ad3fb45Sjoris static struct stat stb1, stb2;
202*3ad3fb45Sjoris static char *ifdefname, *ignore_pats;
203*3ad3fb45Sjoris regex_t ignore_re;
204*3ad3fb45Sjoris 
205*3ad3fb45Sjoris static int  *J;			/* will be overlaid on class */
206*3ad3fb45Sjoris static int  *class;		/* will be overlaid on file[0] */
207*3ad3fb45Sjoris static int  *klist;		/* will be overlaid on file[0] after class */
208*3ad3fb45Sjoris static int  *member;		/* will be overlaid on file[1] */
209*3ad3fb45Sjoris static int   clen;
210*3ad3fb45Sjoris static int   inifdef;		/* whether or not we are in a #ifdef block */
211*3ad3fb45Sjoris static int   diff_len[2];
212*3ad3fb45Sjoris static int   pref, suff;	/* length of prefix and suffix */
213*3ad3fb45Sjoris static int   slen[2];
214*3ad3fb45Sjoris static int   anychange;
215*3ad3fb45Sjoris static long *ixnew;		/* will be overlaid on file[1] */
216*3ad3fb45Sjoris static long *ixold;		/* will be overlaid on klist */
217*3ad3fb45Sjoris static struct cand *clist;	/* merely a free storage pot for candidates */
218*3ad3fb45Sjoris static int   clistlen;		/* the length of clist */
219*3ad3fb45Sjoris static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
220*3ad3fb45Sjoris static u_char *chrtran;		/* translation table for case-folding */
221*3ad3fb45Sjoris static struct context_vec *context_vec_start;
222*3ad3fb45Sjoris static struct context_vec *context_vec_end;
223*3ad3fb45Sjoris static struct context_vec *context_vec_ptr;
224*3ad3fb45Sjoris 
225*3ad3fb45Sjoris #define FUNCTION_CONTEXT_SIZE	41
226*3ad3fb45Sjoris static char lastbuf[FUNCTION_CONTEXT_SIZE];
227*3ad3fb45Sjoris static int  lastline;
228*3ad3fb45Sjoris static int  lastmatchline;
229*3ad3fb45Sjoris BUF  *diffbuf = NULL;
230*3ad3fb45Sjoris 
231*3ad3fb45Sjoris /*
232*3ad3fb45Sjoris  * chrtran points to one of 2 translation tables: cup2low if folding upper to
233*3ad3fb45Sjoris  * lower case clow2low if not folding case
234*3ad3fb45Sjoris  */
235*3ad3fb45Sjoris u_char clow2low[256] = {
236*3ad3fb45Sjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
237*3ad3fb45Sjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
238*3ad3fb45Sjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
239*3ad3fb45Sjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
240*3ad3fb45Sjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
241*3ad3fb45Sjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
242*3ad3fb45Sjoris 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
243*3ad3fb45Sjoris 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
244*3ad3fb45Sjoris 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
245*3ad3fb45Sjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
246*3ad3fb45Sjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
247*3ad3fb45Sjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
248*3ad3fb45Sjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
249*3ad3fb45Sjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
250*3ad3fb45Sjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
251*3ad3fb45Sjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
252*3ad3fb45Sjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
253*3ad3fb45Sjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
254*3ad3fb45Sjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
255*3ad3fb45Sjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
256*3ad3fb45Sjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
257*3ad3fb45Sjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
258*3ad3fb45Sjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
259*3ad3fb45Sjoris 	0xfd, 0xfe, 0xff
260*3ad3fb45Sjoris };
261*3ad3fb45Sjoris 
262*3ad3fb45Sjoris u_char cup2low[256] = {
263*3ad3fb45Sjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
264*3ad3fb45Sjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
265*3ad3fb45Sjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
266*3ad3fb45Sjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
267*3ad3fb45Sjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
268*3ad3fb45Sjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
269*3ad3fb45Sjoris 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
270*3ad3fb45Sjoris 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
271*3ad3fb45Sjoris 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
272*3ad3fb45Sjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
273*3ad3fb45Sjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
274*3ad3fb45Sjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
275*3ad3fb45Sjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
276*3ad3fb45Sjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
277*3ad3fb45Sjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
278*3ad3fb45Sjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
279*3ad3fb45Sjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
280*3ad3fb45Sjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
281*3ad3fb45Sjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
282*3ad3fb45Sjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
283*3ad3fb45Sjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
284*3ad3fb45Sjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
285*3ad3fb45Sjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
286*3ad3fb45Sjoris 	0xfd, 0xfe, 0xff
287*3ad3fb45Sjoris };
288*3ad3fb45Sjoris 
289*3ad3fb45Sjoris int
290*3ad3fb45Sjoris cvs_diffreg(const char *file1, const char *file2, BUF *out)
291*3ad3fb45Sjoris {
292*3ad3fb45Sjoris 	FILE *f1, *f2;
293*3ad3fb45Sjoris 	int i, rval;
294*3ad3fb45Sjoris 	void *tmp;
295*3ad3fb45Sjoris 
296*3ad3fb45Sjoris 	f1 = f2 = NULL;
297*3ad3fb45Sjoris 	rval = D_SAME;
298*3ad3fb45Sjoris 	anychange = 0;
299*3ad3fb45Sjoris 	lastline = 0;
300*3ad3fb45Sjoris 	lastmatchline = 0;
301*3ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
302*3ad3fb45Sjoris 	chrtran = (iflag ? cup2low : clow2low);
303*3ad3fb45Sjoris 	if (out != NULL)
304*3ad3fb45Sjoris 		diffbuf = out;
305*3ad3fb45Sjoris 
306*3ad3fb45Sjoris 	f1 = fopen(file1, "r");
307*3ad3fb45Sjoris 	if (f1 == NULL) {
308*3ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file1);
309*3ad3fb45Sjoris 		goto closem;
310*3ad3fb45Sjoris 	}
311*3ad3fb45Sjoris 
312*3ad3fb45Sjoris 	f2 = fopen(file2, "r");
313*3ad3fb45Sjoris 	if (f2 == NULL) {
314*3ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file2);
315*3ad3fb45Sjoris 		goto closem;
316*3ad3fb45Sjoris 	}
317*3ad3fb45Sjoris 
318*3ad3fb45Sjoris 	if (stat(file1, &stb1) < 0) {
319*3ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file1);
320*3ad3fb45Sjoris 		goto closem;
321*3ad3fb45Sjoris 	}
322*3ad3fb45Sjoris 	if (stat(file2, &stb2) < 0) {
323*3ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file2);
324*3ad3fb45Sjoris 		goto closem;
325*3ad3fb45Sjoris 	}
326*3ad3fb45Sjoris 	switch (files_differ(f1, f2)) {
327*3ad3fb45Sjoris 	case 0:
328*3ad3fb45Sjoris 		goto closem;
329*3ad3fb45Sjoris 	case 1:
330*3ad3fb45Sjoris 		break;
331*3ad3fb45Sjoris 	default:
332*3ad3fb45Sjoris 		/* error */
333*3ad3fb45Sjoris 		goto closem;
334*3ad3fb45Sjoris 	}
335*3ad3fb45Sjoris 
336*3ad3fb45Sjoris 	if (!asciifile(f1) || !asciifile(f2)) {
337*3ad3fb45Sjoris 		rval = D_BINARY;
338*3ad3fb45Sjoris 		goto closem;
339*3ad3fb45Sjoris 	}
340*3ad3fb45Sjoris 	if (prepare(0, f1, stb1.st_size) < 0 ||
341*3ad3fb45Sjoris 	    prepare(1, f2, stb2.st_size) < 0) {
342*3ad3fb45Sjoris 		goto closem;
343*3ad3fb45Sjoris 	}
344*3ad3fb45Sjoris 	prune();
345*3ad3fb45Sjoris 	sort(sfile[0], slen[0]);
346*3ad3fb45Sjoris 	sort(sfile[1], slen[1]);
347*3ad3fb45Sjoris 
348*3ad3fb45Sjoris 	member = (int *)file[1];
349*3ad3fb45Sjoris 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
350*3ad3fb45Sjoris 	tmp = xrealloc(member, slen[1] + 2, sizeof(*member));
351*3ad3fb45Sjoris 	member = tmp;
352*3ad3fb45Sjoris 
353*3ad3fb45Sjoris 	class = (int *)file[0];
354*3ad3fb45Sjoris 	unsort(sfile[0], slen[0], class);
355*3ad3fb45Sjoris 	tmp = xrealloc(class, slen[0] + 2, sizeof(*class));
356*3ad3fb45Sjoris 	class = tmp;
357*3ad3fb45Sjoris 
358*3ad3fb45Sjoris 	klist = xcalloc(slen[0] + 2, sizeof(*klist));
359*3ad3fb45Sjoris 	clen = 0;
360*3ad3fb45Sjoris 	clistlen = 100;
361*3ad3fb45Sjoris 	clist = xcalloc(clistlen, sizeof(*clist));
362*3ad3fb45Sjoris 
363*3ad3fb45Sjoris 	if ((i = stone(class, slen[0], member, klist)) < 0)
364*3ad3fb45Sjoris 		goto closem;
365*3ad3fb45Sjoris 
366*3ad3fb45Sjoris 	xfree(member);
367*3ad3fb45Sjoris 	xfree(class);
368*3ad3fb45Sjoris 
369*3ad3fb45Sjoris 	tmp = xrealloc(J, diff_len[0] + 2, sizeof(*J));
370*3ad3fb45Sjoris 	J = tmp;
371*3ad3fb45Sjoris 	unravel(klist[i]);
372*3ad3fb45Sjoris 	xfree(clist);
373*3ad3fb45Sjoris 	xfree(klist);
374*3ad3fb45Sjoris 
375*3ad3fb45Sjoris 	tmp = xrealloc(ixold, diff_len[0] + 2, sizeof(*ixold));
376*3ad3fb45Sjoris 	ixold = tmp;
377*3ad3fb45Sjoris 
378*3ad3fb45Sjoris 	tmp = xrealloc(ixnew, diff_len[1] + 2, sizeof(*ixnew));
379*3ad3fb45Sjoris 	ixnew = tmp;
380*3ad3fb45Sjoris 	check(f1, f2);
381*3ad3fb45Sjoris 	output(f1, f2);
382*3ad3fb45Sjoris 
383*3ad3fb45Sjoris closem:
384*3ad3fb45Sjoris 	if (anychange == 1) {
385*3ad3fb45Sjoris 		if (rval == D_SAME)
386*3ad3fb45Sjoris 			rval = D_DIFFER;
387*3ad3fb45Sjoris 	}
388*3ad3fb45Sjoris 	if (f1 != NULL)
389*3ad3fb45Sjoris 		fclose(f1);
390*3ad3fb45Sjoris 	if (f2 != NULL)
391*3ad3fb45Sjoris 		fclose(f2);
392*3ad3fb45Sjoris 
393*3ad3fb45Sjoris 	return (rval);
394*3ad3fb45Sjoris }
395*3ad3fb45Sjoris 
396*3ad3fb45Sjoris /*
397*3ad3fb45Sjoris  * Check to see if the given files differ.
398*3ad3fb45Sjoris  * Returns 0 if they are the same, 1 if different, and -1 on error.
399*3ad3fb45Sjoris  * XXX - could use code from cmp(1) [faster]
400*3ad3fb45Sjoris  */
401*3ad3fb45Sjoris static int
402*3ad3fb45Sjoris files_differ(FILE *f1, FILE *f2)
403*3ad3fb45Sjoris {
404*3ad3fb45Sjoris 	char buf1[BUFSIZ], buf2[BUFSIZ];
405*3ad3fb45Sjoris 	size_t i, j;
406*3ad3fb45Sjoris 
407*3ad3fb45Sjoris 	if (stb1.st_size != stb2.st_size)
408*3ad3fb45Sjoris 		return (1);
409*3ad3fb45Sjoris 	for (;;) {
410*3ad3fb45Sjoris 		i = fread(buf1, (size_t)1, sizeof(buf1), f1);
411*3ad3fb45Sjoris 		j = fread(buf2, (size_t)1, sizeof(buf2), f2);
412*3ad3fb45Sjoris 		if (i != j)
413*3ad3fb45Sjoris 			return (1);
414*3ad3fb45Sjoris 		if (i == 0 && j == 0) {
415*3ad3fb45Sjoris 			if (ferror(f1) || ferror(f2))
416*3ad3fb45Sjoris 				return (1);
417*3ad3fb45Sjoris 			return (0);
418*3ad3fb45Sjoris 		}
419*3ad3fb45Sjoris 		if (memcmp(buf1, buf2, i) != 0)
420*3ad3fb45Sjoris 			return (1);
421*3ad3fb45Sjoris 	}
422*3ad3fb45Sjoris }
423*3ad3fb45Sjoris 
424*3ad3fb45Sjoris static int
425*3ad3fb45Sjoris prepare(int i, FILE *fd, off_t filesize)
426*3ad3fb45Sjoris {
427*3ad3fb45Sjoris 	void *tmp;
428*3ad3fb45Sjoris 	struct line *p;
429*3ad3fb45Sjoris 	int j, h;
430*3ad3fb45Sjoris 	size_t sz;
431*3ad3fb45Sjoris 
432*3ad3fb45Sjoris 	rewind(fd);
433*3ad3fb45Sjoris 
434*3ad3fb45Sjoris 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
435*3ad3fb45Sjoris 	if (sz < 100)
436*3ad3fb45Sjoris 		sz = 100;
437*3ad3fb45Sjoris 
438*3ad3fb45Sjoris 	p = xcalloc(sz + 3, sizeof(*p));
439*3ad3fb45Sjoris 	for (j = 0; (h = readhash(fd));) {
440*3ad3fb45Sjoris 		if (j == (int)sz) {
441*3ad3fb45Sjoris 			sz = sz * 3 / 2;
442*3ad3fb45Sjoris 			tmp = xrealloc(p, sz + 3, sizeof(*p));
443*3ad3fb45Sjoris 			p = tmp;
444*3ad3fb45Sjoris 		}
445*3ad3fb45Sjoris 		p[++j].value = h;
446*3ad3fb45Sjoris 	}
447*3ad3fb45Sjoris 	diff_len[i] = j;
448*3ad3fb45Sjoris 	file[i] = p;
449*3ad3fb45Sjoris 
450*3ad3fb45Sjoris 	return (0);
451*3ad3fb45Sjoris }
452*3ad3fb45Sjoris 
453*3ad3fb45Sjoris static void
454*3ad3fb45Sjoris prune(void)
455*3ad3fb45Sjoris {
456*3ad3fb45Sjoris 	int i, j;
457*3ad3fb45Sjoris 
458*3ad3fb45Sjoris 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
459*3ad3fb45Sjoris 	    file[0][pref + 1].value == file[1][pref + 1].value;
460*3ad3fb45Sjoris 	    pref++)
461*3ad3fb45Sjoris 		;
462*3ad3fb45Sjoris 	for (suff = 0;
463*3ad3fb45Sjoris 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
464*3ad3fb45Sjoris 	    (file[0][diff_len[0] - suff].value ==
465*3ad3fb45Sjoris 	    file[1][diff_len[1] - suff].value);
466*3ad3fb45Sjoris 	    suff++)
467*3ad3fb45Sjoris 		;
468*3ad3fb45Sjoris 	for (j = 0; j < 2; j++) {
469*3ad3fb45Sjoris 		sfile[j] = file[j] + pref;
470*3ad3fb45Sjoris 		slen[j] = diff_len[j] - pref - suff;
471*3ad3fb45Sjoris 		for (i = 0; i <= slen[j]; i++)
472*3ad3fb45Sjoris 			sfile[j][i].serial = i;
473*3ad3fb45Sjoris 	}
474*3ad3fb45Sjoris }
475*3ad3fb45Sjoris 
476*3ad3fb45Sjoris static void
477*3ad3fb45Sjoris equiv(struct line *a, int n, struct line *b, int m, int *c)
478*3ad3fb45Sjoris {
479*3ad3fb45Sjoris 	int i, j;
480*3ad3fb45Sjoris 
481*3ad3fb45Sjoris 	i = j = 1;
482*3ad3fb45Sjoris 	while (i <= n && j <= m) {
483*3ad3fb45Sjoris 		if (a[i].value < b[j].value)
484*3ad3fb45Sjoris 			a[i++].value = 0;
485*3ad3fb45Sjoris 		else if (a[i].value == b[j].value)
486*3ad3fb45Sjoris 			a[i++].value = j;
487*3ad3fb45Sjoris 		else
488*3ad3fb45Sjoris 			j++;
489*3ad3fb45Sjoris 	}
490*3ad3fb45Sjoris 	while (i <= n)
491*3ad3fb45Sjoris 		a[i++].value = 0;
492*3ad3fb45Sjoris 	b[m + 1].value = 0;
493*3ad3fb45Sjoris 	j = 0;
494*3ad3fb45Sjoris 	while (++j <= m) {
495*3ad3fb45Sjoris 		c[j] = -b[j].serial;
496*3ad3fb45Sjoris 		while (b[j + 1].value == b[j].value) {
497*3ad3fb45Sjoris 			j++;
498*3ad3fb45Sjoris 			c[j] = b[j].serial;
499*3ad3fb45Sjoris 		}
500*3ad3fb45Sjoris 	}
501*3ad3fb45Sjoris 	c[j] = -1;
502*3ad3fb45Sjoris }
503*3ad3fb45Sjoris 
504*3ad3fb45Sjoris /* Code taken from ping.c */
505*3ad3fb45Sjoris static int
506*3ad3fb45Sjoris isqrt(int n)
507*3ad3fb45Sjoris {
508*3ad3fb45Sjoris 	int y, x = 1;
509*3ad3fb45Sjoris 
510*3ad3fb45Sjoris 	if (n == 0)
511*3ad3fb45Sjoris 		return (0);
512*3ad3fb45Sjoris 
513*3ad3fb45Sjoris 	do { /* newton was a stinker */
514*3ad3fb45Sjoris 		y = x;
515*3ad3fb45Sjoris 		x = n / x;
516*3ad3fb45Sjoris 		x += y;
517*3ad3fb45Sjoris 		x /= 2;
518*3ad3fb45Sjoris 	} while (x - y > 1 || x - y < -1);
519*3ad3fb45Sjoris 
520*3ad3fb45Sjoris 	return (x);
521*3ad3fb45Sjoris }
522*3ad3fb45Sjoris 
523*3ad3fb45Sjoris static int
524*3ad3fb45Sjoris stone(int *a, int n, int *b, int *c)
525*3ad3fb45Sjoris {
526*3ad3fb45Sjoris 	int ret;
527*3ad3fb45Sjoris 	int i, k, y, j, l;
528*3ad3fb45Sjoris 	int oldc, tc, oldl;
529*3ad3fb45Sjoris 	u_int numtries;
530*3ad3fb45Sjoris 
531*3ad3fb45Sjoris 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
532*3ad3fb45Sjoris 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
533*3ad3fb45Sjoris 
534*3ad3fb45Sjoris 	k = 0;
535*3ad3fb45Sjoris 	if ((ret = newcand(0, 0, 0)) < 0)
536*3ad3fb45Sjoris 		return (-1);
537*3ad3fb45Sjoris 	c[0] = ret;
538*3ad3fb45Sjoris 	for (i = 1; i <= n; i++) {
539*3ad3fb45Sjoris 		j = a[i];
540*3ad3fb45Sjoris 		if (j == 0)
541*3ad3fb45Sjoris 			continue;
542*3ad3fb45Sjoris 		y = -b[j];
543*3ad3fb45Sjoris 		oldl = 0;
544*3ad3fb45Sjoris 		oldc = c[0];
545*3ad3fb45Sjoris 		numtries = 0;
546*3ad3fb45Sjoris 		do {
547*3ad3fb45Sjoris 			if (y <= clist[oldc].y)
548*3ad3fb45Sjoris 				continue;
549*3ad3fb45Sjoris 			l = search(c, k, y);
550*3ad3fb45Sjoris 			if (l != oldl + 1)
551*3ad3fb45Sjoris 				oldc = c[l - 1];
552*3ad3fb45Sjoris 			if (l <= k) {
553*3ad3fb45Sjoris 				if (clist[c[l]].y <= y)
554*3ad3fb45Sjoris 					continue;
555*3ad3fb45Sjoris 				tc = c[l];
556*3ad3fb45Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
557*3ad3fb45Sjoris 					return (-1);
558*3ad3fb45Sjoris 				c[l] = ret;
559*3ad3fb45Sjoris 				oldc = tc;
560*3ad3fb45Sjoris 				oldl = l;
561*3ad3fb45Sjoris 				numtries++;
562*3ad3fb45Sjoris 			} else {
563*3ad3fb45Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
564*3ad3fb45Sjoris 					return (-1);
565*3ad3fb45Sjoris 				c[l] = ret;
566*3ad3fb45Sjoris 				k++;
567*3ad3fb45Sjoris 				break;
568*3ad3fb45Sjoris 			}
569*3ad3fb45Sjoris 		} while ((y = b[++j]) > 0 && numtries < bound);
570*3ad3fb45Sjoris 	}
571*3ad3fb45Sjoris 	return (k);
572*3ad3fb45Sjoris }
573*3ad3fb45Sjoris 
574*3ad3fb45Sjoris static int
575*3ad3fb45Sjoris newcand(int x, int y, int pred)
576*3ad3fb45Sjoris {
577*3ad3fb45Sjoris 	struct cand *q, *tmp;
578*3ad3fb45Sjoris 	int newclistlen;
579*3ad3fb45Sjoris 
580*3ad3fb45Sjoris 	if (clen == clistlen) {
581*3ad3fb45Sjoris 		newclistlen = clistlen * 11 / 10;
582*3ad3fb45Sjoris 		tmp = xrealloc(clist, newclistlen, sizeof(*clist));
583*3ad3fb45Sjoris 		clist = tmp;
584*3ad3fb45Sjoris 		clistlen = newclistlen;
585*3ad3fb45Sjoris 	}
586*3ad3fb45Sjoris 	q = clist + clen;
587*3ad3fb45Sjoris 	q->x = x;
588*3ad3fb45Sjoris 	q->y = y;
589*3ad3fb45Sjoris 	q->pred = pred;
590*3ad3fb45Sjoris 	return (clen++);
591*3ad3fb45Sjoris }
592*3ad3fb45Sjoris 
593*3ad3fb45Sjoris static int
594*3ad3fb45Sjoris search(int *c, int k, int y)
595*3ad3fb45Sjoris {
596*3ad3fb45Sjoris 	int i, j, l, t;
597*3ad3fb45Sjoris 
598*3ad3fb45Sjoris 	if (clist[c[k]].y < y)	/* quick look for typical case */
599*3ad3fb45Sjoris 		return (k + 1);
600*3ad3fb45Sjoris 	i = 0;
601*3ad3fb45Sjoris 	j = k + 1;
602*3ad3fb45Sjoris 	for (;;) {
603*3ad3fb45Sjoris 		l = (i + j) / 2;
604*3ad3fb45Sjoris 		if (l <= i)
605*3ad3fb45Sjoris 			break;
606*3ad3fb45Sjoris 		t = clist[c[l]].y;
607*3ad3fb45Sjoris 		if (t > y)
608*3ad3fb45Sjoris 			j = l;
609*3ad3fb45Sjoris 		else if (t < y)
610*3ad3fb45Sjoris 			i = l;
611*3ad3fb45Sjoris 		else
612*3ad3fb45Sjoris 			return (l);
613*3ad3fb45Sjoris 	}
614*3ad3fb45Sjoris 	return (l + 1);
615*3ad3fb45Sjoris }
616*3ad3fb45Sjoris 
617*3ad3fb45Sjoris static void
618*3ad3fb45Sjoris unravel(int p)
619*3ad3fb45Sjoris {
620*3ad3fb45Sjoris 	struct cand *q;
621*3ad3fb45Sjoris 	int i;
622*3ad3fb45Sjoris 
623*3ad3fb45Sjoris 	for (i = 0; i <= diff_len[0]; i++)
624*3ad3fb45Sjoris 		J[i] = i <= pref ? i :
625*3ad3fb45Sjoris 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
626*3ad3fb45Sjoris 	for (q = clist + p; q->y != 0; q = clist + q->pred)
627*3ad3fb45Sjoris 		J[q->x + pref] = q->y + pref;
628*3ad3fb45Sjoris }
629*3ad3fb45Sjoris 
630*3ad3fb45Sjoris /*
631*3ad3fb45Sjoris  * Check does double duty:
632*3ad3fb45Sjoris  *  1.	ferret out any fortuitous correspondences due
633*3ad3fb45Sjoris  *	to confounding by hashing (which result in "jackpot")
634*3ad3fb45Sjoris  *  2.  collect random access indexes to the two files
635*3ad3fb45Sjoris  */
636*3ad3fb45Sjoris static void
637*3ad3fb45Sjoris check(FILE *f1, FILE *f2)
638*3ad3fb45Sjoris {
639*3ad3fb45Sjoris 	int i, j, jackpot, c, d;
640*3ad3fb45Sjoris 	long ctold, ctnew;
641*3ad3fb45Sjoris 
642*3ad3fb45Sjoris 	rewind(f1);
643*3ad3fb45Sjoris 	rewind(f2);
644*3ad3fb45Sjoris 	j = 1;
645*3ad3fb45Sjoris 	ixold[0] = ixnew[0] = 0;
646*3ad3fb45Sjoris 	jackpot = 0;
647*3ad3fb45Sjoris 	ctold = ctnew = 0;
648*3ad3fb45Sjoris 	for (i = 1; i <= diff_len[0]; i++) {
649*3ad3fb45Sjoris 		if (J[i] == 0) {
650*3ad3fb45Sjoris 			ixold[i] = ctold += skipline(f1);
651*3ad3fb45Sjoris 			continue;
652*3ad3fb45Sjoris 		}
653*3ad3fb45Sjoris 		while (j < J[i]) {
654*3ad3fb45Sjoris 			ixnew[j] = ctnew += skipline(f2);
655*3ad3fb45Sjoris 			j++;
656*3ad3fb45Sjoris 		}
657*3ad3fb45Sjoris 		if (bflag == 1 || wflag == 1 || iflag == 1) {
658*3ad3fb45Sjoris 			for (;;) {
659*3ad3fb45Sjoris 				c = getc(f1);
660*3ad3fb45Sjoris 				d = getc(f2);
661*3ad3fb45Sjoris 				/*
662*3ad3fb45Sjoris 				 * GNU diff ignores a missing newline
663*3ad3fb45Sjoris 				 * in one file if bflag || wflag.
664*3ad3fb45Sjoris 				 */
665*3ad3fb45Sjoris 				if ((bflag == 1 || wflag == 1) &&
666*3ad3fb45Sjoris 				    ((c == EOF && d == '\n') ||
667*3ad3fb45Sjoris 				    (c == '\n' && d == EOF))) {
668*3ad3fb45Sjoris 					break;
669*3ad3fb45Sjoris 				}
670*3ad3fb45Sjoris 				ctold++;
671*3ad3fb45Sjoris 				ctnew++;
672*3ad3fb45Sjoris 				if (bflag == 1 && isspace(c) && isspace(d)) {
673*3ad3fb45Sjoris 					do {
674*3ad3fb45Sjoris 						if (c == '\n')
675*3ad3fb45Sjoris 							break;
676*3ad3fb45Sjoris 						ctold++;
677*3ad3fb45Sjoris 					} while (isspace(c = getc(f1)));
678*3ad3fb45Sjoris 					do {
679*3ad3fb45Sjoris 						if (d == '\n')
680*3ad3fb45Sjoris 							break;
681*3ad3fb45Sjoris 						ctnew++;
682*3ad3fb45Sjoris 					} while (isspace(d = getc(f2)));
683*3ad3fb45Sjoris 				} else if (wflag == 1) {
684*3ad3fb45Sjoris 					while (isspace(c) && c != '\n') {
685*3ad3fb45Sjoris 						c = getc(f1);
686*3ad3fb45Sjoris 						ctold++;
687*3ad3fb45Sjoris 					}
688*3ad3fb45Sjoris 					while (isspace(d) && d != '\n') {
689*3ad3fb45Sjoris 						d = getc(f2);
690*3ad3fb45Sjoris 						ctnew++;
691*3ad3fb45Sjoris 					}
692*3ad3fb45Sjoris 				}
693*3ad3fb45Sjoris 				if (chrtran[c] != chrtran[d]) {
694*3ad3fb45Sjoris 					jackpot++;
695*3ad3fb45Sjoris 					J[i] = 0;
696*3ad3fb45Sjoris 					if (c != '\n' && c != EOF)
697*3ad3fb45Sjoris 						ctold += skipline(f1);
698*3ad3fb45Sjoris 					if (d != '\n' && c != EOF)
699*3ad3fb45Sjoris 						ctnew += skipline(f2);
700*3ad3fb45Sjoris 					break;
701*3ad3fb45Sjoris 				}
702*3ad3fb45Sjoris 				if (c == '\n' || c == EOF)
703*3ad3fb45Sjoris 					break;
704*3ad3fb45Sjoris 			}
705*3ad3fb45Sjoris 		} else {
706*3ad3fb45Sjoris 			for (;;) {
707*3ad3fb45Sjoris 				ctold++;
708*3ad3fb45Sjoris 				ctnew++;
709*3ad3fb45Sjoris 				if ((c = getc(f1)) != (d = getc(f2))) {
710*3ad3fb45Sjoris 					/* jackpot++; */
711*3ad3fb45Sjoris 					J[i] = 0;
712*3ad3fb45Sjoris 					if (c != '\n' && c != EOF)
713*3ad3fb45Sjoris 						ctold += skipline(f1);
714*3ad3fb45Sjoris 					if (d != '\n' && c != EOF)
715*3ad3fb45Sjoris 						ctnew += skipline(f2);
716*3ad3fb45Sjoris 					break;
717*3ad3fb45Sjoris 				}
718*3ad3fb45Sjoris 				if (c == '\n' || c == EOF)
719*3ad3fb45Sjoris 					break;
720*3ad3fb45Sjoris 			}
721*3ad3fb45Sjoris 		}
722*3ad3fb45Sjoris 		ixold[i] = ctold;
723*3ad3fb45Sjoris 		ixnew[j] = ctnew;
724*3ad3fb45Sjoris 		j++;
725*3ad3fb45Sjoris 	}
726*3ad3fb45Sjoris 	for (; j <= diff_len[1]; j++)
727*3ad3fb45Sjoris 		ixnew[j] = ctnew += skipline(f2);
728*3ad3fb45Sjoris 	/*
729*3ad3fb45Sjoris 	 * if (jackpot != 0)
730*3ad3fb45Sjoris 	 *	cvs_printf("jackpot\n");
731*3ad3fb45Sjoris 	 */
732*3ad3fb45Sjoris }
733*3ad3fb45Sjoris 
734*3ad3fb45Sjoris /* shellsort CACM #201 */
735*3ad3fb45Sjoris static void
736*3ad3fb45Sjoris sort(struct line *a, int n)
737*3ad3fb45Sjoris {
738*3ad3fb45Sjoris 	struct line *ai, *aim, w;
739*3ad3fb45Sjoris 	int j, m = 0, k;
740*3ad3fb45Sjoris 
741*3ad3fb45Sjoris 	if (n == 0)
742*3ad3fb45Sjoris 		return;
743*3ad3fb45Sjoris 	for (j = 1; j <= n; j *= 2)
744*3ad3fb45Sjoris 		m = 2 * j - 1;
745*3ad3fb45Sjoris 	for (m /= 2; m != 0; m /= 2) {
746*3ad3fb45Sjoris 		k = n - m;
747*3ad3fb45Sjoris 		for (j = 1; j <= k; j++) {
748*3ad3fb45Sjoris 			for (ai = &a[j]; ai > a; ai -= m) {
749*3ad3fb45Sjoris 				aim = &ai[m];
750*3ad3fb45Sjoris 				if (aim < ai)
751*3ad3fb45Sjoris 					break;	/* wraparound */
752*3ad3fb45Sjoris 				if (aim->value > ai[0].value ||
753*3ad3fb45Sjoris 				    (aim->value == ai[0].value &&
754*3ad3fb45Sjoris 					aim->serial > ai[0].serial))
755*3ad3fb45Sjoris 					break;
756*3ad3fb45Sjoris 				w.value = ai[0].value;
757*3ad3fb45Sjoris 				ai[0].value = aim->value;
758*3ad3fb45Sjoris 				aim->value = w.value;
759*3ad3fb45Sjoris 				w.serial = ai[0].serial;
760*3ad3fb45Sjoris 				ai[0].serial = aim->serial;
761*3ad3fb45Sjoris 				aim->serial = w.serial;
762*3ad3fb45Sjoris 			}
763*3ad3fb45Sjoris 		}
764*3ad3fb45Sjoris 	}
765*3ad3fb45Sjoris }
766*3ad3fb45Sjoris 
767*3ad3fb45Sjoris static void
768*3ad3fb45Sjoris unsort(struct line *f, int l, int *b)
769*3ad3fb45Sjoris {
770*3ad3fb45Sjoris 	int *a, i;
771*3ad3fb45Sjoris 
772*3ad3fb45Sjoris 	a = xcalloc(l + 1, sizeof(*a));
773*3ad3fb45Sjoris 	for (i = 1; i <= l; i++)
774*3ad3fb45Sjoris 		a[f[i].serial] = f[i].value;
775*3ad3fb45Sjoris 	for (i = 1; i <= l; i++)
776*3ad3fb45Sjoris 		b[i] = a[i];
777*3ad3fb45Sjoris 	xfree(a);
778*3ad3fb45Sjoris }
779*3ad3fb45Sjoris 
780*3ad3fb45Sjoris static int
781*3ad3fb45Sjoris skipline(FILE *f)
782*3ad3fb45Sjoris {
783*3ad3fb45Sjoris 	int i, c;
784*3ad3fb45Sjoris 
785*3ad3fb45Sjoris 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
786*3ad3fb45Sjoris 		continue;
787*3ad3fb45Sjoris 	return (i);
788*3ad3fb45Sjoris }
789*3ad3fb45Sjoris 
790*3ad3fb45Sjoris static void
791*3ad3fb45Sjoris output(FILE *f1, FILE *f2)
792*3ad3fb45Sjoris {
793*3ad3fb45Sjoris 	int m, i0, i1, j0, j1;
794*3ad3fb45Sjoris 
795*3ad3fb45Sjoris 	rewind(f1);
796*3ad3fb45Sjoris 	rewind(f2);
797*3ad3fb45Sjoris 	m = diff_len[0];
798*3ad3fb45Sjoris 	J[0] = 0;
799*3ad3fb45Sjoris 	J[m + 1] = diff_len[1] + 1;
800*3ad3fb45Sjoris 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
801*3ad3fb45Sjoris 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
802*3ad3fb45Sjoris 			i0++;
803*3ad3fb45Sjoris 		j0 = J[i0 - 1] + 1;
804*3ad3fb45Sjoris 		i1 = i0 - 1;
805*3ad3fb45Sjoris 		while (i1 < m && J[i1 + 1] == 0)
806*3ad3fb45Sjoris 			i1++;
807*3ad3fb45Sjoris 		j1 = J[i1 + 1] - 1;
808*3ad3fb45Sjoris 		J[i1] = j1;
809*3ad3fb45Sjoris 		change(f1, f2, i0, i1, j0, j1);
810*3ad3fb45Sjoris 	}
811*3ad3fb45Sjoris 	if (m == 0)
812*3ad3fb45Sjoris 		change(f1, f2, 1, 0, 1, diff_len[1]);
813*3ad3fb45Sjoris 	if (diff_format == D_IFDEF) {
814*3ad3fb45Sjoris 		for (;;) {
815*3ad3fb45Sjoris #define	c i0
816*3ad3fb45Sjoris 			if ((c = getc(f1)) == EOF)
817*3ad3fb45Sjoris 				return;
818*3ad3fb45Sjoris 			diff_output("%c", c);
819*3ad3fb45Sjoris 		}
820*3ad3fb45Sjoris #undef c
821*3ad3fb45Sjoris 	}
822*3ad3fb45Sjoris 	if (anychange != 0) {
823*3ad3fb45Sjoris 		if (diff_format == D_CONTEXT)
824*3ad3fb45Sjoris 			dump_context_vec(f1, f2);
825*3ad3fb45Sjoris 		else if (diff_format == D_UNIFIED)
826*3ad3fb45Sjoris 			dump_unified_vec(f1, f2);
827*3ad3fb45Sjoris 	}
828*3ad3fb45Sjoris }
829*3ad3fb45Sjoris 
830*3ad3fb45Sjoris static __inline void
831*3ad3fb45Sjoris range(int a, int b, char *separator)
832*3ad3fb45Sjoris {
833*3ad3fb45Sjoris 	diff_output("%d", a > b ? b : a);
834*3ad3fb45Sjoris 	if (a < b)
835*3ad3fb45Sjoris 		diff_output("%s%d", separator, b);
836*3ad3fb45Sjoris }
837*3ad3fb45Sjoris 
838*3ad3fb45Sjoris static __inline void
839*3ad3fb45Sjoris uni_range(int a, int b)
840*3ad3fb45Sjoris {
841*3ad3fb45Sjoris 	if (a < b)
842*3ad3fb45Sjoris 		diff_output("%d,%d", a, b - a + 1);
843*3ad3fb45Sjoris 	else if (a == b)
844*3ad3fb45Sjoris 		diff_output("%d", b);
845*3ad3fb45Sjoris 	else
846*3ad3fb45Sjoris 		diff_output("%d,0", b);
847*3ad3fb45Sjoris }
848*3ad3fb45Sjoris 
849*3ad3fb45Sjoris static char *
850*3ad3fb45Sjoris preadline(int fd, size_t rlen, off_t off)
851*3ad3fb45Sjoris {
852*3ad3fb45Sjoris 	char *line;
853*3ad3fb45Sjoris 	ssize_t nr;
854*3ad3fb45Sjoris 
855*3ad3fb45Sjoris 	line = xmalloc(rlen + 1);
856*3ad3fb45Sjoris 	if ((nr = pread(fd, line, rlen, off)) < 0) {
857*3ad3fb45Sjoris 		cvs_log(LP_ERR, "preadline failed");
858*3ad3fb45Sjoris 		return (NULL);
859*3ad3fb45Sjoris 	}
860*3ad3fb45Sjoris 	line[nr] = '\0';
861*3ad3fb45Sjoris 	return (line);
862*3ad3fb45Sjoris }
863*3ad3fb45Sjoris 
864*3ad3fb45Sjoris static int
865*3ad3fb45Sjoris ignoreline(char *line)
866*3ad3fb45Sjoris {
867*3ad3fb45Sjoris 	int ret;
868*3ad3fb45Sjoris 
869*3ad3fb45Sjoris 	ret = regexec(&ignore_re, line, (size_t)0, NULL, 0);
870*3ad3fb45Sjoris 	xfree(line);
871*3ad3fb45Sjoris 	return (ret == 0);	/* if it matched, it should be ignored. */
872*3ad3fb45Sjoris }
873*3ad3fb45Sjoris 
874*3ad3fb45Sjoris /*
875*3ad3fb45Sjoris  * Indicate that there is a difference between lines a and b of the from file
876*3ad3fb45Sjoris  * to get to lines c to d of the to file.  If a is greater then b then there
877*3ad3fb45Sjoris  * are no lines in the from file involved and this means that there were
878*3ad3fb45Sjoris  * lines appended (beginning at b).  If c is greater than d then there are
879*3ad3fb45Sjoris  * lines missing from the to file.
880*3ad3fb45Sjoris  */
881*3ad3fb45Sjoris static void
882*3ad3fb45Sjoris change(FILE *f1, FILE *f2, int a, int b, int c, int d)
883*3ad3fb45Sjoris {
884*3ad3fb45Sjoris 	int i;
885*3ad3fb45Sjoris 	static size_t max_context = 64;
886*3ad3fb45Sjoris 	char buf[64];
887*3ad3fb45Sjoris 	struct tm *t;
888*3ad3fb45Sjoris 
889*3ad3fb45Sjoris 	if (diff_format != D_IFDEF && a > b && c > d)
890*3ad3fb45Sjoris 		return;
891*3ad3fb45Sjoris 	if (ignore_pats != NULL) {
892*3ad3fb45Sjoris 		char *line;
893*3ad3fb45Sjoris 		/*
894*3ad3fb45Sjoris 		 * All lines in the change, insert, or delete must
895*3ad3fb45Sjoris 		 * match an ignore pattern for the change to be
896*3ad3fb45Sjoris 		 * ignored.
897*3ad3fb45Sjoris 		 */
898*3ad3fb45Sjoris 		if (a <= b) {		/* Changes and deletes. */
899*3ad3fb45Sjoris 			for (i = a; i <= b; i++) {
900*3ad3fb45Sjoris 				line = preadline(fileno(f1),
901*3ad3fb45Sjoris 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
902*3ad3fb45Sjoris 				if (!ignoreline(line))
903*3ad3fb45Sjoris 					goto proceed;
904*3ad3fb45Sjoris 			}
905*3ad3fb45Sjoris 		}
906*3ad3fb45Sjoris 		if (a > b || c <= d) {	/* Changes and inserts. */
907*3ad3fb45Sjoris 			for (i = c; i <= d; i++) {
908*3ad3fb45Sjoris 				line = preadline(fileno(f2),
909*3ad3fb45Sjoris 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
910*3ad3fb45Sjoris 				if (!ignoreline(line))
911*3ad3fb45Sjoris 					goto proceed;
912*3ad3fb45Sjoris 			}
913*3ad3fb45Sjoris 		}
914*3ad3fb45Sjoris 		return;
915*3ad3fb45Sjoris 	}
916*3ad3fb45Sjoris proceed:
917*3ad3fb45Sjoris 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
918*3ad3fb45Sjoris 		/*
919*3ad3fb45Sjoris 		 * Allocate change records as needed.
920*3ad3fb45Sjoris 		 */
921*3ad3fb45Sjoris 		if (context_vec_ptr == context_vec_end - 1) {
922*3ad3fb45Sjoris 			struct context_vec *tmp;
923*3ad3fb45Sjoris 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
924*3ad3fb45Sjoris 			max_context <<= 1;
925*3ad3fb45Sjoris 			tmp = xrealloc(context_vec_start, max_context,
926*3ad3fb45Sjoris 			    sizeof(*context_vec_start));
927*3ad3fb45Sjoris 			context_vec_start = tmp;
928*3ad3fb45Sjoris 			context_vec_end = context_vec_start + max_context;
929*3ad3fb45Sjoris 			context_vec_ptr = context_vec_start + offset;
930*3ad3fb45Sjoris 		}
931*3ad3fb45Sjoris 		if (anychange == 0) {
932*3ad3fb45Sjoris 			/*
933*3ad3fb45Sjoris 			 * Print the context/unidiff header first time through.
934*3ad3fb45Sjoris 			 */
935*3ad3fb45Sjoris 			t = localtime(&stb1.st_mtime);
936*3ad3fb45Sjoris 			(void)strftime(buf, sizeof(buf),
937*3ad3fb45Sjoris 			    "%d %b %G %H:%M:%S", t);
938*3ad3fb45Sjoris 
939*3ad3fb45Sjoris 			diff_output("%s %s	%s",
940*3ad3fb45Sjoris 			    diff_format == D_CONTEXT ? "***" : "---", diff_file,
941*3ad3fb45Sjoris 			    buf);
942*3ad3fb45Sjoris 
943*3ad3fb45Sjoris 			if (diff_rev1 != NULL) {
944*3ad3fb45Sjoris 				rcsnum_tostr(diff_rev1, buf, sizeof(buf));
945*3ad3fb45Sjoris 				diff_output("\t%s", buf);
946*3ad3fb45Sjoris 			}
947*3ad3fb45Sjoris 
948*3ad3fb45Sjoris 			printf("\n");
949*3ad3fb45Sjoris 
950*3ad3fb45Sjoris 			t = localtime(&stb2.st_mtime);
951*3ad3fb45Sjoris 			(void)strftime(buf, sizeof(buf),
952*3ad3fb45Sjoris 			    "%d %b %G %H:%M:%S", t);
953*3ad3fb45Sjoris 
954*3ad3fb45Sjoris 			diff_output("%s %s	%s",
955*3ad3fb45Sjoris 			    diff_format == D_CONTEXT ? "---" : "+++", diff_file,
956*3ad3fb45Sjoris 			    buf);
957*3ad3fb45Sjoris 
958*3ad3fb45Sjoris 			if (diff_rev2 != NULL) {
959*3ad3fb45Sjoris 				rcsnum_tostr(diff_rev2, buf, sizeof(buf));
960*3ad3fb45Sjoris 				diff_output("\t%s", buf);
961*3ad3fb45Sjoris 			}
962*3ad3fb45Sjoris 
963*3ad3fb45Sjoris 			printf("\n");
964*3ad3fb45Sjoris 			anychange = 1;
965*3ad3fb45Sjoris 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
966*3ad3fb45Sjoris 		    c > context_vec_ptr->d + (2 * context) + 1) {
967*3ad3fb45Sjoris 			/*
968*3ad3fb45Sjoris 			 * If this change is more than 'context' lines from the
969*3ad3fb45Sjoris 			 * previous change, dump the record and reset it.
970*3ad3fb45Sjoris 			 */
971*3ad3fb45Sjoris 			if (diff_format == D_CONTEXT)
972*3ad3fb45Sjoris 				dump_context_vec(f1, f2);
973*3ad3fb45Sjoris 			else
974*3ad3fb45Sjoris 				dump_unified_vec(f1, f2);
975*3ad3fb45Sjoris 		}
976*3ad3fb45Sjoris 		context_vec_ptr++;
977*3ad3fb45Sjoris 		context_vec_ptr->a = a;
978*3ad3fb45Sjoris 		context_vec_ptr->b = b;
979*3ad3fb45Sjoris 		context_vec_ptr->c = c;
980*3ad3fb45Sjoris 		context_vec_ptr->d = d;
981*3ad3fb45Sjoris 		return;
982*3ad3fb45Sjoris 	}
983*3ad3fb45Sjoris 	if (anychange == 0)
984*3ad3fb45Sjoris 		anychange = 1;
985*3ad3fb45Sjoris 	switch (diff_format) {
986*3ad3fb45Sjoris 	case D_BRIEF:
987*3ad3fb45Sjoris 		return;
988*3ad3fb45Sjoris 	case D_NORMAL:
989*3ad3fb45Sjoris 		range(a, b, ",");
990*3ad3fb45Sjoris 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
991*3ad3fb45Sjoris 		if (diff_format == D_NORMAL)
992*3ad3fb45Sjoris 			range(c, d, ",");
993*3ad3fb45Sjoris 		diff_output("\n");
994*3ad3fb45Sjoris 		break;
995*3ad3fb45Sjoris 	case D_RCSDIFF:
996*3ad3fb45Sjoris 		if (a > b)
997*3ad3fb45Sjoris 			diff_output("a%d %d\n", b, d - c + 1);
998*3ad3fb45Sjoris 		else {
999*3ad3fb45Sjoris 			diff_output("d%d %d\n", a, b - a + 1);
1000*3ad3fb45Sjoris 
1001*3ad3fb45Sjoris 			if (!(c > d))	/* add changed lines */
1002*3ad3fb45Sjoris 				diff_output("a%d %d\n", b, d - c + 1);
1003*3ad3fb45Sjoris 		}
1004*3ad3fb45Sjoris 		break;
1005*3ad3fb45Sjoris 	}
1006*3ad3fb45Sjoris 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
1007*3ad3fb45Sjoris 		fetch(ixold, a, b, f1, '<', 1);
1008*3ad3fb45Sjoris 		if (a <= b && c <= d && diff_format == D_NORMAL)
1009*3ad3fb45Sjoris 			diff_output("---\n");
1010*3ad3fb45Sjoris 	}
1011*3ad3fb45Sjoris 	fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0);
1012*3ad3fb45Sjoris 	if (inifdef) {
1013*3ad3fb45Sjoris 		diff_output("#endif /* %s */\n", ifdefname);
1014*3ad3fb45Sjoris 		inifdef = 0;
1015*3ad3fb45Sjoris 	}
1016*3ad3fb45Sjoris }
1017*3ad3fb45Sjoris 
1018*3ad3fb45Sjoris static void
1019*3ad3fb45Sjoris fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
1020*3ad3fb45Sjoris {
1021*3ad3fb45Sjoris 	long j, nc;
1022*3ad3fb45Sjoris 	int i, c, col;
1023*3ad3fb45Sjoris 
1024*3ad3fb45Sjoris 	/*
1025*3ad3fb45Sjoris 	 * When doing #ifdef's, copy down to current line
1026*3ad3fb45Sjoris 	 * if this is the first file, so that stuff makes it to output.
1027*3ad3fb45Sjoris 	 */
1028*3ad3fb45Sjoris 	if (diff_format == D_IFDEF && oldfile) {
1029*3ad3fb45Sjoris 		long curpos = ftell(lb);
1030*3ad3fb45Sjoris 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
1031*3ad3fb45Sjoris 		nc = f[a > b ? b : a - 1] - curpos;
1032*3ad3fb45Sjoris 		for (i = 0; i < nc; i++)
1033*3ad3fb45Sjoris 			diff_output("%c", getc(lb));
1034*3ad3fb45Sjoris 	}
1035*3ad3fb45Sjoris 	if (a > b)
1036*3ad3fb45Sjoris 		return;
1037*3ad3fb45Sjoris 	if (diff_format == D_IFDEF) {
1038*3ad3fb45Sjoris 		if (inifdef) {
1039*3ad3fb45Sjoris 			diff_output("#else /* %s%s */\n",
1040*3ad3fb45Sjoris 			    oldfile == 1 ? "!" : "", ifdefname);
1041*3ad3fb45Sjoris 		} else {
1042*3ad3fb45Sjoris 			if (oldfile)
1043*3ad3fb45Sjoris 				diff_output("#ifndef %s\n", ifdefname);
1044*3ad3fb45Sjoris 			else
1045*3ad3fb45Sjoris 				diff_output("#ifdef %s\n", ifdefname);
1046*3ad3fb45Sjoris 		}
1047*3ad3fb45Sjoris 		inifdef = 1 + oldfile;
1048*3ad3fb45Sjoris 	}
1049*3ad3fb45Sjoris 	for (i = a; i <= b; i++) {
1050*3ad3fb45Sjoris 		fseek(lb, f[i - 1], SEEK_SET);
1051*3ad3fb45Sjoris 		nc = f[i] - f[i - 1];
1052*3ad3fb45Sjoris 		if (diff_format != D_IFDEF && ch != '\0') {
1053*3ad3fb45Sjoris 			diff_output("%c", ch);
1054*3ad3fb45Sjoris 			if (Tflag == 1 && (diff_format == D_NORMAL ||
1055*3ad3fb45Sjoris 			    diff_format == D_CONTEXT ||
1056*3ad3fb45Sjoris 			    diff_format == D_UNIFIED))
1057*3ad3fb45Sjoris 				diff_output("\t");
1058*3ad3fb45Sjoris 			else if (diff_format != D_UNIFIED)
1059*3ad3fb45Sjoris 				diff_output(" ");
1060*3ad3fb45Sjoris 		}
1061*3ad3fb45Sjoris 		col = 0;
1062*3ad3fb45Sjoris 		for (j = 0; j < nc; j++) {
1063*3ad3fb45Sjoris 			if ((c = getc(lb)) == EOF) {
1064*3ad3fb45Sjoris 				if (diff_format == D_RCSDIFF)
1065*3ad3fb45Sjoris 					cvs_log(LP_ERR,
1066*3ad3fb45Sjoris 					    "No newline at end of file");
1067*3ad3fb45Sjoris 				else
1068*3ad3fb45Sjoris 					diff_output("\n\\ No newline at end of "
1069*3ad3fb45Sjoris 					    "file");
1070*3ad3fb45Sjoris 				return;
1071*3ad3fb45Sjoris 			}
1072*3ad3fb45Sjoris 			if (c == '\t' && tflag == 1) {
1073*3ad3fb45Sjoris 				do {
1074*3ad3fb45Sjoris 					diff_output(" ");
1075*3ad3fb45Sjoris 				} while (++col & 7);
1076*3ad3fb45Sjoris 			} else {
1077*3ad3fb45Sjoris 				diff_output("%c", c);
1078*3ad3fb45Sjoris 				col++;
1079*3ad3fb45Sjoris 			}
1080*3ad3fb45Sjoris 		}
1081*3ad3fb45Sjoris 	}
1082*3ad3fb45Sjoris }
1083*3ad3fb45Sjoris 
1084*3ad3fb45Sjoris /*
1085*3ad3fb45Sjoris  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
1086*3ad3fb45Sjoris  */
1087*3ad3fb45Sjoris static int
1088*3ad3fb45Sjoris readhash(FILE *f)
1089*3ad3fb45Sjoris {
1090*3ad3fb45Sjoris 	int i, t, space;
1091*3ad3fb45Sjoris 	int sum;
1092*3ad3fb45Sjoris 
1093*3ad3fb45Sjoris 	sum = 1;
1094*3ad3fb45Sjoris 	space = 0;
1095*3ad3fb45Sjoris 	if (bflag != 1 && wflag != 1) {
1096*3ad3fb45Sjoris 		if (iflag == 1)
1097*3ad3fb45Sjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
1098*3ad3fb45Sjoris 				if (t == EOF) {
1099*3ad3fb45Sjoris 					if (i == 0)
1100*3ad3fb45Sjoris 						return (0);
1101*3ad3fb45Sjoris 					break;
1102*3ad3fb45Sjoris 				}
1103*3ad3fb45Sjoris 				sum = sum * 127 + chrtran[t];
1104*3ad3fb45Sjoris 			}
1105*3ad3fb45Sjoris 		else
1106*3ad3fb45Sjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
1107*3ad3fb45Sjoris 				if (t == EOF) {
1108*3ad3fb45Sjoris 					if (i == 0)
1109*3ad3fb45Sjoris 						return (0);
1110*3ad3fb45Sjoris 					break;
1111*3ad3fb45Sjoris 				}
1112*3ad3fb45Sjoris 				sum = sum * 127 + t;
1113*3ad3fb45Sjoris 			}
1114*3ad3fb45Sjoris 	} else {
1115*3ad3fb45Sjoris 		for (i = 0;;) {
1116*3ad3fb45Sjoris 			switch (t = getc(f)) {
1117*3ad3fb45Sjoris 			case '\t':
1118*3ad3fb45Sjoris 			case ' ':
1119*3ad3fb45Sjoris 				space++;
1120*3ad3fb45Sjoris 				continue;
1121*3ad3fb45Sjoris 			default:
1122*3ad3fb45Sjoris 				if (space != 0 && wflag != 1) {
1123*3ad3fb45Sjoris 					i++;
1124*3ad3fb45Sjoris 					space = 0;
1125*3ad3fb45Sjoris 				}
1126*3ad3fb45Sjoris 				sum = sum * 127 + chrtran[t];
1127*3ad3fb45Sjoris 				i++;
1128*3ad3fb45Sjoris 				continue;
1129*3ad3fb45Sjoris 			case EOF:
1130*3ad3fb45Sjoris 				if (i == 0)
1131*3ad3fb45Sjoris 					return (0);
1132*3ad3fb45Sjoris 				/* FALLTHROUGH */
1133*3ad3fb45Sjoris 			case '\n':
1134*3ad3fb45Sjoris 				break;
1135*3ad3fb45Sjoris 			}
1136*3ad3fb45Sjoris 			break;
1137*3ad3fb45Sjoris 		}
1138*3ad3fb45Sjoris 	}
1139*3ad3fb45Sjoris 	/*
1140*3ad3fb45Sjoris 	 * There is a remote possibility that we end up with a zero sum.
1141*3ad3fb45Sjoris 	 * Zero is used as an EOF marker, so return 1 instead.
1142*3ad3fb45Sjoris 	 */
1143*3ad3fb45Sjoris 	return (sum == 0 ? 1 : sum);
1144*3ad3fb45Sjoris }
1145*3ad3fb45Sjoris 
1146*3ad3fb45Sjoris static int
1147*3ad3fb45Sjoris asciifile(FILE *f)
1148*3ad3fb45Sjoris {
1149*3ad3fb45Sjoris 	char buf[BUFSIZ];
1150*3ad3fb45Sjoris 	size_t i, cnt;
1151*3ad3fb45Sjoris 
1152*3ad3fb45Sjoris 	if (aflag == 1 || f == NULL)
1153*3ad3fb45Sjoris 		return (1);
1154*3ad3fb45Sjoris 
1155*3ad3fb45Sjoris 	rewind(f);
1156*3ad3fb45Sjoris 	cnt = fread(buf, (size_t)1, sizeof(buf), f);
1157*3ad3fb45Sjoris 	for (i = 0; i < cnt; i++)
1158*3ad3fb45Sjoris 		if (!isprint(buf[i]) && !isspace(buf[i]))
1159*3ad3fb45Sjoris 			return (0);
1160*3ad3fb45Sjoris 	return (1);
1161*3ad3fb45Sjoris }
1162*3ad3fb45Sjoris 
1163*3ad3fb45Sjoris static char*
1164*3ad3fb45Sjoris match_function(const long *f, int pos, FILE *fp)
1165*3ad3fb45Sjoris {
1166*3ad3fb45Sjoris 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
1167*3ad3fb45Sjoris 	size_t nc;
1168*3ad3fb45Sjoris 	int last = lastline;
1169*3ad3fb45Sjoris 	char *p;
1170*3ad3fb45Sjoris 
1171*3ad3fb45Sjoris 	lastline = pos;
1172*3ad3fb45Sjoris 	while (pos > last) {
1173*3ad3fb45Sjoris 		fseek(fp, f[pos - 1], SEEK_SET);
1174*3ad3fb45Sjoris 		nc = f[pos] - f[pos - 1];
1175*3ad3fb45Sjoris 		if (nc >= sizeof(buf))
1176*3ad3fb45Sjoris 			nc = sizeof(buf) - 1;
1177*3ad3fb45Sjoris 		nc = fread(buf, (size_t)1, nc, fp);
1178*3ad3fb45Sjoris 		if (nc > 0) {
1179*3ad3fb45Sjoris 			buf[nc] = '\0';
1180*3ad3fb45Sjoris 			p = strchr((const char *)buf, '\n');
1181*3ad3fb45Sjoris 			if (p != NULL)
1182*3ad3fb45Sjoris 				*p = '\0';
1183*3ad3fb45Sjoris 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
1184*3ad3fb45Sjoris 				strlcpy(lastbuf, (const char *)buf,
1185*3ad3fb45Sjoris 				    sizeof lastbuf);
1186*3ad3fb45Sjoris 				lastmatchline = pos;
1187*3ad3fb45Sjoris 				return lastbuf;
1188*3ad3fb45Sjoris 			}
1189*3ad3fb45Sjoris 		}
1190*3ad3fb45Sjoris 		pos--;
1191*3ad3fb45Sjoris 	}
1192*3ad3fb45Sjoris 	return (lastmatchline > 0) ? lastbuf : NULL;
1193*3ad3fb45Sjoris }
1194*3ad3fb45Sjoris 
1195*3ad3fb45Sjoris 
1196*3ad3fb45Sjoris /* dump accumulated "context" diff changes */
1197*3ad3fb45Sjoris static void
1198*3ad3fb45Sjoris dump_context_vec(FILE *f1, FILE *f2)
1199*3ad3fb45Sjoris {
1200*3ad3fb45Sjoris 	struct context_vec *cvp = context_vec_start;
1201*3ad3fb45Sjoris 	int lowa, upb, lowc, upd, do_output;
1202*3ad3fb45Sjoris 	int a, b, c, d;
1203*3ad3fb45Sjoris 	char ch, *f;
1204*3ad3fb45Sjoris 
1205*3ad3fb45Sjoris 	if (context_vec_start > context_vec_ptr)
1206*3ad3fb45Sjoris 		return;
1207*3ad3fb45Sjoris 
1208*3ad3fb45Sjoris 	b = d = 0;		/* gcc */
1209*3ad3fb45Sjoris 	lowa = MAX(1, cvp->a - context);
1210*3ad3fb45Sjoris 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1211*3ad3fb45Sjoris 	lowc = MAX(1, cvp->c - context);
1212*3ad3fb45Sjoris 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
1213*3ad3fb45Sjoris 
1214*3ad3fb45Sjoris 	diff_output("***************");
1215*3ad3fb45Sjoris 	if (pflag == 1) {
1216*3ad3fb45Sjoris 		f = match_function(ixold, lowa - 1, f1);
1217*3ad3fb45Sjoris 		if (f != NULL) {
1218*3ad3fb45Sjoris 			diff_output(" ");
1219*3ad3fb45Sjoris 			diff_output("%s", f);
1220*3ad3fb45Sjoris 		}
1221*3ad3fb45Sjoris 	}
1222*3ad3fb45Sjoris 	diff_output("\n*** ");
1223*3ad3fb45Sjoris 	range(lowa, upb, ",");
1224*3ad3fb45Sjoris 	diff_output(" ****\n");
1225*3ad3fb45Sjoris 
1226*3ad3fb45Sjoris 	/*
1227*3ad3fb45Sjoris 	 * Output changes to the "old" file.  The first loop suppresses
1228*3ad3fb45Sjoris 	 * output if there were no changes to the "old" file (we'll see
1229*3ad3fb45Sjoris 	 * the "old" lines as context in the "new" list).
1230*3ad3fb45Sjoris 	 */
1231*3ad3fb45Sjoris 	do_output = 0;
1232*3ad3fb45Sjoris 	for (; cvp <= context_vec_ptr; cvp++)
1233*3ad3fb45Sjoris 		if (cvp->a <= cvp->b) {
1234*3ad3fb45Sjoris 			cvp = context_vec_start;
1235*3ad3fb45Sjoris 			do_output++;
1236*3ad3fb45Sjoris 			break;
1237*3ad3fb45Sjoris 		}
1238*3ad3fb45Sjoris 	if (do_output != 0) {
1239*3ad3fb45Sjoris 		while (cvp <= context_vec_ptr) {
1240*3ad3fb45Sjoris 			a = cvp->a;
1241*3ad3fb45Sjoris 			b = cvp->b;
1242*3ad3fb45Sjoris 			c = cvp->c;
1243*3ad3fb45Sjoris 			d = cvp->d;
1244*3ad3fb45Sjoris 
1245*3ad3fb45Sjoris 			if (a <= b && c <= d)
1246*3ad3fb45Sjoris 				ch = 'c';
1247*3ad3fb45Sjoris 			else
1248*3ad3fb45Sjoris 				ch = (a <= b) ? 'd' : 'a';
1249*3ad3fb45Sjoris 
1250*3ad3fb45Sjoris 			if (ch == 'a')
1251*3ad3fb45Sjoris 				fetch(ixold, lowa, b, f1, ' ', 0);
1252*3ad3fb45Sjoris 			else {
1253*3ad3fb45Sjoris 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
1254*3ad3fb45Sjoris 				fetch(ixold, a, b, f1,
1255*3ad3fb45Sjoris 				    ch == 'c' ? '!' : '-', 0);
1256*3ad3fb45Sjoris 			}
1257*3ad3fb45Sjoris 			lowa = b + 1;
1258*3ad3fb45Sjoris 			cvp++;
1259*3ad3fb45Sjoris 		}
1260*3ad3fb45Sjoris 		fetch(ixold, b + 1, upb, f1, ' ', 0);
1261*3ad3fb45Sjoris 	}
1262*3ad3fb45Sjoris 	/* output changes to the "new" file */
1263*3ad3fb45Sjoris 	diff_output("--- ");
1264*3ad3fb45Sjoris 	range(lowc, upd, ",");
1265*3ad3fb45Sjoris 	diff_output(" ----\n");
1266*3ad3fb45Sjoris 
1267*3ad3fb45Sjoris 	do_output = 0;
1268*3ad3fb45Sjoris 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
1269*3ad3fb45Sjoris 		if (cvp->c <= cvp->d) {
1270*3ad3fb45Sjoris 			cvp = context_vec_start;
1271*3ad3fb45Sjoris 			do_output++;
1272*3ad3fb45Sjoris 			break;
1273*3ad3fb45Sjoris 		}
1274*3ad3fb45Sjoris 	if (do_output != 0) {
1275*3ad3fb45Sjoris 		while (cvp <= context_vec_ptr) {
1276*3ad3fb45Sjoris 			a = cvp->a;
1277*3ad3fb45Sjoris 			b = cvp->b;
1278*3ad3fb45Sjoris 			c = cvp->c;
1279*3ad3fb45Sjoris 			d = cvp->d;
1280*3ad3fb45Sjoris 
1281*3ad3fb45Sjoris 			if (a <= b && c <= d)
1282*3ad3fb45Sjoris 				ch = 'c';
1283*3ad3fb45Sjoris 			else
1284*3ad3fb45Sjoris 				ch = (a <= b) ? 'd' : 'a';
1285*3ad3fb45Sjoris 
1286*3ad3fb45Sjoris 			if (ch == 'd')
1287*3ad3fb45Sjoris 				fetch(ixnew, lowc, d, f2, ' ', 0);
1288*3ad3fb45Sjoris 			else {
1289*3ad3fb45Sjoris 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
1290*3ad3fb45Sjoris 				fetch(ixnew, c, d, f2,
1291*3ad3fb45Sjoris 				    ch == 'c' ? '!' : '+', 0);
1292*3ad3fb45Sjoris 			}
1293*3ad3fb45Sjoris 			lowc = d + 1;
1294*3ad3fb45Sjoris 			cvp++;
1295*3ad3fb45Sjoris 		}
1296*3ad3fb45Sjoris 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
1297*3ad3fb45Sjoris 	}
1298*3ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
1299*3ad3fb45Sjoris }
1300*3ad3fb45Sjoris 
1301*3ad3fb45Sjoris /* dump accumulated "unified" diff changes */
1302*3ad3fb45Sjoris static void
1303*3ad3fb45Sjoris dump_unified_vec(FILE *f1, FILE *f2)
1304*3ad3fb45Sjoris {
1305*3ad3fb45Sjoris 	struct context_vec *cvp = context_vec_start;
1306*3ad3fb45Sjoris 	int lowa, upb, lowc, upd;
1307*3ad3fb45Sjoris 	int a, b, c, d;
1308*3ad3fb45Sjoris 	char ch, *f;
1309*3ad3fb45Sjoris 
1310*3ad3fb45Sjoris 	if (context_vec_start > context_vec_ptr)
1311*3ad3fb45Sjoris 		return;
1312*3ad3fb45Sjoris 
1313*3ad3fb45Sjoris 	b = d = 0;		/* gcc */
1314*3ad3fb45Sjoris 	lowa = MAX(1, cvp->a - context);
1315*3ad3fb45Sjoris 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1316*3ad3fb45Sjoris 	lowc = MAX(1, cvp->c - context);
1317*3ad3fb45Sjoris 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
1318*3ad3fb45Sjoris 
1319*3ad3fb45Sjoris 	diff_output("@@ -");
1320*3ad3fb45Sjoris 	uni_range(lowa, upb);
1321*3ad3fb45Sjoris 	diff_output(" +");
1322*3ad3fb45Sjoris 	uni_range(lowc, upd);
1323*3ad3fb45Sjoris 	diff_output(" @@");
1324*3ad3fb45Sjoris 	if (pflag == 1) {
1325*3ad3fb45Sjoris 		f = match_function(ixold, lowa - 1, f1);
1326*3ad3fb45Sjoris 		if (f != NULL) {
1327*3ad3fb45Sjoris 			diff_output(" ");
1328*3ad3fb45Sjoris 			diff_output("%s", f);
1329*3ad3fb45Sjoris 		}
1330*3ad3fb45Sjoris 	}
1331*3ad3fb45Sjoris 	diff_output("\n");
1332*3ad3fb45Sjoris 
1333*3ad3fb45Sjoris 	/*
1334*3ad3fb45Sjoris 	 * Output changes in "unified" diff format--the old and new lines
1335*3ad3fb45Sjoris 	 * are printed together.
1336*3ad3fb45Sjoris 	 */
1337*3ad3fb45Sjoris 	for (; cvp <= context_vec_ptr; cvp++) {
1338*3ad3fb45Sjoris 		a = cvp->a;
1339*3ad3fb45Sjoris 		b = cvp->b;
1340*3ad3fb45Sjoris 		c = cvp->c;
1341*3ad3fb45Sjoris 		d = cvp->d;
1342*3ad3fb45Sjoris 
1343*3ad3fb45Sjoris 		/*
1344*3ad3fb45Sjoris 		 * c: both new and old changes
1345*3ad3fb45Sjoris 		 * d: only changes in the old file
1346*3ad3fb45Sjoris 		 * a: only changes in the new file
1347*3ad3fb45Sjoris 		 */
1348*3ad3fb45Sjoris 		if (a <= b && c <= d)
1349*3ad3fb45Sjoris 			ch = 'c';
1350*3ad3fb45Sjoris 		else
1351*3ad3fb45Sjoris 			ch = (a <= b) ? 'd' : 'a';
1352*3ad3fb45Sjoris 
1353*3ad3fb45Sjoris 		switch (ch) {
1354*3ad3fb45Sjoris 		case 'c':
1355*3ad3fb45Sjoris 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
1356*3ad3fb45Sjoris 			fetch(ixold, a, b, f1, '-', 0);
1357*3ad3fb45Sjoris 			fetch(ixnew, c, d, f2, '+', 0);
1358*3ad3fb45Sjoris 			break;
1359*3ad3fb45Sjoris 		case 'd':
1360*3ad3fb45Sjoris 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
1361*3ad3fb45Sjoris 			fetch(ixold, a, b, f1, '-', 0);
1362*3ad3fb45Sjoris 			break;
1363*3ad3fb45Sjoris 		case 'a':
1364*3ad3fb45Sjoris 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
1365*3ad3fb45Sjoris 			fetch(ixnew, c, d, f2, '+', 0);
1366*3ad3fb45Sjoris 			break;
1367*3ad3fb45Sjoris 		}
1368*3ad3fb45Sjoris 		lowa = b + 1;
1369*3ad3fb45Sjoris 		lowc = d + 1;
1370*3ad3fb45Sjoris 	}
1371*3ad3fb45Sjoris 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
1372*3ad3fb45Sjoris 
1373*3ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
1374*3ad3fb45Sjoris }
1375*3ad3fb45Sjoris 
1376*3ad3fb45Sjoris void
1377*3ad3fb45Sjoris diff_output(const char *fmt, ...)
1378*3ad3fb45Sjoris {
1379*3ad3fb45Sjoris 	va_list vap;
1380*3ad3fb45Sjoris 	int i;
1381*3ad3fb45Sjoris 	char *str;
1382*3ad3fb45Sjoris 
1383*3ad3fb45Sjoris 	va_start(vap, fmt);
1384*3ad3fb45Sjoris 	i = vasprintf(&str, fmt, vap);
1385*3ad3fb45Sjoris 	va_end(vap);
1386*3ad3fb45Sjoris 	if (i == -1)
1387*3ad3fb45Sjoris 		fatal("diff_output: %s", strerror(errno));
1388*3ad3fb45Sjoris 	if (diffbuf != NULL)
1389*3ad3fb45Sjoris 		cvs_buf_append(diffbuf, str, strlen(str));
1390*3ad3fb45Sjoris 	else
1391*3ad3fb45Sjoris 		cvs_printf("%s", str);
1392*3ad3fb45Sjoris 	xfree(str);
1393*3ad3fb45Sjoris }
1394