xref: /openbsd-src/usr.bin/cvs/diff.c (revision f9b67873f046ea8e270a4e0d6879f7aa9dcd6bfe)
1*f9b67873Sniallo /*	$OpenBSD: diff.c,v 1.58 2005/10/07 23:59:56 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 
188*f9b67873Sniallo static void	 diff_output(const char *, ...);
18908f90673Sjfb static void	 output(const char *, FILE *, const char *, FILE *);
19008f90673Sjfb static void	 check(FILE *, FILE *);
19108f90673Sjfb static void	 range(int, int, char *);
19208f90673Sjfb static void	 uni_range(int, int);
19308f90673Sjfb static void	 dump_context_vec(FILE *, FILE *);
19408f90673Sjfb static void	 dump_unified_vec(FILE *, FILE *);
1957f535ec4Sjfb static int	 prepare(int, FILE *, off_t);
19608f90673Sjfb static void	 prune(void);
19708f90673Sjfb static void	 equiv(struct line *, int, struct line *, int, int *);
19808f90673Sjfb static void	 unravel(int);
19908f90673Sjfb static void	 unsort(struct line *, int, int *);
200ff1f7a8eSxsa static void	 change(const char *, FILE *, const char *, FILE *, int,
201ff1f7a8eSxsa 		    int, int, int);
20208f90673Sjfb static void	 sort(struct line *, int);
20308f90673Sjfb static int	 ignoreline(char *);
20408f90673Sjfb static int	 asciifile(FILE *);
20508f90673Sjfb static int	 fetch(long *, int, int, FILE *, int, int);
20608f90673Sjfb static int	 newcand(int, int, int);
20708f90673Sjfb static int	 search(int *, int, int);
20808f90673Sjfb static int	 skipline(FILE *);
20908f90673Sjfb static int	 isqrt(int);
21008f90673Sjfb static int	 stone(int *, int, int *, int *);
21108f90673Sjfb static int	 readhash(FILE *);
21208f90673Sjfb static int	 files_differ(FILE *, FILE *);
2135e78344dSjfb static char	*match_function(const long *, int, FILE *);
21408f90673Sjfb static char	*preadline(int, size_t, off_t);
21508f90673Sjfb 
21608f90673Sjfb 
217af5bb824Sniallo #if !defined(RCSPROG)
218af5bb824Sniallo static int Nflag;
219af5bb824Sniallo static char diffargs[128];
220af5bb824Sniallo #endif
221af5bb824Sniallo static int aflag, bflag, dflag, iflag, pflag, tflag, Tflag, wflag;
222ece76a70Sjoris static int context;
223*f9b67873Sniallo int diff_format = D_NORMAL;
22408f90673Sjfb static struct stat stb1, stb2;
225af5bb824Sniallo static char *ifdefname, *ignore_pats;
226f5638424Sjfb static const char *diff_file;
22708f90673Sjfb regex_t ignore_re;
22808f90673Sjfb 
22908f90673Sjfb static int  *J;			/* will be overlaid on class */
23008f90673Sjfb static int  *class;		/* will be overlaid on file[0] */
23108f90673Sjfb static int  *klist;		/* will be overlaid on file[0] after class */
23208f90673Sjfb static int  *member;		/* will be overlaid on file[1] */
23308f90673Sjfb static int   clen;
23408f90673Sjfb static int   inifdef;		/* whether or not we are in a #ifdef block */
235e4276007Sjfb static int   diff_len[2];
23608f90673Sjfb static int   pref, suff;	/* length of prefix and suffix */
23708f90673Sjfb static int   slen[2];
23808f90673Sjfb static int   anychange;
23908f90673Sjfb static long *ixnew;		/* will be overlaid on file[1] */
24008f90673Sjfb static long *ixold;		/* will be overlaid on klist */
24108f90673Sjfb static struct cand *clist;	/* merely a free storage pot for candidates */
24208f90673Sjfb static int   clistlen;		/* the length of clist */
24308f90673Sjfb static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
24408f90673Sjfb static u_char *chrtran;		/* translation table for case-folding */
24508f90673Sjfb static struct context_vec *context_vec_start;
24608f90673Sjfb static struct context_vec *context_vec_end;
24708f90673Sjfb static struct context_vec *context_vec_ptr;
24808f90673Sjfb 
24908f90673Sjfb #define FUNCTION_CONTEXT_SIZE	41
2505e78344dSjfb static char lastbuf[FUNCTION_CONTEXT_SIZE];
25108f90673Sjfb static int  lastline;
25208f90673Sjfb static int  lastmatchline;
253*f9b67873Sniallo static BUF  *diffbuf = NULL;
254*f9b67873Sniallo 
25508f90673Sjfb /*
25608f90673Sjfb  * chrtran points to one of 2 translation tables: cup2low if folding upper to
25708f90673Sjfb  * lower case clow2low if not folding case
25808f90673Sjfb  */
25908f90673Sjfb u_char clow2low[256] = {
26008f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
26108f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
26208f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
26308f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
26408f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
26508f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
26608f90673Sjfb 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
26708f90673Sjfb 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
26808f90673Sjfb 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
26908f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
27008f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
27108f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
27208f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
27308f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
27408f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
27508f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
27608f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
27708f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
27808f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
27908f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
28008f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
28108f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
28208f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
28308f90673Sjfb 	0xfd, 0xfe, 0xff
28408f90673Sjfb };
28508f90673Sjfb 
28608f90673Sjfb u_char cup2low[256] = {
28708f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
28808f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
28908f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
29008f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
29108f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
29208f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
29308f90673Sjfb 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
29408f90673Sjfb 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
29508f90673Sjfb 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
29608f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
29708f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
29808f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
29908f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
30008f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
30108f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
30208f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
30308f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
30408f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
30508f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
30608f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
30708f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
30808f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
30908f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
31008f90673Sjfb 	0xfd, 0xfe, 0xff
31108f90673Sjfb };
31208f90673Sjfb 
313af5bb824Sniallo #if !defined(RCSPROG)
314e4276007Sjfb struct cvs_cmd cvs_cmd_diff = {
315e4276007Sjfb 	CVS_OP_DIFF, CVS_REQ_DIFF, "diff",
316e4276007Sjfb 	{ "di", "dif" },
317e4276007Sjfb 	"Show differences between revisions",
318c9150269Sxsa 	"[-cilNnpu] [[-D date] [-r rev] [-D date2 | -r rev2]] "
319c9150269Sxsa 	"[-k mode] [file ...]",
320c9150269Sxsa 	"cD:iklNnpr:Ru",
32116cfc147Sjoris 	NULL,
32216cfc147Sjoris 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
323e4276007Sjfb 	cvs_diff_init,
324e4276007Sjfb 	cvs_diff_pre_exec,
325e4276007Sjfb 	cvs_diff_remote,
326e4276007Sjfb 	cvs_diff_local,
327e4276007Sjfb 	NULL,
328e4276007Sjfb 	cvs_diff_cleanup,
329e4276007Sjfb 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
330e4276007Sjfb };
331e4276007Sjfb 
332e4276007Sjfb 
333e4276007Sjfb struct cvs_cmd cvs_cmd_rdiff = {
334e4276007Sjfb 	CVS_OP_RDIFF, CVS_REQ_DIFF, "rdiff",
335c9150269Sxsa 	{ "pa", "patch" },
336e4276007Sjfb 	"Create 'patch' format diffs between releases",
337c9150269Sxsa 	"[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev "
338c9150269Sxsa 	"[-D date2 | -rev2] module ...",
339c9150269Sxsa 	"cD:flRr:stuV:",
340e4276007Sjfb 	NULL,
341e4276007Sjfb 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
342e4276007Sjfb 	cvs_diff_init,
343e4276007Sjfb 	cvs_diff_pre_exec,
344e4276007Sjfb 	cvs_diff_remote,
345e4276007Sjfb 	cvs_diff_local,
346e4276007Sjfb 	NULL,
347e4276007Sjfb 	cvs_diff_cleanup,
34844381dcbSjoris 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
34916cfc147Sjoris };
350af5bb824Sniallo #endif
35108f90673Sjfb 
352af5bb824Sniallo #if !defined(RCSPROG)
35316cfc147Sjoris static struct diff_arg *dap = NULL;
35416cfc147Sjoris static int recurse;
35516cfc147Sjoris 
356e4276007Sjfb static int
357e4276007Sjfb cvs_diff_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg)
35808f90673Sjfb {
35916cfc147Sjoris 	int ch;
36008f90673Sjfb 
36116cfc147Sjoris 	dap = (struct diff_arg *)malloc(sizeof(*dap));
36216cfc147Sjoris 	if (dap == NULL)
36331274bbfSjoris 		return (CVS_EX_DATA);
36416cfc147Sjoris 	dap->date1 = dap->date2 = dap->rev1 = dap->rev2 = NULL;
365dc6a6879Sjfb 	strlcpy(diffargs, argv[0], sizeof(diffargs));
366dc6a6879Sjfb 
367e4276007Sjfb 	while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) {
36808f90673Sjfb 		switch (ch) {
36908f90673Sjfb 		case 'c':
370f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
371*f9b67873Sniallo 			diff_format = D_CONTEXT;
37208f90673Sjfb 			break;
37308f90673Sjfb 		case 'D':
37416cfc147Sjoris 			if (dap->date1 == NULL && dap->rev1 == NULL) {
37516cfc147Sjoris 				dap->date1 = optarg;
37616cfc147Sjoris 			} else if (dap->date2 == NULL && dap->rev2 == NULL) {
37716cfc147Sjoris 				dap->date2 = optarg;
37816cfc147Sjoris 			} else {
37908f90673Sjfb 				cvs_log(LP_ERR,
38008f90673Sjfb 				    "no more than two revisions/dates can "
38108f90673Sjfb 				    "be specified");
38208f90673Sjfb 			}
38308f90673Sjfb 			break;
38408f90673Sjfb 		case 'l':
385f5638424Sjfb 			strlcat(diffargs, " -l", sizeof(diffargs));
38608f90673Sjfb 			recurse = 0;
387e4276007Sjfb 			cvs_cmd_diff.file_flags &= ~CF_RECURSE;
38808f90673Sjfb 			break;
38908f90673Sjfb 		case 'i':
390f5638424Sjfb 			strlcat(diffargs, " -i", sizeof(diffargs));
39108f90673Sjfb 			iflag = 1;
39208f90673Sjfb 			break;
393c710bc5aSjfb 		case 'N':
394c710bc5aSjfb 			strlcat(diffargs, " -N", sizeof(diffargs));
395c710bc5aSjfb 			Nflag = 1;
396c710bc5aSjfb 			break;
397394180a4Sjfb 		case 'n':
398394180a4Sjfb 			strlcat(diffargs, " -n", sizeof(diffargs));
399*f9b67873Sniallo 			diff_format = D_RCSDIFF;
400394180a4Sjfb 			break;
4015e78344dSjfb 		case 'p':
4025e78344dSjfb 			strlcat(diffargs, " -p", sizeof(diffargs));
4035e78344dSjfb 			pflag = 1;
4045e78344dSjfb 			break;
40508f90673Sjfb 		case 'r':
40616cfc147Sjoris 			if ((dap->rev1 == NULL) && (dap->date1 == NULL)) {
40716cfc147Sjoris 				dap->rev1 = optarg;
40816cfc147Sjoris 			} else if ((dap->rev2 == NULL) &&
40916cfc147Sjoris 			    (dap->date2 == NULL)) {
41016cfc147Sjoris 				dap->rev2 = optarg;
41116cfc147Sjoris 			} else {
41208f90673Sjfb 				cvs_log(LP_ERR,
41308f90673Sjfb 				    "no more than two revisions/dates can "
41408f90673Sjfb 				    "be specified");
41531274bbfSjoris 				return (CVS_EX_USAGE);
41608f90673Sjfb 			}
41708f90673Sjfb 			break;
418f203c484Sjoris 		case 'R':
419e4276007Sjfb 			cvs_cmd_diff.file_flags |= CF_RECURSE;
420f203c484Sjoris 			break;
42108f90673Sjfb 		case 'u':
422f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
423*f9b67873Sniallo 			diff_format = D_UNIFIED;
42408f90673Sjfb 			break;
42508f90673Sjfb 		default:
42631274bbfSjoris 			return (CVS_EX_USAGE);
42708f90673Sjfb 		}
42808f90673Sjfb 	}
42908f90673Sjfb 
43016cfc147Sjoris 	*arg = optind;
431dc6a6879Sjfb 	return (0);
432dc6a6879Sjfb }
433dc6a6879Sjfb 
43416cfc147Sjoris int
43516cfc147Sjoris cvs_diff_cleanup(void)
43616cfc147Sjoris {
437e4276007Sjfb 	if (dap != NULL) {
43816cfc147Sjoris 		free(dap);
439e4276007Sjfb 		dap = NULL;
440e4276007Sjfb 	}
44116cfc147Sjoris 	return (0);
44216cfc147Sjoris }
443dc6a6879Sjfb 
444dc6a6879Sjfb /*
445e4276007Sjfb  * cvs_diff_pre_exec()
446dc6a6879Sjfb  *
447dc6a6879Sjfb  */
448dc6a6879Sjfb int
449e4276007Sjfb cvs_diff_pre_exec(struct cvsroot *root)
450dc6a6879Sjfb {
451e4276007Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
45208f90673Sjfb 		/* send the flags */
4535e78344dSjfb 		if (Nflag && (cvs_sendarg(root, "-N", 0) < 0))
45431274bbfSjoris 			return (CVS_EX_PROTO);
4555e78344dSjfb 		if (pflag && (cvs_sendarg(root, "-p", 0) < 0))
45631274bbfSjoris 			return (CVS_EX_PROTO);
4575e78344dSjfb 
458*f9b67873Sniallo 		if (diff_format == D_CONTEXT) {
459610801e8Sxsa 			if (cvs_sendarg(root, "-c", 0) < 0)
460610801e8Sxsa 				return (CVS_EX_PROTO);
461*f9b67873Sniallo 		} else if (diff_format == D_UNIFIED) {
462610801e8Sxsa 			if (cvs_sendarg(root, "-u", 0) < 0)
463610801e8Sxsa 				return (CVS_EX_PROTO);
464610801e8Sxsa 		}
46508f90673Sjfb 
466dc6a6879Sjfb 		if (dap->rev1 != NULL) {
467610801e8Sxsa 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
468610801e8Sxsa 			    (cvs_sendarg(root, dap->rev1, 0) < 0))
469610801e8Sxsa 				return (CVS_EX_PROTO);
4703917c9bfSderaadt 		} else if (dap->date1 != NULL) {
471610801e8Sxsa 			if ((cvs_sendarg(root, "-D", 0) < 0) ||
472610801e8Sxsa 			    (cvs_sendarg(root, dap->date1, 0) < 0))
473610801e8Sxsa 				return (CVS_EX_PROTO);
47408f90673Sjfb 		}
475dc6a6879Sjfb 		if (dap->rev2 != NULL) {
476610801e8Sxsa 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
477610801e8Sxsa 			    (cvs_sendarg(root, dap->rev2, 0) < 0))
478610801e8Sxsa 				return (CVS_EX_PROTO);
4793917c9bfSderaadt 		} else if (dap->date2 != NULL) {
480610801e8Sxsa 			if ((cvs_sendarg(root, "-D", 0) < 0) ||
481610801e8Sxsa 			    (cvs_sendarg(root, dap->date2, 0) < 0))
482610801e8Sxsa 				return  (CVS_EX_PROTO);
48308f90673Sjfb 		}
484e4276007Sjfb 	}
48508f90673Sjfb 
48608f90673Sjfb 	return (0);
48708f90673Sjfb }
48808f90673Sjfb 
48908f90673Sjfb 
49008f90673Sjfb /*
49108f90673Sjfb  * cvs_diff_file()
49208f90673Sjfb  *
49308f90673Sjfb  * Diff a single file.
49408f90673Sjfb  */
495e4276007Sjfb static int
496e4276007Sjfb cvs_diff_remote(struct cvs_file *cfp, void *arg)
49708f90673Sjfb {
498e4276007Sjfb 	char *dir, *repo;
499e4276007Sjfb 	char fpath[MAXPATHLEN], dfpath[MAXPATHLEN];
500dc6a6879Sjfb 	struct cvsroot *root;
501dc6a6879Sjfb 
502dc6a6879Sjfb 	if (cfp->cf_type == DT_DIR) {
503895e6cf6Sjfb 		if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
504bb029937Sjfb 			root = cfp->cf_parent->cf_root;
505b904ba2eSjoris 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
5063917c9bfSderaadt 		} else {
507bb029937Sjfb 			root = cfp->cf_root;
50816cfc147Sjoris #if 0
509dc6a6879Sjfb 			if ((cfp->cf_parent == NULL) ||
510bb029937Sjfb 			    (root != cfp->cf_parent->cf_root)) {
511dc6a6879Sjfb 				cvs_connect(root);
512e4276007Sjfb 				cvs_diff_pre_exec(root);
513dc6a6879Sjfb 			}
51416cfc147Sjoris #endif
515dc6a6879Sjfb 
516dc6a6879Sjfb 			cvs_senddir(root, cfp);
517895e6cf6Sjfb 		}
518895e6cf6Sjfb 
519dc6a6879Sjfb 		return (0);
520dc6a6879Sjfb 	}
52108f90673Sjfb 
5222d5b8b1dSjfb 	if (cfp->cf_cvstat == CVS_FST_LOST) {
523b904ba2eSjoris 		cvs_log(LP_WARN, "cannot find file %s", cfp->cf_name);
5242d5b8b1dSjfb 		return (0);
5252d5b8b1dSjfb 	}
5262d5b8b1dSjfb 
527c710bc5aSjfb 	diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath));
528895e6cf6Sjfb 
529dc6a6879Sjfb 	if (cfp->cf_parent != NULL) {
530c710bc5aSjfb 		dir = cvs_file_getpath(cfp->cf_parent, dfpath, sizeof(dfpath));
531bb029937Sjfb 		root = cfp->cf_parent->cf_root;
532bb029937Sjfb 		repo = cfp->cf_parent->cf_repo;
5333917c9bfSderaadt 	} else {
534dc6a6879Sjfb 		dir = ".";
535895e6cf6Sjfb 		root = NULL;
536dc6a6879Sjfb 		repo = NULL;
53708f90673Sjfb 	}
53808f90673Sjfb 
539dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
540e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
541dc6a6879Sjfb 		return (0);
54208f90673Sjfb 	}
54308f90673Sjfb 
544e4276007Sjfb 	if (cvs_sendentry(root, cfp) < 0)
54531274bbfSjoris 		return (CVS_EX_PROTO);
54608f90673Sjfb 
547dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UPTODATE) {
548e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name);
54908f90673Sjfb 		return (0);
55008f90673Sjfb 	}
55108f90673Sjfb 
55208f90673Sjfb 	/* at this point, the file is modified */
553e4276007Sjfb 	if ((cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name) < 0) ||
554e4276007Sjfb 	    (cvs_sendfile(root, diff_file) < 0))
555e4276007Sjfb 		return (CVS_EX_PROTO);
556e4276007Sjfb 
557e4276007Sjfb 	return (0);
558e4276007Sjfb }
559e4276007Sjfb 
560e4276007Sjfb static int
561e4276007Sjfb cvs_diff_local(CVSFILE *cf, void *arg)
562e4276007Sjfb {
563e4276007Sjfb 	int len;
564e4276007Sjfb 	char *repo, buf[64];
565e4276007Sjfb 	char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
566e4276007Sjfb 	char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN];
567e4276007Sjfb 	BUF *b1, *b2;
568e4276007Sjfb 	RCSNUM *r1, *r2;
569e4276007Sjfb 	RCSFILE *rf;
570e4276007Sjfb 	struct cvsroot *root;
571e4276007Sjfb 
572e4276007Sjfb 	rf = NULL;
573e4276007Sjfb 	root = CVS_DIR_ROOT(cf);
574e4276007Sjfb 	repo = CVS_DIR_REPO(cf);
575e4276007Sjfb 	diff_file = cvs_file_getpath(cf, fpath, sizeof(fpath));
576e4276007Sjfb 
577e4276007Sjfb 	if (cf->cf_type == DT_DIR) {
578b87c59bdSxsa 		if (verbosity > 1)
579246675edSxsa 			cvs_log(LP_NOTICE, "Diffing %s", fpath);
580e4276007Sjfb 		return (0);
581e4276007Sjfb 	}
582e4276007Sjfb 
583e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_LOST) {
584e4276007Sjfb 		cvs_log(LP_WARN, "cannot find file %s", cf->cf_name);
585e4276007Sjfb 		return (0);
586e4276007Sjfb 	}
587e4276007Sjfb 
588e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_UNKNOWN) {
589e4276007Sjfb 		cvs_log(LP_WARN, "I know nothing about %s", diff_file);
590e4276007Sjfb 		return (0);
5915c0cd766Sniallo 	} else if (cf->cf_cvstat == CVS_FST_UPTODATE)
592e4276007Sjfb 		return (0);
593e4276007Sjfb 
594e4276007Sjfb 	/* at this point, the file is modified */
595e4276007Sjfb 	len = snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
596dc6a6879Sjfb 	    root->cr_dir, repo, diff_file, RCS_FILE_EXT);
597e4276007Sjfb 	if (len == -1 || len >= (int)sizeof(rcspath)) {
59827b85f85Sxsa 		errno = ENAMETOOLONG;
59927b85f85Sxsa 		cvs_log(LP_ERRNO, "%s", rcspath);
60001b3d77aSjoris 		return (CVS_EX_DATA);
60127b85f85Sxsa 	}
60208f90673Sjfb 
6031b6534b8Sjfb 	rf = rcs_open(rcspath, RCS_READ);
604dc6a6879Sjfb 	if (rf == NULL) {
60531274bbfSjoris 		return (CVS_EX_DATA);
606dc6a6879Sjfb 	}
60708f90673Sjfb 
608dc6a6879Sjfb 	cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file,
60908f90673Sjfb 	    RCS_DIFF_DIV, rcspath);
61008f90673Sjfb 
611dc6a6879Sjfb 	if (dap->rev1 == NULL)
612e4276007Sjfb 		r1 = cf->cf_lrev;
61308f90673Sjfb 	else {
61425b74b48Sjfb 		if ((r1 = rcsnum_parse(dap->rev1)) == NULL) {
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);
62895ae1173Sjoris 		return (CVS_EX_DATA);
62995ae1173Sjoris 	}
63095ae1173Sjoris 
631e4276007Sjfb 	if (r1 != cf->cf_lrev)
6327f535ec4Sjfb 		rcsnum_free(r1);
6337f535ec4Sjfb 
634dc6a6879Sjfb 	if (dap->rev2 != NULL) {
635dc6a6879Sjfb 		cvs_printf("retrieving revision %s\n", dap->rev2);
63625b74b48Sjfb 		if ((r2 = rcsnum_parse(dap->rev2)) == NULL) {
63731274bbfSjoris 			return (CVS_EX_DATA);
6387f535ec4Sjfb 		}
63908f90673Sjfb 		b2 = rcs_getrev(rf, r2);
6407f535ec4Sjfb 		rcsnum_free(r2);
6413917c9bfSderaadt 	} else {
642dc6a6879Sjfb 		b2 = cvs_buf_load(diff_file, BUF_AUTOEXT);
64308f90673Sjfb 	}
64408f90673Sjfb 
645dc6a6879Sjfb 	rcs_close(rf);
646dc6a6879Sjfb 
64795ae1173Sjoris 	if (b2 == NULL) {
648289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
64995ae1173Sjoris 		    dap->rev2);
65095ae1173Sjoris 		cvs_buf_free(b1);
65195ae1173Sjoris 		return (CVS_EX_DATA);
65295ae1173Sjoris 	}
65395ae1173Sjoris 
6545ac8b1e7Sjoris 	cvs_printf("%s", diffargs);
6555ac8b1e7Sjoris 	cvs_printf(" -r%s", buf);
656dc6a6879Sjfb 	if (dap->rev2 != NULL)
6575ac8b1e7Sjoris 		cvs_printf(" -r%s", dap->rev2);
6585ac8b1e7Sjoris 	cvs_printf(" %s\n", diff_file);
659946f6157Sdjm 	strlcpy(path_tmp1, "/tmp/diff1.XXXXXXXXXX", sizeof(path_tmp1));
6607f535ec4Sjfb 	if (cvs_buf_write_stmp(b1, path_tmp1, 0600) == -1) {
6617f535ec4Sjfb 		cvs_buf_free(b1);
6627f535ec4Sjfb 		cvs_buf_free(b2);
66331274bbfSjoris 		return (CVS_EX_DATA);
6647f535ec4Sjfb 	}
6657f535ec4Sjfb 	cvs_buf_free(b1);
6667f535ec4Sjfb 
66769853e40Sjfb 	strlcpy(path_tmp2, "/tmp/diff2.XXXXXXXXXX", sizeof(path_tmp2));
668946f6157Sdjm 	if (cvs_buf_write_stmp(b2, path_tmp2, 0600) == -1) {
6697f535ec4Sjfb 		cvs_buf_free(b2);
670946f6157Sdjm 		(void)unlink(path_tmp1);
67131274bbfSjoris 		return (CVS_EX_DATA);
672946f6157Sdjm 	}
6737f535ec4Sjfb 	cvs_buf_free(b2);
6747f535ec4Sjfb 
675*f9b67873Sniallo 	cvs_diffreg(path_tmp1, path_tmp2, NULL);
676946f6157Sdjm 	(void)unlink(path_tmp1);
677946f6157Sdjm 	(void)unlink(path_tmp2);
67808f90673Sjfb 
67908f90673Sjfb 	return (0);
68008f90673Sjfb }
681af5bb824Sniallo #endif
68208f90673Sjfb 
68308f90673Sjfb 
68408f90673Sjfb int
685*f9b67873Sniallo cvs_diffreg(const char *file1, const char *file2, BUF *out)
68608f90673Sjfb {
68708f90673Sjfb 	FILE *f1, *f2;
68808f90673Sjfb 	int i, rval;
6897f535ec4Sjfb 	void *tmp;
69008f90673Sjfb 
69108f90673Sjfb 	f1 = f2 = NULL;
69208f90673Sjfb 	rval = D_SAME;
69308f90673Sjfb 	anychange = 0;
69408f90673Sjfb 	lastline = 0;
69508f90673Sjfb 	lastmatchline = 0;
69608f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
69708f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
698*f9b67873Sniallo 	if (out != NULL)
699*f9b67873Sniallo 		diffbuf = out;
70008f90673Sjfb 
70108f90673Sjfb 	f1 = fopen(file1, "r");
70208f90673Sjfb 	if (f1 == NULL) {
70308f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
70408f90673Sjfb 		goto closem;
70508f90673Sjfb 	}
70608f90673Sjfb 
70708f90673Sjfb 	f2 = fopen(file2, "r");
70808f90673Sjfb 	if (f2 == NULL) {
70908f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
71008f90673Sjfb 		goto closem;
71108f90673Sjfb 	}
71208f90673Sjfb 
71308f90673Sjfb 	switch (files_differ(f1, f2)) {
71408f90673Sjfb 	case 0:
71508f90673Sjfb 		goto closem;
71608f90673Sjfb 	case 1:
71708f90673Sjfb 		break;
71808f90673Sjfb 	default:
71908f90673Sjfb 		/* error */
72008f90673Sjfb 		goto closem;
72108f90673Sjfb 	}
72208f90673Sjfb 
72308f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
72408f90673Sjfb 		rval = D_BINARY;
72508f90673Sjfb 		goto closem;
72608f90673Sjfb 	}
7277f535ec4Sjfb 	if ((prepare(0, f1, stb1.st_size) < 0) ||
7287f535ec4Sjfb 	    (prepare(1, f2, stb2.st_size) < 0)) {
7297f535ec4Sjfb 		goto closem;
7307f535ec4Sjfb 	}
73108f90673Sjfb 	prune();
73208f90673Sjfb 	sort(sfile[0], slen[0]);
73308f90673Sjfb 	sort(sfile[1], slen[1]);
73408f90673Sjfb 
73508f90673Sjfb 	member = (int *)file[1];
73608f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
73718501190Sniallo 	if ((tmp = realloc(member, (slen[1] + 2) * sizeof(int))) == NULL) {
73818501190Sniallo 		free(member);
73918501190Sniallo 		member = NULL;
74018501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize member");
7417f535ec4Sjfb 		goto closem;
74218501190Sniallo 	}
7437f535ec4Sjfb 	member = (int *)tmp;
74408f90673Sjfb 
74508f90673Sjfb 	class = (int *)file[0];
74608f90673Sjfb 	unsort(sfile[0], slen[0], class);
74718501190Sniallo 	if ((tmp = realloc(class, (slen[0] + 2) * sizeof(int))) == NULL) {
74818501190Sniallo 		free(class);
74918501190Sniallo 		class = NULL;
75018501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize class");
7517f535ec4Sjfb 		goto closem;
75218501190Sniallo 	}
7537f535ec4Sjfb 	class = (int *)tmp;
75408f90673Sjfb 
7557f535ec4Sjfb 	if ((klist = malloc((slen[0] + 2) * sizeof(int))) == NULL) {
7567f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate klist");
7577f535ec4Sjfb 		goto closem;
7587f535ec4Sjfb 	}
75908f90673Sjfb 	clen = 0;
76008f90673Sjfb 	clistlen = 100;
7617f535ec4Sjfb 	if ((clist = malloc(clistlen * sizeof(cand))) == NULL) {
7627f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate clist");
7637f535ec4Sjfb 		goto closem;
7647f535ec4Sjfb 	}
765ece76a70Sjoris 
766ece76a70Sjoris 	if ((i = stone(class, slen[0], member, klist)) < 0)
767ece76a70Sjoris 		goto closem;
768ece76a70Sjoris 
76908f90673Sjfb 	free(member);
77008f90673Sjfb 	free(class);
77108f90673Sjfb 
77218501190Sniallo 	if ((tmp = realloc(J, (diff_len[0] + 2) * sizeof(int))) == NULL) {
77318501190Sniallo 		free(J);
77418501190Sniallo 		J = NULL;
77518501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize J");
77618501190Sniallo 		goto closem;
77718501190Sniallo 	}
77818501190Sniallo 	J = (int *)tmp;
77908f90673Sjfb 	unravel(klist[i]);
78008f90673Sjfb 	free(clist);
78108f90673Sjfb 	free(klist);
78208f90673Sjfb 
78318501190Sniallo 	if ((tmp = realloc(ixold, (diff_len[0] + 2) * sizeof(long))) == NULL) {
78418501190Sniallo 		free(ixold);
78518501190Sniallo 		ixold = NULL;
78618501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize ixold");
78718501190Sniallo 		goto closem;
78818501190Sniallo 	}
78918501190Sniallo 	ixold = (long *)tmp;
79018501190Sniallo 	if ((tmp = realloc(ixnew, (diff_len[1] + 2) * sizeof(long))) == NULL) {
79118501190Sniallo 		free(ixnew);
79218501190Sniallo 		ixnew = NULL;
79318501190Sniallo 		cvs_log(LP_ERRNO, "failed to resize ixnew");
79418501190Sniallo 		goto closem;
79518501190Sniallo 	}
79618501190Sniallo 	ixnew = (long *)tmp;
79708f90673Sjfb 	check(f1, f2);
79808f90673Sjfb 	output(file1, f1, file2, f2);
79908f90673Sjfb 
80008f90673Sjfb closem:
80108f90673Sjfb 	if (anychange) {
80208f90673Sjfb 		if (rval == D_SAME)
80308f90673Sjfb 			rval = D_DIFFER;
80408f90673Sjfb 	}
80508f90673Sjfb 	if (f1 != NULL)
80608f90673Sjfb 		fclose(f1);
80708f90673Sjfb 	if (f2 != NULL)
80808f90673Sjfb 		fclose(f2);
8097f535ec4Sjfb 
81008f90673Sjfb 	return (rval);
81108f90673Sjfb }
81208f90673Sjfb 
81308f90673Sjfb /*
81408f90673Sjfb  * Check to see if the given files differ.
81508f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
81608f90673Sjfb  * XXX - could use code from cmp(1) [faster]
81708f90673Sjfb  */
81808f90673Sjfb static int
81908f90673Sjfb files_differ(FILE *f1, FILE *f2)
82008f90673Sjfb {
82108f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
82208f90673Sjfb 	size_t i, j;
82308f90673Sjfb 
82408f90673Sjfb 	if (stb1.st_size != stb2.st_size)
82508f90673Sjfb 		return (1);
82608f90673Sjfb 	for (;;) {
827f1901a5aSxsa 		i = fread(buf1, (size_t)1, sizeof(buf1), f1);
828f1901a5aSxsa 		j = fread(buf2, (size_t)1, sizeof(buf2), f2);
82908f90673Sjfb 		if (i != j)
83008f90673Sjfb 			return (1);
83108f90673Sjfb 		if (i == 0 && j == 0) {
83208f90673Sjfb 			if (ferror(f1) || ferror(f2))
83308f90673Sjfb 				return (1);
83408f90673Sjfb 			return (0);
83508f90673Sjfb 		}
83608f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
83708f90673Sjfb 			return (1);
83808f90673Sjfb 	}
83908f90673Sjfb }
84008f90673Sjfb 
8417f535ec4Sjfb static int
84208f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
84308f90673Sjfb {
8447f535ec4Sjfb 	void *tmp;
84508f90673Sjfb 	struct line *p;
84608f90673Sjfb 	int j, h;
84708f90673Sjfb 	size_t sz;
84808f90673Sjfb 
84908f90673Sjfb 	rewind(fd);
85008f90673Sjfb 
851c48da046Sxsa 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
85208f90673Sjfb 	if (sz < 100)
85308f90673Sjfb 		sz = 100;
85408f90673Sjfb 
8557f535ec4Sjfb 	p = (struct line *)malloc((sz + 3) * sizeof(struct line));
8567f535ec4Sjfb 	if (p == NULL) {
8577f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to prepare line array");
8587f535ec4Sjfb 		return (-1);
8597f535ec4Sjfb 	}
86008f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
86108f90673Sjfb 		if (j == (int)sz) {
86208f90673Sjfb 			sz = sz * 3 / 2;
8637f535ec4Sjfb 			tmp = realloc(p, (sz + 3) * sizeof(struct line));
8647f535ec4Sjfb 			if (tmp == NULL) {
8657f535ec4Sjfb 				cvs_log(LP_ERRNO, "failed to grow line array");
8667f535ec4Sjfb 				free(p);
8677f535ec4Sjfb 				return (-1);
8687f535ec4Sjfb 			}
8697f535ec4Sjfb 			p = (struct line *)tmp;
87008f90673Sjfb 		}
87108f90673Sjfb 		p[++j].value = h;
87208f90673Sjfb 	}
873e4276007Sjfb 	diff_len[i] = j;
87408f90673Sjfb 	file[i] = p;
8757f535ec4Sjfb 
8767f535ec4Sjfb 	return (0);
87708f90673Sjfb }
87808f90673Sjfb 
87908f90673Sjfb static void
88008f90673Sjfb prune(void)
88108f90673Sjfb {
88208f90673Sjfb 	int i, j;
88308f90673Sjfb 
884e4276007Sjfb 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
88508f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
88608f90673Sjfb 	    pref++)
88708f90673Sjfb 		;
888e4276007Sjfb 	for (suff = 0;
889e4276007Sjfb 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
890e4276007Sjfb 	    (file[0][diff_len[0] - suff].value ==
891e4276007Sjfb 	    file[1][diff_len[1] - suff].value);
89208f90673Sjfb 	    suff++)
89308f90673Sjfb 		;
89408f90673Sjfb 	for (j = 0; j < 2; j++) {
89508f90673Sjfb 		sfile[j] = file[j] + pref;
896e4276007Sjfb 		slen[j] = diff_len[j] - pref - suff;
89708f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
89808f90673Sjfb 			sfile[j][i].serial = i;
89908f90673Sjfb 	}
90008f90673Sjfb }
90108f90673Sjfb 
90208f90673Sjfb static void
90308f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
90408f90673Sjfb {
90508f90673Sjfb 	int i, j;
90608f90673Sjfb 
90708f90673Sjfb 	i = j = 1;
90808f90673Sjfb 	while (i <= n && j <= m) {
90908f90673Sjfb 		if (a[i].value < b[j].value)
91008f90673Sjfb 			a[i++].value = 0;
91108f90673Sjfb 		else if (a[i].value == b[j].value)
91208f90673Sjfb 			a[i++].value = j;
91308f90673Sjfb 		else
91408f90673Sjfb 			j++;
91508f90673Sjfb 	}
91608f90673Sjfb 	while (i <= n)
91708f90673Sjfb 		a[i++].value = 0;
91808f90673Sjfb 	b[m + 1].value = 0;
91908f90673Sjfb 	j = 0;
92008f90673Sjfb 	while (++j <= m) {
92108f90673Sjfb 		c[j] = -b[j].serial;
92208f90673Sjfb 		while (b[j + 1].value == b[j].value) {
92308f90673Sjfb 			j++;
92408f90673Sjfb 			c[j] = b[j].serial;
92508f90673Sjfb 		}
92608f90673Sjfb 	}
92708f90673Sjfb 	c[j] = -1;
92808f90673Sjfb }
92908f90673Sjfb 
93008f90673Sjfb /* Code taken from ping.c */
93108f90673Sjfb static int
93208f90673Sjfb isqrt(int n)
93308f90673Sjfb {
93408f90673Sjfb 	int y, x = 1;
93508f90673Sjfb 
93608f90673Sjfb 	if (n == 0)
93708f90673Sjfb 		return (0);
93808f90673Sjfb 
93908f90673Sjfb 	do { /* newton was a stinker */
94008f90673Sjfb 		y = x;
94108f90673Sjfb 		x = n / x;
94208f90673Sjfb 		x += y;
94308f90673Sjfb 		x /= 2;
94408f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
94508f90673Sjfb 
94608f90673Sjfb 	return (x);
94708f90673Sjfb }
94808f90673Sjfb 
94908f90673Sjfb static int
95008f90673Sjfb stone(int *a, int n, int *b, int *c)
95108f90673Sjfb {
952ece76a70Sjoris 	int ret;
95308f90673Sjfb 	int i, k, y, j, l;
95408f90673Sjfb 	int oldc, tc, oldl;
95508f90673Sjfb 	u_int numtries;
95608f90673Sjfb 
957cc649edbSjfb 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
958cc649edbSjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
95908f90673Sjfb 
96008f90673Sjfb 	k = 0;
961ece76a70Sjoris 	if ((ret = newcand(0, 0, 0)) < 0)
962ece76a70Sjoris 		return (-1);
963ece76a70Sjoris 	c[0] = ret;
96408f90673Sjfb 	for (i = 1; i <= n; i++) {
96508f90673Sjfb 		j = a[i];
96608f90673Sjfb 		if (j == 0)
96708f90673Sjfb 			continue;
96808f90673Sjfb 		y = -b[j];
96908f90673Sjfb 		oldl = 0;
97008f90673Sjfb 		oldc = c[0];
97108f90673Sjfb 		numtries = 0;
97208f90673Sjfb 		do {
97308f90673Sjfb 			if (y <= clist[oldc].y)
97408f90673Sjfb 				continue;
97508f90673Sjfb 			l = search(c, k, y);
97608f90673Sjfb 			if (l != oldl + 1)
97708f90673Sjfb 				oldc = c[l - 1];
97808f90673Sjfb 			if (l <= k) {
97908f90673Sjfb 				if (clist[c[l]].y <= y)
98008f90673Sjfb 					continue;
98108f90673Sjfb 				tc = c[l];
982ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
983ece76a70Sjoris 					return (-1);
984ece76a70Sjoris 				c[l] = ret;
98508f90673Sjfb 				oldc = tc;
98608f90673Sjfb 				oldl = l;
98708f90673Sjfb 				numtries++;
98808f90673Sjfb 			} else {
989ece76a70Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
990ece76a70Sjoris 					return (-1);
991ece76a70Sjoris 				c[l] = ret;
99208f90673Sjfb 				k++;
99308f90673Sjfb 				break;
99408f90673Sjfb 			}
99508f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
99608f90673Sjfb 	}
99708f90673Sjfb 	return (k);
99808f90673Sjfb }
99908f90673Sjfb 
100008f90673Sjfb static int
100108f90673Sjfb newcand(int x, int y, int pred)
100208f90673Sjfb {
100318501190Sniallo 	struct cand *q, *tmp;
100418501190Sniallo 	int newclistlen;
100508f90673Sjfb 
100608f90673Sjfb 	if (clen == clistlen) {
100718501190Sniallo 		newclistlen = clistlen * 11 / 10;
100818501190Sniallo 		tmp = realloc(clist, newclistlen * sizeof(cand));
100918501190Sniallo 		if (tmp == NULL) {
10107f535ec4Sjfb 			cvs_log(LP_ERRNO, "failed to resize clist");
10117f535ec4Sjfb 			return (-1);
10127f535ec4Sjfb 		}
101318501190Sniallo 		clist = tmp;
101418501190Sniallo 		clistlen = newclistlen;
101508f90673Sjfb 	}
101608f90673Sjfb 	q = clist + clen;
101708f90673Sjfb 	q->x = x;
101808f90673Sjfb 	q->y = y;
101908f90673Sjfb 	q->pred = pred;
102008f90673Sjfb 	return (clen++);
102108f90673Sjfb }
102208f90673Sjfb 
102308f90673Sjfb static int
102408f90673Sjfb search(int *c, int k, int y)
102508f90673Sjfb {
102608f90673Sjfb 	int i, j, l, t;
102708f90673Sjfb 
102808f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
102908f90673Sjfb 		return (k + 1);
103008f90673Sjfb 	i = 0;
103108f90673Sjfb 	j = k + 1;
103208f90673Sjfb 	while (1) {
103308f90673Sjfb 		l = i + j;
103408f90673Sjfb 		if ((l >>= 1) <= i)
103508f90673Sjfb 			break;
103608f90673Sjfb 		t = clist[c[l]].y;
103708f90673Sjfb 		if (t > y)
103808f90673Sjfb 			j = l;
103908f90673Sjfb 		else if (t < y)
104008f90673Sjfb 			i = l;
104108f90673Sjfb 		else
104208f90673Sjfb 			return (l);
104308f90673Sjfb 	}
104408f90673Sjfb 	return (l + 1);
104508f90673Sjfb }
104608f90673Sjfb 
104708f90673Sjfb static void
104808f90673Sjfb unravel(int p)
104908f90673Sjfb {
105008f90673Sjfb 	struct cand *q;
105108f90673Sjfb 	int i;
105208f90673Sjfb 
1053e4276007Sjfb 	for (i = 0; i <= diff_len[0]; i++)
105408f90673Sjfb 		J[i] = i <= pref ? i :
1055e4276007Sjfb 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
105608f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
105708f90673Sjfb 		J[q->x + pref] = q->y + pref;
105808f90673Sjfb }
105908f90673Sjfb 
106008f90673Sjfb /*
106108f90673Sjfb  * Check does double duty:
106208f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
106308f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
106408f90673Sjfb  *  2.  collect random access indexes to the two files
106508f90673Sjfb  */
106608f90673Sjfb static void
106708f90673Sjfb check(FILE *f1, FILE *f2)
106808f90673Sjfb {
106908f90673Sjfb 	int i, j, jackpot, c, d;
107008f90673Sjfb 	long ctold, ctnew;
107108f90673Sjfb 
107208f90673Sjfb 	rewind(f1);
107308f90673Sjfb 	rewind(f2);
107408f90673Sjfb 	j = 1;
107508f90673Sjfb 	ixold[0] = ixnew[0] = 0;
107608f90673Sjfb 	jackpot = 0;
107708f90673Sjfb 	ctold = ctnew = 0;
1078e4276007Sjfb 	for (i = 1; i <= diff_len[0]; i++) {
107908f90673Sjfb 		if (J[i] == 0) {
108008f90673Sjfb 			ixold[i] = ctold += skipline(f1);
108108f90673Sjfb 			continue;
108208f90673Sjfb 		}
108308f90673Sjfb 		while (j < J[i]) {
108408f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
108508f90673Sjfb 			j++;
108608f90673Sjfb 		}
108708f90673Sjfb 		if (bflag || wflag || iflag) {
108808f90673Sjfb 			for (;;) {
108908f90673Sjfb 				c = getc(f1);
109008f90673Sjfb 				d = getc(f2);
109108f90673Sjfb 				/*
109208f90673Sjfb 				 * GNU diff ignores a missing newline
109308f90673Sjfb 				 * in one file if bflag || wflag.
109408f90673Sjfb 				 */
109508f90673Sjfb 				if ((bflag || wflag) &&
109608f90673Sjfb 				    ((c == EOF && d == '\n') ||
109708f90673Sjfb 				    (c == '\n' && d == EOF))) {
109808f90673Sjfb 					break;
109908f90673Sjfb 				}
110008f90673Sjfb 				ctold++;
110108f90673Sjfb 				ctnew++;
110208f90673Sjfb 				if (bflag && isspace(c) && isspace(d)) {
110308f90673Sjfb 					do {
110408f90673Sjfb 						if (c == '\n')
110508f90673Sjfb 							break;
110608f90673Sjfb 						ctold++;
110708f90673Sjfb 					} while (isspace(c = getc(f1)));
110808f90673Sjfb 					do {
110908f90673Sjfb 						if (d == '\n')
111008f90673Sjfb 							break;
111108f90673Sjfb 						ctnew++;
111208f90673Sjfb 					} while (isspace(d = getc(f2)));
111308f90673Sjfb 				} else if (wflag) {
111408f90673Sjfb 					while (isspace(c) && c != '\n') {
111508f90673Sjfb 						c = getc(f1);
111608f90673Sjfb 						ctold++;
111708f90673Sjfb 					}
111808f90673Sjfb 					while (isspace(d) && d != '\n') {
111908f90673Sjfb 						d = getc(f2);
112008f90673Sjfb 						ctnew++;
112108f90673Sjfb 					}
112208f90673Sjfb 				}
112308f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
112408f90673Sjfb 					jackpot++;
112508f90673Sjfb 					J[i] = 0;
112608f90673Sjfb 					if (c != '\n' && c != EOF)
112708f90673Sjfb 						ctold += skipline(f1);
112808f90673Sjfb 					if (d != '\n' && c != EOF)
112908f90673Sjfb 						ctnew += skipline(f2);
113008f90673Sjfb 					break;
113108f90673Sjfb 				}
113208f90673Sjfb 				if (c == '\n' || c == EOF)
113308f90673Sjfb 					break;
113408f90673Sjfb 			}
113508f90673Sjfb 		} else {
113608f90673Sjfb 			for (;;) {
113708f90673Sjfb 				ctold++;
113808f90673Sjfb 				ctnew++;
113908f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
114008f90673Sjfb 					/* jackpot++; */
114108f90673Sjfb 					J[i] = 0;
114208f90673Sjfb 					if (c != '\n' && c != EOF)
114308f90673Sjfb 						ctold += skipline(f1);
114408f90673Sjfb 					if (d != '\n' && c != EOF)
114508f90673Sjfb 						ctnew += skipline(f2);
114608f90673Sjfb 					break;
114708f90673Sjfb 				}
114808f90673Sjfb 				if (c == '\n' || c == EOF)
114908f90673Sjfb 					break;
115008f90673Sjfb 			}
115108f90673Sjfb 		}
115208f90673Sjfb 		ixold[i] = ctold;
115308f90673Sjfb 		ixnew[j] = ctnew;
115408f90673Sjfb 		j++;
115508f90673Sjfb 	}
1156e4276007Sjfb 	for (; j <= diff_len[1]; j++)
115708f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
115808f90673Sjfb 	/*
115908f90673Sjfb 	 * if (jackpot)
11605ac8b1e7Sjoris 	 *	cvs_printf("jackpot\n");
116108f90673Sjfb 	 */
116208f90673Sjfb }
116308f90673Sjfb 
116408f90673Sjfb /* shellsort CACM #201 */
116508f90673Sjfb static void
116608f90673Sjfb sort(struct line *a, int n)
116708f90673Sjfb {
116808f90673Sjfb 	struct line *ai, *aim, w;
116908f90673Sjfb 	int j, m = 0, k;
117008f90673Sjfb 
117108f90673Sjfb 	if (n == 0)
117208f90673Sjfb 		return;
117308f90673Sjfb 	for (j = 1; j <= n; j *= 2)
117408f90673Sjfb 		m = 2 * j - 1;
117508f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
117608f90673Sjfb 		k = n - m;
117708f90673Sjfb 		for (j = 1; j <= k; j++) {
117808f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
117908f90673Sjfb 				aim = &ai[m];
118008f90673Sjfb 				if (aim < ai)
118108f90673Sjfb 					break;	/* wraparound */
118208f90673Sjfb 				if (aim->value > ai[0].value ||
118308f90673Sjfb 				    (aim->value == ai[0].value &&
118408f90673Sjfb 					aim->serial > ai[0].serial))
118508f90673Sjfb 					break;
118608f90673Sjfb 				w.value = ai[0].value;
118708f90673Sjfb 				ai[0].value = aim->value;
118808f90673Sjfb 				aim->value = w.value;
118908f90673Sjfb 				w.serial = ai[0].serial;
119008f90673Sjfb 				ai[0].serial = aim->serial;
119108f90673Sjfb 				aim->serial = w.serial;
119208f90673Sjfb 			}
119308f90673Sjfb 		}
119408f90673Sjfb 	}
119508f90673Sjfb }
119608f90673Sjfb 
119708f90673Sjfb static void
119808f90673Sjfb unsort(struct line *f, int l, int *b)
119908f90673Sjfb {
120008f90673Sjfb 	int *a, i;
120108f90673Sjfb 
12027f535ec4Sjfb 	if ((a = (int *)malloc((l + 1) * sizeof(int))) == NULL) {
12037f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate sort array");
12047f535ec4Sjfb 		return;
12057f535ec4Sjfb 	}
120608f90673Sjfb 	for (i = 1; i <= l; i++)
120708f90673Sjfb 		a[f[i].serial] = f[i].value;
120808f90673Sjfb 	for (i = 1; i <= l; i++)
120908f90673Sjfb 		b[i] = a[i];
121008f90673Sjfb 	free(a);
121108f90673Sjfb }
121208f90673Sjfb 
121308f90673Sjfb static int
121408f90673Sjfb skipline(FILE *f)
121508f90673Sjfb {
121608f90673Sjfb 	int i, c;
121708f90673Sjfb 
121808f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
121908f90673Sjfb 		continue;
122008f90673Sjfb 	return (i);
122108f90673Sjfb }
122208f90673Sjfb 
122308f90673Sjfb static void
122408f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2)
122508f90673Sjfb {
122608f90673Sjfb 	int m, i0, i1, j0, j1;
122708f90673Sjfb 
122808f90673Sjfb 	rewind(f1);
122908f90673Sjfb 	rewind(f2);
1230e4276007Sjfb 	m = diff_len[0];
123108f90673Sjfb 	J[0] = 0;
1232e4276007Sjfb 	J[m + 1] = diff_len[1] + 1;
123308f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
123408f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
123508f90673Sjfb 			i0++;
123608f90673Sjfb 		j0 = J[i0 - 1] + 1;
123708f90673Sjfb 		i1 = i0 - 1;
123808f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
123908f90673Sjfb 			i1++;
124008f90673Sjfb 		j1 = J[i1 + 1] - 1;
124108f90673Sjfb 		J[i1] = j1;
124208f90673Sjfb 		change(file1, f1, file2, f2, i0, i1, j0, j1);
124308f90673Sjfb 	}
124408f90673Sjfb 	if (m == 0)
1245e4276007Sjfb 		change(file1, f1, file2, f2, 1, 0, 1, diff_len[1]);
1246*f9b67873Sniallo 	if (diff_format == D_IFDEF) {
124708f90673Sjfb 		for (;;) {
124808f90673Sjfb #define	c i0
124908f90673Sjfb 			if ((c = getc(f1)) == EOF)
125008f90673Sjfb 				return;
1251*f9b67873Sniallo 			diff_output("%c", c);
125208f90673Sjfb 		}
125308f90673Sjfb #undef c
125408f90673Sjfb 	}
125508f90673Sjfb 	if (anychange != 0) {
1256*f9b67873Sniallo 		if (diff_format == D_CONTEXT)
125708f90673Sjfb 			dump_context_vec(f1, f2);
1258*f9b67873Sniallo 		else if (diff_format == D_UNIFIED)
125908f90673Sjfb 			dump_unified_vec(f1, f2);
126008f90673Sjfb 	}
126108f90673Sjfb }
126208f90673Sjfb 
126308f90673Sjfb static __inline void
126408f90673Sjfb range(int a, int b, char *separator)
126508f90673Sjfb {
1266*f9b67873Sniallo 	diff_output("%d", a > b ? b : a);
126708f90673Sjfb 	if (a < b)
1268*f9b67873Sniallo 		diff_output("%s%d", separator, b);
126908f90673Sjfb }
127008f90673Sjfb 
127108f90673Sjfb static __inline void
127208f90673Sjfb uni_range(int a, int b)
127308f90673Sjfb {
127408f90673Sjfb 	if (a < b)
1275*f9b67873Sniallo 		diff_output("%d,%d", a, b - a + 1);
127608f90673Sjfb 	else if (a == b)
1277*f9b67873Sniallo 		diff_output("%d", b);
127808f90673Sjfb 	else
1279*f9b67873Sniallo 		diff_output("%d,0", b);
128008f90673Sjfb }
128108f90673Sjfb 
128208f90673Sjfb static char *
12832a0de57dSjfb preadline(int fd, size_t rlen, off_t off)
128408f90673Sjfb {
128508f90673Sjfb 	char *line;
128608f90673Sjfb 	ssize_t nr;
128708f90673Sjfb 
12882a0de57dSjfb 	line = malloc(rlen + 1);
128908f90673Sjfb 	if (line == NULL) {
129008f90673Sjfb 		cvs_log(LP_ERRNO, "failed to allocate line");
129108f90673Sjfb 		return (NULL);
129208f90673Sjfb 	}
12932a0de57dSjfb 	if ((nr = pread(fd, line, rlen, off)) < 0) {
129408f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
129508f90673Sjfb 		return (NULL);
129608f90673Sjfb 	}
129708f90673Sjfb 	line[nr] = '\0';
129808f90673Sjfb 	return (line);
129908f90673Sjfb }
130008f90673Sjfb 
130108f90673Sjfb static int
130208f90673Sjfb ignoreline(char *line)
130308f90673Sjfb {
130408f90673Sjfb 	int ret;
130508f90673Sjfb 
1306f1901a5aSxsa 	ret = regexec(&ignore_re, line, (size_t)0, NULL, 0);
130708f90673Sjfb 	free(line);
130808f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
130908f90673Sjfb }
131008f90673Sjfb 
131108f90673Sjfb /*
131208f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
131308f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
131408f90673Sjfb  * are no lines in the from file involved and this means that there were
131508f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
131608f90673Sjfb  * lines missing from the to file.
131708f90673Sjfb  */
131808f90673Sjfb static void
131908f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2,
132008f90673Sjfb 	int a, int b, int c, int d)
132108f90673Sjfb {
132208f90673Sjfb 	static size_t max_context = 64;
132308f90673Sjfb 	int i;
132408f90673Sjfb 
1325*f9b67873Sniallo 	if (diff_format != D_IFDEF && a > b && c > d)
132608f90673Sjfb 		return;
132708f90673Sjfb 	if (ignore_pats != NULL) {
132808f90673Sjfb 		char *line;
132908f90673Sjfb 		/*
133008f90673Sjfb 		 * All lines in the change, insert, or delete must
133108f90673Sjfb 		 * match an ignore pattern for the change to be
133208f90673Sjfb 		 * ignored.
133308f90673Sjfb 		 */
133408f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
133508f90673Sjfb 			for (i = a; i <= b; i++) {
133608f90673Sjfb 				line = preadline(fileno(f1),
133708f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
133808f90673Sjfb 				if (!ignoreline(line))
133908f90673Sjfb 					goto proceed;
134008f90673Sjfb 			}
134108f90673Sjfb 		}
134208f90673Sjfb 		if (a > b || c <= d) {	/* Changes and inserts. */
134308f90673Sjfb 			for (i = c; i <= d; i++) {
134408f90673Sjfb 				line = preadline(fileno(f2),
134508f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
134608f90673Sjfb 				if (!ignoreline(line))
134708f90673Sjfb 					goto proceed;
134808f90673Sjfb 			}
134908f90673Sjfb 		}
135008f90673Sjfb 		return;
135108f90673Sjfb 	}
135208f90673Sjfb proceed:
1353*f9b67873Sniallo 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
135408f90673Sjfb 		/*
135508f90673Sjfb 		 * Allocate change records as needed.
135608f90673Sjfb 		 */
135708f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
135818501190Sniallo 			struct context_vec *tmp;
135908f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
136008f90673Sjfb 			max_context <<= 1;
13614c06e5f6Sreyk 			if ((tmp = realloc(context_vec_start, max_context *
13624c06e5f6Sreyk 			    sizeof(struct context_vec))) == NULL) {
136318501190Sniallo 				free(context_vec_start);
136418501190Sniallo 				context_vec_start = NULL;
136518501190Sniallo 				cvs_log(LP_ERRNO,
136618501190Sniallo 				    "failed to resize context_vec_start");
136718501190Sniallo 				return;
136818501190Sniallo 			}
136918501190Sniallo 			context_vec_start = tmp;
137008f90673Sjfb 			context_vec_end = context_vec_start + max_context;
137108f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
137208f90673Sjfb 		}
137308f90673Sjfb 		if (anychange == 0) {
137408f90673Sjfb 			/*
137508f90673Sjfb 			 * Print the context/unidiff header first time through.
137608f90673Sjfb 			 */
1377*f9b67873Sniallo 			diff_output("%s %s	%s",
1378*f9b67873Sniallo 			    diff_format == D_CONTEXT ? "***" : "---", diff_file,
137908f90673Sjfb 			    ctime(&stb1.st_mtime));
1380*f9b67873Sniallo 			diff_output("%s %s	%s",
1381*f9b67873Sniallo 			    diff_format == D_CONTEXT ? "---" : "+++", diff_file,
138208f90673Sjfb 			    ctime(&stb2.st_mtime));
138308f90673Sjfb 			anychange = 1;
138408f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
138508f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
138608f90673Sjfb 			/*
138708f90673Sjfb 			 * If this change is more than 'context' lines from the
138808f90673Sjfb 			 * previous change, dump the record and reset it.
138908f90673Sjfb 			 */
1390*f9b67873Sniallo 			if (diff_format == D_CONTEXT)
139108f90673Sjfb 				dump_context_vec(f1, f2);
139208f90673Sjfb 			else
139308f90673Sjfb 				dump_unified_vec(f1, f2);
139408f90673Sjfb 		}
139508f90673Sjfb 		context_vec_ptr++;
139608f90673Sjfb 		context_vec_ptr->a = a;
139708f90673Sjfb 		context_vec_ptr->b = b;
139808f90673Sjfb 		context_vec_ptr->c = c;
139908f90673Sjfb 		context_vec_ptr->d = d;
140008f90673Sjfb 		return;
140108f90673Sjfb 	}
140208f90673Sjfb 	if (anychange == 0)
140308f90673Sjfb 		anychange = 1;
1404*f9b67873Sniallo 	switch (diff_format) {
140508f90673Sjfb 	case D_BRIEF:
140608f90673Sjfb 		return;
140708f90673Sjfb 	case D_NORMAL:
140808f90673Sjfb 		range(a, b, ",");
1409*f9b67873Sniallo 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1410*f9b67873Sniallo 		if (diff_format == D_NORMAL)
141108f90673Sjfb 			range(c, d, ",");
1412*f9b67873Sniallo 		diff_output("\n");
141308f90673Sjfb 		break;
1414394180a4Sjfb 	case D_RCSDIFF:
1415394180a4Sjfb 		if (a > b)
1416*f9b67873Sniallo 			diff_output("a%d %d\n", b, d - c + 1);
1417394180a4Sjfb 		else {
1418*f9b67873Sniallo 			diff_output("d%d %d\n", a, b - a + 1);
1419394180a4Sjfb 
1420394180a4Sjfb 			if (!(c > d))	/* add changed lines */
1421*f9b67873Sniallo 				diff_output("a%d %d\n", b, d - c + 1);
1422394180a4Sjfb 		}
1423394180a4Sjfb 		break;
142408f90673Sjfb 	}
1425*f9b67873Sniallo 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
142608f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
1427*f9b67873Sniallo 		if (a <= b && c <= d && diff_format == D_NORMAL)
1428*f9b67873Sniallo 			diff_output("---");
142908f90673Sjfb 	}
1430*f9b67873Sniallo 	i = fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0);
143108f90673Sjfb 	if (inifdef) {
1432*f9b67873Sniallo 		diff_output("#endif /* %s */\n", ifdefname);
143308f90673Sjfb 		inifdef = 0;
143408f90673Sjfb 	}
143508f90673Sjfb }
143608f90673Sjfb 
143708f90673Sjfb static int
143808f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
143908f90673Sjfb {
144008f90673Sjfb 	int i, j, c, lastc, col, nc;
144108f90673Sjfb 
144208f90673Sjfb 	/*
144308f90673Sjfb 	 * When doing #ifdef's, copy down to current line
144408f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
144508f90673Sjfb 	 */
1446*f9b67873Sniallo 	if (diff_format == D_IFDEF && oldfile) {
144708f90673Sjfb 		long curpos = ftell(lb);
144808f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
144908f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
145008f90673Sjfb 		for (i = 0; i < nc; i++)
1451*f9b67873Sniallo 			diff_output("%c", getc(lb));
145208f90673Sjfb 	}
145308f90673Sjfb 	if (a > b)
145408f90673Sjfb 		return (0);
1455*f9b67873Sniallo 	if (diff_format == D_IFDEF) {
145608f90673Sjfb 		if (inifdef) {
1457*f9b67873Sniallo 			diff_output("#else /* %s%s */\n",
145808f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
145908f90673Sjfb 		} else {
146008f90673Sjfb 			if (oldfile)
1461*f9b67873Sniallo 				diff_output("#ifndef %s\n", ifdefname);
146208f90673Sjfb 			else
1463*f9b67873Sniallo 				diff_output("#ifdef %s\n", ifdefname);
146408f90673Sjfb 		}
146508f90673Sjfb 		inifdef = 1 + oldfile;
146608f90673Sjfb 	}
146708f90673Sjfb 	for (i = a; i <= b; i++) {
146808f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
146908f90673Sjfb 		nc = f[i] - f[i - 1];
1470*f9b67873Sniallo 		if (diff_format != D_IFDEF && ch != '\0') {
1471*f9b67873Sniallo 			diff_output("%c", ch);
1472*f9b67873Sniallo 			if (Tflag && (diff_format == D_NORMAL || diff_format == D_CONTEXT
1473*f9b67873Sniallo 			    || diff_format == D_UNIFIED))
1474*f9b67873Sniallo 				diff_output("\t");
1475*f9b67873Sniallo 			else if (diff_format != D_UNIFIED)
1476*f9b67873Sniallo 				diff_output(" ");
147708f90673Sjfb 		}
147808f90673Sjfb 		col = 0;
147908f90673Sjfb 		for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
148008f90673Sjfb 			if ((c = getc(lb)) == EOF) {
1481*f9b67873Sniallo 				if (diff_format == D_RCSDIFF)
1482394180a4Sjfb 					warnx("No newline at end of file");
1483394180a4Sjfb 				else
1484*f9b67873Sniallo 					diff_output("\n\\ No newline at end of file");
148508f90673Sjfb 				return (0);
148608f90673Sjfb 			}
148708f90673Sjfb 			if (c == '\t' && tflag) {
148808f90673Sjfb 				do {
1489*f9b67873Sniallo 					diff_output(" ");
149008f90673Sjfb 				} while (++col & 7);
149108f90673Sjfb 			} else {
1492*f9b67873Sniallo 				diff_output("%c", c);
149308f90673Sjfb 				col++;
149408f90673Sjfb 			}
149508f90673Sjfb 		}
149608f90673Sjfb 	}
149708f90673Sjfb 	return (0);
149808f90673Sjfb }
149908f90673Sjfb 
150008f90673Sjfb /*
150108f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
150208f90673Sjfb  */
150308f90673Sjfb static int
150408f90673Sjfb readhash(FILE *f)
150508f90673Sjfb {
150608f90673Sjfb 	int i, t, space;
150708f90673Sjfb 	int sum;
150808f90673Sjfb 
150908f90673Sjfb 	sum = 1;
151008f90673Sjfb 	space = 0;
151108f90673Sjfb 	if (!bflag && !wflag) {
151208f90673Sjfb 		if (iflag)
151308f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
151408f90673Sjfb 				if (t == EOF) {
151508f90673Sjfb 					if (i == 0)
151608f90673Sjfb 						return (0);
151708f90673Sjfb 					break;
151808f90673Sjfb 				}
151908f90673Sjfb 				sum = sum * 127 + chrtran[t];
152008f90673Sjfb 			}
152108f90673Sjfb 		else
152208f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
152308f90673Sjfb 				if (t == EOF) {
152408f90673Sjfb 					if (i == 0)
152508f90673Sjfb 						return (0);
152608f90673Sjfb 					break;
152708f90673Sjfb 				}
152808f90673Sjfb 				sum = sum * 127 + t;
152908f90673Sjfb 			}
153008f90673Sjfb 	} else {
153108f90673Sjfb 		for (i = 0;;) {
153208f90673Sjfb 			switch (t = getc(f)) {
153308f90673Sjfb 			case '\t':
153408f90673Sjfb 			case ' ':
153508f90673Sjfb 				space++;
153608f90673Sjfb 				continue;
153708f90673Sjfb 			default:
153808f90673Sjfb 				if (space && !wflag) {
153908f90673Sjfb 					i++;
154008f90673Sjfb 					space = 0;
154108f90673Sjfb 				}
154208f90673Sjfb 				sum = sum * 127 + chrtran[t];
154308f90673Sjfb 				i++;
154408f90673Sjfb 				continue;
154508f90673Sjfb 			case EOF:
154608f90673Sjfb 				if (i == 0)
154708f90673Sjfb 					return (0);
154808f90673Sjfb 				/* FALLTHROUGH */
154908f90673Sjfb 			case '\n':
155008f90673Sjfb 				break;
155108f90673Sjfb 			}
155208f90673Sjfb 			break;
155308f90673Sjfb 		}
155408f90673Sjfb 	}
155508f90673Sjfb 	/*
155608f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
155708f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
155808f90673Sjfb 	 */
155908f90673Sjfb 	return (sum == 0 ? 1 : sum);
156008f90673Sjfb }
156108f90673Sjfb 
156208f90673Sjfb static int
156308f90673Sjfb asciifile(FILE *f)
156408f90673Sjfb {
156508f90673Sjfb 	char buf[BUFSIZ];
156608f90673Sjfb 	int i, cnt;
156708f90673Sjfb 
156808f90673Sjfb 	if (aflag || f == NULL)
156908f90673Sjfb 		return (1);
157008f90673Sjfb 
157108f90673Sjfb 	rewind(f);
1572f1901a5aSxsa 	cnt = fread(buf, (size_t)1, sizeof(buf), f);
157308f90673Sjfb 	for (i = 0; i < cnt; i++)
157408f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
157508f90673Sjfb 			return (0);
157608f90673Sjfb 	return (1);
157708f90673Sjfb }
157808f90673Sjfb 
15795e78344dSjfb static char*
15805e78344dSjfb match_function(const long *f, int pos, FILE *fp)
15815e78344dSjfb {
15825e78344dSjfb 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
15835e78344dSjfb 	size_t nc;
15845e78344dSjfb 	int last = lastline;
15855e78344dSjfb 	char *p;
15865e78344dSjfb 
15875e78344dSjfb 	lastline = pos;
15885e78344dSjfb 	while (pos > last) {
15895e78344dSjfb 		fseek(fp, f[pos - 1], SEEK_SET);
15905e78344dSjfb 		nc = f[pos] - f[pos - 1];
15915e78344dSjfb 		if (nc >= sizeof(buf))
15925e78344dSjfb 			nc = sizeof(buf) - 1;
1593f1901a5aSxsa 		nc = fread(buf, (size_t)1, nc, fp);
15945e78344dSjfb 		if (nc > 0) {
15955e78344dSjfb 			buf[nc] = '\0';
1596634926d6Sniallo 			p = strchr((const char *)buf, '\n');
15975e78344dSjfb 			if (p != NULL)
15985e78344dSjfb 				*p = '\0';
15995e78344dSjfb 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
16004c06e5f6Sreyk 				strlcpy(lastbuf, (const char *)buf,
16014c06e5f6Sreyk 				    sizeof lastbuf);
16025e78344dSjfb 				lastmatchline = pos;
16035e78344dSjfb 				return lastbuf;
16045e78344dSjfb 			}
16055e78344dSjfb 		}
16065e78344dSjfb 		pos--;
16075e78344dSjfb 	}
16085e78344dSjfb 	return (lastmatchline > 0) ? lastbuf : NULL;
16095e78344dSjfb }
16105e78344dSjfb 
161108f90673Sjfb 
161208f90673Sjfb /* dump accumulated "context" diff changes */
161308f90673Sjfb static void
161408f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
161508f90673Sjfb {
161608f90673Sjfb 	struct context_vec *cvp = context_vec_start;
161708f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
161808f90673Sjfb 	int a, b, c, d;
16195e78344dSjfb 	char ch, *f;
162008f90673Sjfb 
162108f90673Sjfb 	if (context_vec_start > context_vec_ptr)
162208f90673Sjfb 		return;
162308f90673Sjfb 
162408f90673Sjfb 	b = d = 0;		/* gcc */
1625dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1626e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1627dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1628e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
162908f90673Sjfb 
1630*f9b67873Sniallo 	diff_output("***************");
16315e78344dSjfb 	if (pflag) {
16325e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
16335e78344dSjfb 		if (f != NULL) {
1634*f9b67873Sniallo 			diff_output(" ");
1635*f9b67873Sniallo 			diff_output("%s", f);
16365e78344dSjfb 		}
16375e78344dSjfb 	}
1638*f9b67873Sniallo 	diff_output("\n*** ");
163908f90673Sjfb 	range(lowa, upb, ",");
1640*f9b67873Sniallo 	diff_output(" ****\n");
164108f90673Sjfb 
164208f90673Sjfb 	/*
164308f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
164408f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
164508f90673Sjfb 	 * the "old" lines as context in the "new" list).
164608f90673Sjfb 	 */
164708f90673Sjfb 	do_output = 0;
164808f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
164908f90673Sjfb 		if (cvp->a <= cvp->b) {
165008f90673Sjfb 			cvp = context_vec_start;
165108f90673Sjfb 			do_output++;
165208f90673Sjfb 			break;
165308f90673Sjfb 		}
165408f90673Sjfb 	if (do_output) {
165508f90673Sjfb 		while (cvp <= context_vec_ptr) {
165608f90673Sjfb 			a = cvp->a;
165708f90673Sjfb 			b = cvp->b;
165808f90673Sjfb 			c = cvp->c;
165908f90673Sjfb 			d = cvp->d;
166008f90673Sjfb 
166108f90673Sjfb 			if (a <= b && c <= d)
166208f90673Sjfb 				ch = 'c';
166308f90673Sjfb 			else
166408f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
166508f90673Sjfb 
166608f90673Sjfb 			if (ch == 'a')
166708f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
166808f90673Sjfb 			else {
166908f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
167008f90673Sjfb 				fetch(ixold, a, b, f1,
167108f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
167208f90673Sjfb 			}
167308f90673Sjfb 			lowa = b + 1;
167408f90673Sjfb 			cvp++;
167508f90673Sjfb 		}
167608f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
167708f90673Sjfb 	}
167808f90673Sjfb 	/* output changes to the "new" file */
1679*f9b67873Sniallo 	diff_output("--- ");
168008f90673Sjfb 	range(lowc, upd, ",");
1681*f9b67873Sniallo 	diff_output(" ----\n");
168208f90673Sjfb 
168308f90673Sjfb 	do_output = 0;
168408f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
168508f90673Sjfb 		if (cvp->c <= cvp->d) {
168608f90673Sjfb 			cvp = context_vec_start;
168708f90673Sjfb 			do_output++;
168808f90673Sjfb 			break;
168908f90673Sjfb 		}
169008f90673Sjfb 	if (do_output) {
169108f90673Sjfb 		while (cvp <= context_vec_ptr) {
169208f90673Sjfb 			a = cvp->a;
169308f90673Sjfb 			b = cvp->b;
169408f90673Sjfb 			c = cvp->c;
169508f90673Sjfb 			d = cvp->d;
169608f90673Sjfb 
169708f90673Sjfb 			if (a <= b && c <= d)
169808f90673Sjfb 				ch = 'c';
169908f90673Sjfb 			else
170008f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
170108f90673Sjfb 
170208f90673Sjfb 			if (ch == 'd')
170308f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
170408f90673Sjfb 			else {
170508f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
170608f90673Sjfb 				fetch(ixnew, c, d, f2,
170708f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
170808f90673Sjfb 			}
170908f90673Sjfb 			lowc = d + 1;
171008f90673Sjfb 			cvp++;
171108f90673Sjfb 		}
171208f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
171308f90673Sjfb 	}
171408f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
171508f90673Sjfb }
171608f90673Sjfb 
171708f90673Sjfb /* dump accumulated "unified" diff changes */
171808f90673Sjfb static void
171908f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
172008f90673Sjfb {
172108f90673Sjfb 	struct context_vec *cvp = context_vec_start;
172208f90673Sjfb 	int lowa, upb, lowc, upd;
172308f90673Sjfb 	int a, b, c, d;
17245e78344dSjfb 	char ch, *f;
172508f90673Sjfb 
172608f90673Sjfb 	if (context_vec_start > context_vec_ptr)
172708f90673Sjfb 		return;
172808f90673Sjfb 
172908f90673Sjfb 	b = d = 0;		/* gcc */
1730dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1731e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1732dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1733e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
173408f90673Sjfb 
1735*f9b67873Sniallo 	diff_output("@@ -");
173608f90673Sjfb 	uni_range(lowa, upb);
1737*f9b67873Sniallo 	diff_output(" +");
173808f90673Sjfb 	uni_range(lowc, upd);
1739*f9b67873Sniallo 	diff_output(" @@");
17405e78344dSjfb 	if (pflag) {
17415e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
17425e78344dSjfb 		if (f != NULL) {
1743*f9b67873Sniallo 			diff_output(" ");
1744*f9b67873Sniallo 			diff_output("%s", f);
17455e78344dSjfb 		}
17465e78344dSjfb 	}
1747*f9b67873Sniallo 	diff_output("\n");
174808f90673Sjfb 
174908f90673Sjfb 	/*
175008f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
175108f90673Sjfb 	 * are printed together.
175208f90673Sjfb 	 */
175308f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
175408f90673Sjfb 		a = cvp->a;
175508f90673Sjfb 		b = cvp->b;
175608f90673Sjfb 		c = cvp->c;
175708f90673Sjfb 		d = cvp->d;
175808f90673Sjfb 
175908f90673Sjfb 		/*
176008f90673Sjfb 		 * c: both new and old changes
176108f90673Sjfb 		 * d: only changes in the old file
176208f90673Sjfb 		 * a: only changes in the new file
176308f90673Sjfb 		 */
176408f90673Sjfb 		if (a <= b && c <= d)
176508f90673Sjfb 			ch = 'c';
176608f90673Sjfb 		else
176708f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
176808f90673Sjfb 
176908f90673Sjfb 		switch (ch) {
177008f90673Sjfb 		case 'c':
177108f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
177208f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
177308f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
177408f90673Sjfb 			break;
177508f90673Sjfb 		case 'd':
177608f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
177708f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
177808f90673Sjfb 			break;
177908f90673Sjfb 		case 'a':
178008f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
178108f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
178208f90673Sjfb 			break;
178308f90673Sjfb 		}
178408f90673Sjfb 		lowa = b + 1;
178508f90673Sjfb 		lowc = d + 1;
178608f90673Sjfb 	}
178708f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
178808f90673Sjfb 
178908f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
179008f90673Sjfb }
1791*f9b67873Sniallo 
1792*f9b67873Sniallo static void
1793*f9b67873Sniallo diff_output(const char *fmt, ...)
1794*f9b67873Sniallo {
1795*f9b67873Sniallo 	va_list vap;
1796*f9b67873Sniallo 	char *str;
1797*f9b67873Sniallo 
1798*f9b67873Sniallo 	va_start(vap, fmt);
1799*f9b67873Sniallo 	vasprintf(&str, fmt, vap);
1800*f9b67873Sniallo 	if (diffbuf != NULL)
1801*f9b67873Sniallo 		cvs_buf_append(diffbuf, str, strlen(str));
1802*f9b67873Sniallo 	else
1803*f9b67873Sniallo 		cvs_printf("%s", str);
1804*f9b67873Sniallo 	free(str);
1805*f9b67873Sniallo 	va_end(vap);
1806*f9b67873Sniallo }
1807