xref: /openbsd-src/usr.bin/cvs/diff.c (revision 4c06e5f6fe6880f7ad96ed56848b684d85ed5ecc)
1*4c06e5f6Sreyk /*	$OpenBSD: diff.c,v 1.57 2005/10/07 21:47:32 reyk 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 information 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;
22208f90673Sjfb static int format = D_NORMAL;
22308f90673Sjfb static struct stat stb1, stb2;
224af5bb824Sniallo static char *ifdefname, *ignore_pats;
225f5638424Sjfb static const char *diff_file;
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;
25208f90673Sjfb /*
25308f90673Sjfb  * chrtran points to one of 2 translation tables: cup2low if folding upper to
25408f90673Sjfb  * lower case clow2low if not folding case
25508f90673Sjfb  */
25608f90673Sjfb u_char clow2low[256] = {
25708f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
25808f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
25908f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
26008f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
26108f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
26208f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
26308f90673Sjfb 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
26408f90673Sjfb 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
26508f90673Sjfb 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
26608f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
26708f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
26808f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
26908f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
27008f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
27108f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
27208f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
27308f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
27408f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
27508f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
27608f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
27708f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
27808f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
27908f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
28008f90673Sjfb 	0xfd, 0xfe, 0xff
28108f90673Sjfb };
28208f90673Sjfb 
28308f90673Sjfb u_char cup2low[256] = {
28408f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
28508f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
28608f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
28708f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
28808f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
28908f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
29008f90673Sjfb 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
29108f90673Sjfb 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
29208f90673Sjfb 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
29308f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
29408f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
29508f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
29608f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
29708f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
29808f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
29908f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
30008f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
30108f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
30208f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
30308f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
30408f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
30508f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
30608f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
30708f90673Sjfb 	0xfd, 0xfe, 0xff
30808f90673Sjfb };
30908f90673Sjfb 
310af5bb824Sniallo #if !defined(RCSPROG)
311e4276007Sjfb struct cvs_cmd cvs_cmd_diff = {
312e4276007Sjfb 	CVS_OP_DIFF, CVS_REQ_DIFF, "diff",
313e4276007Sjfb 	{ "di", "dif" },
314e4276007Sjfb 	"Show differences between revisions",
315c9150269Sxsa 	"[-cilNnpu] [[-D date] [-r rev] [-D date2 | -r rev2]] "
316c9150269Sxsa 	"[-k mode] [file ...]",
317c9150269Sxsa 	"cD:iklNnpr:Ru",
31816cfc147Sjoris 	NULL,
31916cfc147Sjoris 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
320e4276007Sjfb 	cvs_diff_init,
321e4276007Sjfb 	cvs_diff_pre_exec,
322e4276007Sjfb 	cvs_diff_remote,
323e4276007Sjfb 	cvs_diff_local,
324e4276007Sjfb 	NULL,
325e4276007Sjfb 	cvs_diff_cleanup,
326e4276007Sjfb 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
327e4276007Sjfb };
328e4276007Sjfb 
329e4276007Sjfb 
330e4276007Sjfb struct cvs_cmd cvs_cmd_rdiff = {
331e4276007Sjfb 	CVS_OP_RDIFF, CVS_REQ_DIFF, "rdiff",
332c9150269Sxsa 	{ "pa", "patch" },
333e4276007Sjfb 	"Create 'patch' format diffs between releases",
334c9150269Sxsa 	"[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev "
335c9150269Sxsa 	"[-D date2 | -rev2] module ...",
336c9150269Sxsa 	"cD:flRr:stuV:",
337e4276007Sjfb 	NULL,
338e4276007Sjfb 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
339e4276007Sjfb 	cvs_diff_init,
340e4276007Sjfb 	cvs_diff_pre_exec,
341e4276007Sjfb 	cvs_diff_remote,
342e4276007Sjfb 	cvs_diff_local,
343e4276007Sjfb 	NULL,
344e4276007Sjfb 	cvs_diff_cleanup,
34544381dcbSjoris 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
34616cfc147Sjoris };
347af5bb824Sniallo #endif
34808f90673Sjfb 
349af5bb824Sniallo #if !defined(RCSPROG)
35016cfc147Sjoris static struct diff_arg *dap = NULL;
35116cfc147Sjoris static int recurse;
35216cfc147Sjoris 
353e4276007Sjfb static int
354e4276007Sjfb cvs_diff_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg)
35508f90673Sjfb {
35616cfc147Sjoris 	int ch;
35708f90673Sjfb 
35816cfc147Sjoris 	dap = (struct diff_arg *)malloc(sizeof(*dap));
35916cfc147Sjoris 	if (dap == NULL)
36031274bbfSjoris 		return (CVS_EX_DATA);
36116cfc147Sjoris 	dap->date1 = dap->date2 = dap->rev1 = dap->rev2 = NULL;
362dc6a6879Sjfb 	strlcpy(diffargs, argv[0], sizeof(diffargs));
363dc6a6879Sjfb 
364e4276007Sjfb 	while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) {
36508f90673Sjfb 		switch (ch) {
36608f90673Sjfb 		case 'c':
367f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
36808f90673Sjfb 			format = D_CONTEXT;
36908f90673Sjfb 			break;
37008f90673Sjfb 		case 'D':
37116cfc147Sjoris 			if (dap->date1 == NULL && dap->rev1 == NULL) {
37216cfc147Sjoris 				dap->date1 = optarg;
37316cfc147Sjoris 			} else if (dap->date2 == NULL && dap->rev2 == NULL) {
37416cfc147Sjoris 				dap->date2 = optarg;
37516cfc147Sjoris 			} else {
37608f90673Sjfb 				cvs_log(LP_ERR,
37708f90673Sjfb 				    "no more than two revisions/dates can "
37808f90673Sjfb 				    "be specified");
37908f90673Sjfb 			}
38008f90673Sjfb 			break;
38108f90673Sjfb 		case 'l':
382f5638424Sjfb 			strlcat(diffargs, " -l", sizeof(diffargs));
38308f90673Sjfb 			recurse = 0;
384e4276007Sjfb 			cvs_cmd_diff.file_flags &= ~CF_RECURSE;
38508f90673Sjfb 			break;
38608f90673Sjfb 		case 'i':
387f5638424Sjfb 			strlcat(diffargs, " -i", sizeof(diffargs));
38808f90673Sjfb 			iflag = 1;
38908f90673Sjfb 			break;
390c710bc5aSjfb 		case 'N':
391c710bc5aSjfb 			strlcat(diffargs, " -N", sizeof(diffargs));
392c710bc5aSjfb 			Nflag = 1;
393c710bc5aSjfb 			break;
394394180a4Sjfb 		case 'n':
395394180a4Sjfb 			strlcat(diffargs, " -n", sizeof(diffargs));
396394180a4Sjfb 			format = D_RCSDIFF;
397394180a4Sjfb 			break;
3985e78344dSjfb 		case 'p':
3995e78344dSjfb 			strlcat(diffargs, " -p", sizeof(diffargs));
4005e78344dSjfb 			pflag = 1;
4015e78344dSjfb 			break;
40208f90673Sjfb 		case 'r':
40316cfc147Sjoris 			if ((dap->rev1 == NULL) && (dap->date1 == NULL)) {
40416cfc147Sjoris 				dap->rev1 = optarg;
40516cfc147Sjoris 			} else if ((dap->rev2 == NULL) &&
40616cfc147Sjoris 			    (dap->date2 == NULL)) {
40716cfc147Sjoris 				dap->rev2 = optarg;
40816cfc147Sjoris 			} else {
40908f90673Sjfb 				cvs_log(LP_ERR,
41008f90673Sjfb 				    "no more than two revisions/dates can "
41108f90673Sjfb 				    "be specified");
41231274bbfSjoris 				return (CVS_EX_USAGE);
41308f90673Sjfb 			}
41408f90673Sjfb 			break;
415f203c484Sjoris 		case 'R':
416e4276007Sjfb 			cvs_cmd_diff.file_flags |= CF_RECURSE;
417f203c484Sjoris 			break;
41808f90673Sjfb 		case 'u':
419f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
42008f90673Sjfb 			format = D_UNIFIED;
42108f90673Sjfb 			break;
42208f90673Sjfb 		default:
42331274bbfSjoris 			return (CVS_EX_USAGE);
42408f90673Sjfb 		}
42508f90673Sjfb 	}
42608f90673Sjfb 
42716cfc147Sjoris 	*arg = optind;
428dc6a6879Sjfb 	return (0);
429dc6a6879Sjfb }
430dc6a6879Sjfb 
43116cfc147Sjoris int
43216cfc147Sjoris cvs_diff_cleanup(void)
43316cfc147Sjoris {
434e4276007Sjfb 	if (dap != NULL) {
43516cfc147Sjoris 		free(dap);
436e4276007Sjfb 		dap = NULL;
437e4276007Sjfb 	}
43816cfc147Sjoris 	return (0);
43916cfc147Sjoris }
440dc6a6879Sjfb 
441dc6a6879Sjfb /*
442e4276007Sjfb  * cvs_diff_pre_exec()
443dc6a6879Sjfb  *
444dc6a6879Sjfb  */
445dc6a6879Sjfb int
446e4276007Sjfb cvs_diff_pre_exec(struct cvsroot *root)
447dc6a6879Sjfb {
448e4276007Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
44908f90673Sjfb 		/* send the flags */
4505e78344dSjfb 		if (Nflag && (cvs_sendarg(root, "-N", 0) < 0))
45131274bbfSjoris 			return (CVS_EX_PROTO);
4525e78344dSjfb 		if (pflag && (cvs_sendarg(root, "-p", 0) < 0))
45331274bbfSjoris 			return (CVS_EX_PROTO);
4545e78344dSjfb 
455610801e8Sxsa 		if (format == D_CONTEXT) {
456610801e8Sxsa 			if (cvs_sendarg(root, "-c", 0) < 0)
457610801e8Sxsa 				return (CVS_EX_PROTO);
458610801e8Sxsa 		} else if (format == D_UNIFIED) {
459610801e8Sxsa 			if (cvs_sendarg(root, "-u", 0) < 0)
460610801e8Sxsa 				return (CVS_EX_PROTO);
461610801e8Sxsa 		}
46208f90673Sjfb 
463dc6a6879Sjfb 		if (dap->rev1 != NULL) {
464610801e8Sxsa 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
465610801e8Sxsa 			    (cvs_sendarg(root, dap->rev1, 0) < 0))
466610801e8Sxsa 				return (CVS_EX_PROTO);
4673917c9bfSderaadt 		} else if (dap->date1 != NULL) {
468610801e8Sxsa 			if ((cvs_sendarg(root, "-D", 0) < 0) ||
469610801e8Sxsa 			    (cvs_sendarg(root, dap->date1, 0) < 0))
470610801e8Sxsa 				return (CVS_EX_PROTO);
47108f90673Sjfb 		}
472dc6a6879Sjfb 		if (dap->rev2 != NULL) {
473610801e8Sxsa 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
474610801e8Sxsa 			    (cvs_sendarg(root, dap->rev2, 0) < 0))
475610801e8Sxsa 				return (CVS_EX_PROTO);
4763917c9bfSderaadt 		} else if (dap->date2 != NULL) {
477610801e8Sxsa 			if ((cvs_sendarg(root, "-D", 0) < 0) ||
478610801e8Sxsa 			    (cvs_sendarg(root, dap->date2, 0) < 0))
479610801e8Sxsa 				return  (CVS_EX_PROTO);
48008f90673Sjfb 		}
481e4276007Sjfb 	}
48208f90673Sjfb 
48308f90673Sjfb 	return (0);
48408f90673Sjfb }
48508f90673Sjfb 
48608f90673Sjfb 
48708f90673Sjfb /*
48808f90673Sjfb  * cvs_diff_file()
48908f90673Sjfb  *
49008f90673Sjfb  * Diff a single file.
49108f90673Sjfb  */
492e4276007Sjfb static int
493e4276007Sjfb cvs_diff_remote(struct cvs_file *cfp, void *arg)
49408f90673Sjfb {
495e4276007Sjfb 	char *dir, *repo;
496e4276007Sjfb 	char fpath[MAXPATHLEN], dfpath[MAXPATHLEN];
497dc6a6879Sjfb 	struct cvsroot *root;
498dc6a6879Sjfb 
499dc6a6879Sjfb 	if (cfp->cf_type == DT_DIR) {
500895e6cf6Sjfb 		if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
501bb029937Sjfb 			root = cfp->cf_parent->cf_root;
502b904ba2eSjoris 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
5033917c9bfSderaadt 		} else {
504bb029937Sjfb 			root = cfp->cf_root;
50516cfc147Sjoris #if 0
506dc6a6879Sjfb 			if ((cfp->cf_parent == NULL) ||
507bb029937Sjfb 			    (root != cfp->cf_parent->cf_root)) {
508dc6a6879Sjfb 				cvs_connect(root);
509e4276007Sjfb 				cvs_diff_pre_exec(root);
510dc6a6879Sjfb 			}
51116cfc147Sjoris #endif
512dc6a6879Sjfb 
513dc6a6879Sjfb 			cvs_senddir(root, cfp);
514895e6cf6Sjfb 		}
515895e6cf6Sjfb 
516dc6a6879Sjfb 		return (0);
517dc6a6879Sjfb 	}
51808f90673Sjfb 
5192d5b8b1dSjfb 	if (cfp->cf_cvstat == CVS_FST_LOST) {
520b904ba2eSjoris 		cvs_log(LP_WARN, "cannot find file %s", cfp->cf_name);
5212d5b8b1dSjfb 		return (0);
5222d5b8b1dSjfb 	}
5232d5b8b1dSjfb 
524c710bc5aSjfb 	diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath));
525895e6cf6Sjfb 
526dc6a6879Sjfb 	if (cfp->cf_parent != NULL) {
527c710bc5aSjfb 		dir = cvs_file_getpath(cfp->cf_parent, dfpath, sizeof(dfpath));
528bb029937Sjfb 		root = cfp->cf_parent->cf_root;
529bb029937Sjfb 		repo = cfp->cf_parent->cf_repo;
5303917c9bfSderaadt 	} else {
531dc6a6879Sjfb 		dir = ".";
532895e6cf6Sjfb 		root = NULL;
533dc6a6879Sjfb 		repo = NULL;
53408f90673Sjfb 	}
53508f90673Sjfb 
536dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
537e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
538dc6a6879Sjfb 		return (0);
53908f90673Sjfb 	}
54008f90673Sjfb 
541e4276007Sjfb 	if (cvs_sendentry(root, cfp) < 0)
54231274bbfSjoris 		return (CVS_EX_PROTO);
54308f90673Sjfb 
544dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UPTODATE) {
545e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name);
54608f90673Sjfb 		return (0);
54708f90673Sjfb 	}
54808f90673Sjfb 
54908f90673Sjfb 	/* at this point, the file is modified */
550e4276007Sjfb 	if ((cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name) < 0) ||
551e4276007Sjfb 	    (cvs_sendfile(root, diff_file) < 0))
552e4276007Sjfb 		return (CVS_EX_PROTO);
553e4276007Sjfb 
554e4276007Sjfb 	return (0);
555e4276007Sjfb }
556e4276007Sjfb 
557e4276007Sjfb static int
558e4276007Sjfb cvs_diff_local(CVSFILE *cf, void *arg)
559e4276007Sjfb {
560e4276007Sjfb 	int len;
561e4276007Sjfb 	char *repo, buf[64];
562e4276007Sjfb 	char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
563e4276007Sjfb 	char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN];
564e4276007Sjfb 	BUF *b1, *b2;
565e4276007Sjfb 	RCSNUM *r1, *r2;
566e4276007Sjfb 	RCSFILE *rf;
567e4276007Sjfb 	struct cvsroot *root;
568e4276007Sjfb 
569e4276007Sjfb 	rf = NULL;
570e4276007Sjfb 	root = CVS_DIR_ROOT(cf);
571e4276007Sjfb 	repo = CVS_DIR_REPO(cf);
572e4276007Sjfb 	diff_file = cvs_file_getpath(cf, fpath, sizeof(fpath));
573e4276007Sjfb 
574e4276007Sjfb 	if (cf->cf_type == DT_DIR) {
575b87c59bdSxsa 		if (verbosity > 1)
576246675edSxsa 			cvs_log(LP_NOTICE, "Diffing %s", fpath);
577e4276007Sjfb 		return (0);
578e4276007Sjfb 	}
579e4276007Sjfb 
580e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_LOST) {
581e4276007Sjfb 		cvs_log(LP_WARN, "cannot find file %s", cf->cf_name);
582e4276007Sjfb 		return (0);
583e4276007Sjfb 	}
584e4276007Sjfb 
585e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_UNKNOWN) {
586e4276007Sjfb 		cvs_log(LP_WARN, "I know nothing about %s", diff_file);
587e4276007Sjfb 		return (0);
5885c0cd766Sniallo 	} else if (cf->cf_cvstat == CVS_FST_UPTODATE)
589e4276007Sjfb 		return (0);
590e4276007Sjfb 
591e4276007Sjfb 	/* at this point, the file is modified */
592e4276007Sjfb 	len = snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
593dc6a6879Sjfb 	    root->cr_dir, repo, diff_file, RCS_FILE_EXT);
594e4276007Sjfb 	if (len == -1 || len >= (int)sizeof(rcspath)) {
59527b85f85Sxsa 		errno = ENAMETOOLONG;
59627b85f85Sxsa 		cvs_log(LP_ERRNO, "%s", rcspath);
59701b3d77aSjoris 		return (CVS_EX_DATA);
59827b85f85Sxsa 	}
59908f90673Sjfb 
6001b6534b8Sjfb 	rf = rcs_open(rcspath, RCS_READ);
601dc6a6879Sjfb 	if (rf == NULL) {
60231274bbfSjoris 		return (CVS_EX_DATA);
603dc6a6879Sjfb 	}
60408f90673Sjfb 
605dc6a6879Sjfb 	cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file,
60608f90673Sjfb 	    RCS_DIFF_DIV, rcspath);
60708f90673Sjfb 
608dc6a6879Sjfb 	if (dap->rev1 == NULL)
609e4276007Sjfb 		r1 = cf->cf_lrev;
61008f90673Sjfb 	else {
61125b74b48Sjfb 		if ((r1 = rcsnum_parse(dap->rev1)) == NULL) {
61231274bbfSjoris 			return (CVS_EX_DATA);
6137f535ec4Sjfb 		}
61408f90673Sjfb 	}
61508f90673Sjfb 
616dc6a6879Sjfb 	cvs_printf("retrieving revision %s\n",
61708f90673Sjfb 	    rcsnum_tostr(r1, buf, sizeof(buf)));
61808f90673Sjfb 	b1 = rcs_getrev(rf, r1);
61908f90673Sjfb 
62095ae1173Sjoris 	if (b1 == NULL) {
621289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
62295ae1173Sjoris 		    rcsnum_tostr(r1, buf, sizeof(buf)));
62395ae1173Sjoris 		if (r1 != cf->cf_lrev)
62495ae1173Sjoris 			rcsnum_free(r1);
62595ae1173Sjoris 		return (CVS_EX_DATA);
62695ae1173Sjoris 	}
62795ae1173Sjoris 
628e4276007Sjfb 	if (r1 != cf->cf_lrev)
6297f535ec4Sjfb 		rcsnum_free(r1);
6307f535ec4Sjfb 
631dc6a6879Sjfb 	if (dap->rev2 != NULL) {
632dc6a6879Sjfb 		cvs_printf("retrieving revision %s\n", dap->rev2);
63325b74b48Sjfb 		if ((r2 = rcsnum_parse(dap->rev2)) == NULL) {
63431274bbfSjoris 			return (CVS_EX_DATA);
6357f535ec4Sjfb 		}
63608f90673Sjfb 		b2 = rcs_getrev(rf, r2);
6377f535ec4Sjfb 		rcsnum_free(r2);
6383917c9bfSderaadt 	} else {
639dc6a6879Sjfb 		b2 = cvs_buf_load(diff_file, BUF_AUTOEXT);
64008f90673Sjfb 	}
64108f90673Sjfb 
642dc6a6879Sjfb 	rcs_close(rf);
643dc6a6879Sjfb 
64495ae1173Sjoris 	if (b2 == NULL) {
645289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
64695ae1173Sjoris 		    dap->rev2);
64795ae1173Sjoris 		cvs_buf_free(b1);
64895ae1173Sjoris 		return (CVS_EX_DATA);
64995ae1173Sjoris 	}
65095ae1173Sjoris 
6515ac8b1e7Sjoris 	cvs_printf("%s", diffargs);
6525ac8b1e7Sjoris 	cvs_printf(" -r%s", buf);
653dc6a6879Sjfb 	if (dap->rev2 != NULL)
6545ac8b1e7Sjoris 		cvs_printf(" -r%s", dap->rev2);
6555ac8b1e7Sjoris 	cvs_printf(" %s\n", diff_file);
656946f6157Sdjm 	strlcpy(path_tmp1, "/tmp/diff1.XXXXXXXXXX", sizeof(path_tmp1));
6577f535ec4Sjfb 	if (cvs_buf_write_stmp(b1, path_tmp1, 0600) == -1) {
6587f535ec4Sjfb 		cvs_buf_free(b1);
6597f535ec4Sjfb 		cvs_buf_free(b2);
66031274bbfSjoris 		return (CVS_EX_DATA);
6617f535ec4Sjfb 	}
6627f535ec4Sjfb 	cvs_buf_free(b1);
6637f535ec4Sjfb 
66469853e40Sjfb 	strlcpy(path_tmp2, "/tmp/diff2.XXXXXXXXXX", sizeof(path_tmp2));
665946f6157Sdjm 	if (cvs_buf_write_stmp(b2, path_tmp2, 0600) == -1) {
6667f535ec4Sjfb 		cvs_buf_free(b2);
667946f6157Sdjm 		(void)unlink(path_tmp1);
66831274bbfSjoris 		return (CVS_EX_DATA);
669946f6157Sdjm 	}
6707f535ec4Sjfb 	cvs_buf_free(b2);
6717f535ec4Sjfb 
672946f6157Sdjm 	cvs_diffreg(path_tmp1, path_tmp2);
673946f6157Sdjm 	(void)unlink(path_tmp1);
674946f6157Sdjm 	(void)unlink(path_tmp2);
67508f90673Sjfb 
67608f90673Sjfb 	return (0);
67708f90673Sjfb }
678af5bb824Sniallo #endif
67908f90673Sjfb 
68008f90673Sjfb 
68108f90673Sjfb int
68208f90673Sjfb cvs_diffreg(const char *file1, const char *file2)
68308f90673Sjfb {
68408f90673Sjfb 	FILE *f1, *f2;
68508f90673Sjfb 	int i, rval;
6867f535ec4Sjfb 	void *tmp;
68708f90673Sjfb 
68808f90673Sjfb 	f1 = f2 = NULL;
68908f90673Sjfb 	rval = D_SAME;
69008f90673Sjfb 	anychange = 0;
69108f90673Sjfb 	lastline = 0;
69208f90673Sjfb 	lastmatchline = 0;
69308f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
69408f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
69508f90673Sjfb 
69608f90673Sjfb 	f1 = fopen(file1, "r");
69708f90673Sjfb 	if (f1 == NULL) {
69808f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
69908f90673Sjfb 		goto closem;
70008f90673Sjfb 	}
70108f90673Sjfb 
70208f90673Sjfb 	f2 = fopen(file2, "r");
70308f90673Sjfb 	if (f2 == NULL) {
70408f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
70508f90673Sjfb 		goto closem;
70608f90673Sjfb 	}
70708f90673Sjfb 
70808f90673Sjfb 	switch (files_differ(f1, f2)) {
70908f90673Sjfb 	case 0:
71008f90673Sjfb 		goto closem;
71108f90673Sjfb 	case 1:
71208f90673Sjfb 		break;
71308f90673Sjfb 	default:
71408f90673Sjfb 		/* error */
71508f90673Sjfb 		goto closem;
71608f90673Sjfb 	}
71708f90673Sjfb 
71808f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
71908f90673Sjfb 		rval = D_BINARY;
72008f90673Sjfb 		goto closem;
72108f90673Sjfb 	}
7227f535ec4Sjfb 	if ((prepare(0, f1, stb1.st_size) < 0) ||
7237f535ec4Sjfb 	    (prepare(1, f2, stb2.st_size) < 0)) {
7247f535ec4Sjfb 		goto closem;
7257f535ec4Sjfb 	}
72608f90673Sjfb 	prune();
72708f90673Sjfb 	sort(sfile[0], slen[0]);
72808f90673Sjfb 	sort(sfile[1], slen[1]);
72908f90673Sjfb 
73008f90673Sjfb 	member = (int *)file[1];
73108f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
73218501190Sniallo 	if ((tmp = realloc(member, (slen[1] + 2) * sizeof(int))) == NULL) {
73318501190Sniallo 		free(member);
73418501190Sniallo 		member = NULL;
73518501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize member");
7367f535ec4Sjfb 		goto closem;
73718501190Sniallo 	}
7387f535ec4Sjfb 	member = (int *)tmp;
73908f90673Sjfb 
74008f90673Sjfb 	class = (int *)file[0];
74108f90673Sjfb 	unsort(sfile[0], slen[0], class);
74218501190Sniallo 	if ((tmp = realloc(class, (slen[0] + 2) * sizeof(int))) == NULL) {
74318501190Sniallo 		free(class);
74418501190Sniallo 		class = NULL;
74518501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize class");
7467f535ec4Sjfb 		goto closem;
74718501190Sniallo 	}
7487f535ec4Sjfb 	class = (int *)tmp;
74908f90673Sjfb 
7507f535ec4Sjfb 	if ((klist = malloc((slen[0] + 2) * sizeof(int))) == NULL) {
7517f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate klist");
7527f535ec4Sjfb 		goto closem;
7537f535ec4Sjfb 	}
75408f90673Sjfb 	clen = 0;
75508f90673Sjfb 	clistlen = 100;
7567f535ec4Sjfb 	if ((clist = malloc(clistlen * sizeof(cand))) == NULL) {
7577f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate clist");
7587f535ec4Sjfb 		goto closem;
7597f535ec4Sjfb 	}
760ece76a70Sjoris 
761ece76a70Sjoris 	if ((i = stone(class, slen[0], member, klist)) < 0)
762ece76a70Sjoris 		goto closem;
763ece76a70Sjoris 
76408f90673Sjfb 	free(member);
76508f90673Sjfb 	free(class);
76608f90673Sjfb 
76718501190Sniallo 	if ((tmp = realloc(J, (diff_len[0] + 2) * sizeof(int))) == NULL) {
76818501190Sniallo 		free(J);
76918501190Sniallo 		J = NULL;
77018501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize J");
77118501190Sniallo 		goto closem;
77218501190Sniallo 	}
77318501190Sniallo 	J = (int *)tmp;
77408f90673Sjfb 	unravel(klist[i]);
77508f90673Sjfb 	free(clist);
77608f90673Sjfb 	free(klist);
77708f90673Sjfb 
77818501190Sniallo 	if ((tmp = realloc(ixold, (diff_len[0] + 2) * sizeof(long))) == NULL) {
77918501190Sniallo 		free(ixold);
78018501190Sniallo 		ixold = NULL;
78118501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize ixold");
78218501190Sniallo 		goto closem;
78318501190Sniallo 	}
78418501190Sniallo 	ixold = (long *)tmp;
78518501190Sniallo 	if ((tmp = realloc(ixnew, (diff_len[1] + 2) * sizeof(long))) == NULL) {
78618501190Sniallo 		free(ixnew);
78718501190Sniallo 		ixnew = NULL;
78818501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize ixnew");
78918501190Sniallo 		goto closem;
79018501190Sniallo 	}
79118501190Sniallo 	ixnew = (long *)tmp;
79208f90673Sjfb 	check(f1, f2);
79308f90673Sjfb 	output(file1, f1, file2, f2);
79408f90673Sjfb 
79508f90673Sjfb closem:
79608f90673Sjfb 	if (anychange) {
79708f90673Sjfb 		if (rval == D_SAME)
79808f90673Sjfb 			rval = D_DIFFER;
79908f90673Sjfb 	}
80008f90673Sjfb 	if (f1 != NULL)
80108f90673Sjfb 		fclose(f1);
80208f90673Sjfb 	if (f2 != NULL)
80308f90673Sjfb 		fclose(f2);
8047f535ec4Sjfb 
80508f90673Sjfb 	return (rval);
80608f90673Sjfb }
80708f90673Sjfb 
80808f90673Sjfb /*
80908f90673Sjfb  * Check to see if the given files differ.
81008f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
81108f90673Sjfb  * XXX - could use code from cmp(1) [faster]
81208f90673Sjfb  */
81308f90673Sjfb static int
81408f90673Sjfb files_differ(FILE *f1, FILE *f2)
81508f90673Sjfb {
81608f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
81708f90673Sjfb 	size_t i, j;
81808f90673Sjfb 
81908f90673Sjfb 	if (stb1.st_size != stb2.st_size)
82008f90673Sjfb 		return (1);
82108f90673Sjfb 	for (;;) {
822f1901a5aSxsa 		i = fread(buf1, (size_t)1, sizeof(buf1), f1);
823f1901a5aSxsa 		j = fread(buf2, (size_t)1, sizeof(buf2), f2);
82408f90673Sjfb 		if (i != j)
82508f90673Sjfb 			return (1);
82608f90673Sjfb 		if (i == 0 && j == 0) {
82708f90673Sjfb 			if (ferror(f1) || ferror(f2))
82808f90673Sjfb 				return (1);
82908f90673Sjfb 			return (0);
83008f90673Sjfb 		}
83108f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
83208f90673Sjfb 			return (1);
83308f90673Sjfb 	}
83408f90673Sjfb }
83508f90673Sjfb 
8367f535ec4Sjfb static int
83708f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
83808f90673Sjfb {
8397f535ec4Sjfb 	void *tmp;
84008f90673Sjfb 	struct line *p;
84108f90673Sjfb 	int j, h;
84208f90673Sjfb 	size_t sz;
84308f90673Sjfb 
84408f90673Sjfb 	rewind(fd);
84508f90673Sjfb 
846c48da046Sxsa 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
84708f90673Sjfb 	if (sz < 100)
84808f90673Sjfb 		sz = 100;
84908f90673Sjfb 
8507f535ec4Sjfb 	p = (struct line *)malloc((sz + 3) * sizeof(struct line));
8517f535ec4Sjfb 	if (p == NULL) {
8527f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to prepare line array");
8537f535ec4Sjfb 		return (-1);
8547f535ec4Sjfb 	}
85508f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
85608f90673Sjfb 		if (j == (int)sz) {
85708f90673Sjfb 			sz = sz * 3 / 2;
8587f535ec4Sjfb 			tmp = realloc(p, (sz + 3) * sizeof(struct line));
8597f535ec4Sjfb 			if (tmp == NULL) {
8607f535ec4Sjfb 				cvs_log(LP_ERRNO, "failed to grow line array");
8617f535ec4Sjfb 				free(p);
8627f535ec4Sjfb 				return (-1);
8637f535ec4Sjfb 			}
8647f535ec4Sjfb 			p = (struct line *)tmp;
86508f90673Sjfb 		}
86608f90673Sjfb 		p[++j].value = h;
86708f90673Sjfb 	}
868e4276007Sjfb 	diff_len[i] = j;
86908f90673Sjfb 	file[i] = p;
8707f535ec4Sjfb 
8717f535ec4Sjfb 	return (0);
87208f90673Sjfb }
87308f90673Sjfb 
87408f90673Sjfb static void
87508f90673Sjfb prune(void)
87608f90673Sjfb {
87708f90673Sjfb 	int i, j;
87808f90673Sjfb 
879e4276007Sjfb 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
88008f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
88108f90673Sjfb 	    pref++)
88208f90673Sjfb 		;
883e4276007Sjfb 	for (suff = 0;
884e4276007Sjfb 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
885e4276007Sjfb 	    (file[0][diff_len[0] - suff].value ==
886e4276007Sjfb 	    file[1][diff_len[1] - suff].value);
88708f90673Sjfb 	    suff++)
88808f90673Sjfb 		;
88908f90673Sjfb 	for (j = 0; j < 2; j++) {
89008f90673Sjfb 		sfile[j] = file[j] + pref;
891e4276007Sjfb 		slen[j] = diff_len[j] - pref - suff;
89208f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
89308f90673Sjfb 			sfile[j][i].serial = i;
89408f90673Sjfb 	}
89508f90673Sjfb }
89608f90673Sjfb 
89708f90673Sjfb static void
89808f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
89908f90673Sjfb {
90008f90673Sjfb 	int i, j;
90108f90673Sjfb 
90208f90673Sjfb 	i = j = 1;
90308f90673Sjfb 	while (i <= n && j <= m) {
90408f90673Sjfb 		if (a[i].value < b[j].value)
90508f90673Sjfb 			a[i++].value = 0;
90608f90673Sjfb 		else if (a[i].value == b[j].value)
90708f90673Sjfb 			a[i++].value = j;
90808f90673Sjfb 		else
90908f90673Sjfb 			j++;
91008f90673Sjfb 	}
91108f90673Sjfb 	while (i <= n)
91208f90673Sjfb 		a[i++].value = 0;
91308f90673Sjfb 	b[m + 1].value = 0;
91408f90673Sjfb 	j = 0;
91508f90673Sjfb 	while (++j <= m) {
91608f90673Sjfb 		c[j] = -b[j].serial;
91708f90673Sjfb 		while (b[j + 1].value == b[j].value) {
91808f90673Sjfb 			j++;
91908f90673Sjfb 			c[j] = b[j].serial;
92008f90673Sjfb 		}
92108f90673Sjfb 	}
92208f90673Sjfb 	c[j] = -1;
92308f90673Sjfb }
92408f90673Sjfb 
92508f90673Sjfb /* Code taken from ping.c */
92608f90673Sjfb static int
92708f90673Sjfb isqrt(int n)
92808f90673Sjfb {
92908f90673Sjfb 	int y, x = 1;
93008f90673Sjfb 
93108f90673Sjfb 	if (n == 0)
93208f90673Sjfb 		return (0);
93308f90673Sjfb 
93408f90673Sjfb 	do { /* newton was a stinker */
93508f90673Sjfb 		y = x;
93608f90673Sjfb 		x = n / x;
93708f90673Sjfb 		x += y;
93808f90673Sjfb 		x /= 2;
93908f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
94008f90673Sjfb 
94108f90673Sjfb 	return (x);
94208f90673Sjfb }
94308f90673Sjfb 
94408f90673Sjfb static int
94508f90673Sjfb stone(int *a, int n, int *b, int *c)
94608f90673Sjfb {
947ece76a70Sjoris 	int ret;
94808f90673Sjfb 	int i, k, y, j, l;
94908f90673Sjfb 	int oldc, tc, oldl;
95008f90673Sjfb 	u_int numtries;
95108f90673Sjfb 
952cc649edbSjfb 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
953cc649edbSjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
95408f90673Sjfb 
95508f90673Sjfb 	k = 0;
956ece76a70Sjoris 	if ((ret = newcand(0, 0, 0)) < 0)
957ece76a70Sjoris 		return (-1);
958ece76a70Sjoris 	c[0] = ret;
95908f90673Sjfb 	for (i = 1; i <= n; i++) {
96008f90673Sjfb 		j = a[i];
96108f90673Sjfb 		if (j == 0)
96208f90673Sjfb 			continue;
96308f90673Sjfb 		y = -b[j];
96408f90673Sjfb 		oldl = 0;
96508f90673Sjfb 		oldc = c[0];
96608f90673Sjfb 		numtries = 0;
96708f90673Sjfb 		do {
96808f90673Sjfb 			if (y <= clist[oldc].y)
96908f90673Sjfb 				continue;
97008f90673Sjfb 			l = search(c, k, y);
97108f90673Sjfb 			if (l != oldl + 1)
97208f90673Sjfb 				oldc = c[l - 1];
97308f90673Sjfb 			if (l <= k) {
97408f90673Sjfb 				if (clist[c[l]].y <= y)
97508f90673Sjfb 					continue;
97608f90673Sjfb 				tc = c[l];
977ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
978ece76a70Sjoris 					return (-1);
979ece76a70Sjoris 				c[l] = ret;
98008f90673Sjfb 				oldc = tc;
98108f90673Sjfb 				oldl = l;
98208f90673Sjfb 				numtries++;
98308f90673Sjfb 			} else {
984ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
985ece76a70Sjoris 					return (-1);
986ece76a70Sjoris 				c[l] = ret;
98708f90673Sjfb 				k++;
98808f90673Sjfb 				break;
98908f90673Sjfb 			}
99008f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
99108f90673Sjfb 	}
99208f90673Sjfb 	return (k);
99308f90673Sjfb }
99408f90673Sjfb 
99508f90673Sjfb static int
99608f90673Sjfb newcand(int x, int y, int pred)
99708f90673Sjfb {
99818501190Sniallo 	struct cand *q, *tmp;
99918501190Sniallo 	int newclistlen;
100008f90673Sjfb 
100108f90673Sjfb 	if (clen == clistlen) {
100218501190Sniallo 		newclistlen = clistlen * 11 / 10;
100318501190Sniallo 		tmp = realloc(clist, newclistlen * sizeof(cand));
100418501190Sniallo 		if (tmp == NULL) {
10057f535ec4Sjfb 			cvs_log(LP_ERRNO, "failed to resize clist");
10067f535ec4Sjfb 			return (-1);
10077f535ec4Sjfb 		}
100818501190Sniallo 		clist = tmp;
100918501190Sniallo 		clistlen = newclistlen;
101008f90673Sjfb 	}
101108f90673Sjfb 	q = clist + clen;
101208f90673Sjfb 	q->x = x;
101308f90673Sjfb 	q->y = y;
101408f90673Sjfb 	q->pred = pred;
101508f90673Sjfb 	return (clen++);
101608f90673Sjfb }
101708f90673Sjfb 
101808f90673Sjfb static int
101908f90673Sjfb search(int *c, int k, int y)
102008f90673Sjfb {
102108f90673Sjfb 	int i, j, l, t;
102208f90673Sjfb 
102308f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
102408f90673Sjfb 		return (k + 1);
102508f90673Sjfb 	i = 0;
102608f90673Sjfb 	j = k + 1;
102708f90673Sjfb 	while (1) {
102808f90673Sjfb 		l = i + j;
102908f90673Sjfb 		if ((l >>= 1) <= i)
103008f90673Sjfb 			break;
103108f90673Sjfb 		t = clist[c[l]].y;
103208f90673Sjfb 		if (t > y)
103308f90673Sjfb 			j = l;
103408f90673Sjfb 		else if (t < y)
103508f90673Sjfb 			i = l;
103608f90673Sjfb 		else
103708f90673Sjfb 			return (l);
103808f90673Sjfb 	}
103908f90673Sjfb 	return (l + 1);
104008f90673Sjfb }
104108f90673Sjfb 
104208f90673Sjfb static void
104308f90673Sjfb unravel(int p)
104408f90673Sjfb {
104508f90673Sjfb 	struct cand *q;
104608f90673Sjfb 	int i;
104708f90673Sjfb 
1048e4276007Sjfb 	for (i = 0; i <= diff_len[0]; i++)
104908f90673Sjfb 		J[i] = i <= pref ? i :
1050e4276007Sjfb 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
105108f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
105208f90673Sjfb 		J[q->x + pref] = q->y + pref;
105308f90673Sjfb }
105408f90673Sjfb 
105508f90673Sjfb /*
105608f90673Sjfb  * Check does double duty:
105708f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
105808f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
105908f90673Sjfb  *  2.  collect random access indexes to the two files
106008f90673Sjfb  */
106108f90673Sjfb static void
106208f90673Sjfb check(FILE *f1, FILE *f2)
106308f90673Sjfb {
106408f90673Sjfb 	int i, j, jackpot, c, d;
106508f90673Sjfb 	long ctold, ctnew;
106608f90673Sjfb 
106708f90673Sjfb 	rewind(f1);
106808f90673Sjfb 	rewind(f2);
106908f90673Sjfb 	j = 1;
107008f90673Sjfb 	ixold[0] = ixnew[0] = 0;
107108f90673Sjfb 	jackpot = 0;
107208f90673Sjfb 	ctold = ctnew = 0;
1073e4276007Sjfb 	for (i = 1; i <= diff_len[0]; i++) {
107408f90673Sjfb 		if (J[i] == 0) {
107508f90673Sjfb 			ixold[i] = ctold += skipline(f1);
107608f90673Sjfb 			continue;
107708f90673Sjfb 		}
107808f90673Sjfb 		while (j < J[i]) {
107908f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
108008f90673Sjfb 			j++;
108108f90673Sjfb 		}
108208f90673Sjfb 		if (bflag || wflag || iflag) {
108308f90673Sjfb 			for (;;) {
108408f90673Sjfb 				c = getc(f1);
108508f90673Sjfb 				d = getc(f2);
108608f90673Sjfb 				/*
108708f90673Sjfb 				 * GNU diff ignores a missing newline
108808f90673Sjfb 				 * in one file if bflag || wflag.
108908f90673Sjfb 				 */
109008f90673Sjfb 				if ((bflag || wflag) &&
109108f90673Sjfb 				    ((c == EOF && d == '\n') ||
109208f90673Sjfb 				    (c == '\n' && d == EOF))) {
109308f90673Sjfb 					break;
109408f90673Sjfb 				}
109508f90673Sjfb 				ctold++;
109608f90673Sjfb 				ctnew++;
109708f90673Sjfb 				if (bflag && isspace(c) && isspace(d)) {
109808f90673Sjfb 					do {
109908f90673Sjfb 						if (c == '\n')
110008f90673Sjfb 							break;
110108f90673Sjfb 						ctold++;
110208f90673Sjfb 					} while (isspace(c = getc(f1)));
110308f90673Sjfb 					do {
110408f90673Sjfb 						if (d == '\n')
110508f90673Sjfb 							break;
110608f90673Sjfb 						ctnew++;
110708f90673Sjfb 					} while (isspace(d = getc(f2)));
110808f90673Sjfb 				} else if (wflag) {
110908f90673Sjfb 					while (isspace(c) && c != '\n') {
111008f90673Sjfb 						c = getc(f1);
111108f90673Sjfb 						ctold++;
111208f90673Sjfb 					}
111308f90673Sjfb 					while (isspace(d) && d != '\n') {
111408f90673Sjfb 						d = getc(f2);
111508f90673Sjfb 						ctnew++;
111608f90673Sjfb 					}
111708f90673Sjfb 				}
111808f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
111908f90673Sjfb 					jackpot++;
112008f90673Sjfb 					J[i] = 0;
112108f90673Sjfb 					if (c != '\n' && c != EOF)
112208f90673Sjfb 						ctold += skipline(f1);
112308f90673Sjfb 					if (d != '\n' && c != EOF)
112408f90673Sjfb 						ctnew += skipline(f2);
112508f90673Sjfb 					break;
112608f90673Sjfb 				}
112708f90673Sjfb 				if (c == '\n' || c == EOF)
112808f90673Sjfb 					break;
112908f90673Sjfb 			}
113008f90673Sjfb 		} else {
113108f90673Sjfb 			for (;;) {
113208f90673Sjfb 				ctold++;
113308f90673Sjfb 				ctnew++;
113408f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
113508f90673Sjfb 					/* jackpot++; */
113608f90673Sjfb 					J[i] = 0;
113708f90673Sjfb 					if (c != '\n' && c != EOF)
113808f90673Sjfb 						ctold += skipline(f1);
113908f90673Sjfb 					if (d != '\n' && c != EOF)
114008f90673Sjfb 						ctnew += skipline(f2);
114108f90673Sjfb 					break;
114208f90673Sjfb 				}
114308f90673Sjfb 				if (c == '\n' || c == EOF)
114408f90673Sjfb 					break;
114508f90673Sjfb 			}
114608f90673Sjfb 		}
114708f90673Sjfb 		ixold[i] = ctold;
114808f90673Sjfb 		ixnew[j] = ctnew;
114908f90673Sjfb 		j++;
115008f90673Sjfb 	}
1151e4276007Sjfb 	for (; j <= diff_len[1]; j++)
115208f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
115308f90673Sjfb 	/*
115408f90673Sjfb 	 * if (jackpot)
11555ac8b1e7Sjoris 	 *	cvs_printf("jackpot\n");
115608f90673Sjfb 	 */
115708f90673Sjfb }
115808f90673Sjfb 
115908f90673Sjfb /* shellsort CACM #201 */
116008f90673Sjfb static void
116108f90673Sjfb sort(struct line *a, int n)
116208f90673Sjfb {
116308f90673Sjfb 	struct line *ai, *aim, w;
116408f90673Sjfb 	int j, m = 0, k;
116508f90673Sjfb 
116608f90673Sjfb 	if (n == 0)
116708f90673Sjfb 		return;
116808f90673Sjfb 	for (j = 1; j <= n; j *= 2)
116908f90673Sjfb 		m = 2 * j - 1;
117008f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
117108f90673Sjfb 		k = n - m;
117208f90673Sjfb 		for (j = 1; j <= k; j++) {
117308f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
117408f90673Sjfb 				aim = &ai[m];
117508f90673Sjfb 				if (aim < ai)
117608f90673Sjfb 					break;	/* wraparound */
117708f90673Sjfb 				if (aim->value > ai[0].value ||
117808f90673Sjfb 				    (aim->value == ai[0].value &&
117908f90673Sjfb 					aim->serial > ai[0].serial))
118008f90673Sjfb 					break;
118108f90673Sjfb 				w.value = ai[0].value;
118208f90673Sjfb 				ai[0].value = aim->value;
118308f90673Sjfb 				aim->value = w.value;
118408f90673Sjfb 				w.serial = ai[0].serial;
118508f90673Sjfb 				ai[0].serial = aim->serial;
118608f90673Sjfb 				aim->serial = w.serial;
118708f90673Sjfb 			}
118808f90673Sjfb 		}
118908f90673Sjfb 	}
119008f90673Sjfb }
119108f90673Sjfb 
119208f90673Sjfb static void
119308f90673Sjfb unsort(struct line *f, int l, int *b)
119408f90673Sjfb {
119508f90673Sjfb 	int *a, i;
119608f90673Sjfb 
11977f535ec4Sjfb 	if ((a = (int *)malloc((l + 1) * sizeof(int))) == NULL) {
11987f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate sort array");
11997f535ec4Sjfb 		return;
12007f535ec4Sjfb 	}
120108f90673Sjfb 	for (i = 1; i <= l; i++)
120208f90673Sjfb 		a[f[i].serial] = f[i].value;
120308f90673Sjfb 	for (i = 1; i <= l; i++)
120408f90673Sjfb 		b[i] = a[i];
120508f90673Sjfb 	free(a);
120608f90673Sjfb }
120708f90673Sjfb 
120808f90673Sjfb static int
120908f90673Sjfb skipline(FILE *f)
121008f90673Sjfb {
121108f90673Sjfb 	int i, c;
121208f90673Sjfb 
121308f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
121408f90673Sjfb 		continue;
121508f90673Sjfb 	return (i);
121608f90673Sjfb }
121708f90673Sjfb 
121808f90673Sjfb static void
121908f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2)
122008f90673Sjfb {
122108f90673Sjfb 	int m, i0, i1, j0, j1;
122208f90673Sjfb 
122308f90673Sjfb 	rewind(f1);
122408f90673Sjfb 	rewind(f2);
1225e4276007Sjfb 	m = diff_len[0];
122608f90673Sjfb 	J[0] = 0;
1227e4276007Sjfb 	J[m + 1] = diff_len[1] + 1;
122808f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
122908f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
123008f90673Sjfb 			i0++;
123108f90673Sjfb 		j0 = J[i0 - 1] + 1;
123208f90673Sjfb 		i1 = i0 - 1;
123308f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
123408f90673Sjfb 			i1++;
123508f90673Sjfb 		j1 = J[i1 + 1] - 1;
123608f90673Sjfb 		J[i1] = j1;
123708f90673Sjfb 		change(file1, f1, file2, f2, i0, i1, j0, j1);
123808f90673Sjfb 	}
123908f90673Sjfb 	if (m == 0)
1240e4276007Sjfb 		change(file1, f1, file2, f2, 1, 0, 1, diff_len[1]);
124108f90673Sjfb 	if (format == D_IFDEF) {
124208f90673Sjfb 		for (;;) {
124308f90673Sjfb #define	c i0
124408f90673Sjfb 			if ((c = getc(f1)) == EOF)
124508f90673Sjfb 				return;
12465ac8b1e7Sjoris 			cvs_putchar(c);
124708f90673Sjfb 		}
124808f90673Sjfb #undef c
124908f90673Sjfb 	}
125008f90673Sjfb 	if (anychange != 0) {
125108f90673Sjfb 		if (format == D_CONTEXT)
125208f90673Sjfb 			dump_context_vec(f1, f2);
125308f90673Sjfb 		else if (format == D_UNIFIED)
125408f90673Sjfb 			dump_unified_vec(f1, f2);
125508f90673Sjfb 	}
125608f90673Sjfb }
125708f90673Sjfb 
125808f90673Sjfb static __inline void
125908f90673Sjfb range(int a, int b, char *separator)
126008f90673Sjfb {
12615ac8b1e7Sjoris 	cvs_printf("%d", a > b ? b : a);
126208f90673Sjfb 	if (a < b)
12635ac8b1e7Sjoris 		cvs_printf("%s%d", separator, b);
126408f90673Sjfb }
126508f90673Sjfb 
126608f90673Sjfb static __inline void
126708f90673Sjfb uni_range(int a, int b)
126808f90673Sjfb {
126908f90673Sjfb 	if (a < b)
12705ac8b1e7Sjoris 		cvs_printf("%d,%d", a, b - a + 1);
127108f90673Sjfb 	else if (a == b)
12725ac8b1e7Sjoris 		cvs_printf("%d", b);
127308f90673Sjfb 	else
12745ac8b1e7Sjoris 		cvs_printf("%d,0", b);
127508f90673Sjfb }
127608f90673Sjfb 
127708f90673Sjfb static char *
12782a0de57dSjfb preadline(int fd, size_t rlen, off_t off)
127908f90673Sjfb {
128008f90673Sjfb 	char *line;
128108f90673Sjfb 	ssize_t nr;
128208f90673Sjfb 
12832a0de57dSjfb 	line = malloc(rlen + 1);
128408f90673Sjfb 	if (line == NULL) {
128508f90673Sjfb 		cvs_log(LP_ERRNO, "failed to allocate line");
128608f90673Sjfb 		return (NULL);
128708f90673Sjfb 	}
12882a0de57dSjfb 	if ((nr = pread(fd, line, rlen, off)) < 0) {
128908f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
129008f90673Sjfb 		return (NULL);
129108f90673Sjfb 	}
129208f90673Sjfb 	line[nr] = '\0';
129308f90673Sjfb 	return (line);
129408f90673Sjfb }
129508f90673Sjfb 
129608f90673Sjfb static int
129708f90673Sjfb ignoreline(char *line)
129808f90673Sjfb {
129908f90673Sjfb 	int ret;
130008f90673Sjfb 
1301f1901a5aSxsa 	ret = regexec(&ignore_re, line, (size_t)0, NULL, 0);
130208f90673Sjfb 	free(line);
130308f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
130408f90673Sjfb }
130508f90673Sjfb 
130608f90673Sjfb /*
130708f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
130808f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
130908f90673Sjfb  * are no lines in the from file involved and this means that there were
131008f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
131108f90673Sjfb  * lines missing from the to file.
131208f90673Sjfb  */
131308f90673Sjfb static void
131408f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2,
131508f90673Sjfb 	int a, int b, int c, int d)
131608f90673Sjfb {
131708f90673Sjfb 	static size_t max_context = 64;
131808f90673Sjfb 	int i;
131908f90673Sjfb 
132008f90673Sjfb 	if (format != D_IFDEF && a > b && c > d)
132108f90673Sjfb 		return;
132208f90673Sjfb 	if (ignore_pats != NULL) {
132308f90673Sjfb 		char *line;
132408f90673Sjfb 		/*
132508f90673Sjfb 		 * All lines in the change, insert, or delete must
132608f90673Sjfb 		 * match an ignore pattern for the change to be
132708f90673Sjfb 		 * ignored.
132808f90673Sjfb 		 */
132908f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
133008f90673Sjfb 			for (i = a; i <= b; i++) {
133108f90673Sjfb 				line = preadline(fileno(f1),
133208f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
133308f90673Sjfb 				if (!ignoreline(line))
133408f90673Sjfb 					goto proceed;
133508f90673Sjfb 			}
133608f90673Sjfb 		}
133708f90673Sjfb 		if (a > b || c <= d) {	/* Changes and inserts. */
133808f90673Sjfb 			for (i = c; i <= d; i++) {
133908f90673Sjfb 				line = preadline(fileno(f2),
134008f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
134108f90673Sjfb 				if (!ignoreline(line))
134208f90673Sjfb 					goto proceed;
134308f90673Sjfb 			}
134408f90673Sjfb 		}
134508f90673Sjfb 		return;
134608f90673Sjfb 	}
134708f90673Sjfb proceed:
134808f90673Sjfb 	if (format == D_CONTEXT || format == D_UNIFIED) {
134908f90673Sjfb 		/*
135008f90673Sjfb 		 * Allocate change records as needed.
135108f90673Sjfb 		 */
135208f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
135318501190Sniallo 			struct context_vec *tmp;
135408f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
135508f90673Sjfb 			max_context <<= 1;
1356*4c06e5f6Sreyk 			if ((tmp = realloc(context_vec_start, max_context *
1357*4c06e5f6Sreyk 			    sizeof(struct context_vec))) == NULL) {
135818501190Sniallo 				free(context_vec_start);
135918501190Sniallo 				context_vec_start = NULL;
136018501190Sniallo 				cvs_log(LP_ERRNO,
136118501190Sniallo 				    "failed to resize context_vec_start");
136218501190Sniallo 				return;
136318501190Sniallo 			}
136418501190Sniallo 			context_vec_start = tmp;
136508f90673Sjfb 			context_vec_end = context_vec_start + max_context;
136608f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
136708f90673Sjfb 		}
136808f90673Sjfb 		if (anychange == 0) {
136908f90673Sjfb 			/*
137008f90673Sjfb 			 * Print the context/unidiff header first time through.
137108f90673Sjfb 			 */
13725ac8b1e7Sjoris 			cvs_printf("%s %s	%s",
137308f90673Sjfb 			    format == D_CONTEXT ? "***" : "---", diff_file,
137408f90673Sjfb 			    ctime(&stb1.st_mtime));
13755ac8b1e7Sjoris 			cvs_printf("%s %s	%s",
137608f90673Sjfb 			    format == D_CONTEXT ? "---" : "+++", diff_file,
137708f90673Sjfb 			    ctime(&stb2.st_mtime));
137808f90673Sjfb 			anychange = 1;
137908f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
138008f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
138108f90673Sjfb 			/*
138208f90673Sjfb 			 * If this change is more than 'context' lines from the
138308f90673Sjfb 			 * previous change, dump the record and reset it.
138408f90673Sjfb 			 */
138508f90673Sjfb 			if (format == D_CONTEXT)
138608f90673Sjfb 				dump_context_vec(f1, f2);
138708f90673Sjfb 			else
138808f90673Sjfb 				dump_unified_vec(f1, f2);
138908f90673Sjfb 		}
139008f90673Sjfb 		context_vec_ptr++;
139108f90673Sjfb 		context_vec_ptr->a = a;
139208f90673Sjfb 		context_vec_ptr->b = b;
139308f90673Sjfb 		context_vec_ptr->c = c;
139408f90673Sjfb 		context_vec_ptr->d = d;
139508f90673Sjfb 		return;
139608f90673Sjfb 	}
139708f90673Sjfb 	if (anychange == 0)
139808f90673Sjfb 		anychange = 1;
139908f90673Sjfb 	switch (format) {
140008f90673Sjfb 	case D_BRIEF:
140108f90673Sjfb 		return;
140208f90673Sjfb 	case D_NORMAL:
140308f90673Sjfb 		range(a, b, ",");
14045ac8b1e7Sjoris 		cvs_putchar(a > b ? 'a' : c > d ? 'd' : 'c');
140508f90673Sjfb 		if (format == D_NORMAL)
140608f90673Sjfb 			range(c, d, ",");
14075ac8b1e7Sjoris 		cvs_putchar('\n');
140808f90673Sjfb 		break;
1409394180a4Sjfb 	case D_RCSDIFF:
1410394180a4Sjfb 		if (a > b)
14115ac8b1e7Sjoris 			cvs_printf("a%d %d\n", b, d - c + 1);
1412394180a4Sjfb 		else {
14135ac8b1e7Sjoris 			cvs_printf("d%d %d\n", a, b - a + 1);
1414394180a4Sjfb 
1415394180a4Sjfb 			if (!(c > d))	/* add changed lines */
14165ac8b1e7Sjoris 				cvs_printf("a%d %d\n", b, d - c + 1);
1417394180a4Sjfb 		}
1418394180a4Sjfb 		break;
141908f90673Sjfb 	}
142008f90673Sjfb 	if (format == D_NORMAL || format == D_IFDEF) {
142108f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
142208f90673Sjfb 		if (a <= b && c <= d && format == D_NORMAL)
142308f90673Sjfb 			puts("---");
142408f90673Sjfb 	}
142508f90673Sjfb 	i = fetch(ixnew, c, d, f2, format == D_NORMAL ? '>' : '\0', 0);
142608f90673Sjfb 	if (inifdef) {
14275ac8b1e7Sjoris 		cvs_printf("#endif /* %s */\n", ifdefname);
142808f90673Sjfb 		inifdef = 0;
142908f90673Sjfb 	}
143008f90673Sjfb }
143108f90673Sjfb 
143208f90673Sjfb static int
143308f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
143408f90673Sjfb {
143508f90673Sjfb 	int i, j, c, lastc, col, nc;
143608f90673Sjfb 
143708f90673Sjfb 	/*
143808f90673Sjfb 	 * When doing #ifdef's, copy down to current line
143908f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
144008f90673Sjfb 	 */
144108f90673Sjfb 	if (format == D_IFDEF && oldfile) {
144208f90673Sjfb 		long curpos = ftell(lb);
144308f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
144408f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
144508f90673Sjfb 		for (i = 0; i < nc; i++)
14465ac8b1e7Sjoris 			cvs_putchar(getc(lb));
144708f90673Sjfb 	}
144808f90673Sjfb 	if (a > b)
144908f90673Sjfb 		return (0);
145008f90673Sjfb 	if (format == D_IFDEF) {
145108f90673Sjfb 		if (inifdef) {
14525ac8b1e7Sjoris 			cvs_printf("#else /* %s%s */\n",
145308f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
145408f90673Sjfb 		} else {
145508f90673Sjfb 			if (oldfile)
14565ac8b1e7Sjoris 				cvs_printf("#ifndef %s\n", ifdefname);
145708f90673Sjfb 			else
14585ac8b1e7Sjoris 				cvs_printf("#ifdef %s\n", ifdefname);
145908f90673Sjfb 		}
146008f90673Sjfb 		inifdef = 1 + oldfile;
146108f90673Sjfb 	}
146208f90673Sjfb 	for (i = a; i <= b; i++) {
146308f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
146408f90673Sjfb 		nc = f[i] - f[i - 1];
146508f90673Sjfb 		if (format != D_IFDEF && ch != '\0') {
14665ac8b1e7Sjoris 			cvs_putchar(ch);
146708f90673Sjfb 			if (Tflag && (format == D_NORMAL || format == D_CONTEXT
146808f90673Sjfb 			    || format == D_UNIFIED))
14695ac8b1e7Sjoris 				cvs_putchar('\t');
147008f90673Sjfb 			else if (format != D_UNIFIED)
14715ac8b1e7Sjoris 				cvs_putchar(' ');
147208f90673Sjfb 		}
147308f90673Sjfb 		col = 0;
147408f90673Sjfb 		for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
147508f90673Sjfb 			if ((c = getc(lb)) == EOF) {
1476394180a4Sjfb 				if (format == D_RCSDIFF)
1477394180a4Sjfb 					warnx("No newline at end of file");
1478394180a4Sjfb 				else
147908f90673Sjfb 					puts("\n\\ No newline at end of file");
148008f90673Sjfb 				return (0);
148108f90673Sjfb 			}
148208f90673Sjfb 			if (c == '\t' && tflag) {
148308f90673Sjfb 				do {
14845ac8b1e7Sjoris 					cvs_putchar(' ');
148508f90673Sjfb 				} while (++col & 7);
148608f90673Sjfb 			} else {
14875ac8b1e7Sjoris 				cvs_putchar(c);
148808f90673Sjfb 				col++;
148908f90673Sjfb 			}
149008f90673Sjfb 		}
149108f90673Sjfb 	}
149208f90673Sjfb 	return (0);
149308f90673Sjfb }
149408f90673Sjfb 
149508f90673Sjfb /*
149608f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
149708f90673Sjfb  */
149808f90673Sjfb static int
149908f90673Sjfb readhash(FILE *f)
150008f90673Sjfb {
150108f90673Sjfb 	int i, t, space;
150208f90673Sjfb 	int sum;
150308f90673Sjfb 
150408f90673Sjfb 	sum = 1;
150508f90673Sjfb 	space = 0;
150608f90673Sjfb 	if (!bflag && !wflag) {
150708f90673Sjfb 		if (iflag)
150808f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
150908f90673Sjfb 				if (t == EOF) {
151008f90673Sjfb 					if (i == 0)
151108f90673Sjfb 						return (0);
151208f90673Sjfb 					break;
151308f90673Sjfb 				}
151408f90673Sjfb 				sum = sum * 127 + chrtran[t];
151508f90673Sjfb 			}
151608f90673Sjfb 		else
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 + t;
152408f90673Sjfb 			}
152508f90673Sjfb 	} else {
152608f90673Sjfb 		for (i = 0;;) {
152708f90673Sjfb 			switch (t = getc(f)) {
152808f90673Sjfb 			case '\t':
152908f90673Sjfb 			case ' ':
153008f90673Sjfb 				space++;
153108f90673Sjfb 				continue;
153208f90673Sjfb 			default:
153308f90673Sjfb 				if (space && !wflag) {
153408f90673Sjfb 					i++;
153508f90673Sjfb 					space = 0;
153608f90673Sjfb 				}
153708f90673Sjfb 				sum = sum * 127 + chrtran[t];
153808f90673Sjfb 				i++;
153908f90673Sjfb 				continue;
154008f90673Sjfb 			case EOF:
154108f90673Sjfb 				if (i == 0)
154208f90673Sjfb 					return (0);
154308f90673Sjfb 				/* FALLTHROUGH */
154408f90673Sjfb 			case '\n':
154508f90673Sjfb 				break;
154608f90673Sjfb 			}
154708f90673Sjfb 			break;
154808f90673Sjfb 		}
154908f90673Sjfb 	}
155008f90673Sjfb 	/*
155108f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
155208f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
155308f90673Sjfb 	 */
155408f90673Sjfb 	return (sum == 0 ? 1 : sum);
155508f90673Sjfb }
155608f90673Sjfb 
155708f90673Sjfb static int
155808f90673Sjfb asciifile(FILE *f)
155908f90673Sjfb {
156008f90673Sjfb 	char buf[BUFSIZ];
156108f90673Sjfb 	int i, cnt;
156208f90673Sjfb 
156308f90673Sjfb 	if (aflag || f == NULL)
156408f90673Sjfb 		return (1);
156508f90673Sjfb 
156608f90673Sjfb 	rewind(f);
1567f1901a5aSxsa 	cnt = fread(buf, (size_t)1, sizeof(buf), f);
156808f90673Sjfb 	for (i = 0; i < cnt; i++)
156908f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
157008f90673Sjfb 			return (0);
157108f90673Sjfb 	return (1);
157208f90673Sjfb }
157308f90673Sjfb 
15745e78344dSjfb static char*
15755e78344dSjfb match_function(const long *f, int pos, FILE *fp)
15765e78344dSjfb {
15775e78344dSjfb 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
15785e78344dSjfb 	size_t nc;
15795e78344dSjfb 	int last = lastline;
15805e78344dSjfb 	char *p;
15815e78344dSjfb 
15825e78344dSjfb 	lastline = pos;
15835e78344dSjfb 	while (pos > last) {
15845e78344dSjfb 		fseek(fp, f[pos - 1], SEEK_SET);
15855e78344dSjfb 		nc = f[pos] - f[pos - 1];
15865e78344dSjfb 		if (nc >= sizeof(buf))
15875e78344dSjfb 			nc = sizeof(buf) - 1;
1588f1901a5aSxsa 		nc = fread(buf, (size_t)1, nc, fp);
15895e78344dSjfb 		if (nc > 0) {
15905e78344dSjfb 			buf[nc] = '\0';
1591634926d6Sniallo 			p = strchr((const char *)buf, '\n');
15925e78344dSjfb 			if (p != NULL)
15935e78344dSjfb 				*p = '\0';
15945e78344dSjfb 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
1595*4c06e5f6Sreyk 				strlcpy(lastbuf, (const char *)buf,
1596*4c06e5f6Sreyk 				    sizeof lastbuf);
15975e78344dSjfb 				lastmatchline = pos;
15985e78344dSjfb 				return lastbuf;
15995e78344dSjfb 			}
16005e78344dSjfb 		}
16015e78344dSjfb 		pos--;
16025e78344dSjfb 	}
16035e78344dSjfb 	return (lastmatchline > 0) ? lastbuf : NULL;
16045e78344dSjfb }
16055e78344dSjfb 
160608f90673Sjfb 
160708f90673Sjfb /* dump accumulated "context" diff changes */
160808f90673Sjfb static void
160908f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
161008f90673Sjfb {
161108f90673Sjfb 	struct context_vec *cvp = context_vec_start;
161208f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
161308f90673Sjfb 	int a, b, c, d;
16145e78344dSjfb 	char ch, *f;
161508f90673Sjfb 
161608f90673Sjfb 	if (context_vec_start > context_vec_ptr)
161708f90673Sjfb 		return;
161808f90673Sjfb 
161908f90673Sjfb 	b = d = 0;		/* gcc */
1620dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1621e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1622dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1623e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
162408f90673Sjfb 
16255ac8b1e7Sjoris 	cvs_printf("***************");
16265e78344dSjfb 	if (pflag) {
16275e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
16285e78344dSjfb 		if (f != NULL) {
16295ac8b1e7Sjoris 			cvs_putchar(' ');
16305ac8b1e7Sjoris 			cvs_printf("%s", f);
16315e78344dSjfb 		}
16325e78344dSjfb 	}
16335ac8b1e7Sjoris 	cvs_printf("\n*** ");
163408f90673Sjfb 	range(lowa, upb, ",");
16355ac8b1e7Sjoris 	cvs_printf(" ****\n");
163608f90673Sjfb 
163708f90673Sjfb 	/*
163808f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
163908f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
164008f90673Sjfb 	 * the "old" lines as context in the "new" list).
164108f90673Sjfb 	 */
164208f90673Sjfb 	do_output = 0;
164308f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
164408f90673Sjfb 		if (cvp->a <= cvp->b) {
164508f90673Sjfb 			cvp = context_vec_start;
164608f90673Sjfb 			do_output++;
164708f90673Sjfb 			break;
164808f90673Sjfb 		}
164908f90673Sjfb 	if (do_output) {
165008f90673Sjfb 		while (cvp <= context_vec_ptr) {
165108f90673Sjfb 			a = cvp->a;
165208f90673Sjfb 			b = cvp->b;
165308f90673Sjfb 			c = cvp->c;
165408f90673Sjfb 			d = cvp->d;
165508f90673Sjfb 
165608f90673Sjfb 			if (a <= b && c <= d)
165708f90673Sjfb 				ch = 'c';
165808f90673Sjfb 			else
165908f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
166008f90673Sjfb 
166108f90673Sjfb 			if (ch == 'a')
166208f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
166308f90673Sjfb 			else {
166408f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
166508f90673Sjfb 				fetch(ixold, a, b, f1,
166608f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
166708f90673Sjfb 			}
166808f90673Sjfb 			lowa = b + 1;
166908f90673Sjfb 			cvp++;
167008f90673Sjfb 		}
167108f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
167208f90673Sjfb 	}
167308f90673Sjfb 	/* output changes to the "new" file */
16745ac8b1e7Sjoris 	cvs_printf("--- ");
167508f90673Sjfb 	range(lowc, upd, ",");
16765ac8b1e7Sjoris 	cvs_printf(" ----\n");
167708f90673Sjfb 
167808f90673Sjfb 	do_output = 0;
167908f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
168008f90673Sjfb 		if (cvp->c <= cvp->d) {
168108f90673Sjfb 			cvp = context_vec_start;
168208f90673Sjfb 			do_output++;
168308f90673Sjfb 			break;
168408f90673Sjfb 		}
168508f90673Sjfb 	if (do_output) {
168608f90673Sjfb 		while (cvp <= context_vec_ptr) {
168708f90673Sjfb 			a = cvp->a;
168808f90673Sjfb 			b = cvp->b;
168908f90673Sjfb 			c = cvp->c;
169008f90673Sjfb 			d = cvp->d;
169108f90673Sjfb 
169208f90673Sjfb 			if (a <= b && c <= d)
169308f90673Sjfb 				ch = 'c';
169408f90673Sjfb 			else
169508f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
169608f90673Sjfb 
169708f90673Sjfb 			if (ch == 'd')
169808f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
169908f90673Sjfb 			else {
170008f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
170108f90673Sjfb 				fetch(ixnew, c, d, f2,
170208f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
170308f90673Sjfb 			}
170408f90673Sjfb 			lowc = d + 1;
170508f90673Sjfb 			cvp++;
170608f90673Sjfb 		}
170708f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
170808f90673Sjfb 	}
170908f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
171008f90673Sjfb }
171108f90673Sjfb 
171208f90673Sjfb /* dump accumulated "unified" diff changes */
171308f90673Sjfb static void
171408f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
171508f90673Sjfb {
171608f90673Sjfb 	struct context_vec *cvp = context_vec_start;
171708f90673Sjfb 	int lowa, upb, lowc, upd;
171808f90673Sjfb 	int a, b, c, d;
17195e78344dSjfb 	char ch, *f;
172008f90673Sjfb 
172108f90673Sjfb 	if (context_vec_start > context_vec_ptr)
172208f90673Sjfb 		return;
172308f90673Sjfb 
172408f90673Sjfb 	b = d = 0;		/* gcc */
1725dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1726e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1727dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1728e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
172908f90673Sjfb 
17305ac8b1e7Sjoris 	cvs_printf("@@ -");
173108f90673Sjfb 	uni_range(lowa, upb);
17325ac8b1e7Sjoris 	cvs_printf(" +");
173308f90673Sjfb 	uni_range(lowc, upd);
17345ac8b1e7Sjoris 	cvs_printf(" @@");
17355e78344dSjfb 	if (pflag) {
17365e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
17375e78344dSjfb 		if (f != NULL) {
17385ac8b1e7Sjoris 			cvs_putchar(' ');
17395ac8b1e7Sjoris 			cvs_printf("%s", f);
17405e78344dSjfb 		}
17415e78344dSjfb 	}
17425ac8b1e7Sjoris 	cvs_putchar('\n');
174308f90673Sjfb 
174408f90673Sjfb 	/*
174508f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
174608f90673Sjfb 	 * are printed together.
174708f90673Sjfb 	 */
174808f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
174908f90673Sjfb 		a = cvp->a;
175008f90673Sjfb 		b = cvp->b;
175108f90673Sjfb 		c = cvp->c;
175208f90673Sjfb 		d = cvp->d;
175308f90673Sjfb 
175408f90673Sjfb 		/*
175508f90673Sjfb 		 * c: both new and old changes
175608f90673Sjfb 		 * d: only changes in the old file
175708f90673Sjfb 		 * a: only changes in the new file
175808f90673Sjfb 		 */
175908f90673Sjfb 		if (a <= b && c <= d)
176008f90673Sjfb 			ch = 'c';
176108f90673Sjfb 		else
176208f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
176308f90673Sjfb 
176408f90673Sjfb 		switch (ch) {
176508f90673Sjfb 		case 'c':
176608f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
176708f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
176808f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
176908f90673Sjfb 			break;
177008f90673Sjfb 		case 'd':
177108f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
177208f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
177308f90673Sjfb 			break;
177408f90673Sjfb 		case 'a':
177508f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
177608f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
177708f90673Sjfb 			break;
177808f90673Sjfb 		}
177908f90673Sjfb 		lowa = b + 1;
178008f90673Sjfb 		lowc = d + 1;
178108f90673Sjfb 	}
178208f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
178308f90673Sjfb 
178408f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
178508f90673Sjfb }
1786