xref: /openbsd-src/usr.bin/cvs/diff.c (revision b9a6f00ee0621a2fa5965cb2342679ba19d523e3)
1*b9a6f00eSxsa /*	$OpenBSD: diff.c,v 1.76 2006/01/02 17:06:10 xsa 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 
13708f90673Sjfb struct cand {
13808f90673Sjfb 	int	x;
13908f90673Sjfb 	int	y;
14008f90673Sjfb 	int	pred;
14108f90673Sjfb } cand;
14208f90673Sjfb 
14308f90673Sjfb struct line {
14408f90673Sjfb 	int	serial;
14508f90673Sjfb 	int	value;
14608f90673Sjfb } *file[2];
14708f90673Sjfb 
14808f90673Sjfb /*
14908f90673Sjfb  * The following struct is used to record change in formation when
15008f90673Sjfb  * doing a "context" or "unified" diff.  (see routine "change" to
15108f90673Sjfb  * understand the highly mnemonic field names)
15208f90673Sjfb  */
15308f90673Sjfb struct context_vec {
15408f90673Sjfb 	int	a;	/* start line in old file */
15508f90673Sjfb 	int	b;	/* end line in old file */
15608f90673Sjfb 	int	c;	/* start line in new file */
15708f90673Sjfb 	int	d;	/* end line in new file */
15808f90673Sjfb };
15908f90673Sjfb 
160dc6a6879Sjfb struct diff_arg {
161dc6a6879Sjfb 	char	*rev1;
162dc6a6879Sjfb 	char	*rev2;
163dc6a6879Sjfb 	char	*date1;
164dc6a6879Sjfb 	char	*date2;
165dc6a6879Sjfb };
166dc6a6879Sjfb 
167af5bb824Sniallo #if !defined(RCSPROG)
168e4276007Sjfb static int	cvs_diff_init(struct cvs_cmd *, int, char **, int *);
169e4276007Sjfb static int	cvs_diff_remote(CVSFILE *, void *);
170e4276007Sjfb static int	cvs_diff_local(CVSFILE *, void *);
171e4276007Sjfb static int	cvs_diff_pre_exec(struct cvsroot *);
172e4276007Sjfb static int	cvs_diff_cleanup(void);
173af5bb824Sniallo #endif
17416cfc147Sjoris 
17508f90673Sjfb static void	 output(const char *, FILE *, const char *, FILE *);
17608f90673Sjfb static void	 check(FILE *, FILE *);
17708f90673Sjfb static void	 range(int, int, char *);
17808f90673Sjfb static void	 uni_range(int, int);
17908f90673Sjfb static void	 dump_context_vec(FILE *, FILE *);
18008f90673Sjfb static void	 dump_unified_vec(FILE *, FILE *);
1817f535ec4Sjfb static int	 prepare(int, FILE *, off_t);
18208f90673Sjfb static void	 prune(void);
18308f90673Sjfb static void	 equiv(struct line *, int, struct line *, int, int *);
18408f90673Sjfb static void	 unravel(int);
18508f90673Sjfb static void	 unsort(struct line *, int, int *);
186ff1f7a8eSxsa static void	 change(const char *, FILE *, const char *, FILE *, int,
187ff1f7a8eSxsa 		    int, int, int);
18808f90673Sjfb static void	 sort(struct line *, int);
18908f90673Sjfb static int	 ignoreline(char *);
19008f90673Sjfb static int	 asciifile(FILE *);
19108f90673Sjfb static int	 fetch(long *, int, int, FILE *, int, int);
19208f90673Sjfb static int	 newcand(int, int, int);
19308f90673Sjfb static int	 search(int *, int, int);
19408f90673Sjfb static int	 skipline(FILE *);
19508f90673Sjfb static int	 isqrt(int);
19608f90673Sjfb static int	 stone(int *, int, int *, int *);
19708f90673Sjfb static int	 readhash(FILE *);
19808f90673Sjfb static int	 files_differ(FILE *, FILE *);
1995e78344dSjfb static char	*match_function(const long *, int, FILE *);
20008f90673Sjfb static char	*preadline(int, size_t, off_t);
20108f90673Sjfb 
20208f90673Sjfb 
203af5bb824Sniallo #if !defined(RCSPROG)
204af5bb824Sniallo static int Nflag;
205af5bb824Sniallo #endif
206af5bb824Sniallo static int aflag, bflag, dflag, iflag, pflag, tflag, Tflag, wflag;
20778de3304Sniallo static int context = 3;
208f9b67873Sniallo int diff_format = D_NORMAL;
2091ff4dac1Sjoris char *diff_file = NULL;
210c60b216dSxsa char diffargs[128];
21108f90673Sjfb static struct stat stb1, stb2;
212af5bb824Sniallo static char *ifdefname, *ignore_pats;
21308f90673Sjfb regex_t ignore_re;
21408f90673Sjfb 
21508f90673Sjfb static int  *J;			/* will be overlaid on class */
21608f90673Sjfb static int  *class;		/* will be overlaid on file[0] */
21708f90673Sjfb static int  *klist;		/* will be overlaid on file[0] after class */
21808f90673Sjfb static int  *member;		/* will be overlaid on file[1] */
21908f90673Sjfb static int   clen;
22008f90673Sjfb static int   inifdef;		/* whether or not we are in a #ifdef block */
221e4276007Sjfb static int   diff_len[2];
22208f90673Sjfb static int   pref, suff;	/* length of prefix and suffix */
22308f90673Sjfb static int   slen[2];
22408f90673Sjfb static int   anychange;
22508f90673Sjfb static long *ixnew;		/* will be overlaid on file[1] */
22608f90673Sjfb static long *ixold;		/* will be overlaid on klist */
22708f90673Sjfb static struct cand *clist;	/* merely a free storage pot for candidates */
22808f90673Sjfb static int   clistlen;		/* the length of clist */
22908f90673Sjfb static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
23008f90673Sjfb static u_char *chrtran;		/* translation table for case-folding */
23108f90673Sjfb static struct context_vec *context_vec_start;
23208f90673Sjfb static struct context_vec *context_vec_end;
23308f90673Sjfb static struct context_vec *context_vec_ptr;
23408f90673Sjfb 
23508f90673Sjfb #define FUNCTION_CONTEXT_SIZE	41
2365e78344dSjfb static char lastbuf[FUNCTION_CONTEXT_SIZE];
23708f90673Sjfb static int  lastline;
23808f90673Sjfb static int  lastmatchline;
23901af718aSjoris BUF  *diffbuf = NULL;
240f9b67873Sniallo 
24108f90673Sjfb /*
24208f90673Sjfb  * chrtran points to one of 2 translation tables: cup2low if folding upper to
24308f90673Sjfb  * lower case clow2low if not folding case
24408f90673Sjfb  */
24508f90673Sjfb u_char clow2low[256] = {
24608f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
24708f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
24808f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
24908f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
25008f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
25108f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
25208f90673Sjfb 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
25308f90673Sjfb 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
25408f90673Sjfb 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
25508f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
25608f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
25708f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
25808f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
25908f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
26008f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
26108f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
26208f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
26308f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
26408f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
26508f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
26608f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
26708f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
26808f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
26908f90673Sjfb 	0xfd, 0xfe, 0xff
27008f90673Sjfb };
27108f90673Sjfb 
27208f90673Sjfb u_char cup2low[256] = {
27308f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
27408f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
27508f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
27608f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
27708f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
27808f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
27908f90673Sjfb 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
28008f90673Sjfb 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
28108f90673Sjfb 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
28208f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
28308f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
28408f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
28508f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
28608f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
28708f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
28808f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
28908f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
29008f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
29108f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
29208f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
29308f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
29408f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
29508f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
29608f90673Sjfb 	0xfd, 0xfe, 0xff
29708f90673Sjfb };
29808f90673Sjfb 
299af5bb824Sniallo #if !defined(RCSPROG)
300e4276007Sjfb struct cvs_cmd cvs_cmd_diff = {
301e4276007Sjfb 	CVS_OP_DIFF, CVS_REQ_DIFF, "diff",
302e4276007Sjfb 	{ "di", "dif" },
303e4276007Sjfb 	"Show differences between revisions",
304c9150269Sxsa 	"[-cilNnpu] [[-D date] [-r rev] [-D date2 | -r rev2]] "
305c9150269Sxsa 	"[-k mode] [file ...]",
306c9150269Sxsa 	"cD:iklNnpr:Ru",
30716cfc147Sjoris 	NULL,
30816cfc147Sjoris 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
309e4276007Sjfb 	cvs_diff_init,
310e4276007Sjfb 	cvs_diff_pre_exec,
311e4276007Sjfb 	cvs_diff_remote,
312e4276007Sjfb 	cvs_diff_local,
313e4276007Sjfb 	NULL,
314e4276007Sjfb 	cvs_diff_cleanup,
315e4276007Sjfb 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
316e4276007Sjfb };
317e4276007Sjfb 
318e4276007Sjfb 
319e4276007Sjfb struct cvs_cmd cvs_cmd_rdiff = {
320e4276007Sjfb 	CVS_OP_RDIFF, CVS_REQ_DIFF, "rdiff",
321c9150269Sxsa 	{ "pa", "patch" },
322e4276007Sjfb 	"Create 'patch' format diffs between releases",
323c9150269Sxsa 	"[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev "
324c9150269Sxsa 	"[-D date2 | -rev2] module ...",
325c9150269Sxsa 	"cD:flRr:stuV:",
326e4276007Sjfb 	NULL,
327e4276007Sjfb 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
328e4276007Sjfb 	cvs_diff_init,
329e4276007Sjfb 	cvs_diff_pre_exec,
330e4276007Sjfb 	cvs_diff_remote,
331e4276007Sjfb 	cvs_diff_local,
332e4276007Sjfb 	NULL,
333e4276007Sjfb 	cvs_diff_cleanup,
33444381dcbSjoris 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
33516cfc147Sjoris };
336af5bb824Sniallo #endif
33708f90673Sjfb 
338af5bb824Sniallo #if !defined(RCSPROG)
33916cfc147Sjoris static struct diff_arg *dap = NULL;
34016cfc147Sjoris static int recurse;
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));
37008f90673Sjfb 			recurse = 0;
371e4276007Sjfb 			cvs_cmd_diff.file_flags &= ~CF_RECURSE;
37208f90673Sjfb 			break;
37308f90673Sjfb 		case 'i':
374f5638424Sjfb 			strlcat(diffargs, " -i", sizeof(diffargs));
37508f90673Sjfb 			iflag = 1;
37608f90673Sjfb 			break;
377c710bc5aSjfb 		case 'N':
378c710bc5aSjfb 			strlcat(diffargs, " -N", sizeof(diffargs));
379c710bc5aSjfb 			Nflag = 1;
380c710bc5aSjfb 			break;
381394180a4Sjfb 		case 'n':
382394180a4Sjfb 			strlcat(diffargs, " -n", sizeof(diffargs));
383f9b67873Sniallo 			diff_format = D_RCSDIFF;
384394180a4Sjfb 			break;
3855e78344dSjfb 		case 'p':
3865e78344dSjfb 			strlcat(diffargs, " -p", sizeof(diffargs));
3875e78344dSjfb 			pflag = 1;
3885e78344dSjfb 			break;
38908f90673Sjfb 		case 'r':
39016cfc147Sjoris 			if ((dap->rev1 == NULL) && (dap->date1 == NULL)) {
39116cfc147Sjoris 				dap->rev1 = optarg;
39216cfc147Sjoris 			} else if ((dap->rev2 == NULL) &&
39316cfc147Sjoris 			    (dap->date2 == NULL)) {
39416cfc147Sjoris 				dap->rev2 = optarg;
39516cfc147Sjoris 			} else {
39608f90673Sjfb 				cvs_log(LP_ERR,
39708f90673Sjfb 				    "no more than two revisions/dates can "
39808f90673Sjfb 				    "be specified");
39931274bbfSjoris 				return (CVS_EX_USAGE);
40008f90673Sjfb 			}
40108f90673Sjfb 			break;
402f203c484Sjoris 		case 'R':
403e4276007Sjfb 			cvs_cmd_diff.file_flags |= CF_RECURSE;
404f203c484Sjoris 			break;
40508f90673Sjfb 		case 'u':
406f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
407f9b67873Sniallo 			diff_format = D_UNIFIED;
40808f90673Sjfb 			break;
40908f90673Sjfb 		default:
41031274bbfSjoris 			return (CVS_EX_USAGE);
41108f90673Sjfb 		}
41208f90673Sjfb 	}
41308f90673Sjfb 
41416cfc147Sjoris 	*arg = optind;
415dc6a6879Sjfb 	return (0);
416dc6a6879Sjfb }
417dc6a6879Sjfb 
41816cfc147Sjoris int
41916cfc147Sjoris cvs_diff_cleanup(void)
42016cfc147Sjoris {
421e4276007Sjfb 	if (dap != NULL) {
4220450b43bSjoris 		xfree(dap);
423e4276007Sjfb 		dap = NULL;
424e4276007Sjfb 	}
42516cfc147Sjoris 	return (0);
42616cfc147Sjoris }
427dc6a6879Sjfb 
428dc6a6879Sjfb /*
429e4276007Sjfb  * cvs_diff_pre_exec()
430dc6a6879Sjfb  *
431dc6a6879Sjfb  */
432dc6a6879Sjfb int
433e4276007Sjfb cvs_diff_pre_exec(struct cvsroot *root)
434dc6a6879Sjfb {
435e4276007Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
43608f90673Sjfb 		/* send the flags */
4377e393898Sjoris 		if (Nflag == 1)
4387e393898Sjoris 			cvs_sendarg(root, "-N", 0);
4397e393898Sjoris 		if (pflag == 1)
4407e393898Sjoris 			cvs_sendarg(root, "-p", 0);
4415e78344dSjfb 
4427e393898Sjoris 		if (diff_format == D_CONTEXT)
4437e393898Sjoris 			cvs_sendarg(root, "-c", 0);
4447e393898Sjoris 		else if (diff_format == D_UNIFIED)
4457e393898Sjoris 			cvs_sendarg(root, "-u", 0);
44608f90673Sjfb 
447dc6a6879Sjfb 		if (dap->rev1 != NULL) {
4487e393898Sjoris 			cvs_sendarg(root, "-r", 0);
4497e393898Sjoris 			cvs_sendarg(root, dap->rev1, 0);
4503917c9bfSderaadt 		} else if (dap->date1 != NULL) {
4517e393898Sjoris 			cvs_sendarg(root, "-D", 0);
4527e393898Sjoris 			cvs_sendarg(root, dap->date1, 0);
45308f90673Sjfb 		}
454dc6a6879Sjfb 		if (dap->rev2 != NULL) {
4557e393898Sjoris 			cvs_sendarg(root, "-r", 0);
4567e393898Sjoris 			cvs_sendarg(root, dap->rev2, 0);
4573917c9bfSderaadt 		} else if (dap->date2 != NULL) {
4587e393898Sjoris 			cvs_sendarg(root, "-D", 0);
4597e393898Sjoris 			cvs_sendarg(root, dap->date2, 0);
46008f90673Sjfb 		}
461e4276007Sjfb 	}
46208f90673Sjfb 
46308f90673Sjfb 	return (0);
46408f90673Sjfb }
46508f90673Sjfb 
46608f90673Sjfb 
46708f90673Sjfb /*
46808f90673Sjfb  * cvs_diff_file()
46908f90673Sjfb  *
47008f90673Sjfb  * Diff a single file.
47108f90673Sjfb  */
472e4276007Sjfb static int
473e4276007Sjfb cvs_diff_remote(struct cvs_file *cfp, void *arg)
47408f90673Sjfb {
475e4276007Sjfb 	char *dir, *repo;
476e4276007Sjfb 	char fpath[MAXPATHLEN], dfpath[MAXPATHLEN];
477dc6a6879Sjfb 	struct cvsroot *root;
478dc6a6879Sjfb 
479dc6a6879Sjfb 	if (cfp->cf_type == DT_DIR) {
480895e6cf6Sjfb 		if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
481bb029937Sjfb 			root = cfp->cf_parent->cf_root;
482b904ba2eSjoris 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
4833917c9bfSderaadt 		} else {
484bb029937Sjfb 			root = cfp->cf_root;
48516cfc147Sjoris #if 0
486dc6a6879Sjfb 			if ((cfp->cf_parent == NULL) ||
487bb029937Sjfb 			    (root != cfp->cf_parent->cf_root)) {
488dc6a6879Sjfb 				cvs_connect(root);
489e4276007Sjfb 				cvs_diff_pre_exec(root);
490dc6a6879Sjfb 			}
49116cfc147Sjoris #endif
492dc6a6879Sjfb 
493dc6a6879Sjfb 			cvs_senddir(root, cfp);
494895e6cf6Sjfb 		}
495895e6cf6Sjfb 
496dc6a6879Sjfb 		return (0);
497dc6a6879Sjfb 	}
49808f90673Sjfb 
4992d5b8b1dSjfb 	if (cfp->cf_cvstat == CVS_FST_LOST) {
500b904ba2eSjoris 		cvs_log(LP_WARN, "cannot find file %s", cfp->cf_name);
5012d5b8b1dSjfb 		return (0);
5022d5b8b1dSjfb 	}
5032d5b8b1dSjfb 
504c710bc5aSjfb 	diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath));
505895e6cf6Sjfb 
506dc6a6879Sjfb 	if (cfp->cf_parent != NULL) {
507c710bc5aSjfb 		dir = cvs_file_getpath(cfp->cf_parent, dfpath, sizeof(dfpath));
508bb029937Sjfb 		root = cfp->cf_parent->cf_root;
509bb029937Sjfb 		repo = cfp->cf_parent->cf_repo;
5103917c9bfSderaadt 	} else {
511dc6a6879Sjfb 		dir = ".";
512895e6cf6Sjfb 		root = NULL;
513dc6a6879Sjfb 		repo = NULL;
51408f90673Sjfb 	}
51508f90673Sjfb 
516dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
517e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
518dc6a6879Sjfb 		return (0);
51908f90673Sjfb 	}
52008f90673Sjfb 
5217e393898Sjoris 	cvs_sendentry(root, cfp);
52208f90673Sjfb 
523dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UPTODATE) {
524e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name);
52508f90673Sjfb 		return (0);
52608f90673Sjfb 	}
52708f90673Sjfb 
52808f90673Sjfb 	/* at this point, the file is modified */
5297e393898Sjoris 	cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name);
5307e393898Sjoris 	cvs_sendfile(root, diff_file);
531e4276007Sjfb 
532e4276007Sjfb 	return (0);
533e4276007Sjfb }
534e4276007Sjfb 
535e4276007Sjfb static int
536e4276007Sjfb cvs_diff_local(CVSFILE *cf, void *arg)
537e4276007Sjfb {
538e4276007Sjfb 	int len;
539e4276007Sjfb 	char *repo, buf[64];
540e4276007Sjfb 	char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
541e4276007Sjfb 	char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN];
542e4276007Sjfb 	BUF *b1, *b2;
543e4276007Sjfb 	RCSNUM *r1, *r2;
544e4276007Sjfb 	RCSFILE *rf;
545e4276007Sjfb 	struct cvsroot *root;
546bf951d2dSniallo 	struct timeval tv[2], tv2[2];
547bf951d2dSniallo 
548bf951d2dSniallo 	memset(&tv, 0, sizeof(tv));
549bf951d2dSniallo 	memset(&tv2, 0, sizeof(tv2));
550e4276007Sjfb 
551e4276007Sjfb 	rf = NULL;
552e4276007Sjfb 	root = CVS_DIR_ROOT(cf);
553e4276007Sjfb 	repo = CVS_DIR_REPO(cf);
554e4276007Sjfb 	diff_file = cvs_file_getpath(cf, fpath, sizeof(fpath));
555e4276007Sjfb 
556e4276007Sjfb 	if (cf->cf_type == DT_DIR) {
557b87c59bdSxsa 		if (verbosity > 1)
558246675edSxsa 			cvs_log(LP_NOTICE, "Diffing %s", fpath);
559e4276007Sjfb 		return (0);
560e4276007Sjfb 	}
561e4276007Sjfb 
562e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_LOST) {
563e4276007Sjfb 		cvs_log(LP_WARN, "cannot find file %s", cf->cf_name);
564e4276007Sjfb 		return (0);
565e4276007Sjfb 	}
566e4276007Sjfb 
567e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_UNKNOWN) {
568e4276007Sjfb 		cvs_log(LP_WARN, "I know nothing about %s", diff_file);
569e4276007Sjfb 		return (0);
5705c0cd766Sniallo 	} else if (cf->cf_cvstat == CVS_FST_UPTODATE)
571e4276007Sjfb 		return (0);
572e4276007Sjfb 
573e4276007Sjfb 	/* at this point, the file is modified */
574e4276007Sjfb 	len = snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
575dc6a6879Sjfb 	    root->cr_dir, repo, diff_file, RCS_FILE_EXT);
576e4276007Sjfb 	if (len == -1 || len >= (int)sizeof(rcspath)) {
57727b85f85Sxsa 		errno = ENAMETOOLONG;
57827b85f85Sxsa 		cvs_log(LP_ERRNO, "%s", rcspath);
57901b3d77aSjoris 		return (CVS_EX_DATA);
58027b85f85Sxsa 	}
58108f90673Sjfb 
5821b6534b8Sjfb 	rf = rcs_open(rcspath, RCS_READ);
583dc6a6879Sjfb 	if (rf == NULL) {
58431274bbfSjoris 		return (CVS_EX_DATA);
585dc6a6879Sjfb 	}
58608f90673Sjfb 
587dc6a6879Sjfb 	cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file,
58808f90673Sjfb 	    RCS_DIFF_DIV, rcspath);
58908f90673Sjfb 
590dc6a6879Sjfb 	if (dap->rev1 == NULL)
591e4276007Sjfb 		r1 = cf->cf_lrev;
59208f90673Sjfb 	else {
59325b74b48Sjfb 		if ((r1 = rcsnum_parse(dap->rev1)) == NULL) {
59464672f74Sjoris 			rcs_close(rf);
59531274bbfSjoris 			return (CVS_EX_DATA);
5967f535ec4Sjfb 		}
59708f90673Sjfb 	}
59808f90673Sjfb 
599dc6a6879Sjfb 	cvs_printf("retrieving revision %s\n",
60008f90673Sjfb 	    rcsnum_tostr(r1, buf, sizeof(buf)));
60108f90673Sjfb 	b1 = rcs_getrev(rf, r1);
60208f90673Sjfb 
60395ae1173Sjoris 	if (b1 == NULL) {
604289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
60595ae1173Sjoris 		    rcsnum_tostr(r1, buf, sizeof(buf)));
60695ae1173Sjoris 		if (r1 != cf->cf_lrev)
60795ae1173Sjoris 			rcsnum_free(r1);
60864672f74Sjoris 		rcs_close(rf);
60995ae1173Sjoris 		return (CVS_EX_DATA);
61095ae1173Sjoris 	}
611bf951d2dSniallo 	tv[0].tv_sec = (long)rcs_rev_getdate(rf, r1);
612bf951d2dSniallo 	tv[1].tv_sec = tv[0].tv_sec;
61395ae1173Sjoris 
614e4276007Sjfb 	if (r1 != cf->cf_lrev)
6157f535ec4Sjfb 		rcsnum_free(r1);
6167f535ec4Sjfb 
617dc6a6879Sjfb 	if (dap->rev2 != NULL) {
618dc6a6879Sjfb 		cvs_printf("retrieving revision %s\n", dap->rev2);
61925b74b48Sjfb 		if ((r2 = rcsnum_parse(dap->rev2)) == NULL) {
62064672f74Sjoris 			rcs_close(rf);
62131274bbfSjoris 			return (CVS_EX_DATA);
6227f535ec4Sjfb 		}
62308f90673Sjfb 		b2 = rcs_getrev(rf, r2);
624f3e6c8ebSniallo 		tv2[0].tv_sec = (long)rcs_rev_getdate(rf, r2);
625f3e6c8ebSniallo 		tv2[1].tv_sec = tv2[0].tv_sec;
6267f535ec4Sjfb 		rcsnum_free(r2);
6273917c9bfSderaadt 	} else {
628f3e6c8ebSniallo 		struct stat st;
629f3e6c8ebSniallo 		if (stat(diff_file, &st) < 0) {
630f3e6c8ebSniallo 			cvs_log(LP_ERR, "failed to retrieve revision %s\n",
631f3e6c8ebSniallo 			    dap->rev2);
632f3e6c8ebSniallo 			cvs_buf_free(b1);
633f3e6c8ebSniallo 			return (CVS_EX_DATA);
634f3e6c8ebSniallo 		}
635dc6a6879Sjfb 		b2 = cvs_buf_load(diff_file, BUF_AUTOEXT);
636f3e6c8ebSniallo 		tv2[0].tv_sec = st.st_mtime;
637f3e6c8ebSniallo 		tv2[1].tv_sec = st.st_mtime;
63808f90673Sjfb 	}
63908f90673Sjfb 
640dc6a6879Sjfb 	rcs_close(rf);
641dc6a6879Sjfb 
64295ae1173Sjoris 	if (b2 == NULL) {
643289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
64495ae1173Sjoris 		    dap->rev2);
64595ae1173Sjoris 		cvs_buf_free(b1);
64695ae1173Sjoris 		return (CVS_EX_DATA);
64795ae1173Sjoris 	}
64895ae1173Sjoris 
6495ac8b1e7Sjoris 	cvs_printf("%s", diffargs);
6505ac8b1e7Sjoris 	cvs_printf(" -r%s", buf);
651dc6a6879Sjfb 	if (dap->rev2 != NULL)
6525ac8b1e7Sjoris 		cvs_printf(" -r%s", dap->rev2);
6535ac8b1e7Sjoris 	cvs_printf(" %s\n", diff_file);
6541dee9299Sxsa 	strlcpy(path_tmp1, cvs_tmpdir, sizeof(path_tmp1));
6551dee9299Sxsa 	strlcat(path_tmp1, "/diff1.XXXXXXXXXX", sizeof(path_tmp1));
656fbe562adSxsa 	cvs_buf_write_stmp(b1, path_tmp1, 0600);
6577f535ec4Sjfb 	cvs_buf_free(b1);
658bf951d2dSniallo 	if (utimes(path_tmp1, (const struct timeval *)&tv) < 0)
659bf951d2dSniallo 		cvs_log(LP_ERRNO, "error setting utimes");
6607f535ec4Sjfb 
6611dee9299Sxsa 	strlcpy(path_tmp2, cvs_tmpdir, sizeof(path_tmp2));
6621dee9299Sxsa 	strlcat(path_tmp2, "/diff2.XXXXXXXXXX", sizeof(path_tmp2));
6638dbc5e14Sxsa 	cvs_buf_write_stmp(b2, path_tmp2, 0600);
6647f535ec4Sjfb 	cvs_buf_free(b2);
665bf951d2dSniallo 	if (utimes(path_tmp2, (const struct timeval *)&tv2) < 0)
666bf951d2dSniallo 		cvs_log(LP_ERRNO, "error setting utimes");
6677f535ec4Sjfb 
668f9b67873Sniallo 	cvs_diffreg(path_tmp1, path_tmp2, NULL);
669946f6157Sdjm 	(void)unlink(path_tmp1);
670946f6157Sdjm 	(void)unlink(path_tmp2);
67108f90673Sjfb 
67208f90673Sjfb 	return (0);
67308f90673Sjfb }
674af5bb824Sniallo #endif
67508f90673Sjfb 
67608f90673Sjfb 
67708f90673Sjfb int
678f9b67873Sniallo cvs_diffreg(const char *file1, const char *file2, BUF *out)
67908f90673Sjfb {
68008f90673Sjfb 	FILE *f1, *f2;
68108f90673Sjfb 	int i, rval;
6827f535ec4Sjfb 	void *tmp;
68308f90673Sjfb 
68408f90673Sjfb 	f1 = f2 = NULL;
68508f90673Sjfb 	rval = D_SAME;
68608f90673Sjfb 	anychange = 0;
68708f90673Sjfb 	lastline = 0;
68808f90673Sjfb 	lastmatchline = 0;
68908f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
69008f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
691f9b67873Sniallo 	if (out != NULL)
692f9b67873Sniallo 		diffbuf = out;
69308f90673Sjfb 
69408f90673Sjfb 	f1 = fopen(file1, "r");
69508f90673Sjfb 	if (f1 == NULL) {
69608f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
69708f90673Sjfb 		goto closem;
69808f90673Sjfb 	}
69908f90673Sjfb 
70008f90673Sjfb 	f2 = fopen(file2, "r");
70108f90673Sjfb 	if (f2 == NULL) {
70208f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
70308f90673Sjfb 		goto closem;
70408f90673Sjfb 	}
70508f90673Sjfb 
706bf951d2dSniallo 	if (stat(file1, &stb1) < 0) {
707bf951d2dSniallo 		cvs_log(LP_ERRNO, "%s", file1);
708bf951d2dSniallo 		goto closem;
709bf951d2dSniallo 	}
710bf951d2dSniallo 	if (stat(file2, &stb2) < 0) {
711bf951d2dSniallo 		cvs_log(LP_ERRNO, "%s", file2);
712bf951d2dSniallo 		goto closem;
713bf951d2dSniallo 	}
71408f90673Sjfb 	switch (files_differ(f1, f2)) {
71508f90673Sjfb 	case 0:
71608f90673Sjfb 		goto closem;
71708f90673Sjfb 	case 1:
71808f90673Sjfb 		break;
71908f90673Sjfb 	default:
72008f90673Sjfb 		/* error */
72108f90673Sjfb 		goto closem;
72208f90673Sjfb 	}
72308f90673Sjfb 
72408f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
72508f90673Sjfb 		rval = D_BINARY;
72608f90673Sjfb 		goto closem;
72708f90673Sjfb 	}
7287f535ec4Sjfb 	if ((prepare(0, f1, stb1.st_size) < 0) ||
7297f535ec4Sjfb 	    (prepare(1, f2, stb2.st_size) < 0)) {
7307f535ec4Sjfb 		goto closem;
7317f535ec4Sjfb 	}
73208f90673Sjfb 	prune();
73308f90673Sjfb 	sort(sfile[0], slen[0]);
73408f90673Sjfb 	sort(sfile[1], slen[1]);
73508f90673Sjfb 
73608f90673Sjfb 	member = (int *)file[1];
73708f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
7380450b43bSjoris 	tmp = xrealloc(member, (slen[1] + 2) * sizeof(int));
7397f535ec4Sjfb 	member = (int *)tmp;
74008f90673Sjfb 
74108f90673Sjfb 	class = (int *)file[0];
74208f90673Sjfb 	unsort(sfile[0], slen[0], class);
7430450b43bSjoris 	tmp = xrealloc(class, (slen[0] + 2) * sizeof(int));
7447f535ec4Sjfb 	class = (int *)tmp;
74508f90673Sjfb 
7460450b43bSjoris 	klist = xmalloc((slen[0] + 2) * sizeof(int));
74708f90673Sjfb 	clen = 0;
74808f90673Sjfb 	clistlen = 100;
7490450b43bSjoris 	clist = xmalloc(clistlen * sizeof(cand));
750ece76a70Sjoris 
751ece76a70Sjoris 	if ((i = stone(class, slen[0], member, klist)) < 0)
752ece76a70Sjoris 		goto closem;
753ece76a70Sjoris 
7540450b43bSjoris 	xfree(member);
7550450b43bSjoris 	xfree(class);
75608f90673Sjfb 
7570450b43bSjoris 	tmp = xrealloc(J, (diff_len[0] + 2) * sizeof(int));
75818501190Sniallo 	J = (int *)tmp;
75908f90673Sjfb 	unravel(klist[i]);
7600450b43bSjoris 	xfree(clist);
7610450b43bSjoris 	xfree(klist);
76208f90673Sjfb 
7630450b43bSjoris 	tmp = xrealloc(ixold, (diff_len[0] + 2) * sizeof(long));
76418501190Sniallo 	ixold = (long *)tmp;
7650450b43bSjoris 
7660450b43bSjoris 	tmp = xrealloc(ixnew, (diff_len[1] + 2) * sizeof(long));
76718501190Sniallo 	ixnew = (long *)tmp;
76808f90673Sjfb 	check(f1, f2);
76908f90673Sjfb 	output(file1, f1, file2, f2);
77008f90673Sjfb 
77108f90673Sjfb closem:
77237af5b8bSxsa 	if (anychange == 1) {
77308f90673Sjfb 		if (rval == D_SAME)
77408f90673Sjfb 			rval = D_DIFFER;
77508f90673Sjfb 	}
77608f90673Sjfb 	if (f1 != NULL)
77708f90673Sjfb 		fclose(f1);
77808f90673Sjfb 	if (f2 != NULL)
77908f90673Sjfb 		fclose(f2);
7807f535ec4Sjfb 
78108f90673Sjfb 	return (rval);
78208f90673Sjfb }
78308f90673Sjfb 
78408f90673Sjfb /*
78508f90673Sjfb  * Check to see if the given files differ.
78608f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
78708f90673Sjfb  * XXX - could use code from cmp(1) [faster]
78808f90673Sjfb  */
78908f90673Sjfb static int
79008f90673Sjfb files_differ(FILE *f1, FILE *f2)
79108f90673Sjfb {
79208f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
79308f90673Sjfb 	size_t i, j;
79408f90673Sjfb 
79508f90673Sjfb 	if (stb1.st_size != stb2.st_size)
79608f90673Sjfb 		return (1);
79708f90673Sjfb 	for (;;) {
798f1901a5aSxsa 		i = fread(buf1, (size_t)1, sizeof(buf1), f1);
799f1901a5aSxsa 		j = fread(buf2, (size_t)1, sizeof(buf2), f2);
80008f90673Sjfb 		if (i != j)
80108f90673Sjfb 			return (1);
80237af5b8bSxsa 		if ((i == 0) && (j == 0)) {
80308f90673Sjfb 			if (ferror(f1) || ferror(f2))
80408f90673Sjfb 				return (1);
80508f90673Sjfb 			return (0);
80608f90673Sjfb 		}
80708f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
80808f90673Sjfb 			return (1);
80908f90673Sjfb 	}
81008f90673Sjfb }
81108f90673Sjfb 
8127f535ec4Sjfb static int
81308f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
81408f90673Sjfb {
8157f535ec4Sjfb 	void *tmp;
81608f90673Sjfb 	struct line *p;
81708f90673Sjfb 	int j, h;
81808f90673Sjfb 	size_t sz;
81908f90673Sjfb 
82008f90673Sjfb 	rewind(fd);
82108f90673Sjfb 
822c48da046Sxsa 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
82308f90673Sjfb 	if (sz < 100)
82408f90673Sjfb 		sz = 100;
82508f90673Sjfb 
8260450b43bSjoris 	p = (struct line *)xmalloc((sz + 3) * sizeof(struct line));
82708f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
82808f90673Sjfb 		if (j == (int)sz) {
82908f90673Sjfb 			sz = sz * 3 / 2;
8300450b43bSjoris 			tmp = xrealloc(p, (sz + 3) * sizeof(struct line));
8317f535ec4Sjfb 			p = (struct line *)tmp;
83208f90673Sjfb 		}
83308f90673Sjfb 		p[++j].value = h;
83408f90673Sjfb 	}
835e4276007Sjfb 	diff_len[i] = j;
83608f90673Sjfb 	file[i] = p;
8377f535ec4Sjfb 
8387f535ec4Sjfb 	return (0);
83908f90673Sjfb }
84008f90673Sjfb 
84108f90673Sjfb static void
84208f90673Sjfb prune(void)
84308f90673Sjfb {
84408f90673Sjfb 	int i, j;
84508f90673Sjfb 
846e4276007Sjfb 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
84708f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
84808f90673Sjfb 	    pref++)
84908f90673Sjfb 		;
850e4276007Sjfb 	for (suff = 0;
851e4276007Sjfb 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
852e4276007Sjfb 	    (file[0][diff_len[0] - suff].value ==
853e4276007Sjfb 	    file[1][diff_len[1] - suff].value);
85408f90673Sjfb 	    suff++)
85508f90673Sjfb 		;
85608f90673Sjfb 	for (j = 0; j < 2; j++) {
85708f90673Sjfb 		sfile[j] = file[j] + pref;
858e4276007Sjfb 		slen[j] = diff_len[j] - pref - suff;
85908f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
86008f90673Sjfb 			sfile[j][i].serial = i;
86108f90673Sjfb 	}
86208f90673Sjfb }
86308f90673Sjfb 
86408f90673Sjfb static void
86508f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
86608f90673Sjfb {
86708f90673Sjfb 	int i, j;
86808f90673Sjfb 
86908f90673Sjfb 	i = j = 1;
87008f90673Sjfb 	while (i <= n && j <= m) {
87108f90673Sjfb 		if (a[i].value < b[j].value)
87208f90673Sjfb 			a[i++].value = 0;
87308f90673Sjfb 		else if (a[i].value == b[j].value)
87408f90673Sjfb 			a[i++].value = j;
87508f90673Sjfb 		else
87608f90673Sjfb 			j++;
87708f90673Sjfb 	}
87808f90673Sjfb 	while (i <= n)
87908f90673Sjfb 		a[i++].value = 0;
88008f90673Sjfb 	b[m + 1].value = 0;
88108f90673Sjfb 	j = 0;
88208f90673Sjfb 	while (++j <= m) {
88308f90673Sjfb 		c[j] = -b[j].serial;
88408f90673Sjfb 		while (b[j + 1].value == b[j].value) {
88508f90673Sjfb 			j++;
88608f90673Sjfb 			c[j] = b[j].serial;
88708f90673Sjfb 		}
88808f90673Sjfb 	}
88908f90673Sjfb 	c[j] = -1;
89008f90673Sjfb }
89108f90673Sjfb 
89208f90673Sjfb /* Code taken from ping.c */
89308f90673Sjfb static int
89408f90673Sjfb isqrt(int n)
89508f90673Sjfb {
89608f90673Sjfb 	int y, x = 1;
89708f90673Sjfb 
89808f90673Sjfb 	if (n == 0)
89908f90673Sjfb 		return (0);
90008f90673Sjfb 
90108f90673Sjfb 	do { /* newton was a stinker */
90208f90673Sjfb 		y = x;
90308f90673Sjfb 		x = n / x;
90408f90673Sjfb 		x += y;
90508f90673Sjfb 		x /= 2;
90608f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
90708f90673Sjfb 
90808f90673Sjfb 	return (x);
90908f90673Sjfb }
91008f90673Sjfb 
91108f90673Sjfb static int
91208f90673Sjfb stone(int *a, int n, int *b, int *c)
91308f90673Sjfb {
914ece76a70Sjoris 	int ret;
91508f90673Sjfb 	int i, k, y, j, l;
91608f90673Sjfb 	int oldc, tc, oldl;
91708f90673Sjfb 	u_int numtries;
91808f90673Sjfb 
919cc649edbSjfb 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
920cc649edbSjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
92108f90673Sjfb 
92208f90673Sjfb 	k = 0;
923ece76a70Sjoris 	if ((ret = newcand(0, 0, 0)) < 0)
924ece76a70Sjoris 		return (-1);
925ece76a70Sjoris 	c[0] = ret;
92608f90673Sjfb 	for (i = 1; i <= n; i++) {
92708f90673Sjfb 		j = a[i];
92808f90673Sjfb 		if (j == 0)
92908f90673Sjfb 			continue;
93008f90673Sjfb 		y = -b[j];
93108f90673Sjfb 		oldl = 0;
93208f90673Sjfb 		oldc = c[0];
93308f90673Sjfb 		numtries = 0;
93408f90673Sjfb 		do {
93508f90673Sjfb 			if (y <= clist[oldc].y)
93608f90673Sjfb 				continue;
93708f90673Sjfb 			l = search(c, k, y);
93808f90673Sjfb 			if (l != oldl + 1)
93908f90673Sjfb 				oldc = c[l - 1];
94008f90673Sjfb 			if (l <= k) {
94108f90673Sjfb 				if (clist[c[l]].y <= y)
94208f90673Sjfb 					continue;
94308f90673Sjfb 				tc = c[l];
944ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
945ece76a70Sjoris 					return (-1);
946ece76a70Sjoris 				c[l] = ret;
94708f90673Sjfb 				oldc = tc;
94808f90673Sjfb 				oldl = l;
94908f90673Sjfb 				numtries++;
95008f90673Sjfb 			} else {
951ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
952ece76a70Sjoris 					return (-1);
953ece76a70Sjoris 				c[l] = ret;
95408f90673Sjfb 				k++;
95508f90673Sjfb 				break;
95608f90673Sjfb 			}
95708f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
95808f90673Sjfb 	}
95908f90673Sjfb 	return (k);
96008f90673Sjfb }
96108f90673Sjfb 
96208f90673Sjfb static int
96308f90673Sjfb newcand(int x, int y, int pred)
96408f90673Sjfb {
96518501190Sniallo 	struct cand *q, *tmp;
96618501190Sniallo 	int newclistlen;
96708f90673Sjfb 
96808f90673Sjfb 	if (clen == clistlen) {
96918501190Sniallo 		newclistlen = clistlen * 11 / 10;
9700450b43bSjoris 		tmp = xrealloc(clist, newclistlen * sizeof(cand));
97118501190Sniallo 		clist = tmp;
97218501190Sniallo 		clistlen = newclistlen;
97308f90673Sjfb 	}
97408f90673Sjfb 	q = clist + clen;
97508f90673Sjfb 	q->x = x;
97608f90673Sjfb 	q->y = y;
97708f90673Sjfb 	q->pred = pred;
97808f90673Sjfb 	return (clen++);
97908f90673Sjfb }
98008f90673Sjfb 
98108f90673Sjfb static int
98208f90673Sjfb search(int *c, int k, int y)
98308f90673Sjfb {
98408f90673Sjfb 	int i, j, l, t;
98508f90673Sjfb 
98608f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
98708f90673Sjfb 		return (k + 1);
98808f90673Sjfb 	i = 0;
98908f90673Sjfb 	j = k + 1;
99008f90673Sjfb 	while (1) {
99108f90673Sjfb 		l = i + j;
99208f90673Sjfb 		if ((l >>= 1) <= i)
99308f90673Sjfb 			break;
99408f90673Sjfb 		t = clist[c[l]].y;
99508f90673Sjfb 		if (t > y)
99608f90673Sjfb 			j = l;
99708f90673Sjfb 		else if (t < y)
99808f90673Sjfb 			i = l;
99908f90673Sjfb 		else
100008f90673Sjfb 			return (l);
100108f90673Sjfb 	}
100208f90673Sjfb 	return (l + 1);
100308f90673Sjfb }
100408f90673Sjfb 
100508f90673Sjfb static void
100608f90673Sjfb unravel(int p)
100708f90673Sjfb {
100808f90673Sjfb 	struct cand *q;
100908f90673Sjfb 	int i;
101008f90673Sjfb 
1011e4276007Sjfb 	for (i = 0; i <= diff_len[0]; i++)
101208f90673Sjfb 		J[i] = i <= pref ? i :
1013e4276007Sjfb 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
101408f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
101508f90673Sjfb 		J[q->x + pref] = q->y + pref;
101608f90673Sjfb }
101708f90673Sjfb 
101808f90673Sjfb /*
101908f90673Sjfb  * Check does double duty:
102008f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
102108f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
102208f90673Sjfb  *  2.  collect random access indexes to the two files
102308f90673Sjfb  */
102408f90673Sjfb static void
102508f90673Sjfb check(FILE *f1, FILE *f2)
102608f90673Sjfb {
102708f90673Sjfb 	int i, j, jackpot, c, d;
102808f90673Sjfb 	long ctold, ctnew;
102908f90673Sjfb 
103008f90673Sjfb 	rewind(f1);
103108f90673Sjfb 	rewind(f2);
103208f90673Sjfb 	j = 1;
103308f90673Sjfb 	ixold[0] = ixnew[0] = 0;
103408f90673Sjfb 	jackpot = 0;
103508f90673Sjfb 	ctold = ctnew = 0;
1036e4276007Sjfb 	for (i = 1; i <= diff_len[0]; i++) {
103708f90673Sjfb 		if (J[i] == 0) {
103808f90673Sjfb 			ixold[i] = ctold += skipline(f1);
103908f90673Sjfb 			continue;
104008f90673Sjfb 		}
104108f90673Sjfb 		while (j < J[i]) {
104208f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
104308f90673Sjfb 			j++;
104408f90673Sjfb 		}
104548dc77e6Sxsa 		if ((bflag == 1)|| (wflag == 1) || (iflag == 1)) {
104608f90673Sjfb 			for (;;) {
104708f90673Sjfb 				c = getc(f1);
104808f90673Sjfb 				d = getc(f2);
104908f90673Sjfb 				/*
105008f90673Sjfb 				 * GNU diff ignores a missing newline
105108f90673Sjfb 				 * in one file if bflag || wflag.
105208f90673Sjfb 				 */
105348dc77e6Sxsa 				if (((bflag == 1) || (wflag == 1)) &&
105408f90673Sjfb 				    ((c == EOF && d == '\n') ||
105508f90673Sjfb 				    (c == '\n' && d == EOF))) {
105608f90673Sjfb 					break;
105708f90673Sjfb 				}
105808f90673Sjfb 				ctold++;
105908f90673Sjfb 				ctnew++;
106048dc77e6Sxsa 				if ((bflag == 1) && isspace(c) && isspace(d)) {
106108f90673Sjfb 					do {
106208f90673Sjfb 						if (c == '\n')
106308f90673Sjfb 							break;
106408f90673Sjfb 						ctold++;
106508f90673Sjfb 					} while (isspace(c = getc(f1)));
106608f90673Sjfb 					do {
106708f90673Sjfb 						if (d == '\n')
106808f90673Sjfb 							break;
106908f90673Sjfb 						ctnew++;
107008f90673Sjfb 					} while (isspace(d = getc(f2)));
107148dc77e6Sxsa 				} else if (wflag == 1) {
107208f90673Sjfb 					while (isspace(c) && c != '\n') {
107308f90673Sjfb 						c = getc(f1);
107408f90673Sjfb 						ctold++;
107508f90673Sjfb 					}
107608f90673Sjfb 					while (isspace(d) && d != '\n') {
107708f90673Sjfb 						d = getc(f2);
107808f90673Sjfb 						ctnew++;
107908f90673Sjfb 					}
108008f90673Sjfb 				}
108108f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
108208f90673Sjfb 					jackpot++;
108308f90673Sjfb 					J[i] = 0;
108437af5b8bSxsa 					if ((c != '\n') && (c != EOF))
108508f90673Sjfb 						ctold += skipline(f1);
108637af5b8bSxsa 					if ((d != '\n') && (c != EOF))
108708f90673Sjfb 						ctnew += skipline(f2);
108808f90673Sjfb 					break;
108908f90673Sjfb 				}
109037af5b8bSxsa 				if ((c == '\n') || (c == EOF))
109108f90673Sjfb 					break;
109208f90673Sjfb 			}
109308f90673Sjfb 		} else {
109408f90673Sjfb 			for (;;) {
109508f90673Sjfb 				ctold++;
109608f90673Sjfb 				ctnew++;
109708f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
109808f90673Sjfb 					/* jackpot++; */
109908f90673Sjfb 					J[i] = 0;
110037af5b8bSxsa 					if ((c != '\n') && (c != EOF))
110108f90673Sjfb 						ctold += skipline(f1);
110237af5b8bSxsa 					if ((d != '\n') && (c != EOF))
110308f90673Sjfb 						ctnew += skipline(f2);
110408f90673Sjfb 					break;
110508f90673Sjfb 				}
110637af5b8bSxsa 				if ((c == '\n') || (c == EOF))
110708f90673Sjfb 					break;
110808f90673Sjfb 			}
110908f90673Sjfb 		}
111008f90673Sjfb 		ixold[i] = ctold;
111108f90673Sjfb 		ixnew[j] = ctnew;
111208f90673Sjfb 		j++;
111308f90673Sjfb 	}
1114e4276007Sjfb 	for (; j <= diff_len[1]; j++)
111508f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
111608f90673Sjfb 	/*
111737af5b8bSxsa 	 * if (jackpot != 0)
11185ac8b1e7Sjoris 	 *	cvs_printf("jackpot\n");
111908f90673Sjfb 	 */
112008f90673Sjfb }
112108f90673Sjfb 
112208f90673Sjfb /* shellsort CACM #201 */
112308f90673Sjfb static void
112408f90673Sjfb sort(struct line *a, int n)
112508f90673Sjfb {
112608f90673Sjfb 	struct line *ai, *aim, w;
112708f90673Sjfb 	int j, m = 0, k;
112808f90673Sjfb 
112908f90673Sjfb 	if (n == 0)
113008f90673Sjfb 		return;
113108f90673Sjfb 	for (j = 1; j <= n; j *= 2)
113208f90673Sjfb 		m = 2 * j - 1;
113308f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
113408f90673Sjfb 		k = n - m;
113508f90673Sjfb 		for (j = 1; j <= k; j++) {
113608f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
113708f90673Sjfb 				aim = &ai[m];
113808f90673Sjfb 				if (aim < ai)
113908f90673Sjfb 					break;	/* wraparound */
114008f90673Sjfb 				if (aim->value > ai[0].value ||
114108f90673Sjfb 				    (aim->value == ai[0].value &&
114208f90673Sjfb 					aim->serial > ai[0].serial))
114308f90673Sjfb 					break;
114408f90673Sjfb 				w.value = ai[0].value;
114508f90673Sjfb 				ai[0].value = aim->value;
114608f90673Sjfb 				aim->value = w.value;
114708f90673Sjfb 				w.serial = ai[0].serial;
114808f90673Sjfb 				ai[0].serial = aim->serial;
114908f90673Sjfb 				aim->serial = w.serial;
115008f90673Sjfb 			}
115108f90673Sjfb 		}
115208f90673Sjfb 	}
115308f90673Sjfb }
115408f90673Sjfb 
115508f90673Sjfb static void
115608f90673Sjfb unsort(struct line *f, int l, int *b)
115708f90673Sjfb {
115808f90673Sjfb 	int *a, i;
115908f90673Sjfb 
11600450b43bSjoris 	a = (int *)xmalloc((l + 1) * sizeof(int));
116108f90673Sjfb 	for (i = 1; i <= l; i++)
116208f90673Sjfb 		a[f[i].serial] = f[i].value;
116308f90673Sjfb 	for (i = 1; i <= l; i++)
116408f90673Sjfb 		b[i] = a[i];
11650450b43bSjoris 	xfree(a);
116608f90673Sjfb }
116708f90673Sjfb 
116808f90673Sjfb static int
116908f90673Sjfb skipline(FILE *f)
117008f90673Sjfb {
117108f90673Sjfb 	int i, c;
117208f90673Sjfb 
117308f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
117408f90673Sjfb 		continue;
117508f90673Sjfb 	return (i);
117608f90673Sjfb }
117708f90673Sjfb 
117808f90673Sjfb static void
117908f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2)
118008f90673Sjfb {
118108f90673Sjfb 	int m, i0, i1, j0, j1;
118208f90673Sjfb 
118308f90673Sjfb 	rewind(f1);
118408f90673Sjfb 	rewind(f2);
1185e4276007Sjfb 	m = diff_len[0];
118608f90673Sjfb 	J[0] = 0;
1187e4276007Sjfb 	J[m + 1] = diff_len[1] + 1;
118808f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
118908f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
119008f90673Sjfb 			i0++;
119108f90673Sjfb 		j0 = J[i0 - 1] + 1;
119208f90673Sjfb 		i1 = i0 - 1;
119308f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
119408f90673Sjfb 			i1++;
119508f90673Sjfb 		j1 = J[i1 + 1] - 1;
119608f90673Sjfb 		J[i1] = j1;
119708f90673Sjfb 		change(file1, f1, file2, f2, i0, i1, j0, j1);
119808f90673Sjfb 	}
119908f90673Sjfb 	if (m == 0)
1200e4276007Sjfb 		change(file1, f1, file2, f2, 1, 0, 1, diff_len[1]);
1201f9b67873Sniallo 	if (diff_format == D_IFDEF) {
120208f90673Sjfb 		for (;;) {
120308f90673Sjfb #define	c i0
120408f90673Sjfb 			if ((c = getc(f1)) == EOF)
120508f90673Sjfb 				return;
1206f9b67873Sniallo 			diff_output("%c", c);
120708f90673Sjfb 		}
120808f90673Sjfb #undef c
120908f90673Sjfb 	}
121008f90673Sjfb 	if (anychange != 0) {
1211f9b67873Sniallo 		if (diff_format == D_CONTEXT)
121208f90673Sjfb 			dump_context_vec(f1, f2);
1213f9b67873Sniallo 		else if (diff_format == D_UNIFIED)
121408f90673Sjfb 			dump_unified_vec(f1, f2);
121508f90673Sjfb 	}
121608f90673Sjfb }
121708f90673Sjfb 
121808f90673Sjfb static __inline void
121908f90673Sjfb range(int a, int b, char *separator)
122008f90673Sjfb {
1221f9b67873Sniallo 	diff_output("%d", a > b ? b : a);
122208f90673Sjfb 	if (a < b)
1223f9b67873Sniallo 		diff_output("%s%d", separator, b);
122408f90673Sjfb }
122508f90673Sjfb 
122608f90673Sjfb static __inline void
122708f90673Sjfb uni_range(int a, int b)
122808f90673Sjfb {
122908f90673Sjfb 	if (a < b)
1230f9b67873Sniallo 		diff_output("%d,%d", a, b - a + 1);
123108f90673Sjfb 	else if (a == b)
1232f9b67873Sniallo 		diff_output("%d", b);
123308f90673Sjfb 	else
1234f9b67873Sniallo 		diff_output("%d,0", b);
123508f90673Sjfb }
123608f90673Sjfb 
123708f90673Sjfb static char *
12382a0de57dSjfb preadline(int fd, size_t rlen, off_t off)
123908f90673Sjfb {
124008f90673Sjfb 	char *line;
124108f90673Sjfb 	ssize_t nr;
124208f90673Sjfb 
12430450b43bSjoris 	line = xmalloc(rlen + 1);
12442a0de57dSjfb 	if ((nr = pread(fd, line, rlen, off)) < 0) {
124508f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
124608f90673Sjfb 		return (NULL);
124708f90673Sjfb 	}
124808f90673Sjfb 	line[nr] = '\0';
124908f90673Sjfb 	return (line);
125008f90673Sjfb }
125108f90673Sjfb 
125208f90673Sjfb static int
125308f90673Sjfb ignoreline(char *line)
125408f90673Sjfb {
125508f90673Sjfb 	int ret;
125608f90673Sjfb 
1257f1901a5aSxsa 	ret = regexec(&ignore_re, line, (size_t)0, NULL, 0);
12580450b43bSjoris 	xfree(line);
125908f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
126008f90673Sjfb }
126108f90673Sjfb 
126208f90673Sjfb /*
126308f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
126408f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
126508f90673Sjfb  * are no lines in the from file involved and this means that there were
126608f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
126708f90673Sjfb  * lines missing from the to file.
126808f90673Sjfb  */
126908f90673Sjfb static void
127008f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2,
127108f90673Sjfb 	int a, int b, int c, int d)
127208f90673Sjfb {
127308f90673Sjfb 	static size_t max_context = 64;
127408f90673Sjfb 	int i;
127508f90673Sjfb 
1276f9b67873Sniallo 	if (diff_format != D_IFDEF && a > b && c > d)
127708f90673Sjfb 		return;
127808f90673Sjfb 	if (ignore_pats != NULL) {
127908f90673Sjfb 		char *line;
128008f90673Sjfb 		/*
128108f90673Sjfb 		 * All lines in the change, insert, or delete must
128208f90673Sjfb 		 * match an ignore pattern for the change to be
128308f90673Sjfb 		 * ignored.
128408f90673Sjfb 		 */
128508f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
128608f90673Sjfb 			for (i = a; i <= b; i++) {
128708f90673Sjfb 				line = preadline(fileno(f1),
128808f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
128908f90673Sjfb 				if (!ignoreline(line))
129008f90673Sjfb 					goto proceed;
129108f90673Sjfb 			}
129208f90673Sjfb 		}
129337af5b8bSxsa 		if ((a > b) || (c <= d)) {	/* Changes and inserts. */
129408f90673Sjfb 			for (i = c; i <= d; i++) {
129508f90673Sjfb 				line = preadline(fileno(f2),
129608f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
129708f90673Sjfb 				if (!ignoreline(line))
129808f90673Sjfb 					goto proceed;
129908f90673Sjfb 			}
130008f90673Sjfb 		}
130108f90673Sjfb 		return;
130208f90673Sjfb 	}
130308f90673Sjfb proceed:
1304f9b67873Sniallo 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
130508f90673Sjfb 		/*
130608f90673Sjfb 		 * Allocate change records as needed.
130708f90673Sjfb 		 */
130808f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
130918501190Sniallo 			struct context_vec *tmp;
131008f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
131108f90673Sjfb 			max_context <<= 1;
13120450b43bSjoris 			tmp = xrealloc(context_vec_start, max_context *
13130450b43bSjoris 			    sizeof(struct context_vec));
131418501190Sniallo 			context_vec_start = tmp;
131508f90673Sjfb 			context_vec_end = context_vec_start + max_context;
131608f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
131708f90673Sjfb 		}
131808f90673Sjfb 		if (anychange == 0) {
131908f90673Sjfb 			/*
132008f90673Sjfb 			 * Print the context/unidiff header first time through.
132108f90673Sjfb 			 */
1322f9b67873Sniallo 			diff_output("%s %s	%s",
1323f9b67873Sniallo 			    diff_format == D_CONTEXT ? "***" : "---", diff_file,
132408f90673Sjfb 			    ctime(&stb1.st_mtime));
1325f9b67873Sniallo 			diff_output("%s %s	%s",
1326f9b67873Sniallo 			    diff_format == D_CONTEXT ? "---" : "+++", diff_file,
132708f90673Sjfb 			    ctime(&stb2.st_mtime));
132808f90673Sjfb 			anychange = 1;
132908f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
133008f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
133108f90673Sjfb 			/*
133208f90673Sjfb 			 * If this change is more than 'context' lines from the
133308f90673Sjfb 			 * previous change, dump the record and reset it.
133408f90673Sjfb 			 */
1335f9b67873Sniallo 			if (diff_format == D_CONTEXT)
133608f90673Sjfb 				dump_context_vec(f1, f2);
133708f90673Sjfb 			else
133808f90673Sjfb 				dump_unified_vec(f1, f2);
133908f90673Sjfb 		}
134008f90673Sjfb 		context_vec_ptr++;
134108f90673Sjfb 		context_vec_ptr->a = a;
134208f90673Sjfb 		context_vec_ptr->b = b;
134308f90673Sjfb 		context_vec_ptr->c = c;
134408f90673Sjfb 		context_vec_ptr->d = d;
134508f90673Sjfb 		return;
134608f90673Sjfb 	}
134708f90673Sjfb 	if (anychange == 0)
134808f90673Sjfb 		anychange = 1;
1349f9b67873Sniallo 	switch (diff_format) {
135008f90673Sjfb 	case D_BRIEF:
135108f90673Sjfb 		return;
135208f90673Sjfb 	case D_NORMAL:
135308f90673Sjfb 		range(a, b, ",");
1354f9b67873Sniallo 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1355f9b67873Sniallo 		if (diff_format == D_NORMAL)
135608f90673Sjfb 			range(c, d, ",");
1357f9b67873Sniallo 		diff_output("\n");
135808f90673Sjfb 		break;
1359394180a4Sjfb 	case D_RCSDIFF:
1360394180a4Sjfb 		if (a > b)
1361f9b67873Sniallo 			diff_output("a%d %d\n", b, d - c + 1);
1362394180a4Sjfb 		else {
1363f9b67873Sniallo 			diff_output("d%d %d\n", a, b - a + 1);
1364394180a4Sjfb 
1365394180a4Sjfb 			if (!(c > d))	/* add changed lines */
1366f9b67873Sniallo 				diff_output("a%d %d\n", b, d - c + 1);
1367394180a4Sjfb 		}
1368394180a4Sjfb 		break;
136908f90673Sjfb 	}
1370f9b67873Sniallo 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
137108f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
1372f9b67873Sniallo 		if (a <= b && c <= d && diff_format == D_NORMAL)
1373206543eeSjoris 			diff_output("---\n");
137408f90673Sjfb 	}
1375f9b67873Sniallo 	i = fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0);
137608f90673Sjfb 	if (inifdef) {
1377f9b67873Sniallo 		diff_output("#endif /* %s */\n", ifdefname);
137808f90673Sjfb 		inifdef = 0;
137908f90673Sjfb 	}
138008f90673Sjfb }
138108f90673Sjfb 
138208f90673Sjfb static int
138308f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
138408f90673Sjfb {
138508f90673Sjfb 	int i, j, c, lastc, col, nc;
138608f90673Sjfb 
138708f90673Sjfb 	/*
138808f90673Sjfb 	 * When doing #ifdef's, copy down to current line
138908f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
139008f90673Sjfb 	 */
1391f9b67873Sniallo 	if (diff_format == D_IFDEF && oldfile) {
139208f90673Sjfb 		long curpos = ftell(lb);
139308f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
139408f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
139508f90673Sjfb 		for (i = 0; i < nc; i++)
1396f9b67873Sniallo 			diff_output("%c", getc(lb));
139708f90673Sjfb 	}
139808f90673Sjfb 	if (a > b)
139908f90673Sjfb 		return (0);
1400f9b67873Sniallo 	if (diff_format == D_IFDEF) {
140108f90673Sjfb 		if (inifdef) {
1402f9b67873Sniallo 			diff_output("#else /* %s%s */\n",
140308f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
140408f90673Sjfb 		} else {
140508f90673Sjfb 			if (oldfile)
1406f9b67873Sniallo 				diff_output("#ifndef %s\n", ifdefname);
140708f90673Sjfb 			else
1408f9b67873Sniallo 				diff_output("#ifdef %s\n", ifdefname);
140908f90673Sjfb 		}
141008f90673Sjfb 		inifdef = 1 + oldfile;
141108f90673Sjfb 	}
141208f90673Sjfb 	for (i = a; i <= b; i++) {
141308f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
141408f90673Sjfb 		nc = f[i] - f[i - 1];
1415f9b67873Sniallo 		if (diff_format != D_IFDEF && ch != '\0') {
1416f9b67873Sniallo 			diff_output("%c", ch);
141748dc77e6Sxsa 			if ((Tflag == 1 ) && (diff_format == D_NORMAL ||
14189c5161e4Sjoris 			    diff_format == D_CONTEXT ||
14199c5161e4Sjoris 			    diff_format == D_UNIFIED))
1420f9b67873Sniallo 				diff_output("\t");
1421f9b67873Sniallo 			else if (diff_format != D_UNIFIED)
1422f9b67873Sniallo 				diff_output(" ");
142308f90673Sjfb 		}
142408f90673Sjfb 		col = 0;
142508f90673Sjfb 		for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
142608f90673Sjfb 			if ((c = getc(lb)) == EOF) {
1427f9b67873Sniallo 				if (diff_format == D_RCSDIFF)
1428*b9a6f00eSxsa 					cvs_log(LP_WARN,
1429*b9a6f00eSxsa 					    "No newline at end of file");
1430394180a4Sjfb 				else
14319c5161e4Sjoris 					diff_output("\n\\ No newline at end of "
14329c5161e4Sjoris 					    "file");
143308f90673Sjfb 				return (0);
143408f90673Sjfb 			}
143548dc77e6Sxsa 			if ((c == '\t') && (tflag == 1)) {
143608f90673Sjfb 				do {
1437f9b67873Sniallo 					diff_output(" ");
143808f90673Sjfb 				} while (++col & 7);
143908f90673Sjfb 			} else {
1440f9b67873Sniallo 				diff_output("%c", c);
144108f90673Sjfb 				col++;
144208f90673Sjfb 			}
144308f90673Sjfb 		}
144408f90673Sjfb 	}
144508f90673Sjfb 	return (0);
144608f90673Sjfb }
144708f90673Sjfb 
144808f90673Sjfb /*
144908f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
145008f90673Sjfb  */
145108f90673Sjfb static int
145208f90673Sjfb readhash(FILE *f)
145308f90673Sjfb {
145408f90673Sjfb 	int i, t, space;
145508f90673Sjfb 	int sum;
145608f90673Sjfb 
145708f90673Sjfb 	sum = 1;
145808f90673Sjfb 	space = 0;
145948dc77e6Sxsa 	if ((bflag != 1) && (wflag != 1)) {
146048dc77e6Sxsa 		if (iflag == 1)
146108f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
146208f90673Sjfb 				if (t == EOF) {
146308f90673Sjfb 					if (i == 0)
146408f90673Sjfb 						return (0);
146508f90673Sjfb 					break;
146608f90673Sjfb 				}
146708f90673Sjfb 				sum = sum * 127 + chrtran[t];
146808f90673Sjfb 			}
146908f90673Sjfb 		else
147008f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
147108f90673Sjfb 				if (t == EOF) {
147208f90673Sjfb 					if (i == 0)
147308f90673Sjfb 						return (0);
147408f90673Sjfb 					break;
147508f90673Sjfb 				}
147608f90673Sjfb 				sum = sum * 127 + t;
147708f90673Sjfb 			}
147808f90673Sjfb 	} else {
147908f90673Sjfb 		for (i = 0;;) {
148008f90673Sjfb 			switch (t = getc(f)) {
148108f90673Sjfb 			case '\t':
148208f90673Sjfb 			case ' ':
148308f90673Sjfb 				space++;
148408f90673Sjfb 				continue;
148508f90673Sjfb 			default:
148648dc77e6Sxsa 				if ((space != 0) && (wflag != 1)) {
148708f90673Sjfb 					i++;
148808f90673Sjfb 					space = 0;
148908f90673Sjfb 				}
149008f90673Sjfb 				sum = sum * 127 + chrtran[t];
149108f90673Sjfb 				i++;
149208f90673Sjfb 				continue;
149308f90673Sjfb 			case EOF:
149408f90673Sjfb 				if (i == 0)
149508f90673Sjfb 					return (0);
149608f90673Sjfb 				/* FALLTHROUGH */
149708f90673Sjfb 			case '\n':
149808f90673Sjfb 				break;
149908f90673Sjfb 			}
150008f90673Sjfb 			break;
150108f90673Sjfb 		}
150208f90673Sjfb 	}
150308f90673Sjfb 	/*
150408f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
150508f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
150608f90673Sjfb 	 */
150708f90673Sjfb 	return (sum == 0 ? 1 : sum);
150808f90673Sjfb }
150908f90673Sjfb 
151008f90673Sjfb static int
151108f90673Sjfb asciifile(FILE *f)
151208f90673Sjfb {
151308f90673Sjfb 	char buf[BUFSIZ];
151408f90673Sjfb 	int i, cnt;
151508f90673Sjfb 
151648dc77e6Sxsa 	if ((aflag == 1) || (f == NULL))
151708f90673Sjfb 		return (1);
151808f90673Sjfb 
151908f90673Sjfb 	rewind(f);
1520f1901a5aSxsa 	cnt = fread(buf, (size_t)1, sizeof(buf), f);
152108f90673Sjfb 	for (i = 0; i < cnt; i++)
152208f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
152308f90673Sjfb 			return (0);
152408f90673Sjfb 	return (1);
152508f90673Sjfb }
152608f90673Sjfb 
15275e78344dSjfb static char*
15285e78344dSjfb match_function(const long *f, int pos, FILE *fp)
15295e78344dSjfb {
15305e78344dSjfb 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
15315e78344dSjfb 	size_t nc;
15325e78344dSjfb 	int last = lastline;
15335e78344dSjfb 	char *p;
15345e78344dSjfb 
15355e78344dSjfb 	lastline = pos;
15365e78344dSjfb 	while (pos > last) {
15375e78344dSjfb 		fseek(fp, f[pos - 1], SEEK_SET);
15385e78344dSjfb 		nc = f[pos] - f[pos - 1];
15395e78344dSjfb 		if (nc >= sizeof(buf))
15405e78344dSjfb 			nc = sizeof(buf) - 1;
1541f1901a5aSxsa 		nc = fread(buf, (size_t)1, nc, fp);
15425e78344dSjfb 		if (nc > 0) {
15435e78344dSjfb 			buf[nc] = '\0';
1544634926d6Sniallo 			p = strchr((const char *)buf, '\n');
15455e78344dSjfb 			if (p != NULL)
15465e78344dSjfb 				*p = '\0';
15475e78344dSjfb 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
15484c06e5f6Sreyk 				strlcpy(lastbuf, (const char *)buf,
15494c06e5f6Sreyk 				    sizeof lastbuf);
15505e78344dSjfb 				lastmatchline = pos;
15515e78344dSjfb 				return lastbuf;
15525e78344dSjfb 			}
15535e78344dSjfb 		}
15545e78344dSjfb 		pos--;
15555e78344dSjfb 	}
15565e78344dSjfb 	return (lastmatchline > 0) ? lastbuf : NULL;
15575e78344dSjfb }
15585e78344dSjfb 
155908f90673Sjfb 
156008f90673Sjfb /* dump accumulated "context" diff changes */
156108f90673Sjfb static void
156208f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
156308f90673Sjfb {
156408f90673Sjfb 	struct context_vec *cvp = context_vec_start;
156508f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
156608f90673Sjfb 	int a, b, c, d;
15675e78344dSjfb 	char ch, *f;
156808f90673Sjfb 
156908f90673Sjfb 	if (context_vec_start > context_vec_ptr)
157008f90673Sjfb 		return;
157108f90673Sjfb 
157208f90673Sjfb 	b = d = 0;		/* gcc */
1573dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1574e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1575dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1576e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
157708f90673Sjfb 
1578f9b67873Sniallo 	diff_output("***************");
157948dc77e6Sxsa 	if (pflag == 1) {
15805e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
15815e78344dSjfb 		if (f != NULL) {
1582f9b67873Sniallo 			diff_output(" ");
1583f9b67873Sniallo 			diff_output("%s", f);
15845e78344dSjfb 		}
15855e78344dSjfb 	}
1586f9b67873Sniallo 	diff_output("\n*** ");
158708f90673Sjfb 	range(lowa, upb, ",");
1588f9b67873Sniallo 	diff_output(" ****\n");
158908f90673Sjfb 
159008f90673Sjfb 	/*
159108f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
159208f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
159308f90673Sjfb 	 * the "old" lines as context in the "new" list).
159408f90673Sjfb 	 */
159508f90673Sjfb 	do_output = 0;
159608f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
159708f90673Sjfb 		if (cvp->a <= cvp->b) {
159808f90673Sjfb 			cvp = context_vec_start;
159908f90673Sjfb 			do_output++;
160008f90673Sjfb 			break;
160108f90673Sjfb 		}
160237af5b8bSxsa 	if (do_output != 0) {
160308f90673Sjfb 		while (cvp <= context_vec_ptr) {
160408f90673Sjfb 			a = cvp->a;
160508f90673Sjfb 			b = cvp->b;
160608f90673Sjfb 			c = cvp->c;
160708f90673Sjfb 			d = cvp->d;
160808f90673Sjfb 
160908f90673Sjfb 			if (a <= b && c <= d)
161008f90673Sjfb 				ch = 'c';
161108f90673Sjfb 			else
161208f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
161308f90673Sjfb 
161408f90673Sjfb 			if (ch == 'a')
161508f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
161608f90673Sjfb 			else {
161708f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
161808f90673Sjfb 				fetch(ixold, a, b, f1,
161908f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
162008f90673Sjfb 			}
162108f90673Sjfb 			lowa = b + 1;
162208f90673Sjfb 			cvp++;
162308f90673Sjfb 		}
162408f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
162508f90673Sjfb 	}
162608f90673Sjfb 	/* output changes to the "new" file */
1627f9b67873Sniallo 	diff_output("--- ");
162808f90673Sjfb 	range(lowc, upd, ",");
1629f9b67873Sniallo 	diff_output(" ----\n");
163008f90673Sjfb 
163108f90673Sjfb 	do_output = 0;
163208f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
163308f90673Sjfb 		if (cvp->c <= cvp->d) {
163408f90673Sjfb 			cvp = context_vec_start;
163508f90673Sjfb 			do_output++;
163608f90673Sjfb 			break;
163708f90673Sjfb 		}
163837af5b8bSxsa 	if (do_output != 0) {
163908f90673Sjfb 		while (cvp <= context_vec_ptr) {
164008f90673Sjfb 			a = cvp->a;
164108f90673Sjfb 			b = cvp->b;
164208f90673Sjfb 			c = cvp->c;
164308f90673Sjfb 			d = cvp->d;
164408f90673Sjfb 
164508f90673Sjfb 			if (a <= b && c <= d)
164608f90673Sjfb 				ch = 'c';
164708f90673Sjfb 			else
164808f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
164908f90673Sjfb 
165008f90673Sjfb 			if (ch == 'd')
165108f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
165208f90673Sjfb 			else {
165308f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
165408f90673Sjfb 				fetch(ixnew, c, d, f2,
165508f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
165608f90673Sjfb 			}
165708f90673Sjfb 			lowc = d + 1;
165808f90673Sjfb 			cvp++;
165908f90673Sjfb 		}
166008f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
166108f90673Sjfb 	}
166208f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
166308f90673Sjfb }
166408f90673Sjfb 
166508f90673Sjfb /* dump accumulated "unified" diff changes */
166608f90673Sjfb static void
166708f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
166808f90673Sjfb {
166908f90673Sjfb 	struct context_vec *cvp = context_vec_start;
167008f90673Sjfb 	int lowa, upb, lowc, upd;
167108f90673Sjfb 	int a, b, c, d;
16725e78344dSjfb 	char ch, *f;
167308f90673Sjfb 
167408f90673Sjfb 	if (context_vec_start > context_vec_ptr)
167508f90673Sjfb 		return;
167608f90673Sjfb 
167708f90673Sjfb 	b = d = 0;		/* gcc */
1678dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1679e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1680dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1681e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
168208f90673Sjfb 
1683f9b67873Sniallo 	diff_output("@@ -");
168408f90673Sjfb 	uni_range(lowa, upb);
1685f9b67873Sniallo 	diff_output(" +");
168608f90673Sjfb 	uni_range(lowc, upd);
1687f9b67873Sniallo 	diff_output(" @@");
168848dc77e6Sxsa 	if (pflag == 1) {
16895e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
16905e78344dSjfb 		if (f != NULL) {
1691f9b67873Sniallo 			diff_output(" ");
1692f9b67873Sniallo 			diff_output("%s", f);
16935e78344dSjfb 		}
16945e78344dSjfb 	}
1695f9b67873Sniallo 	diff_output("\n");
169608f90673Sjfb 
169708f90673Sjfb 	/*
169808f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
169908f90673Sjfb 	 * are printed together.
170008f90673Sjfb 	 */
170108f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
170208f90673Sjfb 		a = cvp->a;
170308f90673Sjfb 		b = cvp->b;
170408f90673Sjfb 		c = cvp->c;
170508f90673Sjfb 		d = cvp->d;
170608f90673Sjfb 
170708f90673Sjfb 		/*
170808f90673Sjfb 		 * c: both new and old changes
170908f90673Sjfb 		 * d: only changes in the old file
171008f90673Sjfb 		 * a: only changes in the new file
171108f90673Sjfb 		 */
171208f90673Sjfb 		if (a <= b && c <= d)
171308f90673Sjfb 			ch = 'c';
171408f90673Sjfb 		else
171508f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
171608f90673Sjfb 
171708f90673Sjfb 		switch (ch) {
171808f90673Sjfb 		case 'c':
171908f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
172008f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
172108f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
172208f90673Sjfb 			break;
172308f90673Sjfb 		case 'd':
172408f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
172508f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
172608f90673Sjfb 			break;
172708f90673Sjfb 		case 'a':
172808f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
172908f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
173008f90673Sjfb 			break;
173108f90673Sjfb 		}
173208f90673Sjfb 		lowa = b + 1;
173308f90673Sjfb 		lowc = d + 1;
173408f90673Sjfb 	}
173508f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
173608f90673Sjfb 
173708f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
173808f90673Sjfb }
1739f9b67873Sniallo 
174001af718aSjoris void
1741f9b67873Sniallo diff_output(const char *fmt, ...)
1742f9b67873Sniallo {
1743f9b67873Sniallo 	va_list vap;
1744f9b67873Sniallo 	char *str;
1745f9b67873Sniallo 
1746f9b67873Sniallo 	va_start(vap, fmt);
1747f9b67873Sniallo 	vasprintf(&str, fmt, vap);
1748f9b67873Sniallo 	if (diffbuf != NULL)
1749f9b67873Sniallo 		cvs_buf_append(diffbuf, str, strlen(str));
1750f9b67873Sniallo 	else
1751f9b67873Sniallo 		cvs_printf("%s", str);
17520450b43bSjoris 	xfree(str);
1753f9b67873Sniallo 	va_end(vap);
1754f9b67873Sniallo }
1755