xref: /openbsd-src/usr.bin/cvs/diff.c (revision 78de330428d4b39ae438762405e5191487cd44cf)
1*78de3304Sniallo /*	$OpenBSD: diff.c,v 1.68 2005/12/01 20:36:30 niallo 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;
220*78de3304Sniallo 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 
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);
6611dee9299Sxsa 	strlcpy(path_tmp1, cvs_tmpdir, sizeof(path_tmp1));
6621dee9299Sxsa 	strlcat(path_tmp1, "/diff1.XXXXXXXXXX", sizeof(path_tmp1));
6637f535ec4Sjfb 	if (cvs_buf_write_stmp(b1, path_tmp1, 0600) == -1) {
6647f535ec4Sjfb 		cvs_buf_free(b1);
6657f535ec4Sjfb 		cvs_buf_free(b2);
66631274bbfSjoris 		return (CVS_EX_DATA);
6677f535ec4Sjfb 	}
6687f535ec4Sjfb 	cvs_buf_free(b1);
6697f535ec4Sjfb 
6701dee9299Sxsa 	strlcpy(path_tmp2, cvs_tmpdir, sizeof(path_tmp2));
6711dee9299Sxsa 	strlcat(path_tmp2, "/diff2.XXXXXXXXXX", sizeof(path_tmp2));
672946f6157Sdjm 	if (cvs_buf_write_stmp(b2, path_tmp2, 0600) == -1) {
6737f535ec4Sjfb 		cvs_buf_free(b2);
674946f6157Sdjm 		(void)unlink(path_tmp1);
67531274bbfSjoris 		return (CVS_EX_DATA);
676946f6157Sdjm 	}
6777f535ec4Sjfb 	cvs_buf_free(b2);
6787f535ec4Sjfb 
679f9b67873Sniallo 	cvs_diffreg(path_tmp1, path_tmp2, NULL);
680946f6157Sdjm 	(void)unlink(path_tmp1);
681946f6157Sdjm 	(void)unlink(path_tmp2);
68208f90673Sjfb 
68308f90673Sjfb 	return (0);
68408f90673Sjfb }
685af5bb824Sniallo #endif
68608f90673Sjfb 
68708f90673Sjfb 
68808f90673Sjfb int
689f9b67873Sniallo cvs_diffreg(const char *file1, const char *file2, BUF *out)
69008f90673Sjfb {
69108f90673Sjfb 	FILE *f1, *f2;
69208f90673Sjfb 	int i, rval;
6937f535ec4Sjfb 	void *tmp;
69408f90673Sjfb 
69508f90673Sjfb 	f1 = f2 = NULL;
69608f90673Sjfb 	rval = D_SAME;
69708f90673Sjfb 	anychange = 0;
69808f90673Sjfb 	lastline = 0;
69908f90673Sjfb 	lastmatchline = 0;
70008f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
70108f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
702f9b67873Sniallo 	if (out != NULL)
703f9b67873Sniallo 		diffbuf = out;
70408f90673Sjfb 
70508f90673Sjfb 	f1 = fopen(file1, "r");
70608f90673Sjfb 	if (f1 == NULL) {
70708f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
70808f90673Sjfb 		goto closem;
70908f90673Sjfb 	}
71008f90673Sjfb 
71108f90673Sjfb 	f2 = fopen(file2, "r");
71208f90673Sjfb 	if (f2 == NULL) {
71308f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
71408f90673Sjfb 		goto closem;
71508f90673Sjfb 	}
71608f90673Sjfb 
71708f90673Sjfb 	switch (files_differ(f1, f2)) {
71808f90673Sjfb 	case 0:
71908f90673Sjfb 		goto closem;
72008f90673Sjfb 	case 1:
72108f90673Sjfb 		break;
72208f90673Sjfb 	default:
72308f90673Sjfb 		/* error */
72408f90673Sjfb 		goto closem;
72508f90673Sjfb 	}
72608f90673Sjfb 
72708f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
72808f90673Sjfb 		rval = D_BINARY;
72908f90673Sjfb 		goto closem;
73008f90673Sjfb 	}
7317f535ec4Sjfb 	if ((prepare(0, f1, stb1.st_size) < 0) ||
7327f535ec4Sjfb 	    (prepare(1, f2, stb2.st_size) < 0)) {
7337f535ec4Sjfb 		goto closem;
7347f535ec4Sjfb 	}
73508f90673Sjfb 	prune();
73608f90673Sjfb 	sort(sfile[0], slen[0]);
73708f90673Sjfb 	sort(sfile[1], slen[1]);
73808f90673Sjfb 
73908f90673Sjfb 	member = (int *)file[1];
74008f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
74118501190Sniallo 	if ((tmp = realloc(member, (slen[1] + 2) * sizeof(int))) == NULL) {
74218501190Sniallo 		free(member);
74318501190Sniallo 		member = NULL;
74418501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize member");
7457f535ec4Sjfb 		goto closem;
74618501190Sniallo 	}
7477f535ec4Sjfb 	member = (int *)tmp;
74808f90673Sjfb 
74908f90673Sjfb 	class = (int *)file[0];
75008f90673Sjfb 	unsort(sfile[0], slen[0], class);
75118501190Sniallo 	if ((tmp = realloc(class, (slen[0] + 2) * sizeof(int))) == NULL) {
75218501190Sniallo 		free(class);
75318501190Sniallo 		class = NULL;
75418501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize class");
7557f535ec4Sjfb 		goto closem;
75618501190Sniallo 	}
7577f535ec4Sjfb 	class = (int *)tmp;
75808f90673Sjfb 
7597f535ec4Sjfb 	if ((klist = malloc((slen[0] + 2) * sizeof(int))) == NULL) {
7607f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate klist");
7617f535ec4Sjfb 		goto closem;
7627f535ec4Sjfb 	}
76308f90673Sjfb 	clen = 0;
76408f90673Sjfb 	clistlen = 100;
7657f535ec4Sjfb 	if ((clist = malloc(clistlen * sizeof(cand))) == NULL) {
7667f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate clist");
7677f535ec4Sjfb 		goto closem;
7687f535ec4Sjfb 	}
769ece76a70Sjoris 
770ece76a70Sjoris 	if ((i = stone(class, slen[0], member, klist)) < 0)
771ece76a70Sjoris 		goto closem;
772ece76a70Sjoris 
77308f90673Sjfb 	free(member);
77408f90673Sjfb 	free(class);
77508f90673Sjfb 
77618501190Sniallo 	if ((tmp = realloc(J, (diff_len[0] + 2) * sizeof(int))) == NULL) {
77718501190Sniallo 		free(J);
77818501190Sniallo 		J = NULL;
77918501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize J");
78018501190Sniallo 		goto closem;
78118501190Sniallo 	}
78218501190Sniallo 	J = (int *)tmp;
78308f90673Sjfb 	unravel(klist[i]);
78408f90673Sjfb 	free(clist);
78508f90673Sjfb 	free(klist);
78608f90673Sjfb 
78718501190Sniallo 	if ((tmp = realloc(ixold, (diff_len[0] + 2) * sizeof(long))) == NULL) {
78818501190Sniallo 		free(ixold);
78918501190Sniallo 		ixold = NULL;
79018501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize ixold");
79118501190Sniallo 		goto closem;
79218501190Sniallo 	}
79318501190Sniallo 	ixold = (long *)tmp;
79418501190Sniallo 	if ((tmp = realloc(ixnew, (diff_len[1] + 2) * sizeof(long))) == NULL) {
79518501190Sniallo 		free(ixnew);
79618501190Sniallo 		ixnew = NULL;
79718501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize ixnew");
79818501190Sniallo 		goto closem;
79918501190Sniallo 	}
80018501190Sniallo 	ixnew = (long *)tmp;
80108f90673Sjfb 	check(f1, f2);
80208f90673Sjfb 	output(file1, f1, file2, f2);
80308f90673Sjfb 
80408f90673Sjfb closem:
80537af5b8bSxsa 	if (anychange == 1) {
80608f90673Sjfb 		if (rval == D_SAME)
80708f90673Sjfb 			rval = D_DIFFER;
80808f90673Sjfb 	}
80908f90673Sjfb 	if (f1 != NULL)
81008f90673Sjfb 		fclose(f1);
81108f90673Sjfb 	if (f2 != NULL)
81208f90673Sjfb 		fclose(f2);
8137f535ec4Sjfb 
81408f90673Sjfb 	return (rval);
81508f90673Sjfb }
81608f90673Sjfb 
81708f90673Sjfb /*
81808f90673Sjfb  * Check to see if the given files differ.
81908f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
82008f90673Sjfb  * XXX - could use code from cmp(1) [faster]
82108f90673Sjfb  */
82208f90673Sjfb static int
82308f90673Sjfb files_differ(FILE *f1, FILE *f2)
82408f90673Sjfb {
82508f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
82608f90673Sjfb 	size_t i, j;
82708f90673Sjfb 
82808f90673Sjfb 	if (stb1.st_size != stb2.st_size)
82908f90673Sjfb 		return (1);
83008f90673Sjfb 	for (;;) {
831f1901a5aSxsa 		i = fread(buf1, (size_t)1, sizeof(buf1), f1);
832f1901a5aSxsa 		j = fread(buf2, (size_t)1, sizeof(buf2), f2);
83308f90673Sjfb 		if (i != j)
83408f90673Sjfb 			return (1);
83537af5b8bSxsa 		if ((i == 0) && (j == 0)) {
83608f90673Sjfb 			if (ferror(f1) || ferror(f2))
83708f90673Sjfb 				return (1);
83808f90673Sjfb 			return (0);
83908f90673Sjfb 		}
84008f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
84108f90673Sjfb 			return (1);
84208f90673Sjfb 	}
84308f90673Sjfb }
84408f90673Sjfb 
8457f535ec4Sjfb static int
84608f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
84708f90673Sjfb {
8487f535ec4Sjfb 	void *tmp;
84908f90673Sjfb 	struct line *p;
85008f90673Sjfb 	int j, h;
85108f90673Sjfb 	size_t sz;
85208f90673Sjfb 
85308f90673Sjfb 	rewind(fd);
85408f90673Sjfb 
855c48da046Sxsa 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
85608f90673Sjfb 	if (sz < 100)
85708f90673Sjfb 		sz = 100;
85808f90673Sjfb 
8597f535ec4Sjfb 	p = (struct line *)malloc((sz + 3) * sizeof(struct line));
8607f535ec4Sjfb 	if (p == NULL) {
8617f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to prepare line array");
8627f535ec4Sjfb 		return (-1);
8637f535ec4Sjfb 	}
86408f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
86508f90673Sjfb 		if (j == (int)sz) {
86608f90673Sjfb 			sz = sz * 3 / 2;
8677f535ec4Sjfb 			tmp = realloc(p, (sz + 3) * sizeof(struct line));
8687f535ec4Sjfb 			if (tmp == NULL) {
8697f535ec4Sjfb 				cvs_log(LP_ERRNO, "failed to grow line array");
8707f535ec4Sjfb 				free(p);
8717f535ec4Sjfb 				return (-1);
8727f535ec4Sjfb 			}
8737f535ec4Sjfb 			p = (struct line *)tmp;
87408f90673Sjfb 		}
87508f90673Sjfb 		p[++j].value = h;
87608f90673Sjfb 	}
877e4276007Sjfb 	diff_len[i] = j;
87808f90673Sjfb 	file[i] = p;
8797f535ec4Sjfb 
8807f535ec4Sjfb 	return (0);
88108f90673Sjfb }
88208f90673Sjfb 
88308f90673Sjfb static void
88408f90673Sjfb prune(void)
88508f90673Sjfb {
88608f90673Sjfb 	int i, j;
88708f90673Sjfb 
888e4276007Sjfb 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
88908f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
89008f90673Sjfb 	    pref++)
89108f90673Sjfb 		;
892e4276007Sjfb 	for (suff = 0;
893e4276007Sjfb 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
894e4276007Sjfb 	    (file[0][diff_len[0] - suff].value ==
895e4276007Sjfb 	    file[1][diff_len[1] - suff].value);
89608f90673Sjfb 	    suff++)
89708f90673Sjfb 		;
89808f90673Sjfb 	for (j = 0; j < 2; j++) {
89908f90673Sjfb 		sfile[j] = file[j] + pref;
900e4276007Sjfb 		slen[j] = diff_len[j] - pref - suff;
90108f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
90208f90673Sjfb 			sfile[j][i].serial = i;
90308f90673Sjfb 	}
90408f90673Sjfb }
90508f90673Sjfb 
90608f90673Sjfb static void
90708f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
90808f90673Sjfb {
90908f90673Sjfb 	int i, j;
91008f90673Sjfb 
91108f90673Sjfb 	i = j = 1;
91208f90673Sjfb 	while (i <= n && j <= m) {
91308f90673Sjfb 		if (a[i].value < b[j].value)
91408f90673Sjfb 			a[i++].value = 0;
91508f90673Sjfb 		else if (a[i].value == b[j].value)
91608f90673Sjfb 			a[i++].value = j;
91708f90673Sjfb 		else
91808f90673Sjfb 			j++;
91908f90673Sjfb 	}
92008f90673Sjfb 	while (i <= n)
92108f90673Sjfb 		a[i++].value = 0;
92208f90673Sjfb 	b[m + 1].value = 0;
92308f90673Sjfb 	j = 0;
92408f90673Sjfb 	while (++j <= m) {
92508f90673Sjfb 		c[j] = -b[j].serial;
92608f90673Sjfb 		while (b[j + 1].value == b[j].value) {
92708f90673Sjfb 			j++;
92808f90673Sjfb 			c[j] = b[j].serial;
92908f90673Sjfb 		}
93008f90673Sjfb 	}
93108f90673Sjfb 	c[j] = -1;
93208f90673Sjfb }
93308f90673Sjfb 
93408f90673Sjfb /* Code taken from ping.c */
93508f90673Sjfb static int
93608f90673Sjfb isqrt(int n)
93708f90673Sjfb {
93808f90673Sjfb 	int y, x = 1;
93908f90673Sjfb 
94008f90673Sjfb 	if (n == 0)
94108f90673Sjfb 		return (0);
94208f90673Sjfb 
94308f90673Sjfb 	do { /* newton was a stinker */
94408f90673Sjfb 		y = x;
94508f90673Sjfb 		x = n / x;
94608f90673Sjfb 		x += y;
94708f90673Sjfb 		x /= 2;
94808f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
94908f90673Sjfb 
95008f90673Sjfb 	return (x);
95108f90673Sjfb }
95208f90673Sjfb 
95308f90673Sjfb static int
95408f90673Sjfb stone(int *a, int n, int *b, int *c)
95508f90673Sjfb {
956ece76a70Sjoris 	int ret;
95708f90673Sjfb 	int i, k, y, j, l;
95808f90673Sjfb 	int oldc, tc, oldl;
95908f90673Sjfb 	u_int numtries;
96008f90673Sjfb 
961cc649edbSjfb 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
962cc649edbSjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
96308f90673Sjfb 
96408f90673Sjfb 	k = 0;
965ece76a70Sjoris 	if ((ret = newcand(0, 0, 0)) < 0)
966ece76a70Sjoris 		return (-1);
967ece76a70Sjoris 	c[0] = ret;
96808f90673Sjfb 	for (i = 1; i <= n; i++) {
96908f90673Sjfb 		j = a[i];
97008f90673Sjfb 		if (j == 0)
97108f90673Sjfb 			continue;
97208f90673Sjfb 		y = -b[j];
97308f90673Sjfb 		oldl = 0;
97408f90673Sjfb 		oldc = c[0];
97508f90673Sjfb 		numtries = 0;
97608f90673Sjfb 		do {
97708f90673Sjfb 			if (y <= clist[oldc].y)
97808f90673Sjfb 				continue;
97908f90673Sjfb 			l = search(c, k, y);
98008f90673Sjfb 			if (l != oldl + 1)
98108f90673Sjfb 				oldc = c[l - 1];
98208f90673Sjfb 			if (l <= k) {
98308f90673Sjfb 				if (clist[c[l]].y <= y)
98408f90673Sjfb 					continue;
98508f90673Sjfb 				tc = c[l];
986ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
987ece76a70Sjoris 					return (-1);
988ece76a70Sjoris 				c[l] = ret;
98908f90673Sjfb 				oldc = tc;
99008f90673Sjfb 				oldl = l;
99108f90673Sjfb 				numtries++;
99208f90673Sjfb 			} else {
993ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
994ece76a70Sjoris 					return (-1);
995ece76a70Sjoris 				c[l] = ret;
99608f90673Sjfb 				k++;
99708f90673Sjfb 				break;
99808f90673Sjfb 			}
99908f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
100008f90673Sjfb 	}
100108f90673Sjfb 	return (k);
100208f90673Sjfb }
100308f90673Sjfb 
100408f90673Sjfb static int
100508f90673Sjfb newcand(int x, int y, int pred)
100608f90673Sjfb {
100718501190Sniallo 	struct cand *q, *tmp;
100818501190Sniallo 	int newclistlen;
100908f90673Sjfb 
101008f90673Sjfb 	if (clen == clistlen) {
101118501190Sniallo 		newclistlen = clistlen * 11 / 10;
101218501190Sniallo 		tmp = realloc(clist, newclistlen * sizeof(cand));
101318501190Sniallo 		if (tmp == NULL) {
10147f535ec4Sjfb 			cvs_log(LP_ERRNO, "failed to resize clist");
10157f535ec4Sjfb 			return (-1);
10167f535ec4Sjfb 		}
101718501190Sniallo 		clist = tmp;
101818501190Sniallo 		clistlen = newclistlen;
101908f90673Sjfb 	}
102008f90673Sjfb 	q = clist + clen;
102108f90673Sjfb 	q->x = x;
102208f90673Sjfb 	q->y = y;
102308f90673Sjfb 	q->pred = pred;
102408f90673Sjfb 	return (clen++);
102508f90673Sjfb }
102608f90673Sjfb 
102708f90673Sjfb static int
102808f90673Sjfb search(int *c, int k, int y)
102908f90673Sjfb {
103008f90673Sjfb 	int i, j, l, t;
103108f90673Sjfb 
103208f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
103308f90673Sjfb 		return (k + 1);
103408f90673Sjfb 	i = 0;
103508f90673Sjfb 	j = k + 1;
103608f90673Sjfb 	while (1) {
103708f90673Sjfb 		l = i + j;
103808f90673Sjfb 		if ((l >>= 1) <= i)
103908f90673Sjfb 			break;
104008f90673Sjfb 		t = clist[c[l]].y;
104108f90673Sjfb 		if (t > y)
104208f90673Sjfb 			j = l;
104308f90673Sjfb 		else if (t < y)
104408f90673Sjfb 			i = l;
104508f90673Sjfb 		else
104608f90673Sjfb 			return (l);
104708f90673Sjfb 	}
104808f90673Sjfb 	return (l + 1);
104908f90673Sjfb }
105008f90673Sjfb 
105108f90673Sjfb static void
105208f90673Sjfb unravel(int p)
105308f90673Sjfb {
105408f90673Sjfb 	struct cand *q;
105508f90673Sjfb 	int i;
105608f90673Sjfb 
1057e4276007Sjfb 	for (i = 0; i <= diff_len[0]; i++)
105808f90673Sjfb 		J[i] = i <= pref ? i :
1059e4276007Sjfb 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
106008f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
106108f90673Sjfb 		J[q->x + pref] = q->y + pref;
106208f90673Sjfb }
106308f90673Sjfb 
106408f90673Sjfb /*
106508f90673Sjfb  * Check does double duty:
106608f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
106708f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
106808f90673Sjfb  *  2.  collect random access indexes to the two files
106908f90673Sjfb  */
107008f90673Sjfb static void
107108f90673Sjfb check(FILE *f1, FILE *f2)
107208f90673Sjfb {
107308f90673Sjfb 	int i, j, jackpot, c, d;
107408f90673Sjfb 	long ctold, ctnew;
107508f90673Sjfb 
107608f90673Sjfb 	rewind(f1);
107708f90673Sjfb 	rewind(f2);
107808f90673Sjfb 	j = 1;
107908f90673Sjfb 	ixold[0] = ixnew[0] = 0;
108008f90673Sjfb 	jackpot = 0;
108108f90673Sjfb 	ctold = ctnew = 0;
1082e4276007Sjfb 	for (i = 1; i <= diff_len[0]; i++) {
108308f90673Sjfb 		if (J[i] == 0) {
108408f90673Sjfb 			ixold[i] = ctold += skipline(f1);
108508f90673Sjfb 			continue;
108608f90673Sjfb 		}
108708f90673Sjfb 		while (j < J[i]) {
108808f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
108908f90673Sjfb 			j++;
109008f90673Sjfb 		}
109148dc77e6Sxsa 		if ((bflag == 1)|| (wflag == 1) || (iflag == 1)) {
109208f90673Sjfb 			for (;;) {
109308f90673Sjfb 				c = getc(f1);
109408f90673Sjfb 				d = getc(f2);
109508f90673Sjfb 				/*
109608f90673Sjfb 				 * GNU diff ignores a missing newline
109708f90673Sjfb 				 * in one file if bflag || wflag.
109808f90673Sjfb 				 */
109948dc77e6Sxsa 				if (((bflag == 1) || (wflag == 1)) &&
110008f90673Sjfb 				    ((c == EOF && d == '\n') ||
110108f90673Sjfb 				    (c == '\n' && d == EOF))) {
110208f90673Sjfb 					break;
110308f90673Sjfb 				}
110408f90673Sjfb 				ctold++;
110508f90673Sjfb 				ctnew++;
110648dc77e6Sxsa 				if ((bflag == 1) && isspace(c) && isspace(d)) {
110708f90673Sjfb 					do {
110808f90673Sjfb 						if (c == '\n')
110908f90673Sjfb 							break;
111008f90673Sjfb 						ctold++;
111108f90673Sjfb 					} while (isspace(c = getc(f1)));
111208f90673Sjfb 					do {
111308f90673Sjfb 						if (d == '\n')
111408f90673Sjfb 							break;
111508f90673Sjfb 						ctnew++;
111608f90673Sjfb 					} while (isspace(d = getc(f2)));
111748dc77e6Sxsa 				} else if (wflag == 1) {
111808f90673Sjfb 					while (isspace(c) && c != '\n') {
111908f90673Sjfb 						c = getc(f1);
112008f90673Sjfb 						ctold++;
112108f90673Sjfb 					}
112208f90673Sjfb 					while (isspace(d) && d != '\n') {
112308f90673Sjfb 						d = getc(f2);
112408f90673Sjfb 						ctnew++;
112508f90673Sjfb 					}
112608f90673Sjfb 				}
112708f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
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 		} else {
114008f90673Sjfb 			for (;;) {
114108f90673Sjfb 				ctold++;
114208f90673Sjfb 				ctnew++;
114308f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
114408f90673Sjfb 					/* jackpot++; */
114508f90673Sjfb 					J[i] = 0;
114637af5b8bSxsa 					if ((c != '\n') && (c != EOF))
114708f90673Sjfb 						ctold += skipline(f1);
114837af5b8bSxsa 					if ((d != '\n') && (c != EOF))
114908f90673Sjfb 						ctnew += skipline(f2);
115008f90673Sjfb 					break;
115108f90673Sjfb 				}
115237af5b8bSxsa 				if ((c == '\n') || (c == EOF))
115308f90673Sjfb 					break;
115408f90673Sjfb 			}
115508f90673Sjfb 		}
115608f90673Sjfb 		ixold[i] = ctold;
115708f90673Sjfb 		ixnew[j] = ctnew;
115808f90673Sjfb 		j++;
115908f90673Sjfb 	}
1160e4276007Sjfb 	for (; j <= diff_len[1]; j++)
116108f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
116208f90673Sjfb 	/*
116337af5b8bSxsa 	 * if (jackpot != 0)
11645ac8b1e7Sjoris 	 *	cvs_printf("jackpot\n");
116508f90673Sjfb 	 */
116608f90673Sjfb }
116708f90673Sjfb 
116808f90673Sjfb /* shellsort CACM #201 */
116908f90673Sjfb static void
117008f90673Sjfb sort(struct line *a, int n)
117108f90673Sjfb {
117208f90673Sjfb 	struct line *ai, *aim, w;
117308f90673Sjfb 	int j, m = 0, k;
117408f90673Sjfb 
117508f90673Sjfb 	if (n == 0)
117608f90673Sjfb 		return;
117708f90673Sjfb 	for (j = 1; j <= n; j *= 2)
117808f90673Sjfb 		m = 2 * j - 1;
117908f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
118008f90673Sjfb 		k = n - m;
118108f90673Sjfb 		for (j = 1; j <= k; j++) {
118208f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
118308f90673Sjfb 				aim = &ai[m];
118408f90673Sjfb 				if (aim < ai)
118508f90673Sjfb 					break;	/* wraparound */
118608f90673Sjfb 				if (aim->value > ai[0].value ||
118708f90673Sjfb 				    (aim->value == ai[0].value &&
118808f90673Sjfb 					aim->serial > ai[0].serial))
118908f90673Sjfb 					break;
119008f90673Sjfb 				w.value = ai[0].value;
119108f90673Sjfb 				ai[0].value = aim->value;
119208f90673Sjfb 				aim->value = w.value;
119308f90673Sjfb 				w.serial = ai[0].serial;
119408f90673Sjfb 				ai[0].serial = aim->serial;
119508f90673Sjfb 				aim->serial = w.serial;
119608f90673Sjfb 			}
119708f90673Sjfb 		}
119808f90673Sjfb 	}
119908f90673Sjfb }
120008f90673Sjfb 
120108f90673Sjfb static void
120208f90673Sjfb unsort(struct line *f, int l, int *b)
120308f90673Sjfb {
120408f90673Sjfb 	int *a, i;
120508f90673Sjfb 
12067f535ec4Sjfb 	if ((a = (int *)malloc((l + 1) * sizeof(int))) == NULL) {
12077f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate sort array");
12087f535ec4Sjfb 		return;
12097f535ec4Sjfb 	}
121008f90673Sjfb 	for (i = 1; i <= l; i++)
121108f90673Sjfb 		a[f[i].serial] = f[i].value;
121208f90673Sjfb 	for (i = 1; i <= l; i++)
121308f90673Sjfb 		b[i] = a[i];
121408f90673Sjfb 	free(a);
121508f90673Sjfb }
121608f90673Sjfb 
121708f90673Sjfb static int
121808f90673Sjfb skipline(FILE *f)
121908f90673Sjfb {
122008f90673Sjfb 	int i, c;
122108f90673Sjfb 
122208f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
122308f90673Sjfb 		continue;
122408f90673Sjfb 	return (i);
122508f90673Sjfb }
122608f90673Sjfb 
122708f90673Sjfb static void
122808f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2)
122908f90673Sjfb {
123008f90673Sjfb 	int m, i0, i1, j0, j1;
123108f90673Sjfb 
123208f90673Sjfb 	rewind(f1);
123308f90673Sjfb 	rewind(f2);
1234e4276007Sjfb 	m = diff_len[0];
123508f90673Sjfb 	J[0] = 0;
1236e4276007Sjfb 	J[m + 1] = diff_len[1] + 1;
123708f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
123808f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
123908f90673Sjfb 			i0++;
124008f90673Sjfb 		j0 = J[i0 - 1] + 1;
124108f90673Sjfb 		i1 = i0 - 1;
124208f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
124308f90673Sjfb 			i1++;
124408f90673Sjfb 		j1 = J[i1 + 1] - 1;
124508f90673Sjfb 		J[i1] = j1;
124608f90673Sjfb 		change(file1, f1, file2, f2, i0, i1, j0, j1);
124708f90673Sjfb 	}
124808f90673Sjfb 	if (m == 0)
1249e4276007Sjfb 		change(file1, f1, file2, f2, 1, 0, 1, diff_len[1]);
1250f9b67873Sniallo 	if (diff_format == D_IFDEF) {
125108f90673Sjfb 		for (;;) {
125208f90673Sjfb #define	c i0
125308f90673Sjfb 			if ((c = getc(f1)) == EOF)
125408f90673Sjfb 				return;
1255f9b67873Sniallo 			diff_output("%c", c);
125608f90673Sjfb 		}
125708f90673Sjfb #undef c
125808f90673Sjfb 	}
125908f90673Sjfb 	if (anychange != 0) {
1260f9b67873Sniallo 		if (diff_format == D_CONTEXT)
126108f90673Sjfb 			dump_context_vec(f1, f2);
1262f9b67873Sniallo 		else if (diff_format == D_UNIFIED)
126308f90673Sjfb 			dump_unified_vec(f1, f2);
126408f90673Sjfb 	}
126508f90673Sjfb }
126608f90673Sjfb 
126708f90673Sjfb static __inline void
126808f90673Sjfb range(int a, int b, char *separator)
126908f90673Sjfb {
1270f9b67873Sniallo 	diff_output("%d", a > b ? b : a);
127108f90673Sjfb 	if (a < b)
1272f9b67873Sniallo 		diff_output("%s%d", separator, b);
127308f90673Sjfb }
127408f90673Sjfb 
127508f90673Sjfb static __inline void
127608f90673Sjfb uni_range(int a, int b)
127708f90673Sjfb {
127808f90673Sjfb 	if (a < b)
1279f9b67873Sniallo 		diff_output("%d,%d", a, b - a + 1);
128008f90673Sjfb 	else if (a == b)
1281f9b67873Sniallo 		diff_output("%d", b);
128208f90673Sjfb 	else
1283f9b67873Sniallo 		diff_output("%d,0", b);
128408f90673Sjfb }
128508f90673Sjfb 
128608f90673Sjfb static char *
12872a0de57dSjfb preadline(int fd, size_t rlen, off_t off)
128808f90673Sjfb {
128908f90673Sjfb 	char *line;
129008f90673Sjfb 	ssize_t nr;
129108f90673Sjfb 
12922a0de57dSjfb 	line = malloc(rlen + 1);
129308f90673Sjfb 	if (line == NULL) {
129408f90673Sjfb 		cvs_log(LP_ERRNO, "failed to allocate line");
129508f90673Sjfb 		return (NULL);
129608f90673Sjfb 	}
12972a0de57dSjfb 	if ((nr = pread(fd, line, rlen, off)) < 0) {
129808f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
129908f90673Sjfb 		return (NULL);
130008f90673Sjfb 	}
130108f90673Sjfb 	line[nr] = '\0';
130208f90673Sjfb 	return (line);
130308f90673Sjfb }
130408f90673Sjfb 
130508f90673Sjfb static int
130608f90673Sjfb ignoreline(char *line)
130708f90673Sjfb {
130808f90673Sjfb 	int ret;
130908f90673Sjfb 
1310f1901a5aSxsa 	ret = regexec(&ignore_re, line, (size_t)0, NULL, 0);
131108f90673Sjfb 	free(line);
131208f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
131308f90673Sjfb }
131408f90673Sjfb 
131508f90673Sjfb /*
131608f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
131708f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
131808f90673Sjfb  * are no lines in the from file involved and this means that there were
131908f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
132008f90673Sjfb  * lines missing from the to file.
132108f90673Sjfb  */
132208f90673Sjfb static void
132308f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2,
132408f90673Sjfb 	int a, int b, int c, int d)
132508f90673Sjfb {
132608f90673Sjfb 	static size_t max_context = 64;
132708f90673Sjfb 	int i;
132808f90673Sjfb 
1329f9b67873Sniallo 	if (diff_format != D_IFDEF && a > b && c > d)
133008f90673Sjfb 		return;
133108f90673Sjfb 	if (ignore_pats != NULL) {
133208f90673Sjfb 		char *line;
133308f90673Sjfb 		/*
133408f90673Sjfb 		 * All lines in the change, insert, or delete must
133508f90673Sjfb 		 * match an ignore pattern for the change to be
133608f90673Sjfb 		 * ignored.
133708f90673Sjfb 		 */
133808f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
133908f90673Sjfb 			for (i = a; i <= b; i++) {
134008f90673Sjfb 				line = preadline(fileno(f1),
134108f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
134208f90673Sjfb 				if (!ignoreline(line))
134308f90673Sjfb 					goto proceed;
134408f90673Sjfb 			}
134508f90673Sjfb 		}
134637af5b8bSxsa 		if ((a > b) || (c <= d)) {	/* Changes and inserts. */
134708f90673Sjfb 			for (i = c; i <= d; i++) {
134808f90673Sjfb 				line = preadline(fileno(f2),
134908f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
135008f90673Sjfb 				if (!ignoreline(line))
135108f90673Sjfb 					goto proceed;
135208f90673Sjfb 			}
135308f90673Sjfb 		}
135408f90673Sjfb 		return;
135508f90673Sjfb 	}
135608f90673Sjfb proceed:
1357f9b67873Sniallo 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
135808f90673Sjfb 		/*
135908f90673Sjfb 		 * Allocate change records as needed.
136008f90673Sjfb 		 */
136108f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
136218501190Sniallo 			struct context_vec *tmp;
136308f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
136408f90673Sjfb 			max_context <<= 1;
13654c06e5f6Sreyk 			if ((tmp = realloc(context_vec_start, max_context *
13664c06e5f6Sreyk 			    sizeof(struct context_vec))) == NULL) {
136718501190Sniallo 				free(context_vec_start);
136818501190Sniallo 				context_vec_start = NULL;
136918501190Sniallo 				cvs_log(LP_ERRNO,
137018501190Sniallo 				    "failed to resize context_vec_start");
137118501190Sniallo 				return;
137218501190Sniallo 			}
137318501190Sniallo 			context_vec_start = tmp;
137408f90673Sjfb 			context_vec_end = context_vec_start + max_context;
137508f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
137608f90673Sjfb 		}
137708f90673Sjfb 		if (anychange == 0) {
137808f90673Sjfb 			/*
137908f90673Sjfb 			 * Print the context/unidiff header first time through.
138008f90673Sjfb 			 */
1381f9b67873Sniallo 			diff_output("%s %s	%s",
1382f9b67873Sniallo 			    diff_format == D_CONTEXT ? "***" : "---", diff_file,
138308f90673Sjfb 			    ctime(&stb1.st_mtime));
1384f9b67873Sniallo 			diff_output("%s %s	%s",
1385f9b67873Sniallo 			    diff_format == D_CONTEXT ? "---" : "+++", diff_file,
138608f90673Sjfb 			    ctime(&stb2.st_mtime));
138708f90673Sjfb 			anychange = 1;
138808f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
138908f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
139008f90673Sjfb 			/*
139108f90673Sjfb 			 * If this change is more than 'context' lines from the
139208f90673Sjfb 			 * previous change, dump the record and reset it.
139308f90673Sjfb 			 */
1394f9b67873Sniallo 			if (diff_format == D_CONTEXT)
139508f90673Sjfb 				dump_context_vec(f1, f2);
139608f90673Sjfb 			else
139708f90673Sjfb 				dump_unified_vec(f1, f2);
139808f90673Sjfb 		}
139908f90673Sjfb 		context_vec_ptr++;
140008f90673Sjfb 		context_vec_ptr->a = a;
140108f90673Sjfb 		context_vec_ptr->b = b;
140208f90673Sjfb 		context_vec_ptr->c = c;
140308f90673Sjfb 		context_vec_ptr->d = d;
140408f90673Sjfb 		return;
140508f90673Sjfb 	}
140608f90673Sjfb 	if (anychange == 0)
140708f90673Sjfb 		anychange = 1;
1408f9b67873Sniallo 	switch (diff_format) {
140908f90673Sjfb 	case D_BRIEF:
141008f90673Sjfb 		return;
141108f90673Sjfb 	case D_NORMAL:
141208f90673Sjfb 		range(a, b, ",");
1413f9b67873Sniallo 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1414f9b67873Sniallo 		if (diff_format == D_NORMAL)
141508f90673Sjfb 			range(c, d, ",");
1416f9b67873Sniallo 		diff_output("\n");
141708f90673Sjfb 		break;
1418394180a4Sjfb 	case D_RCSDIFF:
1419394180a4Sjfb 		if (a > b)
1420f9b67873Sniallo 			diff_output("a%d %d\n", b, d - c + 1);
1421394180a4Sjfb 		else {
1422f9b67873Sniallo 			diff_output("d%d %d\n", a, b - a + 1);
1423394180a4Sjfb 
1424394180a4Sjfb 			if (!(c > d))	/* add changed lines */
1425f9b67873Sniallo 				diff_output("a%d %d\n", b, d - c + 1);
1426394180a4Sjfb 		}
1427394180a4Sjfb 		break;
142808f90673Sjfb 	}
1429f9b67873Sniallo 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
143008f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
1431f9b67873Sniallo 		if (a <= b && c <= d && diff_format == D_NORMAL)
1432206543eeSjoris 			diff_output("---\n");
143308f90673Sjfb 	}
1434f9b67873Sniallo 	i = fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0);
143508f90673Sjfb 	if (inifdef) {
1436f9b67873Sniallo 		diff_output("#endif /* %s */\n", ifdefname);
143708f90673Sjfb 		inifdef = 0;
143808f90673Sjfb 	}
143908f90673Sjfb }
144008f90673Sjfb 
144108f90673Sjfb static int
144208f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
144308f90673Sjfb {
144408f90673Sjfb 	int i, j, c, lastc, col, nc;
144508f90673Sjfb 
144608f90673Sjfb 	/*
144708f90673Sjfb 	 * When doing #ifdef's, copy down to current line
144808f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
144908f90673Sjfb 	 */
1450f9b67873Sniallo 	if (diff_format == D_IFDEF && oldfile) {
145108f90673Sjfb 		long curpos = ftell(lb);
145208f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
145308f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
145408f90673Sjfb 		for (i = 0; i < nc; i++)
1455f9b67873Sniallo 			diff_output("%c", getc(lb));
145608f90673Sjfb 	}
145708f90673Sjfb 	if (a > b)
145808f90673Sjfb 		return (0);
1459f9b67873Sniallo 	if (diff_format == D_IFDEF) {
146008f90673Sjfb 		if (inifdef) {
1461f9b67873Sniallo 			diff_output("#else /* %s%s */\n",
146208f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
146308f90673Sjfb 		} else {
146408f90673Sjfb 			if (oldfile)
1465f9b67873Sniallo 				diff_output("#ifndef %s\n", ifdefname);
146608f90673Sjfb 			else
1467f9b67873Sniallo 				diff_output("#ifdef %s\n", ifdefname);
146808f90673Sjfb 		}
146908f90673Sjfb 		inifdef = 1 + oldfile;
147008f90673Sjfb 	}
147108f90673Sjfb 	for (i = a; i <= b; i++) {
147208f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
147308f90673Sjfb 		nc = f[i] - f[i - 1];
1474f9b67873Sniallo 		if (diff_format != D_IFDEF && ch != '\0') {
1475f9b67873Sniallo 			diff_output("%c", ch);
147648dc77e6Sxsa 			if ((Tflag == 1 ) && (diff_format == D_NORMAL ||
14779c5161e4Sjoris 			    diff_format == D_CONTEXT ||
14789c5161e4Sjoris 			    diff_format == D_UNIFIED))
1479f9b67873Sniallo 				diff_output("\t");
1480f9b67873Sniallo 			else if (diff_format != D_UNIFIED)
1481f9b67873Sniallo 				diff_output(" ");
148208f90673Sjfb 		}
148308f90673Sjfb 		col = 0;
148408f90673Sjfb 		for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
148508f90673Sjfb 			if ((c = getc(lb)) == EOF) {
1486f9b67873Sniallo 				if (diff_format == D_RCSDIFF)
1487394180a4Sjfb 					warnx("No newline at end of file");
1488394180a4Sjfb 				else
14899c5161e4Sjoris 					diff_output("\n\\ No newline at end of "
14909c5161e4Sjoris 					    "file");
149108f90673Sjfb 				return (0);
149208f90673Sjfb 			}
149348dc77e6Sxsa 			if ((c == '\t') && (tflag == 1)) {
149408f90673Sjfb 				do {
1495f9b67873Sniallo 					diff_output(" ");
149608f90673Sjfb 				} while (++col & 7);
149708f90673Sjfb 			} else {
1498f9b67873Sniallo 				diff_output("%c", c);
149908f90673Sjfb 				col++;
150008f90673Sjfb 			}
150108f90673Sjfb 		}
150208f90673Sjfb 	}
150308f90673Sjfb 	return (0);
150408f90673Sjfb }
150508f90673Sjfb 
150608f90673Sjfb /*
150708f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
150808f90673Sjfb  */
150908f90673Sjfb static int
151008f90673Sjfb readhash(FILE *f)
151108f90673Sjfb {
151208f90673Sjfb 	int i, t, space;
151308f90673Sjfb 	int sum;
151408f90673Sjfb 
151508f90673Sjfb 	sum = 1;
151608f90673Sjfb 	space = 0;
151748dc77e6Sxsa 	if ((bflag != 1) && (wflag != 1)) {
151848dc77e6Sxsa 		if (iflag == 1)
151908f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
152008f90673Sjfb 				if (t == EOF) {
152108f90673Sjfb 					if (i == 0)
152208f90673Sjfb 						return (0);
152308f90673Sjfb 					break;
152408f90673Sjfb 				}
152508f90673Sjfb 				sum = sum * 127 + chrtran[t];
152608f90673Sjfb 			}
152708f90673Sjfb 		else
152808f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
152908f90673Sjfb 				if (t == EOF) {
153008f90673Sjfb 					if (i == 0)
153108f90673Sjfb 						return (0);
153208f90673Sjfb 					break;
153308f90673Sjfb 				}
153408f90673Sjfb 				sum = sum * 127 + t;
153508f90673Sjfb 			}
153608f90673Sjfb 	} else {
153708f90673Sjfb 		for (i = 0;;) {
153808f90673Sjfb 			switch (t = getc(f)) {
153908f90673Sjfb 			case '\t':
154008f90673Sjfb 			case ' ':
154108f90673Sjfb 				space++;
154208f90673Sjfb 				continue;
154308f90673Sjfb 			default:
154448dc77e6Sxsa 				if ((space != 0) && (wflag != 1)) {
154508f90673Sjfb 					i++;
154608f90673Sjfb 					space = 0;
154708f90673Sjfb 				}
154808f90673Sjfb 				sum = sum * 127 + chrtran[t];
154908f90673Sjfb 				i++;
155008f90673Sjfb 				continue;
155108f90673Sjfb 			case EOF:
155208f90673Sjfb 				if (i == 0)
155308f90673Sjfb 					return (0);
155408f90673Sjfb 				/* FALLTHROUGH */
155508f90673Sjfb 			case '\n':
155608f90673Sjfb 				break;
155708f90673Sjfb 			}
155808f90673Sjfb 			break;
155908f90673Sjfb 		}
156008f90673Sjfb 	}
156108f90673Sjfb 	/*
156208f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
156308f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
156408f90673Sjfb 	 */
156508f90673Sjfb 	return (sum == 0 ? 1 : sum);
156608f90673Sjfb }
156708f90673Sjfb 
156808f90673Sjfb static int
156908f90673Sjfb asciifile(FILE *f)
157008f90673Sjfb {
157108f90673Sjfb 	char buf[BUFSIZ];
157208f90673Sjfb 	int i, cnt;
157308f90673Sjfb 
157448dc77e6Sxsa 	if ((aflag == 1) || (f == NULL))
157508f90673Sjfb 		return (1);
157608f90673Sjfb 
157708f90673Sjfb 	rewind(f);
1578f1901a5aSxsa 	cnt = fread(buf, (size_t)1, sizeof(buf), f);
157908f90673Sjfb 	for (i = 0; i < cnt; i++)
158008f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
158108f90673Sjfb 			return (0);
158208f90673Sjfb 	return (1);
158308f90673Sjfb }
158408f90673Sjfb 
15855e78344dSjfb static char*
15865e78344dSjfb match_function(const long *f, int pos, FILE *fp)
15875e78344dSjfb {
15885e78344dSjfb 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
15895e78344dSjfb 	size_t nc;
15905e78344dSjfb 	int last = lastline;
15915e78344dSjfb 	char *p;
15925e78344dSjfb 
15935e78344dSjfb 	lastline = pos;
15945e78344dSjfb 	while (pos > last) {
15955e78344dSjfb 		fseek(fp, f[pos - 1], SEEK_SET);
15965e78344dSjfb 		nc = f[pos] - f[pos - 1];
15975e78344dSjfb 		if (nc >= sizeof(buf))
15985e78344dSjfb 			nc = sizeof(buf) - 1;
1599f1901a5aSxsa 		nc = fread(buf, (size_t)1, nc, fp);
16005e78344dSjfb 		if (nc > 0) {
16015e78344dSjfb 			buf[nc] = '\0';
1602634926d6Sniallo 			p = strchr((const char *)buf, '\n');
16035e78344dSjfb 			if (p != NULL)
16045e78344dSjfb 				*p = '\0';
16055e78344dSjfb 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
16064c06e5f6Sreyk 				strlcpy(lastbuf, (const char *)buf,
16074c06e5f6Sreyk 				    sizeof lastbuf);
16085e78344dSjfb 				lastmatchline = pos;
16095e78344dSjfb 				return lastbuf;
16105e78344dSjfb 			}
16115e78344dSjfb 		}
16125e78344dSjfb 		pos--;
16135e78344dSjfb 	}
16145e78344dSjfb 	return (lastmatchline > 0) ? lastbuf : NULL;
16155e78344dSjfb }
16165e78344dSjfb 
161708f90673Sjfb 
161808f90673Sjfb /* dump accumulated "context" diff changes */
161908f90673Sjfb static void
162008f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
162108f90673Sjfb {
162208f90673Sjfb 	struct context_vec *cvp = context_vec_start;
162308f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
162408f90673Sjfb 	int a, b, c, d;
16255e78344dSjfb 	char ch, *f;
162608f90673Sjfb 
162708f90673Sjfb 	if (context_vec_start > context_vec_ptr)
162808f90673Sjfb 		return;
162908f90673Sjfb 
163008f90673Sjfb 	b = d = 0;		/* gcc */
1631dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1632e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1633dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1634e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
163508f90673Sjfb 
1636f9b67873Sniallo 	diff_output("***************");
163748dc77e6Sxsa 	if (pflag == 1) {
16385e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
16395e78344dSjfb 		if (f != NULL) {
1640f9b67873Sniallo 			diff_output(" ");
1641f9b67873Sniallo 			diff_output("%s", f);
16425e78344dSjfb 		}
16435e78344dSjfb 	}
1644f9b67873Sniallo 	diff_output("\n*** ");
164508f90673Sjfb 	range(lowa, upb, ",");
1646f9b67873Sniallo 	diff_output(" ****\n");
164708f90673Sjfb 
164808f90673Sjfb 	/*
164908f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
165008f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
165108f90673Sjfb 	 * the "old" lines as context in the "new" list).
165208f90673Sjfb 	 */
165308f90673Sjfb 	do_output = 0;
165408f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
165508f90673Sjfb 		if (cvp->a <= cvp->b) {
165608f90673Sjfb 			cvp = context_vec_start;
165708f90673Sjfb 			do_output++;
165808f90673Sjfb 			break;
165908f90673Sjfb 		}
166037af5b8bSxsa 	if (do_output != 0) {
166108f90673Sjfb 		while (cvp <= context_vec_ptr) {
166208f90673Sjfb 			a = cvp->a;
166308f90673Sjfb 			b = cvp->b;
166408f90673Sjfb 			c = cvp->c;
166508f90673Sjfb 			d = cvp->d;
166608f90673Sjfb 
166708f90673Sjfb 			if (a <= b && c <= d)
166808f90673Sjfb 				ch = 'c';
166908f90673Sjfb 			else
167008f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
167108f90673Sjfb 
167208f90673Sjfb 			if (ch == 'a')
167308f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
167408f90673Sjfb 			else {
167508f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
167608f90673Sjfb 				fetch(ixold, a, b, f1,
167708f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
167808f90673Sjfb 			}
167908f90673Sjfb 			lowa = b + 1;
168008f90673Sjfb 			cvp++;
168108f90673Sjfb 		}
168208f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
168308f90673Sjfb 	}
168408f90673Sjfb 	/* output changes to the "new" file */
1685f9b67873Sniallo 	diff_output("--- ");
168608f90673Sjfb 	range(lowc, upd, ",");
1687f9b67873Sniallo 	diff_output(" ----\n");
168808f90673Sjfb 
168908f90673Sjfb 	do_output = 0;
169008f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
169108f90673Sjfb 		if (cvp->c <= cvp->d) {
169208f90673Sjfb 			cvp = context_vec_start;
169308f90673Sjfb 			do_output++;
169408f90673Sjfb 			break;
169508f90673Sjfb 		}
169637af5b8bSxsa 	if (do_output != 0) {
169708f90673Sjfb 		while (cvp <= context_vec_ptr) {
169808f90673Sjfb 			a = cvp->a;
169908f90673Sjfb 			b = cvp->b;
170008f90673Sjfb 			c = cvp->c;
170108f90673Sjfb 			d = cvp->d;
170208f90673Sjfb 
170308f90673Sjfb 			if (a <= b && c <= d)
170408f90673Sjfb 				ch = 'c';
170508f90673Sjfb 			else
170608f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
170708f90673Sjfb 
170808f90673Sjfb 			if (ch == 'd')
170908f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
171008f90673Sjfb 			else {
171108f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
171208f90673Sjfb 				fetch(ixnew, c, d, f2,
171308f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
171408f90673Sjfb 			}
171508f90673Sjfb 			lowc = d + 1;
171608f90673Sjfb 			cvp++;
171708f90673Sjfb 		}
171808f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
171908f90673Sjfb 	}
172008f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
172108f90673Sjfb }
172208f90673Sjfb 
172308f90673Sjfb /* dump accumulated "unified" diff changes */
172408f90673Sjfb static void
172508f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
172608f90673Sjfb {
172708f90673Sjfb 	struct context_vec *cvp = context_vec_start;
172808f90673Sjfb 	int lowa, upb, lowc, upd;
172908f90673Sjfb 	int a, b, c, d;
17305e78344dSjfb 	char ch, *f;
173108f90673Sjfb 
173208f90673Sjfb 	if (context_vec_start > context_vec_ptr)
173308f90673Sjfb 		return;
173408f90673Sjfb 
173508f90673Sjfb 	b = d = 0;		/* gcc */
1736dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1737e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1738dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1739e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
174008f90673Sjfb 
1741f9b67873Sniallo 	diff_output("@@ -");
174208f90673Sjfb 	uni_range(lowa, upb);
1743f9b67873Sniallo 	diff_output(" +");
174408f90673Sjfb 	uni_range(lowc, upd);
1745f9b67873Sniallo 	diff_output(" @@");
174648dc77e6Sxsa 	if (pflag == 1) {
17475e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
17485e78344dSjfb 		if (f != NULL) {
1749f9b67873Sniallo 			diff_output(" ");
1750f9b67873Sniallo 			diff_output("%s", f);
17515e78344dSjfb 		}
17525e78344dSjfb 	}
1753f9b67873Sniallo 	diff_output("\n");
175408f90673Sjfb 
175508f90673Sjfb 	/*
175608f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
175708f90673Sjfb 	 * are printed together.
175808f90673Sjfb 	 */
175908f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
176008f90673Sjfb 		a = cvp->a;
176108f90673Sjfb 		b = cvp->b;
176208f90673Sjfb 		c = cvp->c;
176308f90673Sjfb 		d = cvp->d;
176408f90673Sjfb 
176508f90673Sjfb 		/*
176608f90673Sjfb 		 * c: both new and old changes
176708f90673Sjfb 		 * d: only changes in the old file
176808f90673Sjfb 		 * a: only changes in the new file
176908f90673Sjfb 		 */
177008f90673Sjfb 		if (a <= b && c <= d)
177108f90673Sjfb 			ch = 'c';
177208f90673Sjfb 		else
177308f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
177408f90673Sjfb 
177508f90673Sjfb 		switch (ch) {
177608f90673Sjfb 		case 'c':
177708f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
177808f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
177908f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
178008f90673Sjfb 			break;
178108f90673Sjfb 		case 'd':
178208f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
178308f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
178408f90673Sjfb 			break;
178508f90673Sjfb 		case 'a':
178608f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
178708f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
178808f90673Sjfb 			break;
178908f90673Sjfb 		}
179008f90673Sjfb 		lowa = b + 1;
179108f90673Sjfb 		lowc = d + 1;
179208f90673Sjfb 	}
179308f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
179408f90673Sjfb 
179508f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
179608f90673Sjfb }
1797f9b67873Sniallo 
179801af718aSjoris void
1799f9b67873Sniallo diff_output(const char *fmt, ...)
1800f9b67873Sniallo {
1801f9b67873Sniallo 	va_list vap;
1802f9b67873Sniallo 	char *str;
1803f9b67873Sniallo 
1804f9b67873Sniallo 	va_start(vap, fmt);
1805f9b67873Sniallo 	vasprintf(&str, fmt, vap);
1806f9b67873Sniallo 	if (diffbuf != NULL)
1807f9b67873Sniallo 		cvs_buf_append(diffbuf, str, strlen(str));
1808f9b67873Sniallo 	else
1809f9b67873Sniallo 		cvs_printf("%s", str);
1810f9b67873Sniallo 	free(str);
1811f9b67873Sniallo 	va_end(vap);
1812f9b67873Sniallo }
1813