xref: /openbsd-src/usr.bin/cvs/diff_internals.c (revision f74aa433d7838ae53d3a7e1b45b0fff26ae00ea3)
1*f74aa433Sray /*	$OpenBSD: diff_internals.c,v 1.10 2007/05/29 08:02:59 ray Exp $	*/
23ad3fb45Sjoris /*
33ad3fb45Sjoris  * Copyright (C) Caldera International Inc.  2001-2002.
43ad3fb45Sjoris  * All rights reserved.
53ad3fb45Sjoris  *
63ad3fb45Sjoris  * Redistribution and use in source and binary forms, with or without
73ad3fb45Sjoris  * modification, are permitted provided that the following conditions
83ad3fb45Sjoris  * are met:
93ad3fb45Sjoris  * 1. Redistributions of source code and documentation must retain the above
103ad3fb45Sjoris  *    copyright notice, this list of conditions and the following disclaimer.
113ad3fb45Sjoris  * 2. Redistributions in binary form must reproduce the above copyright
123ad3fb45Sjoris  *    notice, this list of conditions and the following disclaimer in the
133ad3fb45Sjoris  *    documentation and/or other materials provided with the distribution.
143ad3fb45Sjoris  * 3. All advertising materials mentioning features or use of this software
153ad3fb45Sjoris  *    must display the following acknowledgement:
163ad3fb45Sjoris  *	This product includes software developed or owned by Caldera
173ad3fb45Sjoris  *	International, Inc.
183ad3fb45Sjoris  * 4. Neither the name of Caldera International, Inc. nor the names of other
193ad3fb45Sjoris  *    contributors may be used to endorse or promote products derived from
203ad3fb45Sjoris  *    this software without specific prior written permission.
213ad3fb45Sjoris  *
223ad3fb45Sjoris  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
233ad3fb45Sjoris  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
243ad3fb45Sjoris  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
253ad3fb45Sjoris  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
263ad3fb45Sjoris  * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
273ad3fb45Sjoris  * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
283ad3fb45Sjoris  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
293ad3fb45Sjoris  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
303ad3fb45Sjoris  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
313ad3fb45Sjoris  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
323ad3fb45Sjoris  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
333ad3fb45Sjoris  * POSSIBILITY OF SUCH DAMAGE.
343ad3fb45Sjoris  */
353ad3fb45Sjoris /*-
363ad3fb45Sjoris  * Copyright (c) 1991, 1993
373ad3fb45Sjoris  *	The Regents of the University of California.  All rights reserved.
383ad3fb45Sjoris  * Copyright (c) 2004 Jean-Francois Brousseau.  All rights reserved.
393ad3fb45Sjoris  *
403ad3fb45Sjoris  * Redistribution and use in source and binary forms, with or without
413ad3fb45Sjoris  * modification, are permitted provided that the following conditions
423ad3fb45Sjoris  * are met:
433ad3fb45Sjoris  * 1. Redistributions of source code must retain the above copyright
443ad3fb45Sjoris  *    notice, this list of conditions and the following disclaimer.
453ad3fb45Sjoris  * 2. Redistributions in binary form must reproduce the above copyright
463ad3fb45Sjoris  *    notice, this list of conditions and the following disclaimer in the
473ad3fb45Sjoris  *    documentation and/or other materials provided with the distribution.
483ad3fb45Sjoris  * 3. Neither the name of the University nor the names of its contributors
493ad3fb45Sjoris  *    may be used to endorse or promote products derived from this software
503ad3fb45Sjoris  *    without specific prior written permission.
513ad3fb45Sjoris  *
523ad3fb45Sjoris  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
533ad3fb45Sjoris  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
543ad3fb45Sjoris  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
553ad3fb45Sjoris  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
563ad3fb45Sjoris  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
573ad3fb45Sjoris  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
583ad3fb45Sjoris  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
593ad3fb45Sjoris  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
603ad3fb45Sjoris  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
613ad3fb45Sjoris  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
623ad3fb45Sjoris  * SUCH DAMAGE.
633ad3fb45Sjoris  *
643ad3fb45Sjoris  *	@(#)diffreg.c   8.1 (Berkeley) 6/6/93
653ad3fb45Sjoris  */
663ad3fb45Sjoris /*
673ad3fb45Sjoris  *	Uses an algorithm due to Harold Stone, which finds
683ad3fb45Sjoris  *	a pair of longest identical subsequences in the two
693ad3fb45Sjoris  *	files.
703ad3fb45Sjoris  *
713ad3fb45Sjoris  *	The major goal is to generate the match vector J.
723ad3fb45Sjoris  *	J[i] is the index of the line in file1 corresponding
733ad3fb45Sjoris  *	to line i file0. J[i] = 0 if there is no
743ad3fb45Sjoris  *	such line in file1.
753ad3fb45Sjoris  *
763ad3fb45Sjoris  *	Lines are hashed so as to work in core. All potential
773ad3fb45Sjoris  *	matches are located by sorting the lines of each file
783ad3fb45Sjoris  *	on the hash (called ``value''). In particular, this
793ad3fb45Sjoris  *	collects the equivalence classes in file1 together.
803ad3fb45Sjoris  *	Subroutine equiv replaces the value of each line in
813ad3fb45Sjoris  *	file0 by the index of the first element of its
823ad3fb45Sjoris  *	matching equivalence in (the reordered) file1.
833ad3fb45Sjoris  *	To save space equiv squeezes file1 into a single
843ad3fb45Sjoris  *	array member in which the equivalence classes
853ad3fb45Sjoris  *	are simply concatenated, except that their first
863ad3fb45Sjoris  *	members are flagged by changing sign.
873ad3fb45Sjoris  *
883ad3fb45Sjoris  *	Next the indices that point into member are unsorted into
893ad3fb45Sjoris  *	array class according to the original order of file0.
903ad3fb45Sjoris  *
913ad3fb45Sjoris  *	The cleverness lies in routine stone. This marches
923ad3fb45Sjoris  *	through the lines of file0, developing a vector klist
933ad3fb45Sjoris  *	of "k-candidates". At step i a k-candidate is a matched
943ad3fb45Sjoris  *	pair of lines x,y (x in file0 y in file1) such that
953ad3fb45Sjoris  *	there is a common subsequence of length k
963ad3fb45Sjoris  *	between the first i lines of file0 and the first y
973ad3fb45Sjoris  *	lines of file1, but there is no such subsequence for
983ad3fb45Sjoris  *	any smaller y. x is the earliest possible mate to y
993ad3fb45Sjoris  *	that occurs in such a subsequence.
1003ad3fb45Sjoris  *
1013ad3fb45Sjoris  *	Whenever any of the members of the equivalence class of
1023ad3fb45Sjoris  *	lines in file1 matable to a line in file0 has serial number
1033ad3fb45Sjoris  *	less than the y of some k-candidate, that k-candidate
1043ad3fb45Sjoris  *	with the smallest such y is replaced. The new
1053ad3fb45Sjoris  *	k-candidate is chained (via pred) to the current
1063ad3fb45Sjoris  *	k-1 candidate so that the actual subsequence can
1073ad3fb45Sjoris  *	be recovered. When a member has serial number greater
1083ad3fb45Sjoris  *	that the y of all k-candidates, the klist is extended.
1093ad3fb45Sjoris  *	At the end, the longest subsequence is pulled out
1103ad3fb45Sjoris  *	and placed in the array J by unravel
1113ad3fb45Sjoris  *
1123ad3fb45Sjoris  *	With J in hand, the matches there recorded are
1133ad3fb45Sjoris  *	check'ed against reality to assure that no spurious
1143ad3fb45Sjoris  *	matches have crept in due to hashing. If they have,
1153ad3fb45Sjoris  *	they are broken, and "jackpot" is recorded--a harmless
1163ad3fb45Sjoris  *	matter except that a true match for a spuriously
1173ad3fb45Sjoris  *	mated line may now be unnecessarily reported as a change.
1183ad3fb45Sjoris  *
1193ad3fb45Sjoris  *	Much of the complexity of the program comes simply
1203ad3fb45Sjoris  *	from trying to minimize core utilization and
1213ad3fb45Sjoris  *	maximize the range of doable problems by dynamically
1223ad3fb45Sjoris  *	allocating what is needed and reusing what is not.
1233ad3fb45Sjoris  *	The core requirements for problems larger than somewhat
1243ad3fb45Sjoris  *	are (in words) 2*length(file0) + length(file1) +
1253ad3fb45Sjoris  *	3*(number of k-candidates installed),  typically about
1263ad3fb45Sjoris  *	6n words for files of length n.
1273ad3fb45Sjoris  */
1283ad3fb45Sjoris 
1291f8531bdSotto #include <sys/stat.h>
1303ad3fb45Sjoris 
1311f8531bdSotto #include <ctype.h>
1321f8531bdSotto #include <errno.h>
1331f8531bdSotto #include <regex.h>
1341f8531bdSotto #include <stddef.h>
1351f8531bdSotto #include <string.h>
1361f8531bdSotto #include <unistd.h>
1371f8531bdSotto 
1383ad3fb45Sjoris #include "cvs.h"
1393ad3fb45Sjoris #include "diff.h"
1403ad3fb45Sjoris 
1413ad3fb45Sjoris struct cand {
1423ad3fb45Sjoris 	int	x;
1433ad3fb45Sjoris 	int	y;
1443ad3fb45Sjoris 	int	pred;
1453ad3fb45Sjoris } cand;
1463ad3fb45Sjoris 
1473ad3fb45Sjoris struct line {
1483ad3fb45Sjoris 	int	serial;
1493ad3fb45Sjoris 	int	value;
1503ad3fb45Sjoris } *file[2];
1513ad3fb45Sjoris 
1523ad3fb45Sjoris /*
1533ad3fb45Sjoris  * The following struct is used to record change information when
1543ad3fb45Sjoris  * doing a "context" or "unified" diff.  (see routine "change" to
1553ad3fb45Sjoris  * understand the highly mnemonic field names)
1563ad3fb45Sjoris  */
1573ad3fb45Sjoris struct context_vec {
1583ad3fb45Sjoris 	int	a;		/* start line in old file */
1593ad3fb45Sjoris 	int	b;		/* end line in old file */
1603ad3fb45Sjoris 	int	c;		/* start line in new file */
1613ad3fb45Sjoris 	int	d;		/* end line in new file */
1623ad3fb45Sjoris };
1633ad3fb45Sjoris 
1643ad3fb45Sjoris struct diff_arg {
1653ad3fb45Sjoris 	char	*rev1;
1663ad3fb45Sjoris 	char	*rev2;
1673ad3fb45Sjoris 	char	*date1;
1683ad3fb45Sjoris 	char	*date2;
1693ad3fb45Sjoris };
1703ad3fb45Sjoris 
1713ad3fb45Sjoris static void	 output(FILE *, FILE *);
1723ad3fb45Sjoris static void	 check(FILE *, FILE *);
1733ad3fb45Sjoris static void	 range(int, int, char *);
1743ad3fb45Sjoris static void	 uni_range(int, int);
1753ad3fb45Sjoris static void	 dump_context_vec(FILE *, FILE *);
1763ad3fb45Sjoris static void	 dump_unified_vec(FILE *, FILE *);
1773ad3fb45Sjoris static int	 prepare(int, FILE *, off_t);
1783ad3fb45Sjoris static void	 prune(void);
1793ad3fb45Sjoris static void	 equiv(struct line *, int, struct line *, int, int *);
1803ad3fb45Sjoris static void	 unravel(int);
1813ad3fb45Sjoris static void	 unsort(struct line *, int, int *);
1823ad3fb45Sjoris static void	 change(FILE *, FILE *, int, int, int, int);
1833ad3fb45Sjoris static void	 sort(struct line *, int);
1843ad3fb45Sjoris static int	 ignoreline(char *);
1853ad3fb45Sjoris static int	 asciifile(FILE *);
1863ad3fb45Sjoris static void	 fetch(long *, int, int, FILE *, int, int);
1873ad3fb45Sjoris static int	 newcand(int, int, int);
1883ad3fb45Sjoris static int	 search(int *, int, int);
1893ad3fb45Sjoris static int	 skipline(FILE *);
1903ad3fb45Sjoris static int	 isqrt(int);
1913ad3fb45Sjoris static int	 stone(int *, int, int *, int *);
1923ad3fb45Sjoris static int	 readhash(FILE *);
1933ad3fb45Sjoris static int	 files_differ(FILE *, FILE *);
1943ad3fb45Sjoris static char	*match_function(const long *, int, FILE *);
1953ad3fb45Sjoris static char	*preadline(int, size_t, off_t);
1963ad3fb45Sjoris 
197261cb0daSjoris static int aflag, bflag, dflag, iflag, tflag, Tflag, wflag;
1983ad3fb45Sjoris static int context = 3;
1993ad3fb45Sjoris int diff_format = D_NORMAL;
200261cb0daSjoris int diff_pflag = 0;
2013ad3fb45Sjoris char *diff_file = NULL;
2023ad3fb45Sjoris RCSNUM *diff_rev1 = NULL;
2033ad3fb45Sjoris RCSNUM *diff_rev2 = NULL;
2043ad3fb45Sjoris char diffargs[128];
2053ad3fb45Sjoris static struct stat stb1, stb2;
2063ad3fb45Sjoris static char *ifdefname, *ignore_pats;
2073ad3fb45Sjoris regex_t ignore_re;
2083ad3fb45Sjoris 
2093ad3fb45Sjoris static int  *J;			/* will be overlaid on class */
2103ad3fb45Sjoris static int  *class;		/* will be overlaid on file[0] */
2113ad3fb45Sjoris static int  *klist;		/* will be overlaid on file[0] after class */
2123ad3fb45Sjoris static int  *member;		/* will be overlaid on file[1] */
2133ad3fb45Sjoris static int   clen;
2143ad3fb45Sjoris static int   inifdef;		/* whether or not we are in a #ifdef block */
2153ad3fb45Sjoris static int   diff_len[2];
2163ad3fb45Sjoris static int   pref, suff;	/* length of prefix and suffix */
2173ad3fb45Sjoris static int   slen[2];
2183ad3fb45Sjoris static int   anychange;
2193ad3fb45Sjoris static long *ixnew;		/* will be overlaid on file[1] */
2203ad3fb45Sjoris static long *ixold;		/* will be overlaid on klist */
2213ad3fb45Sjoris static struct cand *clist;	/* merely a free storage pot for candidates */
2223ad3fb45Sjoris static int   clistlen;		/* the length of clist */
2233ad3fb45Sjoris static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
2243ad3fb45Sjoris static u_char *chrtran;		/* translation table for case-folding */
2253ad3fb45Sjoris static struct context_vec *context_vec_start;
2263ad3fb45Sjoris static struct context_vec *context_vec_end;
2273ad3fb45Sjoris static struct context_vec *context_vec_ptr;
2283ad3fb45Sjoris 
229e22e6974Sxsa #define FUNCTION_CONTEXT_SIZE	55
2303ad3fb45Sjoris static char lastbuf[FUNCTION_CONTEXT_SIZE];
2313ad3fb45Sjoris static int lastline;
2323ad3fb45Sjoris static int lastmatchline;
2333ad3fb45Sjoris BUF  *diffbuf = NULL;
2343ad3fb45Sjoris 
2353ad3fb45Sjoris /*
2363ad3fb45Sjoris  * chrtran points to one of 2 translation tables: cup2low if folding upper to
2373ad3fb45Sjoris  * lower case clow2low if not folding case
2383ad3fb45Sjoris  */
2393ad3fb45Sjoris u_char clow2low[256] = {
2403ad3fb45Sjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2413ad3fb45Sjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2423ad3fb45Sjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2433ad3fb45Sjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2443ad3fb45Sjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2453ad3fb45Sjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
2463ad3fb45Sjoris 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
2473ad3fb45Sjoris 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
2483ad3fb45Sjoris 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
2493ad3fb45Sjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2503ad3fb45Sjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2513ad3fb45Sjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2523ad3fb45Sjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2533ad3fb45Sjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2543ad3fb45Sjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2553ad3fb45Sjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2563ad3fb45Sjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2573ad3fb45Sjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2583ad3fb45Sjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2593ad3fb45Sjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2603ad3fb45Sjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2613ad3fb45Sjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2623ad3fb45Sjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2633ad3fb45Sjoris 	0xfd, 0xfe, 0xff
2643ad3fb45Sjoris };
2653ad3fb45Sjoris 
2663ad3fb45Sjoris u_char cup2low[256] = {
2673ad3fb45Sjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2683ad3fb45Sjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2693ad3fb45Sjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2703ad3fb45Sjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2713ad3fb45Sjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2723ad3fb45Sjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
2733ad3fb45Sjoris 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
2743ad3fb45Sjoris 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
2753ad3fb45Sjoris 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
2763ad3fb45Sjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2773ad3fb45Sjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2783ad3fb45Sjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2793ad3fb45Sjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2803ad3fb45Sjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2813ad3fb45Sjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2823ad3fb45Sjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2833ad3fb45Sjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2843ad3fb45Sjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2853ad3fb45Sjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2863ad3fb45Sjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2873ad3fb45Sjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2883ad3fb45Sjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2893ad3fb45Sjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2903ad3fb45Sjoris 	0xfd, 0xfe, 0xff
2913ad3fb45Sjoris };
2923ad3fb45Sjoris 
2933ad3fb45Sjoris int
2943ad3fb45Sjoris cvs_diffreg(const char *file1, const char *file2, BUF *out)
2953ad3fb45Sjoris {
2963ad3fb45Sjoris 	FILE *f1, *f2;
2973ad3fb45Sjoris 	int i, rval;
2983ad3fb45Sjoris 
2993ad3fb45Sjoris 	f1 = f2 = NULL;
3003ad3fb45Sjoris 	rval = D_SAME;
3013ad3fb45Sjoris 	anychange = 0;
3023ad3fb45Sjoris 	lastline = 0;
3033ad3fb45Sjoris 	lastmatchline = 0;
3043ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
3053ad3fb45Sjoris 	chrtran = (iflag ? cup2low : clow2low);
3063ad3fb45Sjoris 	if (out != NULL)
3073ad3fb45Sjoris 		diffbuf = out;
3083ad3fb45Sjoris 
3093ad3fb45Sjoris 	f1 = fopen(file1, "r");
3103ad3fb45Sjoris 	if (f1 == NULL) {
3113ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file1);
3123ad3fb45Sjoris 		goto closem;
3133ad3fb45Sjoris 	}
3143ad3fb45Sjoris 
3153ad3fb45Sjoris 	f2 = fopen(file2, "r");
3163ad3fb45Sjoris 	if (f2 == NULL) {
3173ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file2);
3183ad3fb45Sjoris 		goto closem;
3193ad3fb45Sjoris 	}
3203ad3fb45Sjoris 
3213ad3fb45Sjoris 	if (stat(file1, &stb1) < 0) {
3223ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file1);
3233ad3fb45Sjoris 		goto closem;
3243ad3fb45Sjoris 	}
3253ad3fb45Sjoris 	if (stat(file2, &stb2) < 0) {
3263ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file2);
3273ad3fb45Sjoris 		goto closem;
3283ad3fb45Sjoris 	}
3293ad3fb45Sjoris 	switch (files_differ(f1, f2)) {
3303ad3fb45Sjoris 	case 0:
3313ad3fb45Sjoris 		goto closem;
3323ad3fb45Sjoris 	case 1:
3333ad3fb45Sjoris 		break;
3343ad3fb45Sjoris 	default:
3353ad3fb45Sjoris 		/* error */
3363ad3fb45Sjoris 		goto closem;
3373ad3fb45Sjoris 	}
3383ad3fb45Sjoris 
3393ad3fb45Sjoris 	if (!asciifile(f1) || !asciifile(f2)) {
3403ad3fb45Sjoris 		rval = D_BINARY;
3413ad3fb45Sjoris 		goto closem;
3423ad3fb45Sjoris 	}
3433ad3fb45Sjoris 	if (prepare(0, f1, stb1.st_size) < 0 ||
3443ad3fb45Sjoris 	    prepare(1, f2, stb2.st_size) < 0) {
3453ad3fb45Sjoris 		goto closem;
3463ad3fb45Sjoris 	}
3473ad3fb45Sjoris 	prune();
3483ad3fb45Sjoris 	sort(sfile[0], slen[0]);
3493ad3fb45Sjoris 	sort(sfile[1], slen[1]);
3503ad3fb45Sjoris 
3513ad3fb45Sjoris 	member = (int *)file[1];
3523ad3fb45Sjoris 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
35380566be2Sray 	member = xrealloc(member, slen[1] + 2, sizeof(*member));
3543ad3fb45Sjoris 
3553ad3fb45Sjoris 	class = (int *)file[0];
3563ad3fb45Sjoris 	unsort(sfile[0], slen[0], class);
35780566be2Sray 	class = xrealloc(class, slen[0] + 2, sizeof(*class));
3583ad3fb45Sjoris 
3593ad3fb45Sjoris 	klist = xcalloc(slen[0] + 2, sizeof(*klist));
3603ad3fb45Sjoris 	clen = 0;
3613ad3fb45Sjoris 	clistlen = 100;
3623ad3fb45Sjoris 	clist = xcalloc(clistlen, sizeof(*clist));
3633ad3fb45Sjoris 
3643ad3fb45Sjoris 	if ((i = stone(class, slen[0], member, klist)) < 0)
3653ad3fb45Sjoris 		goto closem;
3663ad3fb45Sjoris 
3673ad3fb45Sjoris 	xfree(member);
3683ad3fb45Sjoris 	xfree(class);
3693ad3fb45Sjoris 
37080566be2Sray 	J = xrealloc(J, diff_len[0] + 2, sizeof(*J));
3713ad3fb45Sjoris 	unravel(klist[i]);
3723ad3fb45Sjoris 	xfree(clist);
3733ad3fb45Sjoris 	xfree(klist);
3743ad3fb45Sjoris 
37580566be2Sray 	ixold = xrealloc(ixold, diff_len[0] + 2, sizeof(*ixold));
3763ad3fb45Sjoris 
37780566be2Sray 	ixnew = xrealloc(ixnew, diff_len[1] + 2, sizeof(*ixnew));
3783ad3fb45Sjoris 	check(f1, f2);
3793ad3fb45Sjoris 	output(f1, f2);
3803ad3fb45Sjoris 
3813ad3fb45Sjoris closem:
3823ad3fb45Sjoris 	if (anychange == 1) {
3833ad3fb45Sjoris 		if (rval == D_SAME)
3843ad3fb45Sjoris 			rval = D_DIFFER;
3853ad3fb45Sjoris 	}
3863ad3fb45Sjoris 	if (f1 != NULL)
3873ad3fb45Sjoris 		fclose(f1);
3883ad3fb45Sjoris 	if (f2 != NULL)
3893ad3fb45Sjoris 		fclose(f2);
3903ad3fb45Sjoris 
3913ad3fb45Sjoris 	return (rval);
3923ad3fb45Sjoris }
3933ad3fb45Sjoris 
3943ad3fb45Sjoris /*
3953ad3fb45Sjoris  * Check to see if the given files differ.
3963ad3fb45Sjoris  * Returns 0 if they are the same, 1 if different, and -1 on error.
3973ad3fb45Sjoris  * XXX - could use code from cmp(1) [faster]
3983ad3fb45Sjoris  */
3993ad3fb45Sjoris static int
4003ad3fb45Sjoris files_differ(FILE *f1, FILE *f2)
4013ad3fb45Sjoris {
4023ad3fb45Sjoris 	char buf1[BUFSIZ], buf2[BUFSIZ];
4033ad3fb45Sjoris 	size_t i, j;
4043ad3fb45Sjoris 
4053ad3fb45Sjoris 	if (stb1.st_size != stb2.st_size)
4063ad3fb45Sjoris 		return (1);
4073ad3fb45Sjoris 	for (;;) {
4083ad3fb45Sjoris 		i = fread(buf1, (size_t)1, sizeof(buf1), f1);
4093ad3fb45Sjoris 		j = fread(buf2, (size_t)1, sizeof(buf2), f2);
4103ad3fb45Sjoris 		if (i != j)
4113ad3fb45Sjoris 			return (1);
4123ad3fb45Sjoris 		if (i == 0 && j == 0) {
4133ad3fb45Sjoris 			if (ferror(f1) || ferror(f2))
4143ad3fb45Sjoris 				return (1);
4153ad3fb45Sjoris 			return (0);
4163ad3fb45Sjoris 		}
4173ad3fb45Sjoris 		if (memcmp(buf1, buf2, i) != 0)
4183ad3fb45Sjoris 			return (1);
4193ad3fb45Sjoris 	}
4203ad3fb45Sjoris }
4213ad3fb45Sjoris 
4223ad3fb45Sjoris static int
4233ad3fb45Sjoris prepare(int i, FILE *fd, off_t filesize)
4243ad3fb45Sjoris {
4253ad3fb45Sjoris 	struct line *p;
4263ad3fb45Sjoris 	int j, h;
4273ad3fb45Sjoris 	size_t sz;
4283ad3fb45Sjoris 
4293ad3fb45Sjoris 	rewind(fd);
4303ad3fb45Sjoris 
4313ad3fb45Sjoris 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
4323ad3fb45Sjoris 	if (sz < 100)
4333ad3fb45Sjoris 		sz = 100;
4343ad3fb45Sjoris 
4353ad3fb45Sjoris 	p = xcalloc(sz + 3, sizeof(*p));
4363ad3fb45Sjoris 	for (j = 0; (h = readhash(fd));) {
4373ad3fb45Sjoris 		if (j == (int)sz) {
4383ad3fb45Sjoris 			sz = sz * 3 / 2;
43980566be2Sray 			p = xrealloc(p, sz + 3, sizeof(*p));
4403ad3fb45Sjoris 		}
4413ad3fb45Sjoris 		p[++j].value = h;
4423ad3fb45Sjoris 	}
4433ad3fb45Sjoris 	diff_len[i] = j;
4443ad3fb45Sjoris 	file[i] = p;
4453ad3fb45Sjoris 
4463ad3fb45Sjoris 	return (0);
4473ad3fb45Sjoris }
4483ad3fb45Sjoris 
4493ad3fb45Sjoris static void
4503ad3fb45Sjoris prune(void)
4513ad3fb45Sjoris {
4523ad3fb45Sjoris 	int i, j;
4533ad3fb45Sjoris 
4543ad3fb45Sjoris 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
4553ad3fb45Sjoris 	    file[0][pref + 1].value == file[1][pref + 1].value;
4563ad3fb45Sjoris 	    pref++)
4573ad3fb45Sjoris 		;
4583ad3fb45Sjoris 	for (suff = 0;
4593ad3fb45Sjoris 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
4603ad3fb45Sjoris 	    (file[0][diff_len[0] - suff].value ==
4613ad3fb45Sjoris 	    file[1][diff_len[1] - suff].value);
4623ad3fb45Sjoris 	    suff++)
4633ad3fb45Sjoris 		;
4643ad3fb45Sjoris 	for (j = 0; j < 2; j++) {
4653ad3fb45Sjoris 		sfile[j] = file[j] + pref;
4663ad3fb45Sjoris 		slen[j] = diff_len[j] - pref - suff;
4673ad3fb45Sjoris 		for (i = 0; i <= slen[j]; i++)
4683ad3fb45Sjoris 			sfile[j][i].serial = i;
4693ad3fb45Sjoris 	}
4703ad3fb45Sjoris }
4713ad3fb45Sjoris 
4723ad3fb45Sjoris static void
4733ad3fb45Sjoris equiv(struct line *a, int n, struct line *b, int m, int *c)
4743ad3fb45Sjoris {
4753ad3fb45Sjoris 	int i, j;
4763ad3fb45Sjoris 
4773ad3fb45Sjoris 	i = j = 1;
4783ad3fb45Sjoris 	while (i <= n && j <= m) {
4793ad3fb45Sjoris 		if (a[i].value < b[j].value)
4803ad3fb45Sjoris 			a[i++].value = 0;
4813ad3fb45Sjoris 		else if (a[i].value == b[j].value)
4823ad3fb45Sjoris 			a[i++].value = j;
4833ad3fb45Sjoris 		else
4843ad3fb45Sjoris 			j++;
4853ad3fb45Sjoris 	}
4863ad3fb45Sjoris 	while (i <= n)
4873ad3fb45Sjoris 		a[i++].value = 0;
4883ad3fb45Sjoris 	b[m + 1].value = 0;
4893ad3fb45Sjoris 	j = 0;
4903ad3fb45Sjoris 	while (++j <= m) {
4913ad3fb45Sjoris 		c[j] = -b[j].serial;
4923ad3fb45Sjoris 		while (b[j + 1].value == b[j].value) {
4933ad3fb45Sjoris 			j++;
4943ad3fb45Sjoris 			c[j] = b[j].serial;
4953ad3fb45Sjoris 		}
4963ad3fb45Sjoris 	}
4973ad3fb45Sjoris 	c[j] = -1;
4983ad3fb45Sjoris }
4993ad3fb45Sjoris 
5003ad3fb45Sjoris /* Code taken from ping.c */
5013ad3fb45Sjoris static int
5023ad3fb45Sjoris isqrt(int n)
5033ad3fb45Sjoris {
5043ad3fb45Sjoris 	int y, x = 1;
5053ad3fb45Sjoris 
5063ad3fb45Sjoris 	if (n == 0)
5073ad3fb45Sjoris 		return (0);
5083ad3fb45Sjoris 
5093ad3fb45Sjoris 	do { /* newton was a stinker */
5103ad3fb45Sjoris 		y = x;
5113ad3fb45Sjoris 		x = n / x;
5123ad3fb45Sjoris 		x += y;
5133ad3fb45Sjoris 		x /= 2;
5143ad3fb45Sjoris 	} while (x - y > 1 || x - y < -1);
5153ad3fb45Sjoris 
5163ad3fb45Sjoris 	return (x);
5173ad3fb45Sjoris }
5183ad3fb45Sjoris 
5193ad3fb45Sjoris static int
5203ad3fb45Sjoris stone(int *a, int n, int *b, int *c)
5213ad3fb45Sjoris {
5223ad3fb45Sjoris 	int ret;
5233ad3fb45Sjoris 	int i, k, y, j, l;
5243ad3fb45Sjoris 	int oldc, tc, oldl;
5253ad3fb45Sjoris 	u_int numtries;
5263ad3fb45Sjoris 
5273ad3fb45Sjoris 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
5283ad3fb45Sjoris 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
5293ad3fb45Sjoris 
5303ad3fb45Sjoris 	k = 0;
5313ad3fb45Sjoris 	if ((ret = newcand(0, 0, 0)) < 0)
5323ad3fb45Sjoris 		return (-1);
5333ad3fb45Sjoris 	c[0] = ret;
5343ad3fb45Sjoris 	for (i = 1; i <= n; i++) {
5353ad3fb45Sjoris 		j = a[i];
5363ad3fb45Sjoris 		if (j == 0)
5373ad3fb45Sjoris 			continue;
5383ad3fb45Sjoris 		y = -b[j];
5393ad3fb45Sjoris 		oldl = 0;
5403ad3fb45Sjoris 		oldc = c[0];
5413ad3fb45Sjoris 		numtries = 0;
5423ad3fb45Sjoris 		do {
5433ad3fb45Sjoris 			if (y <= clist[oldc].y)
5443ad3fb45Sjoris 				continue;
5453ad3fb45Sjoris 			l = search(c, k, y);
5463ad3fb45Sjoris 			if (l != oldl + 1)
5473ad3fb45Sjoris 				oldc = c[l - 1];
5483ad3fb45Sjoris 			if (l <= k) {
5493ad3fb45Sjoris 				if (clist[c[l]].y <= y)
5503ad3fb45Sjoris 					continue;
5513ad3fb45Sjoris 				tc = c[l];
5523ad3fb45Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
5533ad3fb45Sjoris 					return (-1);
5543ad3fb45Sjoris 				c[l] = ret;
5553ad3fb45Sjoris 				oldc = tc;
5563ad3fb45Sjoris 				oldl = l;
5573ad3fb45Sjoris 				numtries++;
5583ad3fb45Sjoris 			} else {
5593ad3fb45Sjoris 				if ((ret = newcand(i, y, oldc)) < 0)
5603ad3fb45Sjoris 					return (-1);
5613ad3fb45Sjoris 				c[l] = ret;
5623ad3fb45Sjoris 				k++;
5633ad3fb45Sjoris 				break;
5643ad3fb45Sjoris 			}
5653ad3fb45Sjoris 		} while ((y = b[++j]) > 0 && numtries < bound);
5663ad3fb45Sjoris 	}
5673ad3fb45Sjoris 	return (k);
5683ad3fb45Sjoris }
5693ad3fb45Sjoris 
5703ad3fb45Sjoris static int
5713ad3fb45Sjoris newcand(int x, int y, int pred)
5723ad3fb45Sjoris {
57380566be2Sray 	struct cand *q;
5743ad3fb45Sjoris 
5753ad3fb45Sjoris 	if (clen == clistlen) {
576*f74aa433Sray 		clistlen = clistlen * 11 / 10;
577*f74aa433Sray 		clist = xrealloc(clist, clistlen, sizeof(*clist));
5783ad3fb45Sjoris 	}
5793ad3fb45Sjoris 	q = clist + clen;
5803ad3fb45Sjoris 	q->x = x;
5813ad3fb45Sjoris 	q->y = y;
5823ad3fb45Sjoris 	q->pred = pred;
5833ad3fb45Sjoris 	return (clen++);
5843ad3fb45Sjoris }
5853ad3fb45Sjoris 
5863ad3fb45Sjoris static int
5873ad3fb45Sjoris search(int *c, int k, int y)
5883ad3fb45Sjoris {
5893ad3fb45Sjoris 	int i, j, l, t;
5903ad3fb45Sjoris 
5913ad3fb45Sjoris 	if (clist[c[k]].y < y)	/* quick look for typical case */
5923ad3fb45Sjoris 		return (k + 1);
5933ad3fb45Sjoris 	i = 0;
5943ad3fb45Sjoris 	j = k + 1;
5953ad3fb45Sjoris 	for (;;) {
5963ad3fb45Sjoris 		l = (i + j) / 2;
5973ad3fb45Sjoris 		if (l <= i)
5983ad3fb45Sjoris 			break;
5993ad3fb45Sjoris 		t = clist[c[l]].y;
6003ad3fb45Sjoris 		if (t > y)
6013ad3fb45Sjoris 			j = l;
6023ad3fb45Sjoris 		else if (t < y)
6033ad3fb45Sjoris 			i = l;
6043ad3fb45Sjoris 		else
6053ad3fb45Sjoris 			return (l);
6063ad3fb45Sjoris 	}
6073ad3fb45Sjoris 	return (l + 1);
6083ad3fb45Sjoris }
6093ad3fb45Sjoris 
6103ad3fb45Sjoris static void
6113ad3fb45Sjoris unravel(int p)
6123ad3fb45Sjoris {
6133ad3fb45Sjoris 	struct cand *q;
6143ad3fb45Sjoris 	int i;
6153ad3fb45Sjoris 
6163ad3fb45Sjoris 	for (i = 0; i <= diff_len[0]; i++)
6173ad3fb45Sjoris 		J[i] = i <= pref ? i :
6183ad3fb45Sjoris 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
6193ad3fb45Sjoris 	for (q = clist + p; q->y != 0; q = clist + q->pred)
6203ad3fb45Sjoris 		J[q->x + pref] = q->y + pref;
6213ad3fb45Sjoris }
6223ad3fb45Sjoris 
6233ad3fb45Sjoris /*
6243ad3fb45Sjoris  * Check does double duty:
6253ad3fb45Sjoris  *  1.	ferret out any fortuitous correspondences due
6263ad3fb45Sjoris  *	to confounding by hashing (which result in "jackpot")
6273ad3fb45Sjoris  *  2.  collect random access indexes to the two files
6283ad3fb45Sjoris  */
6293ad3fb45Sjoris static void
6303ad3fb45Sjoris check(FILE *f1, FILE *f2)
6313ad3fb45Sjoris {
6323ad3fb45Sjoris 	int i, j, jackpot, c, d;
6333ad3fb45Sjoris 	long ctold, ctnew;
6343ad3fb45Sjoris 
6353ad3fb45Sjoris 	rewind(f1);
6363ad3fb45Sjoris 	rewind(f2);
6373ad3fb45Sjoris 	j = 1;
6383ad3fb45Sjoris 	ixold[0] = ixnew[0] = 0;
6393ad3fb45Sjoris 	jackpot = 0;
6403ad3fb45Sjoris 	ctold = ctnew = 0;
6413ad3fb45Sjoris 	for (i = 1; i <= diff_len[0]; i++) {
6423ad3fb45Sjoris 		if (J[i] == 0) {
6433ad3fb45Sjoris 			ixold[i] = ctold += skipline(f1);
6443ad3fb45Sjoris 			continue;
6453ad3fb45Sjoris 		}
6463ad3fb45Sjoris 		while (j < J[i]) {
6473ad3fb45Sjoris 			ixnew[j] = ctnew += skipline(f2);
6483ad3fb45Sjoris 			j++;
6493ad3fb45Sjoris 		}
6503ad3fb45Sjoris 		if (bflag == 1 || wflag == 1 || iflag == 1) {
6513ad3fb45Sjoris 			for (;;) {
6523ad3fb45Sjoris 				c = getc(f1);
6533ad3fb45Sjoris 				d = getc(f2);
6543ad3fb45Sjoris 				/*
6553ad3fb45Sjoris 				 * GNU diff ignores a missing newline
6563ad3fb45Sjoris 				 * in one file if bflag || wflag.
6573ad3fb45Sjoris 				 */
6583ad3fb45Sjoris 				if ((bflag == 1 || wflag == 1) &&
6593ad3fb45Sjoris 				    ((c == EOF && d == '\n') ||
6603ad3fb45Sjoris 				    (c == '\n' && d == EOF))) {
6613ad3fb45Sjoris 					break;
6623ad3fb45Sjoris 				}
6633ad3fb45Sjoris 				ctold++;
6643ad3fb45Sjoris 				ctnew++;
6653ad3fb45Sjoris 				if (bflag == 1 && isspace(c) && isspace(d)) {
6663ad3fb45Sjoris 					do {
6673ad3fb45Sjoris 						if (c == '\n')
6683ad3fb45Sjoris 							break;
6693ad3fb45Sjoris 						ctold++;
6703ad3fb45Sjoris 					} while (isspace(c = getc(f1)));
6713ad3fb45Sjoris 					do {
6723ad3fb45Sjoris 						if (d == '\n')
6733ad3fb45Sjoris 							break;
6743ad3fb45Sjoris 						ctnew++;
6753ad3fb45Sjoris 					} while (isspace(d = getc(f2)));
6763ad3fb45Sjoris 				} else if (wflag == 1) {
6773ad3fb45Sjoris 					while (isspace(c) && c != '\n') {
6783ad3fb45Sjoris 						c = getc(f1);
6793ad3fb45Sjoris 						ctold++;
6803ad3fb45Sjoris 					}
6813ad3fb45Sjoris 					while (isspace(d) && d != '\n') {
6823ad3fb45Sjoris 						d = getc(f2);
6833ad3fb45Sjoris 						ctnew++;
6843ad3fb45Sjoris 					}
6853ad3fb45Sjoris 				}
6863ad3fb45Sjoris 				if (chrtran[c] != chrtran[d]) {
6873ad3fb45Sjoris 					jackpot++;
6883ad3fb45Sjoris 					J[i] = 0;
6893ad3fb45Sjoris 					if (c != '\n' && c != EOF)
6903ad3fb45Sjoris 						ctold += skipline(f1);
6913ad3fb45Sjoris 					if (d != '\n' && c != EOF)
6923ad3fb45Sjoris 						ctnew += skipline(f2);
6933ad3fb45Sjoris 					break;
6943ad3fb45Sjoris 				}
6953ad3fb45Sjoris 				if (c == '\n' || c == EOF)
6963ad3fb45Sjoris 					break;
6973ad3fb45Sjoris 			}
6983ad3fb45Sjoris 		} else {
6993ad3fb45Sjoris 			for (;;) {
7003ad3fb45Sjoris 				ctold++;
7013ad3fb45Sjoris 				ctnew++;
7023ad3fb45Sjoris 				if ((c = getc(f1)) != (d = getc(f2))) {
7033ad3fb45Sjoris 					/* jackpot++; */
7043ad3fb45Sjoris 					J[i] = 0;
7053ad3fb45Sjoris 					if (c != '\n' && c != EOF)
7063ad3fb45Sjoris 						ctold += skipline(f1);
7073ad3fb45Sjoris 					if (d != '\n' && c != EOF)
7083ad3fb45Sjoris 						ctnew += skipline(f2);
7093ad3fb45Sjoris 					break;
7103ad3fb45Sjoris 				}
7113ad3fb45Sjoris 				if (c == '\n' || c == EOF)
7123ad3fb45Sjoris 					break;
7133ad3fb45Sjoris 			}
7143ad3fb45Sjoris 		}
7153ad3fb45Sjoris 		ixold[i] = ctold;
7163ad3fb45Sjoris 		ixnew[j] = ctnew;
7173ad3fb45Sjoris 		j++;
7183ad3fb45Sjoris 	}
7193ad3fb45Sjoris 	for (; j <= diff_len[1]; j++)
7203ad3fb45Sjoris 		ixnew[j] = ctnew += skipline(f2);
7213ad3fb45Sjoris 	/*
7223ad3fb45Sjoris 	 * if (jackpot != 0)
7233ad3fb45Sjoris 	 *	cvs_printf("jackpot\n");
7243ad3fb45Sjoris 	 */
7253ad3fb45Sjoris }
7263ad3fb45Sjoris 
7273ad3fb45Sjoris /* shellsort CACM #201 */
7283ad3fb45Sjoris static void
7293ad3fb45Sjoris sort(struct line *a, int n)
7303ad3fb45Sjoris {
7313ad3fb45Sjoris 	struct line *ai, *aim, w;
7323ad3fb45Sjoris 	int j, m = 0, k;
7333ad3fb45Sjoris 
7343ad3fb45Sjoris 	if (n == 0)
7353ad3fb45Sjoris 		return;
7363ad3fb45Sjoris 	for (j = 1; j <= n; j *= 2)
7373ad3fb45Sjoris 		m = 2 * j - 1;
7383ad3fb45Sjoris 	for (m /= 2; m != 0; m /= 2) {
7393ad3fb45Sjoris 		k = n - m;
7403ad3fb45Sjoris 		for (j = 1; j <= k; j++) {
7413ad3fb45Sjoris 			for (ai = &a[j]; ai > a; ai -= m) {
7423ad3fb45Sjoris 				aim = &ai[m];
7433ad3fb45Sjoris 				if (aim < ai)
7443ad3fb45Sjoris 					break;	/* wraparound */
7453ad3fb45Sjoris 				if (aim->value > ai[0].value ||
7463ad3fb45Sjoris 				    (aim->value == ai[0].value &&
7473ad3fb45Sjoris 					aim->serial > ai[0].serial))
7483ad3fb45Sjoris 					break;
7493ad3fb45Sjoris 				w.value = ai[0].value;
7503ad3fb45Sjoris 				ai[0].value = aim->value;
7513ad3fb45Sjoris 				aim->value = w.value;
7523ad3fb45Sjoris 				w.serial = ai[0].serial;
7533ad3fb45Sjoris 				ai[0].serial = aim->serial;
7543ad3fb45Sjoris 				aim->serial = w.serial;
7553ad3fb45Sjoris 			}
7563ad3fb45Sjoris 		}
7573ad3fb45Sjoris 	}
7583ad3fb45Sjoris }
7593ad3fb45Sjoris 
7603ad3fb45Sjoris static void
7613ad3fb45Sjoris unsort(struct line *f, int l, int *b)
7623ad3fb45Sjoris {
7633ad3fb45Sjoris 	int *a, i;
7643ad3fb45Sjoris 
7653ad3fb45Sjoris 	a = xcalloc(l + 1, sizeof(*a));
7663ad3fb45Sjoris 	for (i = 1; i <= l; i++)
7673ad3fb45Sjoris 		a[f[i].serial] = f[i].value;
7683ad3fb45Sjoris 	for (i = 1; i <= l; i++)
7693ad3fb45Sjoris 		b[i] = a[i];
7703ad3fb45Sjoris 	xfree(a);
7713ad3fb45Sjoris }
7723ad3fb45Sjoris 
7733ad3fb45Sjoris static int
7743ad3fb45Sjoris skipline(FILE *f)
7753ad3fb45Sjoris {
7763ad3fb45Sjoris 	int i, c;
7773ad3fb45Sjoris 
7783ad3fb45Sjoris 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
7793ad3fb45Sjoris 		continue;
7803ad3fb45Sjoris 	return (i);
7813ad3fb45Sjoris }
7823ad3fb45Sjoris 
7833ad3fb45Sjoris static void
7843ad3fb45Sjoris output(FILE *f1, FILE *f2)
7853ad3fb45Sjoris {
7863ad3fb45Sjoris 	int m, i0, i1, j0, j1;
7873ad3fb45Sjoris 
7883ad3fb45Sjoris 	rewind(f1);
7893ad3fb45Sjoris 	rewind(f2);
7903ad3fb45Sjoris 	m = diff_len[0];
7913ad3fb45Sjoris 	J[0] = 0;
7923ad3fb45Sjoris 	J[m + 1] = diff_len[1] + 1;
7933ad3fb45Sjoris 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
7943ad3fb45Sjoris 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
7953ad3fb45Sjoris 			i0++;
7963ad3fb45Sjoris 		j0 = J[i0 - 1] + 1;
7973ad3fb45Sjoris 		i1 = i0 - 1;
7983ad3fb45Sjoris 		while (i1 < m && J[i1 + 1] == 0)
7993ad3fb45Sjoris 			i1++;
8003ad3fb45Sjoris 		j1 = J[i1 + 1] - 1;
8013ad3fb45Sjoris 		J[i1] = j1;
8023ad3fb45Sjoris 		change(f1, f2, i0, i1, j0, j1);
8033ad3fb45Sjoris 	}
8043ad3fb45Sjoris 	if (m == 0)
8053ad3fb45Sjoris 		change(f1, f2, 1, 0, 1, diff_len[1]);
8063ad3fb45Sjoris 	if (diff_format == D_IFDEF) {
8073ad3fb45Sjoris 		for (;;) {
8083ad3fb45Sjoris #define	c i0
8093ad3fb45Sjoris 			if ((c = getc(f1)) == EOF)
8103ad3fb45Sjoris 				return;
8113ad3fb45Sjoris 			diff_output("%c", c);
8123ad3fb45Sjoris 		}
8133ad3fb45Sjoris #undef c
8143ad3fb45Sjoris 	}
8153ad3fb45Sjoris 	if (anychange != 0) {
8163ad3fb45Sjoris 		if (diff_format == D_CONTEXT)
8173ad3fb45Sjoris 			dump_context_vec(f1, f2);
8183ad3fb45Sjoris 		else if (diff_format == D_UNIFIED)
8193ad3fb45Sjoris 			dump_unified_vec(f1, f2);
8203ad3fb45Sjoris 	}
8213ad3fb45Sjoris }
8223ad3fb45Sjoris 
8233ad3fb45Sjoris static __inline void
8243ad3fb45Sjoris range(int a, int b, char *separator)
8253ad3fb45Sjoris {
8263ad3fb45Sjoris 	diff_output("%d", a > b ? b : a);
8273ad3fb45Sjoris 	if (a < b)
8283ad3fb45Sjoris 		diff_output("%s%d", separator, b);
8293ad3fb45Sjoris }
8303ad3fb45Sjoris 
8313ad3fb45Sjoris static __inline void
8323ad3fb45Sjoris uni_range(int a, int b)
8333ad3fb45Sjoris {
8343ad3fb45Sjoris 	if (a < b)
8353ad3fb45Sjoris 		diff_output("%d,%d", a, b - a + 1);
8363ad3fb45Sjoris 	else if (a == b)
8373ad3fb45Sjoris 		diff_output("%d", b);
8383ad3fb45Sjoris 	else
8393ad3fb45Sjoris 		diff_output("%d,0", b);
8403ad3fb45Sjoris }
8413ad3fb45Sjoris 
8423ad3fb45Sjoris static char *
8433ad3fb45Sjoris preadline(int fd, size_t rlen, off_t off)
8443ad3fb45Sjoris {
8453ad3fb45Sjoris 	char *line;
8463ad3fb45Sjoris 	ssize_t nr;
8473ad3fb45Sjoris 
8483ad3fb45Sjoris 	line = xmalloc(rlen + 1);
8493ad3fb45Sjoris 	if ((nr = pread(fd, line, rlen, off)) < 0) {
8503ad3fb45Sjoris 		cvs_log(LP_ERR, "preadline failed");
8513ad3fb45Sjoris 		return (NULL);
8523ad3fb45Sjoris 	}
8533ad3fb45Sjoris 	line[nr] = '\0';
8543ad3fb45Sjoris 	return (line);
8553ad3fb45Sjoris }
8563ad3fb45Sjoris 
8573ad3fb45Sjoris static int
8583ad3fb45Sjoris ignoreline(char *line)
8593ad3fb45Sjoris {
8603ad3fb45Sjoris 	int ret;
8613ad3fb45Sjoris 
8623ad3fb45Sjoris 	ret = regexec(&ignore_re, line, (size_t)0, NULL, 0);
8633ad3fb45Sjoris 	xfree(line);
8643ad3fb45Sjoris 	return (ret == 0);	/* if it matched, it should be ignored. */
8653ad3fb45Sjoris }
8663ad3fb45Sjoris 
8673ad3fb45Sjoris /*
8683ad3fb45Sjoris  * Indicate that there is a difference between lines a and b of the from file
8693ad3fb45Sjoris  * to get to lines c to d of the to file.  If a is greater then b then there
8703ad3fb45Sjoris  * are no lines in the from file involved and this means that there were
8713ad3fb45Sjoris  * lines appended (beginning at b).  If c is greater than d then there are
8723ad3fb45Sjoris  * lines missing from the to file.
8733ad3fb45Sjoris  */
8743ad3fb45Sjoris static void
8753ad3fb45Sjoris change(FILE *f1, FILE *f2, int a, int b, int c, int d)
8763ad3fb45Sjoris {
8773ad3fb45Sjoris 	int i;
8783ad3fb45Sjoris 	static size_t max_context = 64;
8793ad3fb45Sjoris 	char buf[64];
8803ad3fb45Sjoris 	struct tm *t;
8813ad3fb45Sjoris 
8823ad3fb45Sjoris 	if (diff_format != D_IFDEF && a > b && c > d)
8833ad3fb45Sjoris 		return;
8843ad3fb45Sjoris 	if (ignore_pats != NULL) {
8853ad3fb45Sjoris 		char *line;
8863ad3fb45Sjoris 		/*
8873ad3fb45Sjoris 		 * All lines in the change, insert, or delete must
8883ad3fb45Sjoris 		 * match an ignore pattern for the change to be
8893ad3fb45Sjoris 		 * ignored.
8903ad3fb45Sjoris 		 */
8913ad3fb45Sjoris 		if (a <= b) {		/* Changes and deletes. */
8923ad3fb45Sjoris 			for (i = a; i <= b; i++) {
8933ad3fb45Sjoris 				line = preadline(fileno(f1),
8943ad3fb45Sjoris 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
8953ad3fb45Sjoris 				if (!ignoreline(line))
8963ad3fb45Sjoris 					goto proceed;
8973ad3fb45Sjoris 			}
8983ad3fb45Sjoris 		}
8993ad3fb45Sjoris 		if (a > b || c <= d) {	/* Changes and inserts. */
9003ad3fb45Sjoris 			for (i = c; i <= d; i++) {
9013ad3fb45Sjoris 				line = preadline(fileno(f2),
9023ad3fb45Sjoris 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
9033ad3fb45Sjoris 				if (!ignoreline(line))
9043ad3fb45Sjoris 					goto proceed;
9053ad3fb45Sjoris 			}
9063ad3fb45Sjoris 		}
9073ad3fb45Sjoris 		return;
9083ad3fb45Sjoris 	}
9093ad3fb45Sjoris proceed:
9103ad3fb45Sjoris 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
9113ad3fb45Sjoris 		/*
9123ad3fb45Sjoris 		 * Allocate change records as needed.
9133ad3fb45Sjoris 		 */
9143ad3fb45Sjoris 		if (context_vec_ptr == context_vec_end - 1) {
9153ad3fb45Sjoris 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
9163ad3fb45Sjoris 			max_context <<= 1;
91780566be2Sray 			context_vec_start = xrealloc(context_vec_start,
91880566be2Sray 			    max_context, sizeof(*context_vec_start));
9193ad3fb45Sjoris 			context_vec_end = context_vec_start + max_context;
9203ad3fb45Sjoris 			context_vec_ptr = context_vec_start + offset;
9213ad3fb45Sjoris 		}
9223ad3fb45Sjoris 		if (anychange == 0) {
9233ad3fb45Sjoris 			/*
9243ad3fb45Sjoris 			 * Print the context/unidiff header first time through.
9253ad3fb45Sjoris 			 */
9263ad3fb45Sjoris 			t = localtime(&stb1.st_mtime);
9273ad3fb45Sjoris 			(void)strftime(buf, sizeof(buf),
9283ad3fb45Sjoris 			    "%d %b %G %H:%M:%S", t);
9293ad3fb45Sjoris 
9303ad3fb45Sjoris 			diff_output("%s %s	%s",
9313ad3fb45Sjoris 			    diff_format == D_CONTEXT ? "***" : "---", diff_file,
9323ad3fb45Sjoris 			    buf);
9333ad3fb45Sjoris 
9343ad3fb45Sjoris 			if (diff_rev1 != NULL) {
9353ad3fb45Sjoris 				rcsnum_tostr(diff_rev1, buf, sizeof(buf));
9363ad3fb45Sjoris 				diff_output("\t%s", buf);
9373ad3fb45Sjoris 			}
9383ad3fb45Sjoris 
9399fac60a5Sjoris 			diff_output("\n");
9403ad3fb45Sjoris 
9413ad3fb45Sjoris 			t = localtime(&stb2.st_mtime);
9423ad3fb45Sjoris 			(void)strftime(buf, sizeof(buf),
9433ad3fb45Sjoris 			    "%d %b %G %H:%M:%S", t);
9443ad3fb45Sjoris 
9453ad3fb45Sjoris 			diff_output("%s %s	%s",
9463ad3fb45Sjoris 			    diff_format == D_CONTEXT ? "---" : "+++", diff_file,
9473ad3fb45Sjoris 			    buf);
9483ad3fb45Sjoris 
9493ad3fb45Sjoris 			if (diff_rev2 != NULL) {
9503ad3fb45Sjoris 				rcsnum_tostr(diff_rev2, buf, sizeof(buf));
9513ad3fb45Sjoris 				diff_output("\t%s", buf);
9523ad3fb45Sjoris 			}
9533ad3fb45Sjoris 
9549fac60a5Sjoris 			diff_output("\n");
9553ad3fb45Sjoris 			anychange = 1;
9563ad3fb45Sjoris 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
9573ad3fb45Sjoris 		    c > context_vec_ptr->d + (2 * context) + 1) {
9583ad3fb45Sjoris 			/*
9593ad3fb45Sjoris 			 * If this change is more than 'context' lines from the
9603ad3fb45Sjoris 			 * previous change, dump the record and reset it.
9613ad3fb45Sjoris 			 */
9623ad3fb45Sjoris 			if (diff_format == D_CONTEXT)
9633ad3fb45Sjoris 				dump_context_vec(f1, f2);
9643ad3fb45Sjoris 			else
9653ad3fb45Sjoris 				dump_unified_vec(f1, f2);
9663ad3fb45Sjoris 		}
9673ad3fb45Sjoris 		context_vec_ptr++;
9683ad3fb45Sjoris 		context_vec_ptr->a = a;
9693ad3fb45Sjoris 		context_vec_ptr->b = b;
9703ad3fb45Sjoris 		context_vec_ptr->c = c;
9713ad3fb45Sjoris 		context_vec_ptr->d = d;
9723ad3fb45Sjoris 		return;
9733ad3fb45Sjoris 	}
9743ad3fb45Sjoris 	if (anychange == 0)
9753ad3fb45Sjoris 		anychange = 1;
9763ad3fb45Sjoris 	switch (diff_format) {
9773ad3fb45Sjoris 	case D_BRIEF:
9783ad3fb45Sjoris 		return;
9793ad3fb45Sjoris 	case D_NORMAL:
9803ad3fb45Sjoris 		range(a, b, ",");
9813ad3fb45Sjoris 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
9823ad3fb45Sjoris 		if (diff_format == D_NORMAL)
9833ad3fb45Sjoris 			range(c, d, ",");
9843ad3fb45Sjoris 		diff_output("\n");
9853ad3fb45Sjoris 		break;
9863ad3fb45Sjoris 	case D_RCSDIFF:
9873ad3fb45Sjoris 		if (a > b)
9883ad3fb45Sjoris 			diff_output("a%d %d\n", b, d - c + 1);
9893ad3fb45Sjoris 		else {
9903ad3fb45Sjoris 			diff_output("d%d %d\n", a, b - a + 1);
9913ad3fb45Sjoris 
9923ad3fb45Sjoris 			if (!(c > d))	/* add changed lines */
9933ad3fb45Sjoris 				diff_output("a%d %d\n", b, d - c + 1);
9943ad3fb45Sjoris 		}
9953ad3fb45Sjoris 		break;
9963ad3fb45Sjoris 	}
9973ad3fb45Sjoris 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
9983ad3fb45Sjoris 		fetch(ixold, a, b, f1, '<', 1);
9993ad3fb45Sjoris 		if (a <= b && c <= d && diff_format == D_NORMAL)
10003ad3fb45Sjoris 			diff_output("---\n");
10013ad3fb45Sjoris 	}
10023ad3fb45Sjoris 	fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0);
10033ad3fb45Sjoris 	if (inifdef) {
10043ad3fb45Sjoris 		diff_output("#endif /* %s */\n", ifdefname);
10053ad3fb45Sjoris 		inifdef = 0;
10063ad3fb45Sjoris 	}
10073ad3fb45Sjoris }
10083ad3fb45Sjoris 
10093ad3fb45Sjoris static void
10103ad3fb45Sjoris fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
10113ad3fb45Sjoris {
10123ad3fb45Sjoris 	long j, nc;
10133ad3fb45Sjoris 	int i, c, col;
10143ad3fb45Sjoris 
10153ad3fb45Sjoris 	/*
10163ad3fb45Sjoris 	 * When doing #ifdef's, copy down to current line
10173ad3fb45Sjoris 	 * if this is the first file, so that stuff makes it to output.
10183ad3fb45Sjoris 	 */
10193ad3fb45Sjoris 	if (diff_format == D_IFDEF && oldfile) {
10203ad3fb45Sjoris 		long curpos = ftell(lb);
10213ad3fb45Sjoris 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
10223ad3fb45Sjoris 		nc = f[a > b ? b : a - 1] - curpos;
10233ad3fb45Sjoris 		for (i = 0; i < nc; i++)
10243ad3fb45Sjoris 			diff_output("%c", getc(lb));
10253ad3fb45Sjoris 	}
10263ad3fb45Sjoris 	if (a > b)
10273ad3fb45Sjoris 		return;
10283ad3fb45Sjoris 	if (diff_format == D_IFDEF) {
10293ad3fb45Sjoris 		if (inifdef) {
10303ad3fb45Sjoris 			diff_output("#else /* %s%s */\n",
10313ad3fb45Sjoris 			    oldfile == 1 ? "!" : "", ifdefname);
10323ad3fb45Sjoris 		} else {
10333ad3fb45Sjoris 			if (oldfile)
10343ad3fb45Sjoris 				diff_output("#ifndef %s\n", ifdefname);
10353ad3fb45Sjoris 			else
10363ad3fb45Sjoris 				diff_output("#ifdef %s\n", ifdefname);
10373ad3fb45Sjoris 		}
10383ad3fb45Sjoris 		inifdef = 1 + oldfile;
10393ad3fb45Sjoris 	}
10403ad3fb45Sjoris 	for (i = a; i <= b; i++) {
10413ad3fb45Sjoris 		fseek(lb, f[i - 1], SEEK_SET);
10423ad3fb45Sjoris 		nc = f[i] - f[i - 1];
10433ad3fb45Sjoris 		if (diff_format != D_IFDEF && ch != '\0') {
10443ad3fb45Sjoris 			diff_output("%c", ch);
10453ad3fb45Sjoris 			if (Tflag == 1 && (diff_format == D_NORMAL ||
10463ad3fb45Sjoris 			    diff_format == D_CONTEXT ||
10473ad3fb45Sjoris 			    diff_format == D_UNIFIED))
10483ad3fb45Sjoris 				diff_output("\t");
10493ad3fb45Sjoris 			else if (diff_format != D_UNIFIED)
10503ad3fb45Sjoris 				diff_output(" ");
10513ad3fb45Sjoris 		}
10523ad3fb45Sjoris 		col = 0;
10533ad3fb45Sjoris 		for (j = 0; j < nc; j++) {
10543ad3fb45Sjoris 			if ((c = getc(lb)) == EOF) {
10553ad3fb45Sjoris 				if (diff_format == D_RCSDIFF)
10563ad3fb45Sjoris 					cvs_log(LP_ERR,
10573ad3fb45Sjoris 					    "No newline at end of file");
10583ad3fb45Sjoris 				else
10593ad3fb45Sjoris 					diff_output("\n\\ No newline at end of "
10603ad3fb45Sjoris 					    "file");
10613ad3fb45Sjoris 				return;
10623ad3fb45Sjoris 			}
10633ad3fb45Sjoris 			if (c == '\t' && tflag == 1) {
10643ad3fb45Sjoris 				do {
10653ad3fb45Sjoris 					diff_output(" ");
10663ad3fb45Sjoris 				} while (++col & 7);
10673ad3fb45Sjoris 			} else {
10683ad3fb45Sjoris 				diff_output("%c", c);
10693ad3fb45Sjoris 				col++;
10703ad3fb45Sjoris 			}
10713ad3fb45Sjoris 		}
10723ad3fb45Sjoris 	}
10733ad3fb45Sjoris }
10743ad3fb45Sjoris 
10753ad3fb45Sjoris /*
10763ad3fb45Sjoris  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
10773ad3fb45Sjoris  */
10783ad3fb45Sjoris static int
10793ad3fb45Sjoris readhash(FILE *f)
10803ad3fb45Sjoris {
10813ad3fb45Sjoris 	int i, t, space;
10823ad3fb45Sjoris 	int sum;
10833ad3fb45Sjoris 
10843ad3fb45Sjoris 	sum = 1;
10853ad3fb45Sjoris 	space = 0;
10863ad3fb45Sjoris 	if (bflag != 1 && wflag != 1) {
10873ad3fb45Sjoris 		if (iflag == 1)
10883ad3fb45Sjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
10893ad3fb45Sjoris 				if (t == EOF) {
10903ad3fb45Sjoris 					if (i == 0)
10913ad3fb45Sjoris 						return (0);
10923ad3fb45Sjoris 					break;
10933ad3fb45Sjoris 				}
10943ad3fb45Sjoris 				sum = sum * 127 + chrtran[t];
10953ad3fb45Sjoris 			}
10963ad3fb45Sjoris 		else
10973ad3fb45Sjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
10983ad3fb45Sjoris 				if (t == EOF) {
10993ad3fb45Sjoris 					if (i == 0)
11003ad3fb45Sjoris 						return (0);
11013ad3fb45Sjoris 					break;
11023ad3fb45Sjoris 				}
11033ad3fb45Sjoris 				sum = sum * 127 + t;
11043ad3fb45Sjoris 			}
11053ad3fb45Sjoris 	} else {
11063ad3fb45Sjoris 		for (i = 0;;) {
11073ad3fb45Sjoris 			switch (t = getc(f)) {
11083ad3fb45Sjoris 			case '\t':
11093ad3fb45Sjoris 			case ' ':
11103ad3fb45Sjoris 				space++;
11113ad3fb45Sjoris 				continue;
11123ad3fb45Sjoris 			default:
11133ad3fb45Sjoris 				if (space != 0 && wflag != 1) {
11143ad3fb45Sjoris 					i++;
11153ad3fb45Sjoris 					space = 0;
11163ad3fb45Sjoris 				}
11173ad3fb45Sjoris 				sum = sum * 127 + chrtran[t];
11183ad3fb45Sjoris 				i++;
11193ad3fb45Sjoris 				continue;
11203ad3fb45Sjoris 			case EOF:
11213ad3fb45Sjoris 				if (i == 0)
11223ad3fb45Sjoris 					return (0);
11233ad3fb45Sjoris 				/* FALLTHROUGH */
11243ad3fb45Sjoris 			case '\n':
11253ad3fb45Sjoris 				break;
11263ad3fb45Sjoris 			}
11273ad3fb45Sjoris 			break;
11283ad3fb45Sjoris 		}
11293ad3fb45Sjoris 	}
11303ad3fb45Sjoris 	/*
11313ad3fb45Sjoris 	 * There is a remote possibility that we end up with a zero sum.
11323ad3fb45Sjoris 	 * Zero is used as an EOF marker, so return 1 instead.
11333ad3fb45Sjoris 	 */
11343ad3fb45Sjoris 	return (sum == 0 ? 1 : sum);
11353ad3fb45Sjoris }
11363ad3fb45Sjoris 
11373ad3fb45Sjoris static int
11383ad3fb45Sjoris asciifile(FILE *f)
11393ad3fb45Sjoris {
11403ad3fb45Sjoris 	char buf[BUFSIZ];
11413ad3fb45Sjoris 	size_t i, cnt;
11423ad3fb45Sjoris 
11433ad3fb45Sjoris 	if (aflag == 1 || f == NULL)
11443ad3fb45Sjoris 		return (1);
11453ad3fb45Sjoris 
11463ad3fb45Sjoris 	rewind(f);
11473ad3fb45Sjoris 	cnt = fread(buf, (size_t)1, sizeof(buf), f);
11483ad3fb45Sjoris 	for (i = 0; i < cnt; i++)
11493ad3fb45Sjoris 		if (!isprint(buf[i]) && !isspace(buf[i]))
11503ad3fb45Sjoris 			return (0);
11513ad3fb45Sjoris 	return (1);
11523ad3fb45Sjoris }
11533ad3fb45Sjoris 
1154e22e6974Sxsa #define begins_with(s, pre) (strncmp(s, pre, sizeof(pre)-1) == 0)
1155e22e6974Sxsa 
11563ad3fb45Sjoris static char*
11573ad3fb45Sjoris match_function(const long *f, int pos, FILE *fp)
11583ad3fb45Sjoris {
11593ad3fb45Sjoris 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
11603ad3fb45Sjoris 	size_t nc;
11613ad3fb45Sjoris 	int last = lastline;
11623ad3fb45Sjoris 	char *p;
1163e22e6974Sxsa 	char *state = NULL;
11643ad3fb45Sjoris 
11653ad3fb45Sjoris 	lastline = pos;
11663ad3fb45Sjoris 	while (pos > last) {
11673ad3fb45Sjoris 		fseek(fp, f[pos - 1], SEEK_SET);
11683ad3fb45Sjoris 		nc = f[pos] - f[pos - 1];
11693ad3fb45Sjoris 		if (nc >= sizeof(buf))
11703ad3fb45Sjoris 			nc = sizeof(buf) - 1;
11713ad3fb45Sjoris 		nc = fread(buf, (size_t)1, nc, fp);
11723ad3fb45Sjoris 		if (nc > 0) {
11733ad3fb45Sjoris 			buf[nc] = '\0';
11743ad3fb45Sjoris 			p = strchr((const char *)buf, '\n');
11753ad3fb45Sjoris 			if (p != NULL)
11763ad3fb45Sjoris 				*p = '\0';
11773ad3fb45Sjoris 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
1178e22e6974Sxsa 				if (begins_with(buf, "private:")) {
1179e22e6974Sxsa 					if (!state)
1180e22e6974Sxsa 						state = " (private)";
1181e22e6974Sxsa 				} else if (begins_with(buf, "protected:")) {
1182e22e6974Sxsa 					if (!state)
1183e22e6974Sxsa 						state = " (protected)";
1184e22e6974Sxsa 				} else if (begins_with(buf, "public:")) {
1185e22e6974Sxsa 					if (!state)
1186e22e6974Sxsa 						state = " (public)";
1187e22e6974Sxsa 				} else {
11883ad3fb45Sjoris 					strlcpy(lastbuf, (const char *)buf,
11893ad3fb45Sjoris 					    sizeof lastbuf);
1190e22e6974Sxsa 					if (state)
1191e22e6974Sxsa 						strlcat(lastbuf, state,
1192e22e6974Sxsa 						    sizeof lastbuf);
11933ad3fb45Sjoris 					lastmatchline = pos;
11943ad3fb45Sjoris 					return lastbuf;
11953ad3fb45Sjoris 				}
11963ad3fb45Sjoris 			}
1197e22e6974Sxsa 		}
11983ad3fb45Sjoris 		pos--;
11993ad3fb45Sjoris 	}
12003ad3fb45Sjoris 	return (lastmatchline > 0) ? lastbuf : NULL;
12013ad3fb45Sjoris }
12023ad3fb45Sjoris 
12033ad3fb45Sjoris 
12043ad3fb45Sjoris /* dump accumulated "context" diff changes */
12053ad3fb45Sjoris static void
12063ad3fb45Sjoris dump_context_vec(FILE *f1, FILE *f2)
12073ad3fb45Sjoris {
12083ad3fb45Sjoris 	struct context_vec *cvp = context_vec_start;
12093ad3fb45Sjoris 	int lowa, upb, lowc, upd, do_output;
12103ad3fb45Sjoris 	int a, b, c, d;
12113ad3fb45Sjoris 	char ch, *f;
12123ad3fb45Sjoris 
12133ad3fb45Sjoris 	if (context_vec_start > context_vec_ptr)
12143ad3fb45Sjoris 		return;
12153ad3fb45Sjoris 
12163ad3fb45Sjoris 	b = d = 0;		/* gcc */
12173ad3fb45Sjoris 	lowa = MAX(1, cvp->a - context);
12183ad3fb45Sjoris 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
12193ad3fb45Sjoris 	lowc = MAX(1, cvp->c - context);
12203ad3fb45Sjoris 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
12213ad3fb45Sjoris 
12223ad3fb45Sjoris 	diff_output("***************");
1223261cb0daSjoris 	if (diff_pflag == 1) {
12243ad3fb45Sjoris 		f = match_function(ixold, lowa - 1, f1);
12253ad3fb45Sjoris 		if (f != NULL) {
12263ad3fb45Sjoris 			diff_output(" ");
12273ad3fb45Sjoris 			diff_output("%s", f);
12283ad3fb45Sjoris 		}
12293ad3fb45Sjoris 	}
12303ad3fb45Sjoris 	diff_output("\n*** ");
12313ad3fb45Sjoris 	range(lowa, upb, ",");
12323ad3fb45Sjoris 	diff_output(" ****\n");
12333ad3fb45Sjoris 
12343ad3fb45Sjoris 	/*
12353ad3fb45Sjoris 	 * Output changes to the "old" file.  The first loop suppresses
12363ad3fb45Sjoris 	 * output if there were no changes to the "old" file (we'll see
12373ad3fb45Sjoris 	 * the "old" lines as context in the "new" list).
12383ad3fb45Sjoris 	 */
12393ad3fb45Sjoris 	do_output = 0;
12403ad3fb45Sjoris 	for (; cvp <= context_vec_ptr; cvp++)
12413ad3fb45Sjoris 		if (cvp->a <= cvp->b) {
12423ad3fb45Sjoris 			cvp = context_vec_start;
12433ad3fb45Sjoris 			do_output++;
12443ad3fb45Sjoris 			break;
12453ad3fb45Sjoris 		}
12463ad3fb45Sjoris 	if (do_output != 0) {
12473ad3fb45Sjoris 		while (cvp <= context_vec_ptr) {
12483ad3fb45Sjoris 			a = cvp->a;
12493ad3fb45Sjoris 			b = cvp->b;
12503ad3fb45Sjoris 			c = cvp->c;
12513ad3fb45Sjoris 			d = cvp->d;
12523ad3fb45Sjoris 
12533ad3fb45Sjoris 			if (a <= b && c <= d)
12543ad3fb45Sjoris 				ch = 'c';
12553ad3fb45Sjoris 			else
12563ad3fb45Sjoris 				ch = (a <= b) ? 'd' : 'a';
12573ad3fb45Sjoris 
12583ad3fb45Sjoris 			if (ch == 'a')
12593ad3fb45Sjoris 				fetch(ixold, lowa, b, f1, ' ', 0);
12603ad3fb45Sjoris 			else {
12613ad3fb45Sjoris 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
12623ad3fb45Sjoris 				fetch(ixold, a, b, f1,
12633ad3fb45Sjoris 				    ch == 'c' ? '!' : '-', 0);
12643ad3fb45Sjoris 			}
12653ad3fb45Sjoris 			lowa = b + 1;
12663ad3fb45Sjoris 			cvp++;
12673ad3fb45Sjoris 		}
12683ad3fb45Sjoris 		fetch(ixold, b + 1, upb, f1, ' ', 0);
12693ad3fb45Sjoris 	}
12703ad3fb45Sjoris 	/* output changes to the "new" file */
12713ad3fb45Sjoris 	diff_output("--- ");
12723ad3fb45Sjoris 	range(lowc, upd, ",");
12733ad3fb45Sjoris 	diff_output(" ----\n");
12743ad3fb45Sjoris 
12753ad3fb45Sjoris 	do_output = 0;
12763ad3fb45Sjoris 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
12773ad3fb45Sjoris 		if (cvp->c <= cvp->d) {
12783ad3fb45Sjoris 			cvp = context_vec_start;
12793ad3fb45Sjoris 			do_output++;
12803ad3fb45Sjoris 			break;
12813ad3fb45Sjoris 		}
12823ad3fb45Sjoris 	if (do_output != 0) {
12833ad3fb45Sjoris 		while (cvp <= context_vec_ptr) {
12843ad3fb45Sjoris 			a = cvp->a;
12853ad3fb45Sjoris 			b = cvp->b;
12863ad3fb45Sjoris 			c = cvp->c;
12873ad3fb45Sjoris 			d = cvp->d;
12883ad3fb45Sjoris 
12893ad3fb45Sjoris 			if (a <= b && c <= d)
12903ad3fb45Sjoris 				ch = 'c';
12913ad3fb45Sjoris 			else
12923ad3fb45Sjoris 				ch = (a <= b) ? 'd' : 'a';
12933ad3fb45Sjoris 
12943ad3fb45Sjoris 			if (ch == 'd')
12953ad3fb45Sjoris 				fetch(ixnew, lowc, d, f2, ' ', 0);
12963ad3fb45Sjoris 			else {
12973ad3fb45Sjoris 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
12983ad3fb45Sjoris 				fetch(ixnew, c, d, f2,
12993ad3fb45Sjoris 				    ch == 'c' ? '!' : '+', 0);
13003ad3fb45Sjoris 			}
13013ad3fb45Sjoris 			lowc = d + 1;
13023ad3fb45Sjoris 			cvp++;
13033ad3fb45Sjoris 		}
13043ad3fb45Sjoris 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
13053ad3fb45Sjoris 	}
13063ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
13073ad3fb45Sjoris }
13083ad3fb45Sjoris 
13093ad3fb45Sjoris /* dump accumulated "unified" diff changes */
13103ad3fb45Sjoris static void
13113ad3fb45Sjoris dump_unified_vec(FILE *f1, FILE *f2)
13123ad3fb45Sjoris {
13133ad3fb45Sjoris 	struct context_vec *cvp = context_vec_start;
13143ad3fb45Sjoris 	int lowa, upb, lowc, upd;
13153ad3fb45Sjoris 	int a, b, c, d;
13163ad3fb45Sjoris 	char ch, *f;
13173ad3fb45Sjoris 
13183ad3fb45Sjoris 	if (context_vec_start > context_vec_ptr)
13193ad3fb45Sjoris 		return;
13203ad3fb45Sjoris 
13213ad3fb45Sjoris 	b = d = 0;		/* gcc */
13223ad3fb45Sjoris 	lowa = MAX(1, cvp->a - context);
13233ad3fb45Sjoris 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
13243ad3fb45Sjoris 	lowc = MAX(1, cvp->c - context);
13253ad3fb45Sjoris 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
13263ad3fb45Sjoris 
13273ad3fb45Sjoris 	diff_output("@@ -");
13283ad3fb45Sjoris 	uni_range(lowa, upb);
13293ad3fb45Sjoris 	diff_output(" +");
13303ad3fb45Sjoris 	uni_range(lowc, upd);
13313ad3fb45Sjoris 	diff_output(" @@");
1332261cb0daSjoris 	if (diff_pflag == 1) {
13333ad3fb45Sjoris 		f = match_function(ixold, lowa - 1, f1);
13343ad3fb45Sjoris 		if (f != NULL) {
13353ad3fb45Sjoris 			diff_output(" ");
13363ad3fb45Sjoris 			diff_output("%s", f);
13373ad3fb45Sjoris 		}
13383ad3fb45Sjoris 	}
13393ad3fb45Sjoris 	diff_output("\n");
13403ad3fb45Sjoris 
13413ad3fb45Sjoris 	/*
13423ad3fb45Sjoris 	 * Output changes in "unified" diff format--the old and new lines
13433ad3fb45Sjoris 	 * are printed together.
13443ad3fb45Sjoris 	 */
13453ad3fb45Sjoris 	for (; cvp <= context_vec_ptr; cvp++) {
13463ad3fb45Sjoris 		a = cvp->a;
13473ad3fb45Sjoris 		b = cvp->b;
13483ad3fb45Sjoris 		c = cvp->c;
13493ad3fb45Sjoris 		d = cvp->d;
13503ad3fb45Sjoris 
13513ad3fb45Sjoris 		/*
13523ad3fb45Sjoris 		 * c: both new and old changes
13533ad3fb45Sjoris 		 * d: only changes in the old file
13543ad3fb45Sjoris 		 * a: only changes in the new file
13553ad3fb45Sjoris 		 */
13563ad3fb45Sjoris 		if (a <= b && c <= d)
13573ad3fb45Sjoris 			ch = 'c';
13583ad3fb45Sjoris 		else
13593ad3fb45Sjoris 			ch = (a <= b) ? 'd' : 'a';
13603ad3fb45Sjoris 
13613ad3fb45Sjoris 		switch (ch) {
13623ad3fb45Sjoris 		case 'c':
13633ad3fb45Sjoris 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
13643ad3fb45Sjoris 			fetch(ixold, a, b, f1, '-', 0);
13653ad3fb45Sjoris 			fetch(ixnew, c, d, f2, '+', 0);
13663ad3fb45Sjoris 			break;
13673ad3fb45Sjoris 		case 'd':
13683ad3fb45Sjoris 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
13693ad3fb45Sjoris 			fetch(ixold, a, b, f1, '-', 0);
13703ad3fb45Sjoris 			break;
13713ad3fb45Sjoris 		case 'a':
13723ad3fb45Sjoris 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
13733ad3fb45Sjoris 			fetch(ixnew, c, d, f2, '+', 0);
13743ad3fb45Sjoris 			break;
13753ad3fb45Sjoris 		}
13763ad3fb45Sjoris 		lowa = b + 1;
13773ad3fb45Sjoris 		lowc = d + 1;
13783ad3fb45Sjoris 	}
13793ad3fb45Sjoris 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
13803ad3fb45Sjoris 
13813ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
13823ad3fb45Sjoris }
13833ad3fb45Sjoris 
13843ad3fb45Sjoris void
13853ad3fb45Sjoris diff_output(const char *fmt, ...)
13863ad3fb45Sjoris {
13873ad3fb45Sjoris 	va_list vap;
13883ad3fb45Sjoris 	int i;
13893ad3fb45Sjoris 	char *str;
13903ad3fb45Sjoris 
13913ad3fb45Sjoris 	va_start(vap, fmt);
13923ad3fb45Sjoris 	i = vasprintf(&str, fmt, vap);
13933ad3fb45Sjoris 	va_end(vap);
13943ad3fb45Sjoris 	if (i == -1)
13953ad3fb45Sjoris 		fatal("diff_output: %s", strerror(errno));
13963ad3fb45Sjoris 	if (diffbuf != NULL)
13973ad3fb45Sjoris 		cvs_buf_append(diffbuf, str, strlen(str));
13983ad3fb45Sjoris 	else
13993ad3fb45Sjoris 		cvs_printf("%s", str);
14003ad3fb45Sjoris 	xfree(str);
14013ad3fb45Sjoris }
1402