xref: /openbsd-src/usr.bin/cvs/diff_internals.c (revision be756b9117bffe072a274367ebb52b9615e6c107)
1*be756b91Stobias /*	$OpenBSD: diff_internals.c,v 1.22 2008/05/30 11:06:17 tobias 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  */
66eea21b3cSray 
67eea21b3cSray #include <sys/param.h>
68eea21b3cSray #include <sys/stat.h>
69eea21b3cSray 
70eea21b3cSray #include <ctype.h>
71eea21b3cSray #include <errno.h>
72eea21b3cSray #include <regex.h>
73eea21b3cSray #include <stddef.h>
74101e9cbeStobias #include <stdio.h>
75eea21b3cSray #include <string.h>
76eea21b3cSray #include <unistd.h>
77eea21b3cSray 
78eea21b3cSray #include "cvs.h"
79eea21b3cSray #include "diff.h"
80eea21b3cSray 
81eea21b3cSray /*
82eea21b3cSray  * diff - compare two files.
83eea21b3cSray  */
84eea21b3cSray 
853ad3fb45Sjoris /*
863ad3fb45Sjoris  *	Uses an algorithm due to Harold Stone, which finds
873ad3fb45Sjoris  *	a pair of longest identical subsequences in the two
883ad3fb45Sjoris  *	files.
893ad3fb45Sjoris  *
903ad3fb45Sjoris  *	The major goal is to generate the match vector J.
913ad3fb45Sjoris  *	J[i] is the index of the line in file1 corresponding
923ad3fb45Sjoris  *	to line i file0. J[i] = 0 if there is no
933ad3fb45Sjoris  *	such line in file1.
943ad3fb45Sjoris  *
953ad3fb45Sjoris  *	Lines are hashed so as to work in core. All potential
963ad3fb45Sjoris  *	matches are located by sorting the lines of each file
973ad3fb45Sjoris  *	on the hash (called ``value''). In particular, this
983ad3fb45Sjoris  *	collects the equivalence classes in file1 together.
993ad3fb45Sjoris  *	Subroutine equiv replaces the value of each line in
1003ad3fb45Sjoris  *	file0 by the index of the first element of its
1013ad3fb45Sjoris  *	matching equivalence in (the reordered) file1.
1023ad3fb45Sjoris  *	To save space equiv squeezes file1 into a single
1033ad3fb45Sjoris  *	array member in which the equivalence classes
1043ad3fb45Sjoris  *	are simply concatenated, except that their first
1053ad3fb45Sjoris  *	members are flagged by changing sign.
1063ad3fb45Sjoris  *
1073ad3fb45Sjoris  *	Next the indices that point into member are unsorted into
1083ad3fb45Sjoris  *	array class according to the original order of file0.
1093ad3fb45Sjoris  *
1103ad3fb45Sjoris  *	The cleverness lies in routine stone. This marches
1113ad3fb45Sjoris  *	through the lines of file0, developing a vector klist
1123ad3fb45Sjoris  *	of "k-candidates". At step i a k-candidate is a matched
1133ad3fb45Sjoris  *	pair of lines x,y (x in file0 y in file1) such that
1143ad3fb45Sjoris  *	there is a common subsequence of length k
1153ad3fb45Sjoris  *	between the first i lines of file0 and the first y
1163ad3fb45Sjoris  *	lines of file1, but there is no such subsequence for
1173ad3fb45Sjoris  *	any smaller y. x is the earliest possible mate to y
1183ad3fb45Sjoris  *	that occurs in such a subsequence.
1193ad3fb45Sjoris  *
1203ad3fb45Sjoris  *	Whenever any of the members of the equivalence class of
1213ad3fb45Sjoris  *	lines in file1 matable to a line in file0 has serial number
1223ad3fb45Sjoris  *	less than the y of some k-candidate, that k-candidate
1233ad3fb45Sjoris  *	with the smallest such y is replaced. The new
1243ad3fb45Sjoris  *	k-candidate is chained (via pred) to the current
1253ad3fb45Sjoris  *	k-1 candidate so that the actual subsequence can
1263ad3fb45Sjoris  *	be recovered. When a member has serial number greater
1273ad3fb45Sjoris  *	that the y of all k-candidates, the klist is extended.
1283ad3fb45Sjoris  *	At the end, the longest subsequence is pulled out
1293ad3fb45Sjoris  *	and placed in the array J by unravel
1303ad3fb45Sjoris  *
1313ad3fb45Sjoris  *	With J in hand, the matches there recorded are
1323ad3fb45Sjoris  *	check'ed against reality to assure that no spurious
1333ad3fb45Sjoris  *	matches have crept in due to hashing. If they have,
1343ad3fb45Sjoris  *	they are broken, and "jackpot" is recorded--a harmless
1353ad3fb45Sjoris  *	matter except that a true match for a spuriously
1363ad3fb45Sjoris  *	mated line may now be unnecessarily reported as a change.
1373ad3fb45Sjoris  *
1383ad3fb45Sjoris  *	Much of the complexity of the program comes simply
1393ad3fb45Sjoris  *	from trying to minimize core utilization and
1403ad3fb45Sjoris  *	maximize the range of doable problems by dynamically
1413ad3fb45Sjoris  *	allocating what is needed and reusing what is not.
1423ad3fb45Sjoris  *	The core requirements for problems larger than somewhat
1433ad3fb45Sjoris  *	are (in words) 2*length(file0) + length(file1) +
1443ad3fb45Sjoris  *	3*(number of k-candidates installed),  typically about
1453ad3fb45Sjoris  *	6n words for files of length n.
1463ad3fb45Sjoris  */
1473ad3fb45Sjoris 
1483ad3fb45Sjoris struct cand {
1493ad3fb45Sjoris 	int	x;
1503ad3fb45Sjoris 	int	y;
1513ad3fb45Sjoris 	int	pred;
152ccf8fb4cSray };
1533ad3fb45Sjoris 
1543ad3fb45Sjoris struct line {
1553ad3fb45Sjoris 	int	serial;
1563ad3fb45Sjoris 	int	value;
1573ad3fb45Sjoris } *file[2];
1583ad3fb45Sjoris 
1593ad3fb45Sjoris /*
1603ad3fb45Sjoris  * The following struct is used to record change information when
1613ad3fb45Sjoris  * doing a "context" or "unified" diff.  (see routine "change" to
1623ad3fb45Sjoris  * understand the highly mnemonic field names)
1633ad3fb45Sjoris  */
1643ad3fb45Sjoris struct context_vec {
1653ad3fb45Sjoris 	int	a;		/* start line in old file */
1663ad3fb45Sjoris 	int	b;		/* end line in old file */
1673ad3fb45Sjoris 	int	c;		/* start line in new file */
1683ad3fb45Sjoris 	int	d;		/* end line in new file */
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 *);
177eea21b3cSray static void	 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 *);
182fd660bf2Stobias static void	 diff_head(void);
183fd660bf2Stobias static void	 rdiff_head(void);
1843ad3fb45Sjoris static void	 change(FILE *, FILE *, int, int, int, int);
1853ad3fb45Sjoris static void	 sort(struct line *, int);
1863ad3fb45Sjoris static int	 ignoreline(char *);
1873ad3fb45Sjoris static int	 asciifile(FILE *);
1883ad3fb45Sjoris static void	 fetch(long *, int, int, FILE *, int, int);
1893ad3fb45Sjoris static int	 newcand(int, int, int);
1903ad3fb45Sjoris static int	 search(int *, int, int);
1913ad3fb45Sjoris static int	 skipline(FILE *);
1923ad3fb45Sjoris static int	 isqrt(int);
1933ad3fb45Sjoris static int	 stone(int *, int, int *, int *);
1943ad3fb45Sjoris static int	 readhash(FILE *);
1953ad3fb45Sjoris static int	 files_differ(FILE *, FILE *);
1963ad3fb45Sjoris static char	*match_function(const long *, int, FILE *);
1973ad3fb45Sjoris static char	*preadline(int, size_t, off_t);
1983ad3fb45Sjoris 
199fd660bf2Stobias static int aflag, bflag, dflag, tflag, Tflag, wflag;
2003ad3fb45Sjoris static int context = 3;
2013ad3fb45Sjoris int diff_format = D_NORMAL;
202fd660bf2Stobias int diff_iflag = 0;
203261cb0daSjoris int diff_pflag = 0;
204*be756b91Stobias const char *diff_file1 = NULL;
205*be756b91Stobias const char *diff_file2 = NULL;
2063ad3fb45Sjoris RCSNUM *diff_rev1 = NULL;
2073ad3fb45Sjoris RCSNUM *diff_rev2 = NULL;
2083ad3fb45Sjoris char diffargs[128];
2093ad3fb45Sjoris static struct stat stb1, stb2;
2103ad3fb45Sjoris static char *ifdefname, *ignore_pats;
2113ad3fb45Sjoris regex_t ignore_re;
2123ad3fb45Sjoris 
2133ad3fb45Sjoris static int  *J;			/* will be overlaid on class */
2143ad3fb45Sjoris static int  *class;		/* will be overlaid on file[0] */
2153ad3fb45Sjoris static int  *klist;		/* will be overlaid on file[0] after class */
2163ad3fb45Sjoris static int  *member;		/* will be overlaid on file[1] */
2173ad3fb45Sjoris static int   clen;
2183ad3fb45Sjoris static int   inifdef;		/* whether or not we are in a #ifdef block */
219eea21b3cSray static int   len[2];
2203ad3fb45Sjoris static int   pref, suff;	/* length of prefix and suffix */
2213ad3fb45Sjoris static int   slen[2];
2223ad3fb45Sjoris static int   anychange;
2233ad3fb45Sjoris static long *ixnew;		/* will be overlaid on file[1] */
2243ad3fb45Sjoris static long *ixold;		/* will be overlaid on klist */
2253ad3fb45Sjoris static struct cand *clist;	/* merely a free storage pot for candidates */
2263ad3fb45Sjoris static int   clistlen;		/* the length of clist */
2273ad3fb45Sjoris static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
2283ad3fb45Sjoris static u_char *chrtran;		/* translation table for case-folding */
2293ad3fb45Sjoris static struct context_vec *context_vec_start;
2303ad3fb45Sjoris static struct context_vec *context_vec_end;
2313ad3fb45Sjoris static struct context_vec *context_vec_ptr;
2323ad3fb45Sjoris 
233e22e6974Sxsa #define FUNCTION_CONTEXT_SIZE	55
2343ad3fb45Sjoris static char lastbuf[FUNCTION_CONTEXT_SIZE];
2353ad3fb45Sjoris static int lastline;
2363ad3fb45Sjoris static int lastmatchline;
2373ad3fb45Sjoris BUF  *diffbuf = NULL;
2383ad3fb45Sjoris 
239eea21b3cSray 
2403ad3fb45Sjoris /*
2413ad3fb45Sjoris  * chrtran points to one of 2 translation tables: cup2low if folding upper to
2423ad3fb45Sjoris  * lower case clow2low if not folding case
2433ad3fb45Sjoris  */
2443ad3fb45Sjoris u_char clow2low[256] = {
2453ad3fb45Sjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2463ad3fb45Sjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2473ad3fb45Sjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2483ad3fb45Sjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2493ad3fb45Sjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2503ad3fb45Sjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
2513ad3fb45Sjoris 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
2523ad3fb45Sjoris 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
2533ad3fb45Sjoris 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
2543ad3fb45Sjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2553ad3fb45Sjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2563ad3fb45Sjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2573ad3fb45Sjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2583ad3fb45Sjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2593ad3fb45Sjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2603ad3fb45Sjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2613ad3fb45Sjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2623ad3fb45Sjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2633ad3fb45Sjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2643ad3fb45Sjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2653ad3fb45Sjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2663ad3fb45Sjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2673ad3fb45Sjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2683ad3fb45Sjoris 	0xfd, 0xfe, 0xff
2693ad3fb45Sjoris };
2703ad3fb45Sjoris 
2713ad3fb45Sjoris u_char cup2low[256] = {
2723ad3fb45Sjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2733ad3fb45Sjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2743ad3fb45Sjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2753ad3fb45Sjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2763ad3fb45Sjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2773ad3fb45Sjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
2783ad3fb45Sjoris 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
2793ad3fb45Sjoris 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
2803ad3fb45Sjoris 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
2813ad3fb45Sjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2823ad3fb45Sjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2833ad3fb45Sjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2843ad3fb45Sjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2853ad3fb45Sjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2863ad3fb45Sjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2873ad3fb45Sjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2883ad3fb45Sjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2893ad3fb45Sjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2903ad3fb45Sjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2913ad3fb45Sjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2923ad3fb45Sjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2933ad3fb45Sjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2943ad3fb45Sjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2953ad3fb45Sjoris 	0xfd, 0xfe, 0xff
2963ad3fb45Sjoris };
2973ad3fb45Sjoris 
2983ad3fb45Sjoris int
2992e0d696aSjoris cvs_diffreg(const char *file1, const char *file2, int _fd1, int _fd2, BUF *out)
3003ad3fb45Sjoris {
3013ad3fb45Sjoris 	FILE *f1, *f2;
3022e0d696aSjoris 	int i, rval, fd1, fd2;
3033ad3fb45Sjoris 
304*be756b91Stobias 	diff_file1 = file1;
305*be756b91Stobias 	diff_file2 = file2;
3063ad3fb45Sjoris 	f1 = f2 = NULL;
3073ad3fb45Sjoris 	rval = D_SAME;
3083ad3fb45Sjoris 	anychange = 0;
3093ad3fb45Sjoris 	lastline = 0;
3103ad3fb45Sjoris 	lastmatchline = 0;
3113ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
312fd660bf2Stobias 	chrtran = (diff_iflag ? cup2low : clow2low);
3133ad3fb45Sjoris 	if (out != NULL)
3143ad3fb45Sjoris 		diffbuf = out;
3153ad3fb45Sjoris 
3162e0d696aSjoris 	fd1 = dup(_fd1);
3172e0d696aSjoris 	if (fd1 == -1)
3182e0d696aSjoris 		fatal("cvs_diffreg: dup: %s", strerror(errno));
3192e0d696aSjoris 
3202e0d696aSjoris 	fd2 = dup(_fd2);
3212e0d696aSjoris 	if (fd2 == -1)
3222e0d696aSjoris 		fatal("cvs_diffreg: dup: %s", strerror(errno));
3232e0d696aSjoris 
324651312a5Sjoris 	if (lseek(fd1, 0, SEEK_SET) < 0)
3252e0d696aSjoris 		fatal("cvs_diffreg: lseek: %s", strerror(errno));
3262e0d696aSjoris 
3272e0d696aSjoris 	f1 = fdopen(fd1, "r");
3283ad3fb45Sjoris 	if (f1 == NULL) {
3293ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file1);
3303ad3fb45Sjoris 		goto closem;
3313ad3fb45Sjoris 	}
3323ad3fb45Sjoris 
333651312a5Sjoris 	if (lseek(fd2, 0, SEEK_SET) < 0)
3342e0d696aSjoris 		fatal("cvs_diffreg: lseek: %s", strerror(errno));
3352e0d696aSjoris 
3362e0d696aSjoris 	f2 = fdopen(fd2, "r");
3373ad3fb45Sjoris 	if (f2 == NULL) {
3383ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file2);
3393ad3fb45Sjoris 		goto closem;
3403ad3fb45Sjoris 	}
3413ad3fb45Sjoris 
3422e0d696aSjoris 	if (fstat(fd1, &stb1) < 0) {
3433ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file1);
3443ad3fb45Sjoris 		goto closem;
3453ad3fb45Sjoris 	}
3462e0d696aSjoris 
3472e0d696aSjoris 	if (fstat(fd2, &stb2) < 0) {
3483ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file2);
3493ad3fb45Sjoris 		goto closem;
3503ad3fb45Sjoris 	}
351eea21b3cSray 
3523ad3fb45Sjoris 	switch (files_differ(f1, f2)) {
3533ad3fb45Sjoris 	case 0:
3543ad3fb45Sjoris 		goto closem;
3553ad3fb45Sjoris 	case 1:
3563ad3fb45Sjoris 		break;
3573ad3fb45Sjoris 	default:
3583ad3fb45Sjoris 		/* error */
3593ad3fb45Sjoris 		goto closem;
3603ad3fb45Sjoris 	}
3613ad3fb45Sjoris 
3623ad3fb45Sjoris 	if (!asciifile(f1) || !asciifile(f2)) {
3633ad3fb45Sjoris 		rval = D_BINARY;
3643ad3fb45Sjoris 		goto closem;
3653ad3fb45Sjoris 	}
366eea21b3cSray 
367eea21b3cSray 	prepare(0, f1, stb1.st_size);
368eea21b3cSray 	prepare(1, f2, stb2.st_size);
369eea21b3cSray 
3703ad3fb45Sjoris 	prune();
3713ad3fb45Sjoris 	sort(sfile[0], slen[0]);
3723ad3fb45Sjoris 	sort(sfile[1], slen[1]);
3733ad3fb45Sjoris 
3743ad3fb45Sjoris 	member = (int *)file[1];
3753ad3fb45Sjoris 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
37680566be2Sray 	member = xrealloc(member, slen[1] + 2, sizeof(*member));
3773ad3fb45Sjoris 
3783ad3fb45Sjoris 	class = (int *)file[0];
3793ad3fb45Sjoris 	unsort(sfile[0], slen[0], class);
38080566be2Sray 	class = xrealloc(class, slen[0] + 2, sizeof(*class));
3813ad3fb45Sjoris 
3823ad3fb45Sjoris 	klist = xcalloc(slen[0] + 2, sizeof(*klist));
3833ad3fb45Sjoris 	clen = 0;
3843ad3fb45Sjoris 	clistlen = 100;
3853ad3fb45Sjoris 	clist = xcalloc(clistlen, sizeof(*clist));
386eea21b3cSray 	i = stone(class, slen[0], member, klist);
3873ad3fb45Sjoris 	xfree(member);
3883ad3fb45Sjoris 	xfree(class);
3893ad3fb45Sjoris 
390eea21b3cSray 	J = xrealloc(J, len[0] + 2, sizeof(*J));
3913ad3fb45Sjoris 	unravel(klist[i]);
3923ad3fb45Sjoris 	xfree(clist);
3933ad3fb45Sjoris 	xfree(klist);
3943ad3fb45Sjoris 
395eea21b3cSray 	ixold = xrealloc(ixold, len[0] + 2, sizeof(*ixold));
396eea21b3cSray 	ixnew = xrealloc(ixnew, len[1] + 2, sizeof(*ixnew));
3973ad3fb45Sjoris 	check(f1, f2);
3983ad3fb45Sjoris 	output(f1, f2);
3993ad3fb45Sjoris 
4003ad3fb45Sjoris closem:
401eea21b3cSray 	if (anychange) {
4023ad3fb45Sjoris 		if (rval == D_SAME)
4033ad3fb45Sjoris 			rval = D_DIFFER;
4043ad3fb45Sjoris 	}
4053ad3fb45Sjoris 	if (f1 != NULL)
4063ad3fb45Sjoris 		fclose(f1);
4072e0d696aSjoris 
4083ad3fb45Sjoris 	if (f2 != NULL)
4093ad3fb45Sjoris 		fclose(f2);
4103ad3fb45Sjoris 
4113ad3fb45Sjoris 	return (rval);
4123ad3fb45Sjoris }
4133ad3fb45Sjoris 
4143ad3fb45Sjoris /*
4153ad3fb45Sjoris  * Check to see if the given files differ.
4163ad3fb45Sjoris  * Returns 0 if they are the same, 1 if different, and -1 on error.
4173ad3fb45Sjoris  * XXX - could use code from cmp(1) [faster]
4183ad3fb45Sjoris  */
4193ad3fb45Sjoris static int
4203ad3fb45Sjoris files_differ(FILE *f1, FILE *f2)
4213ad3fb45Sjoris {
4223ad3fb45Sjoris 	char buf1[BUFSIZ], buf2[BUFSIZ];
4233ad3fb45Sjoris 	size_t i, j;
4243ad3fb45Sjoris 
4253ad3fb45Sjoris 	if (stb1.st_size != stb2.st_size)
4263ad3fb45Sjoris 		return (1);
4273ad3fb45Sjoris 	for (;;) {
428a304c0f4Sray 		i = fread(buf1, 1, sizeof(buf1), f1);
429a304c0f4Sray 		j = fread(buf2, 1, sizeof(buf2), f2);
4303ad3fb45Sjoris 		if (i != j)
4313ad3fb45Sjoris 			return (1);
4323ad3fb45Sjoris 		if (i == 0 && j == 0) {
4333ad3fb45Sjoris 			if (ferror(f1) || ferror(f2))
4343ad3fb45Sjoris 				return (1);
4353ad3fb45Sjoris 			return (0);
4363ad3fb45Sjoris 		}
4373ad3fb45Sjoris 		if (memcmp(buf1, buf2, i) != 0)
4383ad3fb45Sjoris 			return (1);
4393ad3fb45Sjoris 	}
4403ad3fb45Sjoris }
4413ad3fb45Sjoris 
442eea21b3cSray static void
4433ad3fb45Sjoris prepare(int i, FILE *fd, off_t filesize)
4443ad3fb45Sjoris {
4453ad3fb45Sjoris 	struct line *p;
4463ad3fb45Sjoris 	int j, h;
4473ad3fb45Sjoris 	size_t sz;
4483ad3fb45Sjoris 
4493ad3fb45Sjoris 	rewind(fd);
4503ad3fb45Sjoris 
451a304c0f4Sray 	sz = (filesize <= SIZE_MAX ? filesize : SIZE_MAX) / 25;
4523ad3fb45Sjoris 	if (sz < 100)
4533ad3fb45Sjoris 		sz = 100;
4543ad3fb45Sjoris 
4553ad3fb45Sjoris 	p = xcalloc(sz + 3, sizeof(*p));
4563ad3fb45Sjoris 	for (j = 0; (h = readhash(fd));) {
457eea21b3cSray 		if (j == sz) {
4583ad3fb45Sjoris 			sz = sz * 3 / 2;
45980566be2Sray 			p = xrealloc(p, sz + 3, sizeof(*p));
4603ad3fb45Sjoris 		}
4613ad3fb45Sjoris 		p[++j].value = h;
4623ad3fb45Sjoris 	}
463eea21b3cSray 	len[i] = j;
4643ad3fb45Sjoris 	file[i] = p;
4653ad3fb45Sjoris }
4663ad3fb45Sjoris 
4673ad3fb45Sjoris static void
4683ad3fb45Sjoris prune(void)
4693ad3fb45Sjoris {
4703ad3fb45Sjoris 	int i, j;
4713ad3fb45Sjoris 
472eea21b3cSray 	for (pref = 0; pref < len[0] && pref < len[1] &&
4733ad3fb45Sjoris 	    file[0][pref + 1].value == file[1][pref + 1].value;
4743ad3fb45Sjoris 	    pref++)
4753ad3fb45Sjoris 		;
476eea21b3cSray 	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
477eea21b3cSray 	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
4783ad3fb45Sjoris 	    suff++)
4793ad3fb45Sjoris 		;
4803ad3fb45Sjoris 	for (j = 0; j < 2; j++) {
4813ad3fb45Sjoris 		sfile[j] = file[j] + pref;
482eea21b3cSray 		slen[j] = len[j] - pref - suff;
4833ad3fb45Sjoris 		for (i = 0; i <= slen[j]; i++)
4843ad3fb45Sjoris 			sfile[j][i].serial = i;
4853ad3fb45Sjoris 	}
4863ad3fb45Sjoris }
4873ad3fb45Sjoris 
4883ad3fb45Sjoris static void
4893ad3fb45Sjoris equiv(struct line *a, int n, struct line *b, int m, int *c)
4903ad3fb45Sjoris {
4913ad3fb45Sjoris 	int i, j;
4923ad3fb45Sjoris 
4933ad3fb45Sjoris 	i = j = 1;
4943ad3fb45Sjoris 	while (i <= n && j <= m) {
4953ad3fb45Sjoris 		if (a[i].value < b[j].value)
4963ad3fb45Sjoris 			a[i++].value = 0;
4973ad3fb45Sjoris 		else if (a[i].value == b[j].value)
4983ad3fb45Sjoris 			a[i++].value = j;
4993ad3fb45Sjoris 		else
5003ad3fb45Sjoris 			j++;
5013ad3fb45Sjoris 	}
5023ad3fb45Sjoris 	while (i <= n)
5033ad3fb45Sjoris 		a[i++].value = 0;
5043ad3fb45Sjoris 	b[m + 1].value = 0;
5053ad3fb45Sjoris 	j = 0;
5063ad3fb45Sjoris 	while (++j <= m) {
5073ad3fb45Sjoris 		c[j] = -b[j].serial;
5083ad3fb45Sjoris 		while (b[j + 1].value == b[j].value) {
5093ad3fb45Sjoris 			j++;
5103ad3fb45Sjoris 			c[j] = b[j].serial;
5113ad3fb45Sjoris 		}
5123ad3fb45Sjoris 	}
5133ad3fb45Sjoris 	c[j] = -1;
5143ad3fb45Sjoris }
5153ad3fb45Sjoris 
5163ad3fb45Sjoris /* Code taken from ping.c */
5173ad3fb45Sjoris static int
5183ad3fb45Sjoris isqrt(int n)
5193ad3fb45Sjoris {
5203ad3fb45Sjoris 	int y, x = 1;
5213ad3fb45Sjoris 
5223ad3fb45Sjoris 	if (n == 0)
5233ad3fb45Sjoris 		return (0);
5243ad3fb45Sjoris 
5253ad3fb45Sjoris 	do { /* newton was a stinker */
5263ad3fb45Sjoris 		y = x;
5273ad3fb45Sjoris 		x = n / x;
5283ad3fb45Sjoris 		x += y;
5293ad3fb45Sjoris 		x /= 2;
530eea21b3cSray 	} while ((x - y) > 1 || (x - y) < -1);
5313ad3fb45Sjoris 
5323ad3fb45Sjoris 	return (x);
5333ad3fb45Sjoris }
5343ad3fb45Sjoris 
5353ad3fb45Sjoris static int
5363ad3fb45Sjoris stone(int *a, int n, int *b, int *c)
5373ad3fb45Sjoris {
5383ad3fb45Sjoris 	int i, k, y, j, l;
5393ad3fb45Sjoris 	int oldc, tc, oldl;
5403ad3fb45Sjoris 	u_int numtries;
5413ad3fb45Sjoris 
5423ad3fb45Sjoris 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
5433ad3fb45Sjoris 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
5443ad3fb45Sjoris 
5453ad3fb45Sjoris 	k = 0;
546eea21b3cSray 	c[0] = newcand(0, 0, 0);
5473ad3fb45Sjoris 	for (i = 1; i <= n; i++) {
5483ad3fb45Sjoris 		j = a[i];
5493ad3fb45Sjoris 		if (j == 0)
5503ad3fb45Sjoris 			continue;
5513ad3fb45Sjoris 		y = -b[j];
5523ad3fb45Sjoris 		oldl = 0;
5533ad3fb45Sjoris 		oldc = c[0];
5543ad3fb45Sjoris 		numtries = 0;
5553ad3fb45Sjoris 		do {
5563ad3fb45Sjoris 			if (y <= clist[oldc].y)
5573ad3fb45Sjoris 				continue;
5583ad3fb45Sjoris 			l = search(c, k, y);
5593ad3fb45Sjoris 			if (l != oldl + 1)
5603ad3fb45Sjoris 				oldc = c[l - 1];
5613ad3fb45Sjoris 			if (l <= k) {
5623ad3fb45Sjoris 				if (clist[c[l]].y <= y)
5633ad3fb45Sjoris 					continue;
5643ad3fb45Sjoris 				tc = c[l];
565eea21b3cSray 				c[l] = newcand(i, y, oldc);
5663ad3fb45Sjoris 				oldc = tc;
5673ad3fb45Sjoris 				oldl = l;
5683ad3fb45Sjoris 				numtries++;
5693ad3fb45Sjoris 			} else {
570eea21b3cSray 				c[l] = newcand(i, y, oldc);
5713ad3fb45Sjoris 				k++;
5723ad3fb45Sjoris 				break;
5733ad3fb45Sjoris 			}
5743ad3fb45Sjoris 		} while ((y = b[++j]) > 0 && numtries < bound);
5753ad3fb45Sjoris 	}
5763ad3fb45Sjoris 	return (k);
5773ad3fb45Sjoris }
5783ad3fb45Sjoris 
5793ad3fb45Sjoris static int
5803ad3fb45Sjoris newcand(int x, int y, int pred)
5813ad3fb45Sjoris {
58280566be2Sray 	struct cand *q;
5833ad3fb45Sjoris 
5843ad3fb45Sjoris 	if (clen == clistlen) {
585f74aa433Sray 		clistlen = clistlen * 11 / 10;
586f74aa433Sray 		clist = xrealloc(clist, clistlen, sizeof(*clist));
5873ad3fb45Sjoris 	}
5883ad3fb45Sjoris 	q = clist + clen;
5893ad3fb45Sjoris 	q->x = x;
5903ad3fb45Sjoris 	q->y = y;
5913ad3fb45Sjoris 	q->pred = pred;
5923ad3fb45Sjoris 	return (clen++);
5933ad3fb45Sjoris }
5943ad3fb45Sjoris 
5953ad3fb45Sjoris static int
5963ad3fb45Sjoris search(int *c, int k, int y)
5973ad3fb45Sjoris {
5983ad3fb45Sjoris 	int i, j, l, t;
5993ad3fb45Sjoris 
6003ad3fb45Sjoris 	if (clist[c[k]].y < y)	/* quick look for typical case */
6013ad3fb45Sjoris 		return (k + 1);
6023ad3fb45Sjoris 	i = 0;
6033ad3fb45Sjoris 	j = k + 1;
6043ad3fb45Sjoris 	for (;;) {
6053ad3fb45Sjoris 		l = (i + j) / 2;
6063ad3fb45Sjoris 		if (l <= i)
6073ad3fb45Sjoris 			break;
6083ad3fb45Sjoris 		t = clist[c[l]].y;
6093ad3fb45Sjoris 		if (t > y)
6103ad3fb45Sjoris 			j = l;
6113ad3fb45Sjoris 		else if (t < y)
6123ad3fb45Sjoris 			i = l;
6133ad3fb45Sjoris 		else
6143ad3fb45Sjoris 			return (l);
6153ad3fb45Sjoris 	}
6163ad3fb45Sjoris 	return (l + 1);
6173ad3fb45Sjoris }
6183ad3fb45Sjoris 
6193ad3fb45Sjoris static void
6203ad3fb45Sjoris unravel(int p)
6213ad3fb45Sjoris {
6223ad3fb45Sjoris 	struct cand *q;
6233ad3fb45Sjoris 	int i;
6243ad3fb45Sjoris 
625eea21b3cSray 	for (i = 0; i <= len[0]; i++)
6263ad3fb45Sjoris 		J[i] = i <= pref ? i :
627eea21b3cSray 		    i > len[0] - suff ? i + len[1] - len[0] : 0;
6283ad3fb45Sjoris 	for (q = clist + p; q->y != 0; q = clist + q->pred)
6293ad3fb45Sjoris 		J[q->x + pref] = q->y + pref;
6303ad3fb45Sjoris }
6313ad3fb45Sjoris 
6323ad3fb45Sjoris /*
6333ad3fb45Sjoris  * Check does double duty:
6343ad3fb45Sjoris  *  1.	ferret out any fortuitous correspondences due
6353ad3fb45Sjoris  *	to confounding by hashing (which result in "jackpot")
6363ad3fb45Sjoris  *  2.  collect random access indexes to the two files
6373ad3fb45Sjoris  */
6383ad3fb45Sjoris static void
6393ad3fb45Sjoris check(FILE *f1, FILE *f2)
6403ad3fb45Sjoris {
6413ad3fb45Sjoris 	int i, j, jackpot, c, d;
6423ad3fb45Sjoris 	long ctold, ctnew;
6433ad3fb45Sjoris 
6443ad3fb45Sjoris 	rewind(f1);
6453ad3fb45Sjoris 	rewind(f2);
6463ad3fb45Sjoris 	j = 1;
6473ad3fb45Sjoris 	ixold[0] = ixnew[0] = 0;
6483ad3fb45Sjoris 	jackpot = 0;
6493ad3fb45Sjoris 	ctold = ctnew = 0;
650eea21b3cSray 	for (i = 1; i <= len[0]; i++) {
6513ad3fb45Sjoris 		if (J[i] == 0) {
6523ad3fb45Sjoris 			ixold[i] = ctold += skipline(f1);
6533ad3fb45Sjoris 			continue;
6543ad3fb45Sjoris 		}
6553ad3fb45Sjoris 		while (j < J[i]) {
6563ad3fb45Sjoris 			ixnew[j] = ctnew += skipline(f2);
6573ad3fb45Sjoris 			j++;
6583ad3fb45Sjoris 		}
659fd660bf2Stobias 		if (bflag == 1 || wflag == 1 || diff_iflag == 1) {
6603ad3fb45Sjoris 			for (;;) {
6613ad3fb45Sjoris 				c = getc(f1);
6623ad3fb45Sjoris 				d = getc(f2);
6633ad3fb45Sjoris 				/*
6643ad3fb45Sjoris 				 * GNU diff ignores a missing newline
665eea21b3cSray 				 * in one file for -b or -w.
6663ad3fb45Sjoris 				 */
6673ad3fb45Sjoris 				if ((bflag == 1 || wflag == 1) &&
6683ad3fb45Sjoris 				    ((c == EOF && d == '\n') ||
6693ad3fb45Sjoris 				    (c == '\n' && d == EOF))) {
6703ad3fb45Sjoris 					break;
6713ad3fb45Sjoris 				}
6723ad3fb45Sjoris 				ctold++;
6733ad3fb45Sjoris 				ctnew++;
6743ad3fb45Sjoris 				if (bflag == 1 && isspace(c) && isspace(d)) {
6753ad3fb45Sjoris 					do {
6763ad3fb45Sjoris 						if (c == '\n')
6773ad3fb45Sjoris 							break;
6783ad3fb45Sjoris 						ctold++;
6793ad3fb45Sjoris 					} while (isspace(c = getc(f1)));
6803ad3fb45Sjoris 					do {
6813ad3fb45Sjoris 						if (d == '\n')
6823ad3fb45Sjoris 							break;
6833ad3fb45Sjoris 						ctnew++;
6843ad3fb45Sjoris 					} while (isspace(d = getc(f2)));
6853ad3fb45Sjoris 				} else if (wflag == 1) {
6863ad3fb45Sjoris 					while (isspace(c) && c != '\n') {
6873ad3fb45Sjoris 						c = getc(f1);
6883ad3fb45Sjoris 						ctold++;
6893ad3fb45Sjoris 					}
6903ad3fb45Sjoris 					while (isspace(d) && d != '\n') {
6913ad3fb45Sjoris 						d = getc(f2);
6923ad3fb45Sjoris 						ctnew++;
6933ad3fb45Sjoris 					}
6943ad3fb45Sjoris 				}
6953ad3fb45Sjoris 				if (chrtran[c] != chrtran[d]) {
6963ad3fb45Sjoris 					jackpot++;
6973ad3fb45Sjoris 					J[i] = 0;
6983ad3fb45Sjoris 					if (c != '\n' && c != EOF)
6993ad3fb45Sjoris 						ctold += skipline(f1);
7003ad3fb45Sjoris 					if (d != '\n' && c != EOF)
7013ad3fb45Sjoris 						ctnew += skipline(f2);
7023ad3fb45Sjoris 					break;
7033ad3fb45Sjoris 				}
7043ad3fb45Sjoris 				if (c == '\n' || c == EOF)
7053ad3fb45Sjoris 					break;
7063ad3fb45Sjoris 			}
7073ad3fb45Sjoris 		} else {
7083ad3fb45Sjoris 			for (;;) {
7093ad3fb45Sjoris 				ctold++;
7103ad3fb45Sjoris 				ctnew++;
7113ad3fb45Sjoris 				if ((c = getc(f1)) != (d = getc(f2))) {
7123ad3fb45Sjoris 					/* jackpot++; */
7133ad3fb45Sjoris 					J[i] = 0;
7143ad3fb45Sjoris 					if (c != '\n' && c != EOF)
7153ad3fb45Sjoris 						ctold += skipline(f1);
7163ad3fb45Sjoris 					if (d != '\n' && c != EOF)
7173ad3fb45Sjoris 						ctnew += skipline(f2);
7183ad3fb45Sjoris 					break;
7193ad3fb45Sjoris 				}
7203ad3fb45Sjoris 				if (c == '\n' || c == EOF)
7213ad3fb45Sjoris 					break;
7223ad3fb45Sjoris 			}
7233ad3fb45Sjoris 		}
7243ad3fb45Sjoris 		ixold[i] = ctold;
7253ad3fb45Sjoris 		ixnew[j] = ctnew;
7263ad3fb45Sjoris 		j++;
7273ad3fb45Sjoris 	}
728eea21b3cSray 	for (; j <= len[1]; j++)
7293ad3fb45Sjoris 		ixnew[j] = ctnew += skipline(f2);
7303ad3fb45Sjoris 	/*
731eea21b3cSray 	 * if (jackpot)
732eea21b3cSray 	 *	fprintf(stderr, "jackpot\n");
7333ad3fb45Sjoris 	 */
7343ad3fb45Sjoris }
7353ad3fb45Sjoris 
7363ad3fb45Sjoris /* shellsort CACM #201 */
7373ad3fb45Sjoris static void
7383ad3fb45Sjoris sort(struct line *a, int n)
7393ad3fb45Sjoris {
7403ad3fb45Sjoris 	struct line *ai, *aim, w;
7413ad3fb45Sjoris 	int j, m = 0, k;
7423ad3fb45Sjoris 
7433ad3fb45Sjoris 	if (n == 0)
7443ad3fb45Sjoris 		return;
7453ad3fb45Sjoris 	for (j = 1; j <= n; j *= 2)
7463ad3fb45Sjoris 		m = 2 * j - 1;
7473ad3fb45Sjoris 	for (m /= 2; m != 0; m /= 2) {
7483ad3fb45Sjoris 		k = n - m;
7493ad3fb45Sjoris 		for (j = 1; j <= k; j++) {
7503ad3fb45Sjoris 			for (ai = &a[j]; ai > a; ai -= m) {
7513ad3fb45Sjoris 				aim = &ai[m];
7523ad3fb45Sjoris 				if (aim < ai)
7533ad3fb45Sjoris 					break;	/* wraparound */
7543ad3fb45Sjoris 				if (aim->value > ai[0].value ||
7553ad3fb45Sjoris 				    (aim->value == ai[0].value &&
7563ad3fb45Sjoris 					aim->serial > ai[0].serial))
7573ad3fb45Sjoris 					break;
7583ad3fb45Sjoris 				w.value = ai[0].value;
7593ad3fb45Sjoris 				ai[0].value = aim->value;
7603ad3fb45Sjoris 				aim->value = w.value;
7613ad3fb45Sjoris 				w.serial = ai[0].serial;
7623ad3fb45Sjoris 				ai[0].serial = aim->serial;
7633ad3fb45Sjoris 				aim->serial = w.serial;
7643ad3fb45Sjoris 			}
7653ad3fb45Sjoris 		}
7663ad3fb45Sjoris 	}
7673ad3fb45Sjoris }
7683ad3fb45Sjoris 
7693ad3fb45Sjoris static void
7703ad3fb45Sjoris unsort(struct line *f, int l, int *b)
7713ad3fb45Sjoris {
7723ad3fb45Sjoris 	int *a, i;
7733ad3fb45Sjoris 
7743ad3fb45Sjoris 	a = xcalloc(l + 1, sizeof(*a));
7753ad3fb45Sjoris 	for (i = 1; i <= l; i++)
7763ad3fb45Sjoris 		a[f[i].serial] = f[i].value;
7773ad3fb45Sjoris 	for (i = 1; i <= l; i++)
7783ad3fb45Sjoris 		b[i] = a[i];
7793ad3fb45Sjoris 	xfree(a);
7803ad3fb45Sjoris }
7813ad3fb45Sjoris 
7823ad3fb45Sjoris static int
7833ad3fb45Sjoris skipline(FILE *f)
7843ad3fb45Sjoris {
7853ad3fb45Sjoris 	int i, c;
7863ad3fb45Sjoris 
7873ad3fb45Sjoris 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
7883ad3fb45Sjoris 		continue;
7893ad3fb45Sjoris 	return (i);
7903ad3fb45Sjoris }
7913ad3fb45Sjoris 
7923ad3fb45Sjoris static void
7933ad3fb45Sjoris output(FILE *f1, FILE *f2)
7943ad3fb45Sjoris {
7953ad3fb45Sjoris 	int m, i0, i1, j0, j1;
7963ad3fb45Sjoris 
7973ad3fb45Sjoris 	rewind(f1);
7983ad3fb45Sjoris 	rewind(f2);
799eea21b3cSray 	m = len[0];
8003ad3fb45Sjoris 	J[0] = 0;
801eea21b3cSray 	J[m + 1] = len[1] + 1;
8023ad3fb45Sjoris 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
8033ad3fb45Sjoris 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
8043ad3fb45Sjoris 			i0++;
8053ad3fb45Sjoris 		j0 = J[i0 - 1] + 1;
8063ad3fb45Sjoris 		i1 = i0 - 1;
8073ad3fb45Sjoris 		while (i1 < m && J[i1 + 1] == 0)
8083ad3fb45Sjoris 			i1++;
8093ad3fb45Sjoris 		j1 = J[i1 + 1] - 1;
8103ad3fb45Sjoris 		J[i1] = j1;
8113ad3fb45Sjoris 		change(f1, f2, i0, i1, j0, j1);
8123ad3fb45Sjoris 	}
8133ad3fb45Sjoris 	if (m == 0)
814eea21b3cSray 		change(f1, f2, 1, 0, 1, len[1]);
8153ad3fb45Sjoris 	if (diff_format == D_IFDEF) {
8163ad3fb45Sjoris 		for (;;) {
8173ad3fb45Sjoris #define	c i0
8183ad3fb45Sjoris 			if ((c = getc(f1)) == EOF)
8193ad3fb45Sjoris 				return;
8203ad3fb45Sjoris 			diff_output("%c", c);
8213ad3fb45Sjoris 		}
8223ad3fb45Sjoris #undef c
8233ad3fb45Sjoris 	}
8243ad3fb45Sjoris 	if (anychange != 0) {
8253ad3fb45Sjoris 		if (diff_format == D_CONTEXT)
8263ad3fb45Sjoris 			dump_context_vec(f1, f2);
8273ad3fb45Sjoris 		else if (diff_format == D_UNIFIED)
8283ad3fb45Sjoris 			dump_unified_vec(f1, f2);
8293ad3fb45Sjoris 	}
8303ad3fb45Sjoris }
8313ad3fb45Sjoris 
832a304c0f4Sray static void
8333ad3fb45Sjoris range(int a, int b, char *separator)
8343ad3fb45Sjoris {
8353ad3fb45Sjoris 	diff_output("%d", a > b ? b : a);
8363ad3fb45Sjoris 	if (a < b)
8373ad3fb45Sjoris 		diff_output("%s%d", separator, b);
8383ad3fb45Sjoris }
8393ad3fb45Sjoris 
840a304c0f4Sray static void
8413ad3fb45Sjoris uni_range(int a, int b)
8423ad3fb45Sjoris {
8433ad3fb45Sjoris 	if (a < b)
8443ad3fb45Sjoris 		diff_output("%d,%d", a, b - a + 1);
8453ad3fb45Sjoris 	else if (a == b)
8463ad3fb45Sjoris 		diff_output("%d", b);
8473ad3fb45Sjoris 	else
8483ad3fb45Sjoris 		diff_output("%d,0", b);
8493ad3fb45Sjoris }
8503ad3fb45Sjoris 
8513ad3fb45Sjoris static char *
8523ad3fb45Sjoris preadline(int fd, size_t rlen, off_t off)
8533ad3fb45Sjoris {
8543ad3fb45Sjoris 	char *line;
8553ad3fb45Sjoris 	ssize_t nr;
8563ad3fb45Sjoris 
8573ad3fb45Sjoris 	line = xmalloc(rlen + 1);
8583ad3fb45Sjoris 	if ((nr = pread(fd, line, rlen, off)) < 0) {
8593ad3fb45Sjoris 		cvs_log(LP_ERR, "preadline failed");
860a304c0f4Sray 		xfree(line);
8613ad3fb45Sjoris 		return (NULL);
8623ad3fb45Sjoris 	}
8633ad3fb45Sjoris 	line[nr] = '\0';
8643ad3fb45Sjoris 	return (line);
8653ad3fb45Sjoris }
8663ad3fb45Sjoris 
8673ad3fb45Sjoris static int
8683ad3fb45Sjoris ignoreline(char *line)
8693ad3fb45Sjoris {
8703ad3fb45Sjoris 	int ret;
8713ad3fb45Sjoris 
872a304c0f4Sray 	ret = regexec(&ignore_re, line, 0, NULL, 0);
8733ad3fb45Sjoris 	xfree(line);
8743ad3fb45Sjoris 	return (ret == 0);	/* if it matched, it should be ignored. */
8753ad3fb45Sjoris }
8763ad3fb45Sjoris 
877fd660bf2Stobias static void
878fd660bf2Stobias diff_head(void)
879fd660bf2Stobias {
880fd660bf2Stobias 	char buf[64];
881fd660bf2Stobias 	struct tm *t;
882fd660bf2Stobias 	time_t curr_time;
883fd660bf2Stobias 
884fd660bf2Stobias 	if (diff_rev1 != NULL) {
885fd660bf2Stobias 		t = gmtime(&stb1.st_mtime);
886fd660bf2Stobias 	} else {
887fd660bf2Stobias 		time(&curr_time);
888fd660bf2Stobias 		t = localtime(&curr_time);
889fd660bf2Stobias 	}
890fd660bf2Stobias 
891d00c0f9eStobias 	(void)strftime(buf, sizeof(buf), "%b %G %H:%M:%S -0000", t);
892d00c0f9eStobias 	diff_output("%s %s	%d %s", diff_format == D_CONTEXT ?
893*be756b91Stobias 	    "***" : "---", diff_file1, t->tm_mday, buf);
894fd660bf2Stobias 
895fd660bf2Stobias 	if (diff_rev1 != NULL) {
896fd660bf2Stobias 		rcsnum_tostr(diff_rev1, buf, sizeof(buf));
897fd660bf2Stobias 		diff_output("\t%s", buf);
898fd660bf2Stobias 	}
899fd660bf2Stobias 
900fd660bf2Stobias 	diff_output("\n");
901fd660bf2Stobias 
902fd660bf2Stobias 	t = gmtime(&stb2.st_mtime);
903fd660bf2Stobias 
904d00c0f9eStobias 	(void)strftime(buf, sizeof(buf), "%b %G %H:%M:%S -0000", t);
905d00c0f9eStobias 	diff_output("%s %s	%d %s", diff_format == D_CONTEXT ?
906*be756b91Stobias 	    "---" : "+++", diff_file2, t->tm_mday, buf);
907fd660bf2Stobias 
908fd660bf2Stobias 	if (diff_rev2 != NULL) {
909fd660bf2Stobias 		rcsnum_tostr(diff_rev2, buf, sizeof(buf));
910fd660bf2Stobias 		diff_output("\t%s", buf);
911fd660bf2Stobias 	}
912fd660bf2Stobias 
913fd660bf2Stobias 	diff_output("\n");
914fd660bf2Stobias }
915fd660bf2Stobias 
916fd660bf2Stobias static void
917fd660bf2Stobias rdiff_head(void)
918fd660bf2Stobias {
919fd660bf2Stobias 	char buf[64];
920fd660bf2Stobias 	struct tm *t;
921fd660bf2Stobias 	time_t curr_time;
922fd660bf2Stobias 
923fd660bf2Stobias 	if (diff_rev1 != NULL) {
924fd660bf2Stobias 		t = localtime(&stb1.st_mtime);
925fd660bf2Stobias 	} else {
926fd660bf2Stobias 		time(&curr_time);
927fd660bf2Stobias 		t = localtime(&curr_time);
928fd660bf2Stobias 	}
929fd660bf2Stobias 
930fd660bf2Stobias 	diff_output("%s ", diff_format == D_CONTEXT ? "***" : "---");
931fd660bf2Stobias 
932fd660bf2Stobias 	if (diff_rev1 == NULL) {
933fd660bf2Stobias 		diff_output("%s", CVS_PATH_DEVNULL);
934fd660bf2Stobias 		t = gmtime(&stb1.st_atime);
935fd660bf2Stobias 	} else {
936fd660bf2Stobias 		rcsnum_tostr(diff_rev1, buf, sizeof(buf));
937*be756b91Stobias 		diff_output("%s:%s", diff_file1, buf);
938fd660bf2Stobias 	}
939fd660bf2Stobias 
940fd660bf2Stobias 	(void)strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %G", t);
941fd660bf2Stobias 	diff_output("\t%s\n", buf);
942fd660bf2Stobias 
943fd660bf2Stobias 	if (diff_rev2 != NULL) {
944fd660bf2Stobias 		t = localtime(&stb2.st_mtime);
945fd660bf2Stobias 	} else {
946fd660bf2Stobias 		time(&curr_time);
947fd660bf2Stobias 		t = localtime(&curr_time);
948fd660bf2Stobias 	}
949fd660bf2Stobias 
950fd660bf2Stobias 	(void)strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %G", t);
951fd660bf2Stobias 
952fd660bf2Stobias 	diff_output("%s %s	%s\n", diff_format == D_CONTEXT ? "---" : "+++",
953*be756b91Stobias 	    diff_file2, buf);
954fd660bf2Stobias }
955fd660bf2Stobias 
9563ad3fb45Sjoris /*
9573ad3fb45Sjoris  * Indicate that there is a difference between lines a and b of the from file
9583ad3fb45Sjoris  * to get to lines c to d of the to file.  If a is greater then b then there
9593ad3fb45Sjoris  * are no lines in the from file involved and this means that there were
9603ad3fb45Sjoris  * lines appended (beginning at b).  If c is greater than d then there are
9613ad3fb45Sjoris  * lines missing from the to file.
9623ad3fb45Sjoris  */
9633ad3fb45Sjoris static void
9643ad3fb45Sjoris change(FILE *f1, FILE *f2, int a, int b, int c, int d)
9653ad3fb45Sjoris {
9663ad3fb45Sjoris 	static size_t max_context = 64;
967eea21b3cSray 	int i;
9683ad3fb45Sjoris 
9693ad3fb45Sjoris 	if (diff_format != D_IFDEF && a > b && c > d)
9703ad3fb45Sjoris 		return;
9713ad3fb45Sjoris 	if (ignore_pats != NULL) {
9723ad3fb45Sjoris 		char *line;
9733ad3fb45Sjoris 		/*
9743ad3fb45Sjoris 		 * All lines in the change, insert, or delete must
9753ad3fb45Sjoris 		 * match an ignore pattern for the change to be
9763ad3fb45Sjoris 		 * ignored.
9773ad3fb45Sjoris 		 */
9783ad3fb45Sjoris 		if (a <= b) {		/* Changes and deletes. */
9793ad3fb45Sjoris 			for (i = a; i <= b; i++) {
9803ad3fb45Sjoris 				line = preadline(fileno(f1),
9813ad3fb45Sjoris 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
9823ad3fb45Sjoris 				if (!ignoreline(line))
9833ad3fb45Sjoris 					goto proceed;
9843ad3fb45Sjoris 			}
9853ad3fb45Sjoris 		}
9863ad3fb45Sjoris 		if (a > b || c <= d) {	/* Changes and inserts. */
9873ad3fb45Sjoris 			for (i = c; i <= d; i++) {
9883ad3fb45Sjoris 				line = preadline(fileno(f2),
9893ad3fb45Sjoris 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
9903ad3fb45Sjoris 				if (!ignoreline(line))
9913ad3fb45Sjoris 					goto proceed;
9923ad3fb45Sjoris 			}
9933ad3fb45Sjoris 		}
9943ad3fb45Sjoris 		return;
9953ad3fb45Sjoris 	}
9963ad3fb45Sjoris proceed:
9973ad3fb45Sjoris 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
9983ad3fb45Sjoris 		/*
9993ad3fb45Sjoris 		 * Allocate change records as needed.
10003ad3fb45Sjoris 		 */
10013ad3fb45Sjoris 		if (context_vec_ptr == context_vec_end - 1) {
10023ad3fb45Sjoris 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
10033ad3fb45Sjoris 			max_context <<= 1;
100480566be2Sray 			context_vec_start = xrealloc(context_vec_start,
100580566be2Sray 			    max_context, sizeof(*context_vec_start));
10063ad3fb45Sjoris 			context_vec_end = context_vec_start + max_context;
10073ad3fb45Sjoris 			context_vec_ptr = context_vec_start + offset;
10083ad3fb45Sjoris 		}
10093ad3fb45Sjoris 		if (anychange == 0) {
10103ad3fb45Sjoris 			/*
10113ad3fb45Sjoris 			 * Print the context/unidiff header first time through.
10123ad3fb45Sjoris 			 */
1013fd660bf2Stobias 			if (cvs_cmdop == CVS_OP_RDIFF)
1014fd660bf2Stobias 				rdiff_head();
1015fd660bf2Stobias 			else
1016fd660bf2Stobias 				diff_head();
10173ad3fb45Sjoris 
10183ad3fb45Sjoris 			anychange = 1;
10193ad3fb45Sjoris 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
10203ad3fb45Sjoris 		    c > context_vec_ptr->d + (2 * context) + 1) {
10213ad3fb45Sjoris 			/*
10223ad3fb45Sjoris 			 * If this change is more than 'context' lines from the
10233ad3fb45Sjoris 			 * previous change, dump the record and reset it.
10243ad3fb45Sjoris 			 */
10253ad3fb45Sjoris 			if (diff_format == D_CONTEXT)
10263ad3fb45Sjoris 				dump_context_vec(f1, f2);
10273ad3fb45Sjoris 			else
10283ad3fb45Sjoris 				dump_unified_vec(f1, f2);
10293ad3fb45Sjoris 		}
10303ad3fb45Sjoris 		context_vec_ptr++;
10313ad3fb45Sjoris 		context_vec_ptr->a = a;
10323ad3fb45Sjoris 		context_vec_ptr->b = b;
10333ad3fb45Sjoris 		context_vec_ptr->c = c;
10343ad3fb45Sjoris 		context_vec_ptr->d = d;
10353ad3fb45Sjoris 		return;
10363ad3fb45Sjoris 	}
10373ad3fb45Sjoris 	if (anychange == 0)
10383ad3fb45Sjoris 		anychange = 1;
10393ad3fb45Sjoris 	switch (diff_format) {
10403ad3fb45Sjoris 	case D_BRIEF:
10413ad3fb45Sjoris 		return;
10423ad3fb45Sjoris 	case D_NORMAL:
10433ad3fb45Sjoris 		range(a, b, ",");
10443ad3fb45Sjoris 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
10453ad3fb45Sjoris 		if (diff_format == D_NORMAL)
10463ad3fb45Sjoris 			range(c, d, ",");
10473ad3fb45Sjoris 		diff_output("\n");
10483ad3fb45Sjoris 		break;
10493ad3fb45Sjoris 	case D_RCSDIFF:
10503ad3fb45Sjoris 		if (a > b)
10513ad3fb45Sjoris 			diff_output("a%d %d\n", b, d - c + 1);
10523ad3fb45Sjoris 		else {
10533ad3fb45Sjoris 			diff_output("d%d %d\n", a, b - a + 1);
10543ad3fb45Sjoris 
10553ad3fb45Sjoris 			if (!(c > d))	/* add changed lines */
10563ad3fb45Sjoris 				diff_output("a%d %d\n", b, d - c + 1);
10573ad3fb45Sjoris 		}
10583ad3fb45Sjoris 		break;
10593ad3fb45Sjoris 	}
10603ad3fb45Sjoris 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
10613ad3fb45Sjoris 		fetch(ixold, a, b, f1, '<', 1);
10623ad3fb45Sjoris 		if (a <= b && c <= d && diff_format == D_NORMAL)
10633ad3fb45Sjoris 			diff_output("---\n");
10643ad3fb45Sjoris 	}
10653ad3fb45Sjoris 	fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0);
10663ad3fb45Sjoris 	if (inifdef) {
10673ad3fb45Sjoris 		diff_output("#endif /* %s */\n", ifdefname);
10683ad3fb45Sjoris 		inifdef = 0;
10693ad3fb45Sjoris 	}
10703ad3fb45Sjoris }
10713ad3fb45Sjoris 
10723ad3fb45Sjoris static void
10733ad3fb45Sjoris fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
10743ad3fb45Sjoris {
10753ad3fb45Sjoris 	long j, nc;
10763ad3fb45Sjoris 	int i, c, col;
10773ad3fb45Sjoris 
10783ad3fb45Sjoris 	/*
10793ad3fb45Sjoris 	 * When doing #ifdef's, copy down to current line
10803ad3fb45Sjoris 	 * if this is the first file, so that stuff makes it to output.
10813ad3fb45Sjoris 	 */
10823ad3fb45Sjoris 	if (diff_format == D_IFDEF && oldfile) {
10833ad3fb45Sjoris 		long curpos = ftell(lb);
10843ad3fb45Sjoris 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
10853ad3fb45Sjoris 		nc = f[a > b ? b : a - 1] - curpos;
10863ad3fb45Sjoris 		for (i = 0; i < nc; i++)
10873ad3fb45Sjoris 			diff_output("%c", getc(lb));
10883ad3fb45Sjoris 	}
10893ad3fb45Sjoris 	if (a > b)
10903ad3fb45Sjoris 		return;
10913ad3fb45Sjoris 	if (diff_format == D_IFDEF) {
10923ad3fb45Sjoris 		if (inifdef) {
10933ad3fb45Sjoris 			diff_output("#else /* %s%s */\n",
10943ad3fb45Sjoris 			    oldfile == 1 ? "!" : "", ifdefname);
10953ad3fb45Sjoris 		} else {
10963ad3fb45Sjoris 			if (oldfile)
10973ad3fb45Sjoris 				diff_output("#ifndef %s\n", ifdefname);
10983ad3fb45Sjoris 			else
10993ad3fb45Sjoris 				diff_output("#ifdef %s\n", ifdefname);
11003ad3fb45Sjoris 		}
11013ad3fb45Sjoris 		inifdef = 1 + oldfile;
11023ad3fb45Sjoris 	}
11033ad3fb45Sjoris 	for (i = a; i <= b; i++) {
11043ad3fb45Sjoris 		fseek(lb, f[i - 1], SEEK_SET);
11053ad3fb45Sjoris 		nc = f[i] - f[i - 1];
11063ad3fb45Sjoris 		if (diff_format != D_IFDEF && ch != '\0') {
11073ad3fb45Sjoris 			diff_output("%c", ch);
11083ad3fb45Sjoris 			if (Tflag == 1 && (diff_format == D_NORMAL ||
11093ad3fb45Sjoris 			    diff_format == D_CONTEXT ||
11103ad3fb45Sjoris 			    diff_format == D_UNIFIED))
11113ad3fb45Sjoris 				diff_output("\t");
11123ad3fb45Sjoris 			else if (diff_format != D_UNIFIED)
11133ad3fb45Sjoris 				diff_output(" ");
11143ad3fb45Sjoris 		}
11153ad3fb45Sjoris 		col = 0;
11163ad3fb45Sjoris 		for (j = 0; j < nc; j++) {
11173ad3fb45Sjoris 			if ((c = getc(lb)) == EOF) {
11183ad3fb45Sjoris 				if (diff_format == D_RCSDIFF)
11193ad3fb45Sjoris 					cvs_log(LP_ERR,
11203ad3fb45Sjoris 					    "No newline at end of file");
11213ad3fb45Sjoris 				else
11223ad3fb45Sjoris 					diff_output("\n\\ No newline at end of "
112325d5ee6bSjoris 					    "file\n");
11243ad3fb45Sjoris 				return;
11253ad3fb45Sjoris 			}
11263ad3fb45Sjoris 			if (c == '\t' && tflag == 1) {
11273ad3fb45Sjoris 				do {
11283ad3fb45Sjoris 					diff_output(" ");
11293ad3fb45Sjoris 				} while (++col & 7);
11303ad3fb45Sjoris 			} else {
11313ad3fb45Sjoris 				diff_output("%c", c);
11323ad3fb45Sjoris 				col++;
11333ad3fb45Sjoris 			}
11343ad3fb45Sjoris 		}
11353ad3fb45Sjoris 	}
11363ad3fb45Sjoris }
11373ad3fb45Sjoris 
11383ad3fb45Sjoris /*
11393ad3fb45Sjoris  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
11403ad3fb45Sjoris  */
11413ad3fb45Sjoris static int
11423ad3fb45Sjoris readhash(FILE *f)
11433ad3fb45Sjoris {
11443ad3fb45Sjoris 	int i, t, space;
11453ad3fb45Sjoris 	int sum;
11463ad3fb45Sjoris 
11473ad3fb45Sjoris 	sum = 1;
11483ad3fb45Sjoris 	space = 0;
11493ad3fb45Sjoris 	if (bflag != 1 && wflag != 1) {
1150fd660bf2Stobias 		if (diff_iflag == 1)
11513ad3fb45Sjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
11523ad3fb45Sjoris 				if (t == EOF) {
11533ad3fb45Sjoris 					if (i == 0)
11543ad3fb45Sjoris 						return (0);
11553ad3fb45Sjoris 					break;
11563ad3fb45Sjoris 				}
11573ad3fb45Sjoris 				sum = sum * 127 + chrtran[t];
11583ad3fb45Sjoris 			}
11593ad3fb45Sjoris 		else
11603ad3fb45Sjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
11613ad3fb45Sjoris 				if (t == EOF) {
11623ad3fb45Sjoris 					if (i == 0)
11633ad3fb45Sjoris 						return (0);
11643ad3fb45Sjoris 					break;
11653ad3fb45Sjoris 				}
11663ad3fb45Sjoris 				sum = sum * 127 + t;
11673ad3fb45Sjoris 			}
11683ad3fb45Sjoris 	} else {
11693ad3fb45Sjoris 		for (i = 0;;) {
11703ad3fb45Sjoris 			switch (t = getc(f)) {
11713ad3fb45Sjoris 			case '\t':
1172eea21b3cSray 			case '\r':
1173eea21b3cSray 			case '\v':
1174eea21b3cSray 			case '\f':
11753ad3fb45Sjoris 			case ' ':
11763ad3fb45Sjoris 				space++;
11773ad3fb45Sjoris 				continue;
11783ad3fb45Sjoris 			default:
11793ad3fb45Sjoris 				if (space != 0 && wflag != 1) {
11803ad3fb45Sjoris 					i++;
11813ad3fb45Sjoris 					space = 0;
11823ad3fb45Sjoris 				}
11833ad3fb45Sjoris 				sum = sum * 127 + chrtran[t];
11843ad3fb45Sjoris 				i++;
11853ad3fb45Sjoris 				continue;
11863ad3fb45Sjoris 			case EOF:
11873ad3fb45Sjoris 				if (i == 0)
11883ad3fb45Sjoris 					return (0);
11893ad3fb45Sjoris 				/* FALLTHROUGH */
11903ad3fb45Sjoris 			case '\n':
11913ad3fb45Sjoris 				break;
11923ad3fb45Sjoris 			}
11933ad3fb45Sjoris 			break;
11943ad3fb45Sjoris 		}
11953ad3fb45Sjoris 	}
11963ad3fb45Sjoris 	/*
11973ad3fb45Sjoris 	 * There is a remote possibility that we end up with a zero sum.
11983ad3fb45Sjoris 	 * Zero is used as an EOF marker, so return 1 instead.
11993ad3fb45Sjoris 	 */
12003ad3fb45Sjoris 	return (sum == 0 ? 1 : sum);
12013ad3fb45Sjoris }
12023ad3fb45Sjoris 
12033ad3fb45Sjoris static int
12043ad3fb45Sjoris asciifile(FILE *f)
12053ad3fb45Sjoris {
1206eea21b3cSray 	unsigned char buf[BUFSIZ];
12073ad3fb45Sjoris 	size_t i, cnt;
12083ad3fb45Sjoris 
12093ad3fb45Sjoris 	if (aflag == 1 || f == NULL)
12103ad3fb45Sjoris 		return (1);
12113ad3fb45Sjoris 
12123ad3fb45Sjoris 	rewind(f);
1213a304c0f4Sray 	cnt = fread(buf, 1, sizeof(buf), f);
12143ad3fb45Sjoris 	for (i = 0; i < cnt; i++)
12153ad3fb45Sjoris 		if (!isprint(buf[i]) && !isspace(buf[i]))
12163ad3fb45Sjoris 			return (0);
12173ad3fb45Sjoris 	return (1);
12183ad3fb45Sjoris }
12193ad3fb45Sjoris 
1220e22e6974Sxsa #define begins_with(s, pre) (strncmp(s, pre, sizeof(pre)-1) == 0)
1221e22e6974Sxsa 
12223ad3fb45Sjoris static char *
12233ad3fb45Sjoris match_function(const long *f, int pos, FILE *fp)
12243ad3fb45Sjoris {
12253ad3fb45Sjoris 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
12263ad3fb45Sjoris 	size_t nc;
12273ad3fb45Sjoris 	int last = lastline;
12283ad3fb45Sjoris 	char *p;
1229e22e6974Sxsa 	char *state = NULL;
12303ad3fb45Sjoris 
12313ad3fb45Sjoris 	lastline = pos;
12323ad3fb45Sjoris 	while (pos > last) {
12333ad3fb45Sjoris 		fseek(fp, f[pos - 1], SEEK_SET);
12343ad3fb45Sjoris 		nc = f[pos] - f[pos - 1];
12353ad3fb45Sjoris 		if (nc >= sizeof(buf))
12363ad3fb45Sjoris 			nc = sizeof(buf) - 1;
1237a304c0f4Sray 		nc = fread(buf, 1, nc, fp);
12383ad3fb45Sjoris 		if (nc > 0) {
12393ad3fb45Sjoris 			buf[nc] = '\0';
12403ad3fb45Sjoris 			p = strchr((const char *)buf, '\n');
12413ad3fb45Sjoris 			if (p != NULL)
12423ad3fb45Sjoris 				*p = '\0';
12433ad3fb45Sjoris 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
1244e22e6974Sxsa 				if (begins_with(buf, "private:")) {
1245e22e6974Sxsa 					if (!state)
1246e22e6974Sxsa 						state = " (private)";
1247e22e6974Sxsa 				} else if (begins_with(buf, "protected:")) {
1248e22e6974Sxsa 					if (!state)
1249e22e6974Sxsa 						state = " (protected)";
1250e22e6974Sxsa 				} else if (begins_with(buf, "public:")) {
1251e22e6974Sxsa 					if (!state)
1252e22e6974Sxsa 						state = " (public)";
1253e22e6974Sxsa 				} else {
1254eea21b3cSray 					strlcpy(lastbuf, buf,
12553ad3fb45Sjoris 					    sizeof lastbuf);
1256e22e6974Sxsa 					if (state)
1257e22e6974Sxsa 						strlcat(lastbuf, state,
1258e22e6974Sxsa 						    sizeof lastbuf);
12593ad3fb45Sjoris 					lastmatchline = pos;
12603ad3fb45Sjoris 					return lastbuf;
12613ad3fb45Sjoris 				}
12623ad3fb45Sjoris 			}
1263e22e6974Sxsa 		}
12643ad3fb45Sjoris 		pos--;
12653ad3fb45Sjoris 	}
1266eea21b3cSray 	return lastmatchline > 0 ? lastbuf : NULL;
12673ad3fb45Sjoris }
12683ad3fb45Sjoris 
12693ad3fb45Sjoris /* dump accumulated "context" diff changes */
12703ad3fb45Sjoris static void
12713ad3fb45Sjoris dump_context_vec(FILE *f1, FILE *f2)
12723ad3fb45Sjoris {
12733ad3fb45Sjoris 	struct context_vec *cvp = context_vec_start;
12743ad3fb45Sjoris 	int lowa, upb, lowc, upd, do_output;
12753ad3fb45Sjoris 	int a, b, c, d;
12763ad3fb45Sjoris 	char ch, *f;
12773ad3fb45Sjoris 
12783ad3fb45Sjoris 	if (context_vec_start > context_vec_ptr)
12793ad3fb45Sjoris 		return;
12803ad3fb45Sjoris 
12813ad3fb45Sjoris 	b = d = 0;		/* gcc */
12823ad3fb45Sjoris 	lowa = MAX(1, cvp->a - context);
1283eea21b3cSray 	upb = MIN(len[0], context_vec_ptr->b + context);
12843ad3fb45Sjoris 	lowc = MAX(1, cvp->c - context);
1285eea21b3cSray 	upd = MIN(len[1], context_vec_ptr->d + context);
12863ad3fb45Sjoris 
12873ad3fb45Sjoris 	diff_output("***************");
1288261cb0daSjoris 	if (diff_pflag == 1) {
12893ad3fb45Sjoris 		f = match_function(ixold, lowa-1, f1);
12903ad3fb45Sjoris 		if (f != NULL) {
12913ad3fb45Sjoris 			diff_output(" ");
12923ad3fb45Sjoris 			diff_output("%s", f);
12933ad3fb45Sjoris 		}
12943ad3fb45Sjoris 	}
12953ad3fb45Sjoris 	diff_output("\n*** ");
12963ad3fb45Sjoris 	range(lowa, upb, ",");
12973ad3fb45Sjoris 	diff_output(" ****\n");
12983ad3fb45Sjoris 
12993ad3fb45Sjoris 	/*
13003ad3fb45Sjoris 	 * Output changes to the "old" file.  The first loop suppresses
13013ad3fb45Sjoris 	 * output if there were no changes to the "old" file (we'll see
13023ad3fb45Sjoris 	 * the "old" lines as context in the "new" list).
13033ad3fb45Sjoris 	 */
13043ad3fb45Sjoris 	do_output = 0;
13053ad3fb45Sjoris 	for (; cvp <= context_vec_ptr; cvp++)
13063ad3fb45Sjoris 		if (cvp->a <= cvp->b) {
13073ad3fb45Sjoris 			cvp = context_vec_start;
13083ad3fb45Sjoris 			do_output++;
13093ad3fb45Sjoris 			break;
13103ad3fb45Sjoris 		}
1311eea21b3cSray 	if (do_output) {
13123ad3fb45Sjoris 		while (cvp <= context_vec_ptr) {
13133ad3fb45Sjoris 			a = cvp->a;
13143ad3fb45Sjoris 			b = cvp->b;
13153ad3fb45Sjoris 			c = cvp->c;
13163ad3fb45Sjoris 			d = cvp->d;
13173ad3fb45Sjoris 
13183ad3fb45Sjoris 			if (a <= b && c <= d)
13193ad3fb45Sjoris 				ch = 'c';
13203ad3fb45Sjoris 			else
13213ad3fb45Sjoris 				ch = (a <= b) ? 'd' : 'a';
13223ad3fb45Sjoris 
13233ad3fb45Sjoris 			if (ch == 'a')
13243ad3fb45Sjoris 				fetch(ixold, lowa, b, f1, ' ', 0);
13253ad3fb45Sjoris 			else {
13263ad3fb45Sjoris 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
13273ad3fb45Sjoris 				fetch(ixold, a, b, f1,
13283ad3fb45Sjoris 				    ch == 'c' ? '!' : '-', 0);
13293ad3fb45Sjoris 			}
13303ad3fb45Sjoris 			lowa = b + 1;
13313ad3fb45Sjoris 			cvp++;
13323ad3fb45Sjoris 		}
13333ad3fb45Sjoris 		fetch(ixold, b + 1, upb, f1, ' ', 0);
13343ad3fb45Sjoris 	}
13353ad3fb45Sjoris 	/* output changes to the "new" file */
13363ad3fb45Sjoris 	diff_output("--- ");
13373ad3fb45Sjoris 	range(lowc, upd, ",");
13383ad3fb45Sjoris 	diff_output(" ----\n");
13393ad3fb45Sjoris 
13403ad3fb45Sjoris 	do_output = 0;
13413ad3fb45Sjoris 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
13423ad3fb45Sjoris 		if (cvp->c <= cvp->d) {
13433ad3fb45Sjoris 			cvp = context_vec_start;
13443ad3fb45Sjoris 			do_output++;
13453ad3fb45Sjoris 			break;
13463ad3fb45Sjoris 		}
1347eea21b3cSray 	if (do_output) {
13483ad3fb45Sjoris 		while (cvp <= context_vec_ptr) {
13493ad3fb45Sjoris 			a = cvp->a;
13503ad3fb45Sjoris 			b = cvp->b;
13513ad3fb45Sjoris 			c = cvp->c;
13523ad3fb45Sjoris 			d = cvp->d;
13533ad3fb45Sjoris 
13543ad3fb45Sjoris 			if (a <= b && c <= d)
13553ad3fb45Sjoris 				ch = 'c';
13563ad3fb45Sjoris 			else
13573ad3fb45Sjoris 				ch = (a <= b) ? 'd' : 'a';
13583ad3fb45Sjoris 
13593ad3fb45Sjoris 			if (ch == 'd')
13603ad3fb45Sjoris 				fetch(ixnew, lowc, d, f2, ' ', 0);
13613ad3fb45Sjoris 			else {
13623ad3fb45Sjoris 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
13633ad3fb45Sjoris 				fetch(ixnew, c, d, f2,
13643ad3fb45Sjoris 				    ch == 'c' ? '!' : '+', 0);
13653ad3fb45Sjoris 			}
13663ad3fb45Sjoris 			lowc = d + 1;
13673ad3fb45Sjoris 			cvp++;
13683ad3fb45Sjoris 		}
13693ad3fb45Sjoris 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
13703ad3fb45Sjoris 	}
13713ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
13723ad3fb45Sjoris }
13733ad3fb45Sjoris 
13743ad3fb45Sjoris /* dump accumulated "unified" diff changes */
13753ad3fb45Sjoris static void
13763ad3fb45Sjoris dump_unified_vec(FILE *f1, FILE *f2)
13773ad3fb45Sjoris {
13783ad3fb45Sjoris 	struct context_vec *cvp = context_vec_start;
13793ad3fb45Sjoris 	int lowa, upb, lowc, upd;
13803ad3fb45Sjoris 	int a, b, c, d;
13813ad3fb45Sjoris 	char ch, *f;
13823ad3fb45Sjoris 
13833ad3fb45Sjoris 	if (context_vec_start > context_vec_ptr)
13843ad3fb45Sjoris 		return;
13853ad3fb45Sjoris 
13863ad3fb45Sjoris 	b = d = 0;		/* gcc */
13873ad3fb45Sjoris 	lowa = MAX(1, cvp->a - context);
1388eea21b3cSray 	upb = MIN(len[0], context_vec_ptr->b + context);
13893ad3fb45Sjoris 	lowc = MAX(1, cvp->c - context);
1390eea21b3cSray 	upd = MIN(len[1], context_vec_ptr->d + context);
13913ad3fb45Sjoris 
13923ad3fb45Sjoris 	diff_output("@@ -");
13933ad3fb45Sjoris 	uni_range(lowa, upb);
13943ad3fb45Sjoris 	diff_output(" +");
13953ad3fb45Sjoris 	uni_range(lowc, upd);
13963ad3fb45Sjoris 	diff_output(" @@");
1397261cb0daSjoris 	if (diff_pflag == 1) {
13983ad3fb45Sjoris 		f = match_function(ixold, lowa-1, f1);
13993ad3fb45Sjoris 		if (f != NULL) {
14003ad3fb45Sjoris 			diff_output(" ");
14013ad3fb45Sjoris 			diff_output("%s", f);
14023ad3fb45Sjoris 		}
14033ad3fb45Sjoris 	}
14043ad3fb45Sjoris 	diff_output("\n");
14053ad3fb45Sjoris 
14063ad3fb45Sjoris 	/*
14073ad3fb45Sjoris 	 * Output changes in "unified" diff format--the old and new lines
14083ad3fb45Sjoris 	 * are printed together.
14093ad3fb45Sjoris 	 */
14103ad3fb45Sjoris 	for (; cvp <= context_vec_ptr; cvp++) {
14113ad3fb45Sjoris 		a = cvp->a;
14123ad3fb45Sjoris 		b = cvp->b;
14133ad3fb45Sjoris 		c = cvp->c;
14143ad3fb45Sjoris 		d = cvp->d;
14153ad3fb45Sjoris 
14163ad3fb45Sjoris 		/*
14173ad3fb45Sjoris 		 * c: both new and old changes
14183ad3fb45Sjoris 		 * d: only changes in the old file
14193ad3fb45Sjoris 		 * a: only changes in the new file
14203ad3fb45Sjoris 		 */
14213ad3fb45Sjoris 		if (a <= b && c <= d)
14223ad3fb45Sjoris 			ch = 'c';
14233ad3fb45Sjoris 		else
14243ad3fb45Sjoris 			ch = (a <= b) ? 'd' : 'a';
14253ad3fb45Sjoris 
14263ad3fb45Sjoris 		switch (ch) {
14273ad3fb45Sjoris 		case 'c':
14283ad3fb45Sjoris 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
14293ad3fb45Sjoris 			fetch(ixold, a, b, f1, '-', 0);
14303ad3fb45Sjoris 			fetch(ixnew, c, d, f2, '+', 0);
14313ad3fb45Sjoris 			break;
14323ad3fb45Sjoris 		case 'd':
14333ad3fb45Sjoris 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
14343ad3fb45Sjoris 			fetch(ixold, a, b, f1, '-', 0);
14353ad3fb45Sjoris 			break;
14363ad3fb45Sjoris 		case 'a':
14373ad3fb45Sjoris 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
14383ad3fb45Sjoris 			fetch(ixnew, c, d, f2, '+', 0);
14393ad3fb45Sjoris 			break;
14403ad3fb45Sjoris 		}
14413ad3fb45Sjoris 		lowa = b + 1;
14423ad3fb45Sjoris 		lowc = d + 1;
14433ad3fb45Sjoris 	}
14443ad3fb45Sjoris 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
14453ad3fb45Sjoris 
14463ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
14473ad3fb45Sjoris }
14483ad3fb45Sjoris 
14493ad3fb45Sjoris void
14503ad3fb45Sjoris diff_output(const char *fmt, ...)
14513ad3fb45Sjoris {
14523ad3fb45Sjoris 	va_list vap;
14533ad3fb45Sjoris 	int i;
14543ad3fb45Sjoris 	char *str;
14553ad3fb45Sjoris 
14563ad3fb45Sjoris 	va_start(vap, fmt);
14573ad3fb45Sjoris 	i = vasprintf(&str, fmt, vap);
14583ad3fb45Sjoris 	va_end(vap);
14593ad3fb45Sjoris 	if (i == -1)
14609a0ecc80Stobias 		fatal("diff_output: could not allocate memory");
14613ad3fb45Sjoris 	if (diffbuf != NULL)
14623ad3fb45Sjoris 		cvs_buf_append(diffbuf, str, strlen(str));
14633ad3fb45Sjoris 	else
14643ad3fb45Sjoris 		cvs_printf("%s", str);
14653ad3fb45Sjoris 	xfree(str);
14663ad3fb45Sjoris }
1467