xref: /openbsd-src/usr.bin/cvs/diff_internals.c (revision b9fc9a728fce9c4289b7e9a992665e28d5629a54)
1*b9fc9a72Sderaadt /*	$OpenBSD: diff_internals.c,v 1.36 2015/01/16 06:40:07 deraadt 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 
67*b9fc9a72Sderaadt #include <sys/types.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>
766534056aStobias #include <time.h>
77eea21b3cSray #include <unistd.h>
78eea21b3cSray 
79eea21b3cSray #include "cvs.h"
80eea21b3cSray #include "diff.h"
81eea21b3cSray 
82*b9fc9a72Sderaadt #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
83*b9fc9a72Sderaadt #define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
84*b9fc9a72Sderaadt 
85eea21b3cSray /*
86eea21b3cSray  * diff - compare two files.
87eea21b3cSray  */
88eea21b3cSray 
893ad3fb45Sjoris /*
903ad3fb45Sjoris  *	Uses an algorithm due to Harold Stone, which finds
913ad3fb45Sjoris  *	a pair of longest identical subsequences in the two
923ad3fb45Sjoris  *	files.
933ad3fb45Sjoris  *
943ad3fb45Sjoris  *	The major goal is to generate the match vector J.
953ad3fb45Sjoris  *	J[i] is the index of the line in file1 corresponding
963ad3fb45Sjoris  *	to line i file0. J[i] = 0 if there is no
973ad3fb45Sjoris  *	such line in file1.
983ad3fb45Sjoris  *
993ad3fb45Sjoris  *	Lines are hashed so as to work in core. All potential
1003ad3fb45Sjoris  *	matches are located by sorting the lines of each file
1013ad3fb45Sjoris  *	on the hash (called ``value''). In particular, this
1023ad3fb45Sjoris  *	collects the equivalence classes in file1 together.
1033ad3fb45Sjoris  *	Subroutine equiv replaces the value of each line in
1043ad3fb45Sjoris  *	file0 by the index of the first element of its
1053ad3fb45Sjoris  *	matching equivalence in (the reordered) file1.
1063ad3fb45Sjoris  *	To save space equiv squeezes file1 into a single
1073ad3fb45Sjoris  *	array member in which the equivalence classes
1083ad3fb45Sjoris  *	are simply concatenated, except that their first
1093ad3fb45Sjoris  *	members are flagged by changing sign.
1103ad3fb45Sjoris  *
1113ad3fb45Sjoris  *	Next the indices that point into member are unsorted into
1123ad3fb45Sjoris  *	array class according to the original order of file0.
1133ad3fb45Sjoris  *
1143ad3fb45Sjoris  *	The cleverness lies in routine stone. This marches
1153ad3fb45Sjoris  *	through the lines of file0, developing a vector klist
1163ad3fb45Sjoris  *	of "k-candidates". At step i a k-candidate is a matched
1173ad3fb45Sjoris  *	pair of lines x,y (x in file0 y in file1) such that
1183ad3fb45Sjoris  *	there is a common subsequence of length k
1193ad3fb45Sjoris  *	between the first i lines of file0 and the first y
1203ad3fb45Sjoris  *	lines of file1, but there is no such subsequence for
1213ad3fb45Sjoris  *	any smaller y. x is the earliest possible mate to y
1223ad3fb45Sjoris  *	that occurs in such a subsequence.
1233ad3fb45Sjoris  *
1243ad3fb45Sjoris  *	Whenever any of the members of the equivalence class of
1253ad3fb45Sjoris  *	lines in file1 matable to a line in file0 has serial number
1263ad3fb45Sjoris  *	less than the y of some k-candidate, that k-candidate
1273ad3fb45Sjoris  *	with the smallest such y is replaced. The new
1283ad3fb45Sjoris  *	k-candidate is chained (via pred) to the current
1293ad3fb45Sjoris  *	k-1 candidate so that the actual subsequence can
1303ad3fb45Sjoris  *	be recovered. When a member has serial number greater
1313ad3fb45Sjoris  *	that the y of all k-candidates, the klist is extended.
1323ad3fb45Sjoris  *	At the end, the longest subsequence is pulled out
1333ad3fb45Sjoris  *	and placed in the array J by unravel
1343ad3fb45Sjoris  *
1353ad3fb45Sjoris  *	With J in hand, the matches there recorded are
1363ad3fb45Sjoris  *	check'ed against reality to assure that no spurious
1373ad3fb45Sjoris  *	matches have crept in due to hashing. If they have,
1383ad3fb45Sjoris  *	they are broken, and "jackpot" is recorded--a harmless
1393ad3fb45Sjoris  *	matter except that a true match for a spuriously
1403ad3fb45Sjoris  *	mated line may now be unnecessarily reported as a change.
1413ad3fb45Sjoris  *
1423ad3fb45Sjoris  *	Much of the complexity of the program comes simply
1433ad3fb45Sjoris  *	from trying to minimize core utilization and
1443ad3fb45Sjoris  *	maximize the range of doable problems by dynamically
1453ad3fb45Sjoris  *	allocating what is needed and reusing what is not.
1463ad3fb45Sjoris  *	The core requirements for problems larger than somewhat
1473ad3fb45Sjoris  *	are (in words) 2*length(file0) + length(file1) +
1483ad3fb45Sjoris  *	3*(number of k-candidates installed),  typically about
1493ad3fb45Sjoris  *	6n words for files of length n.
1503ad3fb45Sjoris  */
1513ad3fb45Sjoris 
1523ad3fb45Sjoris struct cand {
1533ad3fb45Sjoris 	int	x;
1543ad3fb45Sjoris 	int	y;
1553ad3fb45Sjoris 	int	pred;
156ccf8fb4cSray };
1573ad3fb45Sjoris 
1583ad3fb45Sjoris struct line {
1593ad3fb45Sjoris 	int	serial;
1603ad3fb45Sjoris 	int	value;
1613ad3fb45Sjoris } *file[2];
1623ad3fb45Sjoris 
1633ad3fb45Sjoris /*
1643ad3fb45Sjoris  * The following struct is used to record change information when
1653ad3fb45Sjoris  * doing a "context" or "unified" diff.  (see routine "change" to
1663ad3fb45Sjoris  * understand the highly mnemonic field names)
1673ad3fb45Sjoris  */
1683ad3fb45Sjoris struct context_vec {
1693ad3fb45Sjoris 	int	a;		/* start line in old file */
1703ad3fb45Sjoris 	int	b;		/* end line in old file */
1713ad3fb45Sjoris 	int	c;		/* start line in new file */
1723ad3fb45Sjoris 	int	d;		/* end line in new file */
1733ad3fb45Sjoris };
1743ad3fb45Sjoris 
175219c50abSray static void	 output(FILE *, FILE *, int);
176219c50abSray static void	 check(FILE *, FILE *, int);
1773ad3fb45Sjoris static void	 range(int, int, char *);
1783ad3fb45Sjoris static void	 uni_range(int, int);
179219c50abSray static void	 dump_context_vec(FILE *, FILE *, int);
180219c50abSray static void	 dump_unified_vec(FILE *, FILE *, int);
181219c50abSray static void	 prepare(int, FILE *, off_t, int);
1823ad3fb45Sjoris static void	 prune(void);
1833ad3fb45Sjoris static void	 equiv(struct line *, int, struct line *, int, int *);
1843ad3fb45Sjoris static void	 unravel(int);
1853ad3fb45Sjoris static void	 unsort(struct line *, int, int *);
186fd660bf2Stobias static void	 diff_head(void);
187fd660bf2Stobias static void	 rdiff_head(void);
188219c50abSray static void	 change(FILE *, FILE *, int, int, int, int, int);
1893ad3fb45Sjoris static void	 sort(struct line *, int);
1903ad3fb45Sjoris static int	 ignoreline(char *);
1913ad3fb45Sjoris static int	 asciifile(FILE *);
192219c50abSray static void	 fetch(long *, int, int, FILE *, int, int, int);
1933ad3fb45Sjoris static int	 newcand(int, int, int);
1943ad3fb45Sjoris static int	 search(int *, int, int);
1953ad3fb45Sjoris static int	 skipline(FILE *);
1963ad3fb45Sjoris static int	 isqrt(int);
197219c50abSray static int	 stone(int *, int, int *, int *, int);
198219c50abSray static int	 readhash(FILE *, int);
1993ad3fb45Sjoris static int	 files_differ(FILE *, FILE *);
2003ad3fb45Sjoris static char	*match_function(const long *, int, FILE *);
2013ad3fb45Sjoris static char	*preadline(int, size_t, off_t);
2023ad3fb45Sjoris 
203219c50abSray static int Tflag;
20472026f1aSnicm int diff_context = 3;
2053ad3fb45Sjoris int diff_format = D_NORMAL;
206be756b91Stobias const char *diff_file1 = NULL;
207be756b91Stobias const char *diff_file2 = NULL;
2083ad3fb45Sjoris RCSNUM *diff_rev1 = NULL;
2093ad3fb45Sjoris RCSNUM *diff_rev2 = NULL;
2107bb3ddb0Sray char diffargs[512];
2113ad3fb45Sjoris static struct stat stb1, stb2;
2123ad3fb45Sjoris static char *ifdefname, *ignore_pats;
2133ad3fb45Sjoris regex_t ignore_re;
2143ad3fb45Sjoris 
2153ad3fb45Sjoris static int  *J;			/* will be overlaid on class */
2163ad3fb45Sjoris static int  *class;		/* will be overlaid on file[0] */
2173ad3fb45Sjoris static int  *klist;		/* will be overlaid on file[0] after class */
2183ad3fb45Sjoris static int  *member;		/* will be overlaid on file[1] */
2193ad3fb45Sjoris static int   clen;
2203ad3fb45Sjoris static int   inifdef;		/* whether or not we are in a #ifdef block */
221eea21b3cSray static int   len[2];
2223ad3fb45Sjoris static int   pref, suff;	/* length of prefix and suffix */
2233ad3fb45Sjoris static int   slen[2];
2243ad3fb45Sjoris static int   anychange;
2253ad3fb45Sjoris static long *ixnew;		/* will be overlaid on file[1] */
2263ad3fb45Sjoris static long *ixold;		/* will be overlaid on klist */
2273ad3fb45Sjoris static struct cand *clist;	/* merely a free storage pot for candidates */
2283ad3fb45Sjoris static int   clistlen;		/* the length of clist */
2293ad3fb45Sjoris static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
2303ad3fb45Sjoris static u_char *chrtran;		/* translation table for case-folding */
2313ad3fb45Sjoris static struct context_vec *context_vec_start;
2323ad3fb45Sjoris static struct context_vec *context_vec_end;
2333ad3fb45Sjoris static struct context_vec *context_vec_ptr;
2343ad3fb45Sjoris 
235e22e6974Sxsa #define FUNCTION_CONTEXT_SIZE	55
2363ad3fb45Sjoris static char lastbuf[FUNCTION_CONTEXT_SIZE];
2373ad3fb45Sjoris static int lastline;
2383ad3fb45Sjoris static int lastmatchline;
2393ad3fb45Sjoris BUF  *diffbuf = NULL;
2403ad3fb45Sjoris 
241eea21b3cSray 
2423ad3fb45Sjoris /*
2433ad3fb45Sjoris  * chrtran points to one of 2 translation tables: cup2low if folding upper to
2443ad3fb45Sjoris  * lower case clow2low if not folding case
2453ad3fb45Sjoris  */
2463ad3fb45Sjoris u_char clow2low[256] = {
2473ad3fb45Sjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2483ad3fb45Sjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2493ad3fb45Sjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2503ad3fb45Sjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2513ad3fb45Sjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2523ad3fb45Sjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
2533ad3fb45Sjoris 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
2543ad3fb45Sjoris 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
2553ad3fb45Sjoris 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
2563ad3fb45Sjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2573ad3fb45Sjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2583ad3fb45Sjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2593ad3fb45Sjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2603ad3fb45Sjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2613ad3fb45Sjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2623ad3fb45Sjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2633ad3fb45Sjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2643ad3fb45Sjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2653ad3fb45Sjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2663ad3fb45Sjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2673ad3fb45Sjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2683ad3fb45Sjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2693ad3fb45Sjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2703ad3fb45Sjoris 	0xfd, 0xfe, 0xff
2713ad3fb45Sjoris };
2723ad3fb45Sjoris 
2733ad3fb45Sjoris u_char cup2low[256] = {
2743ad3fb45Sjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2753ad3fb45Sjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2763ad3fb45Sjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2773ad3fb45Sjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2783ad3fb45Sjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2793ad3fb45Sjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
2803ad3fb45Sjoris 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
2813ad3fb45Sjoris 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
2823ad3fb45Sjoris 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
2833ad3fb45Sjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2843ad3fb45Sjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2853ad3fb45Sjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2863ad3fb45Sjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2873ad3fb45Sjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2883ad3fb45Sjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2893ad3fb45Sjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2903ad3fb45Sjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2913ad3fb45Sjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2923ad3fb45Sjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2933ad3fb45Sjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2943ad3fb45Sjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2953ad3fb45Sjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2963ad3fb45Sjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2973ad3fb45Sjoris 	0xfd, 0xfe, 0xff
2983ad3fb45Sjoris };
2993ad3fb45Sjoris 
3003ad3fb45Sjoris int
30157003866Sray diffreg(const char *file1, const char *file2, int _fd1, int _fd2,
302219c50abSray     BUF *out, int flags)
3033ad3fb45Sjoris {
3043ad3fb45Sjoris 	FILE *f1, *f2;
3052e0d696aSjoris 	int i, rval, fd1, fd2;
3063ad3fb45Sjoris 
307be756b91Stobias 	diff_file1 = file1;
308be756b91Stobias 	diff_file2 = file2;
3093ad3fb45Sjoris 	f1 = f2 = NULL;
3103ad3fb45Sjoris 	rval = D_SAME;
3113ad3fb45Sjoris 	anychange = 0;
3123ad3fb45Sjoris 	lastline = 0;
3133ad3fb45Sjoris 	lastmatchline = 0;
3143ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
315219c50abSray 	if (flags & D_IGNORECASE)
316219c50abSray 		chrtran = cup2low;
317219c50abSray 	else
318219c50abSray 		chrtran = clow2low;
3193ad3fb45Sjoris 	if (out != NULL)
3203ad3fb45Sjoris 		diffbuf = out;
3213ad3fb45Sjoris 
3222e0d696aSjoris 	fd1 = dup(_fd1);
3232e0d696aSjoris 	if (fd1 == -1)
32457003866Sray 		fatal("diffreg: dup: %s", strerror(errno));
3252e0d696aSjoris 
3262e0d696aSjoris 	fd2 = dup(_fd2);
3272e0d696aSjoris 	if (fd2 == -1)
32857003866Sray 		fatal("diffreg: dup: %s", strerror(errno));
3292e0d696aSjoris 
330651312a5Sjoris 	if (lseek(fd1, 0, SEEK_SET) < 0)
33157003866Sray 		fatal("diffreg: lseek: %s", strerror(errno));
3322e0d696aSjoris 
3332e0d696aSjoris 	f1 = fdopen(fd1, "r");
3343ad3fb45Sjoris 	if (f1 == NULL) {
3353ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file1);
3363ad3fb45Sjoris 		goto closem;
3373ad3fb45Sjoris 	}
3383ad3fb45Sjoris 
339651312a5Sjoris 	if (lseek(fd2, 0, SEEK_SET) < 0)
34057003866Sray 		fatal("diffreg: lseek: %s", strerror(errno));
3412e0d696aSjoris 
3422e0d696aSjoris 	f2 = fdopen(fd2, "r");
3433ad3fb45Sjoris 	if (f2 == NULL) {
3443ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file2);
3453ad3fb45Sjoris 		goto closem;
3463ad3fb45Sjoris 	}
3473ad3fb45Sjoris 
3482e0d696aSjoris 	if (fstat(fd1, &stb1) < 0) {
3493ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file1);
3503ad3fb45Sjoris 		goto closem;
3513ad3fb45Sjoris 	}
3522e0d696aSjoris 
3532e0d696aSjoris 	if (fstat(fd2, &stb2) < 0) {
3543ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file2);
3553ad3fb45Sjoris 		goto closem;
3563ad3fb45Sjoris 	}
357eea21b3cSray 
3583ad3fb45Sjoris 	switch (files_differ(f1, f2)) {
3593ad3fb45Sjoris 	case 0:
3603ad3fb45Sjoris 		goto closem;
3613ad3fb45Sjoris 	case 1:
3623ad3fb45Sjoris 		break;
3633ad3fb45Sjoris 	default:
3643ad3fb45Sjoris 		/* error */
3653ad3fb45Sjoris 		goto closem;
3663ad3fb45Sjoris 	}
3673ad3fb45Sjoris 
368219c50abSray 	if ((flags & D_FORCEASCII) == 0 &&
369219c50abSray 	    (!asciifile(f1) || !asciifile(f2))) {
3703ad3fb45Sjoris 		rval = D_BINARY;
3713ad3fb45Sjoris 		goto closem;
3723ad3fb45Sjoris 	}
373eea21b3cSray 
374219c50abSray 	prepare(0, f1, stb1.st_size, flags);
375219c50abSray 	prepare(1, f2, stb2.st_size, flags);
376eea21b3cSray 
3773ad3fb45Sjoris 	prune();
3783ad3fb45Sjoris 	sort(sfile[0], slen[0]);
3793ad3fb45Sjoris 	sort(sfile[1], slen[1]);
3803ad3fb45Sjoris 
3813ad3fb45Sjoris 	member = (int *)file[1];
3823ad3fb45Sjoris 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
383caa2ffb0Sderaadt 	member = xreallocarray(member, slen[1] + 2, sizeof(*member));
3843ad3fb45Sjoris 
3853ad3fb45Sjoris 	class = (int *)file[0];
3863ad3fb45Sjoris 	unsort(sfile[0], slen[0], class);
387caa2ffb0Sderaadt 	class = xreallocarray(class, slen[0] + 2, sizeof(*class));
3883ad3fb45Sjoris 
3893ad3fb45Sjoris 	klist = xcalloc(slen[0] + 2, sizeof(*klist));
3903ad3fb45Sjoris 	clen = 0;
3913ad3fb45Sjoris 	clistlen = 100;
3923ad3fb45Sjoris 	clist = xcalloc(clistlen, sizeof(*clist));
393219c50abSray 	i = stone(class, slen[0], member, klist, flags);
3943ad3fb45Sjoris 	xfree(member);
3953ad3fb45Sjoris 	xfree(class);
3963ad3fb45Sjoris 
397caa2ffb0Sderaadt 	J = xreallocarray(J, len[0] + 2, sizeof(*J));
3983ad3fb45Sjoris 	unravel(klist[i]);
3993ad3fb45Sjoris 	xfree(clist);
4003ad3fb45Sjoris 	xfree(klist);
4013ad3fb45Sjoris 
402caa2ffb0Sderaadt 	ixold = xreallocarray(ixold, len[0] + 2, sizeof(*ixold));
403caa2ffb0Sderaadt 	ixnew = xreallocarray(ixnew, len[1] + 2, sizeof(*ixnew));
404219c50abSray 	check(f1, f2, flags);
405219c50abSray 	output(f1, f2, flags);
4063ad3fb45Sjoris 
4073ad3fb45Sjoris closem:
408eea21b3cSray 	if (anychange) {
4093ad3fb45Sjoris 		if (rval == D_SAME)
4103ad3fb45Sjoris 			rval = D_DIFFER;
4113ad3fb45Sjoris 	}
4123ad3fb45Sjoris 	if (f1 != NULL)
4133ad3fb45Sjoris 		fclose(f1);
4143ad3fb45Sjoris 	if (f2 != NULL)
4153ad3fb45Sjoris 		fclose(f2);
4163ad3fb45Sjoris 
4173ad3fb45Sjoris 	return (rval);
4183ad3fb45Sjoris }
4193ad3fb45Sjoris 
4203ad3fb45Sjoris /*
4213ad3fb45Sjoris  * Check to see if the given files differ.
4223ad3fb45Sjoris  * Returns 0 if they are the same, 1 if different, and -1 on error.
4233ad3fb45Sjoris  * XXX - could use code from cmp(1) [faster]
4243ad3fb45Sjoris  */
4253ad3fb45Sjoris static int
4263ad3fb45Sjoris files_differ(FILE *f1, FILE *f2)
4273ad3fb45Sjoris {
4283ad3fb45Sjoris 	char buf1[BUFSIZ], buf2[BUFSIZ];
4293ad3fb45Sjoris 	size_t i, j;
4303ad3fb45Sjoris 
4313ad3fb45Sjoris 	if (stb1.st_size != stb2.st_size)
4323ad3fb45Sjoris 		return (1);
4333ad3fb45Sjoris 	for (;;) {
434a304c0f4Sray 		i = fread(buf1, 1, sizeof(buf1), f1);
435a304c0f4Sray 		j = fread(buf2, 1, sizeof(buf2), f2);
43609523d6fSray 		if ((!i && ferror(f1)) || (!j && ferror(f2)))
43709523d6fSray 			return (-1);
4383ad3fb45Sjoris 		if (i != j)
4393ad3fb45Sjoris 			return (1);
44009523d6fSray 		if (i == 0)
4413ad3fb45Sjoris 			return (0);
4423ad3fb45Sjoris 		if (memcmp(buf1, buf2, i) != 0)
4433ad3fb45Sjoris 			return (1);
4443ad3fb45Sjoris 	}
4453ad3fb45Sjoris }
4463ad3fb45Sjoris 
447eea21b3cSray static void
448219c50abSray prepare(int i, FILE *fd, off_t filesize, int flags)
4493ad3fb45Sjoris {
4503ad3fb45Sjoris 	struct line *p;
4513ad3fb45Sjoris 	int j, h;
4523ad3fb45Sjoris 	size_t sz;
4533ad3fb45Sjoris 
4543ad3fb45Sjoris 	rewind(fd);
4553ad3fb45Sjoris 
456a304c0f4Sray 	sz = (filesize <= SIZE_MAX ? filesize : SIZE_MAX) / 25;
4573ad3fb45Sjoris 	if (sz < 100)
4583ad3fb45Sjoris 		sz = 100;
4593ad3fb45Sjoris 
4603ad3fb45Sjoris 	p = xcalloc(sz + 3, sizeof(*p));
461219c50abSray 	for (j = 0; (h = readhash(fd, flags));) {
462eea21b3cSray 		if (j == sz) {
4633ad3fb45Sjoris 			sz = sz * 3 / 2;
464caa2ffb0Sderaadt 			p = xreallocarray(p, sz + 3, sizeof(*p));
4653ad3fb45Sjoris 		}
4663ad3fb45Sjoris 		p[++j].value = h;
4673ad3fb45Sjoris 	}
468eea21b3cSray 	len[i] = j;
4693ad3fb45Sjoris 	file[i] = p;
4703ad3fb45Sjoris }
4713ad3fb45Sjoris 
4723ad3fb45Sjoris static void
4733ad3fb45Sjoris prune(void)
4743ad3fb45Sjoris {
4753ad3fb45Sjoris 	int i, j;
4763ad3fb45Sjoris 
477eea21b3cSray 	for (pref = 0; pref < len[0] && pref < len[1] &&
4783ad3fb45Sjoris 	    file[0][pref + 1].value == file[1][pref + 1].value;
4793ad3fb45Sjoris 	    pref++)
4803ad3fb45Sjoris 		;
481eea21b3cSray 	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
482eea21b3cSray 	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
4833ad3fb45Sjoris 	    suff++)
4843ad3fb45Sjoris 		;
4853ad3fb45Sjoris 	for (j = 0; j < 2; j++) {
4863ad3fb45Sjoris 		sfile[j] = file[j] + pref;
487eea21b3cSray 		slen[j] = len[j] - pref - suff;
4883ad3fb45Sjoris 		for (i = 0; i <= slen[j]; i++)
4893ad3fb45Sjoris 			sfile[j][i].serial = i;
4903ad3fb45Sjoris 	}
4913ad3fb45Sjoris }
4923ad3fb45Sjoris 
4933ad3fb45Sjoris static void
4943ad3fb45Sjoris equiv(struct line *a, int n, struct line *b, int m, int *c)
4953ad3fb45Sjoris {
4963ad3fb45Sjoris 	int i, j;
4973ad3fb45Sjoris 
4983ad3fb45Sjoris 	i = j = 1;
4993ad3fb45Sjoris 	while (i <= n && j <= m) {
5003ad3fb45Sjoris 		if (a[i].value < b[j].value)
5013ad3fb45Sjoris 			a[i++].value = 0;
5023ad3fb45Sjoris 		else if (a[i].value == b[j].value)
5033ad3fb45Sjoris 			a[i++].value = j;
5043ad3fb45Sjoris 		else
5053ad3fb45Sjoris 			j++;
5063ad3fb45Sjoris 	}
5073ad3fb45Sjoris 	while (i <= n)
5083ad3fb45Sjoris 		a[i++].value = 0;
5093ad3fb45Sjoris 	b[m + 1].value = 0;
5103ad3fb45Sjoris 	j = 0;
5113ad3fb45Sjoris 	while (++j <= m) {
5123ad3fb45Sjoris 		c[j] = -b[j].serial;
5133ad3fb45Sjoris 		while (b[j + 1].value == b[j].value) {
5143ad3fb45Sjoris 			j++;
5153ad3fb45Sjoris 			c[j] = b[j].serial;
5163ad3fb45Sjoris 		}
5173ad3fb45Sjoris 	}
5183ad3fb45Sjoris 	c[j] = -1;
5193ad3fb45Sjoris }
5203ad3fb45Sjoris 
5213ad3fb45Sjoris /* Code taken from ping.c */
5223ad3fb45Sjoris static int
5233ad3fb45Sjoris isqrt(int n)
5243ad3fb45Sjoris {
5253ad3fb45Sjoris 	int y, x = 1;
5263ad3fb45Sjoris 
5273ad3fb45Sjoris 	if (n == 0)
5283ad3fb45Sjoris 		return (0);
5293ad3fb45Sjoris 
5303ad3fb45Sjoris 	do { /* newton was a stinker */
5313ad3fb45Sjoris 		y = x;
5323ad3fb45Sjoris 		x = n / x;
5333ad3fb45Sjoris 		x += y;
5343ad3fb45Sjoris 		x /= 2;
535eea21b3cSray 	} while ((x - y) > 1 || (x - y) < -1);
5363ad3fb45Sjoris 
5373ad3fb45Sjoris 	return (x);
5383ad3fb45Sjoris }
5393ad3fb45Sjoris 
5403ad3fb45Sjoris static int
541219c50abSray stone(int *a, int n, int *b, int *c, int flags)
5423ad3fb45Sjoris {
5433ad3fb45Sjoris 	int i, k, y, j, l;
544df890a16Snicm 	int oldc, tc, oldl, sq;
545df890a16Snicm 	u_int numtries, bound;
5463ad3fb45Sjoris 
547df890a16Snicm 	if (flags & D_MINIMAL)
548df890a16Snicm 		bound = UINT_MAX;
549df890a16Snicm 	else {
550df890a16Snicm 		sq = isqrt(n);
551*b9fc9a72Sderaadt 		bound = MAXIMUM(256, sq);
552df890a16Snicm 	}
5533ad3fb45Sjoris 
5543ad3fb45Sjoris 	k = 0;
555eea21b3cSray 	c[0] = newcand(0, 0, 0);
5563ad3fb45Sjoris 	for (i = 1; i <= n; i++) {
5573ad3fb45Sjoris 		j = a[i];
5583ad3fb45Sjoris 		if (j == 0)
5593ad3fb45Sjoris 			continue;
5603ad3fb45Sjoris 		y = -b[j];
5613ad3fb45Sjoris 		oldl = 0;
5623ad3fb45Sjoris 		oldc = c[0];
5633ad3fb45Sjoris 		numtries = 0;
5643ad3fb45Sjoris 		do {
5653ad3fb45Sjoris 			if (y <= clist[oldc].y)
5663ad3fb45Sjoris 				continue;
5673ad3fb45Sjoris 			l = search(c, k, y);
5683ad3fb45Sjoris 			if (l != oldl + 1)
5693ad3fb45Sjoris 				oldc = c[l - 1];
5703ad3fb45Sjoris 			if (l <= k) {
5713ad3fb45Sjoris 				if (clist[c[l]].y <= y)
5723ad3fb45Sjoris 					continue;
5733ad3fb45Sjoris 				tc = c[l];
574eea21b3cSray 				c[l] = newcand(i, y, oldc);
5753ad3fb45Sjoris 				oldc = tc;
5763ad3fb45Sjoris 				oldl = l;
5773ad3fb45Sjoris 				numtries++;
5783ad3fb45Sjoris 			} else {
579eea21b3cSray 				c[l] = newcand(i, y, oldc);
5803ad3fb45Sjoris 				k++;
5813ad3fb45Sjoris 				break;
5823ad3fb45Sjoris 			}
5833ad3fb45Sjoris 		} while ((y = b[++j]) > 0 && numtries < bound);
5843ad3fb45Sjoris 	}
5853ad3fb45Sjoris 	return (k);
5863ad3fb45Sjoris }
5873ad3fb45Sjoris 
5883ad3fb45Sjoris static int
5893ad3fb45Sjoris newcand(int x, int y, int pred)
5903ad3fb45Sjoris {
59180566be2Sray 	struct cand *q;
5923ad3fb45Sjoris 
5933ad3fb45Sjoris 	if (clen == clistlen) {
594f74aa433Sray 		clistlen = clistlen * 11 / 10;
595caa2ffb0Sderaadt 		clist = xreallocarray(clist, clistlen, sizeof(*clist));
5963ad3fb45Sjoris 	}
5973ad3fb45Sjoris 	q = clist + clen;
5983ad3fb45Sjoris 	q->x = x;
5993ad3fb45Sjoris 	q->y = y;
6003ad3fb45Sjoris 	q->pred = pred;
6013ad3fb45Sjoris 	return (clen++);
6023ad3fb45Sjoris }
6033ad3fb45Sjoris 
6043ad3fb45Sjoris static int
6053ad3fb45Sjoris search(int *c, int k, int y)
6063ad3fb45Sjoris {
6073ad3fb45Sjoris 	int i, j, l, t;
6083ad3fb45Sjoris 
6093ad3fb45Sjoris 	if (clist[c[k]].y < y)	/* quick look for typical case */
6103ad3fb45Sjoris 		return (k + 1);
6113ad3fb45Sjoris 	i = 0;
6123ad3fb45Sjoris 	j = k + 1;
6133ad3fb45Sjoris 	for (;;) {
6143ad3fb45Sjoris 		l = (i + j) / 2;
6153ad3fb45Sjoris 		if (l <= i)
6163ad3fb45Sjoris 			break;
6173ad3fb45Sjoris 		t = clist[c[l]].y;
6183ad3fb45Sjoris 		if (t > y)
6193ad3fb45Sjoris 			j = l;
6203ad3fb45Sjoris 		else if (t < y)
6213ad3fb45Sjoris 			i = l;
6223ad3fb45Sjoris 		else
6233ad3fb45Sjoris 			return (l);
6243ad3fb45Sjoris 	}
6253ad3fb45Sjoris 	return (l + 1);
6263ad3fb45Sjoris }
6273ad3fb45Sjoris 
6283ad3fb45Sjoris static void
6293ad3fb45Sjoris unravel(int p)
6303ad3fb45Sjoris {
6313ad3fb45Sjoris 	struct cand *q;
6323ad3fb45Sjoris 	int i;
6333ad3fb45Sjoris 
634eea21b3cSray 	for (i = 0; i <= len[0]; i++)
6353ad3fb45Sjoris 		J[i] = i <= pref ? i :
636eea21b3cSray 		    i > len[0] - suff ? i + len[1] - len[0] : 0;
6373ad3fb45Sjoris 	for (q = clist + p; q->y != 0; q = clist + q->pred)
6383ad3fb45Sjoris 		J[q->x + pref] = q->y + pref;
6393ad3fb45Sjoris }
6403ad3fb45Sjoris 
6413ad3fb45Sjoris /*
6423ad3fb45Sjoris  * Check does double duty:
6433ad3fb45Sjoris  *  1.	ferret out any fortuitous correspondences due
6443ad3fb45Sjoris  *	to confounding by hashing (which result in "jackpot")
6453ad3fb45Sjoris  *  2.  collect random access indexes to the two files
6463ad3fb45Sjoris  */
6473ad3fb45Sjoris static void
648219c50abSray check(FILE *f1, FILE *f2, int flags)
6493ad3fb45Sjoris {
6503ad3fb45Sjoris 	int i, j, jackpot, c, d;
6513ad3fb45Sjoris 	long ctold, ctnew;
6523ad3fb45Sjoris 
6533ad3fb45Sjoris 	rewind(f1);
6543ad3fb45Sjoris 	rewind(f2);
6553ad3fb45Sjoris 	j = 1;
6563ad3fb45Sjoris 	ixold[0] = ixnew[0] = 0;
6573ad3fb45Sjoris 	jackpot = 0;
6583ad3fb45Sjoris 	ctold = ctnew = 0;
659eea21b3cSray 	for (i = 1; i <= len[0]; i++) {
6603ad3fb45Sjoris 		if (J[i] == 0) {
6613ad3fb45Sjoris 			ixold[i] = ctold += skipline(f1);
6623ad3fb45Sjoris 			continue;
6633ad3fb45Sjoris 		}
6643ad3fb45Sjoris 		while (j < J[i]) {
6653ad3fb45Sjoris 			ixnew[j] = ctnew += skipline(f2);
6663ad3fb45Sjoris 			j++;
6673ad3fb45Sjoris 		}
668219c50abSray 		if (flags & (D_FOLDBLANKS|D_IGNOREBLANKS|D_IGNORECASE)) {
6693ad3fb45Sjoris 			for (;;) {
6703ad3fb45Sjoris 				c = getc(f1);
6713ad3fb45Sjoris 				d = getc(f2);
6723ad3fb45Sjoris 				/*
6733ad3fb45Sjoris 				 * GNU diff ignores a missing newline
674eea21b3cSray 				 * in one file for -b or -w.
6753ad3fb45Sjoris 				 */
676219c50abSray 				if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) &&
6773ad3fb45Sjoris 				    ((c == EOF && d == '\n') ||
6783ad3fb45Sjoris 				    (c == '\n' && d == EOF))) {
6793ad3fb45Sjoris 					break;
6803ad3fb45Sjoris 				}
6813ad3fb45Sjoris 				ctold++;
6823ad3fb45Sjoris 				ctnew++;
683219c50abSray 				if ((flags & D_FOLDBLANKS) && isspace(c) &&
684219c50abSray 				    isspace(d)) {
6853ad3fb45Sjoris 					do {
6863ad3fb45Sjoris 						if (c == '\n')
6873ad3fb45Sjoris 							break;
6883ad3fb45Sjoris 						ctold++;
6893ad3fb45Sjoris 					} while (isspace(c = getc(f1)));
6903ad3fb45Sjoris 					do {
6913ad3fb45Sjoris 						if (d == '\n')
6923ad3fb45Sjoris 							break;
6933ad3fb45Sjoris 						ctnew++;
6943ad3fb45Sjoris 					} while (isspace(d = getc(f2)));
695219c50abSray 				} else if ((flags & D_IGNOREBLANKS)) {
6963ad3fb45Sjoris 					while (isspace(c) && c != '\n') {
6973ad3fb45Sjoris 						c = getc(f1);
6983ad3fb45Sjoris 						ctold++;
6993ad3fb45Sjoris 					}
7003ad3fb45Sjoris 					while (isspace(d) && d != '\n') {
7013ad3fb45Sjoris 						d = getc(f2);
7023ad3fb45Sjoris 						ctnew++;
7033ad3fb45Sjoris 					}
7043ad3fb45Sjoris 				}
7053ad3fb45Sjoris 				if (chrtran[c] != chrtran[d]) {
7063ad3fb45Sjoris 					jackpot++;
7073ad3fb45Sjoris 					J[i] = 0;
7083ad3fb45Sjoris 					if (c != '\n' && c != EOF)
7093ad3fb45Sjoris 						ctold += skipline(f1);
7103ad3fb45Sjoris 					if (d != '\n' && c != EOF)
7113ad3fb45Sjoris 						ctnew += skipline(f2);
7123ad3fb45Sjoris 					break;
7133ad3fb45Sjoris 				}
7143ad3fb45Sjoris 				if (c == '\n' || c == EOF)
7153ad3fb45Sjoris 					break;
7163ad3fb45Sjoris 			}
7173ad3fb45Sjoris 		} else {
7183ad3fb45Sjoris 			for (;;) {
7193ad3fb45Sjoris 				ctold++;
7203ad3fb45Sjoris 				ctnew++;
7213ad3fb45Sjoris 				if ((c = getc(f1)) != (d = getc(f2))) {
7223ad3fb45Sjoris 					/* jackpot++; */
7233ad3fb45Sjoris 					J[i] = 0;
7243ad3fb45Sjoris 					if (c != '\n' && c != EOF)
7253ad3fb45Sjoris 						ctold += skipline(f1);
7263ad3fb45Sjoris 					if (d != '\n' && c != EOF)
7273ad3fb45Sjoris 						ctnew += skipline(f2);
7283ad3fb45Sjoris 					break;
7293ad3fb45Sjoris 				}
7303ad3fb45Sjoris 				if (c == '\n' || c == EOF)
7313ad3fb45Sjoris 					break;
7323ad3fb45Sjoris 			}
7333ad3fb45Sjoris 		}
7343ad3fb45Sjoris 		ixold[i] = ctold;
7353ad3fb45Sjoris 		ixnew[j] = ctnew;
7363ad3fb45Sjoris 		j++;
7373ad3fb45Sjoris 	}
738eea21b3cSray 	for (; j <= len[1]; j++)
7393ad3fb45Sjoris 		ixnew[j] = ctnew += skipline(f2);
7403ad3fb45Sjoris 	/*
741eea21b3cSray 	 * if (jackpot)
742eea21b3cSray 	 *	fprintf(stderr, "jackpot\n");
7433ad3fb45Sjoris 	 */
7443ad3fb45Sjoris }
7453ad3fb45Sjoris 
7463ad3fb45Sjoris /* shellsort CACM #201 */
7473ad3fb45Sjoris static void
7483ad3fb45Sjoris sort(struct line *a, int n)
7493ad3fb45Sjoris {
7503ad3fb45Sjoris 	struct line *ai, *aim, w;
7513ad3fb45Sjoris 	int j, m = 0, k;
7523ad3fb45Sjoris 
7533ad3fb45Sjoris 	if (n == 0)
7543ad3fb45Sjoris 		return;
7553ad3fb45Sjoris 	for (j = 1; j <= n; j *= 2)
7563ad3fb45Sjoris 		m = 2 * j - 1;
7573ad3fb45Sjoris 	for (m /= 2; m != 0; m /= 2) {
7583ad3fb45Sjoris 		k = n - m;
7593ad3fb45Sjoris 		for (j = 1; j <= k; j++) {
7603ad3fb45Sjoris 			for (ai = &a[j]; ai > a; ai -= m) {
7613ad3fb45Sjoris 				aim = &ai[m];
7623ad3fb45Sjoris 				if (aim < ai)
7633ad3fb45Sjoris 					break;	/* wraparound */
7643ad3fb45Sjoris 				if (aim->value > ai[0].value ||
7653ad3fb45Sjoris 				    (aim->value == ai[0].value &&
7663ad3fb45Sjoris 					aim->serial > ai[0].serial))
7673ad3fb45Sjoris 					break;
7683ad3fb45Sjoris 				w.value = ai[0].value;
7693ad3fb45Sjoris 				ai[0].value = aim->value;
7703ad3fb45Sjoris 				aim->value = w.value;
7713ad3fb45Sjoris 				w.serial = ai[0].serial;
7723ad3fb45Sjoris 				ai[0].serial = aim->serial;
7733ad3fb45Sjoris 				aim->serial = w.serial;
7743ad3fb45Sjoris 			}
7753ad3fb45Sjoris 		}
7763ad3fb45Sjoris 	}
7773ad3fb45Sjoris }
7783ad3fb45Sjoris 
7793ad3fb45Sjoris static void
7803ad3fb45Sjoris unsort(struct line *f, int l, int *b)
7813ad3fb45Sjoris {
7823ad3fb45Sjoris 	int *a, i;
7833ad3fb45Sjoris 
7843ad3fb45Sjoris 	a = xcalloc(l + 1, sizeof(*a));
7853ad3fb45Sjoris 	for (i = 1; i <= l; i++)
7863ad3fb45Sjoris 		a[f[i].serial] = f[i].value;
7873ad3fb45Sjoris 	for (i = 1; i <= l; i++)
7883ad3fb45Sjoris 		b[i] = a[i];
7893ad3fb45Sjoris 	xfree(a);
7903ad3fb45Sjoris }
7913ad3fb45Sjoris 
7923ad3fb45Sjoris static int
7933ad3fb45Sjoris skipline(FILE *f)
7943ad3fb45Sjoris {
7953ad3fb45Sjoris 	int i, c;
7963ad3fb45Sjoris 
7973ad3fb45Sjoris 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
7983ad3fb45Sjoris 		continue;
7993ad3fb45Sjoris 	return (i);
8003ad3fb45Sjoris }
8013ad3fb45Sjoris 
8023ad3fb45Sjoris static void
803219c50abSray output(FILE *f1, FILE *f2, int flags)
8043ad3fb45Sjoris {
8053ad3fb45Sjoris 	int m, i0, i1, j0, j1;
8063ad3fb45Sjoris 
8073ad3fb45Sjoris 	rewind(f1);
8083ad3fb45Sjoris 	rewind(f2);
809eea21b3cSray 	m = len[0];
8103ad3fb45Sjoris 	J[0] = 0;
811eea21b3cSray 	J[m + 1] = len[1] + 1;
8123ad3fb45Sjoris 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
8133ad3fb45Sjoris 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
8143ad3fb45Sjoris 			i0++;
8153ad3fb45Sjoris 		j0 = J[i0 - 1] + 1;
8163ad3fb45Sjoris 		i1 = i0 - 1;
8173ad3fb45Sjoris 		while (i1 < m && J[i1 + 1] == 0)
8183ad3fb45Sjoris 			i1++;
8193ad3fb45Sjoris 		j1 = J[i1 + 1] - 1;
8203ad3fb45Sjoris 		J[i1] = j1;
821219c50abSray 		change(f1, f2, i0, i1, j0, j1, flags);
8223ad3fb45Sjoris 	}
8233ad3fb45Sjoris 	if (m == 0)
824219c50abSray 		change(f1, f2, 1, 0, 1, len[1], flags);
8253ad3fb45Sjoris 	if (diff_format == D_IFDEF) {
8263ad3fb45Sjoris 		for (;;) {
8273ad3fb45Sjoris #define	c i0
8283ad3fb45Sjoris 			if ((c = getc(f1)) == EOF)
8293ad3fb45Sjoris 				return;
8303ad3fb45Sjoris 			diff_output("%c", c);
8313ad3fb45Sjoris 		}
8323ad3fb45Sjoris #undef c
8333ad3fb45Sjoris 	}
8343ad3fb45Sjoris 	if (anychange != 0) {
8353ad3fb45Sjoris 		if (diff_format == D_CONTEXT)
836219c50abSray 			dump_context_vec(f1, f2, flags);
8373ad3fb45Sjoris 		else if (diff_format == D_UNIFIED)
838219c50abSray 			dump_unified_vec(f1, f2, flags);
8393ad3fb45Sjoris 	}
8403ad3fb45Sjoris }
8413ad3fb45Sjoris 
842a304c0f4Sray static void
8433ad3fb45Sjoris range(int a, int b, char *separator)
8443ad3fb45Sjoris {
8453ad3fb45Sjoris 	diff_output("%d", a > b ? b : a);
8463ad3fb45Sjoris 	if (a < b)
8473ad3fb45Sjoris 		diff_output("%s%d", separator, b);
8483ad3fb45Sjoris }
8493ad3fb45Sjoris 
850a304c0f4Sray static void
8513ad3fb45Sjoris uni_range(int a, int b)
8523ad3fb45Sjoris {
8533ad3fb45Sjoris 	if (a < b)
8543ad3fb45Sjoris 		diff_output("%d,%d", a, b - a + 1);
8553ad3fb45Sjoris 	else if (a == b)
8563ad3fb45Sjoris 		diff_output("%d", b);
8573ad3fb45Sjoris 	else
8583ad3fb45Sjoris 		diff_output("%d,0", b);
8593ad3fb45Sjoris }
8603ad3fb45Sjoris 
8613ad3fb45Sjoris static char *
8623ad3fb45Sjoris preadline(int fd, size_t rlen, off_t off)
8633ad3fb45Sjoris {
8643ad3fb45Sjoris 	char *line;
8653ad3fb45Sjoris 	ssize_t nr;
8663ad3fb45Sjoris 
8673ad3fb45Sjoris 	line = xmalloc(rlen + 1);
8687a6efebcSray 	if ((nr = pread(fd, line, rlen, off)) < 0)
8697a6efebcSray 		fatal("preadline: %s", strerror(errno));
8703ad3fb45Sjoris 	line[nr] = '\0';
8713ad3fb45Sjoris 	return (line);
8723ad3fb45Sjoris }
8733ad3fb45Sjoris 
8743ad3fb45Sjoris static int
8753ad3fb45Sjoris ignoreline(char *line)
8763ad3fb45Sjoris {
8773ad3fb45Sjoris 	int ret;
8783ad3fb45Sjoris 
879a304c0f4Sray 	ret = regexec(&ignore_re, line, 0, NULL, 0);
8803ad3fb45Sjoris 	xfree(line);
8813ad3fb45Sjoris 	return (ret == 0);	/* if it matched, it should be ignored. */
8823ad3fb45Sjoris }
8833ad3fb45Sjoris 
884fd660bf2Stobias static void
885fd660bf2Stobias diff_head(void)
886fd660bf2Stobias {
887fd660bf2Stobias 	char buf[64];
8886534056aStobias 	struct tm t;
889fd660bf2Stobias 	time_t curr_time;
890fd660bf2Stobias 
891fd660bf2Stobias 	if (diff_rev1 != NULL) {
8926534056aStobias 		gmtime_r(&stb1.st_mtime, &t);
893fd660bf2Stobias 	} else {
894fd660bf2Stobias 		time(&curr_time);
8956534056aStobias 		localtime_r(&curr_time, &t);
896fd660bf2Stobias 	}
897fd660bf2Stobias 
8986534056aStobias 	(void)strftime(buf, sizeof(buf), "%b %G %H:%M:%S -0000", &t);
899d00c0f9eStobias 	diff_output("%s %s	%d %s", diff_format == D_CONTEXT ?
9006534056aStobias 	    "***" : "---", diff_file1, t.tm_mday, buf);
901fd660bf2Stobias 
902fd660bf2Stobias 	if (diff_rev1 != NULL) {
903fd660bf2Stobias 		rcsnum_tostr(diff_rev1, buf, sizeof(buf));
904fd660bf2Stobias 		diff_output("\t%s", buf);
905fd660bf2Stobias 	}
906fd660bf2Stobias 
907fd660bf2Stobias 	diff_output("\n");
908fd660bf2Stobias 
9096534056aStobias 	gmtime_r(&stb2.st_mtime, &t);
910fd660bf2Stobias 
9116534056aStobias 	(void)strftime(buf, sizeof(buf), "%b %G %H:%M:%S -0000", &t);
912d00c0f9eStobias 	diff_output("%s %s	%d %s", diff_format == D_CONTEXT ?
9136534056aStobias 	    "---" : "+++", diff_file2, t.tm_mday, buf);
914fd660bf2Stobias 
915fd660bf2Stobias 	if (diff_rev2 != NULL) {
916fd660bf2Stobias 		rcsnum_tostr(diff_rev2, buf, sizeof(buf));
917fd660bf2Stobias 		diff_output("\t%s", buf);
918fd660bf2Stobias 	}
919fd660bf2Stobias 
920fd660bf2Stobias 	diff_output("\n");
921fd660bf2Stobias }
922fd660bf2Stobias 
923fd660bf2Stobias static void
924fd660bf2Stobias rdiff_head(void)
925fd660bf2Stobias {
926fd660bf2Stobias 	char buf[64];
9276534056aStobias 	struct tm t;
928fd660bf2Stobias 	time_t curr_time;
929fd660bf2Stobias 
930fd660bf2Stobias 	diff_output("%s ", diff_format == D_CONTEXT ? "***" : "---");
931fd660bf2Stobias 
932fd660bf2Stobias 	if (diff_rev1 == NULL) {
933fd660bf2Stobias 		diff_output("%s", CVS_PATH_DEVNULL);
9346534056aStobias 		gmtime_r(&stb1.st_atime, &t);
935fd660bf2Stobias 	} else {
936fd660bf2Stobias 		rcsnum_tostr(diff_rev1, buf, sizeof(buf));
937be756b91Stobias 		diff_output("%s:%s", diff_file1, buf);
93845ea4f19Stobias 		localtime_r(&stb1.st_mtime, &t);
939fd660bf2Stobias 	}
940fd660bf2Stobias 
9416534056aStobias 	(void)strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %G", &t);
942fd660bf2Stobias 	diff_output("\t%s\n", buf);
943fd660bf2Stobias 
944fd660bf2Stobias 	if (diff_rev2 != NULL) {
9456534056aStobias 		localtime_r(&stb2.st_mtime, &t);
946fd660bf2Stobias 	} else {
947fd660bf2Stobias 		time(&curr_time);
9486534056aStobias 		localtime_r(&curr_time, &t);
949fd660bf2Stobias 	}
950fd660bf2Stobias 
9516534056aStobias 	(void)strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %G", &t);
952fd660bf2Stobias 
953fd660bf2Stobias 	diff_output("%s %s	%s\n", diff_format == D_CONTEXT ? "---" : "+++",
954be756b91Stobias 	    diff_file2, buf);
955fd660bf2Stobias }
956fd660bf2Stobias 
9573ad3fb45Sjoris /*
9583ad3fb45Sjoris  * Indicate that there is a difference between lines a and b of the from file
9593ad3fb45Sjoris  * to get to lines c to d of the to file.  If a is greater then b then there
9603ad3fb45Sjoris  * are no lines in the from file involved and this means that there were
9613ad3fb45Sjoris  * lines appended (beginning at b).  If c is greater than d then there are
9623ad3fb45Sjoris  * lines missing from the to file.
9633ad3fb45Sjoris  */
9643ad3fb45Sjoris static void
965219c50abSray change(FILE *f1, FILE *f2, int a, int b, int c, int d, int flags)
9663ad3fb45Sjoris {
9673ad3fb45Sjoris 	static size_t max_context = 64;
968eea21b3cSray 	int i;
9693ad3fb45Sjoris 
9703ad3fb45Sjoris 	if (diff_format != D_IFDEF && a > b && c > d)
9713ad3fb45Sjoris 		return;
9723ad3fb45Sjoris 	if (ignore_pats != NULL) {
9733ad3fb45Sjoris 		char *line;
9743ad3fb45Sjoris 		/*
9753ad3fb45Sjoris 		 * All lines in the change, insert, or delete must
9763ad3fb45Sjoris 		 * match an ignore pattern for the change to be
9773ad3fb45Sjoris 		 * ignored.
9783ad3fb45Sjoris 		 */
9793ad3fb45Sjoris 		if (a <= b) {		/* Changes and deletes. */
9803ad3fb45Sjoris 			for (i = a; i <= b; i++) {
9813ad3fb45Sjoris 				line = preadline(fileno(f1),
9823ad3fb45Sjoris 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
9833ad3fb45Sjoris 				if (!ignoreline(line))
9843ad3fb45Sjoris 					goto proceed;
9853ad3fb45Sjoris 			}
9863ad3fb45Sjoris 		}
9873ad3fb45Sjoris 		if (a > b || c <= d) {	/* Changes and inserts. */
9883ad3fb45Sjoris 			for (i = c; i <= d; i++) {
9893ad3fb45Sjoris 				line = preadline(fileno(f2),
9903ad3fb45Sjoris 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
9913ad3fb45Sjoris 				if (!ignoreline(line))
9923ad3fb45Sjoris 					goto proceed;
9933ad3fb45Sjoris 			}
9943ad3fb45Sjoris 		}
9953ad3fb45Sjoris 		return;
9963ad3fb45Sjoris 	}
9973ad3fb45Sjoris proceed:
9983ad3fb45Sjoris 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
9993ad3fb45Sjoris 		/*
10003ad3fb45Sjoris 		 * Allocate change records as needed.
10013ad3fb45Sjoris 		 */
10023ad3fb45Sjoris 		if (context_vec_ptr == context_vec_end - 1) {
10033ad3fb45Sjoris 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
10043ad3fb45Sjoris 			max_context <<= 1;
1005caa2ffb0Sderaadt 			context_vec_start = xreallocarray(context_vec_start,
100680566be2Sray 			    max_context, sizeof(*context_vec_start));
10073ad3fb45Sjoris 			context_vec_end = context_vec_start + max_context;
10083ad3fb45Sjoris 			context_vec_ptr = context_vec_start + offset;
10093ad3fb45Sjoris 		}
10103ad3fb45Sjoris 		if (anychange == 0) {
10113ad3fb45Sjoris 			/*
10123ad3fb45Sjoris 			 * Print the context/unidiff header first time through.
10133ad3fb45Sjoris 			 */
1014fd660bf2Stobias 			if (cvs_cmdop == CVS_OP_RDIFF)
1015fd660bf2Stobias 				rdiff_head();
1016fd660bf2Stobias 			else
1017fd660bf2Stobias 				diff_head();
10183ad3fb45Sjoris 
10193ad3fb45Sjoris 			anychange = 1;
102072026f1aSnicm 		} else if (a > context_vec_ptr->b + (2 * diff_context) + 1 &&
102172026f1aSnicm 		    c > context_vec_ptr->d + (2 * diff_context) + 1) {
10223ad3fb45Sjoris 			/*
10233ad3fb45Sjoris 			 * If this change is more than 'context' lines from the
10243ad3fb45Sjoris 			 * previous change, dump the record and reset it.
10253ad3fb45Sjoris 			 */
10263ad3fb45Sjoris 			if (diff_format == D_CONTEXT)
1027219c50abSray 				dump_context_vec(f1, f2, flags);
10283ad3fb45Sjoris 			else
1029219c50abSray 				dump_unified_vec(f1, f2, flags);
10303ad3fb45Sjoris 		}
10313ad3fb45Sjoris 		context_vec_ptr++;
10323ad3fb45Sjoris 		context_vec_ptr->a = a;
10333ad3fb45Sjoris 		context_vec_ptr->b = b;
10343ad3fb45Sjoris 		context_vec_ptr->c = c;
10353ad3fb45Sjoris 		context_vec_ptr->d = d;
10363ad3fb45Sjoris 		return;
10373ad3fb45Sjoris 	}
10383ad3fb45Sjoris 	if (anychange == 0)
10393ad3fb45Sjoris 		anychange = 1;
10403ad3fb45Sjoris 	switch (diff_format) {
10413ad3fb45Sjoris 	case D_BRIEF:
10423ad3fb45Sjoris 		return;
10433ad3fb45Sjoris 	case D_NORMAL:
10443ad3fb45Sjoris 		range(a, b, ",");
10453ad3fb45Sjoris 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
10463ad3fb45Sjoris 		if (diff_format == D_NORMAL)
10473ad3fb45Sjoris 			range(c, d, ",");
10483ad3fb45Sjoris 		diff_output("\n");
10493ad3fb45Sjoris 		break;
10503ad3fb45Sjoris 	case D_RCSDIFF:
10513ad3fb45Sjoris 		if (a > b)
10523ad3fb45Sjoris 			diff_output("a%d %d\n", b, d - c + 1);
10533ad3fb45Sjoris 		else {
10543ad3fb45Sjoris 			diff_output("d%d %d\n", a, b - a + 1);
10553ad3fb45Sjoris 
10563ad3fb45Sjoris 			if (!(c > d))	/* add changed lines */
10573ad3fb45Sjoris 				diff_output("a%d %d\n", b, d - c + 1);
10583ad3fb45Sjoris 		}
10593ad3fb45Sjoris 		break;
10603ad3fb45Sjoris 	}
10613ad3fb45Sjoris 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
1062219c50abSray 		fetch(ixold, a, b, f1, '<', 1, flags);
10633ad3fb45Sjoris 		if (a <= b && c <= d && diff_format == D_NORMAL)
10643ad3fb45Sjoris 			diff_output("---\n");
10653ad3fb45Sjoris 	}
1066219c50abSray 	fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0, flags);
10673ad3fb45Sjoris 	if (inifdef) {
10683ad3fb45Sjoris 		diff_output("#endif /* %s */\n", ifdefname);
10693ad3fb45Sjoris 		inifdef = 0;
10703ad3fb45Sjoris 	}
10713ad3fb45Sjoris }
10723ad3fb45Sjoris 
10733ad3fb45Sjoris static void
1074219c50abSray fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags)
10753ad3fb45Sjoris {
10763ad3fb45Sjoris 	long j, nc;
10773ad3fb45Sjoris 	int i, c, col;
10783ad3fb45Sjoris 
10793ad3fb45Sjoris 	/*
10803ad3fb45Sjoris 	 * When doing #ifdef's, copy down to current line
10813ad3fb45Sjoris 	 * if this is the first file, so that stuff makes it to output.
10823ad3fb45Sjoris 	 */
10833ad3fb45Sjoris 	if (diff_format == D_IFDEF && oldfile) {
10843ad3fb45Sjoris 		long curpos = ftell(lb);
10853ad3fb45Sjoris 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
10863ad3fb45Sjoris 		nc = f[a > b ? b : a - 1] - curpos;
10873ad3fb45Sjoris 		for (i = 0; i < nc; i++)
10883ad3fb45Sjoris 			diff_output("%c", getc(lb));
10893ad3fb45Sjoris 	}
10903ad3fb45Sjoris 	if (a > b)
10913ad3fb45Sjoris 		return;
10923ad3fb45Sjoris 	if (diff_format == D_IFDEF) {
10933ad3fb45Sjoris 		if (inifdef) {
10943ad3fb45Sjoris 			diff_output("#else /* %s%s */\n",
10953ad3fb45Sjoris 			    oldfile == 1 ? "!" : "", ifdefname);
10963ad3fb45Sjoris 		} else {
10973ad3fb45Sjoris 			if (oldfile)
10983ad3fb45Sjoris 				diff_output("#ifndef %s\n", ifdefname);
10993ad3fb45Sjoris 			else
11003ad3fb45Sjoris 				diff_output("#ifdef %s\n", ifdefname);
11013ad3fb45Sjoris 		}
11023ad3fb45Sjoris 		inifdef = 1 + oldfile;
11033ad3fb45Sjoris 	}
11043ad3fb45Sjoris 	for (i = a; i <= b; i++) {
11053ad3fb45Sjoris 		fseek(lb, f[i - 1], SEEK_SET);
11063ad3fb45Sjoris 		nc = f[i] - f[i - 1];
11073ad3fb45Sjoris 		if (diff_format != D_IFDEF && ch != '\0') {
11083ad3fb45Sjoris 			diff_output("%c", ch);
11093ad3fb45Sjoris 			if (Tflag == 1 && (diff_format == D_NORMAL ||
11103ad3fb45Sjoris 			    diff_format == D_CONTEXT ||
11113ad3fb45Sjoris 			    diff_format == D_UNIFIED))
11123ad3fb45Sjoris 				diff_output("\t");
11133ad3fb45Sjoris 			else if (diff_format != D_UNIFIED)
11143ad3fb45Sjoris 				diff_output(" ");
11153ad3fb45Sjoris 		}
11163ad3fb45Sjoris 		col = 0;
11173ad3fb45Sjoris 		for (j = 0; j < nc; j++) {
11183ad3fb45Sjoris 			if ((c = getc(lb)) == EOF) {
11193ad3fb45Sjoris 				if (diff_format == D_RCSDIFF)
11203ad3fb45Sjoris 					cvs_log(LP_ERR,
11213ad3fb45Sjoris 					    "No newline at end of file");
11223ad3fb45Sjoris 				else
11233ad3fb45Sjoris 					diff_output("\n\\ No newline at end of "
112425d5ee6bSjoris 					    "file\n");
11253ad3fb45Sjoris 				return;
11263ad3fb45Sjoris 			}
1127219c50abSray 			if (c == '\t' && (flags & D_EXPANDTABS)) {
11283ad3fb45Sjoris 				do {
11293ad3fb45Sjoris 					diff_output(" ");
11303ad3fb45Sjoris 				} while (++col & 7);
11313ad3fb45Sjoris 			} else {
11323ad3fb45Sjoris 				diff_output("%c", c);
11333ad3fb45Sjoris 				col++;
11343ad3fb45Sjoris 			}
11353ad3fb45Sjoris 		}
11363ad3fb45Sjoris 	}
11373ad3fb45Sjoris }
11383ad3fb45Sjoris 
11393ad3fb45Sjoris /*
11403ad3fb45Sjoris  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
11413ad3fb45Sjoris  */
11423ad3fb45Sjoris static int
1143219c50abSray readhash(FILE *f, int flags)
11443ad3fb45Sjoris {
11453ad3fb45Sjoris 	int i, t, space;
11463ad3fb45Sjoris 	int sum;
11473ad3fb45Sjoris 
11483ad3fb45Sjoris 	sum = 1;
11493ad3fb45Sjoris 	space = 0;
1150219c50abSray 	if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) == 0) {
1151219c50abSray 		if (flags & D_IGNORECASE)
11523ad3fb45Sjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
11533ad3fb45Sjoris 				if (t == EOF) {
11543ad3fb45Sjoris 					if (i == 0)
11553ad3fb45Sjoris 						return (0);
11563ad3fb45Sjoris 					break;
11573ad3fb45Sjoris 				}
11583ad3fb45Sjoris 				sum = sum * 127 + chrtran[t];
11593ad3fb45Sjoris 			}
11603ad3fb45Sjoris 		else
11613ad3fb45Sjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
11623ad3fb45Sjoris 				if (t == EOF) {
11633ad3fb45Sjoris 					if (i == 0)
11643ad3fb45Sjoris 						return (0);
11653ad3fb45Sjoris 					break;
11663ad3fb45Sjoris 				}
11673ad3fb45Sjoris 				sum = sum * 127 + t;
11683ad3fb45Sjoris 			}
11693ad3fb45Sjoris 	} else {
11703ad3fb45Sjoris 		for (i = 0;;) {
11713ad3fb45Sjoris 			switch (t = getc(f)) {
11723ad3fb45Sjoris 			case '\t':
1173eea21b3cSray 			case '\r':
1174eea21b3cSray 			case '\v':
1175eea21b3cSray 			case '\f':
11763ad3fb45Sjoris 			case ' ':
11773ad3fb45Sjoris 				space++;
11783ad3fb45Sjoris 				continue;
11793ad3fb45Sjoris 			default:
1180219c50abSray 				if (space && (flags & D_IGNOREBLANKS) == 0) {
11813ad3fb45Sjoris 					i++;
11823ad3fb45Sjoris 					space = 0;
11833ad3fb45Sjoris 				}
11843ad3fb45Sjoris 				sum = sum * 127 + chrtran[t];
11853ad3fb45Sjoris 				i++;
11863ad3fb45Sjoris 				continue;
11873ad3fb45Sjoris 			case EOF:
11883ad3fb45Sjoris 				if (i == 0)
11893ad3fb45Sjoris 					return (0);
11903ad3fb45Sjoris 				/* FALLTHROUGH */
11913ad3fb45Sjoris 			case '\n':
11923ad3fb45Sjoris 				break;
11933ad3fb45Sjoris 			}
11943ad3fb45Sjoris 			break;
11953ad3fb45Sjoris 		}
11963ad3fb45Sjoris 	}
11973ad3fb45Sjoris 	/*
11983ad3fb45Sjoris 	 * There is a remote possibility that we end up with a zero sum.
11993ad3fb45Sjoris 	 * Zero is used as an EOF marker, so return 1 instead.
12003ad3fb45Sjoris 	 */
12013ad3fb45Sjoris 	return (sum == 0 ? 1 : sum);
12023ad3fb45Sjoris }
12033ad3fb45Sjoris 
12043ad3fb45Sjoris static int
12053ad3fb45Sjoris asciifile(FILE *f)
12063ad3fb45Sjoris {
1207eea21b3cSray 	unsigned char buf[BUFSIZ];
12083ad3fb45Sjoris 	size_t i, cnt;
12093ad3fb45Sjoris 
1210219c50abSray 	if (f == NULL)
12113ad3fb45Sjoris 		return (1);
12123ad3fb45Sjoris 
12133ad3fb45Sjoris 	rewind(f);
1214a304c0f4Sray 	cnt = fread(buf, 1, sizeof(buf), f);
12153ad3fb45Sjoris 	for (i = 0; i < cnt; i++)
12163ad3fb45Sjoris 		if (!isprint(buf[i]) && !isspace(buf[i]))
12173ad3fb45Sjoris 			return (0);
12183ad3fb45Sjoris 	return (1);
12193ad3fb45Sjoris }
12203ad3fb45Sjoris 
1221e22e6974Sxsa #define begins_with(s, pre) (strncmp(s, pre, sizeof(pre)-1) == 0)
1222e22e6974Sxsa 
12233ad3fb45Sjoris static char *
12243ad3fb45Sjoris match_function(const long *f, int pos, FILE *fp)
12253ad3fb45Sjoris {
12263ad3fb45Sjoris 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
12273ad3fb45Sjoris 	size_t nc;
12283ad3fb45Sjoris 	int last = lastline;
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';
124057003866Sray 			buf[strcspn(buf, "\n")] = '\0';
12413ad3fb45Sjoris 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
1242e22e6974Sxsa 				if (begins_with(buf, "private:")) {
1243e22e6974Sxsa 					if (!state)
1244e22e6974Sxsa 						state = " (private)";
1245e22e6974Sxsa 				} else if (begins_with(buf, "protected:")) {
1246e22e6974Sxsa 					if (!state)
1247e22e6974Sxsa 						state = " (protected)";
1248e22e6974Sxsa 				} else if (begins_with(buf, "public:")) {
1249e22e6974Sxsa 					if (!state)
1250e22e6974Sxsa 						state = " (public)";
1251e22e6974Sxsa 				} else {
125257003866Sray 					strlcpy(lastbuf, buf, sizeof lastbuf);
1253e22e6974Sxsa 					if (state)
1254e22e6974Sxsa 						strlcat(lastbuf, state,
1255e22e6974Sxsa 						    sizeof lastbuf);
12563ad3fb45Sjoris 					lastmatchline = pos;
12573ad3fb45Sjoris 					return lastbuf;
12583ad3fb45Sjoris 				}
12593ad3fb45Sjoris 			}
1260e22e6974Sxsa 		}
12613ad3fb45Sjoris 		pos--;
12623ad3fb45Sjoris 	}
1263eea21b3cSray 	return lastmatchline > 0 ? lastbuf : NULL;
12643ad3fb45Sjoris }
12653ad3fb45Sjoris 
12663ad3fb45Sjoris /* dump accumulated "context" diff changes */
12673ad3fb45Sjoris static void
1268219c50abSray dump_context_vec(FILE *f1, FILE *f2, int flags)
12693ad3fb45Sjoris {
12703ad3fb45Sjoris 	struct context_vec *cvp = context_vec_start;
12713ad3fb45Sjoris 	int lowa, upb, lowc, upd, do_output;
12723ad3fb45Sjoris 	int a, b, c, d;
12733ad3fb45Sjoris 	char ch, *f;
12743ad3fb45Sjoris 
12753ad3fb45Sjoris 	if (context_vec_start > context_vec_ptr)
12763ad3fb45Sjoris 		return;
12773ad3fb45Sjoris 
12783ad3fb45Sjoris 	b = d = 0;		/* gcc */
1279*b9fc9a72Sderaadt 	lowa = MAXIMUM(1, cvp->a - diff_context);
1280*b9fc9a72Sderaadt 	upb = MINIMUM(len[0], context_vec_ptr->b + diff_context);
1281*b9fc9a72Sderaadt 	lowc = MAXIMUM(1, cvp->c - diff_context);
1282*b9fc9a72Sderaadt 	upd = MINIMUM(len[1], context_vec_ptr->d + diff_context);
12833ad3fb45Sjoris 
12843ad3fb45Sjoris 	diff_output("***************");
1285219c50abSray 	if ((flags & D_PROTOTYPE)) {
12863ad3fb45Sjoris 		f = match_function(ixold, lowa-1, f1);
1287d5d01609Sray 		if (f != NULL)
12883ad3fb45Sjoris 			diff_output(" %s", f);
12893ad3fb45Sjoris 	}
12903ad3fb45Sjoris 	diff_output("\n*** ");
12913ad3fb45Sjoris 	range(lowa, upb, ",");
12923ad3fb45Sjoris 	diff_output(" ****\n");
12933ad3fb45Sjoris 
12943ad3fb45Sjoris 	/*
12953ad3fb45Sjoris 	 * Output changes to the "old" file.  The first loop suppresses
12963ad3fb45Sjoris 	 * output if there were no changes to the "old" file (we'll see
12973ad3fb45Sjoris 	 * the "old" lines as context in the "new" list).
12983ad3fb45Sjoris 	 */
12993ad3fb45Sjoris 	do_output = 0;
13003ad3fb45Sjoris 	for (; cvp <= context_vec_ptr; cvp++)
13013ad3fb45Sjoris 		if (cvp->a <= cvp->b) {
13023ad3fb45Sjoris 			cvp = context_vec_start;
13033ad3fb45Sjoris 			do_output++;
13043ad3fb45Sjoris 			break;
13053ad3fb45Sjoris 		}
1306eea21b3cSray 	if (do_output) {
13073ad3fb45Sjoris 		while (cvp <= context_vec_ptr) {
13083ad3fb45Sjoris 			a = cvp->a;
13093ad3fb45Sjoris 			b = cvp->b;
13103ad3fb45Sjoris 			c = cvp->c;
13113ad3fb45Sjoris 			d = cvp->d;
13123ad3fb45Sjoris 
13133ad3fb45Sjoris 			if (a <= b && c <= d)
13143ad3fb45Sjoris 				ch = 'c';
13153ad3fb45Sjoris 			else
13163ad3fb45Sjoris 				ch = (a <= b) ? 'd' : 'a';
13173ad3fb45Sjoris 
13183ad3fb45Sjoris 			if (ch == 'a')
1319219c50abSray 				fetch(ixold, lowa, b, f1, ' ', 0, flags);
13203ad3fb45Sjoris 			else {
1321219c50abSray 				fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
13223ad3fb45Sjoris 				fetch(ixold, a, b, f1,
1323219c50abSray 				    ch == 'c' ? '!' : '-', 0, flags);
13243ad3fb45Sjoris 			}
13253ad3fb45Sjoris 			lowa = b + 1;
13263ad3fb45Sjoris 			cvp++;
13273ad3fb45Sjoris 		}
1328219c50abSray 		fetch(ixold, b + 1, upb, f1, ' ', 0, flags);
13293ad3fb45Sjoris 	}
13303ad3fb45Sjoris 	/* output changes to the "new" file */
13313ad3fb45Sjoris 	diff_output("--- ");
13323ad3fb45Sjoris 	range(lowc, upd, ",");
13333ad3fb45Sjoris 	diff_output(" ----\n");
13343ad3fb45Sjoris 
13353ad3fb45Sjoris 	do_output = 0;
13363ad3fb45Sjoris 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
13373ad3fb45Sjoris 		if (cvp->c <= cvp->d) {
13383ad3fb45Sjoris 			cvp = context_vec_start;
13393ad3fb45Sjoris 			do_output++;
13403ad3fb45Sjoris 			break;
13413ad3fb45Sjoris 		}
1342eea21b3cSray 	if (do_output) {
13433ad3fb45Sjoris 		while (cvp <= context_vec_ptr) {
13443ad3fb45Sjoris 			a = cvp->a;
13453ad3fb45Sjoris 			b = cvp->b;
13463ad3fb45Sjoris 			c = cvp->c;
13473ad3fb45Sjoris 			d = cvp->d;
13483ad3fb45Sjoris 
13493ad3fb45Sjoris 			if (a <= b && c <= d)
13503ad3fb45Sjoris 				ch = 'c';
13513ad3fb45Sjoris 			else
13523ad3fb45Sjoris 				ch = (a <= b) ? 'd' : 'a';
13533ad3fb45Sjoris 
13543ad3fb45Sjoris 			if (ch == 'd')
1355219c50abSray 				fetch(ixnew, lowc, d, f2, ' ', 0, flags);
13563ad3fb45Sjoris 			else {
1357219c50abSray 				fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
13583ad3fb45Sjoris 				fetch(ixnew, c, d, f2,
1359219c50abSray 				    ch == 'c' ? '!' : '+', 0, flags);
13603ad3fb45Sjoris 			}
13613ad3fb45Sjoris 			lowc = d + 1;
13623ad3fb45Sjoris 			cvp++;
13633ad3fb45Sjoris 		}
1364219c50abSray 		fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
13653ad3fb45Sjoris 	}
13663ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
13673ad3fb45Sjoris }
13683ad3fb45Sjoris 
13693ad3fb45Sjoris /* dump accumulated "unified" diff changes */
13703ad3fb45Sjoris static void
1371219c50abSray dump_unified_vec(FILE *f1, FILE *f2, int flags)
13723ad3fb45Sjoris {
13733ad3fb45Sjoris 	struct context_vec *cvp = context_vec_start;
13743ad3fb45Sjoris 	int lowa, upb, lowc, upd;
13753ad3fb45Sjoris 	int a, b, c, d;
13763ad3fb45Sjoris 	char ch, *f;
13773ad3fb45Sjoris 
13783ad3fb45Sjoris 	if (context_vec_start > context_vec_ptr)
13793ad3fb45Sjoris 		return;
13803ad3fb45Sjoris 
13813ad3fb45Sjoris 	b = d = 0;		/* gcc */
1382*b9fc9a72Sderaadt 	lowa = MAXIMUM(1, cvp->a - diff_context);
1383*b9fc9a72Sderaadt 	upb = MINIMUM(len[0], context_vec_ptr->b + diff_context);
1384*b9fc9a72Sderaadt 	lowc = MAXIMUM(1, cvp->c - diff_context);
1385*b9fc9a72Sderaadt 	upd = MINIMUM(len[1], context_vec_ptr->d + diff_context);
13863ad3fb45Sjoris 
13873ad3fb45Sjoris 	diff_output("@@ -");
13883ad3fb45Sjoris 	uni_range(lowa, upb);
13893ad3fb45Sjoris 	diff_output(" +");
13903ad3fb45Sjoris 	uni_range(lowc, upd);
13913ad3fb45Sjoris 	diff_output(" @@");
1392219c50abSray 	if ((flags & D_PROTOTYPE)) {
13933ad3fb45Sjoris 		f = match_function(ixold, lowa-1, f1);
1394d5d01609Sray 		if (f != NULL)
13953ad3fb45Sjoris 			diff_output(" %s", f);
13963ad3fb45Sjoris 	}
13973ad3fb45Sjoris 	diff_output("\n");
13983ad3fb45Sjoris 
13993ad3fb45Sjoris 	/*
14003ad3fb45Sjoris 	 * Output changes in "unified" diff format--the old and new lines
14013ad3fb45Sjoris 	 * are printed together.
14023ad3fb45Sjoris 	 */
14033ad3fb45Sjoris 	for (; cvp <= context_vec_ptr; cvp++) {
14043ad3fb45Sjoris 		a = cvp->a;
14053ad3fb45Sjoris 		b = cvp->b;
14063ad3fb45Sjoris 		c = cvp->c;
14073ad3fb45Sjoris 		d = cvp->d;
14083ad3fb45Sjoris 
14093ad3fb45Sjoris 		/*
14103ad3fb45Sjoris 		 * c: both new and old changes
14113ad3fb45Sjoris 		 * d: only changes in the old file
14123ad3fb45Sjoris 		 * a: only changes in the new file
14133ad3fb45Sjoris 		 */
14143ad3fb45Sjoris 		if (a <= b && c <= d)
14153ad3fb45Sjoris 			ch = 'c';
14163ad3fb45Sjoris 		else
14173ad3fb45Sjoris 			ch = (a <= b) ? 'd' : 'a';
14183ad3fb45Sjoris 
14193ad3fb45Sjoris 		switch (ch) {
14203ad3fb45Sjoris 		case 'c':
1421219c50abSray 			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1422219c50abSray 			fetch(ixold, a, b, f1, '-', 0, flags);
1423219c50abSray 			fetch(ixnew, c, d, f2, '+', 0, flags);
14243ad3fb45Sjoris 			break;
14253ad3fb45Sjoris 		case 'd':
1426219c50abSray 			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1427219c50abSray 			fetch(ixold, a, b, f1, '-', 0, flags);
14283ad3fb45Sjoris 			break;
14293ad3fb45Sjoris 		case 'a':
1430219c50abSray 			fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
1431219c50abSray 			fetch(ixnew, c, d, f2, '+', 0, flags);
14323ad3fb45Sjoris 			break;
14333ad3fb45Sjoris 		}
14343ad3fb45Sjoris 		lowa = b + 1;
14353ad3fb45Sjoris 		lowc = d + 1;
14363ad3fb45Sjoris 	}
1437219c50abSray 	fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
14383ad3fb45Sjoris 
14393ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
14403ad3fb45Sjoris }
14413ad3fb45Sjoris 
14423ad3fb45Sjoris void
14433ad3fb45Sjoris diff_output(const char *fmt, ...)
14443ad3fb45Sjoris {
14453ad3fb45Sjoris 	va_list vap;
14463ad3fb45Sjoris 	int i;
14473ad3fb45Sjoris 	char *str;
14483ad3fb45Sjoris 
14493ad3fb45Sjoris 	va_start(vap, fmt);
14503ad3fb45Sjoris 	i = vasprintf(&str, fmt, vap);
14513ad3fb45Sjoris 	va_end(vap);
14523ad3fb45Sjoris 	if (i == -1)
14539a0ecc80Stobias 		fatal("diff_output: could not allocate memory");
14543ad3fb45Sjoris 	if (diffbuf != NULL)
14557bb3ddb0Sray 		buf_puts(diffbuf, str);
14563ad3fb45Sjoris 	else
14573ad3fb45Sjoris 		cvs_printf("%s", str);
14583ad3fb45Sjoris 	xfree(str);
14593ad3fb45Sjoris }
1460