xref: /openbsd-src/usr.bin/cvs/diff.c (revision ec6ed1ab1769975d1495b2870472c00ec4d924fa)
1*ec6ed1abSjoris /*	$OpenBSD: diff.c,v 1.86 2006/04/01 20:11:25 joris Exp $	*/
208f90673Sjfb /*
308f90673Sjfb  * Copyright (C) Caldera International Inc.  2001-2002.
408f90673Sjfb  * All rights reserved.
508f90673Sjfb  *
608f90673Sjfb  * Redistribution and use in source and binary forms, with or without
708f90673Sjfb  * modification, are permitted provided that the following conditions
808f90673Sjfb  * are met:
908f90673Sjfb  * 1. Redistributions of source code and documentation must retain the above
1008f90673Sjfb  *    copyright notice, this list of conditions and the following disclaimer.
1108f90673Sjfb  * 2. Redistributions in binary form must reproduce the above copyright
1208f90673Sjfb  *    notice, this list of conditions and the following disclaimer in the
1308f90673Sjfb  *    documentation and/or other materials provided with the distribution.
1408f90673Sjfb  * 3. All advertising materials mentioning features or use of this software
1508f90673Sjfb  *    must display the following acknowledgement:
1608f90673Sjfb  *	This product includes software developed or owned by Caldera
1708f90673Sjfb  *	International, Inc.
1808f90673Sjfb  * 4. Neither the name of Caldera International, Inc. nor the names of other
1908f90673Sjfb  *    contributors may be used to endorse or promote products derived from
2008f90673Sjfb  *    this software without specific prior written permission.
2108f90673Sjfb  *
2208f90673Sjfb  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
2308f90673Sjfb  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
2408f90673Sjfb  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2508f90673Sjfb  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2608f90673Sjfb  * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
2708f90673Sjfb  * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2808f90673Sjfb  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2908f90673Sjfb  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3008f90673Sjfb  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3108f90673Sjfb  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
3208f90673Sjfb  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3308f90673Sjfb  * POSSIBILITY OF SUCH DAMAGE.
3408f90673Sjfb  */
3508f90673Sjfb /*-
3608f90673Sjfb  * Copyright (c) 1991, 1993
3708f90673Sjfb  *	The Regents of the University of California.  All rights reserved.
3808f90673Sjfb  * Copyright (c) 2004 Jean-Francois Brousseau.  All rights reserved.
3908f90673Sjfb  *
4008f90673Sjfb  * Redistribution and use in source and binary forms, with or without
4108f90673Sjfb  * modification, are permitted provided that the following conditions
4208f90673Sjfb  * are met:
4308f90673Sjfb  * 1. Redistributions of source code must retain the above copyright
4408f90673Sjfb  *    notice, this list of conditions and the following disclaimer.
4508f90673Sjfb  * 2. Redistributions in binary form must reproduce the above copyright
4608f90673Sjfb  *    notice, this list of conditions and the following disclaimer in the
4708f90673Sjfb  *    documentation and/or other materials provided with the distribution.
4808f90673Sjfb  * 3. Neither the name of the University nor the names of its contributors
4908f90673Sjfb  *    may be used to endorse or promote products derived from this software
5008f90673Sjfb  *    without specific prior written permission.
5108f90673Sjfb  *
5208f90673Sjfb  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5308f90673Sjfb  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5408f90673Sjfb  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5508f90673Sjfb  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5608f90673Sjfb  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5708f90673Sjfb  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5808f90673Sjfb  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5908f90673Sjfb  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6008f90673Sjfb  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6108f90673Sjfb  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6208f90673Sjfb  * SUCH DAMAGE.
6308f90673Sjfb  *
6408f90673Sjfb  *	@(#)diffreg.c   8.1 (Berkeley) 6/6/93
6508f90673Sjfb  */
6608f90673Sjfb /*
6708f90673Sjfb  *	Uses an algorithm due to Harold Stone, which finds
6808f90673Sjfb  *	a pair of longest identical subsequences in the two
6908f90673Sjfb  *	files.
7008f90673Sjfb  *
7108f90673Sjfb  *	The major goal is to generate the match vector J.
7208f90673Sjfb  *	J[i] is the index of the line in file1 corresponding
7308f90673Sjfb  *	to line i file0. J[i] = 0 if there is no
7408f90673Sjfb  *	such line in file1.
7508f90673Sjfb  *
7608f90673Sjfb  *	Lines are hashed so as to work in core. All potential
7708f90673Sjfb  *	matches are located by sorting the lines of each file
7808f90673Sjfb  *	on the hash (called ``value''). In particular, this
7908f90673Sjfb  *	collects the equivalence classes in file1 together.
8008f90673Sjfb  *	Subroutine equiv replaces the value of each line in
8108f90673Sjfb  *	file0 by the index of the first element of its
8208f90673Sjfb  *	matching equivalence in (the reordered) file1.
8308f90673Sjfb  *	To save space equiv squeezes file1 into a single
8408f90673Sjfb  *	array member in which the equivalence classes
8508f90673Sjfb  *	are simply concatenated, except that their first
8608f90673Sjfb  *	members are flagged by changing sign.
8708f90673Sjfb  *
8808f90673Sjfb  *	Next the indices that point into member are unsorted into
8908f90673Sjfb  *	array class according to the original order of file0.
9008f90673Sjfb  *
9108f90673Sjfb  *	The cleverness lies in routine stone. This marches
9208f90673Sjfb  *	through the lines of file0, developing a vector klist
9308f90673Sjfb  *	of "k-candidates". At step i a k-candidate is a matched
9408f90673Sjfb  *	pair of lines x,y (x in file0 y in file1) such that
9508f90673Sjfb  *	there is a common subsequence of length k
9608f90673Sjfb  *	between the first i lines of file0 and the first y
9708f90673Sjfb  *	lines of file1, but there is no such subsequence for
9808f90673Sjfb  *	any smaller y. x is the earliest possible mate to y
9908f90673Sjfb  *	that occurs in such a subsequence.
10008f90673Sjfb  *
10108f90673Sjfb  *	Whenever any of the members of the equivalence class of
10208f90673Sjfb  *	lines in file1 matable to a line in file0 has serial number
10308f90673Sjfb  *	less than the y of some k-candidate, that k-candidate
10408f90673Sjfb  *	with the smallest such y is replaced. The new
10508f90673Sjfb  *	k-candidate is chained (via pred) to the current
10608f90673Sjfb  *	k-1 candidate so that the actual subsequence can
10708f90673Sjfb  *	be recovered. When a member has serial number greater
10808f90673Sjfb  *	that the y of all k-candidates, the klist is extended.
10908f90673Sjfb  *	At the end, the longest subsequence is pulled out
11008f90673Sjfb  *	and placed in the array J by unravel
11108f90673Sjfb  *
11208f90673Sjfb  *	With J in hand, the matches there recorded are
11308f90673Sjfb  *	check'ed against reality to assure that no spurious
11408f90673Sjfb  *	matches have crept in due to hashing. If they have,
11508f90673Sjfb  *	they are broken, and "jackpot" is recorded--a harmless
11608f90673Sjfb  *	matter except that a true match for a spuriously
11708f90673Sjfb  *	mated line may now be unnecessarily reported as a change.
11808f90673Sjfb  *
11908f90673Sjfb  *	Much of the complexity of the program comes simply
12008f90673Sjfb  *	from trying to minimize core utilization and
12108f90673Sjfb  *	maximize the range of doable problems by dynamically
12208f90673Sjfb  *	allocating what is needed and reusing what is not.
12308f90673Sjfb  *	The core requirements for problems larger than somewhat
12408f90673Sjfb  *	are (in words) 2*length(file0) + length(file1) +
12508f90673Sjfb  *	3*(number of k-candidates installed),  typically about
12608f90673Sjfb  *	6n words for files of length n.
12708f90673Sjfb  */
12808f90673Sjfb 
129ac41f80cSxsa #include "includes.h"
13008f90673Sjfb 
1319225b0caSxsa #include "buf.h"
13208f90673Sjfb #include "cvs.h"
133af5bb824Sniallo #include "diff.h"
13408f90673Sjfb #include "log.h"
135dc6a6879Sjfb #include "proto.h"
13608f90673Sjfb 
137*ec6ed1abSjoris #include "xmalloc.h"
138*ec6ed1abSjoris 
13908f90673Sjfb struct cand {
14008f90673Sjfb 	int	x;
14108f90673Sjfb 	int	y;
14208f90673Sjfb 	int	pred;
14308f90673Sjfb } cand;
14408f90673Sjfb 
14508f90673Sjfb struct line {
14608f90673Sjfb 	int	serial;
14708f90673Sjfb 	int	value;
14808f90673Sjfb } *file[2];
14908f90673Sjfb 
15008f90673Sjfb /*
15108f90673Sjfb  * The following struct is used to record change in formation when
15208f90673Sjfb  * doing a "context" or "unified" diff.  (see routine "change" to
15308f90673Sjfb  * understand the highly mnemonic field names)
15408f90673Sjfb  */
15508f90673Sjfb struct context_vec {
15608f90673Sjfb 	int	a;	/* start line in old file */
15708f90673Sjfb 	int	b;	/* end line in old file */
15808f90673Sjfb 	int	c;	/* start line in new file */
15908f90673Sjfb 	int	d;	/* end line in new file */
16008f90673Sjfb };
16108f90673Sjfb 
162dc6a6879Sjfb struct diff_arg {
163dc6a6879Sjfb 	char	*rev1;
164dc6a6879Sjfb 	char	*rev2;
165dc6a6879Sjfb 	char	*date1;
166dc6a6879Sjfb 	char	*date2;
167dc6a6879Sjfb };
168dc6a6879Sjfb 
169af5bb824Sniallo #if !defined(RCSPROG)
170e4276007Sjfb static int	cvs_diff_init(struct cvs_cmd *, int, char **, int *);
171e4276007Sjfb static int	cvs_diff_remote(CVSFILE *, void *);
172e4276007Sjfb static int	cvs_diff_local(CVSFILE *, void *);
173e4276007Sjfb static int	cvs_diff_pre_exec(struct cvsroot *);
174e4276007Sjfb static int	cvs_diff_cleanup(void);
175af5bb824Sniallo #endif
17616cfc147Sjoris 
177b2dc910bSray static void	 output(FILE *, FILE *);
17808f90673Sjfb static void	 check(FILE *, FILE *);
17908f90673Sjfb static void	 range(int, int, char *);
18008f90673Sjfb static void	 uni_range(int, int);
18108f90673Sjfb static void	 dump_context_vec(FILE *, FILE *);
18208f90673Sjfb static void	 dump_unified_vec(FILE *, FILE *);
1837f535ec4Sjfb static int	 prepare(int, FILE *, off_t);
18408f90673Sjfb static void	 prune(void);
18508f90673Sjfb static void	 equiv(struct line *, int, struct line *, int, int *);
18608f90673Sjfb static void	 unravel(int);
18708f90673Sjfb static void	 unsort(struct line *, int, int *);
188b2dc910bSray static void	 change(FILE *, FILE *, int, int, int, int);
18908f90673Sjfb static void	 sort(struct line *, int);
19008f90673Sjfb static int	 ignoreline(char *);
19108f90673Sjfb static int	 asciifile(FILE *);
192a44f42a4Sxsa static void	 fetch(long *, int, int, FILE *, int, int);
19308f90673Sjfb static int	 newcand(int, int, int);
19408f90673Sjfb static int	 search(int *, int, int);
19508f90673Sjfb static int	 skipline(FILE *);
19608f90673Sjfb static int	 isqrt(int);
19708f90673Sjfb static int	 stone(int *, int, int *, int *);
19808f90673Sjfb static int	 readhash(FILE *);
19908f90673Sjfb static int	 files_differ(FILE *, FILE *);
2005e78344dSjfb static char	*match_function(const long *, int, FILE *);
20108f90673Sjfb static char	*preadline(int, size_t, off_t);
20208f90673Sjfb 
20308f90673Sjfb 
204af5bb824Sniallo #if !defined(RCSPROG)
205af5bb824Sniallo static int Nflag;
206af5bb824Sniallo #endif
207af5bb824Sniallo static int aflag, bflag, dflag, iflag, pflag, tflag, Tflag, wflag;
20878de3304Sniallo static int context = 3;
209f9b67873Sniallo int diff_format = D_NORMAL;
2101ff4dac1Sjoris char *diff_file = NULL;
211c60b216dSxsa char diffargs[128];
21208f90673Sjfb static struct stat stb1, stb2;
213af5bb824Sniallo static char *ifdefname, *ignore_pats;
21408f90673Sjfb regex_t ignore_re;
21508f90673Sjfb 
21608f90673Sjfb static int  *J;			/* will be overlaid on class */
21708f90673Sjfb static int  *class;		/* will be overlaid on file[0] */
21808f90673Sjfb static int  *klist;		/* will be overlaid on file[0] after class */
21908f90673Sjfb static int  *member;		/* will be overlaid on file[1] */
22008f90673Sjfb static int   clen;
22108f90673Sjfb static int   inifdef;		/* whether or not we are in a #ifdef block */
222e4276007Sjfb static int   diff_len[2];
22308f90673Sjfb static int   pref, suff;	/* length of prefix and suffix */
22408f90673Sjfb static int   slen[2];
22508f90673Sjfb static int   anychange;
22608f90673Sjfb static long *ixnew;		/* will be overlaid on file[1] */
22708f90673Sjfb static long *ixold;		/* will be overlaid on klist */
22808f90673Sjfb static struct cand *clist;	/* merely a free storage pot for candidates */
22908f90673Sjfb static int   clistlen;		/* the length of clist */
23008f90673Sjfb static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
23108f90673Sjfb static u_char *chrtran;		/* translation table for case-folding */
23208f90673Sjfb static struct context_vec *context_vec_start;
23308f90673Sjfb static struct context_vec *context_vec_end;
23408f90673Sjfb static struct context_vec *context_vec_ptr;
23508f90673Sjfb 
23608f90673Sjfb #define FUNCTION_CONTEXT_SIZE	41
2375e78344dSjfb static char lastbuf[FUNCTION_CONTEXT_SIZE];
23808f90673Sjfb static int  lastline;
23908f90673Sjfb static int  lastmatchline;
24001af718aSjoris BUF  *diffbuf = NULL;
241f9b67873Sniallo 
24208f90673Sjfb /*
24308f90673Sjfb  * chrtran points to one of 2 translation tables: cup2low if folding upper to
24408f90673Sjfb  * lower case clow2low if not folding case
24508f90673Sjfb  */
24608f90673Sjfb u_char clow2low[256] = {
24708f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
24808f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
24908f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
25008f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
25108f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
25208f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
25308f90673Sjfb 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
25408f90673Sjfb 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
25508f90673Sjfb 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
25608f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
25708f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
25808f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
25908f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
26008f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
26108f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
26208f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
26308f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
26408f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
26508f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
26608f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
26708f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
26808f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
26908f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
27008f90673Sjfb 	0xfd, 0xfe, 0xff
27108f90673Sjfb };
27208f90673Sjfb 
27308f90673Sjfb u_char cup2low[256] = {
27408f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
27508f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
27608f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
27708f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
27808f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
27908f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
28008f90673Sjfb 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
28108f90673Sjfb 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
28208f90673Sjfb 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
28308f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
28408f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
28508f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
28608f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
28708f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
28808f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
28908f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
29008f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
29108f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
29208f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
29308f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
29408f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
29508f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
29608f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
29708f90673Sjfb 	0xfd, 0xfe, 0xff
29808f90673Sjfb };
29908f90673Sjfb 
300af5bb824Sniallo #if !defined(RCSPROG)
301e4276007Sjfb struct cvs_cmd cvs_cmd_diff = {
302e4276007Sjfb 	CVS_OP_DIFF, CVS_REQ_DIFF, "diff",
303e4276007Sjfb 	{ "di", "dif" },
304e4276007Sjfb 	"Show differences between revisions",
305c9150269Sxsa 	"[-cilNnpu] [[-D date] [-r rev] [-D date2 | -r rev2]] "
306c9150269Sxsa 	"[-k mode] [file ...]",
307c9150269Sxsa 	"cD:iklNnpr:Ru",
30816cfc147Sjoris 	NULL,
30916cfc147Sjoris 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
310e4276007Sjfb 	cvs_diff_init,
311e4276007Sjfb 	cvs_diff_pre_exec,
312e4276007Sjfb 	cvs_diff_remote,
313e4276007Sjfb 	cvs_diff_local,
314e4276007Sjfb 	NULL,
315e4276007Sjfb 	cvs_diff_cleanup,
316e4276007Sjfb 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
317e4276007Sjfb };
318e4276007Sjfb 
319e4276007Sjfb 
320e4276007Sjfb struct cvs_cmd cvs_cmd_rdiff = {
321e4276007Sjfb 	CVS_OP_RDIFF, CVS_REQ_DIFF, "rdiff",
322c9150269Sxsa 	{ "pa", "patch" },
323e4276007Sjfb 	"Create 'patch' format diffs between releases",
324c9150269Sxsa 	"[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev "
325c9150269Sxsa 	"[-D date2 | -rev2] module ...",
326c9150269Sxsa 	"cD:flRr:stuV:",
327e4276007Sjfb 	NULL,
328e4276007Sjfb 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
329e4276007Sjfb 	cvs_diff_init,
330e4276007Sjfb 	cvs_diff_pre_exec,
331e4276007Sjfb 	cvs_diff_remote,
332e4276007Sjfb 	cvs_diff_local,
333e4276007Sjfb 	NULL,
334e4276007Sjfb 	cvs_diff_cleanup,
33544381dcbSjoris 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
33616cfc147Sjoris };
337af5bb824Sniallo #endif
33808f90673Sjfb 
339af5bb824Sniallo #if !defined(RCSPROG)
34016cfc147Sjoris static struct diff_arg *dap = NULL;
34116cfc147Sjoris 
342e4276007Sjfb static int
343e4276007Sjfb cvs_diff_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg)
34408f90673Sjfb {
34516cfc147Sjoris 	int ch;
34608f90673Sjfb 
3470450b43bSjoris 	dap = (struct diff_arg *)xmalloc(sizeof(*dap));
34816cfc147Sjoris 	dap->date1 = dap->date2 = dap->rev1 = dap->rev2 = NULL;
349dc6a6879Sjfb 	strlcpy(diffargs, argv[0], sizeof(diffargs));
350dc6a6879Sjfb 
351e4276007Sjfb 	while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) {
35208f90673Sjfb 		switch (ch) {
35308f90673Sjfb 		case 'c':
354f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
355f9b67873Sniallo 			diff_format = D_CONTEXT;
35608f90673Sjfb 			break;
35708f90673Sjfb 		case 'D':
35816cfc147Sjoris 			if (dap->date1 == NULL && dap->rev1 == NULL) {
35916cfc147Sjoris 				dap->date1 = optarg;
36016cfc147Sjoris 			} else if (dap->date2 == NULL && dap->rev2 == NULL) {
36116cfc147Sjoris 				dap->date2 = optarg;
36216cfc147Sjoris 			} else {
36308f90673Sjfb 				cvs_log(LP_ERR,
36408f90673Sjfb 				    "no more than two revisions/dates can "
36508f90673Sjfb 				    "be specified");
36608f90673Sjfb 			}
36708f90673Sjfb 			break;
36808f90673Sjfb 		case 'l':
369f5638424Sjfb 			strlcat(diffargs, " -l", sizeof(diffargs));
370e4276007Sjfb 			cvs_cmd_diff.file_flags &= ~CF_RECURSE;
37108f90673Sjfb 			break;
37208f90673Sjfb 		case 'i':
373f5638424Sjfb 			strlcat(diffargs, " -i", sizeof(diffargs));
37408f90673Sjfb 			iflag = 1;
37508f90673Sjfb 			break;
376c710bc5aSjfb 		case 'N':
377c710bc5aSjfb 			strlcat(diffargs, " -N", sizeof(diffargs));
378c710bc5aSjfb 			Nflag = 1;
379c710bc5aSjfb 			break;
380394180a4Sjfb 		case 'n':
381394180a4Sjfb 			strlcat(diffargs, " -n", sizeof(diffargs));
382f9b67873Sniallo 			diff_format = D_RCSDIFF;
383394180a4Sjfb 			break;
3845e78344dSjfb 		case 'p':
3855e78344dSjfb 			strlcat(diffargs, " -p", sizeof(diffargs));
3865e78344dSjfb 			pflag = 1;
3875e78344dSjfb 			break;
38808f90673Sjfb 		case 'r':
38916cfc147Sjoris 			if ((dap->rev1 == NULL) && (dap->date1 == NULL)) {
39016cfc147Sjoris 				dap->rev1 = optarg;
39116cfc147Sjoris 			} else if ((dap->rev2 == NULL) &&
39216cfc147Sjoris 			    (dap->date2 == NULL)) {
39316cfc147Sjoris 				dap->rev2 = optarg;
39416cfc147Sjoris 			} else {
39508f90673Sjfb 				cvs_log(LP_ERR,
39608f90673Sjfb 				    "no more than two revisions/dates can "
39708f90673Sjfb 				    "be specified");
39831274bbfSjoris 				return (CVS_EX_USAGE);
39908f90673Sjfb 			}
40008f90673Sjfb 			break;
401f203c484Sjoris 		case 'R':
402e4276007Sjfb 			cvs_cmd_diff.file_flags |= CF_RECURSE;
403f203c484Sjoris 			break;
40408f90673Sjfb 		case 'u':
405f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
406f9b67873Sniallo 			diff_format = D_UNIFIED;
40708f90673Sjfb 			break;
40808f90673Sjfb 		default:
40931274bbfSjoris 			return (CVS_EX_USAGE);
41008f90673Sjfb 		}
41108f90673Sjfb 	}
41208f90673Sjfb 
41316cfc147Sjoris 	*arg = optind;
414dc6a6879Sjfb 	return (0);
415dc6a6879Sjfb }
416dc6a6879Sjfb 
41716cfc147Sjoris int
41816cfc147Sjoris cvs_diff_cleanup(void)
41916cfc147Sjoris {
420e4276007Sjfb 	if (dap != NULL) {
4210450b43bSjoris 		xfree(dap);
422e4276007Sjfb 		dap = NULL;
423e4276007Sjfb 	}
42416cfc147Sjoris 	return (0);
42516cfc147Sjoris }
426dc6a6879Sjfb 
427dc6a6879Sjfb /*
428e4276007Sjfb  * cvs_diff_pre_exec()
429dc6a6879Sjfb  *
430dc6a6879Sjfb  */
431dc6a6879Sjfb int
432e4276007Sjfb cvs_diff_pre_exec(struct cvsroot *root)
433dc6a6879Sjfb {
434e4276007Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
43508f90673Sjfb 		/* send the flags */
4367e393898Sjoris 		if (Nflag == 1)
4377e393898Sjoris 			cvs_sendarg(root, "-N", 0);
4387e393898Sjoris 		if (pflag == 1)
4397e393898Sjoris 			cvs_sendarg(root, "-p", 0);
4405e78344dSjfb 
4417e393898Sjoris 		if (diff_format == D_CONTEXT)
4427e393898Sjoris 			cvs_sendarg(root, "-c", 0);
4437e393898Sjoris 		else if (diff_format == D_UNIFIED)
4447e393898Sjoris 			cvs_sendarg(root, "-u", 0);
44508f90673Sjfb 
446dc6a6879Sjfb 		if (dap->rev1 != NULL) {
4477e393898Sjoris 			cvs_sendarg(root, "-r", 0);
4487e393898Sjoris 			cvs_sendarg(root, dap->rev1, 0);
4493917c9bfSderaadt 		} else if (dap->date1 != NULL) {
4507e393898Sjoris 			cvs_sendarg(root, "-D", 0);
4517e393898Sjoris 			cvs_sendarg(root, dap->date1, 0);
45208f90673Sjfb 		}
453dc6a6879Sjfb 		if (dap->rev2 != NULL) {
4547e393898Sjoris 			cvs_sendarg(root, "-r", 0);
4557e393898Sjoris 			cvs_sendarg(root, dap->rev2, 0);
4563917c9bfSderaadt 		} else if (dap->date2 != NULL) {
4577e393898Sjoris 			cvs_sendarg(root, "-D", 0);
4587e393898Sjoris 			cvs_sendarg(root, dap->date2, 0);
45908f90673Sjfb 		}
460e4276007Sjfb 	}
46108f90673Sjfb 
46208f90673Sjfb 	return (0);
46308f90673Sjfb }
46408f90673Sjfb 
46508f90673Sjfb 
46608f90673Sjfb /*
46708f90673Sjfb  * cvs_diff_file()
46808f90673Sjfb  *
46908f90673Sjfb  * Diff a single file.
47008f90673Sjfb  */
471e4276007Sjfb static int
472e4276007Sjfb cvs_diff_remote(struct cvs_file *cfp, void *arg)
47308f90673Sjfb {
474d990145dSray 	char fpath[MAXPATHLEN];
475dc6a6879Sjfb 	struct cvsroot *root;
476dc6a6879Sjfb 
477dc6a6879Sjfb 	if (cfp->cf_type == DT_DIR) {
478895e6cf6Sjfb 		if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
479bb029937Sjfb 			root = cfp->cf_parent->cf_root;
480b904ba2eSjoris 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
4813917c9bfSderaadt 		} else {
482bb029937Sjfb 			root = cfp->cf_root;
48316cfc147Sjoris #if 0
484dc6a6879Sjfb 			if ((cfp->cf_parent == NULL) ||
485bb029937Sjfb 			    (root != cfp->cf_parent->cf_root)) {
486dc6a6879Sjfb 				cvs_connect(root);
487e4276007Sjfb 				cvs_diff_pre_exec(root);
488dc6a6879Sjfb 			}
48916cfc147Sjoris #endif
490dc6a6879Sjfb 
491dc6a6879Sjfb 			cvs_senddir(root, cfp);
492895e6cf6Sjfb 		}
493895e6cf6Sjfb 
494dc6a6879Sjfb 		return (0);
495dc6a6879Sjfb 	}
49608f90673Sjfb 
4972d5b8b1dSjfb 	if (cfp->cf_cvstat == CVS_FST_LOST) {
498b904ba2eSjoris 		cvs_log(LP_WARN, "cannot find file %s", cfp->cf_name);
4992d5b8b1dSjfb 		return (0);
5002d5b8b1dSjfb 	}
5012d5b8b1dSjfb 
502c710bc5aSjfb 	diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath));
503895e6cf6Sjfb 
504d990145dSray 	if (cfp->cf_parent != NULL)
505bb029937Sjfb 		root = cfp->cf_parent->cf_root;
506d990145dSray 	else
507895e6cf6Sjfb 		root = NULL;
50808f90673Sjfb 
509dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
510e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
511dc6a6879Sjfb 		return (0);
51208f90673Sjfb 	}
51308f90673Sjfb 
5147e393898Sjoris 	cvs_sendentry(root, cfp);
51508f90673Sjfb 
516dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UPTODATE) {
517e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name);
51808f90673Sjfb 		return (0);
51908f90673Sjfb 	}
52008f90673Sjfb 
52108f90673Sjfb 	/* at this point, the file is modified */
5227e393898Sjoris 	cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name);
5237e393898Sjoris 	cvs_sendfile(root, diff_file);
524e4276007Sjfb 
525e4276007Sjfb 	return (0);
526e4276007Sjfb }
527e4276007Sjfb 
528e4276007Sjfb static int
529e4276007Sjfb cvs_diff_local(CVSFILE *cf, void *arg)
530e4276007Sjfb {
531d990145dSray 	char buf[64];
532e4276007Sjfb 	char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
533e4276007Sjfb 	char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN];
534e4276007Sjfb 	BUF *b1, *b2;
535e4276007Sjfb 	RCSNUM *r1, *r2;
536e4276007Sjfb 	RCSFILE *rf;
537bf951d2dSniallo 	struct timeval tv[2], tv2[2];
538bf951d2dSniallo 
539bf951d2dSniallo 	memset(&tv, 0, sizeof(tv));
540bf951d2dSniallo 	memset(&tv2, 0, sizeof(tv2));
541e4276007Sjfb 
542e4276007Sjfb 	rf = NULL;
543e4276007Sjfb 	diff_file = cvs_file_getpath(cf, fpath, sizeof(fpath));
544e4276007Sjfb 
545e4276007Sjfb 	if (cf->cf_type == DT_DIR) {
546b87c59bdSxsa 		if (verbosity > 1)
547246675edSxsa 			cvs_log(LP_NOTICE, "Diffing %s", fpath);
548e4276007Sjfb 		return (0);
549e4276007Sjfb 	}
550e4276007Sjfb 
551e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_LOST) {
552e4276007Sjfb 		cvs_log(LP_WARN, "cannot find file %s", cf->cf_name);
553e4276007Sjfb 		return (0);
554e4276007Sjfb 	}
555e4276007Sjfb 
556e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_UNKNOWN) {
557e4276007Sjfb 		cvs_log(LP_WARN, "I know nothing about %s", diff_file);
558e4276007Sjfb 		return (0);
5595c0cd766Sniallo 	} else if (cf->cf_cvstat == CVS_FST_UPTODATE)
560e4276007Sjfb 		return (0);
561e4276007Sjfb 
562e4276007Sjfb 	/* at this point, the file is modified */
563cb9f80caSxsa 	cvs_rcs_getpath(cf, rcspath, sizeof(rcspath));
56408f90673Sjfb 
5653956f8daSxsa 	if ((rf = rcs_open(rcspath, RCS_READ)) == NULL)
5663956f8daSxsa 		fatal("cvs_diff_local: rcs_open `%s': %s", rcspath,
56766c1e2eaSxsa 		    rcs_errstr(rcs_errno));
56808f90673Sjfb 
569dc6a6879Sjfb 	cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file,
57008f90673Sjfb 	    RCS_DIFF_DIV, rcspath);
57108f90673Sjfb 
572dc6a6879Sjfb 	if (dap->rev1 == NULL)
573e4276007Sjfb 		r1 = cf->cf_lrev;
57408f90673Sjfb 	else {
5753956f8daSxsa 		if ((r1 = rcsnum_parse(dap->rev1)) == NULL)
5763956f8daSxsa 			fatal("cvs_diff_local: rcsnum_parse failed");
57708f90673Sjfb 	}
57808f90673Sjfb 
579dc6a6879Sjfb 	cvs_printf("retrieving revision %s\n",
58008f90673Sjfb 	    rcsnum_tostr(r1, buf, sizeof(buf)));
58108f90673Sjfb 	b1 = rcs_getrev(rf, r1);
58208f90673Sjfb 
58395ae1173Sjoris 	if (b1 == NULL) {
5840e9dc9e3Sniallo 		cvs_log(LP_ERR, "failed to retrieve revision %s",
58595ae1173Sjoris 		    rcsnum_tostr(r1, buf, sizeof(buf)));
58695ae1173Sjoris 		if (r1 != cf->cf_lrev)
58795ae1173Sjoris 			rcsnum_free(r1);
58864672f74Sjoris 		rcs_close(rf);
58995ae1173Sjoris 		return (CVS_EX_DATA);
59095ae1173Sjoris 	}
591bf951d2dSniallo 	tv[0].tv_sec = (long)rcs_rev_getdate(rf, r1);
592bf951d2dSniallo 	tv[1].tv_sec = tv[0].tv_sec;
59395ae1173Sjoris 
594e4276007Sjfb 	if (r1 != cf->cf_lrev)
5957f535ec4Sjfb 		rcsnum_free(r1);
5967f535ec4Sjfb 
597dc6a6879Sjfb 	if (dap->rev2 != NULL) {
598dc6a6879Sjfb 		cvs_printf("retrieving revision %s\n", dap->rev2);
59925b74b48Sjfb 		if ((r2 = rcsnum_parse(dap->rev2)) == NULL) {
60064672f74Sjoris 			rcs_close(rf);
60131274bbfSjoris 			return (CVS_EX_DATA);
6027f535ec4Sjfb 		}
60308f90673Sjfb 		b2 = rcs_getrev(rf, r2);
604f3e6c8ebSniallo 		tv2[0].tv_sec = (long)rcs_rev_getdate(rf, r2);
605f3e6c8ebSniallo 		tv2[1].tv_sec = tv2[0].tv_sec;
6067f535ec4Sjfb 		rcsnum_free(r2);
6073917c9bfSderaadt 	} else {
608f3e6c8ebSniallo 		struct stat st;
609f3e6c8ebSniallo 		if (stat(diff_file, &st) < 0) {
6100e9dc9e3Sniallo 			cvs_log(LP_ERR, "failed to retrieve revision %s",
611f3e6c8ebSniallo 			    dap->rev2);
612f3e6c8ebSniallo 			cvs_buf_free(b1);
613f3e6c8ebSniallo 			return (CVS_EX_DATA);
614f3e6c8ebSniallo 		}
615dc6a6879Sjfb 		b2 = cvs_buf_load(diff_file, BUF_AUTOEXT);
616f3e6c8ebSniallo 		tv2[0].tv_sec = st.st_mtime;
617f3e6c8ebSniallo 		tv2[1].tv_sec = st.st_mtime;
61808f90673Sjfb 	}
61908f90673Sjfb 
620dc6a6879Sjfb 	rcs_close(rf);
621dc6a6879Sjfb 
62295ae1173Sjoris 	if (b2 == NULL) {
6230e9dc9e3Sniallo 		cvs_log(LP_ERR, "failed to retrieve revision %s",
62495ae1173Sjoris 		    dap->rev2);
62595ae1173Sjoris 		cvs_buf_free(b1);
62695ae1173Sjoris 		return (CVS_EX_DATA);
62795ae1173Sjoris 	}
62895ae1173Sjoris 
6295ac8b1e7Sjoris 	cvs_printf("%s", diffargs);
6305ac8b1e7Sjoris 	cvs_printf(" -r%s", buf);
631dc6a6879Sjfb 	if (dap->rev2 != NULL)
6325ac8b1e7Sjoris 		cvs_printf(" -r%s", dap->rev2);
6335ac8b1e7Sjoris 	cvs_printf(" %s\n", diff_file);
6341dee9299Sxsa 	strlcpy(path_tmp1, cvs_tmpdir, sizeof(path_tmp1));
6351dee9299Sxsa 	strlcat(path_tmp1, "/diff1.XXXXXXXXXX", sizeof(path_tmp1));
636fbe562adSxsa 	cvs_buf_write_stmp(b1, path_tmp1, 0600);
6377f535ec4Sjfb 	cvs_buf_free(b1);
638bf951d2dSniallo 	if (utimes(path_tmp1, (const struct timeval *)&tv) < 0)
639bf951d2dSniallo 		cvs_log(LP_ERRNO, "error setting utimes");
6407f535ec4Sjfb 
6411dee9299Sxsa 	strlcpy(path_tmp2, cvs_tmpdir, sizeof(path_tmp2));
6421dee9299Sxsa 	strlcat(path_tmp2, "/diff2.XXXXXXXXXX", sizeof(path_tmp2));
6438dbc5e14Sxsa 	cvs_buf_write_stmp(b2, path_tmp2, 0600);
6447f535ec4Sjfb 	cvs_buf_free(b2);
645bf951d2dSniallo 	if (utimes(path_tmp2, (const struct timeval *)&tv2) < 0)
646bf951d2dSniallo 		cvs_log(LP_ERRNO, "error setting utimes");
6477f535ec4Sjfb 
648f9b67873Sniallo 	cvs_diffreg(path_tmp1, path_tmp2, NULL);
649946f6157Sdjm 	(void)unlink(path_tmp1);
650946f6157Sdjm 	(void)unlink(path_tmp2);
65108f90673Sjfb 
65208f90673Sjfb 	return (0);
65308f90673Sjfb }
654af5bb824Sniallo #endif
65508f90673Sjfb 
65608f90673Sjfb 
65708f90673Sjfb int
658f9b67873Sniallo cvs_diffreg(const char *file1, const char *file2, BUF *out)
65908f90673Sjfb {
66008f90673Sjfb 	FILE *f1, *f2;
66108f90673Sjfb 	int i, rval;
6627f535ec4Sjfb 	void *tmp;
66308f90673Sjfb 
66408f90673Sjfb 	f1 = f2 = NULL;
66508f90673Sjfb 	rval = D_SAME;
66608f90673Sjfb 	anychange = 0;
66708f90673Sjfb 	lastline = 0;
66808f90673Sjfb 	lastmatchline = 0;
66908f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
67008f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
671f9b67873Sniallo 	if (out != NULL)
672f9b67873Sniallo 		diffbuf = out;
67308f90673Sjfb 
67408f90673Sjfb 	f1 = fopen(file1, "r");
67508f90673Sjfb 	if (f1 == NULL) {
67608f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
67708f90673Sjfb 		goto closem;
67808f90673Sjfb 	}
67908f90673Sjfb 
68008f90673Sjfb 	f2 = fopen(file2, "r");
68108f90673Sjfb 	if (f2 == NULL) {
68208f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
68308f90673Sjfb 		goto closem;
68408f90673Sjfb 	}
68508f90673Sjfb 
686bf951d2dSniallo 	if (stat(file1, &stb1) < 0) {
687bf951d2dSniallo 		cvs_log(LP_ERRNO, "%s", file1);
688bf951d2dSniallo 		goto closem;
689bf951d2dSniallo 	}
690bf951d2dSniallo 	if (stat(file2, &stb2) < 0) {
691bf951d2dSniallo 		cvs_log(LP_ERRNO, "%s", file2);
692bf951d2dSniallo 		goto closem;
693bf951d2dSniallo 	}
69408f90673Sjfb 	switch (files_differ(f1, f2)) {
69508f90673Sjfb 	case 0:
69608f90673Sjfb 		goto closem;
69708f90673Sjfb 	case 1:
69808f90673Sjfb 		break;
69908f90673Sjfb 	default:
70008f90673Sjfb 		/* error */
70108f90673Sjfb 		goto closem;
70208f90673Sjfb 	}
70308f90673Sjfb 
70408f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
70508f90673Sjfb 		rval = D_BINARY;
70608f90673Sjfb 		goto closem;
70708f90673Sjfb 	}
7087f535ec4Sjfb 	if ((prepare(0, f1, stb1.st_size) < 0) ||
7097f535ec4Sjfb 	    (prepare(1, f2, stb2.st_size) < 0)) {
7107f535ec4Sjfb 		goto closem;
7117f535ec4Sjfb 	}
71208f90673Sjfb 	prune();
71308f90673Sjfb 	sort(sfile[0], slen[0]);
71408f90673Sjfb 	sort(sfile[1], slen[1]);
71508f90673Sjfb 
71608f90673Sjfb 	member = (int *)file[1];
71708f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
7182e0e138cSray 	tmp = xrealloc(member, slen[1] + 2, sizeof(int));
7197f535ec4Sjfb 	member = (int *)tmp;
72008f90673Sjfb 
72108f90673Sjfb 	class = (int *)file[0];
72208f90673Sjfb 	unsort(sfile[0], slen[0], class);
7232e0e138cSray 	tmp = xrealloc(class, slen[0] + 2, sizeof(int));
7247f535ec4Sjfb 	class = (int *)tmp;
72508f90673Sjfb 
726b39d2e28Sray 	klist = xcalloc(slen[0] + 2, sizeof(int));
72708f90673Sjfb 	clen = 0;
72808f90673Sjfb 	clistlen = 100;
729b39d2e28Sray 	clist = xcalloc(clistlen, sizeof(cand));
730ece76a70Sjoris 
731ece76a70Sjoris 	if ((i = stone(class, slen[0], member, klist)) < 0)
732ece76a70Sjoris 		goto closem;
733ece76a70Sjoris 
7340450b43bSjoris 	xfree(member);
7350450b43bSjoris 	xfree(class);
73608f90673Sjfb 
7372e0e138cSray 	tmp = xrealloc(J, diff_len[0] + 2, sizeof(int));
73818501190Sniallo 	J = (int *)tmp;
73908f90673Sjfb 	unravel(klist[i]);
7400450b43bSjoris 	xfree(clist);
7410450b43bSjoris 	xfree(klist);
74208f90673Sjfb 
7432e0e138cSray 	tmp = xrealloc(ixold, diff_len[0] + 2, sizeof(long));
74418501190Sniallo 	ixold = (long *)tmp;
7450450b43bSjoris 
7462e0e138cSray 	tmp = xrealloc(ixnew, diff_len[1] + 2, sizeof(long));
74718501190Sniallo 	ixnew = (long *)tmp;
74808f90673Sjfb 	check(f1, f2);
749b2dc910bSray 	output(f1, f2);
75008f90673Sjfb 
75108f90673Sjfb closem:
75237af5b8bSxsa 	if (anychange == 1) {
75308f90673Sjfb 		if (rval == D_SAME)
75408f90673Sjfb 			rval = D_DIFFER;
75508f90673Sjfb 	}
75608f90673Sjfb 	if (f1 != NULL)
75708f90673Sjfb 		fclose(f1);
75808f90673Sjfb 	if (f2 != NULL)
75908f90673Sjfb 		fclose(f2);
7607f535ec4Sjfb 
76108f90673Sjfb 	return (rval);
76208f90673Sjfb }
76308f90673Sjfb 
76408f90673Sjfb /*
76508f90673Sjfb  * Check to see if the given files differ.
76608f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
76708f90673Sjfb  * XXX - could use code from cmp(1) [faster]
76808f90673Sjfb  */
76908f90673Sjfb static int
77008f90673Sjfb files_differ(FILE *f1, FILE *f2)
77108f90673Sjfb {
77208f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
77308f90673Sjfb 	size_t i, j;
77408f90673Sjfb 
77508f90673Sjfb 	if (stb1.st_size != stb2.st_size)
77608f90673Sjfb 		return (1);
77708f90673Sjfb 	for (;;) {
778f1901a5aSxsa 		i = fread(buf1, (size_t)1, sizeof(buf1), f1);
779f1901a5aSxsa 		j = fread(buf2, (size_t)1, sizeof(buf2), f2);
78008f90673Sjfb 		if (i != j)
78108f90673Sjfb 			return (1);
78237af5b8bSxsa 		if ((i == 0) && (j == 0)) {
78308f90673Sjfb 			if (ferror(f1) || ferror(f2))
78408f90673Sjfb 				return (1);
78508f90673Sjfb 			return (0);
78608f90673Sjfb 		}
78708f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
78808f90673Sjfb 			return (1);
78908f90673Sjfb 	}
79008f90673Sjfb }
79108f90673Sjfb 
7927f535ec4Sjfb static int
79308f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
79408f90673Sjfb {
7957f535ec4Sjfb 	void *tmp;
79608f90673Sjfb 	struct line *p;
79708f90673Sjfb 	int j, h;
79808f90673Sjfb 	size_t sz;
79908f90673Sjfb 
80008f90673Sjfb 	rewind(fd);
80108f90673Sjfb 
802c48da046Sxsa 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
80308f90673Sjfb 	if (sz < 100)
80408f90673Sjfb 		sz = 100;
80508f90673Sjfb 
806b39d2e28Sray 	p = (struct line *)xcalloc(sz + 3, sizeof(struct line));
80708f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
80808f90673Sjfb 		if (j == (int)sz) {
80908f90673Sjfb 			sz = sz * 3 / 2;
8102e0e138cSray 			tmp = xrealloc(p, sz + 3, sizeof(struct line));
8117f535ec4Sjfb 			p = (struct line *)tmp;
81208f90673Sjfb 		}
81308f90673Sjfb 		p[++j].value = h;
81408f90673Sjfb 	}
815e4276007Sjfb 	diff_len[i] = j;
81608f90673Sjfb 	file[i] = p;
8177f535ec4Sjfb 
8187f535ec4Sjfb 	return (0);
81908f90673Sjfb }
82008f90673Sjfb 
82108f90673Sjfb static void
82208f90673Sjfb prune(void)
82308f90673Sjfb {
82408f90673Sjfb 	int i, j;
82508f90673Sjfb 
826e4276007Sjfb 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
82708f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
82808f90673Sjfb 	    pref++)
82908f90673Sjfb 		;
830e4276007Sjfb 	for (suff = 0;
831e4276007Sjfb 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
832e4276007Sjfb 	    (file[0][diff_len[0] - suff].value ==
833e4276007Sjfb 	    file[1][diff_len[1] - suff].value);
83408f90673Sjfb 	    suff++)
83508f90673Sjfb 		;
83608f90673Sjfb 	for (j = 0; j < 2; j++) {
83708f90673Sjfb 		sfile[j] = file[j] + pref;
838e4276007Sjfb 		slen[j] = diff_len[j] - pref - suff;
83908f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
84008f90673Sjfb 			sfile[j][i].serial = i;
84108f90673Sjfb 	}
84208f90673Sjfb }
84308f90673Sjfb 
84408f90673Sjfb static void
84508f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
84608f90673Sjfb {
84708f90673Sjfb 	int i, j;
84808f90673Sjfb 
84908f90673Sjfb 	i = j = 1;
85008f90673Sjfb 	while (i <= n && j <= m) {
85108f90673Sjfb 		if (a[i].value < b[j].value)
85208f90673Sjfb 			a[i++].value = 0;
85308f90673Sjfb 		else if (a[i].value == b[j].value)
85408f90673Sjfb 			a[i++].value = j;
85508f90673Sjfb 		else
85608f90673Sjfb 			j++;
85708f90673Sjfb 	}
85808f90673Sjfb 	while (i <= n)
85908f90673Sjfb 		a[i++].value = 0;
86008f90673Sjfb 	b[m + 1].value = 0;
86108f90673Sjfb 	j = 0;
86208f90673Sjfb 	while (++j <= m) {
86308f90673Sjfb 		c[j] = -b[j].serial;
86408f90673Sjfb 		while (b[j + 1].value == b[j].value) {
86508f90673Sjfb 			j++;
86608f90673Sjfb 			c[j] = b[j].serial;
86708f90673Sjfb 		}
86808f90673Sjfb 	}
86908f90673Sjfb 	c[j] = -1;
87008f90673Sjfb }
87108f90673Sjfb 
87208f90673Sjfb /* Code taken from ping.c */
87308f90673Sjfb static int
87408f90673Sjfb isqrt(int n)
87508f90673Sjfb {
87608f90673Sjfb 	int y, x = 1;
87708f90673Sjfb 
87808f90673Sjfb 	if (n == 0)
87908f90673Sjfb 		return (0);
88008f90673Sjfb 
88108f90673Sjfb 	do { /* newton was a stinker */
88208f90673Sjfb 		y = x;
88308f90673Sjfb 		x = n / x;
88408f90673Sjfb 		x += y;
88508f90673Sjfb 		x /= 2;
88608f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
88708f90673Sjfb 
88808f90673Sjfb 	return (x);
88908f90673Sjfb }
89008f90673Sjfb 
89108f90673Sjfb static int
89208f90673Sjfb stone(int *a, int n, int *b, int *c)
89308f90673Sjfb {
894ece76a70Sjoris 	int ret;
89508f90673Sjfb 	int i, k, y, j, l;
89608f90673Sjfb 	int oldc, tc, oldl;
89708f90673Sjfb 	u_int numtries;
89808f90673Sjfb 
899cc649edbSjfb 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
900cc649edbSjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
90108f90673Sjfb 
90208f90673Sjfb 	k = 0;
903ece76a70Sjoris 	if ((ret = newcand(0, 0, 0)) < 0)
904ece76a70Sjoris 		return (-1);
905ece76a70Sjoris 	c[0] = ret;
90608f90673Sjfb 	for (i = 1; i <= n; i++) {
90708f90673Sjfb 		j = a[i];
90808f90673Sjfb 		if (j == 0)
90908f90673Sjfb 			continue;
91008f90673Sjfb 		y = -b[j];
91108f90673Sjfb 		oldl = 0;
91208f90673Sjfb 		oldc = c[0];
91308f90673Sjfb 		numtries = 0;
91408f90673Sjfb 		do {
91508f90673Sjfb 			if (y <= clist[oldc].y)
91608f90673Sjfb 				continue;
91708f90673Sjfb 			l = search(c, k, y);
91808f90673Sjfb 			if (l != oldl + 1)
91908f90673Sjfb 				oldc = c[l - 1];
92008f90673Sjfb 			if (l <= k) {
92108f90673Sjfb 				if (clist[c[l]].y <= y)
92208f90673Sjfb 					continue;
92308f90673Sjfb 				tc = c[l];
924ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
925ece76a70Sjoris 					return (-1);
926ece76a70Sjoris 				c[l] = ret;
92708f90673Sjfb 				oldc = tc;
92808f90673Sjfb 				oldl = l;
92908f90673Sjfb 				numtries++;
93008f90673Sjfb 			} else {
931ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
932ece76a70Sjoris 					return (-1);
933ece76a70Sjoris 				c[l] = ret;
93408f90673Sjfb 				k++;
93508f90673Sjfb 				break;
93608f90673Sjfb 			}
93708f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
93808f90673Sjfb 	}
93908f90673Sjfb 	return (k);
94008f90673Sjfb }
94108f90673Sjfb 
94208f90673Sjfb static int
94308f90673Sjfb newcand(int x, int y, int pred)
94408f90673Sjfb {
94518501190Sniallo 	struct cand *q, *tmp;
94618501190Sniallo 	int newclistlen;
94708f90673Sjfb 
94808f90673Sjfb 	if (clen == clistlen) {
94918501190Sniallo 		newclistlen = clistlen * 11 / 10;
9502e0e138cSray 		tmp = xrealloc(clist, newclistlen, sizeof(cand));
95118501190Sniallo 		clist = tmp;
95218501190Sniallo 		clistlen = newclistlen;
95308f90673Sjfb 	}
95408f90673Sjfb 	q = clist + clen;
95508f90673Sjfb 	q->x = x;
95608f90673Sjfb 	q->y = y;
95708f90673Sjfb 	q->pred = pred;
95808f90673Sjfb 	return (clen++);
95908f90673Sjfb }
96008f90673Sjfb 
96108f90673Sjfb static int
96208f90673Sjfb search(int *c, int k, int y)
96308f90673Sjfb {
96408f90673Sjfb 	int i, j, l, t;
96508f90673Sjfb 
96608f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
96708f90673Sjfb 		return (k + 1);
96808f90673Sjfb 	i = 0;
96908f90673Sjfb 	j = k + 1;
970b2dc910bSray 	for (;;) {
971b2dc910bSray 		l = (i + j) / 2;
972b2dc910bSray 		if (l <= i)
97308f90673Sjfb 			break;
97408f90673Sjfb 		t = clist[c[l]].y;
97508f90673Sjfb 		if (t > y)
97608f90673Sjfb 			j = l;
97708f90673Sjfb 		else if (t < y)
97808f90673Sjfb 			i = l;
97908f90673Sjfb 		else
98008f90673Sjfb 			return (l);
98108f90673Sjfb 	}
98208f90673Sjfb 	return (l + 1);
98308f90673Sjfb }
98408f90673Sjfb 
98508f90673Sjfb static void
98608f90673Sjfb unravel(int p)
98708f90673Sjfb {
98808f90673Sjfb 	struct cand *q;
98908f90673Sjfb 	int i;
99008f90673Sjfb 
991e4276007Sjfb 	for (i = 0; i <= diff_len[0]; i++)
99208f90673Sjfb 		J[i] = i <= pref ? i :
993e4276007Sjfb 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
99408f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
99508f90673Sjfb 		J[q->x + pref] = q->y + pref;
99608f90673Sjfb }
99708f90673Sjfb 
99808f90673Sjfb /*
99908f90673Sjfb  * Check does double duty:
100008f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
100108f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
100208f90673Sjfb  *  2.  collect random access indexes to the two files
100308f90673Sjfb  */
100408f90673Sjfb static void
100508f90673Sjfb check(FILE *f1, FILE *f2)
100608f90673Sjfb {
100708f90673Sjfb 	int i, j, jackpot, c, d;
100808f90673Sjfb 	long ctold, ctnew;
100908f90673Sjfb 
101008f90673Sjfb 	rewind(f1);
101108f90673Sjfb 	rewind(f2);
101208f90673Sjfb 	j = 1;
101308f90673Sjfb 	ixold[0] = ixnew[0] = 0;
101408f90673Sjfb 	jackpot = 0;
101508f90673Sjfb 	ctold = ctnew = 0;
1016e4276007Sjfb 	for (i = 1; i <= diff_len[0]; i++) {
101708f90673Sjfb 		if (J[i] == 0) {
101808f90673Sjfb 			ixold[i] = ctold += skipline(f1);
101908f90673Sjfb 			continue;
102008f90673Sjfb 		}
102108f90673Sjfb 		while (j < J[i]) {
102208f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
102308f90673Sjfb 			j++;
102408f90673Sjfb 		}
102548dc77e6Sxsa 		if ((bflag == 1)|| (wflag == 1) || (iflag == 1)) {
102608f90673Sjfb 			for (;;) {
102708f90673Sjfb 				c = getc(f1);
102808f90673Sjfb 				d = getc(f2);
102908f90673Sjfb 				/*
103008f90673Sjfb 				 * GNU diff ignores a missing newline
103108f90673Sjfb 				 * in one file if bflag || wflag.
103208f90673Sjfb 				 */
103348dc77e6Sxsa 				if (((bflag == 1) || (wflag == 1)) &&
103408f90673Sjfb 				    ((c == EOF && d == '\n') ||
103508f90673Sjfb 				    (c == '\n' && d == EOF))) {
103608f90673Sjfb 					break;
103708f90673Sjfb 				}
103808f90673Sjfb 				ctold++;
103908f90673Sjfb 				ctnew++;
104048dc77e6Sxsa 				if ((bflag == 1) && isspace(c) && isspace(d)) {
104108f90673Sjfb 					do {
104208f90673Sjfb 						if (c == '\n')
104308f90673Sjfb 							break;
104408f90673Sjfb 						ctold++;
104508f90673Sjfb 					} while (isspace(c = getc(f1)));
104608f90673Sjfb 					do {
104708f90673Sjfb 						if (d == '\n')
104808f90673Sjfb 							break;
104908f90673Sjfb 						ctnew++;
105008f90673Sjfb 					} while (isspace(d = getc(f2)));
105148dc77e6Sxsa 				} else if (wflag == 1) {
105208f90673Sjfb 					while (isspace(c) && c != '\n') {
105308f90673Sjfb 						c = getc(f1);
105408f90673Sjfb 						ctold++;
105508f90673Sjfb 					}
105608f90673Sjfb 					while (isspace(d) && d != '\n') {
105708f90673Sjfb 						d = getc(f2);
105808f90673Sjfb 						ctnew++;
105908f90673Sjfb 					}
106008f90673Sjfb 				}
106108f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
106208f90673Sjfb 					jackpot++;
106308f90673Sjfb 					J[i] = 0;
106437af5b8bSxsa 					if ((c != '\n') && (c != EOF))
106508f90673Sjfb 						ctold += skipline(f1);
106637af5b8bSxsa 					if ((d != '\n') && (c != EOF))
106708f90673Sjfb 						ctnew += skipline(f2);
106808f90673Sjfb 					break;
106908f90673Sjfb 				}
107037af5b8bSxsa 				if ((c == '\n') || (c == EOF))
107108f90673Sjfb 					break;
107208f90673Sjfb 			}
107308f90673Sjfb 		} else {
107408f90673Sjfb 			for (;;) {
107508f90673Sjfb 				ctold++;
107608f90673Sjfb 				ctnew++;
107708f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
107808f90673Sjfb 					/* jackpot++; */
107908f90673Sjfb 					J[i] = 0;
108037af5b8bSxsa 					if ((c != '\n') && (c != EOF))
108108f90673Sjfb 						ctold += skipline(f1);
108237af5b8bSxsa 					if ((d != '\n') && (c != EOF))
108308f90673Sjfb 						ctnew += skipline(f2);
108408f90673Sjfb 					break;
108508f90673Sjfb 				}
108637af5b8bSxsa 				if ((c == '\n') || (c == EOF))
108708f90673Sjfb 					break;
108808f90673Sjfb 			}
108908f90673Sjfb 		}
109008f90673Sjfb 		ixold[i] = ctold;
109108f90673Sjfb 		ixnew[j] = ctnew;
109208f90673Sjfb 		j++;
109308f90673Sjfb 	}
1094e4276007Sjfb 	for (; j <= diff_len[1]; j++)
109508f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
109608f90673Sjfb 	/*
109737af5b8bSxsa 	 * if (jackpot != 0)
10985ac8b1e7Sjoris 	 *	cvs_printf("jackpot\n");
109908f90673Sjfb 	 */
110008f90673Sjfb }
110108f90673Sjfb 
110208f90673Sjfb /* shellsort CACM #201 */
110308f90673Sjfb static void
110408f90673Sjfb sort(struct line *a, int n)
110508f90673Sjfb {
110608f90673Sjfb 	struct line *ai, *aim, w;
110708f90673Sjfb 	int j, m = 0, k;
110808f90673Sjfb 
110908f90673Sjfb 	if (n == 0)
111008f90673Sjfb 		return;
111108f90673Sjfb 	for (j = 1; j <= n; j *= 2)
111208f90673Sjfb 		m = 2 * j - 1;
111308f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
111408f90673Sjfb 		k = n - m;
111508f90673Sjfb 		for (j = 1; j <= k; j++) {
111608f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
111708f90673Sjfb 				aim = &ai[m];
111808f90673Sjfb 				if (aim < ai)
111908f90673Sjfb 					break;	/* wraparound */
112008f90673Sjfb 				if (aim->value > ai[0].value ||
112108f90673Sjfb 				    (aim->value == ai[0].value &&
112208f90673Sjfb 					aim->serial > ai[0].serial))
112308f90673Sjfb 					break;
112408f90673Sjfb 				w.value = ai[0].value;
112508f90673Sjfb 				ai[0].value = aim->value;
112608f90673Sjfb 				aim->value = w.value;
112708f90673Sjfb 				w.serial = ai[0].serial;
112808f90673Sjfb 				ai[0].serial = aim->serial;
112908f90673Sjfb 				aim->serial = w.serial;
113008f90673Sjfb 			}
113108f90673Sjfb 		}
113208f90673Sjfb 	}
113308f90673Sjfb }
113408f90673Sjfb 
113508f90673Sjfb static void
113608f90673Sjfb unsort(struct line *f, int l, int *b)
113708f90673Sjfb {
113808f90673Sjfb 	int *a, i;
113908f90673Sjfb 
1140b39d2e28Sray 	a = (int *)xcalloc(l + 1, sizeof(int));
114108f90673Sjfb 	for (i = 1; i <= l; i++)
114208f90673Sjfb 		a[f[i].serial] = f[i].value;
114308f90673Sjfb 	for (i = 1; i <= l; i++)
114408f90673Sjfb 		b[i] = a[i];
11450450b43bSjoris 	xfree(a);
114608f90673Sjfb }
114708f90673Sjfb 
114808f90673Sjfb static int
114908f90673Sjfb skipline(FILE *f)
115008f90673Sjfb {
115108f90673Sjfb 	int i, c;
115208f90673Sjfb 
115308f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
115408f90673Sjfb 		continue;
115508f90673Sjfb 	return (i);
115608f90673Sjfb }
115708f90673Sjfb 
115808f90673Sjfb static void
1159b2dc910bSray output(FILE *f1, FILE *f2)
116008f90673Sjfb {
116108f90673Sjfb 	int m, i0, i1, j0, j1;
116208f90673Sjfb 
116308f90673Sjfb 	rewind(f1);
116408f90673Sjfb 	rewind(f2);
1165e4276007Sjfb 	m = diff_len[0];
116608f90673Sjfb 	J[0] = 0;
1167e4276007Sjfb 	J[m + 1] = diff_len[1] + 1;
116808f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
116908f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
117008f90673Sjfb 			i0++;
117108f90673Sjfb 		j0 = J[i0 - 1] + 1;
117208f90673Sjfb 		i1 = i0 - 1;
117308f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
117408f90673Sjfb 			i1++;
117508f90673Sjfb 		j1 = J[i1 + 1] - 1;
117608f90673Sjfb 		J[i1] = j1;
1177b2dc910bSray 		change(f1, f2, i0, i1, j0, j1);
117808f90673Sjfb 	}
117908f90673Sjfb 	if (m == 0)
1180b2dc910bSray 		change(f1, f2, 1, 0, 1, diff_len[1]);
1181f9b67873Sniallo 	if (diff_format == D_IFDEF) {
118208f90673Sjfb 		for (;;) {
118308f90673Sjfb #define	c i0
118408f90673Sjfb 			if ((c = getc(f1)) == EOF)
118508f90673Sjfb 				return;
1186f9b67873Sniallo 			diff_output("%c", c);
118708f90673Sjfb 		}
118808f90673Sjfb #undef c
118908f90673Sjfb 	}
119008f90673Sjfb 	if (anychange != 0) {
1191f9b67873Sniallo 		if (diff_format == D_CONTEXT)
119208f90673Sjfb 			dump_context_vec(f1, f2);
1193f9b67873Sniallo 		else if (diff_format == D_UNIFIED)
119408f90673Sjfb 			dump_unified_vec(f1, f2);
119508f90673Sjfb 	}
119608f90673Sjfb }
119708f90673Sjfb 
119808f90673Sjfb static __inline void
119908f90673Sjfb range(int a, int b, char *separator)
120008f90673Sjfb {
1201f9b67873Sniallo 	diff_output("%d", a > b ? b : a);
120208f90673Sjfb 	if (a < b)
1203f9b67873Sniallo 		diff_output("%s%d", separator, b);
120408f90673Sjfb }
120508f90673Sjfb 
120608f90673Sjfb static __inline void
120708f90673Sjfb uni_range(int a, int b)
120808f90673Sjfb {
120908f90673Sjfb 	if (a < b)
1210f9b67873Sniallo 		diff_output("%d,%d", a, b - a + 1);
121108f90673Sjfb 	else if (a == b)
1212f9b67873Sniallo 		diff_output("%d", b);
121308f90673Sjfb 	else
1214f9b67873Sniallo 		diff_output("%d,0", b);
121508f90673Sjfb }
121608f90673Sjfb 
121708f90673Sjfb static char *
12182a0de57dSjfb preadline(int fd, size_t rlen, off_t off)
121908f90673Sjfb {
122008f90673Sjfb 	char *line;
122108f90673Sjfb 	ssize_t nr;
122208f90673Sjfb 
12230450b43bSjoris 	line = xmalloc(rlen + 1);
12242a0de57dSjfb 	if ((nr = pread(fd, line, rlen, off)) < 0) {
122508f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
122608f90673Sjfb 		return (NULL);
122708f90673Sjfb 	}
122808f90673Sjfb 	line[nr] = '\0';
122908f90673Sjfb 	return (line);
123008f90673Sjfb }
123108f90673Sjfb 
123208f90673Sjfb static int
123308f90673Sjfb ignoreline(char *line)
123408f90673Sjfb {
123508f90673Sjfb 	int ret;
123608f90673Sjfb 
1237f1901a5aSxsa 	ret = regexec(&ignore_re, line, (size_t)0, NULL, 0);
12380450b43bSjoris 	xfree(line);
123908f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
124008f90673Sjfb }
124108f90673Sjfb 
124208f90673Sjfb /*
124308f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
124408f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
124508f90673Sjfb  * are no lines in the from file involved and this means that there were
124608f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
124708f90673Sjfb  * lines missing from the to file.
124808f90673Sjfb  */
124908f90673Sjfb static void
1250b2dc910bSray change(FILE *f1, FILE *f2, int a, int b, int c, int d)
125108f90673Sjfb {
125208f90673Sjfb 	static size_t max_context = 64;
125308f90673Sjfb 	int i;
125408f90673Sjfb 
1255f9b67873Sniallo 	if (diff_format != D_IFDEF && a > b && c > d)
125608f90673Sjfb 		return;
125708f90673Sjfb 	if (ignore_pats != NULL) {
125808f90673Sjfb 		char *line;
125908f90673Sjfb 		/*
126008f90673Sjfb 		 * All lines in the change, insert, or delete must
126108f90673Sjfb 		 * match an ignore pattern for the change to be
126208f90673Sjfb 		 * ignored.
126308f90673Sjfb 		 */
126408f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
126508f90673Sjfb 			for (i = a; i <= b; i++) {
126608f90673Sjfb 				line = preadline(fileno(f1),
126708f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
126808f90673Sjfb 				if (!ignoreline(line))
126908f90673Sjfb 					goto proceed;
127008f90673Sjfb 			}
127108f90673Sjfb 		}
127237af5b8bSxsa 		if ((a > b) || (c <= d)) {	/* Changes and inserts. */
127308f90673Sjfb 			for (i = c; i <= d; i++) {
127408f90673Sjfb 				line = preadline(fileno(f2),
127508f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
127608f90673Sjfb 				if (!ignoreline(line))
127708f90673Sjfb 					goto proceed;
127808f90673Sjfb 			}
127908f90673Sjfb 		}
128008f90673Sjfb 		return;
128108f90673Sjfb 	}
128208f90673Sjfb proceed:
1283f9b67873Sniallo 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
128408f90673Sjfb 		/*
128508f90673Sjfb 		 * Allocate change records as needed.
128608f90673Sjfb 		 */
128708f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
128818501190Sniallo 			struct context_vec *tmp;
128908f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
129008f90673Sjfb 			max_context <<= 1;
12912e0e138cSray 			tmp = xrealloc(context_vec_start, max_context,
12920450b43bSjoris 			    sizeof(struct context_vec));
129318501190Sniallo 			context_vec_start = tmp;
129408f90673Sjfb 			context_vec_end = context_vec_start + max_context;
129508f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
129608f90673Sjfb 		}
129708f90673Sjfb 		if (anychange == 0) {
129808f90673Sjfb 			/*
129908f90673Sjfb 			 * Print the context/unidiff header first time through.
130008f90673Sjfb 			 */
1301f9b67873Sniallo 			diff_output("%s %s	%s",
1302f9b67873Sniallo 			    diff_format == D_CONTEXT ? "***" : "---", diff_file,
130308f90673Sjfb 			    ctime(&stb1.st_mtime));
1304f9b67873Sniallo 			diff_output("%s %s	%s",
1305f9b67873Sniallo 			    diff_format == D_CONTEXT ? "---" : "+++", diff_file,
130608f90673Sjfb 			    ctime(&stb2.st_mtime));
130708f90673Sjfb 			anychange = 1;
130808f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
130908f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
131008f90673Sjfb 			/*
131108f90673Sjfb 			 * If this change is more than 'context' lines from the
131208f90673Sjfb 			 * previous change, dump the record and reset it.
131308f90673Sjfb 			 */
1314f9b67873Sniallo 			if (diff_format == D_CONTEXT)
131508f90673Sjfb 				dump_context_vec(f1, f2);
131608f90673Sjfb 			else
131708f90673Sjfb 				dump_unified_vec(f1, f2);
131808f90673Sjfb 		}
131908f90673Sjfb 		context_vec_ptr++;
132008f90673Sjfb 		context_vec_ptr->a = a;
132108f90673Sjfb 		context_vec_ptr->b = b;
132208f90673Sjfb 		context_vec_ptr->c = c;
132308f90673Sjfb 		context_vec_ptr->d = d;
132408f90673Sjfb 		return;
132508f90673Sjfb 	}
132608f90673Sjfb 	if (anychange == 0)
132708f90673Sjfb 		anychange = 1;
1328f9b67873Sniallo 	switch (diff_format) {
132908f90673Sjfb 	case D_BRIEF:
133008f90673Sjfb 		return;
133108f90673Sjfb 	case D_NORMAL:
133208f90673Sjfb 		range(a, b, ",");
1333f9b67873Sniallo 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1334f9b67873Sniallo 		if (diff_format == D_NORMAL)
133508f90673Sjfb 			range(c, d, ",");
1336f9b67873Sniallo 		diff_output("\n");
133708f90673Sjfb 		break;
1338394180a4Sjfb 	case D_RCSDIFF:
1339394180a4Sjfb 		if (a > b)
1340f9b67873Sniallo 			diff_output("a%d %d\n", b, d - c + 1);
1341394180a4Sjfb 		else {
1342f9b67873Sniallo 			diff_output("d%d %d\n", a, b - a + 1);
1343394180a4Sjfb 
1344394180a4Sjfb 			if (!(c > d))	/* add changed lines */
1345f9b67873Sniallo 				diff_output("a%d %d\n", b, d - c + 1);
1346394180a4Sjfb 		}
1347394180a4Sjfb 		break;
134808f90673Sjfb 	}
1349f9b67873Sniallo 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
135008f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
1351f9b67873Sniallo 		if (a <= b && c <= d && diff_format == D_NORMAL)
1352206543eeSjoris 			diff_output("---\n");
135308f90673Sjfb 	}
1354a44f42a4Sxsa 	fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0);
135508f90673Sjfb 	if (inifdef) {
1356f9b67873Sniallo 		diff_output("#endif /* %s */\n", ifdefname);
135708f90673Sjfb 		inifdef = 0;
135808f90673Sjfb 	}
135908f90673Sjfb }
136008f90673Sjfb 
1361a44f42a4Sxsa static void
136208f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
136308f90673Sjfb {
1364d990145dSray 	long j, nc;
1365d990145dSray 	int i, c, col;
136608f90673Sjfb 
136708f90673Sjfb 	/*
136808f90673Sjfb 	 * When doing #ifdef's, copy down to current line
136908f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
137008f90673Sjfb 	 */
1371f9b67873Sniallo 	if (diff_format == D_IFDEF && oldfile) {
137208f90673Sjfb 		long curpos = ftell(lb);
137308f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
137408f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
137508f90673Sjfb 		for (i = 0; i < nc; i++)
1376f9b67873Sniallo 			diff_output("%c", getc(lb));
137708f90673Sjfb 	}
137808f90673Sjfb 	if (a > b)
1379a44f42a4Sxsa 		return;
1380f9b67873Sniallo 	if (diff_format == D_IFDEF) {
138108f90673Sjfb 		if (inifdef) {
1382f9b67873Sniallo 			diff_output("#else /* %s%s */\n",
138308f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
138408f90673Sjfb 		} else {
138508f90673Sjfb 			if (oldfile)
1386f9b67873Sniallo 				diff_output("#ifndef %s\n", ifdefname);
138708f90673Sjfb 			else
1388f9b67873Sniallo 				diff_output("#ifdef %s\n", ifdefname);
138908f90673Sjfb 		}
139008f90673Sjfb 		inifdef = 1 + oldfile;
139108f90673Sjfb 	}
139208f90673Sjfb 	for (i = a; i <= b; i++) {
139308f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
139408f90673Sjfb 		nc = f[i] - f[i - 1];
1395f9b67873Sniallo 		if (diff_format != D_IFDEF && ch != '\0') {
1396f9b67873Sniallo 			diff_output("%c", ch);
139748dc77e6Sxsa 			if ((Tflag == 1 ) && (diff_format == D_NORMAL ||
13989c5161e4Sjoris 			    diff_format == D_CONTEXT ||
13999c5161e4Sjoris 			    diff_format == D_UNIFIED))
1400f9b67873Sniallo 				diff_output("\t");
1401f9b67873Sniallo 			else if (diff_format != D_UNIFIED)
1402f9b67873Sniallo 				diff_output(" ");
140308f90673Sjfb 		}
140408f90673Sjfb 		col = 0;
1405d990145dSray 		for (j = 0; j < nc; j++) {
140608f90673Sjfb 			if ((c = getc(lb)) == EOF) {
1407f9b67873Sniallo 				if (diff_format == D_RCSDIFF)
1408b9a6f00eSxsa 					cvs_log(LP_WARN,
1409b9a6f00eSxsa 					    "No newline at end of file");
1410394180a4Sjfb 				else
14119c5161e4Sjoris 					diff_output("\n\\ No newline at end of "
14129c5161e4Sjoris 					    "file");
1413a44f42a4Sxsa 				return;
141408f90673Sjfb 			}
141548dc77e6Sxsa 			if ((c == '\t') && (tflag == 1)) {
141608f90673Sjfb 				do {
1417f9b67873Sniallo 					diff_output(" ");
141808f90673Sjfb 				} while (++col & 7);
141908f90673Sjfb 			} else {
1420f9b67873Sniallo 				diff_output("%c", c);
142108f90673Sjfb 				col++;
142208f90673Sjfb 			}
142308f90673Sjfb 		}
142408f90673Sjfb 	}
142508f90673Sjfb }
142608f90673Sjfb 
142708f90673Sjfb /*
142808f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
142908f90673Sjfb  */
143008f90673Sjfb static int
143108f90673Sjfb readhash(FILE *f)
143208f90673Sjfb {
143308f90673Sjfb 	int i, t, space;
143408f90673Sjfb 	int sum;
143508f90673Sjfb 
143608f90673Sjfb 	sum = 1;
143708f90673Sjfb 	space = 0;
143848dc77e6Sxsa 	if ((bflag != 1) && (wflag != 1)) {
143948dc77e6Sxsa 		if (iflag == 1)
144008f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
144108f90673Sjfb 				if (t == EOF) {
144208f90673Sjfb 					if (i == 0)
144308f90673Sjfb 						return (0);
144408f90673Sjfb 					break;
144508f90673Sjfb 				}
144608f90673Sjfb 				sum = sum * 127 + chrtran[t];
144708f90673Sjfb 			}
144808f90673Sjfb 		else
144908f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
145008f90673Sjfb 				if (t == EOF) {
145108f90673Sjfb 					if (i == 0)
145208f90673Sjfb 						return (0);
145308f90673Sjfb 					break;
145408f90673Sjfb 				}
145508f90673Sjfb 				sum = sum * 127 + t;
145608f90673Sjfb 			}
145708f90673Sjfb 	} else {
145808f90673Sjfb 		for (i = 0;;) {
145908f90673Sjfb 			switch (t = getc(f)) {
146008f90673Sjfb 			case '\t':
146108f90673Sjfb 			case ' ':
146208f90673Sjfb 				space++;
146308f90673Sjfb 				continue;
146408f90673Sjfb 			default:
146548dc77e6Sxsa 				if ((space != 0) && (wflag != 1)) {
146608f90673Sjfb 					i++;
146708f90673Sjfb 					space = 0;
146808f90673Sjfb 				}
146908f90673Sjfb 				sum = sum * 127 + chrtran[t];
147008f90673Sjfb 				i++;
147108f90673Sjfb 				continue;
147208f90673Sjfb 			case EOF:
147308f90673Sjfb 				if (i == 0)
147408f90673Sjfb 					return (0);
147508f90673Sjfb 				/* FALLTHROUGH */
147608f90673Sjfb 			case '\n':
147708f90673Sjfb 				break;
147808f90673Sjfb 			}
147908f90673Sjfb 			break;
148008f90673Sjfb 		}
148108f90673Sjfb 	}
148208f90673Sjfb 	/*
148308f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
148408f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
148508f90673Sjfb 	 */
148608f90673Sjfb 	return (sum == 0 ? 1 : sum);
148708f90673Sjfb }
148808f90673Sjfb 
148908f90673Sjfb static int
149008f90673Sjfb asciifile(FILE *f)
149108f90673Sjfb {
149208f90673Sjfb 	char buf[BUFSIZ];
1493d990145dSray 	size_t i, cnt;
149408f90673Sjfb 
149548dc77e6Sxsa 	if ((aflag == 1) || (f == NULL))
149608f90673Sjfb 		return (1);
149708f90673Sjfb 
149808f90673Sjfb 	rewind(f);
1499f1901a5aSxsa 	cnt = fread(buf, (size_t)1, sizeof(buf), f);
150008f90673Sjfb 	for (i = 0; i < cnt; i++)
150108f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
150208f90673Sjfb 			return (0);
150308f90673Sjfb 	return (1);
150408f90673Sjfb }
150508f90673Sjfb 
15065e78344dSjfb static char*
15075e78344dSjfb match_function(const long *f, int pos, FILE *fp)
15085e78344dSjfb {
15095e78344dSjfb 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
15105e78344dSjfb 	size_t nc;
15115e78344dSjfb 	int last = lastline;
15125e78344dSjfb 	char *p;
15135e78344dSjfb 
15145e78344dSjfb 	lastline = pos;
15155e78344dSjfb 	while (pos > last) {
15165e78344dSjfb 		fseek(fp, f[pos - 1], SEEK_SET);
15175e78344dSjfb 		nc = f[pos] - f[pos - 1];
15185e78344dSjfb 		if (nc >= sizeof(buf))
15195e78344dSjfb 			nc = sizeof(buf) - 1;
1520f1901a5aSxsa 		nc = fread(buf, (size_t)1, nc, fp);
15215e78344dSjfb 		if (nc > 0) {
15225e78344dSjfb 			buf[nc] = '\0';
1523634926d6Sniallo 			p = strchr((const char *)buf, '\n');
15245e78344dSjfb 			if (p != NULL)
15255e78344dSjfb 				*p = '\0';
15265e78344dSjfb 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
15274c06e5f6Sreyk 				strlcpy(lastbuf, (const char *)buf,
15284c06e5f6Sreyk 				    sizeof lastbuf);
15295e78344dSjfb 				lastmatchline = pos;
15305e78344dSjfb 				return lastbuf;
15315e78344dSjfb 			}
15325e78344dSjfb 		}
15335e78344dSjfb 		pos--;
15345e78344dSjfb 	}
15355e78344dSjfb 	return (lastmatchline > 0) ? lastbuf : NULL;
15365e78344dSjfb }
15375e78344dSjfb 
153808f90673Sjfb 
153908f90673Sjfb /* dump accumulated "context" diff changes */
154008f90673Sjfb static void
154108f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
154208f90673Sjfb {
154308f90673Sjfb 	struct context_vec *cvp = context_vec_start;
154408f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
154508f90673Sjfb 	int a, b, c, d;
15465e78344dSjfb 	char ch, *f;
154708f90673Sjfb 
154808f90673Sjfb 	if (context_vec_start > context_vec_ptr)
154908f90673Sjfb 		return;
155008f90673Sjfb 
155108f90673Sjfb 	b = d = 0;		/* gcc */
1552dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1553e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1554dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1555e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
155608f90673Sjfb 
1557f9b67873Sniallo 	diff_output("***************");
155848dc77e6Sxsa 	if (pflag == 1) {
15595e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
15605e78344dSjfb 		if (f != NULL) {
1561f9b67873Sniallo 			diff_output(" ");
1562f9b67873Sniallo 			diff_output("%s", f);
15635e78344dSjfb 		}
15645e78344dSjfb 	}
1565f9b67873Sniallo 	diff_output("\n*** ");
156608f90673Sjfb 	range(lowa, upb, ",");
1567f9b67873Sniallo 	diff_output(" ****\n");
156808f90673Sjfb 
156908f90673Sjfb 	/*
157008f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
157108f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
157208f90673Sjfb 	 * the "old" lines as context in the "new" list).
157308f90673Sjfb 	 */
157408f90673Sjfb 	do_output = 0;
157508f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
157608f90673Sjfb 		if (cvp->a <= cvp->b) {
157708f90673Sjfb 			cvp = context_vec_start;
157808f90673Sjfb 			do_output++;
157908f90673Sjfb 			break;
158008f90673Sjfb 		}
158137af5b8bSxsa 	if (do_output != 0) {
158208f90673Sjfb 		while (cvp <= context_vec_ptr) {
158308f90673Sjfb 			a = cvp->a;
158408f90673Sjfb 			b = cvp->b;
158508f90673Sjfb 			c = cvp->c;
158608f90673Sjfb 			d = cvp->d;
158708f90673Sjfb 
158808f90673Sjfb 			if (a <= b && c <= d)
158908f90673Sjfb 				ch = 'c';
159008f90673Sjfb 			else
159108f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
159208f90673Sjfb 
159308f90673Sjfb 			if (ch == 'a')
159408f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
159508f90673Sjfb 			else {
159608f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
159708f90673Sjfb 				fetch(ixold, a, b, f1,
159808f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
159908f90673Sjfb 			}
160008f90673Sjfb 			lowa = b + 1;
160108f90673Sjfb 			cvp++;
160208f90673Sjfb 		}
160308f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
160408f90673Sjfb 	}
160508f90673Sjfb 	/* output changes to the "new" file */
1606f9b67873Sniallo 	diff_output("--- ");
160708f90673Sjfb 	range(lowc, upd, ",");
1608f9b67873Sniallo 	diff_output(" ----\n");
160908f90673Sjfb 
161008f90673Sjfb 	do_output = 0;
161108f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
161208f90673Sjfb 		if (cvp->c <= cvp->d) {
161308f90673Sjfb 			cvp = context_vec_start;
161408f90673Sjfb 			do_output++;
161508f90673Sjfb 			break;
161608f90673Sjfb 		}
161737af5b8bSxsa 	if (do_output != 0) {
161808f90673Sjfb 		while (cvp <= context_vec_ptr) {
161908f90673Sjfb 			a = cvp->a;
162008f90673Sjfb 			b = cvp->b;
162108f90673Sjfb 			c = cvp->c;
162208f90673Sjfb 			d = cvp->d;
162308f90673Sjfb 
162408f90673Sjfb 			if (a <= b && c <= d)
162508f90673Sjfb 				ch = 'c';
162608f90673Sjfb 			else
162708f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
162808f90673Sjfb 
162908f90673Sjfb 			if (ch == 'd')
163008f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
163108f90673Sjfb 			else {
163208f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
163308f90673Sjfb 				fetch(ixnew, c, d, f2,
163408f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
163508f90673Sjfb 			}
163608f90673Sjfb 			lowc = d + 1;
163708f90673Sjfb 			cvp++;
163808f90673Sjfb 		}
163908f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
164008f90673Sjfb 	}
164108f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
164208f90673Sjfb }
164308f90673Sjfb 
164408f90673Sjfb /* dump accumulated "unified" diff changes */
164508f90673Sjfb static void
164608f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
164708f90673Sjfb {
164808f90673Sjfb 	struct context_vec *cvp = context_vec_start;
164908f90673Sjfb 	int lowa, upb, lowc, upd;
165008f90673Sjfb 	int a, b, c, d;
16515e78344dSjfb 	char ch, *f;
165208f90673Sjfb 
165308f90673Sjfb 	if (context_vec_start > context_vec_ptr)
165408f90673Sjfb 		return;
165508f90673Sjfb 
165608f90673Sjfb 	b = d = 0;		/* gcc */
1657dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1658e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1659dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1660e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
166108f90673Sjfb 
1662f9b67873Sniallo 	diff_output("@@ -");
166308f90673Sjfb 	uni_range(lowa, upb);
1664f9b67873Sniallo 	diff_output(" +");
166508f90673Sjfb 	uni_range(lowc, upd);
1666f9b67873Sniallo 	diff_output(" @@");
166748dc77e6Sxsa 	if (pflag == 1) {
16685e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
16695e78344dSjfb 		if (f != NULL) {
1670f9b67873Sniallo 			diff_output(" ");
1671f9b67873Sniallo 			diff_output("%s", f);
16725e78344dSjfb 		}
16735e78344dSjfb 	}
1674f9b67873Sniallo 	diff_output("\n");
167508f90673Sjfb 
167608f90673Sjfb 	/*
167708f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
167808f90673Sjfb 	 * are printed together.
167908f90673Sjfb 	 */
168008f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
168108f90673Sjfb 		a = cvp->a;
168208f90673Sjfb 		b = cvp->b;
168308f90673Sjfb 		c = cvp->c;
168408f90673Sjfb 		d = cvp->d;
168508f90673Sjfb 
168608f90673Sjfb 		/*
168708f90673Sjfb 		 * c: both new and old changes
168808f90673Sjfb 		 * d: only changes in the old file
168908f90673Sjfb 		 * a: only changes in the new file
169008f90673Sjfb 		 */
169108f90673Sjfb 		if (a <= b && c <= d)
169208f90673Sjfb 			ch = 'c';
169308f90673Sjfb 		else
169408f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
169508f90673Sjfb 
169608f90673Sjfb 		switch (ch) {
169708f90673Sjfb 		case 'c':
169808f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
169908f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
170008f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
170108f90673Sjfb 			break;
170208f90673Sjfb 		case 'd':
170308f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
170408f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
170508f90673Sjfb 			break;
170608f90673Sjfb 		case 'a':
170708f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
170808f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
170908f90673Sjfb 			break;
171008f90673Sjfb 		}
171108f90673Sjfb 		lowa = b + 1;
171208f90673Sjfb 		lowc = d + 1;
171308f90673Sjfb 	}
171408f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
171508f90673Sjfb 
171608f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
171708f90673Sjfb }
1718f9b67873Sniallo 
171901af718aSjoris void
1720f9b67873Sniallo diff_output(const char *fmt, ...)
1721f9b67873Sniallo {
1722f9b67873Sniallo 	va_list vap;
1723f9b67873Sniallo 	char *str;
1724f9b67873Sniallo 
1725f9b67873Sniallo 	va_start(vap, fmt);
1726f9b67873Sniallo 	vasprintf(&str, fmt, vap);
1727f9b67873Sniallo 	if (diffbuf != NULL)
1728f9b67873Sniallo 		cvs_buf_append(diffbuf, str, strlen(str));
1729f9b67873Sniallo 	else
1730f9b67873Sniallo 		cvs_printf("%s", str);
17310450b43bSjoris 	xfree(str);
1732f9b67873Sniallo 	va_end(vap);
1733f9b67873Sniallo }
1734