xref: /openbsd-src/usr.bin/cvs/diff.c (revision 37af5b8bad567ca9db836c7b2c34dc604fac7ed2)
1*37af5b8bSxsa /*	$OpenBSD: diff.c,v 1.65 2005/11/18 10:30:34 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 
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 static char diffargs[128];
219af5bb824Sniallo #endif
220af5bb824Sniallo static int aflag, bflag, dflag, iflag, pflag, tflag, Tflag, wflag;
221ece76a70Sjoris static int context;
222f9b67873Sniallo int diff_format = D_NORMAL;
2231ff4dac1Sjoris char *diff_file = NULL;
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 
36016cfc147Sjoris 	dap = (struct diff_arg *)malloc(sizeof(*dap));
36116cfc147Sjoris 	if (dap == NULL)
36231274bbfSjoris 		return (CVS_EX_DATA);
36316cfc147Sjoris 	dap->date1 = dap->date2 = dap->rev1 = dap->rev2 = NULL;
364dc6a6879Sjfb 	strlcpy(diffargs, argv[0], sizeof(diffargs));
365dc6a6879Sjfb 
366e4276007Sjfb 	while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) {
36708f90673Sjfb 		switch (ch) {
36808f90673Sjfb 		case 'c':
369f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
370f9b67873Sniallo 			diff_format = D_CONTEXT;
37108f90673Sjfb 			break;
37208f90673Sjfb 		case 'D':
37316cfc147Sjoris 			if (dap->date1 == NULL && dap->rev1 == NULL) {
37416cfc147Sjoris 				dap->date1 = optarg;
37516cfc147Sjoris 			} else if (dap->date2 == NULL && dap->rev2 == NULL) {
37616cfc147Sjoris 				dap->date2 = optarg;
37716cfc147Sjoris 			} else {
37808f90673Sjfb 				cvs_log(LP_ERR,
37908f90673Sjfb 				    "no more than two revisions/dates can "
38008f90673Sjfb 				    "be specified");
38108f90673Sjfb 			}
38208f90673Sjfb 			break;
38308f90673Sjfb 		case 'l':
384f5638424Sjfb 			strlcat(diffargs, " -l", sizeof(diffargs));
38508f90673Sjfb 			recurse = 0;
386e4276007Sjfb 			cvs_cmd_diff.file_flags &= ~CF_RECURSE;
38708f90673Sjfb 			break;
38808f90673Sjfb 		case 'i':
389f5638424Sjfb 			strlcat(diffargs, " -i", sizeof(diffargs));
39008f90673Sjfb 			iflag = 1;
39108f90673Sjfb 			break;
392c710bc5aSjfb 		case 'N':
393c710bc5aSjfb 			strlcat(diffargs, " -N", sizeof(diffargs));
394c710bc5aSjfb 			Nflag = 1;
395c710bc5aSjfb 			break;
396394180a4Sjfb 		case 'n':
397394180a4Sjfb 			strlcat(diffargs, " -n", sizeof(diffargs));
398f9b67873Sniallo 			diff_format = D_RCSDIFF;
399394180a4Sjfb 			break;
4005e78344dSjfb 		case 'p':
4015e78344dSjfb 			strlcat(diffargs, " -p", sizeof(diffargs));
4025e78344dSjfb 			pflag = 1;
4035e78344dSjfb 			break;
40408f90673Sjfb 		case 'r':
40516cfc147Sjoris 			if ((dap->rev1 == NULL) && (dap->date1 == NULL)) {
40616cfc147Sjoris 				dap->rev1 = optarg;
40716cfc147Sjoris 			} else if ((dap->rev2 == NULL) &&
40816cfc147Sjoris 			    (dap->date2 == NULL)) {
40916cfc147Sjoris 				dap->rev2 = optarg;
41016cfc147Sjoris 			} else {
41108f90673Sjfb 				cvs_log(LP_ERR,
41208f90673Sjfb 				    "no more than two revisions/dates can "
41308f90673Sjfb 				    "be specified");
41431274bbfSjoris 				return (CVS_EX_USAGE);
41508f90673Sjfb 			}
41608f90673Sjfb 			break;
417f203c484Sjoris 		case 'R':
418e4276007Sjfb 			cvs_cmd_diff.file_flags |= CF_RECURSE;
419f203c484Sjoris 			break;
42008f90673Sjfb 		case 'u':
421f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
422f9b67873Sniallo 			diff_format = D_UNIFIED;
42308f90673Sjfb 			break;
42408f90673Sjfb 		default:
42531274bbfSjoris 			return (CVS_EX_USAGE);
42608f90673Sjfb 		}
42708f90673Sjfb 	}
42808f90673Sjfb 
42916cfc147Sjoris 	*arg = optind;
430dc6a6879Sjfb 	return (0);
431dc6a6879Sjfb }
432dc6a6879Sjfb 
43316cfc147Sjoris int
43416cfc147Sjoris cvs_diff_cleanup(void)
43516cfc147Sjoris {
436e4276007Sjfb 	if (dap != NULL) {
43716cfc147Sjoris 		free(dap);
438e4276007Sjfb 		dap = NULL;
439e4276007Sjfb 	}
44016cfc147Sjoris 	return (0);
44116cfc147Sjoris }
442dc6a6879Sjfb 
443dc6a6879Sjfb /*
444e4276007Sjfb  * cvs_diff_pre_exec()
445dc6a6879Sjfb  *
446dc6a6879Sjfb  */
447dc6a6879Sjfb int
448e4276007Sjfb cvs_diff_pre_exec(struct cvsroot *root)
449dc6a6879Sjfb {
450e4276007Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
45108f90673Sjfb 		/* send the flags */
45248dc77e6Sxsa 		if ((Nflag == 1) && (cvs_sendarg(root, "-N", 0) < 0))
45331274bbfSjoris 			return (CVS_EX_PROTO);
45448dc77e6Sxsa 		if ((pflag ==1) && (cvs_sendarg(root, "-p", 0) < 0))
45531274bbfSjoris 			return (CVS_EX_PROTO);
4565e78344dSjfb 
457f9b67873Sniallo 		if (diff_format == D_CONTEXT) {
458610801e8Sxsa 			if (cvs_sendarg(root, "-c", 0) < 0)
459610801e8Sxsa 				return (CVS_EX_PROTO);
460f9b67873Sniallo 		} else if (diff_format == D_UNIFIED) {
461610801e8Sxsa 			if (cvs_sendarg(root, "-u", 0) < 0)
462610801e8Sxsa 				return (CVS_EX_PROTO);
463610801e8Sxsa 		}
46408f90673Sjfb 
465dc6a6879Sjfb 		if (dap->rev1 != NULL) {
466610801e8Sxsa 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
467610801e8Sxsa 			    (cvs_sendarg(root, dap->rev1, 0) < 0))
468610801e8Sxsa 				return (CVS_EX_PROTO);
4693917c9bfSderaadt 		} else if (dap->date1 != NULL) {
470610801e8Sxsa 			if ((cvs_sendarg(root, "-D", 0) < 0) ||
471610801e8Sxsa 			    (cvs_sendarg(root, dap->date1, 0) < 0))
472610801e8Sxsa 				return (CVS_EX_PROTO);
47308f90673Sjfb 		}
474dc6a6879Sjfb 		if (dap->rev2 != NULL) {
475610801e8Sxsa 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
476610801e8Sxsa 			    (cvs_sendarg(root, dap->rev2, 0) < 0))
477610801e8Sxsa 				return (CVS_EX_PROTO);
4783917c9bfSderaadt 		} else if (dap->date2 != NULL) {
479610801e8Sxsa 			if ((cvs_sendarg(root, "-D", 0) < 0) ||
480610801e8Sxsa 			    (cvs_sendarg(root, dap->date2, 0) < 0))
481610801e8Sxsa 				return  (CVS_EX_PROTO);
48208f90673Sjfb 		}
483e4276007Sjfb 	}
48408f90673Sjfb 
48508f90673Sjfb 	return (0);
48608f90673Sjfb }
48708f90673Sjfb 
48808f90673Sjfb 
48908f90673Sjfb /*
49008f90673Sjfb  * cvs_diff_file()
49108f90673Sjfb  *
49208f90673Sjfb  * Diff a single file.
49308f90673Sjfb  */
494e4276007Sjfb static int
495e4276007Sjfb cvs_diff_remote(struct cvs_file *cfp, void *arg)
49608f90673Sjfb {
497e4276007Sjfb 	char *dir, *repo;
498e4276007Sjfb 	char fpath[MAXPATHLEN], dfpath[MAXPATHLEN];
499dc6a6879Sjfb 	struct cvsroot *root;
500dc6a6879Sjfb 
501dc6a6879Sjfb 	if (cfp->cf_type == DT_DIR) {
502895e6cf6Sjfb 		if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
503bb029937Sjfb 			root = cfp->cf_parent->cf_root;
504b904ba2eSjoris 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
5053917c9bfSderaadt 		} else {
506bb029937Sjfb 			root = cfp->cf_root;
50716cfc147Sjoris #if 0
508dc6a6879Sjfb 			if ((cfp->cf_parent == NULL) ||
509bb029937Sjfb 			    (root != cfp->cf_parent->cf_root)) {
510dc6a6879Sjfb 				cvs_connect(root);
511e4276007Sjfb 				cvs_diff_pre_exec(root);
512dc6a6879Sjfb 			}
51316cfc147Sjoris #endif
514dc6a6879Sjfb 
515dc6a6879Sjfb 			cvs_senddir(root, cfp);
516895e6cf6Sjfb 		}
517895e6cf6Sjfb 
518dc6a6879Sjfb 		return (0);
519dc6a6879Sjfb 	}
52008f90673Sjfb 
5212d5b8b1dSjfb 	if (cfp->cf_cvstat == CVS_FST_LOST) {
522b904ba2eSjoris 		cvs_log(LP_WARN, "cannot find file %s", cfp->cf_name);
5232d5b8b1dSjfb 		return (0);
5242d5b8b1dSjfb 	}
5252d5b8b1dSjfb 
526c710bc5aSjfb 	diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath));
527895e6cf6Sjfb 
528dc6a6879Sjfb 	if (cfp->cf_parent != NULL) {
529c710bc5aSjfb 		dir = cvs_file_getpath(cfp->cf_parent, dfpath, sizeof(dfpath));
530bb029937Sjfb 		root = cfp->cf_parent->cf_root;
531bb029937Sjfb 		repo = cfp->cf_parent->cf_repo;
5323917c9bfSderaadt 	} else {
533dc6a6879Sjfb 		dir = ".";
534895e6cf6Sjfb 		root = NULL;
535dc6a6879Sjfb 		repo = NULL;
53608f90673Sjfb 	}
53708f90673Sjfb 
538dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
539e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
540dc6a6879Sjfb 		return (0);
54108f90673Sjfb 	}
54208f90673Sjfb 
543e4276007Sjfb 	if (cvs_sendentry(root, cfp) < 0)
54431274bbfSjoris 		return (CVS_EX_PROTO);
54508f90673Sjfb 
546dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UPTODATE) {
547e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name);
54808f90673Sjfb 		return (0);
54908f90673Sjfb 	}
55008f90673Sjfb 
55108f90673Sjfb 	/* at this point, the file is modified */
552e4276007Sjfb 	if ((cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name) < 0) ||
553e4276007Sjfb 	    (cvs_sendfile(root, diff_file) < 0))
554e4276007Sjfb 		return (CVS_EX_PROTO);
555e4276007Sjfb 
556e4276007Sjfb 	return (0);
557e4276007Sjfb }
558e4276007Sjfb 
559e4276007Sjfb static int
560e4276007Sjfb cvs_diff_local(CVSFILE *cf, void *arg)
561e4276007Sjfb {
562e4276007Sjfb 	int len;
563e4276007Sjfb 	char *repo, buf[64];
564e4276007Sjfb 	char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
565e4276007Sjfb 	char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN];
566e4276007Sjfb 	BUF *b1, *b2;
567e4276007Sjfb 	RCSNUM *r1, *r2;
568e4276007Sjfb 	RCSFILE *rf;
569e4276007Sjfb 	struct cvsroot *root;
570e4276007Sjfb 
571e4276007Sjfb 	rf = NULL;
572e4276007Sjfb 	root = CVS_DIR_ROOT(cf);
573e4276007Sjfb 	repo = CVS_DIR_REPO(cf);
574e4276007Sjfb 	diff_file = cvs_file_getpath(cf, fpath, sizeof(fpath));
575e4276007Sjfb 
576e4276007Sjfb 	if (cf->cf_type == DT_DIR) {
577b87c59bdSxsa 		if (verbosity > 1)
578246675edSxsa 			cvs_log(LP_NOTICE, "Diffing %s", fpath);
579e4276007Sjfb 		return (0);
580e4276007Sjfb 	}
581e4276007Sjfb 
582e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_LOST) {
583e4276007Sjfb 		cvs_log(LP_WARN, "cannot find file %s", cf->cf_name);
584e4276007Sjfb 		return (0);
585e4276007Sjfb 	}
586e4276007Sjfb 
587e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_UNKNOWN) {
588e4276007Sjfb 		cvs_log(LP_WARN, "I know nothing about %s", diff_file);
589e4276007Sjfb 		return (0);
5905c0cd766Sniallo 	} else if (cf->cf_cvstat == CVS_FST_UPTODATE)
591e4276007Sjfb 		return (0);
592e4276007Sjfb 
593e4276007Sjfb 	/* at this point, the file is modified */
594e4276007Sjfb 	len = snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
595dc6a6879Sjfb 	    root->cr_dir, repo, diff_file, RCS_FILE_EXT);
596e4276007Sjfb 	if (len == -1 || len >= (int)sizeof(rcspath)) {
59727b85f85Sxsa 		errno = ENAMETOOLONG;
59827b85f85Sxsa 		cvs_log(LP_ERRNO, "%s", rcspath);
59901b3d77aSjoris 		return (CVS_EX_DATA);
60027b85f85Sxsa 	}
60108f90673Sjfb 
6021b6534b8Sjfb 	rf = rcs_open(rcspath, RCS_READ);
603dc6a6879Sjfb 	if (rf == NULL) {
60431274bbfSjoris 		return (CVS_EX_DATA);
605dc6a6879Sjfb 	}
60608f90673Sjfb 
607dc6a6879Sjfb 	cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file,
60808f90673Sjfb 	    RCS_DIFF_DIV, rcspath);
60908f90673Sjfb 
610dc6a6879Sjfb 	if (dap->rev1 == NULL)
611e4276007Sjfb 		r1 = cf->cf_lrev;
61208f90673Sjfb 	else {
61325b74b48Sjfb 		if ((r1 = rcsnum_parse(dap->rev1)) == NULL) {
61464672f74Sjoris 			rcs_close(rf);
61531274bbfSjoris 			return (CVS_EX_DATA);
6167f535ec4Sjfb 		}
61708f90673Sjfb 	}
61808f90673Sjfb 
619dc6a6879Sjfb 	cvs_printf("retrieving revision %s\n",
62008f90673Sjfb 	    rcsnum_tostr(r1, buf, sizeof(buf)));
62108f90673Sjfb 	b1 = rcs_getrev(rf, r1);
62208f90673Sjfb 
62395ae1173Sjoris 	if (b1 == NULL) {
624289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
62595ae1173Sjoris 		    rcsnum_tostr(r1, buf, sizeof(buf)));
62695ae1173Sjoris 		if (r1 != cf->cf_lrev)
62795ae1173Sjoris 			rcsnum_free(r1);
62864672f74Sjoris 		rcs_close(rf);
62995ae1173Sjoris 		return (CVS_EX_DATA);
63095ae1173Sjoris 	}
63195ae1173Sjoris 
632e4276007Sjfb 	if (r1 != cf->cf_lrev)
6337f535ec4Sjfb 		rcsnum_free(r1);
6347f535ec4Sjfb 
635dc6a6879Sjfb 	if (dap->rev2 != NULL) {
636dc6a6879Sjfb 		cvs_printf("retrieving revision %s\n", dap->rev2);
63725b74b48Sjfb 		if ((r2 = rcsnum_parse(dap->rev2)) == NULL) {
63864672f74Sjoris 			rcs_close(rf);
63931274bbfSjoris 			return (CVS_EX_DATA);
6407f535ec4Sjfb 		}
64108f90673Sjfb 		b2 = rcs_getrev(rf, r2);
6427f535ec4Sjfb 		rcsnum_free(r2);
6433917c9bfSderaadt 	} else {
644dc6a6879Sjfb 		b2 = cvs_buf_load(diff_file, BUF_AUTOEXT);
64508f90673Sjfb 	}
64608f90673Sjfb 
647dc6a6879Sjfb 	rcs_close(rf);
648dc6a6879Sjfb 
64995ae1173Sjoris 	if (b2 == NULL) {
650289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
65195ae1173Sjoris 		    dap->rev2);
65295ae1173Sjoris 		cvs_buf_free(b1);
65395ae1173Sjoris 		return (CVS_EX_DATA);
65495ae1173Sjoris 	}
65595ae1173Sjoris 
6565ac8b1e7Sjoris 	cvs_printf("%s", diffargs);
6575ac8b1e7Sjoris 	cvs_printf(" -r%s", buf);
658dc6a6879Sjfb 	if (dap->rev2 != NULL)
6595ac8b1e7Sjoris 		cvs_printf(" -r%s", dap->rev2);
6605ac8b1e7Sjoris 	cvs_printf(" %s\n", diff_file);
661946f6157Sdjm 	strlcpy(path_tmp1, "/tmp/diff1.XXXXXXXXXX", sizeof(path_tmp1));
6627f535ec4Sjfb 	if (cvs_buf_write_stmp(b1, path_tmp1, 0600) == -1) {
6637f535ec4Sjfb 		cvs_buf_free(b1);
6647f535ec4Sjfb 		cvs_buf_free(b2);
66531274bbfSjoris 		return (CVS_EX_DATA);
6667f535ec4Sjfb 	}
6677f535ec4Sjfb 	cvs_buf_free(b1);
6687f535ec4Sjfb 
66969853e40Sjfb 	strlcpy(path_tmp2, "/tmp/diff2.XXXXXXXXXX", sizeof(path_tmp2));
670946f6157Sdjm 	if (cvs_buf_write_stmp(b2, path_tmp2, 0600) == -1) {
6717f535ec4Sjfb 		cvs_buf_free(b2);
672946f6157Sdjm 		(void)unlink(path_tmp1);
67331274bbfSjoris 		return (CVS_EX_DATA);
674946f6157Sdjm 	}
6757f535ec4Sjfb 	cvs_buf_free(b2);
6767f535ec4Sjfb 
677f9b67873Sniallo 	cvs_diffreg(path_tmp1, path_tmp2, NULL);
678946f6157Sdjm 	(void)unlink(path_tmp1);
679946f6157Sdjm 	(void)unlink(path_tmp2);
68008f90673Sjfb 
68108f90673Sjfb 	return (0);
68208f90673Sjfb }
683af5bb824Sniallo #endif
68408f90673Sjfb 
68508f90673Sjfb 
68608f90673Sjfb int
687f9b67873Sniallo cvs_diffreg(const char *file1, const char *file2, BUF *out)
68808f90673Sjfb {
68908f90673Sjfb 	FILE *f1, *f2;
69008f90673Sjfb 	int i, rval;
6917f535ec4Sjfb 	void *tmp;
69208f90673Sjfb 
69308f90673Sjfb 	f1 = f2 = NULL;
69408f90673Sjfb 	rval = D_SAME;
69508f90673Sjfb 	anychange = 0;
69608f90673Sjfb 	lastline = 0;
69708f90673Sjfb 	lastmatchline = 0;
69808f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
69908f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
700f9b67873Sniallo 	if (out != NULL)
701f9b67873Sniallo 		diffbuf = out;
70208f90673Sjfb 
70308f90673Sjfb 	f1 = fopen(file1, "r");
70408f90673Sjfb 	if (f1 == NULL) {
70508f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
70608f90673Sjfb 		goto closem;
70708f90673Sjfb 	}
70808f90673Sjfb 
70908f90673Sjfb 	f2 = fopen(file2, "r");
71008f90673Sjfb 	if (f2 == NULL) {
71108f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
71208f90673Sjfb 		goto closem;
71308f90673Sjfb 	}
71408f90673Sjfb 
71508f90673Sjfb 	switch (files_differ(f1, f2)) {
71608f90673Sjfb 	case 0:
71708f90673Sjfb 		goto closem;
71808f90673Sjfb 	case 1:
71908f90673Sjfb 		break;
72008f90673Sjfb 	default:
72108f90673Sjfb 		/* error */
72208f90673Sjfb 		goto closem;
72308f90673Sjfb 	}
72408f90673Sjfb 
72508f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
72608f90673Sjfb 		rval = D_BINARY;
72708f90673Sjfb 		goto closem;
72808f90673Sjfb 	}
7297f535ec4Sjfb 	if ((prepare(0, f1, stb1.st_size) < 0) ||
7307f535ec4Sjfb 	    (prepare(1, f2, stb2.st_size) < 0)) {
7317f535ec4Sjfb 		goto closem;
7327f535ec4Sjfb 	}
73308f90673Sjfb 	prune();
73408f90673Sjfb 	sort(sfile[0], slen[0]);
73508f90673Sjfb 	sort(sfile[1], slen[1]);
73608f90673Sjfb 
73708f90673Sjfb 	member = (int *)file[1];
73808f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
73918501190Sniallo 	if ((tmp = realloc(member, (slen[1] + 2) * sizeof(int))) == NULL) {
74018501190Sniallo 		free(member);
74118501190Sniallo 		member = NULL;
74218501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize member");
7437f535ec4Sjfb 		goto closem;
74418501190Sniallo 	}
7457f535ec4Sjfb 	member = (int *)tmp;
74608f90673Sjfb 
74708f90673Sjfb 	class = (int *)file[0];
74808f90673Sjfb 	unsort(sfile[0], slen[0], class);
74918501190Sniallo 	if ((tmp = realloc(class, (slen[0] + 2) * sizeof(int))) == NULL) {
75018501190Sniallo 		free(class);
75118501190Sniallo 		class = NULL;
75218501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize class");
7537f535ec4Sjfb 		goto closem;
75418501190Sniallo 	}
7557f535ec4Sjfb 	class = (int *)tmp;
75608f90673Sjfb 
7577f535ec4Sjfb 	if ((klist = malloc((slen[0] + 2) * sizeof(int))) == NULL) {
7587f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate klist");
7597f535ec4Sjfb 		goto closem;
7607f535ec4Sjfb 	}
76108f90673Sjfb 	clen = 0;
76208f90673Sjfb 	clistlen = 100;
7637f535ec4Sjfb 	if ((clist = malloc(clistlen * sizeof(cand))) == NULL) {
7647f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate clist");
7657f535ec4Sjfb 		goto closem;
7667f535ec4Sjfb 	}
767ece76a70Sjoris 
768ece76a70Sjoris 	if ((i = stone(class, slen[0], member, klist)) < 0)
769ece76a70Sjoris 		goto closem;
770ece76a70Sjoris 
77108f90673Sjfb 	free(member);
77208f90673Sjfb 	free(class);
77308f90673Sjfb 
77418501190Sniallo 	if ((tmp = realloc(J, (diff_len[0] + 2) * sizeof(int))) == NULL) {
77518501190Sniallo 		free(J);
77618501190Sniallo 		J = NULL;
77718501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize J");
77818501190Sniallo 		goto closem;
77918501190Sniallo 	}
78018501190Sniallo 	J = (int *)tmp;
78108f90673Sjfb 	unravel(klist[i]);
78208f90673Sjfb 	free(clist);
78308f90673Sjfb 	free(klist);
78408f90673Sjfb 
78518501190Sniallo 	if ((tmp = realloc(ixold, (diff_len[0] + 2) * sizeof(long))) == NULL) {
78618501190Sniallo 		free(ixold);
78718501190Sniallo 		ixold = NULL;
78818501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize ixold");
78918501190Sniallo 		goto closem;
79018501190Sniallo 	}
79118501190Sniallo 	ixold = (long *)tmp;
79218501190Sniallo 	if ((tmp = realloc(ixnew, (diff_len[1] + 2) * sizeof(long))) == NULL) {
79318501190Sniallo 		free(ixnew);
79418501190Sniallo 		ixnew = NULL;
79518501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize ixnew");
79618501190Sniallo 		goto closem;
79718501190Sniallo 	}
79818501190Sniallo 	ixnew = (long *)tmp;
79908f90673Sjfb 	check(f1, f2);
80008f90673Sjfb 	output(file1, f1, file2, f2);
80108f90673Sjfb 
80208f90673Sjfb closem:
803*37af5b8bSxsa 	if (anychange == 1) {
80408f90673Sjfb 		if (rval == D_SAME)
80508f90673Sjfb 			rval = D_DIFFER;
80608f90673Sjfb 	}
80708f90673Sjfb 	if (f1 != NULL)
80808f90673Sjfb 		fclose(f1);
80908f90673Sjfb 	if (f2 != NULL)
81008f90673Sjfb 		fclose(f2);
8117f535ec4Sjfb 
81208f90673Sjfb 	return (rval);
81308f90673Sjfb }
81408f90673Sjfb 
81508f90673Sjfb /*
81608f90673Sjfb  * Check to see if the given files differ.
81708f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
81808f90673Sjfb  * XXX - could use code from cmp(1) [faster]
81908f90673Sjfb  */
82008f90673Sjfb static int
82108f90673Sjfb files_differ(FILE *f1, FILE *f2)
82208f90673Sjfb {
82308f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
82408f90673Sjfb 	size_t i, j;
82508f90673Sjfb 
82608f90673Sjfb 	if (stb1.st_size != stb2.st_size)
82708f90673Sjfb 		return (1);
82808f90673Sjfb 	for (;;) {
829f1901a5aSxsa 		i = fread(buf1, (size_t)1, sizeof(buf1), f1);
830f1901a5aSxsa 		j = fread(buf2, (size_t)1, sizeof(buf2), f2);
83108f90673Sjfb 		if (i != j)
83208f90673Sjfb 			return (1);
833*37af5b8bSxsa 		if ((i == 0) && (j == 0)) {
83408f90673Sjfb 			if (ferror(f1) || ferror(f2))
83508f90673Sjfb 				return (1);
83608f90673Sjfb 			return (0);
83708f90673Sjfb 		}
83808f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
83908f90673Sjfb 			return (1);
84008f90673Sjfb 	}
84108f90673Sjfb }
84208f90673Sjfb 
8437f535ec4Sjfb static int
84408f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
84508f90673Sjfb {
8467f535ec4Sjfb 	void *tmp;
84708f90673Sjfb 	struct line *p;
84808f90673Sjfb 	int j, h;
84908f90673Sjfb 	size_t sz;
85008f90673Sjfb 
85108f90673Sjfb 	rewind(fd);
85208f90673Sjfb 
853c48da046Sxsa 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
85408f90673Sjfb 	if (sz < 100)
85508f90673Sjfb 		sz = 100;
85608f90673Sjfb 
8577f535ec4Sjfb 	p = (struct line *)malloc((sz + 3) * sizeof(struct line));
8587f535ec4Sjfb 	if (p == NULL) {
8597f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to prepare line array");
8607f535ec4Sjfb 		return (-1);
8617f535ec4Sjfb 	}
86208f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
86308f90673Sjfb 		if (j == (int)sz) {
86408f90673Sjfb 			sz = sz * 3 / 2;
8657f535ec4Sjfb 			tmp = realloc(p, (sz + 3) * sizeof(struct line));
8667f535ec4Sjfb 			if (tmp == NULL) {
8677f535ec4Sjfb 				cvs_log(LP_ERRNO, "failed to grow line array");
8687f535ec4Sjfb 				free(p);
8697f535ec4Sjfb 				return (-1);
8707f535ec4Sjfb 			}
8717f535ec4Sjfb 			p = (struct line *)tmp;
87208f90673Sjfb 		}
87308f90673Sjfb 		p[++j].value = h;
87408f90673Sjfb 	}
875e4276007Sjfb 	diff_len[i] = j;
87608f90673Sjfb 	file[i] = p;
8777f535ec4Sjfb 
8787f535ec4Sjfb 	return (0);
87908f90673Sjfb }
88008f90673Sjfb 
88108f90673Sjfb static void
88208f90673Sjfb prune(void)
88308f90673Sjfb {
88408f90673Sjfb 	int i, j;
88508f90673Sjfb 
886e4276007Sjfb 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
88708f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
88808f90673Sjfb 	    pref++)
88908f90673Sjfb 		;
890e4276007Sjfb 	for (suff = 0;
891e4276007Sjfb 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
892e4276007Sjfb 	    (file[0][diff_len[0] - suff].value ==
893e4276007Sjfb 	    file[1][diff_len[1] - suff].value);
89408f90673Sjfb 	    suff++)
89508f90673Sjfb 		;
89608f90673Sjfb 	for (j = 0; j < 2; j++) {
89708f90673Sjfb 		sfile[j] = file[j] + pref;
898e4276007Sjfb 		slen[j] = diff_len[j] - pref - suff;
89908f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
90008f90673Sjfb 			sfile[j][i].serial = i;
90108f90673Sjfb 	}
90208f90673Sjfb }
90308f90673Sjfb 
90408f90673Sjfb static void
90508f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
90608f90673Sjfb {
90708f90673Sjfb 	int i, j;
90808f90673Sjfb 
90908f90673Sjfb 	i = j = 1;
91008f90673Sjfb 	while (i <= n && j <= m) {
91108f90673Sjfb 		if (a[i].value < b[j].value)
91208f90673Sjfb 			a[i++].value = 0;
91308f90673Sjfb 		else if (a[i].value == b[j].value)
91408f90673Sjfb 			a[i++].value = j;
91508f90673Sjfb 		else
91608f90673Sjfb 			j++;
91708f90673Sjfb 	}
91808f90673Sjfb 	while (i <= n)
91908f90673Sjfb 		a[i++].value = 0;
92008f90673Sjfb 	b[m + 1].value = 0;
92108f90673Sjfb 	j = 0;
92208f90673Sjfb 	while (++j <= m) {
92308f90673Sjfb 		c[j] = -b[j].serial;
92408f90673Sjfb 		while (b[j + 1].value == b[j].value) {
92508f90673Sjfb 			j++;
92608f90673Sjfb 			c[j] = b[j].serial;
92708f90673Sjfb 		}
92808f90673Sjfb 	}
92908f90673Sjfb 	c[j] = -1;
93008f90673Sjfb }
93108f90673Sjfb 
93208f90673Sjfb /* Code taken from ping.c */
93308f90673Sjfb static int
93408f90673Sjfb isqrt(int n)
93508f90673Sjfb {
93608f90673Sjfb 	int y, x = 1;
93708f90673Sjfb 
93808f90673Sjfb 	if (n == 0)
93908f90673Sjfb 		return (0);
94008f90673Sjfb 
94108f90673Sjfb 	do { /* newton was a stinker */
94208f90673Sjfb 		y = x;
94308f90673Sjfb 		x = n / x;
94408f90673Sjfb 		x += y;
94508f90673Sjfb 		x /= 2;
94608f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
94708f90673Sjfb 
94808f90673Sjfb 	return (x);
94908f90673Sjfb }
95008f90673Sjfb 
95108f90673Sjfb static int
95208f90673Sjfb stone(int *a, int n, int *b, int *c)
95308f90673Sjfb {
954ece76a70Sjoris 	int ret;
95508f90673Sjfb 	int i, k, y, j, l;
95608f90673Sjfb 	int oldc, tc, oldl;
95708f90673Sjfb 	u_int numtries;
95808f90673Sjfb 
959cc649edbSjfb 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
960cc649edbSjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
96108f90673Sjfb 
96208f90673Sjfb 	k = 0;
963ece76a70Sjoris 	if ((ret = newcand(0, 0, 0)) < 0)
964ece76a70Sjoris 		return (-1);
965ece76a70Sjoris 	c[0] = ret;
96608f90673Sjfb 	for (i = 1; i <= n; i++) {
96708f90673Sjfb 		j = a[i];
96808f90673Sjfb 		if (j == 0)
96908f90673Sjfb 			continue;
97008f90673Sjfb 		y = -b[j];
97108f90673Sjfb 		oldl = 0;
97208f90673Sjfb 		oldc = c[0];
97308f90673Sjfb 		numtries = 0;
97408f90673Sjfb 		do {
97508f90673Sjfb 			if (y <= clist[oldc].y)
97608f90673Sjfb 				continue;
97708f90673Sjfb 			l = search(c, k, y);
97808f90673Sjfb 			if (l != oldl + 1)
97908f90673Sjfb 				oldc = c[l - 1];
98008f90673Sjfb 			if (l <= k) {
98108f90673Sjfb 				if (clist[c[l]].y <= y)
98208f90673Sjfb 					continue;
98308f90673Sjfb 				tc = c[l];
984ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
985ece76a70Sjoris 					return (-1);
986ece76a70Sjoris 				c[l] = ret;
98708f90673Sjfb 				oldc = tc;
98808f90673Sjfb 				oldl = l;
98908f90673Sjfb 				numtries++;
99008f90673Sjfb 			} else {
991ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
992ece76a70Sjoris 					return (-1);
993ece76a70Sjoris 				c[l] = ret;
99408f90673Sjfb 				k++;
99508f90673Sjfb 				break;
99608f90673Sjfb 			}
99708f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
99808f90673Sjfb 	}
99908f90673Sjfb 	return (k);
100008f90673Sjfb }
100108f90673Sjfb 
100208f90673Sjfb static int
100308f90673Sjfb newcand(int x, int y, int pred)
100408f90673Sjfb {
100518501190Sniallo 	struct cand *q, *tmp;
100618501190Sniallo 	int newclistlen;
100708f90673Sjfb 
100808f90673Sjfb 	if (clen == clistlen) {
100918501190Sniallo 		newclistlen = clistlen * 11 / 10;
101018501190Sniallo 		tmp = realloc(clist, newclistlen * sizeof(cand));
101118501190Sniallo 		if (tmp == NULL) {
10127f535ec4Sjfb 			cvs_log(LP_ERRNO, "failed to resize clist");
10137f535ec4Sjfb 			return (-1);
10147f535ec4Sjfb 		}
101518501190Sniallo 		clist = tmp;
101618501190Sniallo 		clistlen = newclistlen;
101708f90673Sjfb 	}
101808f90673Sjfb 	q = clist + clen;
101908f90673Sjfb 	q->x = x;
102008f90673Sjfb 	q->y = y;
102108f90673Sjfb 	q->pred = pred;
102208f90673Sjfb 	return (clen++);
102308f90673Sjfb }
102408f90673Sjfb 
102508f90673Sjfb static int
102608f90673Sjfb search(int *c, int k, int y)
102708f90673Sjfb {
102808f90673Sjfb 	int i, j, l, t;
102908f90673Sjfb 
103008f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
103108f90673Sjfb 		return (k + 1);
103208f90673Sjfb 	i = 0;
103308f90673Sjfb 	j = k + 1;
103408f90673Sjfb 	while (1) {
103508f90673Sjfb 		l = i + j;
103608f90673Sjfb 		if ((l >>= 1) <= i)
103708f90673Sjfb 			break;
103808f90673Sjfb 		t = clist[c[l]].y;
103908f90673Sjfb 		if (t > y)
104008f90673Sjfb 			j = l;
104108f90673Sjfb 		else if (t < y)
104208f90673Sjfb 			i = l;
104308f90673Sjfb 		else
104408f90673Sjfb 			return (l);
104508f90673Sjfb 	}
104608f90673Sjfb 	return (l + 1);
104708f90673Sjfb }
104808f90673Sjfb 
104908f90673Sjfb static void
105008f90673Sjfb unravel(int p)
105108f90673Sjfb {
105208f90673Sjfb 	struct cand *q;
105308f90673Sjfb 	int i;
105408f90673Sjfb 
1055e4276007Sjfb 	for (i = 0; i <= diff_len[0]; i++)
105608f90673Sjfb 		J[i] = i <= pref ? i :
1057e4276007Sjfb 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
105808f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
105908f90673Sjfb 		J[q->x + pref] = q->y + pref;
106008f90673Sjfb }
106108f90673Sjfb 
106208f90673Sjfb /*
106308f90673Sjfb  * Check does double duty:
106408f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
106508f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
106608f90673Sjfb  *  2.  collect random access indexes to the two files
106708f90673Sjfb  */
106808f90673Sjfb static void
106908f90673Sjfb check(FILE *f1, FILE *f2)
107008f90673Sjfb {
107108f90673Sjfb 	int i, j, jackpot, c, d;
107208f90673Sjfb 	long ctold, ctnew;
107308f90673Sjfb 
107408f90673Sjfb 	rewind(f1);
107508f90673Sjfb 	rewind(f2);
107608f90673Sjfb 	j = 1;
107708f90673Sjfb 	ixold[0] = ixnew[0] = 0;
107808f90673Sjfb 	jackpot = 0;
107908f90673Sjfb 	ctold = ctnew = 0;
1080e4276007Sjfb 	for (i = 1; i <= diff_len[0]; i++) {
108108f90673Sjfb 		if (J[i] == 0) {
108208f90673Sjfb 			ixold[i] = ctold += skipline(f1);
108308f90673Sjfb 			continue;
108408f90673Sjfb 		}
108508f90673Sjfb 		while (j < J[i]) {
108608f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
108708f90673Sjfb 			j++;
108808f90673Sjfb 		}
108948dc77e6Sxsa 		if ((bflag == 1)|| (wflag == 1) || (iflag == 1)) {
109008f90673Sjfb 			for (;;) {
109108f90673Sjfb 				c = getc(f1);
109208f90673Sjfb 				d = getc(f2);
109308f90673Sjfb 				/*
109408f90673Sjfb 				 * GNU diff ignores a missing newline
109508f90673Sjfb 				 * in one file if bflag || wflag.
109608f90673Sjfb 				 */
109748dc77e6Sxsa 				if (((bflag == 1) || (wflag == 1)) &&
109808f90673Sjfb 				    ((c == EOF && d == '\n') ||
109908f90673Sjfb 				    (c == '\n' && d == EOF))) {
110008f90673Sjfb 					break;
110108f90673Sjfb 				}
110208f90673Sjfb 				ctold++;
110308f90673Sjfb 				ctnew++;
110448dc77e6Sxsa 				if ((bflag == 1) && isspace(c) && isspace(d)) {
110508f90673Sjfb 					do {
110608f90673Sjfb 						if (c == '\n')
110708f90673Sjfb 							break;
110808f90673Sjfb 						ctold++;
110908f90673Sjfb 					} while (isspace(c = getc(f1)));
111008f90673Sjfb 					do {
111108f90673Sjfb 						if (d == '\n')
111208f90673Sjfb 							break;
111308f90673Sjfb 						ctnew++;
111408f90673Sjfb 					} while (isspace(d = getc(f2)));
111548dc77e6Sxsa 				} else if (wflag == 1) {
111608f90673Sjfb 					while (isspace(c) && c != '\n') {
111708f90673Sjfb 						c = getc(f1);
111808f90673Sjfb 						ctold++;
111908f90673Sjfb 					}
112008f90673Sjfb 					while (isspace(d) && d != '\n') {
112108f90673Sjfb 						d = getc(f2);
112208f90673Sjfb 						ctnew++;
112308f90673Sjfb 					}
112408f90673Sjfb 				}
112508f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
112608f90673Sjfb 					jackpot++;
112708f90673Sjfb 					J[i] = 0;
1128*37af5b8bSxsa 					if ((c != '\n') && (c != EOF))
112908f90673Sjfb 						ctold += skipline(f1);
1130*37af5b8bSxsa 					if ((d != '\n') && (c != EOF))
113108f90673Sjfb 						ctnew += skipline(f2);
113208f90673Sjfb 					break;
113308f90673Sjfb 				}
1134*37af5b8bSxsa 				if ((c == '\n') || (c == EOF))
113508f90673Sjfb 					break;
113608f90673Sjfb 			}
113708f90673Sjfb 		} else {
113808f90673Sjfb 			for (;;) {
113908f90673Sjfb 				ctold++;
114008f90673Sjfb 				ctnew++;
114108f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
114208f90673Sjfb 					/* jackpot++; */
114308f90673Sjfb 					J[i] = 0;
1144*37af5b8bSxsa 					if ((c != '\n') && (c != EOF))
114508f90673Sjfb 						ctold += skipline(f1);
1146*37af5b8bSxsa 					if ((d != '\n') && (c != EOF))
114708f90673Sjfb 						ctnew += skipline(f2);
114808f90673Sjfb 					break;
114908f90673Sjfb 				}
1150*37af5b8bSxsa 				if ((c == '\n') || (c == EOF))
115108f90673Sjfb 					break;
115208f90673Sjfb 			}
115308f90673Sjfb 		}
115408f90673Sjfb 		ixold[i] = ctold;
115508f90673Sjfb 		ixnew[j] = ctnew;
115608f90673Sjfb 		j++;
115708f90673Sjfb 	}
1158e4276007Sjfb 	for (; j <= diff_len[1]; j++)
115908f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
116008f90673Sjfb 	/*
1161*37af5b8bSxsa 	 * if (jackpot != 0)
11625ac8b1e7Sjoris 	 *	cvs_printf("jackpot\n");
116308f90673Sjfb 	 */
116408f90673Sjfb }
116508f90673Sjfb 
116608f90673Sjfb /* shellsort CACM #201 */
116708f90673Sjfb static void
116808f90673Sjfb sort(struct line *a, int n)
116908f90673Sjfb {
117008f90673Sjfb 	struct line *ai, *aim, w;
117108f90673Sjfb 	int j, m = 0, k;
117208f90673Sjfb 
117308f90673Sjfb 	if (n == 0)
117408f90673Sjfb 		return;
117508f90673Sjfb 	for (j = 1; j <= n; j *= 2)
117608f90673Sjfb 		m = 2 * j - 1;
117708f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
117808f90673Sjfb 		k = n - m;
117908f90673Sjfb 		for (j = 1; j <= k; j++) {
118008f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
118108f90673Sjfb 				aim = &ai[m];
118208f90673Sjfb 				if (aim < ai)
118308f90673Sjfb 					break;	/* wraparound */
118408f90673Sjfb 				if (aim->value > ai[0].value ||
118508f90673Sjfb 				    (aim->value == ai[0].value &&
118608f90673Sjfb 					aim->serial > ai[0].serial))
118708f90673Sjfb 					break;
118808f90673Sjfb 				w.value = ai[0].value;
118908f90673Sjfb 				ai[0].value = aim->value;
119008f90673Sjfb 				aim->value = w.value;
119108f90673Sjfb 				w.serial = ai[0].serial;
119208f90673Sjfb 				ai[0].serial = aim->serial;
119308f90673Sjfb 				aim->serial = w.serial;
119408f90673Sjfb 			}
119508f90673Sjfb 		}
119608f90673Sjfb 	}
119708f90673Sjfb }
119808f90673Sjfb 
119908f90673Sjfb static void
120008f90673Sjfb unsort(struct line *f, int l, int *b)
120108f90673Sjfb {
120208f90673Sjfb 	int *a, i;
120308f90673Sjfb 
12047f535ec4Sjfb 	if ((a = (int *)malloc((l + 1) * sizeof(int))) == NULL) {
12057f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate sort array");
12067f535ec4Sjfb 		return;
12077f535ec4Sjfb 	}
120808f90673Sjfb 	for (i = 1; i <= l; i++)
120908f90673Sjfb 		a[f[i].serial] = f[i].value;
121008f90673Sjfb 	for (i = 1; i <= l; i++)
121108f90673Sjfb 		b[i] = a[i];
121208f90673Sjfb 	free(a);
121308f90673Sjfb }
121408f90673Sjfb 
121508f90673Sjfb static int
121608f90673Sjfb skipline(FILE *f)
121708f90673Sjfb {
121808f90673Sjfb 	int i, c;
121908f90673Sjfb 
122008f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
122108f90673Sjfb 		continue;
122208f90673Sjfb 	return (i);
122308f90673Sjfb }
122408f90673Sjfb 
122508f90673Sjfb static void
122608f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2)
122708f90673Sjfb {
122808f90673Sjfb 	int m, i0, i1, j0, j1;
122908f90673Sjfb 
123008f90673Sjfb 	rewind(f1);
123108f90673Sjfb 	rewind(f2);
1232e4276007Sjfb 	m = diff_len[0];
123308f90673Sjfb 	J[0] = 0;
1234e4276007Sjfb 	J[m + 1] = diff_len[1] + 1;
123508f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
123608f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
123708f90673Sjfb 			i0++;
123808f90673Sjfb 		j0 = J[i0 - 1] + 1;
123908f90673Sjfb 		i1 = i0 - 1;
124008f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
124108f90673Sjfb 			i1++;
124208f90673Sjfb 		j1 = J[i1 + 1] - 1;
124308f90673Sjfb 		J[i1] = j1;
124408f90673Sjfb 		change(file1, f1, file2, f2, i0, i1, j0, j1);
124508f90673Sjfb 	}
124608f90673Sjfb 	if (m == 0)
1247e4276007Sjfb 		change(file1, f1, file2, f2, 1, 0, 1, diff_len[1]);
1248f9b67873Sniallo 	if (diff_format == D_IFDEF) {
124908f90673Sjfb 		for (;;) {
125008f90673Sjfb #define	c i0
125108f90673Sjfb 			if ((c = getc(f1)) == EOF)
125208f90673Sjfb 				return;
1253f9b67873Sniallo 			diff_output("%c", c);
125408f90673Sjfb 		}
125508f90673Sjfb #undef c
125608f90673Sjfb 	}
125708f90673Sjfb 	if (anychange != 0) {
1258f9b67873Sniallo 		if (diff_format == D_CONTEXT)
125908f90673Sjfb 			dump_context_vec(f1, f2);
1260f9b67873Sniallo 		else if (diff_format == D_UNIFIED)
126108f90673Sjfb 			dump_unified_vec(f1, f2);
126208f90673Sjfb 	}
126308f90673Sjfb }
126408f90673Sjfb 
126508f90673Sjfb static __inline void
126608f90673Sjfb range(int a, int b, char *separator)
126708f90673Sjfb {
1268f9b67873Sniallo 	diff_output("%d", a > b ? b : a);
126908f90673Sjfb 	if (a < b)
1270f9b67873Sniallo 		diff_output("%s%d", separator, b);
127108f90673Sjfb }
127208f90673Sjfb 
127308f90673Sjfb static __inline void
127408f90673Sjfb uni_range(int a, int b)
127508f90673Sjfb {
127608f90673Sjfb 	if (a < b)
1277f9b67873Sniallo 		diff_output("%d,%d", a, b - a + 1);
127808f90673Sjfb 	else if (a == b)
1279f9b67873Sniallo 		diff_output("%d", b);
128008f90673Sjfb 	else
1281f9b67873Sniallo 		diff_output("%d,0", b);
128208f90673Sjfb }
128308f90673Sjfb 
128408f90673Sjfb static char *
12852a0de57dSjfb preadline(int fd, size_t rlen, off_t off)
128608f90673Sjfb {
128708f90673Sjfb 	char *line;
128808f90673Sjfb 	ssize_t nr;
128908f90673Sjfb 
12902a0de57dSjfb 	line = malloc(rlen + 1);
129108f90673Sjfb 	if (line == NULL) {
129208f90673Sjfb 		cvs_log(LP_ERRNO, "failed to allocate line");
129308f90673Sjfb 		return (NULL);
129408f90673Sjfb 	}
12952a0de57dSjfb 	if ((nr = pread(fd, line, rlen, off)) < 0) {
129608f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
129708f90673Sjfb 		return (NULL);
129808f90673Sjfb 	}
129908f90673Sjfb 	line[nr] = '\0';
130008f90673Sjfb 	return (line);
130108f90673Sjfb }
130208f90673Sjfb 
130308f90673Sjfb static int
130408f90673Sjfb ignoreline(char *line)
130508f90673Sjfb {
130608f90673Sjfb 	int ret;
130708f90673Sjfb 
1308f1901a5aSxsa 	ret = regexec(&ignore_re, line, (size_t)0, NULL, 0);
130908f90673Sjfb 	free(line);
131008f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
131108f90673Sjfb }
131208f90673Sjfb 
131308f90673Sjfb /*
131408f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
131508f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
131608f90673Sjfb  * are no lines in the from file involved and this means that there were
131708f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
131808f90673Sjfb  * lines missing from the to file.
131908f90673Sjfb  */
132008f90673Sjfb static void
132108f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2,
132208f90673Sjfb 	int a, int b, int c, int d)
132308f90673Sjfb {
132408f90673Sjfb 	static size_t max_context = 64;
132508f90673Sjfb 	int i;
132608f90673Sjfb 
1327f9b67873Sniallo 	if (diff_format != D_IFDEF && a > b && c > d)
132808f90673Sjfb 		return;
132908f90673Sjfb 	if (ignore_pats != NULL) {
133008f90673Sjfb 		char *line;
133108f90673Sjfb 		/*
133208f90673Sjfb 		 * All lines in the change, insert, or delete must
133308f90673Sjfb 		 * match an ignore pattern for the change to be
133408f90673Sjfb 		 * ignored.
133508f90673Sjfb 		 */
133608f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
133708f90673Sjfb 			for (i = a; i <= b; i++) {
133808f90673Sjfb 				line = preadline(fileno(f1),
133908f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
134008f90673Sjfb 				if (!ignoreline(line))
134108f90673Sjfb 					goto proceed;
134208f90673Sjfb 			}
134308f90673Sjfb 		}
1344*37af5b8bSxsa 		if ((a > b) || (c <= d)) {	/* Changes and inserts. */
134508f90673Sjfb 			for (i = c; i <= d; i++) {
134608f90673Sjfb 				line = preadline(fileno(f2),
134708f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
134808f90673Sjfb 				if (!ignoreline(line))
134908f90673Sjfb 					goto proceed;
135008f90673Sjfb 			}
135108f90673Sjfb 		}
135208f90673Sjfb 		return;
135308f90673Sjfb 	}
135408f90673Sjfb proceed:
1355f9b67873Sniallo 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
135608f90673Sjfb 		/*
135708f90673Sjfb 		 * Allocate change records as needed.
135808f90673Sjfb 		 */
135908f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
136018501190Sniallo 			struct context_vec *tmp;
136108f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
136208f90673Sjfb 			max_context <<= 1;
13634c06e5f6Sreyk 			if ((tmp = realloc(context_vec_start, max_context *
13644c06e5f6Sreyk 			    sizeof(struct context_vec))) == NULL) {
136518501190Sniallo 				free(context_vec_start);
136618501190Sniallo 				context_vec_start = NULL;
136718501190Sniallo 				cvs_log(LP_ERRNO,
136818501190Sniallo 				    "failed to resize context_vec_start");
136918501190Sniallo 				return;
137018501190Sniallo 			}
137118501190Sniallo 			context_vec_start = tmp;
137208f90673Sjfb 			context_vec_end = context_vec_start + max_context;
137308f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
137408f90673Sjfb 		}
137508f90673Sjfb 		if (anychange == 0) {
137608f90673Sjfb 			/*
137708f90673Sjfb 			 * Print the context/unidiff header first time through.
137808f90673Sjfb 			 */
1379f9b67873Sniallo 			diff_output("%s %s	%s",
1380f9b67873Sniallo 			    diff_format == D_CONTEXT ? "***" : "---", diff_file,
138108f90673Sjfb 			    ctime(&stb1.st_mtime));
1382f9b67873Sniallo 			diff_output("%s %s	%s",
1383f9b67873Sniallo 			    diff_format == D_CONTEXT ? "---" : "+++", diff_file,
138408f90673Sjfb 			    ctime(&stb2.st_mtime));
138508f90673Sjfb 			anychange = 1;
138608f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
138708f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
138808f90673Sjfb 			/*
138908f90673Sjfb 			 * If this change is more than 'context' lines from the
139008f90673Sjfb 			 * previous change, dump the record and reset it.
139108f90673Sjfb 			 */
1392f9b67873Sniallo 			if (diff_format == D_CONTEXT)
139308f90673Sjfb 				dump_context_vec(f1, f2);
139408f90673Sjfb 			else
139508f90673Sjfb 				dump_unified_vec(f1, f2);
139608f90673Sjfb 		}
139708f90673Sjfb 		context_vec_ptr++;
139808f90673Sjfb 		context_vec_ptr->a = a;
139908f90673Sjfb 		context_vec_ptr->b = b;
140008f90673Sjfb 		context_vec_ptr->c = c;
140108f90673Sjfb 		context_vec_ptr->d = d;
140208f90673Sjfb 		return;
140308f90673Sjfb 	}
140408f90673Sjfb 	if (anychange == 0)
140508f90673Sjfb 		anychange = 1;
1406f9b67873Sniallo 	switch (diff_format) {
140708f90673Sjfb 	case D_BRIEF:
140808f90673Sjfb 		return;
140908f90673Sjfb 	case D_NORMAL:
141008f90673Sjfb 		range(a, b, ",");
1411f9b67873Sniallo 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1412f9b67873Sniallo 		if (diff_format == D_NORMAL)
141308f90673Sjfb 			range(c, d, ",");
1414f9b67873Sniallo 		diff_output("\n");
141508f90673Sjfb 		break;
1416394180a4Sjfb 	case D_RCSDIFF:
1417394180a4Sjfb 		if (a > b)
1418f9b67873Sniallo 			diff_output("a%d %d\n", b, d - c + 1);
1419394180a4Sjfb 		else {
1420f9b67873Sniallo 			diff_output("d%d %d\n", a, b - a + 1);
1421394180a4Sjfb 
1422394180a4Sjfb 			if (!(c > d))	/* add changed lines */
1423f9b67873Sniallo 				diff_output("a%d %d\n", b, d - c + 1);
1424394180a4Sjfb 		}
1425394180a4Sjfb 		break;
142608f90673Sjfb 	}
1427f9b67873Sniallo 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
142808f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
1429f9b67873Sniallo 		if (a <= b && c <= d && diff_format == D_NORMAL)
1430206543eeSjoris 			diff_output("---\n");
143108f90673Sjfb 	}
1432f9b67873Sniallo 	i = fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0);
143308f90673Sjfb 	if (inifdef) {
1434f9b67873Sniallo 		diff_output("#endif /* %s */\n", ifdefname);
143508f90673Sjfb 		inifdef = 0;
143608f90673Sjfb 	}
143708f90673Sjfb }
143808f90673Sjfb 
143908f90673Sjfb static int
144008f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
144108f90673Sjfb {
144208f90673Sjfb 	int i, j, c, lastc, col, nc;
144308f90673Sjfb 
144408f90673Sjfb 	/*
144508f90673Sjfb 	 * When doing #ifdef's, copy down to current line
144608f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
144708f90673Sjfb 	 */
1448f9b67873Sniallo 	if (diff_format == D_IFDEF && oldfile) {
144908f90673Sjfb 		long curpos = ftell(lb);
145008f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
145108f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
145208f90673Sjfb 		for (i = 0; i < nc; i++)
1453f9b67873Sniallo 			diff_output("%c", getc(lb));
145408f90673Sjfb 	}
145508f90673Sjfb 	if (a > b)
145608f90673Sjfb 		return (0);
1457f9b67873Sniallo 	if (diff_format == D_IFDEF) {
145808f90673Sjfb 		if (inifdef) {
1459f9b67873Sniallo 			diff_output("#else /* %s%s */\n",
146008f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
146108f90673Sjfb 		} else {
146208f90673Sjfb 			if (oldfile)
1463f9b67873Sniallo 				diff_output("#ifndef %s\n", ifdefname);
146408f90673Sjfb 			else
1465f9b67873Sniallo 				diff_output("#ifdef %s\n", ifdefname);
146608f90673Sjfb 		}
146708f90673Sjfb 		inifdef = 1 + oldfile;
146808f90673Sjfb 	}
146908f90673Sjfb 	for (i = a; i <= b; i++) {
147008f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
147108f90673Sjfb 		nc = f[i] - f[i - 1];
1472f9b67873Sniallo 		if (diff_format != D_IFDEF && ch != '\0') {
1473f9b67873Sniallo 			diff_output("%c", ch);
147448dc77e6Sxsa 			if ((Tflag == 1 ) && (diff_format == D_NORMAL ||
14759c5161e4Sjoris 			    diff_format == D_CONTEXT ||
14769c5161e4Sjoris 			    diff_format == D_UNIFIED))
1477f9b67873Sniallo 				diff_output("\t");
1478f9b67873Sniallo 			else if (diff_format != D_UNIFIED)
1479f9b67873Sniallo 				diff_output(" ");
148008f90673Sjfb 		}
148108f90673Sjfb 		col = 0;
148208f90673Sjfb 		for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
148308f90673Sjfb 			if ((c = getc(lb)) == EOF) {
1484f9b67873Sniallo 				if (diff_format == D_RCSDIFF)
1485394180a4Sjfb 					warnx("No newline at end of file");
1486394180a4Sjfb 				else
14879c5161e4Sjoris 					diff_output("\n\\ No newline at end of "
14889c5161e4Sjoris 					    "file");
148908f90673Sjfb 				return (0);
149008f90673Sjfb 			}
149148dc77e6Sxsa 			if ((c == '\t') && (tflag == 1)) {
149208f90673Sjfb 				do {
1493f9b67873Sniallo 					diff_output(" ");
149408f90673Sjfb 				} while (++col & 7);
149508f90673Sjfb 			} else {
1496f9b67873Sniallo 				diff_output("%c", c);
149708f90673Sjfb 				col++;
149808f90673Sjfb 			}
149908f90673Sjfb 		}
150008f90673Sjfb 	}
150108f90673Sjfb 	return (0);
150208f90673Sjfb }
150308f90673Sjfb 
150408f90673Sjfb /*
150508f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
150608f90673Sjfb  */
150708f90673Sjfb static int
150808f90673Sjfb readhash(FILE *f)
150908f90673Sjfb {
151008f90673Sjfb 	int i, t, space;
151108f90673Sjfb 	int sum;
151208f90673Sjfb 
151308f90673Sjfb 	sum = 1;
151408f90673Sjfb 	space = 0;
151548dc77e6Sxsa 	if ((bflag != 1) && (wflag != 1)) {
151648dc77e6Sxsa 		if (iflag == 1)
151708f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
151808f90673Sjfb 				if (t == EOF) {
151908f90673Sjfb 					if (i == 0)
152008f90673Sjfb 						return (0);
152108f90673Sjfb 					break;
152208f90673Sjfb 				}
152308f90673Sjfb 				sum = sum * 127 + chrtran[t];
152408f90673Sjfb 			}
152508f90673Sjfb 		else
152608f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
152708f90673Sjfb 				if (t == EOF) {
152808f90673Sjfb 					if (i == 0)
152908f90673Sjfb 						return (0);
153008f90673Sjfb 					break;
153108f90673Sjfb 				}
153208f90673Sjfb 				sum = sum * 127 + t;
153308f90673Sjfb 			}
153408f90673Sjfb 	} else {
153508f90673Sjfb 		for (i = 0;;) {
153608f90673Sjfb 			switch (t = getc(f)) {
153708f90673Sjfb 			case '\t':
153808f90673Sjfb 			case ' ':
153908f90673Sjfb 				space++;
154008f90673Sjfb 				continue;
154108f90673Sjfb 			default:
154248dc77e6Sxsa 				if ((space != 0) && (wflag != 1)) {
154308f90673Sjfb 					i++;
154408f90673Sjfb 					space = 0;
154508f90673Sjfb 				}
154608f90673Sjfb 				sum = sum * 127 + chrtran[t];
154708f90673Sjfb 				i++;
154808f90673Sjfb 				continue;
154908f90673Sjfb 			case EOF:
155008f90673Sjfb 				if (i == 0)
155108f90673Sjfb 					return (0);
155208f90673Sjfb 				/* FALLTHROUGH */
155308f90673Sjfb 			case '\n':
155408f90673Sjfb 				break;
155508f90673Sjfb 			}
155608f90673Sjfb 			break;
155708f90673Sjfb 		}
155808f90673Sjfb 	}
155908f90673Sjfb 	/*
156008f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
156108f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
156208f90673Sjfb 	 */
156308f90673Sjfb 	return (sum == 0 ? 1 : sum);
156408f90673Sjfb }
156508f90673Sjfb 
156608f90673Sjfb static int
156708f90673Sjfb asciifile(FILE *f)
156808f90673Sjfb {
156908f90673Sjfb 	char buf[BUFSIZ];
157008f90673Sjfb 	int i, cnt;
157108f90673Sjfb 
157248dc77e6Sxsa 	if ((aflag == 1) || (f == NULL))
157308f90673Sjfb 		return (1);
157408f90673Sjfb 
157508f90673Sjfb 	rewind(f);
1576f1901a5aSxsa 	cnt = fread(buf, (size_t)1, sizeof(buf), f);
157708f90673Sjfb 	for (i = 0; i < cnt; i++)
157808f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
157908f90673Sjfb 			return (0);
158008f90673Sjfb 	return (1);
158108f90673Sjfb }
158208f90673Sjfb 
15835e78344dSjfb static char*
15845e78344dSjfb match_function(const long *f, int pos, FILE *fp)
15855e78344dSjfb {
15865e78344dSjfb 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
15875e78344dSjfb 	size_t nc;
15885e78344dSjfb 	int last = lastline;
15895e78344dSjfb 	char *p;
15905e78344dSjfb 
15915e78344dSjfb 	lastline = pos;
15925e78344dSjfb 	while (pos > last) {
15935e78344dSjfb 		fseek(fp, f[pos - 1], SEEK_SET);
15945e78344dSjfb 		nc = f[pos] - f[pos - 1];
15955e78344dSjfb 		if (nc >= sizeof(buf))
15965e78344dSjfb 			nc = sizeof(buf) - 1;
1597f1901a5aSxsa 		nc = fread(buf, (size_t)1, nc, fp);
15985e78344dSjfb 		if (nc > 0) {
15995e78344dSjfb 			buf[nc] = '\0';
1600634926d6Sniallo 			p = strchr((const char *)buf, '\n');
16015e78344dSjfb 			if (p != NULL)
16025e78344dSjfb 				*p = '\0';
16035e78344dSjfb 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
16044c06e5f6Sreyk 				strlcpy(lastbuf, (const char *)buf,
16054c06e5f6Sreyk 				    sizeof lastbuf);
16065e78344dSjfb 				lastmatchline = pos;
16075e78344dSjfb 				return lastbuf;
16085e78344dSjfb 			}
16095e78344dSjfb 		}
16105e78344dSjfb 		pos--;
16115e78344dSjfb 	}
16125e78344dSjfb 	return (lastmatchline > 0) ? lastbuf : NULL;
16135e78344dSjfb }
16145e78344dSjfb 
161508f90673Sjfb 
161608f90673Sjfb /* dump accumulated "context" diff changes */
161708f90673Sjfb static void
161808f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
161908f90673Sjfb {
162008f90673Sjfb 	struct context_vec *cvp = context_vec_start;
162108f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
162208f90673Sjfb 	int a, b, c, d;
16235e78344dSjfb 	char ch, *f;
162408f90673Sjfb 
162508f90673Sjfb 	if (context_vec_start > context_vec_ptr)
162608f90673Sjfb 		return;
162708f90673Sjfb 
162808f90673Sjfb 	b = d = 0;		/* gcc */
1629dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1630e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1631dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1632e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
163308f90673Sjfb 
1634f9b67873Sniallo 	diff_output("***************");
163548dc77e6Sxsa 	if (pflag == 1) {
16365e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
16375e78344dSjfb 		if (f != NULL) {
1638f9b67873Sniallo 			diff_output(" ");
1639f9b67873Sniallo 			diff_output("%s", f);
16405e78344dSjfb 		}
16415e78344dSjfb 	}
1642f9b67873Sniallo 	diff_output("\n*** ");
164308f90673Sjfb 	range(lowa, upb, ",");
1644f9b67873Sniallo 	diff_output(" ****\n");
164508f90673Sjfb 
164608f90673Sjfb 	/*
164708f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
164808f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
164908f90673Sjfb 	 * the "old" lines as context in the "new" list).
165008f90673Sjfb 	 */
165108f90673Sjfb 	do_output = 0;
165208f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
165308f90673Sjfb 		if (cvp->a <= cvp->b) {
165408f90673Sjfb 			cvp = context_vec_start;
165508f90673Sjfb 			do_output++;
165608f90673Sjfb 			break;
165708f90673Sjfb 		}
1658*37af5b8bSxsa 	if (do_output != 0) {
165908f90673Sjfb 		while (cvp <= context_vec_ptr) {
166008f90673Sjfb 			a = cvp->a;
166108f90673Sjfb 			b = cvp->b;
166208f90673Sjfb 			c = cvp->c;
166308f90673Sjfb 			d = cvp->d;
166408f90673Sjfb 
166508f90673Sjfb 			if (a <= b && c <= d)
166608f90673Sjfb 				ch = 'c';
166708f90673Sjfb 			else
166808f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
166908f90673Sjfb 
167008f90673Sjfb 			if (ch == 'a')
167108f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
167208f90673Sjfb 			else {
167308f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
167408f90673Sjfb 				fetch(ixold, a, b, f1,
167508f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
167608f90673Sjfb 			}
167708f90673Sjfb 			lowa = b + 1;
167808f90673Sjfb 			cvp++;
167908f90673Sjfb 		}
168008f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
168108f90673Sjfb 	}
168208f90673Sjfb 	/* output changes to the "new" file */
1683f9b67873Sniallo 	diff_output("--- ");
168408f90673Sjfb 	range(lowc, upd, ",");
1685f9b67873Sniallo 	diff_output(" ----\n");
168608f90673Sjfb 
168708f90673Sjfb 	do_output = 0;
168808f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
168908f90673Sjfb 		if (cvp->c <= cvp->d) {
169008f90673Sjfb 			cvp = context_vec_start;
169108f90673Sjfb 			do_output++;
169208f90673Sjfb 			break;
169308f90673Sjfb 		}
1694*37af5b8bSxsa 	if (do_output != 0) {
169508f90673Sjfb 		while (cvp <= context_vec_ptr) {
169608f90673Sjfb 			a = cvp->a;
169708f90673Sjfb 			b = cvp->b;
169808f90673Sjfb 			c = cvp->c;
169908f90673Sjfb 			d = cvp->d;
170008f90673Sjfb 
170108f90673Sjfb 			if (a <= b && c <= d)
170208f90673Sjfb 				ch = 'c';
170308f90673Sjfb 			else
170408f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
170508f90673Sjfb 
170608f90673Sjfb 			if (ch == 'd')
170708f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
170808f90673Sjfb 			else {
170908f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
171008f90673Sjfb 				fetch(ixnew, c, d, f2,
171108f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
171208f90673Sjfb 			}
171308f90673Sjfb 			lowc = d + 1;
171408f90673Sjfb 			cvp++;
171508f90673Sjfb 		}
171608f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
171708f90673Sjfb 	}
171808f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
171908f90673Sjfb }
172008f90673Sjfb 
172108f90673Sjfb /* dump accumulated "unified" diff changes */
172208f90673Sjfb static void
172308f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
172408f90673Sjfb {
172508f90673Sjfb 	struct context_vec *cvp = context_vec_start;
172608f90673Sjfb 	int lowa, upb, lowc, upd;
172708f90673Sjfb 	int a, b, c, d;
17285e78344dSjfb 	char ch, *f;
172908f90673Sjfb 
173008f90673Sjfb 	if (context_vec_start > context_vec_ptr)
173108f90673Sjfb 		return;
173208f90673Sjfb 
173308f90673Sjfb 	b = d = 0;		/* gcc */
1734dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1735e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1736dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1737e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
173808f90673Sjfb 
1739f9b67873Sniallo 	diff_output("@@ -");
174008f90673Sjfb 	uni_range(lowa, upb);
1741f9b67873Sniallo 	diff_output(" +");
174208f90673Sjfb 	uni_range(lowc, upd);
1743f9b67873Sniallo 	diff_output(" @@");
174448dc77e6Sxsa 	if (pflag == 1) {
17455e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
17465e78344dSjfb 		if (f != NULL) {
1747f9b67873Sniallo 			diff_output(" ");
1748f9b67873Sniallo 			diff_output("%s", f);
17495e78344dSjfb 		}
17505e78344dSjfb 	}
1751f9b67873Sniallo 	diff_output("\n");
175208f90673Sjfb 
175308f90673Sjfb 	/*
175408f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
175508f90673Sjfb 	 * are printed together.
175608f90673Sjfb 	 */
175708f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
175808f90673Sjfb 		a = cvp->a;
175908f90673Sjfb 		b = cvp->b;
176008f90673Sjfb 		c = cvp->c;
176108f90673Sjfb 		d = cvp->d;
176208f90673Sjfb 
176308f90673Sjfb 		/*
176408f90673Sjfb 		 * c: both new and old changes
176508f90673Sjfb 		 * d: only changes in the old file
176608f90673Sjfb 		 * a: only changes in the new file
176708f90673Sjfb 		 */
176808f90673Sjfb 		if (a <= b && c <= d)
176908f90673Sjfb 			ch = 'c';
177008f90673Sjfb 		else
177108f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
177208f90673Sjfb 
177308f90673Sjfb 		switch (ch) {
177408f90673Sjfb 		case 'c':
177508f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
177608f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
177708f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
177808f90673Sjfb 			break;
177908f90673Sjfb 		case 'd':
178008f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
178108f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
178208f90673Sjfb 			break;
178308f90673Sjfb 		case 'a':
178408f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
178508f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
178608f90673Sjfb 			break;
178708f90673Sjfb 		}
178808f90673Sjfb 		lowa = b + 1;
178908f90673Sjfb 		lowc = d + 1;
179008f90673Sjfb 	}
179108f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
179208f90673Sjfb 
179308f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
179408f90673Sjfb }
1795f9b67873Sniallo 
179601af718aSjoris void
1797f9b67873Sniallo diff_output(const char *fmt, ...)
1798f9b67873Sniallo {
1799f9b67873Sniallo 	va_list vap;
1800f9b67873Sniallo 	char *str;
1801f9b67873Sniallo 
1802f9b67873Sniallo 	va_start(vap, fmt);
1803f9b67873Sniallo 	vasprintf(&str, fmt, vap);
1804f9b67873Sniallo 	if (diffbuf != NULL)
1805f9b67873Sniallo 		cvs_buf_append(diffbuf, str, strlen(str));
1806f9b67873Sniallo 	else
1807f9b67873Sniallo 		cvs_printf("%s", str);
1808f9b67873Sniallo 	free(str);
1809f9b67873Sniallo 	va_end(vap);
1810f9b67873Sniallo }
1811