xref: /openbsd-src/usr.bin/cvs/diff.c (revision 0450b43bdbf8d53e9344e764e99a49ed83762f26)
1*0450b43bSjoris /*	$OpenBSD: diff.c,v 1.71 2005/12/10 20:27:45 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 
12908f90673Sjfb #include <sys/stat.h>
13008f90673Sjfb 
1319225b0caSxsa #include <ctype.h>
1329225b0caSxsa #include <dirent.h>
133394180a4Sjfb #include <err.h>
13408f90673Sjfb #include <errno.h>
13508f90673Sjfb #include <fcntl.h>
13608f90673Sjfb #include <paths.h>
13708f90673Sjfb #include <regex.h>
13808f90673Sjfb #include <stddef.h>
1399225b0caSxsa #include <stdio.h>
1409225b0caSxsa #include <stdlib.h>
14108f90673Sjfb #include <string.h>
1429225b0caSxsa #include <unistd.h>
14308f90673Sjfb 
1449225b0caSxsa #include "buf.h"
14508f90673Sjfb #include "cvs.h"
146af5bb824Sniallo #include "diff.h"
14708f90673Sjfb #include "log.h"
148dc6a6879Sjfb #include "proto.h"
14908f90673Sjfb 
15008f90673Sjfb struct cand {
15108f90673Sjfb 	int	x;
15208f90673Sjfb 	int	y;
15308f90673Sjfb 	int	pred;
15408f90673Sjfb } cand;
15508f90673Sjfb 
15608f90673Sjfb struct line {
15708f90673Sjfb 	int	serial;
15808f90673Sjfb 	int	value;
15908f90673Sjfb } *file[2];
16008f90673Sjfb 
16108f90673Sjfb /*
16208f90673Sjfb  * The following struct is used to record change in formation when
16308f90673Sjfb  * doing a "context" or "unified" diff.  (see routine "change" to
16408f90673Sjfb  * understand the highly mnemonic field names)
16508f90673Sjfb  */
16608f90673Sjfb struct context_vec {
16708f90673Sjfb 	int	a;	/* start line in old file */
16808f90673Sjfb 	int	b;	/* end line in old file */
16908f90673Sjfb 	int	c;	/* start line in new file */
17008f90673Sjfb 	int	d;	/* end line in new file */
17108f90673Sjfb };
17208f90673Sjfb 
173dc6a6879Sjfb struct diff_arg {
174dc6a6879Sjfb 	char	*rev1;
175dc6a6879Sjfb 	char	*rev2;
176dc6a6879Sjfb 	char	*date1;
177dc6a6879Sjfb 	char	*date2;
178dc6a6879Sjfb };
179dc6a6879Sjfb 
180af5bb824Sniallo #if !defined(RCSPROG)
181e4276007Sjfb static int	cvs_diff_init(struct cvs_cmd *, int, char **, int *);
182e4276007Sjfb static int	cvs_diff_remote(CVSFILE *, void *);
183e4276007Sjfb static int	cvs_diff_local(CVSFILE *, void *);
184e4276007Sjfb static int	cvs_diff_pre_exec(struct cvsroot *);
185e4276007Sjfb static int	cvs_diff_cleanup(void);
186af5bb824Sniallo #endif
18716cfc147Sjoris 
18808f90673Sjfb static void	 output(const char *, FILE *, const char *, FILE *);
18908f90673Sjfb static void	 check(FILE *, FILE *);
19008f90673Sjfb static void	 range(int, int, char *);
19108f90673Sjfb static void	 uni_range(int, int);
19208f90673Sjfb static void	 dump_context_vec(FILE *, FILE *);
19308f90673Sjfb static void	 dump_unified_vec(FILE *, FILE *);
1947f535ec4Sjfb static int	 prepare(int, FILE *, off_t);
19508f90673Sjfb static void	 prune(void);
19608f90673Sjfb static void	 equiv(struct line *, int, struct line *, int, int *);
19708f90673Sjfb static void	 unravel(int);
19808f90673Sjfb static void	 unsort(struct line *, int, int *);
199ff1f7a8eSxsa static void	 change(const char *, FILE *, const char *, FILE *, int,
200ff1f7a8eSxsa 		    int, int, int);
20108f90673Sjfb static void	 sort(struct line *, int);
20208f90673Sjfb static int	 ignoreline(char *);
20308f90673Sjfb static int	 asciifile(FILE *);
20408f90673Sjfb static int	 fetch(long *, int, int, FILE *, int, int);
20508f90673Sjfb static int	 newcand(int, int, int);
20608f90673Sjfb static int	 search(int *, int, int);
20708f90673Sjfb static int	 skipline(FILE *);
20808f90673Sjfb static int	 isqrt(int);
20908f90673Sjfb static int	 stone(int *, int, int *, int *);
21008f90673Sjfb static int	 readhash(FILE *);
21108f90673Sjfb static int	 files_differ(FILE *, FILE *);
2125e78344dSjfb static char	*match_function(const long *, int, FILE *);
21308f90673Sjfb static char	*preadline(int, size_t, off_t);
21408f90673Sjfb 
21508f90673Sjfb 
216af5bb824Sniallo #if !defined(RCSPROG)
217af5bb824Sniallo static int Nflag;
218af5bb824Sniallo #endif
219af5bb824Sniallo static int aflag, bflag, dflag, iflag, pflag, tflag, Tflag, wflag;
22078de3304Sniallo static int context = 3;
221f9b67873Sniallo int diff_format = D_NORMAL;
2221ff4dac1Sjoris char *diff_file = NULL;
223c60b216dSxsa char diffargs[128];
22408f90673Sjfb static struct stat stb1, stb2;
225af5bb824Sniallo static char *ifdefname, *ignore_pats;
22608f90673Sjfb regex_t ignore_re;
22708f90673Sjfb 
22808f90673Sjfb static int  *J;			/* will be overlaid on class */
22908f90673Sjfb static int  *class;		/* will be overlaid on file[0] */
23008f90673Sjfb static int  *klist;		/* will be overlaid on file[0] after class */
23108f90673Sjfb static int  *member;		/* will be overlaid on file[1] */
23208f90673Sjfb static int   clen;
23308f90673Sjfb static int   inifdef;		/* whether or not we are in a #ifdef block */
234e4276007Sjfb static int   diff_len[2];
23508f90673Sjfb static int   pref, suff;	/* length of prefix and suffix */
23608f90673Sjfb static int   slen[2];
23708f90673Sjfb static int   anychange;
23808f90673Sjfb static long *ixnew;		/* will be overlaid on file[1] */
23908f90673Sjfb static long *ixold;		/* will be overlaid on klist */
24008f90673Sjfb static struct cand *clist;	/* merely a free storage pot for candidates */
24108f90673Sjfb static int   clistlen;		/* the length of clist */
24208f90673Sjfb static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
24308f90673Sjfb static u_char *chrtran;		/* translation table for case-folding */
24408f90673Sjfb static struct context_vec *context_vec_start;
24508f90673Sjfb static struct context_vec *context_vec_end;
24608f90673Sjfb static struct context_vec *context_vec_ptr;
24708f90673Sjfb 
24808f90673Sjfb #define FUNCTION_CONTEXT_SIZE	41
2495e78344dSjfb static char lastbuf[FUNCTION_CONTEXT_SIZE];
25008f90673Sjfb static int  lastline;
25108f90673Sjfb static int  lastmatchline;
25201af718aSjoris BUF  *diffbuf = NULL;
253f9b67873Sniallo 
25408f90673Sjfb /*
25508f90673Sjfb  * chrtran points to one of 2 translation tables: cup2low if folding upper to
25608f90673Sjfb  * lower case clow2low if not folding case
25708f90673Sjfb  */
25808f90673Sjfb u_char clow2low[256] = {
25908f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
26008f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
26108f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
26208f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
26308f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
26408f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
26508f90673Sjfb 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
26608f90673Sjfb 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
26708f90673Sjfb 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
26808f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
26908f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
27008f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
27108f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
27208f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
27308f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
27408f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
27508f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
27608f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
27708f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
27808f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
27908f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
28008f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
28108f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
28208f90673Sjfb 	0xfd, 0xfe, 0xff
28308f90673Sjfb };
28408f90673Sjfb 
28508f90673Sjfb u_char cup2low[256] = {
28608f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
28708f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
28808f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
28908f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
29008f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
29108f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
29208f90673Sjfb 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
29308f90673Sjfb 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
29408f90673Sjfb 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
29508f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
29608f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
29708f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
29808f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
29908f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
30008f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
30108f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
30208f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
30308f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
30408f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
30508f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
30608f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
30708f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
30808f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
30908f90673Sjfb 	0xfd, 0xfe, 0xff
31008f90673Sjfb };
31108f90673Sjfb 
312af5bb824Sniallo #if !defined(RCSPROG)
313e4276007Sjfb struct cvs_cmd cvs_cmd_diff = {
314e4276007Sjfb 	CVS_OP_DIFF, CVS_REQ_DIFF, "diff",
315e4276007Sjfb 	{ "di", "dif" },
316e4276007Sjfb 	"Show differences between revisions",
317c9150269Sxsa 	"[-cilNnpu] [[-D date] [-r rev] [-D date2 | -r rev2]] "
318c9150269Sxsa 	"[-k mode] [file ...]",
319c9150269Sxsa 	"cD:iklNnpr:Ru",
32016cfc147Sjoris 	NULL,
32116cfc147Sjoris 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
322e4276007Sjfb 	cvs_diff_init,
323e4276007Sjfb 	cvs_diff_pre_exec,
324e4276007Sjfb 	cvs_diff_remote,
325e4276007Sjfb 	cvs_diff_local,
326e4276007Sjfb 	NULL,
327e4276007Sjfb 	cvs_diff_cleanup,
328e4276007Sjfb 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
329e4276007Sjfb };
330e4276007Sjfb 
331e4276007Sjfb 
332e4276007Sjfb struct cvs_cmd cvs_cmd_rdiff = {
333e4276007Sjfb 	CVS_OP_RDIFF, CVS_REQ_DIFF, "rdiff",
334c9150269Sxsa 	{ "pa", "patch" },
335e4276007Sjfb 	"Create 'patch' format diffs between releases",
336c9150269Sxsa 	"[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev "
337c9150269Sxsa 	"[-D date2 | -rev2] module ...",
338c9150269Sxsa 	"cD:flRr:stuV:",
339e4276007Sjfb 	NULL,
340e4276007Sjfb 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
341e4276007Sjfb 	cvs_diff_init,
342e4276007Sjfb 	cvs_diff_pre_exec,
343e4276007Sjfb 	cvs_diff_remote,
344e4276007Sjfb 	cvs_diff_local,
345e4276007Sjfb 	NULL,
346e4276007Sjfb 	cvs_diff_cleanup,
34744381dcbSjoris 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
34816cfc147Sjoris };
349af5bb824Sniallo #endif
35008f90673Sjfb 
351af5bb824Sniallo #if !defined(RCSPROG)
35216cfc147Sjoris static struct diff_arg *dap = NULL;
35316cfc147Sjoris static int recurse;
35416cfc147Sjoris 
355e4276007Sjfb static int
356e4276007Sjfb cvs_diff_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg)
35708f90673Sjfb {
35816cfc147Sjoris 	int ch;
35908f90673Sjfb 
360*0450b43bSjoris 	dap = (struct diff_arg *)xmalloc(sizeof(*dap));
36116cfc147Sjoris 	dap->date1 = dap->date2 = dap->rev1 = dap->rev2 = NULL;
362dc6a6879Sjfb 	strlcpy(diffargs, argv[0], sizeof(diffargs));
363dc6a6879Sjfb 
364e4276007Sjfb 	while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) {
36508f90673Sjfb 		switch (ch) {
36608f90673Sjfb 		case 'c':
367f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
368f9b67873Sniallo 			diff_format = D_CONTEXT;
36908f90673Sjfb 			break;
37008f90673Sjfb 		case 'D':
37116cfc147Sjoris 			if (dap->date1 == NULL && dap->rev1 == NULL) {
37216cfc147Sjoris 				dap->date1 = optarg;
37316cfc147Sjoris 			} else if (dap->date2 == NULL && dap->rev2 == NULL) {
37416cfc147Sjoris 				dap->date2 = optarg;
37516cfc147Sjoris 			} else {
37608f90673Sjfb 				cvs_log(LP_ERR,
37708f90673Sjfb 				    "no more than two revisions/dates can "
37808f90673Sjfb 				    "be specified");
37908f90673Sjfb 			}
38008f90673Sjfb 			break;
38108f90673Sjfb 		case 'l':
382f5638424Sjfb 			strlcat(diffargs, " -l", sizeof(diffargs));
38308f90673Sjfb 			recurse = 0;
384e4276007Sjfb 			cvs_cmd_diff.file_flags &= ~CF_RECURSE;
38508f90673Sjfb 			break;
38608f90673Sjfb 		case 'i':
387f5638424Sjfb 			strlcat(diffargs, " -i", sizeof(diffargs));
38808f90673Sjfb 			iflag = 1;
38908f90673Sjfb 			break;
390c710bc5aSjfb 		case 'N':
391c710bc5aSjfb 			strlcat(diffargs, " -N", sizeof(diffargs));
392c710bc5aSjfb 			Nflag = 1;
393c710bc5aSjfb 			break;
394394180a4Sjfb 		case 'n':
395394180a4Sjfb 			strlcat(diffargs, " -n", sizeof(diffargs));
396f9b67873Sniallo 			diff_format = D_RCSDIFF;
397394180a4Sjfb 			break;
3985e78344dSjfb 		case 'p':
3995e78344dSjfb 			strlcat(diffargs, " -p", sizeof(diffargs));
4005e78344dSjfb 			pflag = 1;
4015e78344dSjfb 			break;
40208f90673Sjfb 		case 'r':
40316cfc147Sjoris 			if ((dap->rev1 == NULL) && (dap->date1 == NULL)) {
40416cfc147Sjoris 				dap->rev1 = optarg;
40516cfc147Sjoris 			} else if ((dap->rev2 == NULL) &&
40616cfc147Sjoris 			    (dap->date2 == NULL)) {
40716cfc147Sjoris 				dap->rev2 = optarg;
40816cfc147Sjoris 			} else {
40908f90673Sjfb 				cvs_log(LP_ERR,
41008f90673Sjfb 				    "no more than two revisions/dates can "
41108f90673Sjfb 				    "be specified");
41231274bbfSjoris 				return (CVS_EX_USAGE);
41308f90673Sjfb 			}
41408f90673Sjfb 			break;
415f203c484Sjoris 		case 'R':
416e4276007Sjfb 			cvs_cmd_diff.file_flags |= CF_RECURSE;
417f203c484Sjoris 			break;
41808f90673Sjfb 		case 'u':
419f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
420f9b67873Sniallo 			diff_format = D_UNIFIED;
42108f90673Sjfb 			break;
42208f90673Sjfb 		default:
42331274bbfSjoris 			return (CVS_EX_USAGE);
42408f90673Sjfb 		}
42508f90673Sjfb 	}
42608f90673Sjfb 
42716cfc147Sjoris 	*arg = optind;
428dc6a6879Sjfb 	return (0);
429dc6a6879Sjfb }
430dc6a6879Sjfb 
43116cfc147Sjoris int
43216cfc147Sjoris cvs_diff_cleanup(void)
43316cfc147Sjoris {
434e4276007Sjfb 	if (dap != NULL) {
435*0450b43bSjoris 		xfree(dap);
436e4276007Sjfb 		dap = NULL;
437e4276007Sjfb 	}
43816cfc147Sjoris 	return (0);
43916cfc147Sjoris }
440dc6a6879Sjfb 
441dc6a6879Sjfb /*
442e4276007Sjfb  * cvs_diff_pre_exec()
443dc6a6879Sjfb  *
444dc6a6879Sjfb  */
445dc6a6879Sjfb int
446e4276007Sjfb cvs_diff_pre_exec(struct cvsroot *root)
447dc6a6879Sjfb {
448e4276007Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
44908f90673Sjfb 		/* send the flags */
45048dc77e6Sxsa 		if ((Nflag == 1) && (cvs_sendarg(root, "-N", 0) < 0))
45131274bbfSjoris 			return (CVS_EX_PROTO);
45248dc77e6Sxsa 		if ((pflag ==1) && (cvs_sendarg(root, "-p", 0) < 0))
45331274bbfSjoris 			return (CVS_EX_PROTO);
4545e78344dSjfb 
455f9b67873Sniallo 		if (diff_format == D_CONTEXT) {
456610801e8Sxsa 			if (cvs_sendarg(root, "-c", 0) < 0)
457610801e8Sxsa 				return (CVS_EX_PROTO);
458f9b67873Sniallo 		} else if (diff_format == D_UNIFIED) {
459610801e8Sxsa 			if (cvs_sendarg(root, "-u", 0) < 0)
460610801e8Sxsa 				return (CVS_EX_PROTO);
461610801e8Sxsa 		}
46208f90673Sjfb 
463dc6a6879Sjfb 		if (dap->rev1 != NULL) {
464610801e8Sxsa 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
465610801e8Sxsa 			    (cvs_sendarg(root, dap->rev1, 0) < 0))
466610801e8Sxsa 				return (CVS_EX_PROTO);
4673917c9bfSderaadt 		} else if (dap->date1 != NULL) {
468610801e8Sxsa 			if ((cvs_sendarg(root, "-D", 0) < 0) ||
469610801e8Sxsa 			    (cvs_sendarg(root, dap->date1, 0) < 0))
470610801e8Sxsa 				return (CVS_EX_PROTO);
47108f90673Sjfb 		}
472dc6a6879Sjfb 		if (dap->rev2 != NULL) {
473610801e8Sxsa 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
474610801e8Sxsa 			    (cvs_sendarg(root, dap->rev2, 0) < 0))
475610801e8Sxsa 				return (CVS_EX_PROTO);
4763917c9bfSderaadt 		} else if (dap->date2 != NULL) {
477610801e8Sxsa 			if ((cvs_sendarg(root, "-D", 0) < 0) ||
478610801e8Sxsa 			    (cvs_sendarg(root, dap->date2, 0) < 0))
479610801e8Sxsa 				return  (CVS_EX_PROTO);
48008f90673Sjfb 		}
481e4276007Sjfb 	}
48208f90673Sjfb 
48308f90673Sjfb 	return (0);
48408f90673Sjfb }
48508f90673Sjfb 
48608f90673Sjfb 
48708f90673Sjfb /*
48808f90673Sjfb  * cvs_diff_file()
48908f90673Sjfb  *
49008f90673Sjfb  * Diff a single file.
49108f90673Sjfb  */
492e4276007Sjfb static int
493e4276007Sjfb cvs_diff_remote(struct cvs_file *cfp, void *arg)
49408f90673Sjfb {
495e4276007Sjfb 	char *dir, *repo;
496e4276007Sjfb 	char fpath[MAXPATHLEN], dfpath[MAXPATHLEN];
497dc6a6879Sjfb 	struct cvsroot *root;
498dc6a6879Sjfb 
499dc6a6879Sjfb 	if (cfp->cf_type == DT_DIR) {
500895e6cf6Sjfb 		if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
501bb029937Sjfb 			root = cfp->cf_parent->cf_root;
502b904ba2eSjoris 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
5033917c9bfSderaadt 		} else {
504bb029937Sjfb 			root = cfp->cf_root;
50516cfc147Sjoris #if 0
506dc6a6879Sjfb 			if ((cfp->cf_parent == NULL) ||
507bb029937Sjfb 			    (root != cfp->cf_parent->cf_root)) {
508dc6a6879Sjfb 				cvs_connect(root);
509e4276007Sjfb 				cvs_diff_pre_exec(root);
510dc6a6879Sjfb 			}
51116cfc147Sjoris #endif
512dc6a6879Sjfb 
513dc6a6879Sjfb 			cvs_senddir(root, cfp);
514895e6cf6Sjfb 		}
515895e6cf6Sjfb 
516dc6a6879Sjfb 		return (0);
517dc6a6879Sjfb 	}
51808f90673Sjfb 
5192d5b8b1dSjfb 	if (cfp->cf_cvstat == CVS_FST_LOST) {
520b904ba2eSjoris 		cvs_log(LP_WARN, "cannot find file %s", cfp->cf_name);
5212d5b8b1dSjfb 		return (0);
5222d5b8b1dSjfb 	}
5232d5b8b1dSjfb 
524c710bc5aSjfb 	diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath));
525895e6cf6Sjfb 
526dc6a6879Sjfb 	if (cfp->cf_parent != NULL) {
527c710bc5aSjfb 		dir = cvs_file_getpath(cfp->cf_parent, dfpath, sizeof(dfpath));
528bb029937Sjfb 		root = cfp->cf_parent->cf_root;
529bb029937Sjfb 		repo = cfp->cf_parent->cf_repo;
5303917c9bfSderaadt 	} else {
531dc6a6879Sjfb 		dir = ".";
532895e6cf6Sjfb 		root = NULL;
533dc6a6879Sjfb 		repo = NULL;
53408f90673Sjfb 	}
53508f90673Sjfb 
536dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
537e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
538dc6a6879Sjfb 		return (0);
53908f90673Sjfb 	}
54008f90673Sjfb 
541e4276007Sjfb 	if (cvs_sendentry(root, cfp) < 0)
54231274bbfSjoris 		return (CVS_EX_PROTO);
54308f90673Sjfb 
544dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UPTODATE) {
545e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name);
54608f90673Sjfb 		return (0);
54708f90673Sjfb 	}
54808f90673Sjfb 
54908f90673Sjfb 	/* at this point, the file is modified */
550e4276007Sjfb 	if ((cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name) < 0) ||
551e4276007Sjfb 	    (cvs_sendfile(root, diff_file) < 0))
552e4276007Sjfb 		return (CVS_EX_PROTO);
553e4276007Sjfb 
554e4276007Sjfb 	return (0);
555e4276007Sjfb }
556e4276007Sjfb 
557e4276007Sjfb static int
558e4276007Sjfb cvs_diff_local(CVSFILE *cf, void *arg)
559e4276007Sjfb {
560e4276007Sjfb 	int len;
561e4276007Sjfb 	char *repo, buf[64];
562e4276007Sjfb 	char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
563e4276007Sjfb 	char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN];
564e4276007Sjfb 	BUF *b1, *b2;
565e4276007Sjfb 	RCSNUM *r1, *r2;
566e4276007Sjfb 	RCSFILE *rf;
567e4276007Sjfb 	struct cvsroot *root;
568bf951d2dSniallo 	struct timeval tv[2], tv2[2];
569bf951d2dSniallo 
570bf951d2dSniallo 	memset(&tv, 0, sizeof(tv));
571bf951d2dSniallo 	memset(&tv2, 0, sizeof(tv2));
572e4276007Sjfb 
573e4276007Sjfb 	rf = NULL;
574e4276007Sjfb 	root = CVS_DIR_ROOT(cf);
575e4276007Sjfb 	repo = CVS_DIR_REPO(cf);
576e4276007Sjfb 	diff_file = cvs_file_getpath(cf, fpath, sizeof(fpath));
577e4276007Sjfb 
578e4276007Sjfb 	if (cf->cf_type == DT_DIR) {
579b87c59bdSxsa 		if (verbosity > 1)
580246675edSxsa 			cvs_log(LP_NOTICE, "Diffing %s", fpath);
581e4276007Sjfb 		return (0);
582e4276007Sjfb 	}
583e4276007Sjfb 
584e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_LOST) {
585e4276007Sjfb 		cvs_log(LP_WARN, "cannot find file %s", cf->cf_name);
586e4276007Sjfb 		return (0);
587e4276007Sjfb 	}
588e4276007Sjfb 
589e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_UNKNOWN) {
590e4276007Sjfb 		cvs_log(LP_WARN, "I know nothing about %s", diff_file);
591e4276007Sjfb 		return (0);
5925c0cd766Sniallo 	} else if (cf->cf_cvstat == CVS_FST_UPTODATE)
593e4276007Sjfb 		return (0);
594e4276007Sjfb 
595e4276007Sjfb 	/* at this point, the file is modified */
596e4276007Sjfb 	len = snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
597dc6a6879Sjfb 	    root->cr_dir, repo, diff_file, RCS_FILE_EXT);
598e4276007Sjfb 	if (len == -1 || len >= (int)sizeof(rcspath)) {
59927b85f85Sxsa 		errno = ENAMETOOLONG;
60027b85f85Sxsa 		cvs_log(LP_ERRNO, "%s", rcspath);
60101b3d77aSjoris 		return (CVS_EX_DATA);
60227b85f85Sxsa 	}
60308f90673Sjfb 
6041b6534b8Sjfb 	rf = rcs_open(rcspath, RCS_READ);
605dc6a6879Sjfb 	if (rf == NULL) {
60631274bbfSjoris 		return (CVS_EX_DATA);
607dc6a6879Sjfb 	}
60808f90673Sjfb 
609dc6a6879Sjfb 	cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file,
61008f90673Sjfb 	    RCS_DIFF_DIV, rcspath);
61108f90673Sjfb 
612dc6a6879Sjfb 	if (dap->rev1 == NULL)
613e4276007Sjfb 		r1 = cf->cf_lrev;
61408f90673Sjfb 	else {
61525b74b48Sjfb 		if ((r1 = rcsnum_parse(dap->rev1)) == NULL) {
61664672f74Sjoris 			rcs_close(rf);
61731274bbfSjoris 			return (CVS_EX_DATA);
6187f535ec4Sjfb 		}
61908f90673Sjfb 	}
62008f90673Sjfb 
621dc6a6879Sjfb 	cvs_printf("retrieving revision %s\n",
62208f90673Sjfb 	    rcsnum_tostr(r1, buf, sizeof(buf)));
62308f90673Sjfb 	b1 = rcs_getrev(rf, r1);
62408f90673Sjfb 
62595ae1173Sjoris 	if (b1 == NULL) {
626289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
62795ae1173Sjoris 		    rcsnum_tostr(r1, buf, sizeof(buf)));
62895ae1173Sjoris 		if (r1 != cf->cf_lrev)
62995ae1173Sjoris 			rcsnum_free(r1);
63064672f74Sjoris 		rcs_close(rf);
63195ae1173Sjoris 		return (CVS_EX_DATA);
63295ae1173Sjoris 	}
633bf951d2dSniallo 	tv[0].tv_sec = (long)rcs_rev_getdate(rf, r1);
634bf951d2dSniallo 	tv[1].tv_sec = tv[0].tv_sec;
63595ae1173Sjoris 
636e4276007Sjfb 	if (r1 != cf->cf_lrev)
6377f535ec4Sjfb 		rcsnum_free(r1);
6387f535ec4Sjfb 
639dc6a6879Sjfb 	if (dap->rev2 != NULL) {
640dc6a6879Sjfb 		cvs_printf("retrieving revision %s\n", dap->rev2);
64125b74b48Sjfb 		if ((r2 = rcsnum_parse(dap->rev2)) == NULL) {
64264672f74Sjoris 			rcs_close(rf);
64331274bbfSjoris 			return (CVS_EX_DATA);
6447f535ec4Sjfb 		}
64508f90673Sjfb 		b2 = rcs_getrev(rf, r2);
646f3e6c8ebSniallo 		tv2[0].tv_sec = (long)rcs_rev_getdate(rf, r2);
647f3e6c8ebSniallo 		tv2[1].tv_sec = tv2[0].tv_sec;
6487f535ec4Sjfb 		rcsnum_free(r2);
6493917c9bfSderaadt 	} else {
650f3e6c8ebSniallo 		struct stat st;
651f3e6c8ebSniallo 		if (stat(diff_file, &st) < 0) {
652f3e6c8ebSniallo 			cvs_log(LP_ERR, "failed to retrieve revision %s\n",
653f3e6c8ebSniallo 			    dap->rev2);
654f3e6c8ebSniallo 			cvs_buf_free(b1);
655f3e6c8ebSniallo 			return (CVS_EX_DATA);
656f3e6c8ebSniallo 		}
657dc6a6879Sjfb 		b2 = cvs_buf_load(diff_file, BUF_AUTOEXT);
658f3e6c8ebSniallo 		tv2[0].tv_sec = st.st_mtime;
659f3e6c8ebSniallo 		tv2[1].tv_sec = st.st_mtime;
66008f90673Sjfb 	}
66108f90673Sjfb 
662dc6a6879Sjfb 	rcs_close(rf);
663dc6a6879Sjfb 
66495ae1173Sjoris 	if (b2 == NULL) {
665289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
66695ae1173Sjoris 		    dap->rev2);
66795ae1173Sjoris 		cvs_buf_free(b1);
66895ae1173Sjoris 		return (CVS_EX_DATA);
66995ae1173Sjoris 	}
67095ae1173Sjoris 
6715ac8b1e7Sjoris 	cvs_printf("%s", diffargs);
6725ac8b1e7Sjoris 	cvs_printf(" -r%s", buf);
673dc6a6879Sjfb 	if (dap->rev2 != NULL)
6745ac8b1e7Sjoris 		cvs_printf(" -r%s", dap->rev2);
6755ac8b1e7Sjoris 	cvs_printf(" %s\n", diff_file);
6761dee9299Sxsa 	strlcpy(path_tmp1, cvs_tmpdir, sizeof(path_tmp1));
6771dee9299Sxsa 	strlcat(path_tmp1, "/diff1.XXXXXXXXXX", sizeof(path_tmp1));
6787f535ec4Sjfb 	if (cvs_buf_write_stmp(b1, path_tmp1, 0600) == -1) {
6797f535ec4Sjfb 		cvs_buf_free(b1);
6807f535ec4Sjfb 		cvs_buf_free(b2);
68131274bbfSjoris 		return (CVS_EX_DATA);
6827f535ec4Sjfb 	}
6837f535ec4Sjfb 	cvs_buf_free(b1);
684bf951d2dSniallo 	if (utimes(path_tmp1, (const struct timeval *)&tv) < 0)
685bf951d2dSniallo 		cvs_log(LP_ERRNO, "error setting utimes");
6867f535ec4Sjfb 
6871dee9299Sxsa 	strlcpy(path_tmp2, cvs_tmpdir, sizeof(path_tmp2));
6881dee9299Sxsa 	strlcat(path_tmp2, "/diff2.XXXXXXXXXX", sizeof(path_tmp2));
689946f6157Sdjm 	if (cvs_buf_write_stmp(b2, path_tmp2, 0600) == -1) {
6907f535ec4Sjfb 		cvs_buf_free(b2);
691946f6157Sdjm 		(void)unlink(path_tmp1);
69231274bbfSjoris 		return (CVS_EX_DATA);
693946f6157Sdjm 	}
6947f535ec4Sjfb 	cvs_buf_free(b2);
695bf951d2dSniallo 	if (utimes(path_tmp2, (const struct timeval *)&tv2) < 0)
696bf951d2dSniallo 		cvs_log(LP_ERRNO, "error setting utimes");
6977f535ec4Sjfb 
698f9b67873Sniallo 	cvs_diffreg(path_tmp1, path_tmp2, NULL);
699946f6157Sdjm 	(void)unlink(path_tmp1);
700946f6157Sdjm 	(void)unlink(path_tmp2);
70108f90673Sjfb 
70208f90673Sjfb 	return (0);
70308f90673Sjfb }
704af5bb824Sniallo #endif
70508f90673Sjfb 
70608f90673Sjfb 
70708f90673Sjfb int
708f9b67873Sniallo cvs_diffreg(const char *file1, const char *file2, BUF *out)
70908f90673Sjfb {
71008f90673Sjfb 	FILE *f1, *f2;
71108f90673Sjfb 	int i, rval;
7127f535ec4Sjfb 	void *tmp;
71308f90673Sjfb 
71408f90673Sjfb 	f1 = f2 = NULL;
71508f90673Sjfb 	rval = D_SAME;
71608f90673Sjfb 	anychange = 0;
71708f90673Sjfb 	lastline = 0;
71808f90673Sjfb 	lastmatchline = 0;
71908f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
72008f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
721f9b67873Sniallo 	if (out != NULL)
722f9b67873Sniallo 		diffbuf = out;
72308f90673Sjfb 
72408f90673Sjfb 	f1 = fopen(file1, "r");
72508f90673Sjfb 	if (f1 == NULL) {
72608f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
72708f90673Sjfb 		goto closem;
72808f90673Sjfb 	}
72908f90673Sjfb 
73008f90673Sjfb 	f2 = fopen(file2, "r");
73108f90673Sjfb 	if (f2 == NULL) {
73208f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
73308f90673Sjfb 		goto closem;
73408f90673Sjfb 	}
73508f90673Sjfb 
736bf951d2dSniallo 	if (stat(file1, &stb1) < 0) {
737bf951d2dSniallo 		cvs_log(LP_ERRNO, "%s", file1);
738bf951d2dSniallo 		goto closem;
739bf951d2dSniallo 	}
740bf951d2dSniallo 	if (stat(file2, &stb2) < 0) {
741bf951d2dSniallo 		cvs_log(LP_ERRNO, "%s", file2);
742bf951d2dSniallo 		goto closem;
743bf951d2dSniallo 	}
74408f90673Sjfb 	switch (files_differ(f1, f2)) {
74508f90673Sjfb 	case 0:
74608f90673Sjfb 		goto closem;
74708f90673Sjfb 	case 1:
74808f90673Sjfb 		break;
74908f90673Sjfb 	default:
75008f90673Sjfb 		/* error */
75108f90673Sjfb 		goto closem;
75208f90673Sjfb 	}
75308f90673Sjfb 
75408f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
75508f90673Sjfb 		rval = D_BINARY;
75608f90673Sjfb 		goto closem;
75708f90673Sjfb 	}
7587f535ec4Sjfb 	if ((prepare(0, f1, stb1.st_size) < 0) ||
7597f535ec4Sjfb 	    (prepare(1, f2, stb2.st_size) < 0)) {
7607f535ec4Sjfb 		goto closem;
7617f535ec4Sjfb 	}
76208f90673Sjfb 	prune();
76308f90673Sjfb 	sort(sfile[0], slen[0]);
76408f90673Sjfb 	sort(sfile[1], slen[1]);
76508f90673Sjfb 
76608f90673Sjfb 	member = (int *)file[1];
76708f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
768*0450b43bSjoris 	tmp = xrealloc(member, (slen[1] + 2) * sizeof(int));
7697f535ec4Sjfb 	member = (int *)tmp;
77008f90673Sjfb 
77108f90673Sjfb 	class = (int *)file[0];
77208f90673Sjfb 	unsort(sfile[0], slen[0], class);
773*0450b43bSjoris 	tmp = xrealloc(class, (slen[0] + 2) * sizeof(int));
7747f535ec4Sjfb 	class = (int *)tmp;
77508f90673Sjfb 
776*0450b43bSjoris 	klist = xmalloc((slen[0] + 2) * sizeof(int));
77708f90673Sjfb 	clen = 0;
77808f90673Sjfb 	clistlen = 100;
779*0450b43bSjoris 	clist = xmalloc(clistlen * sizeof(cand));
780ece76a70Sjoris 
781ece76a70Sjoris 	if ((i = stone(class, slen[0], member, klist)) < 0)
782ece76a70Sjoris 		goto closem;
783ece76a70Sjoris 
784*0450b43bSjoris 	xfree(member);
785*0450b43bSjoris 	xfree(class);
78608f90673Sjfb 
787*0450b43bSjoris 	tmp = xrealloc(J, (diff_len[0] + 2) * sizeof(int));
78818501190Sniallo 	J = (int *)tmp;
78908f90673Sjfb 	unravel(klist[i]);
790*0450b43bSjoris 	xfree(clist);
791*0450b43bSjoris 	xfree(klist);
79208f90673Sjfb 
793*0450b43bSjoris 	tmp = xrealloc(ixold, (diff_len[0] + 2) * sizeof(long));
79418501190Sniallo 	ixold = (long *)tmp;
795*0450b43bSjoris 
796*0450b43bSjoris 	tmp = xrealloc(ixnew, (diff_len[1] + 2) * sizeof(long));
79718501190Sniallo 	ixnew = (long *)tmp;
79808f90673Sjfb 	check(f1, f2);
79908f90673Sjfb 	output(file1, f1, file2, f2);
80008f90673Sjfb 
80108f90673Sjfb closem:
80237af5b8bSxsa 	if (anychange == 1) {
80308f90673Sjfb 		if (rval == D_SAME)
80408f90673Sjfb 			rval = D_DIFFER;
80508f90673Sjfb 	}
80608f90673Sjfb 	if (f1 != NULL)
80708f90673Sjfb 		fclose(f1);
80808f90673Sjfb 	if (f2 != NULL)
80908f90673Sjfb 		fclose(f2);
8107f535ec4Sjfb 
81108f90673Sjfb 	return (rval);
81208f90673Sjfb }
81308f90673Sjfb 
81408f90673Sjfb /*
81508f90673Sjfb  * Check to see if the given files differ.
81608f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
81708f90673Sjfb  * XXX - could use code from cmp(1) [faster]
81808f90673Sjfb  */
81908f90673Sjfb static int
82008f90673Sjfb files_differ(FILE *f1, FILE *f2)
82108f90673Sjfb {
82208f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
82308f90673Sjfb 	size_t i, j;
82408f90673Sjfb 
82508f90673Sjfb 	if (stb1.st_size != stb2.st_size)
82608f90673Sjfb 		return (1);
82708f90673Sjfb 	for (;;) {
828f1901a5aSxsa 		i = fread(buf1, (size_t)1, sizeof(buf1), f1);
829f1901a5aSxsa 		j = fread(buf2, (size_t)1, sizeof(buf2), f2);
83008f90673Sjfb 		if (i != j)
83108f90673Sjfb 			return (1);
83237af5b8bSxsa 		if ((i == 0) && (j == 0)) {
83308f90673Sjfb 			if (ferror(f1) || ferror(f2))
83408f90673Sjfb 				return (1);
83508f90673Sjfb 			return (0);
83608f90673Sjfb 		}
83708f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
83808f90673Sjfb 			return (1);
83908f90673Sjfb 	}
84008f90673Sjfb }
84108f90673Sjfb 
8427f535ec4Sjfb static int
84308f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
84408f90673Sjfb {
8457f535ec4Sjfb 	void *tmp;
84608f90673Sjfb 	struct line *p;
84708f90673Sjfb 	int j, h;
84808f90673Sjfb 	size_t sz;
84908f90673Sjfb 
85008f90673Sjfb 	rewind(fd);
85108f90673Sjfb 
852c48da046Sxsa 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
85308f90673Sjfb 	if (sz < 100)
85408f90673Sjfb 		sz = 100;
85508f90673Sjfb 
856*0450b43bSjoris 	p = (struct line *)xmalloc((sz + 3) * sizeof(struct line));
85708f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
85808f90673Sjfb 		if (j == (int)sz) {
85908f90673Sjfb 			sz = sz * 3 / 2;
860*0450b43bSjoris 			tmp = xrealloc(p, (sz + 3) * sizeof(struct line));
8617f535ec4Sjfb 			p = (struct line *)tmp;
86208f90673Sjfb 		}
86308f90673Sjfb 		p[++j].value = h;
86408f90673Sjfb 	}
865e4276007Sjfb 	diff_len[i] = j;
86608f90673Sjfb 	file[i] = p;
8677f535ec4Sjfb 
8687f535ec4Sjfb 	return (0);
86908f90673Sjfb }
87008f90673Sjfb 
87108f90673Sjfb static void
87208f90673Sjfb prune(void)
87308f90673Sjfb {
87408f90673Sjfb 	int i, j;
87508f90673Sjfb 
876e4276007Sjfb 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
87708f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
87808f90673Sjfb 	    pref++)
87908f90673Sjfb 		;
880e4276007Sjfb 	for (suff = 0;
881e4276007Sjfb 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
882e4276007Sjfb 	    (file[0][diff_len[0] - suff].value ==
883e4276007Sjfb 	    file[1][diff_len[1] - suff].value);
88408f90673Sjfb 	    suff++)
88508f90673Sjfb 		;
88608f90673Sjfb 	for (j = 0; j < 2; j++) {
88708f90673Sjfb 		sfile[j] = file[j] + pref;
888e4276007Sjfb 		slen[j] = diff_len[j] - pref - suff;
88908f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
89008f90673Sjfb 			sfile[j][i].serial = i;
89108f90673Sjfb 	}
89208f90673Sjfb }
89308f90673Sjfb 
89408f90673Sjfb static void
89508f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
89608f90673Sjfb {
89708f90673Sjfb 	int i, j;
89808f90673Sjfb 
89908f90673Sjfb 	i = j = 1;
90008f90673Sjfb 	while (i <= n && j <= m) {
90108f90673Sjfb 		if (a[i].value < b[j].value)
90208f90673Sjfb 			a[i++].value = 0;
90308f90673Sjfb 		else if (a[i].value == b[j].value)
90408f90673Sjfb 			a[i++].value = j;
90508f90673Sjfb 		else
90608f90673Sjfb 			j++;
90708f90673Sjfb 	}
90808f90673Sjfb 	while (i <= n)
90908f90673Sjfb 		a[i++].value = 0;
91008f90673Sjfb 	b[m + 1].value = 0;
91108f90673Sjfb 	j = 0;
91208f90673Sjfb 	while (++j <= m) {
91308f90673Sjfb 		c[j] = -b[j].serial;
91408f90673Sjfb 		while (b[j + 1].value == b[j].value) {
91508f90673Sjfb 			j++;
91608f90673Sjfb 			c[j] = b[j].serial;
91708f90673Sjfb 		}
91808f90673Sjfb 	}
91908f90673Sjfb 	c[j] = -1;
92008f90673Sjfb }
92108f90673Sjfb 
92208f90673Sjfb /* Code taken from ping.c */
92308f90673Sjfb static int
92408f90673Sjfb isqrt(int n)
92508f90673Sjfb {
92608f90673Sjfb 	int y, x = 1;
92708f90673Sjfb 
92808f90673Sjfb 	if (n == 0)
92908f90673Sjfb 		return (0);
93008f90673Sjfb 
93108f90673Sjfb 	do { /* newton was a stinker */
93208f90673Sjfb 		y = x;
93308f90673Sjfb 		x = n / x;
93408f90673Sjfb 		x += y;
93508f90673Sjfb 		x /= 2;
93608f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
93708f90673Sjfb 
93808f90673Sjfb 	return (x);
93908f90673Sjfb }
94008f90673Sjfb 
94108f90673Sjfb static int
94208f90673Sjfb stone(int *a, int n, int *b, int *c)
94308f90673Sjfb {
944ece76a70Sjoris 	int ret;
94508f90673Sjfb 	int i, k, y, j, l;
94608f90673Sjfb 	int oldc, tc, oldl;
94708f90673Sjfb 	u_int numtries;
94808f90673Sjfb 
949cc649edbSjfb 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
950cc649edbSjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
95108f90673Sjfb 
95208f90673Sjfb 	k = 0;
953ece76a70Sjoris 	if ((ret = newcand(0, 0, 0)) < 0)
954ece76a70Sjoris 		return (-1);
955ece76a70Sjoris 	c[0] = ret;
95608f90673Sjfb 	for (i = 1; i <= n; i++) {
95708f90673Sjfb 		j = a[i];
95808f90673Sjfb 		if (j == 0)
95908f90673Sjfb 			continue;
96008f90673Sjfb 		y = -b[j];
96108f90673Sjfb 		oldl = 0;
96208f90673Sjfb 		oldc = c[0];
96308f90673Sjfb 		numtries = 0;
96408f90673Sjfb 		do {
96508f90673Sjfb 			if (y <= clist[oldc].y)
96608f90673Sjfb 				continue;
96708f90673Sjfb 			l = search(c, k, y);
96808f90673Sjfb 			if (l != oldl + 1)
96908f90673Sjfb 				oldc = c[l - 1];
97008f90673Sjfb 			if (l <= k) {
97108f90673Sjfb 				if (clist[c[l]].y <= y)
97208f90673Sjfb 					continue;
97308f90673Sjfb 				tc = c[l];
974ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
975ece76a70Sjoris 					return (-1);
976ece76a70Sjoris 				c[l] = ret;
97708f90673Sjfb 				oldc = tc;
97808f90673Sjfb 				oldl = l;
97908f90673Sjfb 				numtries++;
98008f90673Sjfb 			} else {
981ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
982ece76a70Sjoris 					return (-1);
983ece76a70Sjoris 				c[l] = ret;
98408f90673Sjfb 				k++;
98508f90673Sjfb 				break;
98608f90673Sjfb 			}
98708f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
98808f90673Sjfb 	}
98908f90673Sjfb 	return (k);
99008f90673Sjfb }
99108f90673Sjfb 
99208f90673Sjfb static int
99308f90673Sjfb newcand(int x, int y, int pred)
99408f90673Sjfb {
99518501190Sniallo 	struct cand *q, *tmp;
99618501190Sniallo 	int newclistlen;
99708f90673Sjfb 
99808f90673Sjfb 	if (clen == clistlen) {
99918501190Sniallo 		newclistlen = clistlen * 11 / 10;
1000*0450b43bSjoris 		tmp = xrealloc(clist, newclistlen * sizeof(cand));
100118501190Sniallo 		clist = tmp;
100218501190Sniallo 		clistlen = newclistlen;
100308f90673Sjfb 	}
100408f90673Sjfb 	q = clist + clen;
100508f90673Sjfb 	q->x = x;
100608f90673Sjfb 	q->y = y;
100708f90673Sjfb 	q->pred = pred;
100808f90673Sjfb 	return (clen++);
100908f90673Sjfb }
101008f90673Sjfb 
101108f90673Sjfb static int
101208f90673Sjfb search(int *c, int k, int y)
101308f90673Sjfb {
101408f90673Sjfb 	int i, j, l, t;
101508f90673Sjfb 
101608f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
101708f90673Sjfb 		return (k + 1);
101808f90673Sjfb 	i = 0;
101908f90673Sjfb 	j = k + 1;
102008f90673Sjfb 	while (1) {
102108f90673Sjfb 		l = i + j;
102208f90673Sjfb 		if ((l >>= 1) <= i)
102308f90673Sjfb 			break;
102408f90673Sjfb 		t = clist[c[l]].y;
102508f90673Sjfb 		if (t > y)
102608f90673Sjfb 			j = l;
102708f90673Sjfb 		else if (t < y)
102808f90673Sjfb 			i = l;
102908f90673Sjfb 		else
103008f90673Sjfb 			return (l);
103108f90673Sjfb 	}
103208f90673Sjfb 	return (l + 1);
103308f90673Sjfb }
103408f90673Sjfb 
103508f90673Sjfb static void
103608f90673Sjfb unravel(int p)
103708f90673Sjfb {
103808f90673Sjfb 	struct cand *q;
103908f90673Sjfb 	int i;
104008f90673Sjfb 
1041e4276007Sjfb 	for (i = 0; i <= diff_len[0]; i++)
104208f90673Sjfb 		J[i] = i <= pref ? i :
1043e4276007Sjfb 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
104408f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
104508f90673Sjfb 		J[q->x + pref] = q->y + pref;
104608f90673Sjfb }
104708f90673Sjfb 
104808f90673Sjfb /*
104908f90673Sjfb  * Check does double duty:
105008f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
105108f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
105208f90673Sjfb  *  2.  collect random access indexes to the two files
105308f90673Sjfb  */
105408f90673Sjfb static void
105508f90673Sjfb check(FILE *f1, FILE *f2)
105608f90673Sjfb {
105708f90673Sjfb 	int i, j, jackpot, c, d;
105808f90673Sjfb 	long ctold, ctnew;
105908f90673Sjfb 
106008f90673Sjfb 	rewind(f1);
106108f90673Sjfb 	rewind(f2);
106208f90673Sjfb 	j = 1;
106308f90673Sjfb 	ixold[0] = ixnew[0] = 0;
106408f90673Sjfb 	jackpot = 0;
106508f90673Sjfb 	ctold = ctnew = 0;
1066e4276007Sjfb 	for (i = 1; i <= diff_len[0]; i++) {
106708f90673Sjfb 		if (J[i] == 0) {
106808f90673Sjfb 			ixold[i] = ctold += skipline(f1);
106908f90673Sjfb 			continue;
107008f90673Sjfb 		}
107108f90673Sjfb 		while (j < J[i]) {
107208f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
107308f90673Sjfb 			j++;
107408f90673Sjfb 		}
107548dc77e6Sxsa 		if ((bflag == 1)|| (wflag == 1) || (iflag == 1)) {
107608f90673Sjfb 			for (;;) {
107708f90673Sjfb 				c = getc(f1);
107808f90673Sjfb 				d = getc(f2);
107908f90673Sjfb 				/*
108008f90673Sjfb 				 * GNU diff ignores a missing newline
108108f90673Sjfb 				 * in one file if bflag || wflag.
108208f90673Sjfb 				 */
108348dc77e6Sxsa 				if (((bflag == 1) || (wflag == 1)) &&
108408f90673Sjfb 				    ((c == EOF && d == '\n') ||
108508f90673Sjfb 				    (c == '\n' && d == EOF))) {
108608f90673Sjfb 					break;
108708f90673Sjfb 				}
108808f90673Sjfb 				ctold++;
108908f90673Sjfb 				ctnew++;
109048dc77e6Sxsa 				if ((bflag == 1) && isspace(c) && isspace(d)) {
109108f90673Sjfb 					do {
109208f90673Sjfb 						if (c == '\n')
109308f90673Sjfb 							break;
109408f90673Sjfb 						ctold++;
109508f90673Sjfb 					} while (isspace(c = getc(f1)));
109608f90673Sjfb 					do {
109708f90673Sjfb 						if (d == '\n')
109808f90673Sjfb 							break;
109908f90673Sjfb 						ctnew++;
110008f90673Sjfb 					} while (isspace(d = getc(f2)));
110148dc77e6Sxsa 				} else if (wflag == 1) {
110208f90673Sjfb 					while (isspace(c) && c != '\n') {
110308f90673Sjfb 						c = getc(f1);
110408f90673Sjfb 						ctold++;
110508f90673Sjfb 					}
110608f90673Sjfb 					while (isspace(d) && d != '\n') {
110708f90673Sjfb 						d = getc(f2);
110808f90673Sjfb 						ctnew++;
110908f90673Sjfb 					}
111008f90673Sjfb 				}
111108f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
111208f90673Sjfb 					jackpot++;
111308f90673Sjfb 					J[i] = 0;
111437af5b8bSxsa 					if ((c != '\n') && (c != EOF))
111508f90673Sjfb 						ctold += skipline(f1);
111637af5b8bSxsa 					if ((d != '\n') && (c != EOF))
111708f90673Sjfb 						ctnew += skipline(f2);
111808f90673Sjfb 					break;
111908f90673Sjfb 				}
112037af5b8bSxsa 				if ((c == '\n') || (c == EOF))
112108f90673Sjfb 					break;
112208f90673Sjfb 			}
112308f90673Sjfb 		} else {
112408f90673Sjfb 			for (;;) {
112508f90673Sjfb 				ctold++;
112608f90673Sjfb 				ctnew++;
112708f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
112808f90673Sjfb 					/* jackpot++; */
112908f90673Sjfb 					J[i] = 0;
113037af5b8bSxsa 					if ((c != '\n') && (c != EOF))
113108f90673Sjfb 						ctold += skipline(f1);
113237af5b8bSxsa 					if ((d != '\n') && (c != EOF))
113308f90673Sjfb 						ctnew += skipline(f2);
113408f90673Sjfb 					break;
113508f90673Sjfb 				}
113637af5b8bSxsa 				if ((c == '\n') || (c == EOF))
113708f90673Sjfb 					break;
113808f90673Sjfb 			}
113908f90673Sjfb 		}
114008f90673Sjfb 		ixold[i] = ctold;
114108f90673Sjfb 		ixnew[j] = ctnew;
114208f90673Sjfb 		j++;
114308f90673Sjfb 	}
1144e4276007Sjfb 	for (; j <= diff_len[1]; j++)
114508f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
114608f90673Sjfb 	/*
114737af5b8bSxsa 	 * if (jackpot != 0)
11485ac8b1e7Sjoris 	 *	cvs_printf("jackpot\n");
114908f90673Sjfb 	 */
115008f90673Sjfb }
115108f90673Sjfb 
115208f90673Sjfb /* shellsort CACM #201 */
115308f90673Sjfb static void
115408f90673Sjfb sort(struct line *a, int n)
115508f90673Sjfb {
115608f90673Sjfb 	struct line *ai, *aim, w;
115708f90673Sjfb 	int j, m = 0, k;
115808f90673Sjfb 
115908f90673Sjfb 	if (n == 0)
116008f90673Sjfb 		return;
116108f90673Sjfb 	for (j = 1; j <= n; j *= 2)
116208f90673Sjfb 		m = 2 * j - 1;
116308f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
116408f90673Sjfb 		k = n - m;
116508f90673Sjfb 		for (j = 1; j <= k; j++) {
116608f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
116708f90673Sjfb 				aim = &ai[m];
116808f90673Sjfb 				if (aim < ai)
116908f90673Sjfb 					break;	/* wraparound */
117008f90673Sjfb 				if (aim->value > ai[0].value ||
117108f90673Sjfb 				    (aim->value == ai[0].value &&
117208f90673Sjfb 					aim->serial > ai[0].serial))
117308f90673Sjfb 					break;
117408f90673Sjfb 				w.value = ai[0].value;
117508f90673Sjfb 				ai[0].value = aim->value;
117608f90673Sjfb 				aim->value = w.value;
117708f90673Sjfb 				w.serial = ai[0].serial;
117808f90673Sjfb 				ai[0].serial = aim->serial;
117908f90673Sjfb 				aim->serial = w.serial;
118008f90673Sjfb 			}
118108f90673Sjfb 		}
118208f90673Sjfb 	}
118308f90673Sjfb }
118408f90673Sjfb 
118508f90673Sjfb static void
118608f90673Sjfb unsort(struct line *f, int l, int *b)
118708f90673Sjfb {
118808f90673Sjfb 	int *a, i;
118908f90673Sjfb 
1190*0450b43bSjoris 	a = (int *)xmalloc((l + 1) * sizeof(int));
119108f90673Sjfb 	for (i = 1; i <= l; i++)
119208f90673Sjfb 		a[f[i].serial] = f[i].value;
119308f90673Sjfb 	for (i = 1; i <= l; i++)
119408f90673Sjfb 		b[i] = a[i];
1195*0450b43bSjoris 	xfree(a);
119608f90673Sjfb }
119708f90673Sjfb 
119808f90673Sjfb static int
119908f90673Sjfb skipline(FILE *f)
120008f90673Sjfb {
120108f90673Sjfb 	int i, c;
120208f90673Sjfb 
120308f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
120408f90673Sjfb 		continue;
120508f90673Sjfb 	return (i);
120608f90673Sjfb }
120708f90673Sjfb 
120808f90673Sjfb static void
120908f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2)
121008f90673Sjfb {
121108f90673Sjfb 	int m, i0, i1, j0, j1;
121208f90673Sjfb 
121308f90673Sjfb 	rewind(f1);
121408f90673Sjfb 	rewind(f2);
1215e4276007Sjfb 	m = diff_len[0];
121608f90673Sjfb 	J[0] = 0;
1217e4276007Sjfb 	J[m + 1] = diff_len[1] + 1;
121808f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
121908f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
122008f90673Sjfb 			i0++;
122108f90673Sjfb 		j0 = J[i0 - 1] + 1;
122208f90673Sjfb 		i1 = i0 - 1;
122308f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
122408f90673Sjfb 			i1++;
122508f90673Sjfb 		j1 = J[i1 + 1] - 1;
122608f90673Sjfb 		J[i1] = j1;
122708f90673Sjfb 		change(file1, f1, file2, f2, i0, i1, j0, j1);
122808f90673Sjfb 	}
122908f90673Sjfb 	if (m == 0)
1230e4276007Sjfb 		change(file1, f1, file2, f2, 1, 0, 1, diff_len[1]);
1231f9b67873Sniallo 	if (diff_format == D_IFDEF) {
123208f90673Sjfb 		for (;;) {
123308f90673Sjfb #define	c i0
123408f90673Sjfb 			if ((c = getc(f1)) == EOF)
123508f90673Sjfb 				return;
1236f9b67873Sniallo 			diff_output("%c", c);
123708f90673Sjfb 		}
123808f90673Sjfb #undef c
123908f90673Sjfb 	}
124008f90673Sjfb 	if (anychange != 0) {
1241f9b67873Sniallo 		if (diff_format == D_CONTEXT)
124208f90673Sjfb 			dump_context_vec(f1, f2);
1243f9b67873Sniallo 		else if (diff_format == D_UNIFIED)
124408f90673Sjfb 			dump_unified_vec(f1, f2);
124508f90673Sjfb 	}
124608f90673Sjfb }
124708f90673Sjfb 
124808f90673Sjfb static __inline void
124908f90673Sjfb range(int a, int b, char *separator)
125008f90673Sjfb {
1251f9b67873Sniallo 	diff_output("%d", a > b ? b : a);
125208f90673Sjfb 	if (a < b)
1253f9b67873Sniallo 		diff_output("%s%d", separator, b);
125408f90673Sjfb }
125508f90673Sjfb 
125608f90673Sjfb static __inline void
125708f90673Sjfb uni_range(int a, int b)
125808f90673Sjfb {
125908f90673Sjfb 	if (a < b)
1260f9b67873Sniallo 		diff_output("%d,%d", a, b - a + 1);
126108f90673Sjfb 	else if (a == b)
1262f9b67873Sniallo 		diff_output("%d", b);
126308f90673Sjfb 	else
1264f9b67873Sniallo 		diff_output("%d,0", b);
126508f90673Sjfb }
126608f90673Sjfb 
126708f90673Sjfb static char *
12682a0de57dSjfb preadline(int fd, size_t rlen, off_t off)
126908f90673Sjfb {
127008f90673Sjfb 	char *line;
127108f90673Sjfb 	ssize_t nr;
127208f90673Sjfb 
1273*0450b43bSjoris 	line = xmalloc(rlen + 1);
12742a0de57dSjfb 	if ((nr = pread(fd, line, rlen, off)) < 0) {
127508f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
127608f90673Sjfb 		return (NULL);
127708f90673Sjfb 	}
127808f90673Sjfb 	line[nr] = '\0';
127908f90673Sjfb 	return (line);
128008f90673Sjfb }
128108f90673Sjfb 
128208f90673Sjfb static int
128308f90673Sjfb ignoreline(char *line)
128408f90673Sjfb {
128508f90673Sjfb 	int ret;
128608f90673Sjfb 
1287f1901a5aSxsa 	ret = regexec(&ignore_re, line, (size_t)0, NULL, 0);
1288*0450b43bSjoris 	xfree(line);
128908f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
129008f90673Sjfb }
129108f90673Sjfb 
129208f90673Sjfb /*
129308f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
129408f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
129508f90673Sjfb  * are no lines in the from file involved and this means that there were
129608f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
129708f90673Sjfb  * lines missing from the to file.
129808f90673Sjfb  */
129908f90673Sjfb static void
130008f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2,
130108f90673Sjfb 	int a, int b, int c, int d)
130208f90673Sjfb {
130308f90673Sjfb 	static size_t max_context = 64;
130408f90673Sjfb 	int i;
130508f90673Sjfb 
1306f9b67873Sniallo 	if (diff_format != D_IFDEF && a > b && c > d)
130708f90673Sjfb 		return;
130808f90673Sjfb 	if (ignore_pats != NULL) {
130908f90673Sjfb 		char *line;
131008f90673Sjfb 		/*
131108f90673Sjfb 		 * All lines in the change, insert, or delete must
131208f90673Sjfb 		 * match an ignore pattern for the change to be
131308f90673Sjfb 		 * ignored.
131408f90673Sjfb 		 */
131508f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
131608f90673Sjfb 			for (i = a; i <= b; i++) {
131708f90673Sjfb 				line = preadline(fileno(f1),
131808f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
131908f90673Sjfb 				if (!ignoreline(line))
132008f90673Sjfb 					goto proceed;
132108f90673Sjfb 			}
132208f90673Sjfb 		}
132337af5b8bSxsa 		if ((a > b) || (c <= d)) {	/* Changes and inserts. */
132408f90673Sjfb 			for (i = c; i <= d; i++) {
132508f90673Sjfb 				line = preadline(fileno(f2),
132608f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
132708f90673Sjfb 				if (!ignoreline(line))
132808f90673Sjfb 					goto proceed;
132908f90673Sjfb 			}
133008f90673Sjfb 		}
133108f90673Sjfb 		return;
133208f90673Sjfb 	}
133308f90673Sjfb proceed:
1334f9b67873Sniallo 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
133508f90673Sjfb 		/*
133608f90673Sjfb 		 * Allocate change records as needed.
133708f90673Sjfb 		 */
133808f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
133918501190Sniallo 			struct context_vec *tmp;
134008f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
134108f90673Sjfb 			max_context <<= 1;
1342*0450b43bSjoris 			tmp = xrealloc(context_vec_start, max_context *
1343*0450b43bSjoris 			    sizeof(struct context_vec));
134418501190Sniallo 			context_vec_start = tmp;
134508f90673Sjfb 			context_vec_end = context_vec_start + max_context;
134608f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
134708f90673Sjfb 		}
134808f90673Sjfb 		if (anychange == 0) {
134908f90673Sjfb 			/*
135008f90673Sjfb 			 * Print the context/unidiff header first time through.
135108f90673Sjfb 			 */
1352f9b67873Sniallo 			diff_output("%s %s	%s",
1353f9b67873Sniallo 			    diff_format == D_CONTEXT ? "***" : "---", diff_file,
135408f90673Sjfb 			    ctime(&stb1.st_mtime));
1355f9b67873Sniallo 			diff_output("%s %s	%s",
1356f9b67873Sniallo 			    diff_format == D_CONTEXT ? "---" : "+++", diff_file,
135708f90673Sjfb 			    ctime(&stb2.st_mtime));
135808f90673Sjfb 			anychange = 1;
135908f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
136008f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
136108f90673Sjfb 			/*
136208f90673Sjfb 			 * If this change is more than 'context' lines from the
136308f90673Sjfb 			 * previous change, dump the record and reset it.
136408f90673Sjfb 			 */
1365f9b67873Sniallo 			if (diff_format == D_CONTEXT)
136608f90673Sjfb 				dump_context_vec(f1, f2);
136708f90673Sjfb 			else
136808f90673Sjfb 				dump_unified_vec(f1, f2);
136908f90673Sjfb 		}
137008f90673Sjfb 		context_vec_ptr++;
137108f90673Sjfb 		context_vec_ptr->a = a;
137208f90673Sjfb 		context_vec_ptr->b = b;
137308f90673Sjfb 		context_vec_ptr->c = c;
137408f90673Sjfb 		context_vec_ptr->d = d;
137508f90673Sjfb 		return;
137608f90673Sjfb 	}
137708f90673Sjfb 	if (anychange == 0)
137808f90673Sjfb 		anychange = 1;
1379f9b67873Sniallo 	switch (diff_format) {
138008f90673Sjfb 	case D_BRIEF:
138108f90673Sjfb 		return;
138208f90673Sjfb 	case D_NORMAL:
138308f90673Sjfb 		range(a, b, ",");
1384f9b67873Sniallo 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1385f9b67873Sniallo 		if (diff_format == D_NORMAL)
138608f90673Sjfb 			range(c, d, ",");
1387f9b67873Sniallo 		diff_output("\n");
138808f90673Sjfb 		break;
1389394180a4Sjfb 	case D_RCSDIFF:
1390394180a4Sjfb 		if (a > b)
1391f9b67873Sniallo 			diff_output("a%d %d\n", b, d - c + 1);
1392394180a4Sjfb 		else {
1393f9b67873Sniallo 			diff_output("d%d %d\n", a, b - a + 1);
1394394180a4Sjfb 
1395394180a4Sjfb 			if (!(c > d))	/* add changed lines */
1396f9b67873Sniallo 				diff_output("a%d %d\n", b, d - c + 1);
1397394180a4Sjfb 		}
1398394180a4Sjfb 		break;
139908f90673Sjfb 	}
1400f9b67873Sniallo 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
140108f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
1402f9b67873Sniallo 		if (a <= b && c <= d && diff_format == D_NORMAL)
1403206543eeSjoris 			diff_output("---\n");
140408f90673Sjfb 	}
1405f9b67873Sniallo 	i = fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0);
140608f90673Sjfb 	if (inifdef) {
1407f9b67873Sniallo 		diff_output("#endif /* %s */\n", ifdefname);
140808f90673Sjfb 		inifdef = 0;
140908f90673Sjfb 	}
141008f90673Sjfb }
141108f90673Sjfb 
141208f90673Sjfb static int
141308f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
141408f90673Sjfb {
141508f90673Sjfb 	int i, j, c, lastc, col, nc;
141608f90673Sjfb 
141708f90673Sjfb 	/*
141808f90673Sjfb 	 * When doing #ifdef's, copy down to current line
141908f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
142008f90673Sjfb 	 */
1421f9b67873Sniallo 	if (diff_format == D_IFDEF && oldfile) {
142208f90673Sjfb 		long curpos = ftell(lb);
142308f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
142408f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
142508f90673Sjfb 		for (i = 0; i < nc; i++)
1426f9b67873Sniallo 			diff_output("%c", getc(lb));
142708f90673Sjfb 	}
142808f90673Sjfb 	if (a > b)
142908f90673Sjfb 		return (0);
1430f9b67873Sniallo 	if (diff_format == D_IFDEF) {
143108f90673Sjfb 		if (inifdef) {
1432f9b67873Sniallo 			diff_output("#else /* %s%s */\n",
143308f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
143408f90673Sjfb 		} else {
143508f90673Sjfb 			if (oldfile)
1436f9b67873Sniallo 				diff_output("#ifndef %s\n", ifdefname);
143708f90673Sjfb 			else
1438f9b67873Sniallo 				diff_output("#ifdef %s\n", ifdefname);
143908f90673Sjfb 		}
144008f90673Sjfb 		inifdef = 1 + oldfile;
144108f90673Sjfb 	}
144208f90673Sjfb 	for (i = a; i <= b; i++) {
144308f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
144408f90673Sjfb 		nc = f[i] - f[i - 1];
1445f9b67873Sniallo 		if (diff_format != D_IFDEF && ch != '\0') {
1446f9b67873Sniallo 			diff_output("%c", ch);
144748dc77e6Sxsa 			if ((Tflag == 1 ) && (diff_format == D_NORMAL ||
14489c5161e4Sjoris 			    diff_format == D_CONTEXT ||
14499c5161e4Sjoris 			    diff_format == D_UNIFIED))
1450f9b67873Sniallo 				diff_output("\t");
1451f9b67873Sniallo 			else if (diff_format != D_UNIFIED)
1452f9b67873Sniallo 				diff_output(" ");
145308f90673Sjfb 		}
145408f90673Sjfb 		col = 0;
145508f90673Sjfb 		for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
145608f90673Sjfb 			if ((c = getc(lb)) == EOF) {
1457f9b67873Sniallo 				if (diff_format == D_RCSDIFF)
1458394180a4Sjfb 					warnx("No newline at end of file");
1459394180a4Sjfb 				else
14609c5161e4Sjoris 					diff_output("\n\\ No newline at end of "
14619c5161e4Sjoris 					    "file");
146208f90673Sjfb 				return (0);
146308f90673Sjfb 			}
146448dc77e6Sxsa 			if ((c == '\t') && (tflag == 1)) {
146508f90673Sjfb 				do {
1466f9b67873Sniallo 					diff_output(" ");
146708f90673Sjfb 				} while (++col & 7);
146808f90673Sjfb 			} else {
1469f9b67873Sniallo 				diff_output("%c", c);
147008f90673Sjfb 				col++;
147108f90673Sjfb 			}
147208f90673Sjfb 		}
147308f90673Sjfb 	}
147408f90673Sjfb 	return (0);
147508f90673Sjfb }
147608f90673Sjfb 
147708f90673Sjfb /*
147808f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
147908f90673Sjfb  */
148008f90673Sjfb static int
148108f90673Sjfb readhash(FILE *f)
148208f90673Sjfb {
148308f90673Sjfb 	int i, t, space;
148408f90673Sjfb 	int sum;
148508f90673Sjfb 
148608f90673Sjfb 	sum = 1;
148708f90673Sjfb 	space = 0;
148848dc77e6Sxsa 	if ((bflag != 1) && (wflag != 1)) {
148948dc77e6Sxsa 		if (iflag == 1)
149008f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
149108f90673Sjfb 				if (t == EOF) {
149208f90673Sjfb 					if (i == 0)
149308f90673Sjfb 						return (0);
149408f90673Sjfb 					break;
149508f90673Sjfb 				}
149608f90673Sjfb 				sum = sum * 127 + chrtran[t];
149708f90673Sjfb 			}
149808f90673Sjfb 		else
149908f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
150008f90673Sjfb 				if (t == EOF) {
150108f90673Sjfb 					if (i == 0)
150208f90673Sjfb 						return (0);
150308f90673Sjfb 					break;
150408f90673Sjfb 				}
150508f90673Sjfb 				sum = sum * 127 + t;
150608f90673Sjfb 			}
150708f90673Sjfb 	} else {
150808f90673Sjfb 		for (i = 0;;) {
150908f90673Sjfb 			switch (t = getc(f)) {
151008f90673Sjfb 			case '\t':
151108f90673Sjfb 			case ' ':
151208f90673Sjfb 				space++;
151308f90673Sjfb 				continue;
151408f90673Sjfb 			default:
151548dc77e6Sxsa 				if ((space != 0) && (wflag != 1)) {
151608f90673Sjfb 					i++;
151708f90673Sjfb 					space = 0;
151808f90673Sjfb 				}
151908f90673Sjfb 				sum = sum * 127 + chrtran[t];
152008f90673Sjfb 				i++;
152108f90673Sjfb 				continue;
152208f90673Sjfb 			case EOF:
152308f90673Sjfb 				if (i == 0)
152408f90673Sjfb 					return (0);
152508f90673Sjfb 				/* FALLTHROUGH */
152608f90673Sjfb 			case '\n':
152708f90673Sjfb 				break;
152808f90673Sjfb 			}
152908f90673Sjfb 			break;
153008f90673Sjfb 		}
153108f90673Sjfb 	}
153208f90673Sjfb 	/*
153308f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
153408f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
153508f90673Sjfb 	 */
153608f90673Sjfb 	return (sum == 0 ? 1 : sum);
153708f90673Sjfb }
153808f90673Sjfb 
153908f90673Sjfb static int
154008f90673Sjfb asciifile(FILE *f)
154108f90673Sjfb {
154208f90673Sjfb 	char buf[BUFSIZ];
154308f90673Sjfb 	int i, cnt;
154408f90673Sjfb 
154548dc77e6Sxsa 	if ((aflag == 1) || (f == NULL))
154608f90673Sjfb 		return (1);
154708f90673Sjfb 
154808f90673Sjfb 	rewind(f);
1549f1901a5aSxsa 	cnt = fread(buf, (size_t)1, sizeof(buf), f);
155008f90673Sjfb 	for (i = 0; i < cnt; i++)
155108f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
155208f90673Sjfb 			return (0);
155308f90673Sjfb 	return (1);
155408f90673Sjfb }
155508f90673Sjfb 
15565e78344dSjfb static char*
15575e78344dSjfb match_function(const long *f, int pos, FILE *fp)
15585e78344dSjfb {
15595e78344dSjfb 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
15605e78344dSjfb 	size_t nc;
15615e78344dSjfb 	int last = lastline;
15625e78344dSjfb 	char *p;
15635e78344dSjfb 
15645e78344dSjfb 	lastline = pos;
15655e78344dSjfb 	while (pos > last) {
15665e78344dSjfb 		fseek(fp, f[pos - 1], SEEK_SET);
15675e78344dSjfb 		nc = f[pos] - f[pos - 1];
15685e78344dSjfb 		if (nc >= sizeof(buf))
15695e78344dSjfb 			nc = sizeof(buf) - 1;
1570f1901a5aSxsa 		nc = fread(buf, (size_t)1, nc, fp);
15715e78344dSjfb 		if (nc > 0) {
15725e78344dSjfb 			buf[nc] = '\0';
1573634926d6Sniallo 			p = strchr((const char *)buf, '\n');
15745e78344dSjfb 			if (p != NULL)
15755e78344dSjfb 				*p = '\0';
15765e78344dSjfb 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
15774c06e5f6Sreyk 				strlcpy(lastbuf, (const char *)buf,
15784c06e5f6Sreyk 				    sizeof lastbuf);
15795e78344dSjfb 				lastmatchline = pos;
15805e78344dSjfb 				return lastbuf;
15815e78344dSjfb 			}
15825e78344dSjfb 		}
15835e78344dSjfb 		pos--;
15845e78344dSjfb 	}
15855e78344dSjfb 	return (lastmatchline > 0) ? lastbuf : NULL;
15865e78344dSjfb }
15875e78344dSjfb 
158808f90673Sjfb 
158908f90673Sjfb /* dump accumulated "context" diff changes */
159008f90673Sjfb static void
159108f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
159208f90673Sjfb {
159308f90673Sjfb 	struct context_vec *cvp = context_vec_start;
159408f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
159508f90673Sjfb 	int a, b, c, d;
15965e78344dSjfb 	char ch, *f;
159708f90673Sjfb 
159808f90673Sjfb 	if (context_vec_start > context_vec_ptr)
159908f90673Sjfb 		return;
160008f90673Sjfb 
160108f90673Sjfb 	b = d = 0;		/* gcc */
1602dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1603e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1604dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1605e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
160608f90673Sjfb 
1607f9b67873Sniallo 	diff_output("***************");
160848dc77e6Sxsa 	if (pflag == 1) {
16095e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
16105e78344dSjfb 		if (f != NULL) {
1611f9b67873Sniallo 			diff_output(" ");
1612f9b67873Sniallo 			diff_output("%s", f);
16135e78344dSjfb 		}
16145e78344dSjfb 	}
1615f9b67873Sniallo 	diff_output("\n*** ");
161608f90673Sjfb 	range(lowa, upb, ",");
1617f9b67873Sniallo 	diff_output(" ****\n");
161808f90673Sjfb 
161908f90673Sjfb 	/*
162008f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
162108f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
162208f90673Sjfb 	 * the "old" lines as context in the "new" list).
162308f90673Sjfb 	 */
162408f90673Sjfb 	do_output = 0;
162508f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
162608f90673Sjfb 		if (cvp->a <= cvp->b) {
162708f90673Sjfb 			cvp = context_vec_start;
162808f90673Sjfb 			do_output++;
162908f90673Sjfb 			break;
163008f90673Sjfb 		}
163137af5b8bSxsa 	if (do_output != 0) {
163208f90673Sjfb 		while (cvp <= context_vec_ptr) {
163308f90673Sjfb 			a = cvp->a;
163408f90673Sjfb 			b = cvp->b;
163508f90673Sjfb 			c = cvp->c;
163608f90673Sjfb 			d = cvp->d;
163708f90673Sjfb 
163808f90673Sjfb 			if (a <= b && c <= d)
163908f90673Sjfb 				ch = 'c';
164008f90673Sjfb 			else
164108f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
164208f90673Sjfb 
164308f90673Sjfb 			if (ch == 'a')
164408f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
164508f90673Sjfb 			else {
164608f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
164708f90673Sjfb 				fetch(ixold, a, b, f1,
164808f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
164908f90673Sjfb 			}
165008f90673Sjfb 			lowa = b + 1;
165108f90673Sjfb 			cvp++;
165208f90673Sjfb 		}
165308f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
165408f90673Sjfb 	}
165508f90673Sjfb 	/* output changes to the "new" file */
1656f9b67873Sniallo 	diff_output("--- ");
165708f90673Sjfb 	range(lowc, upd, ",");
1658f9b67873Sniallo 	diff_output(" ----\n");
165908f90673Sjfb 
166008f90673Sjfb 	do_output = 0;
166108f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
166208f90673Sjfb 		if (cvp->c <= cvp->d) {
166308f90673Sjfb 			cvp = context_vec_start;
166408f90673Sjfb 			do_output++;
166508f90673Sjfb 			break;
166608f90673Sjfb 		}
166737af5b8bSxsa 	if (do_output != 0) {
166808f90673Sjfb 		while (cvp <= context_vec_ptr) {
166908f90673Sjfb 			a = cvp->a;
167008f90673Sjfb 			b = cvp->b;
167108f90673Sjfb 			c = cvp->c;
167208f90673Sjfb 			d = cvp->d;
167308f90673Sjfb 
167408f90673Sjfb 			if (a <= b && c <= d)
167508f90673Sjfb 				ch = 'c';
167608f90673Sjfb 			else
167708f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
167808f90673Sjfb 
167908f90673Sjfb 			if (ch == 'd')
168008f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
168108f90673Sjfb 			else {
168208f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
168308f90673Sjfb 				fetch(ixnew, c, d, f2,
168408f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
168508f90673Sjfb 			}
168608f90673Sjfb 			lowc = d + 1;
168708f90673Sjfb 			cvp++;
168808f90673Sjfb 		}
168908f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
169008f90673Sjfb 	}
169108f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
169208f90673Sjfb }
169308f90673Sjfb 
169408f90673Sjfb /* dump accumulated "unified" diff changes */
169508f90673Sjfb static void
169608f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
169708f90673Sjfb {
169808f90673Sjfb 	struct context_vec *cvp = context_vec_start;
169908f90673Sjfb 	int lowa, upb, lowc, upd;
170008f90673Sjfb 	int a, b, c, d;
17015e78344dSjfb 	char ch, *f;
170208f90673Sjfb 
170308f90673Sjfb 	if (context_vec_start > context_vec_ptr)
170408f90673Sjfb 		return;
170508f90673Sjfb 
170608f90673Sjfb 	b = d = 0;		/* gcc */
1707dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1708e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1709dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1710e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
171108f90673Sjfb 
1712f9b67873Sniallo 	diff_output("@@ -");
171308f90673Sjfb 	uni_range(lowa, upb);
1714f9b67873Sniallo 	diff_output(" +");
171508f90673Sjfb 	uni_range(lowc, upd);
1716f9b67873Sniallo 	diff_output(" @@");
171748dc77e6Sxsa 	if (pflag == 1) {
17185e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
17195e78344dSjfb 		if (f != NULL) {
1720f9b67873Sniallo 			diff_output(" ");
1721f9b67873Sniallo 			diff_output("%s", f);
17225e78344dSjfb 		}
17235e78344dSjfb 	}
1724f9b67873Sniallo 	diff_output("\n");
172508f90673Sjfb 
172608f90673Sjfb 	/*
172708f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
172808f90673Sjfb 	 * are printed together.
172908f90673Sjfb 	 */
173008f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
173108f90673Sjfb 		a = cvp->a;
173208f90673Sjfb 		b = cvp->b;
173308f90673Sjfb 		c = cvp->c;
173408f90673Sjfb 		d = cvp->d;
173508f90673Sjfb 
173608f90673Sjfb 		/*
173708f90673Sjfb 		 * c: both new and old changes
173808f90673Sjfb 		 * d: only changes in the old file
173908f90673Sjfb 		 * a: only changes in the new file
174008f90673Sjfb 		 */
174108f90673Sjfb 		if (a <= b && c <= d)
174208f90673Sjfb 			ch = 'c';
174308f90673Sjfb 		else
174408f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
174508f90673Sjfb 
174608f90673Sjfb 		switch (ch) {
174708f90673Sjfb 		case 'c':
174808f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
174908f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
175008f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
175108f90673Sjfb 			break;
175208f90673Sjfb 		case 'd':
175308f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
175408f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
175508f90673Sjfb 			break;
175608f90673Sjfb 		case 'a':
175708f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
175808f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
175908f90673Sjfb 			break;
176008f90673Sjfb 		}
176108f90673Sjfb 		lowa = b + 1;
176208f90673Sjfb 		lowc = d + 1;
176308f90673Sjfb 	}
176408f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
176508f90673Sjfb 
176608f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
176708f90673Sjfb }
1768f9b67873Sniallo 
176901af718aSjoris void
1770f9b67873Sniallo diff_output(const char *fmt, ...)
1771f9b67873Sniallo {
1772f9b67873Sniallo 	va_list vap;
1773f9b67873Sniallo 	char *str;
1774f9b67873Sniallo 
1775f9b67873Sniallo 	va_start(vap, fmt);
1776f9b67873Sniallo 	vasprintf(&str, fmt, vap);
1777f9b67873Sniallo 	if (diffbuf != NULL)
1778f9b67873Sniallo 		cvs_buf_append(diffbuf, str, strlen(str));
1779f9b67873Sniallo 	else
1780f9b67873Sniallo 		cvs_printf("%s", str);
1781*0450b43bSjoris 	xfree(str);
1782f9b67873Sniallo 	va_end(vap);
1783f9b67873Sniallo }
1784