xref: /openbsd-src/usr.bin/cvs/diff_internals.c (revision caa2ffb0b03850db17af0f8c10b96ed868191c3f)
1*caa2ffb0Sderaadt /*	$OpenBSD: diff_internals.c,v 1.35 2014/12/01 21:58:46 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 
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>
766534056aStobias #include <time.h>
77eea21b3cSray #include <unistd.h>
78eea21b3cSray 
79eea21b3cSray #include "cvs.h"
80eea21b3cSray #include "diff.h"
81eea21b3cSray 
82eea21b3cSray /*
83eea21b3cSray  * diff - compare two files.
84eea21b3cSray  */
85eea21b3cSray 
863ad3fb45Sjoris /*
873ad3fb45Sjoris  *	Uses an algorithm due to Harold Stone, which finds
883ad3fb45Sjoris  *	a pair of longest identical subsequences in the two
893ad3fb45Sjoris  *	files.
903ad3fb45Sjoris  *
913ad3fb45Sjoris  *	The major goal is to generate the match vector J.
923ad3fb45Sjoris  *	J[i] is the index of the line in file1 corresponding
933ad3fb45Sjoris  *	to line i file0. J[i] = 0 if there is no
943ad3fb45Sjoris  *	such line in file1.
953ad3fb45Sjoris  *
963ad3fb45Sjoris  *	Lines are hashed so as to work in core. All potential
973ad3fb45Sjoris  *	matches are located by sorting the lines of each file
983ad3fb45Sjoris  *	on the hash (called ``value''). In particular, this
993ad3fb45Sjoris  *	collects the equivalence classes in file1 together.
1003ad3fb45Sjoris  *	Subroutine equiv replaces the value of each line in
1013ad3fb45Sjoris  *	file0 by the index of the first element of its
1023ad3fb45Sjoris  *	matching equivalence in (the reordered) file1.
1033ad3fb45Sjoris  *	To save space equiv squeezes file1 into a single
1043ad3fb45Sjoris  *	array member in which the equivalence classes
1053ad3fb45Sjoris  *	are simply concatenated, except that their first
1063ad3fb45Sjoris  *	members are flagged by changing sign.
1073ad3fb45Sjoris  *
1083ad3fb45Sjoris  *	Next the indices that point into member are unsorted into
1093ad3fb45Sjoris  *	array class according to the original order of file0.
1103ad3fb45Sjoris  *
1113ad3fb45Sjoris  *	The cleverness lies in routine stone. This marches
1123ad3fb45Sjoris  *	through the lines of file0, developing a vector klist
1133ad3fb45Sjoris  *	of "k-candidates". At step i a k-candidate is a matched
1143ad3fb45Sjoris  *	pair of lines x,y (x in file0 y in file1) such that
1153ad3fb45Sjoris  *	there is a common subsequence of length k
1163ad3fb45Sjoris  *	between the first i lines of file0 and the first y
1173ad3fb45Sjoris  *	lines of file1, but there is no such subsequence for
1183ad3fb45Sjoris  *	any smaller y. x is the earliest possible mate to y
1193ad3fb45Sjoris  *	that occurs in such a subsequence.
1203ad3fb45Sjoris  *
1213ad3fb45Sjoris  *	Whenever any of the members of the equivalence class of
1223ad3fb45Sjoris  *	lines in file1 matable to a line in file0 has serial number
1233ad3fb45Sjoris  *	less than the y of some k-candidate, that k-candidate
1243ad3fb45Sjoris  *	with the smallest such y is replaced. The new
1253ad3fb45Sjoris  *	k-candidate is chained (via pred) to the current
1263ad3fb45Sjoris  *	k-1 candidate so that the actual subsequence can
1273ad3fb45Sjoris  *	be recovered. When a member has serial number greater
1283ad3fb45Sjoris  *	that the y of all k-candidates, the klist is extended.
1293ad3fb45Sjoris  *	At the end, the longest subsequence is pulled out
1303ad3fb45Sjoris  *	and placed in the array J by unravel
1313ad3fb45Sjoris  *
1323ad3fb45Sjoris  *	With J in hand, the matches there recorded are
1333ad3fb45Sjoris  *	check'ed against reality to assure that no spurious
1343ad3fb45Sjoris  *	matches have crept in due to hashing. If they have,
1353ad3fb45Sjoris  *	they are broken, and "jackpot" is recorded--a harmless
1363ad3fb45Sjoris  *	matter except that a true match for a spuriously
1373ad3fb45Sjoris  *	mated line may now be unnecessarily reported as a change.
1383ad3fb45Sjoris  *
1393ad3fb45Sjoris  *	Much of the complexity of the program comes simply
1403ad3fb45Sjoris  *	from trying to minimize core utilization and
1413ad3fb45Sjoris  *	maximize the range of doable problems by dynamically
1423ad3fb45Sjoris  *	allocating what is needed and reusing what is not.
1433ad3fb45Sjoris  *	The core requirements for problems larger than somewhat
1443ad3fb45Sjoris  *	are (in words) 2*length(file0) + length(file1) +
1453ad3fb45Sjoris  *	3*(number of k-candidates installed),  typically about
1463ad3fb45Sjoris  *	6n words for files of length n.
1473ad3fb45Sjoris  */
1483ad3fb45Sjoris 
1493ad3fb45Sjoris struct cand {
1503ad3fb45Sjoris 	int	x;
1513ad3fb45Sjoris 	int	y;
1523ad3fb45Sjoris 	int	pred;
153ccf8fb4cSray };
1543ad3fb45Sjoris 
1553ad3fb45Sjoris struct line {
1563ad3fb45Sjoris 	int	serial;
1573ad3fb45Sjoris 	int	value;
1583ad3fb45Sjoris } *file[2];
1593ad3fb45Sjoris 
1603ad3fb45Sjoris /*
1613ad3fb45Sjoris  * The following struct is used to record change information when
1623ad3fb45Sjoris  * doing a "context" or "unified" diff.  (see routine "change" to
1633ad3fb45Sjoris  * understand the highly mnemonic field names)
1643ad3fb45Sjoris  */
1653ad3fb45Sjoris struct context_vec {
1663ad3fb45Sjoris 	int	a;		/* start line in old file */
1673ad3fb45Sjoris 	int	b;		/* end line in old file */
1683ad3fb45Sjoris 	int	c;		/* start line in new file */
1693ad3fb45Sjoris 	int	d;		/* end line in new file */
1703ad3fb45Sjoris };
1713ad3fb45Sjoris 
172219c50abSray static void	 output(FILE *, FILE *, int);
173219c50abSray static void	 check(FILE *, FILE *, int);
1743ad3fb45Sjoris static void	 range(int, int, char *);
1753ad3fb45Sjoris static void	 uni_range(int, int);
176219c50abSray static void	 dump_context_vec(FILE *, FILE *, int);
177219c50abSray static void	 dump_unified_vec(FILE *, FILE *, int);
178219c50abSray static void	 prepare(int, FILE *, off_t, int);
1793ad3fb45Sjoris static void	 prune(void);
1803ad3fb45Sjoris static void	 equiv(struct line *, int, struct line *, int, int *);
1813ad3fb45Sjoris static void	 unravel(int);
1823ad3fb45Sjoris static void	 unsort(struct line *, int, int *);
183fd660bf2Stobias static void	 diff_head(void);
184fd660bf2Stobias static void	 rdiff_head(void);
185219c50abSray static void	 change(FILE *, FILE *, int, int, int, int, int);
1863ad3fb45Sjoris static void	 sort(struct line *, int);
1873ad3fb45Sjoris static int	 ignoreline(char *);
1883ad3fb45Sjoris static int	 asciifile(FILE *);
189219c50abSray static void	 fetch(long *, int, int, FILE *, int, int, int);
1903ad3fb45Sjoris static int	 newcand(int, int, int);
1913ad3fb45Sjoris static int	 search(int *, int, int);
1923ad3fb45Sjoris static int	 skipline(FILE *);
1933ad3fb45Sjoris static int	 isqrt(int);
194219c50abSray static int	 stone(int *, int, int *, int *, int);
195219c50abSray static int	 readhash(FILE *, int);
1963ad3fb45Sjoris static int	 files_differ(FILE *, FILE *);
1973ad3fb45Sjoris static char	*match_function(const long *, int, FILE *);
1983ad3fb45Sjoris static char	*preadline(int, size_t, off_t);
1993ad3fb45Sjoris 
200219c50abSray static int Tflag;
20172026f1aSnicm int diff_context = 3;
2023ad3fb45Sjoris int diff_format = D_NORMAL;
203be756b91Stobias const char *diff_file1 = NULL;
204be756b91Stobias const char *diff_file2 = NULL;
2053ad3fb45Sjoris RCSNUM *diff_rev1 = NULL;
2063ad3fb45Sjoris RCSNUM *diff_rev2 = NULL;
2077bb3ddb0Sray char diffargs[512];
2083ad3fb45Sjoris static struct stat stb1, stb2;
2093ad3fb45Sjoris static char *ifdefname, *ignore_pats;
2103ad3fb45Sjoris regex_t ignore_re;
2113ad3fb45Sjoris 
2123ad3fb45Sjoris static int  *J;			/* will be overlaid on class */
2133ad3fb45Sjoris static int  *class;		/* will be overlaid on file[0] */
2143ad3fb45Sjoris static int  *klist;		/* will be overlaid on file[0] after class */
2153ad3fb45Sjoris static int  *member;		/* will be overlaid on file[1] */
2163ad3fb45Sjoris static int   clen;
2173ad3fb45Sjoris static int   inifdef;		/* whether or not we are in a #ifdef block */
218eea21b3cSray static int   len[2];
2193ad3fb45Sjoris static int   pref, suff;	/* length of prefix and suffix */
2203ad3fb45Sjoris static int   slen[2];
2213ad3fb45Sjoris static int   anychange;
2223ad3fb45Sjoris static long *ixnew;		/* will be overlaid on file[1] */
2233ad3fb45Sjoris static long *ixold;		/* will be overlaid on klist */
2243ad3fb45Sjoris static struct cand *clist;	/* merely a free storage pot for candidates */
2253ad3fb45Sjoris static int   clistlen;		/* the length of clist */
2263ad3fb45Sjoris static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
2273ad3fb45Sjoris static u_char *chrtran;		/* translation table for case-folding */
2283ad3fb45Sjoris static struct context_vec *context_vec_start;
2293ad3fb45Sjoris static struct context_vec *context_vec_end;
2303ad3fb45Sjoris static struct context_vec *context_vec_ptr;
2313ad3fb45Sjoris 
232e22e6974Sxsa #define FUNCTION_CONTEXT_SIZE	55
2333ad3fb45Sjoris static char lastbuf[FUNCTION_CONTEXT_SIZE];
2343ad3fb45Sjoris static int lastline;
2353ad3fb45Sjoris static int lastmatchline;
2363ad3fb45Sjoris BUF  *diffbuf = NULL;
2373ad3fb45Sjoris 
238eea21b3cSray 
2393ad3fb45Sjoris /*
2403ad3fb45Sjoris  * chrtran points to one of 2 translation tables: cup2low if folding upper to
2413ad3fb45Sjoris  * lower case clow2low if not folding case
2423ad3fb45Sjoris  */
2433ad3fb45Sjoris u_char clow2low[256] = {
2443ad3fb45Sjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2453ad3fb45Sjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2463ad3fb45Sjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2473ad3fb45Sjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2483ad3fb45Sjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2493ad3fb45Sjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
2503ad3fb45Sjoris 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
2513ad3fb45Sjoris 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
2523ad3fb45Sjoris 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
2533ad3fb45Sjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2543ad3fb45Sjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2553ad3fb45Sjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2563ad3fb45Sjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2573ad3fb45Sjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2583ad3fb45Sjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2593ad3fb45Sjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2603ad3fb45Sjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2613ad3fb45Sjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2623ad3fb45Sjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2633ad3fb45Sjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2643ad3fb45Sjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2653ad3fb45Sjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2663ad3fb45Sjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2673ad3fb45Sjoris 	0xfd, 0xfe, 0xff
2683ad3fb45Sjoris };
2693ad3fb45Sjoris 
2703ad3fb45Sjoris u_char cup2low[256] = {
2713ad3fb45Sjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2723ad3fb45Sjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2733ad3fb45Sjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2743ad3fb45Sjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2753ad3fb45Sjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2763ad3fb45Sjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
2773ad3fb45Sjoris 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
2783ad3fb45Sjoris 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
2793ad3fb45Sjoris 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
2803ad3fb45Sjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2813ad3fb45Sjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2823ad3fb45Sjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2833ad3fb45Sjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2843ad3fb45Sjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2853ad3fb45Sjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2863ad3fb45Sjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2873ad3fb45Sjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2883ad3fb45Sjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2893ad3fb45Sjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2903ad3fb45Sjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2913ad3fb45Sjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2923ad3fb45Sjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2933ad3fb45Sjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2943ad3fb45Sjoris 	0xfd, 0xfe, 0xff
2953ad3fb45Sjoris };
2963ad3fb45Sjoris 
2973ad3fb45Sjoris int
29857003866Sray diffreg(const char *file1, const char *file2, int _fd1, int _fd2,
299219c50abSray     BUF *out, int flags)
3003ad3fb45Sjoris {
3013ad3fb45Sjoris 	FILE *f1, *f2;
3022e0d696aSjoris 	int i, rval, fd1, fd2;
3033ad3fb45Sjoris 
304be756b91Stobias 	diff_file1 = file1;
305be756b91Stobias 	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;
312219c50abSray 	if (flags & D_IGNORECASE)
313219c50abSray 		chrtran = cup2low;
314219c50abSray 	else
315219c50abSray 		chrtran = clow2low;
3163ad3fb45Sjoris 	if (out != NULL)
3173ad3fb45Sjoris 		diffbuf = out;
3183ad3fb45Sjoris 
3192e0d696aSjoris 	fd1 = dup(_fd1);
3202e0d696aSjoris 	if (fd1 == -1)
32157003866Sray 		fatal("diffreg: dup: %s", strerror(errno));
3222e0d696aSjoris 
3232e0d696aSjoris 	fd2 = dup(_fd2);
3242e0d696aSjoris 	if (fd2 == -1)
32557003866Sray 		fatal("diffreg: dup: %s", strerror(errno));
3262e0d696aSjoris 
327651312a5Sjoris 	if (lseek(fd1, 0, SEEK_SET) < 0)
32857003866Sray 		fatal("diffreg: lseek: %s", strerror(errno));
3292e0d696aSjoris 
3302e0d696aSjoris 	f1 = fdopen(fd1, "r");
3313ad3fb45Sjoris 	if (f1 == NULL) {
3323ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file1);
3333ad3fb45Sjoris 		goto closem;
3343ad3fb45Sjoris 	}
3353ad3fb45Sjoris 
336651312a5Sjoris 	if (lseek(fd2, 0, SEEK_SET) < 0)
33757003866Sray 		fatal("diffreg: lseek: %s", strerror(errno));
3382e0d696aSjoris 
3392e0d696aSjoris 	f2 = fdopen(fd2, "r");
3403ad3fb45Sjoris 	if (f2 == NULL) {
3413ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file2);
3423ad3fb45Sjoris 		goto closem;
3433ad3fb45Sjoris 	}
3443ad3fb45Sjoris 
3452e0d696aSjoris 	if (fstat(fd1, &stb1) < 0) {
3463ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file1);
3473ad3fb45Sjoris 		goto closem;
3483ad3fb45Sjoris 	}
3492e0d696aSjoris 
3502e0d696aSjoris 	if (fstat(fd2, &stb2) < 0) {
3513ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file2);
3523ad3fb45Sjoris 		goto closem;
3533ad3fb45Sjoris 	}
354eea21b3cSray 
3553ad3fb45Sjoris 	switch (files_differ(f1, f2)) {
3563ad3fb45Sjoris 	case 0:
3573ad3fb45Sjoris 		goto closem;
3583ad3fb45Sjoris 	case 1:
3593ad3fb45Sjoris 		break;
3603ad3fb45Sjoris 	default:
3613ad3fb45Sjoris 		/* error */
3623ad3fb45Sjoris 		goto closem;
3633ad3fb45Sjoris 	}
3643ad3fb45Sjoris 
365219c50abSray 	if ((flags & D_FORCEASCII) == 0 &&
366219c50abSray 	    (!asciifile(f1) || !asciifile(f2))) {
3673ad3fb45Sjoris 		rval = D_BINARY;
3683ad3fb45Sjoris 		goto closem;
3693ad3fb45Sjoris 	}
370eea21b3cSray 
371219c50abSray 	prepare(0, f1, stb1.st_size, flags);
372219c50abSray 	prepare(1, f2, stb2.st_size, flags);
373eea21b3cSray 
3743ad3fb45Sjoris 	prune();
3753ad3fb45Sjoris 	sort(sfile[0], slen[0]);
3763ad3fb45Sjoris 	sort(sfile[1], slen[1]);
3773ad3fb45Sjoris 
3783ad3fb45Sjoris 	member = (int *)file[1];
3793ad3fb45Sjoris 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
380*caa2ffb0Sderaadt 	member = xreallocarray(member, slen[1] + 2, sizeof(*member));
3813ad3fb45Sjoris 
3823ad3fb45Sjoris 	class = (int *)file[0];
3833ad3fb45Sjoris 	unsort(sfile[0], slen[0], class);
384*caa2ffb0Sderaadt 	class = xreallocarray(class, slen[0] + 2, sizeof(*class));
3853ad3fb45Sjoris 
3863ad3fb45Sjoris 	klist = xcalloc(slen[0] + 2, sizeof(*klist));
3873ad3fb45Sjoris 	clen = 0;
3883ad3fb45Sjoris 	clistlen = 100;
3893ad3fb45Sjoris 	clist = xcalloc(clistlen, sizeof(*clist));
390219c50abSray 	i = stone(class, slen[0], member, klist, flags);
3913ad3fb45Sjoris 	xfree(member);
3923ad3fb45Sjoris 	xfree(class);
3933ad3fb45Sjoris 
394*caa2ffb0Sderaadt 	J = xreallocarray(J, len[0] + 2, sizeof(*J));
3953ad3fb45Sjoris 	unravel(klist[i]);
3963ad3fb45Sjoris 	xfree(clist);
3973ad3fb45Sjoris 	xfree(klist);
3983ad3fb45Sjoris 
399*caa2ffb0Sderaadt 	ixold = xreallocarray(ixold, len[0] + 2, sizeof(*ixold));
400*caa2ffb0Sderaadt 	ixnew = xreallocarray(ixnew, len[1] + 2, sizeof(*ixnew));
401219c50abSray 	check(f1, f2, flags);
402219c50abSray 	output(f1, f2, flags);
4033ad3fb45Sjoris 
4043ad3fb45Sjoris closem:
405eea21b3cSray 	if (anychange) {
4063ad3fb45Sjoris 		if (rval == D_SAME)
4073ad3fb45Sjoris 			rval = D_DIFFER;
4083ad3fb45Sjoris 	}
4093ad3fb45Sjoris 	if (f1 != NULL)
4103ad3fb45Sjoris 		fclose(f1);
4113ad3fb45Sjoris 	if (f2 != NULL)
4123ad3fb45Sjoris 		fclose(f2);
4133ad3fb45Sjoris 
4143ad3fb45Sjoris 	return (rval);
4153ad3fb45Sjoris }
4163ad3fb45Sjoris 
4173ad3fb45Sjoris /*
4183ad3fb45Sjoris  * Check to see if the given files differ.
4193ad3fb45Sjoris  * Returns 0 if they are the same, 1 if different, and -1 on error.
4203ad3fb45Sjoris  * XXX - could use code from cmp(1) [faster]
4213ad3fb45Sjoris  */
4223ad3fb45Sjoris static int
4233ad3fb45Sjoris files_differ(FILE *f1, FILE *f2)
4243ad3fb45Sjoris {
4253ad3fb45Sjoris 	char buf1[BUFSIZ], buf2[BUFSIZ];
4263ad3fb45Sjoris 	size_t i, j;
4273ad3fb45Sjoris 
4283ad3fb45Sjoris 	if (stb1.st_size != stb2.st_size)
4293ad3fb45Sjoris 		return (1);
4303ad3fb45Sjoris 	for (;;) {
431a304c0f4Sray 		i = fread(buf1, 1, sizeof(buf1), f1);
432a304c0f4Sray 		j = fread(buf2, 1, sizeof(buf2), f2);
43309523d6fSray 		if ((!i && ferror(f1)) || (!j && ferror(f2)))
43409523d6fSray 			return (-1);
4353ad3fb45Sjoris 		if (i != j)
4363ad3fb45Sjoris 			return (1);
43709523d6fSray 		if (i == 0)
4383ad3fb45Sjoris 			return (0);
4393ad3fb45Sjoris 		if (memcmp(buf1, buf2, i) != 0)
4403ad3fb45Sjoris 			return (1);
4413ad3fb45Sjoris 	}
4423ad3fb45Sjoris }
4433ad3fb45Sjoris 
444eea21b3cSray static void
445219c50abSray prepare(int i, FILE *fd, off_t filesize, int flags)
4463ad3fb45Sjoris {
4473ad3fb45Sjoris 	struct line *p;
4483ad3fb45Sjoris 	int j, h;
4493ad3fb45Sjoris 	size_t sz;
4503ad3fb45Sjoris 
4513ad3fb45Sjoris 	rewind(fd);
4523ad3fb45Sjoris 
453a304c0f4Sray 	sz = (filesize <= SIZE_MAX ? filesize : SIZE_MAX) / 25;
4543ad3fb45Sjoris 	if (sz < 100)
4553ad3fb45Sjoris 		sz = 100;
4563ad3fb45Sjoris 
4573ad3fb45Sjoris 	p = xcalloc(sz + 3, sizeof(*p));
458219c50abSray 	for (j = 0; (h = readhash(fd, flags));) {
459eea21b3cSray 		if (j == sz) {
4603ad3fb45Sjoris 			sz = sz * 3 / 2;
461*caa2ffb0Sderaadt 			p = xreallocarray(p, sz + 3, sizeof(*p));
4623ad3fb45Sjoris 		}
4633ad3fb45Sjoris 		p[++j].value = h;
4643ad3fb45Sjoris 	}
465eea21b3cSray 	len[i] = j;
4663ad3fb45Sjoris 	file[i] = p;
4673ad3fb45Sjoris }
4683ad3fb45Sjoris 
4693ad3fb45Sjoris static void
4703ad3fb45Sjoris prune(void)
4713ad3fb45Sjoris {
4723ad3fb45Sjoris 	int i, j;
4733ad3fb45Sjoris 
474eea21b3cSray 	for (pref = 0; pref < len[0] && pref < len[1] &&
4753ad3fb45Sjoris 	    file[0][pref + 1].value == file[1][pref + 1].value;
4763ad3fb45Sjoris 	    pref++)
4773ad3fb45Sjoris 		;
478eea21b3cSray 	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
479eea21b3cSray 	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
4803ad3fb45Sjoris 	    suff++)
4813ad3fb45Sjoris 		;
4823ad3fb45Sjoris 	for (j = 0; j < 2; j++) {
4833ad3fb45Sjoris 		sfile[j] = file[j] + pref;
484eea21b3cSray 		slen[j] = len[j] - pref - suff;
4853ad3fb45Sjoris 		for (i = 0; i <= slen[j]; i++)
4863ad3fb45Sjoris 			sfile[j][i].serial = i;
4873ad3fb45Sjoris 	}
4883ad3fb45Sjoris }
4893ad3fb45Sjoris 
4903ad3fb45Sjoris static void
4913ad3fb45Sjoris equiv(struct line *a, int n, struct line *b, int m, int *c)
4923ad3fb45Sjoris {
4933ad3fb45Sjoris 	int i, j;
4943ad3fb45Sjoris 
4953ad3fb45Sjoris 	i = j = 1;
4963ad3fb45Sjoris 	while (i <= n && j <= m) {
4973ad3fb45Sjoris 		if (a[i].value < b[j].value)
4983ad3fb45Sjoris 			a[i++].value = 0;
4993ad3fb45Sjoris 		else if (a[i].value == b[j].value)
5003ad3fb45Sjoris 			a[i++].value = j;
5013ad3fb45Sjoris 		else
5023ad3fb45Sjoris 			j++;
5033ad3fb45Sjoris 	}
5043ad3fb45Sjoris 	while (i <= n)
5053ad3fb45Sjoris 		a[i++].value = 0;
5063ad3fb45Sjoris 	b[m + 1].value = 0;
5073ad3fb45Sjoris 	j = 0;
5083ad3fb45Sjoris 	while (++j <= m) {
5093ad3fb45Sjoris 		c[j] = -b[j].serial;
5103ad3fb45Sjoris 		while (b[j + 1].value == b[j].value) {
5113ad3fb45Sjoris 			j++;
5123ad3fb45Sjoris 			c[j] = b[j].serial;
5133ad3fb45Sjoris 		}
5143ad3fb45Sjoris 	}
5153ad3fb45Sjoris 	c[j] = -1;
5163ad3fb45Sjoris }
5173ad3fb45Sjoris 
5183ad3fb45Sjoris /* Code taken from ping.c */
5193ad3fb45Sjoris static int
5203ad3fb45Sjoris isqrt(int n)
5213ad3fb45Sjoris {
5223ad3fb45Sjoris 	int y, x = 1;
5233ad3fb45Sjoris 
5243ad3fb45Sjoris 	if (n == 0)
5253ad3fb45Sjoris 		return (0);
5263ad3fb45Sjoris 
5273ad3fb45Sjoris 	do { /* newton was a stinker */
5283ad3fb45Sjoris 		y = x;
5293ad3fb45Sjoris 		x = n / x;
5303ad3fb45Sjoris 		x += y;
5313ad3fb45Sjoris 		x /= 2;
532eea21b3cSray 	} while ((x - y) > 1 || (x - y) < -1);
5333ad3fb45Sjoris 
5343ad3fb45Sjoris 	return (x);
5353ad3fb45Sjoris }
5363ad3fb45Sjoris 
5373ad3fb45Sjoris static int
538219c50abSray stone(int *a, int n, int *b, int *c, int flags)
5393ad3fb45Sjoris {
5403ad3fb45Sjoris 	int i, k, y, j, l;
541df890a16Snicm 	int oldc, tc, oldl, sq;
542df890a16Snicm 	u_int numtries, bound;
5433ad3fb45Sjoris 
544df890a16Snicm 	if (flags & D_MINIMAL)
545df890a16Snicm 		bound = UINT_MAX;
546df890a16Snicm 	else {
547df890a16Snicm 		sq = isqrt(n);
548df890a16Snicm 		bound = MAX(256, sq);
549df890a16Snicm 	}
5503ad3fb45Sjoris 
5513ad3fb45Sjoris 	k = 0;
552eea21b3cSray 	c[0] = newcand(0, 0, 0);
5533ad3fb45Sjoris 	for (i = 1; i <= n; i++) {
5543ad3fb45Sjoris 		j = a[i];
5553ad3fb45Sjoris 		if (j == 0)
5563ad3fb45Sjoris 			continue;
5573ad3fb45Sjoris 		y = -b[j];
5583ad3fb45Sjoris 		oldl = 0;
5593ad3fb45Sjoris 		oldc = c[0];
5603ad3fb45Sjoris 		numtries = 0;
5613ad3fb45Sjoris 		do {
5623ad3fb45Sjoris 			if (y <= clist[oldc].y)
5633ad3fb45Sjoris 				continue;
5643ad3fb45Sjoris 			l = search(c, k, y);
5653ad3fb45Sjoris 			if (l != oldl + 1)
5663ad3fb45Sjoris 				oldc = c[l - 1];
5673ad3fb45Sjoris 			if (l <= k) {
5683ad3fb45Sjoris 				if (clist[c[l]].y <= y)
5693ad3fb45Sjoris 					continue;
5703ad3fb45Sjoris 				tc = c[l];
571eea21b3cSray 				c[l] = newcand(i, y, oldc);
5723ad3fb45Sjoris 				oldc = tc;
5733ad3fb45Sjoris 				oldl = l;
5743ad3fb45Sjoris 				numtries++;
5753ad3fb45Sjoris 			} else {
576eea21b3cSray 				c[l] = newcand(i, y, oldc);
5773ad3fb45Sjoris 				k++;
5783ad3fb45Sjoris 				break;
5793ad3fb45Sjoris 			}
5803ad3fb45Sjoris 		} while ((y = b[++j]) > 0 && numtries < bound);
5813ad3fb45Sjoris 	}
5823ad3fb45Sjoris 	return (k);
5833ad3fb45Sjoris }
5843ad3fb45Sjoris 
5853ad3fb45Sjoris static int
5863ad3fb45Sjoris newcand(int x, int y, int pred)
5873ad3fb45Sjoris {
58880566be2Sray 	struct cand *q;
5893ad3fb45Sjoris 
5903ad3fb45Sjoris 	if (clen == clistlen) {
591f74aa433Sray 		clistlen = clistlen * 11 / 10;
592*caa2ffb0Sderaadt 		clist = xreallocarray(clist, clistlen, sizeof(*clist));
5933ad3fb45Sjoris 	}
5943ad3fb45Sjoris 	q = clist + clen;
5953ad3fb45Sjoris 	q->x = x;
5963ad3fb45Sjoris 	q->y = y;
5973ad3fb45Sjoris 	q->pred = pred;
5983ad3fb45Sjoris 	return (clen++);
5993ad3fb45Sjoris }
6003ad3fb45Sjoris 
6013ad3fb45Sjoris static int
6023ad3fb45Sjoris search(int *c, int k, int y)
6033ad3fb45Sjoris {
6043ad3fb45Sjoris 	int i, j, l, t;
6053ad3fb45Sjoris 
6063ad3fb45Sjoris 	if (clist[c[k]].y < y)	/* quick look for typical case */
6073ad3fb45Sjoris 		return (k + 1);
6083ad3fb45Sjoris 	i = 0;
6093ad3fb45Sjoris 	j = k + 1;
6103ad3fb45Sjoris 	for (;;) {
6113ad3fb45Sjoris 		l = (i + j) / 2;
6123ad3fb45Sjoris 		if (l <= i)
6133ad3fb45Sjoris 			break;
6143ad3fb45Sjoris 		t = clist[c[l]].y;
6153ad3fb45Sjoris 		if (t > y)
6163ad3fb45Sjoris 			j = l;
6173ad3fb45Sjoris 		else if (t < y)
6183ad3fb45Sjoris 			i = l;
6193ad3fb45Sjoris 		else
6203ad3fb45Sjoris 			return (l);
6213ad3fb45Sjoris 	}
6223ad3fb45Sjoris 	return (l + 1);
6233ad3fb45Sjoris }
6243ad3fb45Sjoris 
6253ad3fb45Sjoris static void
6263ad3fb45Sjoris unravel(int p)
6273ad3fb45Sjoris {
6283ad3fb45Sjoris 	struct cand *q;
6293ad3fb45Sjoris 	int i;
6303ad3fb45Sjoris 
631eea21b3cSray 	for (i = 0; i <= len[0]; i++)
6323ad3fb45Sjoris 		J[i] = i <= pref ? i :
633eea21b3cSray 		    i > len[0] - suff ? i + len[1] - len[0] : 0;
6343ad3fb45Sjoris 	for (q = clist + p; q->y != 0; q = clist + q->pred)
6353ad3fb45Sjoris 		J[q->x + pref] = q->y + pref;
6363ad3fb45Sjoris }
6373ad3fb45Sjoris 
6383ad3fb45Sjoris /*
6393ad3fb45Sjoris  * Check does double duty:
6403ad3fb45Sjoris  *  1.	ferret out any fortuitous correspondences due
6413ad3fb45Sjoris  *	to confounding by hashing (which result in "jackpot")
6423ad3fb45Sjoris  *  2.  collect random access indexes to the two files
6433ad3fb45Sjoris  */
6443ad3fb45Sjoris static void
645219c50abSray check(FILE *f1, FILE *f2, int flags)
6463ad3fb45Sjoris {
6473ad3fb45Sjoris 	int i, j, jackpot, c, d;
6483ad3fb45Sjoris 	long ctold, ctnew;
6493ad3fb45Sjoris 
6503ad3fb45Sjoris 	rewind(f1);
6513ad3fb45Sjoris 	rewind(f2);
6523ad3fb45Sjoris 	j = 1;
6533ad3fb45Sjoris 	ixold[0] = ixnew[0] = 0;
6543ad3fb45Sjoris 	jackpot = 0;
6553ad3fb45Sjoris 	ctold = ctnew = 0;
656eea21b3cSray 	for (i = 1; i <= len[0]; i++) {
6573ad3fb45Sjoris 		if (J[i] == 0) {
6583ad3fb45Sjoris 			ixold[i] = ctold += skipline(f1);
6593ad3fb45Sjoris 			continue;
6603ad3fb45Sjoris 		}
6613ad3fb45Sjoris 		while (j < J[i]) {
6623ad3fb45Sjoris 			ixnew[j] = ctnew += skipline(f2);
6633ad3fb45Sjoris 			j++;
6643ad3fb45Sjoris 		}
665219c50abSray 		if (flags & (D_FOLDBLANKS|D_IGNOREBLANKS|D_IGNORECASE)) {
6663ad3fb45Sjoris 			for (;;) {
6673ad3fb45Sjoris 				c = getc(f1);
6683ad3fb45Sjoris 				d = getc(f2);
6693ad3fb45Sjoris 				/*
6703ad3fb45Sjoris 				 * GNU diff ignores a missing newline
671eea21b3cSray 				 * in one file for -b or -w.
6723ad3fb45Sjoris 				 */
673219c50abSray 				if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) &&
6743ad3fb45Sjoris 				    ((c == EOF && d == '\n') ||
6753ad3fb45Sjoris 				    (c == '\n' && d == EOF))) {
6763ad3fb45Sjoris 					break;
6773ad3fb45Sjoris 				}
6783ad3fb45Sjoris 				ctold++;
6793ad3fb45Sjoris 				ctnew++;
680219c50abSray 				if ((flags & D_FOLDBLANKS) && isspace(c) &&
681219c50abSray 				    isspace(d)) {
6823ad3fb45Sjoris 					do {
6833ad3fb45Sjoris 						if (c == '\n')
6843ad3fb45Sjoris 							break;
6853ad3fb45Sjoris 						ctold++;
6863ad3fb45Sjoris 					} while (isspace(c = getc(f1)));
6873ad3fb45Sjoris 					do {
6883ad3fb45Sjoris 						if (d == '\n')
6893ad3fb45Sjoris 							break;
6903ad3fb45Sjoris 						ctnew++;
6913ad3fb45Sjoris 					} while (isspace(d = getc(f2)));
692219c50abSray 				} else if ((flags & D_IGNOREBLANKS)) {
6933ad3fb45Sjoris 					while (isspace(c) && c != '\n') {
6943ad3fb45Sjoris 						c = getc(f1);
6953ad3fb45Sjoris 						ctold++;
6963ad3fb45Sjoris 					}
6973ad3fb45Sjoris 					while (isspace(d) && d != '\n') {
6983ad3fb45Sjoris 						d = getc(f2);
6993ad3fb45Sjoris 						ctnew++;
7003ad3fb45Sjoris 					}
7013ad3fb45Sjoris 				}
7023ad3fb45Sjoris 				if (chrtran[c] != chrtran[d]) {
7033ad3fb45Sjoris 					jackpot++;
7043ad3fb45Sjoris 					J[i] = 0;
7053ad3fb45Sjoris 					if (c != '\n' && c != EOF)
7063ad3fb45Sjoris 						ctold += skipline(f1);
7073ad3fb45Sjoris 					if (d != '\n' && c != EOF)
7083ad3fb45Sjoris 						ctnew += skipline(f2);
7093ad3fb45Sjoris 					break;
7103ad3fb45Sjoris 				}
7113ad3fb45Sjoris 				if (c == '\n' || c == EOF)
7123ad3fb45Sjoris 					break;
7133ad3fb45Sjoris 			}
7143ad3fb45Sjoris 		} else {
7153ad3fb45Sjoris 			for (;;) {
7163ad3fb45Sjoris 				ctold++;
7173ad3fb45Sjoris 				ctnew++;
7183ad3fb45Sjoris 				if ((c = getc(f1)) != (d = getc(f2))) {
7193ad3fb45Sjoris 					/* jackpot++; */
7203ad3fb45Sjoris 					J[i] = 0;
7213ad3fb45Sjoris 					if (c != '\n' && c != EOF)
7223ad3fb45Sjoris 						ctold += skipline(f1);
7233ad3fb45Sjoris 					if (d != '\n' && c != EOF)
7243ad3fb45Sjoris 						ctnew += skipline(f2);
7253ad3fb45Sjoris 					break;
7263ad3fb45Sjoris 				}
7273ad3fb45Sjoris 				if (c == '\n' || c == EOF)
7283ad3fb45Sjoris 					break;
7293ad3fb45Sjoris 			}
7303ad3fb45Sjoris 		}
7313ad3fb45Sjoris 		ixold[i] = ctold;
7323ad3fb45Sjoris 		ixnew[j] = ctnew;
7333ad3fb45Sjoris 		j++;
7343ad3fb45Sjoris 	}
735eea21b3cSray 	for (; j <= len[1]; j++)
7363ad3fb45Sjoris 		ixnew[j] = ctnew += skipline(f2);
7373ad3fb45Sjoris 	/*
738eea21b3cSray 	 * if (jackpot)
739eea21b3cSray 	 *	fprintf(stderr, "jackpot\n");
7403ad3fb45Sjoris 	 */
7413ad3fb45Sjoris }
7423ad3fb45Sjoris 
7433ad3fb45Sjoris /* shellsort CACM #201 */
7443ad3fb45Sjoris static void
7453ad3fb45Sjoris sort(struct line *a, int n)
7463ad3fb45Sjoris {
7473ad3fb45Sjoris 	struct line *ai, *aim, w;
7483ad3fb45Sjoris 	int j, m = 0, k;
7493ad3fb45Sjoris 
7503ad3fb45Sjoris 	if (n == 0)
7513ad3fb45Sjoris 		return;
7523ad3fb45Sjoris 	for (j = 1; j <= n; j *= 2)
7533ad3fb45Sjoris 		m = 2 * j - 1;
7543ad3fb45Sjoris 	for (m /= 2; m != 0; m /= 2) {
7553ad3fb45Sjoris 		k = n - m;
7563ad3fb45Sjoris 		for (j = 1; j <= k; j++) {
7573ad3fb45Sjoris 			for (ai = &a[j]; ai > a; ai -= m) {
7583ad3fb45Sjoris 				aim = &ai[m];
7593ad3fb45Sjoris 				if (aim < ai)
7603ad3fb45Sjoris 					break;	/* wraparound */
7613ad3fb45Sjoris 				if (aim->value > ai[0].value ||
7623ad3fb45Sjoris 				    (aim->value == ai[0].value &&
7633ad3fb45Sjoris 					aim->serial > ai[0].serial))
7643ad3fb45Sjoris 					break;
7653ad3fb45Sjoris 				w.value = ai[0].value;
7663ad3fb45Sjoris 				ai[0].value = aim->value;
7673ad3fb45Sjoris 				aim->value = w.value;
7683ad3fb45Sjoris 				w.serial = ai[0].serial;
7693ad3fb45Sjoris 				ai[0].serial = aim->serial;
7703ad3fb45Sjoris 				aim->serial = w.serial;
7713ad3fb45Sjoris 			}
7723ad3fb45Sjoris 		}
7733ad3fb45Sjoris 	}
7743ad3fb45Sjoris }
7753ad3fb45Sjoris 
7763ad3fb45Sjoris static void
7773ad3fb45Sjoris unsort(struct line *f, int l, int *b)
7783ad3fb45Sjoris {
7793ad3fb45Sjoris 	int *a, i;
7803ad3fb45Sjoris 
7813ad3fb45Sjoris 	a = xcalloc(l + 1, sizeof(*a));
7823ad3fb45Sjoris 	for (i = 1; i <= l; i++)
7833ad3fb45Sjoris 		a[f[i].serial] = f[i].value;
7843ad3fb45Sjoris 	for (i = 1; i <= l; i++)
7853ad3fb45Sjoris 		b[i] = a[i];
7863ad3fb45Sjoris 	xfree(a);
7873ad3fb45Sjoris }
7883ad3fb45Sjoris 
7893ad3fb45Sjoris static int
7903ad3fb45Sjoris skipline(FILE *f)
7913ad3fb45Sjoris {
7923ad3fb45Sjoris 	int i, c;
7933ad3fb45Sjoris 
7943ad3fb45Sjoris 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
7953ad3fb45Sjoris 		continue;
7963ad3fb45Sjoris 	return (i);
7973ad3fb45Sjoris }
7983ad3fb45Sjoris 
7993ad3fb45Sjoris static void
800219c50abSray output(FILE *f1, FILE *f2, int flags)
8013ad3fb45Sjoris {
8023ad3fb45Sjoris 	int m, i0, i1, j0, j1;
8033ad3fb45Sjoris 
8043ad3fb45Sjoris 	rewind(f1);
8053ad3fb45Sjoris 	rewind(f2);
806eea21b3cSray 	m = len[0];
8073ad3fb45Sjoris 	J[0] = 0;
808eea21b3cSray 	J[m + 1] = len[1] + 1;
8093ad3fb45Sjoris 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
8103ad3fb45Sjoris 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
8113ad3fb45Sjoris 			i0++;
8123ad3fb45Sjoris 		j0 = J[i0 - 1] + 1;
8133ad3fb45Sjoris 		i1 = i0 - 1;
8143ad3fb45Sjoris 		while (i1 < m && J[i1 + 1] == 0)
8153ad3fb45Sjoris 			i1++;
8163ad3fb45Sjoris 		j1 = J[i1 + 1] - 1;
8173ad3fb45Sjoris 		J[i1] = j1;
818219c50abSray 		change(f1, f2, i0, i1, j0, j1, flags);
8193ad3fb45Sjoris 	}
8203ad3fb45Sjoris 	if (m == 0)
821219c50abSray 		change(f1, f2, 1, 0, 1, len[1], flags);
8223ad3fb45Sjoris 	if (diff_format == D_IFDEF) {
8233ad3fb45Sjoris 		for (;;) {
8243ad3fb45Sjoris #define	c i0
8253ad3fb45Sjoris 			if ((c = getc(f1)) == EOF)
8263ad3fb45Sjoris 				return;
8273ad3fb45Sjoris 			diff_output("%c", c);
8283ad3fb45Sjoris 		}
8293ad3fb45Sjoris #undef c
8303ad3fb45Sjoris 	}
8313ad3fb45Sjoris 	if (anychange != 0) {
8323ad3fb45Sjoris 		if (diff_format == D_CONTEXT)
833219c50abSray 			dump_context_vec(f1, f2, flags);
8343ad3fb45Sjoris 		else if (diff_format == D_UNIFIED)
835219c50abSray 			dump_unified_vec(f1, f2, flags);
8363ad3fb45Sjoris 	}
8373ad3fb45Sjoris }
8383ad3fb45Sjoris 
839a304c0f4Sray static void
8403ad3fb45Sjoris range(int a, int b, char *separator)
8413ad3fb45Sjoris {
8423ad3fb45Sjoris 	diff_output("%d", a > b ? b : a);
8433ad3fb45Sjoris 	if (a < b)
8443ad3fb45Sjoris 		diff_output("%s%d", separator, b);
8453ad3fb45Sjoris }
8463ad3fb45Sjoris 
847a304c0f4Sray static void
8483ad3fb45Sjoris uni_range(int a, int b)
8493ad3fb45Sjoris {
8503ad3fb45Sjoris 	if (a < b)
8513ad3fb45Sjoris 		diff_output("%d,%d", a, b - a + 1);
8523ad3fb45Sjoris 	else if (a == b)
8533ad3fb45Sjoris 		diff_output("%d", b);
8543ad3fb45Sjoris 	else
8553ad3fb45Sjoris 		diff_output("%d,0", b);
8563ad3fb45Sjoris }
8573ad3fb45Sjoris 
8583ad3fb45Sjoris static char *
8593ad3fb45Sjoris preadline(int fd, size_t rlen, off_t off)
8603ad3fb45Sjoris {
8613ad3fb45Sjoris 	char *line;
8623ad3fb45Sjoris 	ssize_t nr;
8633ad3fb45Sjoris 
8643ad3fb45Sjoris 	line = xmalloc(rlen + 1);
8657a6efebcSray 	if ((nr = pread(fd, line, rlen, off)) < 0)
8667a6efebcSray 		fatal("preadline: %s", strerror(errno));
8673ad3fb45Sjoris 	line[nr] = '\0';
8683ad3fb45Sjoris 	return (line);
8693ad3fb45Sjoris }
8703ad3fb45Sjoris 
8713ad3fb45Sjoris static int
8723ad3fb45Sjoris ignoreline(char *line)
8733ad3fb45Sjoris {
8743ad3fb45Sjoris 	int ret;
8753ad3fb45Sjoris 
876a304c0f4Sray 	ret = regexec(&ignore_re, line, 0, NULL, 0);
8773ad3fb45Sjoris 	xfree(line);
8783ad3fb45Sjoris 	return (ret == 0);	/* if it matched, it should be ignored. */
8793ad3fb45Sjoris }
8803ad3fb45Sjoris 
881fd660bf2Stobias static void
882fd660bf2Stobias diff_head(void)
883fd660bf2Stobias {
884fd660bf2Stobias 	char buf[64];
8856534056aStobias 	struct tm t;
886fd660bf2Stobias 	time_t curr_time;
887fd660bf2Stobias 
888fd660bf2Stobias 	if (diff_rev1 != NULL) {
8896534056aStobias 		gmtime_r(&stb1.st_mtime, &t);
890fd660bf2Stobias 	} else {
891fd660bf2Stobias 		time(&curr_time);
8926534056aStobias 		localtime_r(&curr_time, &t);
893fd660bf2Stobias 	}
894fd660bf2Stobias 
8956534056aStobias 	(void)strftime(buf, sizeof(buf), "%b %G %H:%M:%S -0000", &t);
896d00c0f9eStobias 	diff_output("%s %s	%d %s", diff_format == D_CONTEXT ?
8976534056aStobias 	    "***" : "---", diff_file1, t.tm_mday, buf);
898fd660bf2Stobias 
899fd660bf2Stobias 	if (diff_rev1 != NULL) {
900fd660bf2Stobias 		rcsnum_tostr(diff_rev1, buf, sizeof(buf));
901fd660bf2Stobias 		diff_output("\t%s", buf);
902fd660bf2Stobias 	}
903fd660bf2Stobias 
904fd660bf2Stobias 	diff_output("\n");
905fd660bf2Stobias 
9066534056aStobias 	gmtime_r(&stb2.st_mtime, &t);
907fd660bf2Stobias 
9086534056aStobias 	(void)strftime(buf, sizeof(buf), "%b %G %H:%M:%S -0000", &t);
909d00c0f9eStobias 	diff_output("%s %s	%d %s", diff_format == D_CONTEXT ?
9106534056aStobias 	    "---" : "+++", diff_file2, t.tm_mday, buf);
911fd660bf2Stobias 
912fd660bf2Stobias 	if (diff_rev2 != NULL) {
913fd660bf2Stobias 		rcsnum_tostr(diff_rev2, buf, sizeof(buf));
914fd660bf2Stobias 		diff_output("\t%s", buf);
915fd660bf2Stobias 	}
916fd660bf2Stobias 
917fd660bf2Stobias 	diff_output("\n");
918fd660bf2Stobias }
919fd660bf2Stobias 
920fd660bf2Stobias static void
921fd660bf2Stobias rdiff_head(void)
922fd660bf2Stobias {
923fd660bf2Stobias 	char buf[64];
9246534056aStobias 	struct tm t;
925fd660bf2Stobias 	time_t curr_time;
926fd660bf2Stobias 
927fd660bf2Stobias 	diff_output("%s ", diff_format == D_CONTEXT ? "***" : "---");
928fd660bf2Stobias 
929fd660bf2Stobias 	if (diff_rev1 == NULL) {
930fd660bf2Stobias 		diff_output("%s", CVS_PATH_DEVNULL);
9316534056aStobias 		gmtime_r(&stb1.st_atime, &t);
932fd660bf2Stobias 	} else {
933fd660bf2Stobias 		rcsnum_tostr(diff_rev1, buf, sizeof(buf));
934be756b91Stobias 		diff_output("%s:%s", diff_file1, buf);
93545ea4f19Stobias 		localtime_r(&stb1.st_mtime, &t);
936fd660bf2Stobias 	}
937fd660bf2Stobias 
9386534056aStobias 	(void)strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %G", &t);
939fd660bf2Stobias 	diff_output("\t%s\n", buf);
940fd660bf2Stobias 
941fd660bf2Stobias 	if (diff_rev2 != NULL) {
9426534056aStobias 		localtime_r(&stb2.st_mtime, &t);
943fd660bf2Stobias 	} else {
944fd660bf2Stobias 		time(&curr_time);
9456534056aStobias 		localtime_r(&curr_time, &t);
946fd660bf2Stobias 	}
947fd660bf2Stobias 
9486534056aStobias 	(void)strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %G", &t);
949fd660bf2Stobias 
950fd660bf2Stobias 	diff_output("%s %s	%s\n", diff_format == D_CONTEXT ? "---" : "+++",
951be756b91Stobias 	    diff_file2, buf);
952fd660bf2Stobias }
953fd660bf2Stobias 
9543ad3fb45Sjoris /*
9553ad3fb45Sjoris  * Indicate that there is a difference between lines a and b of the from file
9563ad3fb45Sjoris  * to get to lines c to d of the to file.  If a is greater then b then there
9573ad3fb45Sjoris  * are no lines in the from file involved and this means that there were
9583ad3fb45Sjoris  * lines appended (beginning at b).  If c is greater than d then there are
9593ad3fb45Sjoris  * lines missing from the to file.
9603ad3fb45Sjoris  */
9613ad3fb45Sjoris static void
962219c50abSray change(FILE *f1, FILE *f2, int a, int b, int c, int d, int flags)
9633ad3fb45Sjoris {
9643ad3fb45Sjoris 	static size_t max_context = 64;
965eea21b3cSray 	int i;
9663ad3fb45Sjoris 
9673ad3fb45Sjoris 	if (diff_format != D_IFDEF && a > b && c > d)
9683ad3fb45Sjoris 		return;
9693ad3fb45Sjoris 	if (ignore_pats != NULL) {
9703ad3fb45Sjoris 		char *line;
9713ad3fb45Sjoris 		/*
9723ad3fb45Sjoris 		 * All lines in the change, insert, or delete must
9733ad3fb45Sjoris 		 * match an ignore pattern for the change to be
9743ad3fb45Sjoris 		 * ignored.
9753ad3fb45Sjoris 		 */
9763ad3fb45Sjoris 		if (a <= b) {		/* Changes and deletes. */
9773ad3fb45Sjoris 			for (i = a; i <= b; i++) {
9783ad3fb45Sjoris 				line = preadline(fileno(f1),
9793ad3fb45Sjoris 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
9803ad3fb45Sjoris 				if (!ignoreline(line))
9813ad3fb45Sjoris 					goto proceed;
9823ad3fb45Sjoris 			}
9833ad3fb45Sjoris 		}
9843ad3fb45Sjoris 		if (a > b || c <= d) {	/* Changes and inserts. */
9853ad3fb45Sjoris 			for (i = c; i <= d; i++) {
9863ad3fb45Sjoris 				line = preadline(fileno(f2),
9873ad3fb45Sjoris 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
9883ad3fb45Sjoris 				if (!ignoreline(line))
9893ad3fb45Sjoris 					goto proceed;
9903ad3fb45Sjoris 			}
9913ad3fb45Sjoris 		}
9923ad3fb45Sjoris 		return;
9933ad3fb45Sjoris 	}
9943ad3fb45Sjoris proceed:
9953ad3fb45Sjoris 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
9963ad3fb45Sjoris 		/*
9973ad3fb45Sjoris 		 * Allocate change records as needed.
9983ad3fb45Sjoris 		 */
9993ad3fb45Sjoris 		if (context_vec_ptr == context_vec_end - 1) {
10003ad3fb45Sjoris 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
10013ad3fb45Sjoris 			max_context <<= 1;
1002*caa2ffb0Sderaadt 			context_vec_start = xreallocarray(context_vec_start,
100380566be2Sray 			    max_context, sizeof(*context_vec_start));
10043ad3fb45Sjoris 			context_vec_end = context_vec_start + max_context;
10053ad3fb45Sjoris 			context_vec_ptr = context_vec_start + offset;
10063ad3fb45Sjoris 		}
10073ad3fb45Sjoris 		if (anychange == 0) {
10083ad3fb45Sjoris 			/*
10093ad3fb45Sjoris 			 * Print the context/unidiff header first time through.
10103ad3fb45Sjoris 			 */
1011fd660bf2Stobias 			if (cvs_cmdop == CVS_OP_RDIFF)
1012fd660bf2Stobias 				rdiff_head();
1013fd660bf2Stobias 			else
1014fd660bf2Stobias 				diff_head();
10153ad3fb45Sjoris 
10163ad3fb45Sjoris 			anychange = 1;
101772026f1aSnicm 		} else if (a > context_vec_ptr->b + (2 * diff_context) + 1 &&
101872026f1aSnicm 		    c > context_vec_ptr->d + (2 * diff_context) + 1) {
10193ad3fb45Sjoris 			/*
10203ad3fb45Sjoris 			 * If this change is more than 'context' lines from the
10213ad3fb45Sjoris 			 * previous change, dump the record and reset it.
10223ad3fb45Sjoris 			 */
10233ad3fb45Sjoris 			if (diff_format == D_CONTEXT)
1024219c50abSray 				dump_context_vec(f1, f2, flags);
10253ad3fb45Sjoris 			else
1026219c50abSray 				dump_unified_vec(f1, f2, flags);
10273ad3fb45Sjoris 		}
10283ad3fb45Sjoris 		context_vec_ptr++;
10293ad3fb45Sjoris 		context_vec_ptr->a = a;
10303ad3fb45Sjoris 		context_vec_ptr->b = b;
10313ad3fb45Sjoris 		context_vec_ptr->c = c;
10323ad3fb45Sjoris 		context_vec_ptr->d = d;
10333ad3fb45Sjoris 		return;
10343ad3fb45Sjoris 	}
10353ad3fb45Sjoris 	if (anychange == 0)
10363ad3fb45Sjoris 		anychange = 1;
10373ad3fb45Sjoris 	switch (diff_format) {
10383ad3fb45Sjoris 	case D_BRIEF:
10393ad3fb45Sjoris 		return;
10403ad3fb45Sjoris 	case D_NORMAL:
10413ad3fb45Sjoris 		range(a, b, ",");
10423ad3fb45Sjoris 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
10433ad3fb45Sjoris 		if (diff_format == D_NORMAL)
10443ad3fb45Sjoris 			range(c, d, ",");
10453ad3fb45Sjoris 		diff_output("\n");
10463ad3fb45Sjoris 		break;
10473ad3fb45Sjoris 	case D_RCSDIFF:
10483ad3fb45Sjoris 		if (a > b)
10493ad3fb45Sjoris 			diff_output("a%d %d\n", b, d - c + 1);
10503ad3fb45Sjoris 		else {
10513ad3fb45Sjoris 			diff_output("d%d %d\n", a, b - a + 1);
10523ad3fb45Sjoris 
10533ad3fb45Sjoris 			if (!(c > d))	/* add changed lines */
10543ad3fb45Sjoris 				diff_output("a%d %d\n", b, d - c + 1);
10553ad3fb45Sjoris 		}
10563ad3fb45Sjoris 		break;
10573ad3fb45Sjoris 	}
10583ad3fb45Sjoris 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
1059219c50abSray 		fetch(ixold, a, b, f1, '<', 1, flags);
10603ad3fb45Sjoris 		if (a <= b && c <= d && diff_format == D_NORMAL)
10613ad3fb45Sjoris 			diff_output("---\n");
10623ad3fb45Sjoris 	}
1063219c50abSray 	fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0, flags);
10643ad3fb45Sjoris 	if (inifdef) {
10653ad3fb45Sjoris 		diff_output("#endif /* %s */\n", ifdefname);
10663ad3fb45Sjoris 		inifdef = 0;
10673ad3fb45Sjoris 	}
10683ad3fb45Sjoris }
10693ad3fb45Sjoris 
10703ad3fb45Sjoris static void
1071219c50abSray fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags)
10723ad3fb45Sjoris {
10733ad3fb45Sjoris 	long j, nc;
10743ad3fb45Sjoris 	int i, c, col;
10753ad3fb45Sjoris 
10763ad3fb45Sjoris 	/*
10773ad3fb45Sjoris 	 * When doing #ifdef's, copy down to current line
10783ad3fb45Sjoris 	 * if this is the first file, so that stuff makes it to output.
10793ad3fb45Sjoris 	 */
10803ad3fb45Sjoris 	if (diff_format == D_IFDEF && oldfile) {
10813ad3fb45Sjoris 		long curpos = ftell(lb);
10823ad3fb45Sjoris 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
10833ad3fb45Sjoris 		nc = f[a > b ? b : a - 1] - curpos;
10843ad3fb45Sjoris 		for (i = 0; i < nc; i++)
10853ad3fb45Sjoris 			diff_output("%c", getc(lb));
10863ad3fb45Sjoris 	}
10873ad3fb45Sjoris 	if (a > b)
10883ad3fb45Sjoris 		return;
10893ad3fb45Sjoris 	if (diff_format == D_IFDEF) {
10903ad3fb45Sjoris 		if (inifdef) {
10913ad3fb45Sjoris 			diff_output("#else /* %s%s */\n",
10923ad3fb45Sjoris 			    oldfile == 1 ? "!" : "", ifdefname);
10933ad3fb45Sjoris 		} else {
10943ad3fb45Sjoris 			if (oldfile)
10953ad3fb45Sjoris 				diff_output("#ifndef %s\n", ifdefname);
10963ad3fb45Sjoris 			else
10973ad3fb45Sjoris 				diff_output("#ifdef %s\n", ifdefname);
10983ad3fb45Sjoris 		}
10993ad3fb45Sjoris 		inifdef = 1 + oldfile;
11003ad3fb45Sjoris 	}
11013ad3fb45Sjoris 	for (i = a; i <= b; i++) {
11023ad3fb45Sjoris 		fseek(lb, f[i - 1], SEEK_SET);
11033ad3fb45Sjoris 		nc = f[i] - f[i - 1];
11043ad3fb45Sjoris 		if (diff_format != D_IFDEF && ch != '\0') {
11053ad3fb45Sjoris 			diff_output("%c", ch);
11063ad3fb45Sjoris 			if (Tflag == 1 && (diff_format == D_NORMAL ||
11073ad3fb45Sjoris 			    diff_format == D_CONTEXT ||
11083ad3fb45Sjoris 			    diff_format == D_UNIFIED))
11093ad3fb45Sjoris 				diff_output("\t");
11103ad3fb45Sjoris 			else if (diff_format != D_UNIFIED)
11113ad3fb45Sjoris 				diff_output(" ");
11123ad3fb45Sjoris 		}
11133ad3fb45Sjoris 		col = 0;
11143ad3fb45Sjoris 		for (j = 0; j < nc; j++) {
11153ad3fb45Sjoris 			if ((c = getc(lb)) == EOF) {
11163ad3fb45Sjoris 				if (diff_format == D_RCSDIFF)
11173ad3fb45Sjoris 					cvs_log(LP_ERR,
11183ad3fb45Sjoris 					    "No newline at end of file");
11193ad3fb45Sjoris 				else
11203ad3fb45Sjoris 					diff_output("\n\\ No newline at end of "
112125d5ee6bSjoris 					    "file\n");
11223ad3fb45Sjoris 				return;
11233ad3fb45Sjoris 			}
1124219c50abSray 			if (c == '\t' && (flags & D_EXPANDTABS)) {
11253ad3fb45Sjoris 				do {
11263ad3fb45Sjoris 					diff_output(" ");
11273ad3fb45Sjoris 				} while (++col & 7);
11283ad3fb45Sjoris 			} else {
11293ad3fb45Sjoris 				diff_output("%c", c);
11303ad3fb45Sjoris 				col++;
11313ad3fb45Sjoris 			}
11323ad3fb45Sjoris 		}
11333ad3fb45Sjoris 	}
11343ad3fb45Sjoris }
11353ad3fb45Sjoris 
11363ad3fb45Sjoris /*
11373ad3fb45Sjoris  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
11383ad3fb45Sjoris  */
11393ad3fb45Sjoris static int
1140219c50abSray readhash(FILE *f, int flags)
11413ad3fb45Sjoris {
11423ad3fb45Sjoris 	int i, t, space;
11433ad3fb45Sjoris 	int sum;
11443ad3fb45Sjoris 
11453ad3fb45Sjoris 	sum = 1;
11463ad3fb45Sjoris 	space = 0;
1147219c50abSray 	if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) == 0) {
1148219c50abSray 		if (flags & D_IGNORECASE)
11493ad3fb45Sjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
11503ad3fb45Sjoris 				if (t == EOF) {
11513ad3fb45Sjoris 					if (i == 0)
11523ad3fb45Sjoris 						return (0);
11533ad3fb45Sjoris 					break;
11543ad3fb45Sjoris 				}
11553ad3fb45Sjoris 				sum = sum * 127 + chrtran[t];
11563ad3fb45Sjoris 			}
11573ad3fb45Sjoris 		else
11583ad3fb45Sjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
11593ad3fb45Sjoris 				if (t == EOF) {
11603ad3fb45Sjoris 					if (i == 0)
11613ad3fb45Sjoris 						return (0);
11623ad3fb45Sjoris 					break;
11633ad3fb45Sjoris 				}
11643ad3fb45Sjoris 				sum = sum * 127 + t;
11653ad3fb45Sjoris 			}
11663ad3fb45Sjoris 	} else {
11673ad3fb45Sjoris 		for (i = 0;;) {
11683ad3fb45Sjoris 			switch (t = getc(f)) {
11693ad3fb45Sjoris 			case '\t':
1170eea21b3cSray 			case '\r':
1171eea21b3cSray 			case '\v':
1172eea21b3cSray 			case '\f':
11733ad3fb45Sjoris 			case ' ':
11743ad3fb45Sjoris 				space++;
11753ad3fb45Sjoris 				continue;
11763ad3fb45Sjoris 			default:
1177219c50abSray 				if (space && (flags & D_IGNOREBLANKS) == 0) {
11783ad3fb45Sjoris 					i++;
11793ad3fb45Sjoris 					space = 0;
11803ad3fb45Sjoris 				}
11813ad3fb45Sjoris 				sum = sum * 127 + chrtran[t];
11823ad3fb45Sjoris 				i++;
11833ad3fb45Sjoris 				continue;
11843ad3fb45Sjoris 			case EOF:
11853ad3fb45Sjoris 				if (i == 0)
11863ad3fb45Sjoris 					return (0);
11873ad3fb45Sjoris 				/* FALLTHROUGH */
11883ad3fb45Sjoris 			case '\n':
11893ad3fb45Sjoris 				break;
11903ad3fb45Sjoris 			}
11913ad3fb45Sjoris 			break;
11923ad3fb45Sjoris 		}
11933ad3fb45Sjoris 	}
11943ad3fb45Sjoris 	/*
11953ad3fb45Sjoris 	 * There is a remote possibility that we end up with a zero sum.
11963ad3fb45Sjoris 	 * Zero is used as an EOF marker, so return 1 instead.
11973ad3fb45Sjoris 	 */
11983ad3fb45Sjoris 	return (sum == 0 ? 1 : sum);
11993ad3fb45Sjoris }
12003ad3fb45Sjoris 
12013ad3fb45Sjoris static int
12023ad3fb45Sjoris asciifile(FILE *f)
12033ad3fb45Sjoris {
1204eea21b3cSray 	unsigned char buf[BUFSIZ];
12053ad3fb45Sjoris 	size_t i, cnt;
12063ad3fb45Sjoris 
1207219c50abSray 	if (f == NULL)
12083ad3fb45Sjoris 		return (1);
12093ad3fb45Sjoris 
12103ad3fb45Sjoris 	rewind(f);
1211a304c0f4Sray 	cnt = fread(buf, 1, sizeof(buf), f);
12123ad3fb45Sjoris 	for (i = 0; i < cnt; i++)
12133ad3fb45Sjoris 		if (!isprint(buf[i]) && !isspace(buf[i]))
12143ad3fb45Sjoris 			return (0);
12153ad3fb45Sjoris 	return (1);
12163ad3fb45Sjoris }
12173ad3fb45Sjoris 
1218e22e6974Sxsa #define begins_with(s, pre) (strncmp(s, pre, sizeof(pre)-1) == 0)
1219e22e6974Sxsa 
12203ad3fb45Sjoris static char *
12213ad3fb45Sjoris match_function(const long *f, int pos, FILE *fp)
12223ad3fb45Sjoris {
12233ad3fb45Sjoris 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
12243ad3fb45Sjoris 	size_t nc;
12253ad3fb45Sjoris 	int last = lastline;
1226e22e6974Sxsa 	char *state = NULL;
12273ad3fb45Sjoris 
12283ad3fb45Sjoris 	lastline = pos;
12293ad3fb45Sjoris 	while (pos > last) {
12303ad3fb45Sjoris 		fseek(fp, f[pos - 1], SEEK_SET);
12313ad3fb45Sjoris 		nc = f[pos] - f[pos - 1];
12323ad3fb45Sjoris 		if (nc >= sizeof(buf))
12333ad3fb45Sjoris 			nc = sizeof(buf) - 1;
1234a304c0f4Sray 		nc = fread(buf, 1, nc, fp);
12353ad3fb45Sjoris 		if (nc > 0) {
12363ad3fb45Sjoris 			buf[nc] = '\0';
123757003866Sray 			buf[strcspn(buf, "\n")] = '\0';
12383ad3fb45Sjoris 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
1239e22e6974Sxsa 				if (begins_with(buf, "private:")) {
1240e22e6974Sxsa 					if (!state)
1241e22e6974Sxsa 						state = " (private)";
1242e22e6974Sxsa 				} else if (begins_with(buf, "protected:")) {
1243e22e6974Sxsa 					if (!state)
1244e22e6974Sxsa 						state = " (protected)";
1245e22e6974Sxsa 				} else if (begins_with(buf, "public:")) {
1246e22e6974Sxsa 					if (!state)
1247e22e6974Sxsa 						state = " (public)";
1248e22e6974Sxsa 				} else {
124957003866Sray 					strlcpy(lastbuf, buf, sizeof lastbuf);
1250e22e6974Sxsa 					if (state)
1251e22e6974Sxsa 						strlcat(lastbuf, state,
1252e22e6974Sxsa 						    sizeof lastbuf);
12533ad3fb45Sjoris 					lastmatchline = pos;
12543ad3fb45Sjoris 					return lastbuf;
12553ad3fb45Sjoris 				}
12563ad3fb45Sjoris 			}
1257e22e6974Sxsa 		}
12583ad3fb45Sjoris 		pos--;
12593ad3fb45Sjoris 	}
1260eea21b3cSray 	return lastmatchline > 0 ? lastbuf : NULL;
12613ad3fb45Sjoris }
12623ad3fb45Sjoris 
12633ad3fb45Sjoris /* dump accumulated "context" diff changes */
12643ad3fb45Sjoris static void
1265219c50abSray dump_context_vec(FILE *f1, FILE *f2, int flags)
12663ad3fb45Sjoris {
12673ad3fb45Sjoris 	struct context_vec *cvp = context_vec_start;
12683ad3fb45Sjoris 	int lowa, upb, lowc, upd, do_output;
12693ad3fb45Sjoris 	int a, b, c, d;
12703ad3fb45Sjoris 	char ch, *f;
12713ad3fb45Sjoris 
12723ad3fb45Sjoris 	if (context_vec_start > context_vec_ptr)
12733ad3fb45Sjoris 		return;
12743ad3fb45Sjoris 
12753ad3fb45Sjoris 	b = d = 0;		/* gcc */
127672026f1aSnicm 	lowa = MAX(1, cvp->a - diff_context);
127772026f1aSnicm 	upb = MIN(len[0], context_vec_ptr->b + diff_context);
127872026f1aSnicm 	lowc = MAX(1, cvp->c - diff_context);
127972026f1aSnicm 	upd = MIN(len[1], context_vec_ptr->d + diff_context);
12803ad3fb45Sjoris 
12813ad3fb45Sjoris 	diff_output("***************");
1282219c50abSray 	if ((flags & D_PROTOTYPE)) {
12833ad3fb45Sjoris 		f = match_function(ixold, lowa-1, f1);
1284d5d01609Sray 		if (f != NULL)
12853ad3fb45Sjoris 			diff_output(" %s", f);
12863ad3fb45Sjoris 	}
12873ad3fb45Sjoris 	diff_output("\n*** ");
12883ad3fb45Sjoris 	range(lowa, upb, ",");
12893ad3fb45Sjoris 	diff_output(" ****\n");
12903ad3fb45Sjoris 
12913ad3fb45Sjoris 	/*
12923ad3fb45Sjoris 	 * Output changes to the "old" file.  The first loop suppresses
12933ad3fb45Sjoris 	 * output if there were no changes to the "old" file (we'll see
12943ad3fb45Sjoris 	 * the "old" lines as context in the "new" list).
12953ad3fb45Sjoris 	 */
12963ad3fb45Sjoris 	do_output = 0;
12973ad3fb45Sjoris 	for (; cvp <= context_vec_ptr; cvp++)
12983ad3fb45Sjoris 		if (cvp->a <= cvp->b) {
12993ad3fb45Sjoris 			cvp = context_vec_start;
13003ad3fb45Sjoris 			do_output++;
13013ad3fb45Sjoris 			break;
13023ad3fb45Sjoris 		}
1303eea21b3cSray 	if (do_output) {
13043ad3fb45Sjoris 		while (cvp <= context_vec_ptr) {
13053ad3fb45Sjoris 			a = cvp->a;
13063ad3fb45Sjoris 			b = cvp->b;
13073ad3fb45Sjoris 			c = cvp->c;
13083ad3fb45Sjoris 			d = cvp->d;
13093ad3fb45Sjoris 
13103ad3fb45Sjoris 			if (a <= b && c <= d)
13113ad3fb45Sjoris 				ch = 'c';
13123ad3fb45Sjoris 			else
13133ad3fb45Sjoris 				ch = (a <= b) ? 'd' : 'a';
13143ad3fb45Sjoris 
13153ad3fb45Sjoris 			if (ch == 'a')
1316219c50abSray 				fetch(ixold, lowa, b, f1, ' ', 0, flags);
13173ad3fb45Sjoris 			else {
1318219c50abSray 				fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
13193ad3fb45Sjoris 				fetch(ixold, a, b, f1,
1320219c50abSray 				    ch == 'c' ? '!' : '-', 0, flags);
13213ad3fb45Sjoris 			}
13223ad3fb45Sjoris 			lowa = b + 1;
13233ad3fb45Sjoris 			cvp++;
13243ad3fb45Sjoris 		}
1325219c50abSray 		fetch(ixold, b + 1, upb, f1, ' ', 0, flags);
13263ad3fb45Sjoris 	}
13273ad3fb45Sjoris 	/* output changes to the "new" file */
13283ad3fb45Sjoris 	diff_output("--- ");
13293ad3fb45Sjoris 	range(lowc, upd, ",");
13303ad3fb45Sjoris 	diff_output(" ----\n");
13313ad3fb45Sjoris 
13323ad3fb45Sjoris 	do_output = 0;
13333ad3fb45Sjoris 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
13343ad3fb45Sjoris 		if (cvp->c <= cvp->d) {
13353ad3fb45Sjoris 			cvp = context_vec_start;
13363ad3fb45Sjoris 			do_output++;
13373ad3fb45Sjoris 			break;
13383ad3fb45Sjoris 		}
1339eea21b3cSray 	if (do_output) {
13403ad3fb45Sjoris 		while (cvp <= context_vec_ptr) {
13413ad3fb45Sjoris 			a = cvp->a;
13423ad3fb45Sjoris 			b = cvp->b;
13433ad3fb45Sjoris 			c = cvp->c;
13443ad3fb45Sjoris 			d = cvp->d;
13453ad3fb45Sjoris 
13463ad3fb45Sjoris 			if (a <= b && c <= d)
13473ad3fb45Sjoris 				ch = 'c';
13483ad3fb45Sjoris 			else
13493ad3fb45Sjoris 				ch = (a <= b) ? 'd' : 'a';
13503ad3fb45Sjoris 
13513ad3fb45Sjoris 			if (ch == 'd')
1352219c50abSray 				fetch(ixnew, lowc, d, f2, ' ', 0, flags);
13533ad3fb45Sjoris 			else {
1354219c50abSray 				fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
13553ad3fb45Sjoris 				fetch(ixnew, c, d, f2,
1356219c50abSray 				    ch == 'c' ? '!' : '+', 0, flags);
13573ad3fb45Sjoris 			}
13583ad3fb45Sjoris 			lowc = d + 1;
13593ad3fb45Sjoris 			cvp++;
13603ad3fb45Sjoris 		}
1361219c50abSray 		fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
13623ad3fb45Sjoris 	}
13633ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
13643ad3fb45Sjoris }
13653ad3fb45Sjoris 
13663ad3fb45Sjoris /* dump accumulated "unified" diff changes */
13673ad3fb45Sjoris static void
1368219c50abSray dump_unified_vec(FILE *f1, FILE *f2, int flags)
13693ad3fb45Sjoris {
13703ad3fb45Sjoris 	struct context_vec *cvp = context_vec_start;
13713ad3fb45Sjoris 	int lowa, upb, lowc, upd;
13723ad3fb45Sjoris 	int a, b, c, d;
13733ad3fb45Sjoris 	char ch, *f;
13743ad3fb45Sjoris 
13753ad3fb45Sjoris 	if (context_vec_start > context_vec_ptr)
13763ad3fb45Sjoris 		return;
13773ad3fb45Sjoris 
13783ad3fb45Sjoris 	b = d = 0;		/* gcc */
137972026f1aSnicm 	lowa = MAX(1, cvp->a - diff_context);
138072026f1aSnicm 	upb = MIN(len[0], context_vec_ptr->b + diff_context);
138172026f1aSnicm 	lowc = MAX(1, cvp->c - diff_context);
138272026f1aSnicm 	upd = MIN(len[1], context_vec_ptr->d + diff_context);
13833ad3fb45Sjoris 
13843ad3fb45Sjoris 	diff_output("@@ -");
13853ad3fb45Sjoris 	uni_range(lowa, upb);
13863ad3fb45Sjoris 	diff_output(" +");
13873ad3fb45Sjoris 	uni_range(lowc, upd);
13883ad3fb45Sjoris 	diff_output(" @@");
1389219c50abSray 	if ((flags & D_PROTOTYPE)) {
13903ad3fb45Sjoris 		f = match_function(ixold, lowa-1, f1);
1391d5d01609Sray 		if (f != NULL)
13923ad3fb45Sjoris 			diff_output(" %s", f);
13933ad3fb45Sjoris 	}
13943ad3fb45Sjoris 	diff_output("\n");
13953ad3fb45Sjoris 
13963ad3fb45Sjoris 	/*
13973ad3fb45Sjoris 	 * Output changes in "unified" diff format--the old and new lines
13983ad3fb45Sjoris 	 * are printed together.
13993ad3fb45Sjoris 	 */
14003ad3fb45Sjoris 	for (; cvp <= context_vec_ptr; cvp++) {
14013ad3fb45Sjoris 		a = cvp->a;
14023ad3fb45Sjoris 		b = cvp->b;
14033ad3fb45Sjoris 		c = cvp->c;
14043ad3fb45Sjoris 		d = cvp->d;
14053ad3fb45Sjoris 
14063ad3fb45Sjoris 		/*
14073ad3fb45Sjoris 		 * c: both new and old changes
14083ad3fb45Sjoris 		 * d: only changes in the old file
14093ad3fb45Sjoris 		 * a: only changes in the new file
14103ad3fb45Sjoris 		 */
14113ad3fb45Sjoris 		if (a <= b && c <= d)
14123ad3fb45Sjoris 			ch = 'c';
14133ad3fb45Sjoris 		else
14143ad3fb45Sjoris 			ch = (a <= b) ? 'd' : 'a';
14153ad3fb45Sjoris 
14163ad3fb45Sjoris 		switch (ch) {
14173ad3fb45Sjoris 		case 'c':
1418219c50abSray 			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1419219c50abSray 			fetch(ixold, a, b, f1, '-', 0, flags);
1420219c50abSray 			fetch(ixnew, c, d, f2, '+', 0, flags);
14213ad3fb45Sjoris 			break;
14223ad3fb45Sjoris 		case 'd':
1423219c50abSray 			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1424219c50abSray 			fetch(ixold, a, b, f1, '-', 0, flags);
14253ad3fb45Sjoris 			break;
14263ad3fb45Sjoris 		case 'a':
1427219c50abSray 			fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
1428219c50abSray 			fetch(ixnew, c, d, f2, '+', 0, flags);
14293ad3fb45Sjoris 			break;
14303ad3fb45Sjoris 		}
14313ad3fb45Sjoris 		lowa = b + 1;
14323ad3fb45Sjoris 		lowc = d + 1;
14333ad3fb45Sjoris 	}
1434219c50abSray 	fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
14353ad3fb45Sjoris 
14363ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
14373ad3fb45Sjoris }
14383ad3fb45Sjoris 
14393ad3fb45Sjoris void
14403ad3fb45Sjoris diff_output(const char *fmt, ...)
14413ad3fb45Sjoris {
14423ad3fb45Sjoris 	va_list vap;
14433ad3fb45Sjoris 	int i;
14443ad3fb45Sjoris 	char *str;
14453ad3fb45Sjoris 
14463ad3fb45Sjoris 	va_start(vap, fmt);
14473ad3fb45Sjoris 	i = vasprintf(&str, fmt, vap);
14483ad3fb45Sjoris 	va_end(vap);
14493ad3fb45Sjoris 	if (i == -1)
14509a0ecc80Stobias 		fatal("diff_output: could not allocate memory");
14513ad3fb45Sjoris 	if (diffbuf != NULL)
14527bb3ddb0Sray 		buf_puts(diffbuf, str);
14533ad3fb45Sjoris 	else
14543ad3fb45Sjoris 		cvs_printf("%s", str);
14553ad3fb45Sjoris 	xfree(str);
14563ad3fb45Sjoris }
1457