xref: /openbsd-src/usr.bin/cvs/diff_internals.c (revision 3aaa63eb46949490a39db9c6d82aacc8ee5d8551)
1*3aaa63ebSderaadt /*	$OpenBSD: diff_internals.c,v 1.40 2019/06/28 13:35:00 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 
67b9fc9a72Sderaadt #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>
741357284aSmillert #include <stdint.h>
75101e9cbeStobias #include <stdio.h>
76397ddb8aSnicm #include <stdlib.h>
77eea21b3cSray #include <string.h>
786534056aStobias #include <time.h>
79eea21b3cSray #include <unistd.h>
80eea21b3cSray 
81eea21b3cSray #include "cvs.h"
82eea21b3cSray #include "diff.h"
83eea21b3cSray 
84b9fc9a72Sderaadt #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
85b9fc9a72Sderaadt #define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
86b9fc9a72Sderaadt 
87eea21b3cSray /*
88eea21b3cSray  * diff - compare two files.
89eea21b3cSray  */
90eea21b3cSray 
913ad3fb45Sjoris /*
923ad3fb45Sjoris  *	Uses an algorithm due to Harold Stone, which finds
933ad3fb45Sjoris  *	a pair of longest identical subsequences in the two
943ad3fb45Sjoris  *	files.
953ad3fb45Sjoris  *
963ad3fb45Sjoris  *	The major goal is to generate the match vector J.
973ad3fb45Sjoris  *	J[i] is the index of the line in file1 corresponding
983ad3fb45Sjoris  *	to line i file0. J[i] = 0 if there is no
993ad3fb45Sjoris  *	such line in file1.
1003ad3fb45Sjoris  *
1013ad3fb45Sjoris  *	Lines are hashed so as to work in core. All potential
1023ad3fb45Sjoris  *	matches are located by sorting the lines of each file
1033ad3fb45Sjoris  *	on the hash (called ``value''). In particular, this
1043ad3fb45Sjoris  *	collects the equivalence classes in file1 together.
1053ad3fb45Sjoris  *	Subroutine equiv replaces the value of each line in
1063ad3fb45Sjoris  *	file0 by the index of the first element of its
1073ad3fb45Sjoris  *	matching equivalence in (the reordered) file1.
1083ad3fb45Sjoris  *	To save space equiv squeezes file1 into a single
1093ad3fb45Sjoris  *	array member in which the equivalence classes
1103ad3fb45Sjoris  *	are simply concatenated, except that their first
1113ad3fb45Sjoris  *	members are flagged by changing sign.
1123ad3fb45Sjoris  *
1133ad3fb45Sjoris  *	Next the indices that point into member are unsorted into
1143ad3fb45Sjoris  *	array class according to the original order of file0.
1153ad3fb45Sjoris  *
1163ad3fb45Sjoris  *	The cleverness lies in routine stone. This marches
1173ad3fb45Sjoris  *	through the lines of file0, developing a vector klist
1183ad3fb45Sjoris  *	of "k-candidates". At step i a k-candidate is a matched
1193ad3fb45Sjoris  *	pair of lines x,y (x in file0 y in file1) such that
1203ad3fb45Sjoris  *	there is a common subsequence of length k
1213ad3fb45Sjoris  *	between the first i lines of file0 and the first y
1223ad3fb45Sjoris  *	lines of file1, but there is no such subsequence for
1233ad3fb45Sjoris  *	any smaller y. x is the earliest possible mate to y
1243ad3fb45Sjoris  *	that occurs in such a subsequence.
1253ad3fb45Sjoris  *
1263ad3fb45Sjoris  *	Whenever any of the members of the equivalence class of
1273ad3fb45Sjoris  *	lines in file1 matable to a line in file0 has serial number
1283ad3fb45Sjoris  *	less than the y of some k-candidate, that k-candidate
1293ad3fb45Sjoris  *	with the smallest such y is replaced. The new
1303ad3fb45Sjoris  *	k-candidate is chained (via pred) to the current
1313ad3fb45Sjoris  *	k-1 candidate so that the actual subsequence can
1323ad3fb45Sjoris  *	be recovered. When a member has serial number greater
1333ad3fb45Sjoris  *	that the y of all k-candidates, the klist is extended.
1343ad3fb45Sjoris  *	At the end, the longest subsequence is pulled out
1353ad3fb45Sjoris  *	and placed in the array J by unravel
1363ad3fb45Sjoris  *
1373ad3fb45Sjoris  *	With J in hand, the matches there recorded are
1383ad3fb45Sjoris  *	check'ed against reality to assure that no spurious
1393ad3fb45Sjoris  *	matches have crept in due to hashing. If they have,
1403ad3fb45Sjoris  *	they are broken, and "jackpot" is recorded--a harmless
1413ad3fb45Sjoris  *	matter except that a true match for a spuriously
1423ad3fb45Sjoris  *	mated line may now be unnecessarily reported as a change.
1433ad3fb45Sjoris  *
1443ad3fb45Sjoris  *	Much of the complexity of the program comes simply
1453ad3fb45Sjoris  *	from trying to minimize core utilization and
1463ad3fb45Sjoris  *	maximize the range of doable problems by dynamically
1473ad3fb45Sjoris  *	allocating what is needed and reusing what is not.
1483ad3fb45Sjoris  *	The core requirements for problems larger than somewhat
1493ad3fb45Sjoris  *	are (in words) 2*length(file0) + length(file1) +
1503ad3fb45Sjoris  *	3*(number of k-candidates installed),  typically about
1513ad3fb45Sjoris  *	6n words for files of length n.
1523ad3fb45Sjoris  */
1533ad3fb45Sjoris 
1543ad3fb45Sjoris struct cand {
1553ad3fb45Sjoris 	int	x;
1563ad3fb45Sjoris 	int	y;
1573ad3fb45Sjoris 	int	pred;
158ccf8fb4cSray };
1593ad3fb45Sjoris 
1603ad3fb45Sjoris struct line {
1613ad3fb45Sjoris 	int	serial;
1623ad3fb45Sjoris 	int	value;
1633ad3fb45Sjoris } *file[2];
1643ad3fb45Sjoris 
1653ad3fb45Sjoris /*
1663ad3fb45Sjoris  * The following struct is used to record change information when
1673ad3fb45Sjoris  * doing a "context" or "unified" diff.  (see routine "change" to
1683ad3fb45Sjoris  * understand the highly mnemonic field names)
1693ad3fb45Sjoris  */
1703ad3fb45Sjoris struct context_vec {
1713ad3fb45Sjoris 	int	a;		/* start line in old file */
1723ad3fb45Sjoris 	int	b;		/* end line in old file */
1733ad3fb45Sjoris 	int	c;		/* start line in new file */
1743ad3fb45Sjoris 	int	d;		/* end line in new file */
1753ad3fb45Sjoris };
1763ad3fb45Sjoris 
177219c50abSray static void	 output(FILE *, FILE *, int);
178219c50abSray static void	 check(FILE *, FILE *, int);
1793ad3fb45Sjoris static void	 range(int, int, char *);
1803ad3fb45Sjoris static void	 uni_range(int, int);
181219c50abSray static void	 dump_context_vec(FILE *, FILE *, int);
182219c50abSray static void	 dump_unified_vec(FILE *, FILE *, int);
183219c50abSray static void	 prepare(int, FILE *, off_t, int);
1843ad3fb45Sjoris static void	 prune(void);
1853ad3fb45Sjoris static void	 equiv(struct line *, int, struct line *, int, int *);
1863ad3fb45Sjoris static void	 unravel(int);
1873ad3fb45Sjoris static void	 unsort(struct line *, int, int *);
188fd660bf2Stobias static void	 diff_head(void);
189fd660bf2Stobias static void	 rdiff_head(void);
190219c50abSray static void	 change(FILE *, FILE *, int, int, int, int, int);
1913ad3fb45Sjoris static void	 sort(struct line *, int);
1923ad3fb45Sjoris static int	 ignoreline(char *);
1933ad3fb45Sjoris static int	 asciifile(FILE *);
194219c50abSray static void	 fetch(long *, int, int, FILE *, int, int, int);
1953ad3fb45Sjoris static int	 newcand(int, int, int);
1963ad3fb45Sjoris static int	 search(int *, int, int);
1973ad3fb45Sjoris static int	 skipline(FILE *);
1983ad3fb45Sjoris static int	 isqrt(int);
199219c50abSray static int	 stone(int *, int, int *, int *, int);
200219c50abSray static int	 readhash(FILE *, int);
2013ad3fb45Sjoris static int	 files_differ(FILE *, FILE *);
2023ad3fb45Sjoris static char	*match_function(const long *, int, FILE *);
2033ad3fb45Sjoris static char	*preadline(int, size_t, off_t);
2043ad3fb45Sjoris 
205219c50abSray static int Tflag;
20672026f1aSnicm int diff_context = 3;
2073ad3fb45Sjoris int diff_format = D_NORMAL;
208be756b91Stobias const char *diff_file1 = NULL;
209be756b91Stobias const char *diff_file2 = NULL;
2103ad3fb45Sjoris RCSNUM *diff_rev1 = NULL;
2113ad3fb45Sjoris RCSNUM *diff_rev2 = NULL;
2127bb3ddb0Sray char diffargs[512];
2133ad3fb45Sjoris static struct stat stb1, stb2;
2143ad3fb45Sjoris static char *ifdefname, *ignore_pats;
2153ad3fb45Sjoris regex_t ignore_re;
2163ad3fb45Sjoris 
2173ad3fb45Sjoris static int  *J;			/* will be overlaid on class */
2183ad3fb45Sjoris static int  *class;		/* will be overlaid on file[0] */
2193ad3fb45Sjoris static int  *klist;		/* will be overlaid on file[0] after class */
2203ad3fb45Sjoris static int  *member;		/* will be overlaid on file[1] */
2213ad3fb45Sjoris static int   clen;
2223ad3fb45Sjoris static int   inifdef;		/* whether or not we are in a #ifdef block */
223eea21b3cSray static int   len[2];
2243ad3fb45Sjoris static int   pref, suff;	/* length of prefix and suffix */
2253ad3fb45Sjoris static int   slen[2];
2263ad3fb45Sjoris static int   anychange;
2273ad3fb45Sjoris static long *ixnew;		/* will be overlaid on file[1] */
2283ad3fb45Sjoris static long *ixold;		/* will be overlaid on klist */
2293ad3fb45Sjoris static struct cand *clist;	/* merely a free storage pot for candidates */
2303ad3fb45Sjoris static int   clistlen;		/* the length of clist */
2313ad3fb45Sjoris static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
2323ad3fb45Sjoris static u_char *chrtran;		/* translation table for case-folding */
2333ad3fb45Sjoris static struct context_vec *context_vec_start;
2343ad3fb45Sjoris static struct context_vec *context_vec_end;
2353ad3fb45Sjoris static struct context_vec *context_vec_ptr;
2363ad3fb45Sjoris 
237e22e6974Sxsa #define FUNCTION_CONTEXT_SIZE	55
2383ad3fb45Sjoris static char lastbuf[FUNCTION_CONTEXT_SIZE];
2393ad3fb45Sjoris static int lastline;
2403ad3fb45Sjoris static int lastmatchline;
2413ad3fb45Sjoris BUF  *diffbuf = NULL;
2423ad3fb45Sjoris 
243eea21b3cSray 
2443ad3fb45Sjoris /*
2453ad3fb45Sjoris  * chrtran points to one of 2 translation tables: cup2low if folding upper to
2463ad3fb45Sjoris  * lower case clow2low if not folding case
2473ad3fb45Sjoris  */
2483ad3fb45Sjoris u_char clow2low[256] = {
2493ad3fb45Sjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2503ad3fb45Sjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2513ad3fb45Sjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2523ad3fb45Sjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2533ad3fb45Sjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2543ad3fb45Sjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
2553ad3fb45Sjoris 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
2563ad3fb45Sjoris 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
2573ad3fb45Sjoris 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
2583ad3fb45Sjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2593ad3fb45Sjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2603ad3fb45Sjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2613ad3fb45Sjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2623ad3fb45Sjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2633ad3fb45Sjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2643ad3fb45Sjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2653ad3fb45Sjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2663ad3fb45Sjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2673ad3fb45Sjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2683ad3fb45Sjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2693ad3fb45Sjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2703ad3fb45Sjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2713ad3fb45Sjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2723ad3fb45Sjoris 	0xfd, 0xfe, 0xff
2733ad3fb45Sjoris };
2743ad3fb45Sjoris 
2753ad3fb45Sjoris u_char cup2low[256] = {
2763ad3fb45Sjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2773ad3fb45Sjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2783ad3fb45Sjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2793ad3fb45Sjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2803ad3fb45Sjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2813ad3fb45Sjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
2823ad3fb45Sjoris 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
2833ad3fb45Sjoris 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
2843ad3fb45Sjoris 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
2853ad3fb45Sjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2863ad3fb45Sjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2873ad3fb45Sjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2883ad3fb45Sjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2893ad3fb45Sjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2903ad3fb45Sjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2913ad3fb45Sjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2923ad3fb45Sjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2933ad3fb45Sjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2943ad3fb45Sjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2953ad3fb45Sjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2963ad3fb45Sjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2973ad3fb45Sjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2983ad3fb45Sjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2993ad3fb45Sjoris 	0xfd, 0xfe, 0xff
3003ad3fb45Sjoris };
3013ad3fb45Sjoris 
3023ad3fb45Sjoris int
diffreg(const char * file1,const char * file2,int _fd1,int _fd2,BUF * out,int flags)30357003866Sray diffreg(const char *file1, const char *file2, int _fd1, int _fd2,
304219c50abSray     BUF *out, int flags)
3053ad3fb45Sjoris {
3063ad3fb45Sjoris 	FILE *f1, *f2;
3072e0d696aSjoris 	int i, rval, fd1, fd2;
3083ad3fb45Sjoris 
309be756b91Stobias 	diff_file1 = file1;
310be756b91Stobias 	diff_file2 = file2;
3113ad3fb45Sjoris 	f1 = f2 = NULL;
3123ad3fb45Sjoris 	rval = D_SAME;
3133ad3fb45Sjoris 	anychange = 0;
3143ad3fb45Sjoris 	lastline = 0;
3153ad3fb45Sjoris 	lastmatchline = 0;
3163ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
317219c50abSray 	if (flags & D_IGNORECASE)
318219c50abSray 		chrtran = cup2low;
319219c50abSray 	else
320219c50abSray 		chrtran = clow2low;
3213ad3fb45Sjoris 	if (out != NULL)
3223ad3fb45Sjoris 		diffbuf = out;
3233ad3fb45Sjoris 
3242e0d696aSjoris 	fd1 = dup(_fd1);
3252e0d696aSjoris 	if (fd1 == -1)
32657003866Sray 		fatal("diffreg: dup: %s", strerror(errno));
3272e0d696aSjoris 
3282e0d696aSjoris 	fd2 = dup(_fd2);
3292e0d696aSjoris 	if (fd2 == -1)
33057003866Sray 		fatal("diffreg: dup: %s", strerror(errno));
3312e0d696aSjoris 
332*3aaa63ebSderaadt 	if (lseek(fd1, 0, SEEK_SET) == -1)
33357003866Sray 		fatal("diffreg: lseek: %s", strerror(errno));
3342e0d696aSjoris 
3352e0d696aSjoris 	f1 = fdopen(fd1, "r");
3363ad3fb45Sjoris 	if (f1 == NULL) {
3373ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file1);
3383ad3fb45Sjoris 		goto closem;
3393ad3fb45Sjoris 	}
3403ad3fb45Sjoris 
341*3aaa63ebSderaadt 	if (lseek(fd2, 0, SEEK_SET) == -1)
34257003866Sray 		fatal("diffreg: lseek: %s", strerror(errno));
3432e0d696aSjoris 
3442e0d696aSjoris 	f2 = fdopen(fd2, "r");
3453ad3fb45Sjoris 	if (f2 == NULL) {
3463ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file2);
3473ad3fb45Sjoris 		goto closem;
3483ad3fb45Sjoris 	}
3493ad3fb45Sjoris 
350*3aaa63ebSderaadt 	if (fstat(fd1, &stb1) == -1) {
3513ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file1);
3523ad3fb45Sjoris 		goto closem;
3533ad3fb45Sjoris 	}
3542e0d696aSjoris 
355*3aaa63ebSderaadt 	if (fstat(fd2, &stb2) == -1) {
3563ad3fb45Sjoris 		cvs_log(LP_ERR, "%s", file2);
3573ad3fb45Sjoris 		goto closem;
3583ad3fb45Sjoris 	}
359eea21b3cSray 
3603ad3fb45Sjoris 	switch (files_differ(f1, f2)) {
3613ad3fb45Sjoris 	case 0:
3623ad3fb45Sjoris 		goto closem;
3633ad3fb45Sjoris 	case 1:
3643ad3fb45Sjoris 		break;
3653ad3fb45Sjoris 	default:
3663ad3fb45Sjoris 		/* error */
3673ad3fb45Sjoris 		goto closem;
3683ad3fb45Sjoris 	}
3693ad3fb45Sjoris 
370219c50abSray 	if ((flags & D_FORCEASCII) == 0 &&
371219c50abSray 	    (!asciifile(f1) || !asciifile(f2))) {
3723ad3fb45Sjoris 		rval = D_BINARY;
3733ad3fb45Sjoris 		goto closem;
3743ad3fb45Sjoris 	}
375eea21b3cSray 
376219c50abSray 	prepare(0, f1, stb1.st_size, flags);
377219c50abSray 	prepare(1, f2, stb2.st_size, flags);
378eea21b3cSray 
3793ad3fb45Sjoris 	prune();
3803ad3fb45Sjoris 	sort(sfile[0], slen[0]);
3813ad3fb45Sjoris 	sort(sfile[1], slen[1]);
3823ad3fb45Sjoris 
3833ad3fb45Sjoris 	member = (int *)file[1];
3843ad3fb45Sjoris 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
385caa2ffb0Sderaadt 	member = xreallocarray(member, slen[1] + 2, sizeof(*member));
3863ad3fb45Sjoris 
3873ad3fb45Sjoris 	class = (int *)file[0];
3883ad3fb45Sjoris 	unsort(sfile[0], slen[0], class);
389caa2ffb0Sderaadt 	class = xreallocarray(class, slen[0] + 2, sizeof(*class));
3903ad3fb45Sjoris 
3913ad3fb45Sjoris 	klist = xcalloc(slen[0] + 2, sizeof(*klist));
3923ad3fb45Sjoris 	clen = 0;
3933ad3fb45Sjoris 	clistlen = 100;
3943ad3fb45Sjoris 	clist = xcalloc(clistlen, sizeof(*clist));
395219c50abSray 	i = stone(class, slen[0], member, klist, flags);
396397ddb8aSnicm 	free(member);
397397ddb8aSnicm 	free(class);
3983ad3fb45Sjoris 
399caa2ffb0Sderaadt 	J = xreallocarray(J, len[0] + 2, sizeof(*J));
4003ad3fb45Sjoris 	unravel(klist[i]);
401397ddb8aSnicm 	free(clist);
402397ddb8aSnicm 	free(klist);
4033ad3fb45Sjoris 
404caa2ffb0Sderaadt 	ixold = xreallocarray(ixold, len[0] + 2, sizeof(*ixold));
405caa2ffb0Sderaadt 	ixnew = xreallocarray(ixnew, len[1] + 2, sizeof(*ixnew));
406219c50abSray 	check(f1, f2, flags);
407219c50abSray 	output(f1, f2, flags);
4083ad3fb45Sjoris 
4093ad3fb45Sjoris closem:
410eea21b3cSray 	if (anychange) {
4113ad3fb45Sjoris 		if (rval == D_SAME)
4123ad3fb45Sjoris 			rval = D_DIFFER;
4133ad3fb45Sjoris 	}
4143ad3fb45Sjoris 	if (f1 != NULL)
4153ad3fb45Sjoris 		fclose(f1);
4163ad3fb45Sjoris 	if (f2 != NULL)
4173ad3fb45Sjoris 		fclose(f2);
4183ad3fb45Sjoris 
4193ad3fb45Sjoris 	return (rval);
4203ad3fb45Sjoris }
4213ad3fb45Sjoris 
4223ad3fb45Sjoris /*
4233ad3fb45Sjoris  * Check to see if the given files differ.
4243ad3fb45Sjoris  * Returns 0 if they are the same, 1 if different, and -1 on error.
4253ad3fb45Sjoris  * XXX - could use code from cmp(1) [faster]
4263ad3fb45Sjoris  */
4273ad3fb45Sjoris static int
files_differ(FILE * f1,FILE * f2)4283ad3fb45Sjoris files_differ(FILE *f1, FILE *f2)
4293ad3fb45Sjoris {
4303ad3fb45Sjoris 	char buf1[BUFSIZ], buf2[BUFSIZ];
4313ad3fb45Sjoris 	size_t i, j;
4323ad3fb45Sjoris 
4333ad3fb45Sjoris 	if (stb1.st_size != stb2.st_size)
4343ad3fb45Sjoris 		return (1);
4353ad3fb45Sjoris 	for (;;) {
436a304c0f4Sray 		i = fread(buf1, 1, sizeof(buf1), f1);
437a304c0f4Sray 		j = fread(buf2, 1, sizeof(buf2), f2);
43809523d6fSray 		if ((!i && ferror(f1)) || (!j && ferror(f2)))
43909523d6fSray 			return (-1);
4403ad3fb45Sjoris 		if (i != j)
4413ad3fb45Sjoris 			return (1);
44209523d6fSray 		if (i == 0)
4433ad3fb45Sjoris 			return (0);
4443ad3fb45Sjoris 		if (memcmp(buf1, buf2, i) != 0)
4453ad3fb45Sjoris 			return (1);
4463ad3fb45Sjoris 	}
4473ad3fb45Sjoris }
4483ad3fb45Sjoris 
449eea21b3cSray static void
prepare(int i,FILE * fd,off_t filesize,int flags)450219c50abSray prepare(int i, FILE *fd, off_t filesize, int flags)
4513ad3fb45Sjoris {
4523ad3fb45Sjoris 	struct line *p;
4533ad3fb45Sjoris 	int j, h;
4543ad3fb45Sjoris 	size_t sz;
4553ad3fb45Sjoris 
4563ad3fb45Sjoris 	rewind(fd);
4573ad3fb45Sjoris 
458ae886706Smillert 	sz = ((uintmax_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
4593ad3fb45Sjoris 	if (sz < 100)
4603ad3fb45Sjoris 		sz = 100;
4613ad3fb45Sjoris 
4623ad3fb45Sjoris 	p = xcalloc(sz + 3, sizeof(*p));
463219c50abSray 	for (j = 0; (h = readhash(fd, flags));) {
464ae886706Smillert 		if ((size_t)j == sz) {
4653ad3fb45Sjoris 			sz = sz * 3 / 2;
466caa2ffb0Sderaadt 			p = xreallocarray(p, sz + 3, sizeof(*p));
4673ad3fb45Sjoris 		}
4683ad3fb45Sjoris 		p[++j].value = h;
4693ad3fb45Sjoris 	}
470eea21b3cSray 	len[i] = j;
4713ad3fb45Sjoris 	file[i] = p;
4723ad3fb45Sjoris }
4733ad3fb45Sjoris 
4743ad3fb45Sjoris static void
prune(void)4753ad3fb45Sjoris prune(void)
4763ad3fb45Sjoris {
4773ad3fb45Sjoris 	int i, j;
4783ad3fb45Sjoris 
479eea21b3cSray 	for (pref = 0; pref < len[0] && pref < len[1] &&
4803ad3fb45Sjoris 	    file[0][pref + 1].value == file[1][pref + 1].value;
4813ad3fb45Sjoris 	    pref++)
4823ad3fb45Sjoris 		;
483eea21b3cSray 	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
484eea21b3cSray 	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
4853ad3fb45Sjoris 	    suff++)
4863ad3fb45Sjoris 		;
4873ad3fb45Sjoris 	for (j = 0; j < 2; j++) {
4883ad3fb45Sjoris 		sfile[j] = file[j] + pref;
489eea21b3cSray 		slen[j] = len[j] - pref - suff;
4903ad3fb45Sjoris 		for (i = 0; i <= slen[j]; i++)
4913ad3fb45Sjoris 			sfile[j][i].serial = i;
4923ad3fb45Sjoris 	}
4933ad3fb45Sjoris }
4943ad3fb45Sjoris 
4953ad3fb45Sjoris static void
equiv(struct line * a,int n,struct line * b,int m,int * c)4963ad3fb45Sjoris equiv(struct line *a, int n, struct line *b, int m, int *c)
4973ad3fb45Sjoris {
4983ad3fb45Sjoris 	int i, j;
4993ad3fb45Sjoris 
5003ad3fb45Sjoris 	i = j = 1;
5013ad3fb45Sjoris 	while (i <= n && j <= m) {
5023ad3fb45Sjoris 		if (a[i].value < b[j].value)
5033ad3fb45Sjoris 			a[i++].value = 0;
5043ad3fb45Sjoris 		else if (a[i].value == b[j].value)
5053ad3fb45Sjoris 			a[i++].value = j;
5063ad3fb45Sjoris 		else
5073ad3fb45Sjoris 			j++;
5083ad3fb45Sjoris 	}
5093ad3fb45Sjoris 	while (i <= n)
5103ad3fb45Sjoris 		a[i++].value = 0;
5113ad3fb45Sjoris 	b[m + 1].value = 0;
5123ad3fb45Sjoris 	j = 0;
5133ad3fb45Sjoris 	while (++j <= m) {
5143ad3fb45Sjoris 		c[j] = -b[j].serial;
5153ad3fb45Sjoris 		while (b[j + 1].value == b[j].value) {
5163ad3fb45Sjoris 			j++;
5173ad3fb45Sjoris 			c[j] = b[j].serial;
5183ad3fb45Sjoris 		}
5193ad3fb45Sjoris 	}
5203ad3fb45Sjoris 	c[j] = -1;
5213ad3fb45Sjoris }
5223ad3fb45Sjoris 
5233ad3fb45Sjoris /* Code taken from ping.c */
5243ad3fb45Sjoris static int
isqrt(int n)5253ad3fb45Sjoris isqrt(int n)
5263ad3fb45Sjoris {
5273ad3fb45Sjoris 	int y, x = 1;
5283ad3fb45Sjoris 
5293ad3fb45Sjoris 	if (n == 0)
5303ad3fb45Sjoris 		return (0);
5313ad3fb45Sjoris 
5323ad3fb45Sjoris 	do { /* newton was a stinker */
5333ad3fb45Sjoris 		y = x;
5343ad3fb45Sjoris 		x = n / x;
5353ad3fb45Sjoris 		x += y;
5363ad3fb45Sjoris 		x /= 2;
537eea21b3cSray 	} while ((x - y) > 1 || (x - y) < -1);
5383ad3fb45Sjoris 
5393ad3fb45Sjoris 	return (x);
5403ad3fb45Sjoris }
5413ad3fb45Sjoris 
5423ad3fb45Sjoris static int
stone(int * a,int n,int * b,int * c,int flags)543219c50abSray stone(int *a, int n, int *b, int *c, int flags)
5443ad3fb45Sjoris {
5453ad3fb45Sjoris 	int i, k, y, j, l;
546df890a16Snicm 	int oldc, tc, oldl, sq;
547df890a16Snicm 	u_int numtries, bound;
5483ad3fb45Sjoris 
549df890a16Snicm 	if (flags & D_MINIMAL)
550df890a16Snicm 		bound = UINT_MAX;
551df890a16Snicm 	else {
552df890a16Snicm 		sq = isqrt(n);
553b9fc9a72Sderaadt 		bound = MAXIMUM(256, sq);
554df890a16Snicm 	}
5553ad3fb45Sjoris 
5563ad3fb45Sjoris 	k = 0;
557eea21b3cSray 	c[0] = newcand(0, 0, 0);
5583ad3fb45Sjoris 	for (i = 1; i <= n; i++) {
5593ad3fb45Sjoris 		j = a[i];
5603ad3fb45Sjoris 		if (j == 0)
5613ad3fb45Sjoris 			continue;
5623ad3fb45Sjoris 		y = -b[j];
5633ad3fb45Sjoris 		oldl = 0;
5643ad3fb45Sjoris 		oldc = c[0];
5653ad3fb45Sjoris 		numtries = 0;
5663ad3fb45Sjoris 		do {
5673ad3fb45Sjoris 			if (y <= clist[oldc].y)
5683ad3fb45Sjoris 				continue;
5693ad3fb45Sjoris 			l = search(c, k, y);
5703ad3fb45Sjoris 			if (l != oldl + 1)
5713ad3fb45Sjoris 				oldc = c[l - 1];
5723ad3fb45Sjoris 			if (l <= k) {
5733ad3fb45Sjoris 				if (clist[c[l]].y <= y)
5743ad3fb45Sjoris 					continue;
5753ad3fb45Sjoris 				tc = c[l];
576eea21b3cSray 				c[l] = newcand(i, y, oldc);
5773ad3fb45Sjoris 				oldc = tc;
5783ad3fb45Sjoris 				oldl = l;
5793ad3fb45Sjoris 				numtries++;
5803ad3fb45Sjoris 			} else {
581eea21b3cSray 				c[l] = newcand(i, y, oldc);
5823ad3fb45Sjoris 				k++;
5833ad3fb45Sjoris 				break;
5843ad3fb45Sjoris 			}
5853ad3fb45Sjoris 		} while ((y = b[++j]) > 0 && numtries < bound);
5863ad3fb45Sjoris 	}
5873ad3fb45Sjoris 	return (k);
5883ad3fb45Sjoris }
5893ad3fb45Sjoris 
5903ad3fb45Sjoris static int
newcand(int x,int y,int pred)5913ad3fb45Sjoris newcand(int x, int y, int pred)
5923ad3fb45Sjoris {
59380566be2Sray 	struct cand *q;
5943ad3fb45Sjoris 
5953ad3fb45Sjoris 	if (clen == clistlen) {
596f74aa433Sray 		clistlen = clistlen * 11 / 10;
597caa2ffb0Sderaadt 		clist = xreallocarray(clist, clistlen, sizeof(*clist));
5983ad3fb45Sjoris 	}
5993ad3fb45Sjoris 	q = clist + clen;
6003ad3fb45Sjoris 	q->x = x;
6013ad3fb45Sjoris 	q->y = y;
6023ad3fb45Sjoris 	q->pred = pred;
6033ad3fb45Sjoris 	return (clen++);
6043ad3fb45Sjoris }
6053ad3fb45Sjoris 
6063ad3fb45Sjoris static int
search(int * c,int k,int y)6073ad3fb45Sjoris search(int *c, int k, int y)
6083ad3fb45Sjoris {
6093ad3fb45Sjoris 	int i, j, l, t;
6103ad3fb45Sjoris 
6113ad3fb45Sjoris 	if (clist[c[k]].y < y)	/* quick look for typical case */
6123ad3fb45Sjoris 		return (k + 1);
6133ad3fb45Sjoris 	i = 0;
6143ad3fb45Sjoris 	j = k + 1;
6153ad3fb45Sjoris 	for (;;) {
6163ad3fb45Sjoris 		l = (i + j) / 2;
6173ad3fb45Sjoris 		if (l <= i)
6183ad3fb45Sjoris 			break;
6193ad3fb45Sjoris 		t = clist[c[l]].y;
6203ad3fb45Sjoris 		if (t > y)
6213ad3fb45Sjoris 			j = l;
6223ad3fb45Sjoris 		else if (t < y)
6233ad3fb45Sjoris 			i = l;
6243ad3fb45Sjoris 		else
6253ad3fb45Sjoris 			return (l);
6263ad3fb45Sjoris 	}
6273ad3fb45Sjoris 	return (l + 1);
6283ad3fb45Sjoris }
6293ad3fb45Sjoris 
6303ad3fb45Sjoris static void
unravel(int p)6313ad3fb45Sjoris unravel(int p)
6323ad3fb45Sjoris {
6333ad3fb45Sjoris 	struct cand *q;
6343ad3fb45Sjoris 	int i;
6353ad3fb45Sjoris 
636eea21b3cSray 	for (i = 0; i <= len[0]; i++)
6373ad3fb45Sjoris 		J[i] = i <= pref ? i :
638eea21b3cSray 		    i > len[0] - suff ? i + len[1] - len[0] : 0;
6393ad3fb45Sjoris 	for (q = clist + p; q->y != 0; q = clist + q->pred)
6403ad3fb45Sjoris 		J[q->x + pref] = q->y + pref;
6413ad3fb45Sjoris }
6423ad3fb45Sjoris 
6433ad3fb45Sjoris /*
6443ad3fb45Sjoris  * Check does double duty:
6453ad3fb45Sjoris  *  1.	ferret out any fortuitous correspondences due
6463ad3fb45Sjoris  *	to confounding by hashing (which result in "jackpot")
6473ad3fb45Sjoris  *  2.  collect random access indexes to the two files
6483ad3fb45Sjoris  */
6493ad3fb45Sjoris static void
check(FILE * f1,FILE * f2,int flags)650219c50abSray check(FILE *f1, FILE *f2, int flags)
6513ad3fb45Sjoris {
6523ad3fb45Sjoris 	int i, j, jackpot, c, d;
6533ad3fb45Sjoris 	long ctold, ctnew;
6543ad3fb45Sjoris 
6553ad3fb45Sjoris 	rewind(f1);
6563ad3fb45Sjoris 	rewind(f2);
6573ad3fb45Sjoris 	j = 1;
6583ad3fb45Sjoris 	ixold[0] = ixnew[0] = 0;
6593ad3fb45Sjoris 	jackpot = 0;
6603ad3fb45Sjoris 	ctold = ctnew = 0;
661eea21b3cSray 	for (i = 1; i <= len[0]; i++) {
6623ad3fb45Sjoris 		if (J[i] == 0) {
6633ad3fb45Sjoris 			ixold[i] = ctold += skipline(f1);
6643ad3fb45Sjoris 			continue;
6653ad3fb45Sjoris 		}
6663ad3fb45Sjoris 		while (j < J[i]) {
6673ad3fb45Sjoris 			ixnew[j] = ctnew += skipline(f2);
6683ad3fb45Sjoris 			j++;
6693ad3fb45Sjoris 		}
670219c50abSray 		if (flags & (D_FOLDBLANKS|D_IGNOREBLANKS|D_IGNORECASE)) {
6713ad3fb45Sjoris 			for (;;) {
6723ad3fb45Sjoris 				c = getc(f1);
6733ad3fb45Sjoris 				d = getc(f2);
6743ad3fb45Sjoris 				/*
6753ad3fb45Sjoris 				 * GNU diff ignores a missing newline
676eea21b3cSray 				 * in one file for -b or -w.
6773ad3fb45Sjoris 				 */
678219c50abSray 				if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) &&
6793ad3fb45Sjoris 				    ((c == EOF && d == '\n') ||
6803ad3fb45Sjoris 				    (c == '\n' && d == EOF))) {
6813ad3fb45Sjoris 					break;
6823ad3fb45Sjoris 				}
6833ad3fb45Sjoris 				ctold++;
6843ad3fb45Sjoris 				ctnew++;
685219c50abSray 				if ((flags & D_FOLDBLANKS) && isspace(c) &&
686219c50abSray 				    isspace(d)) {
6873ad3fb45Sjoris 					do {
6883ad3fb45Sjoris 						if (c == '\n')
6893ad3fb45Sjoris 							break;
6903ad3fb45Sjoris 						ctold++;
6913ad3fb45Sjoris 					} while (isspace(c = getc(f1)));
6923ad3fb45Sjoris 					do {
6933ad3fb45Sjoris 						if (d == '\n')
6943ad3fb45Sjoris 							break;
6953ad3fb45Sjoris 						ctnew++;
6963ad3fb45Sjoris 					} while (isspace(d = getc(f2)));
697219c50abSray 				} else if ((flags & D_IGNOREBLANKS)) {
6983ad3fb45Sjoris 					while (isspace(c) && c != '\n') {
6993ad3fb45Sjoris 						c = getc(f1);
7003ad3fb45Sjoris 						ctold++;
7013ad3fb45Sjoris 					}
7023ad3fb45Sjoris 					while (isspace(d) && d != '\n') {
7033ad3fb45Sjoris 						d = getc(f2);
7043ad3fb45Sjoris 						ctnew++;
7053ad3fb45Sjoris 					}
7063ad3fb45Sjoris 				}
7073ad3fb45Sjoris 				if (chrtran[c] != chrtran[d]) {
7083ad3fb45Sjoris 					jackpot++;
7093ad3fb45Sjoris 					J[i] = 0;
7103ad3fb45Sjoris 					if (c != '\n' && c != EOF)
7113ad3fb45Sjoris 						ctold += skipline(f1);
7123ad3fb45Sjoris 					if (d != '\n' && c != EOF)
7133ad3fb45Sjoris 						ctnew += skipline(f2);
7143ad3fb45Sjoris 					break;
7153ad3fb45Sjoris 				}
7163ad3fb45Sjoris 				if (c == '\n' || c == EOF)
7173ad3fb45Sjoris 					break;
7183ad3fb45Sjoris 			}
7193ad3fb45Sjoris 		} else {
7203ad3fb45Sjoris 			for (;;) {
7213ad3fb45Sjoris 				ctold++;
7223ad3fb45Sjoris 				ctnew++;
7233ad3fb45Sjoris 				if ((c = getc(f1)) != (d = getc(f2))) {
7243ad3fb45Sjoris 					/* jackpot++; */
7253ad3fb45Sjoris 					J[i] = 0;
7263ad3fb45Sjoris 					if (c != '\n' && c != EOF)
7273ad3fb45Sjoris 						ctold += skipline(f1);
7283ad3fb45Sjoris 					if (d != '\n' && c != EOF)
7293ad3fb45Sjoris 						ctnew += skipline(f2);
7303ad3fb45Sjoris 					break;
7313ad3fb45Sjoris 				}
7323ad3fb45Sjoris 				if (c == '\n' || c == EOF)
7333ad3fb45Sjoris 					break;
7343ad3fb45Sjoris 			}
7353ad3fb45Sjoris 		}
7363ad3fb45Sjoris 		ixold[i] = ctold;
7373ad3fb45Sjoris 		ixnew[j] = ctnew;
7383ad3fb45Sjoris 		j++;
7393ad3fb45Sjoris 	}
740eea21b3cSray 	for (; j <= len[1]; j++)
7413ad3fb45Sjoris 		ixnew[j] = ctnew += skipline(f2);
7423ad3fb45Sjoris 	/*
743eea21b3cSray 	 * if (jackpot)
744eea21b3cSray 	 *	fprintf(stderr, "jackpot\n");
7453ad3fb45Sjoris 	 */
7463ad3fb45Sjoris }
7473ad3fb45Sjoris 
7483ad3fb45Sjoris /* shellsort CACM #201 */
7493ad3fb45Sjoris static void
sort(struct line * a,int n)7503ad3fb45Sjoris sort(struct line *a, int n)
7513ad3fb45Sjoris {
7523ad3fb45Sjoris 	struct line *ai, *aim, w;
7533ad3fb45Sjoris 	int j, m = 0, k;
7543ad3fb45Sjoris 
7553ad3fb45Sjoris 	if (n == 0)
7563ad3fb45Sjoris 		return;
7573ad3fb45Sjoris 	for (j = 1; j <= n; j *= 2)
7583ad3fb45Sjoris 		m = 2 * j - 1;
7593ad3fb45Sjoris 	for (m /= 2; m != 0; m /= 2) {
7603ad3fb45Sjoris 		k = n - m;
7613ad3fb45Sjoris 		for (j = 1; j <= k; j++) {
7623ad3fb45Sjoris 			for (ai = &a[j]; ai > a; ai -= m) {
7633ad3fb45Sjoris 				aim = &ai[m];
7643ad3fb45Sjoris 				if (aim < ai)
7653ad3fb45Sjoris 					break;	/* wraparound */
7663ad3fb45Sjoris 				if (aim->value > ai[0].value ||
7673ad3fb45Sjoris 				    (aim->value == ai[0].value &&
7683ad3fb45Sjoris 					aim->serial > ai[0].serial))
7693ad3fb45Sjoris 					break;
7703ad3fb45Sjoris 				w.value = ai[0].value;
7713ad3fb45Sjoris 				ai[0].value = aim->value;
7723ad3fb45Sjoris 				aim->value = w.value;
7733ad3fb45Sjoris 				w.serial = ai[0].serial;
7743ad3fb45Sjoris 				ai[0].serial = aim->serial;
7753ad3fb45Sjoris 				aim->serial = w.serial;
7763ad3fb45Sjoris 			}
7773ad3fb45Sjoris 		}
7783ad3fb45Sjoris 	}
7793ad3fb45Sjoris }
7803ad3fb45Sjoris 
7813ad3fb45Sjoris static void
unsort(struct line * f,int l,int * b)7823ad3fb45Sjoris unsort(struct line *f, int l, int *b)
7833ad3fb45Sjoris {
7843ad3fb45Sjoris 	int *a, i;
7853ad3fb45Sjoris 
7863ad3fb45Sjoris 	a = xcalloc(l + 1, sizeof(*a));
7873ad3fb45Sjoris 	for (i = 1; i <= l; i++)
7883ad3fb45Sjoris 		a[f[i].serial] = f[i].value;
7893ad3fb45Sjoris 	for (i = 1; i <= l; i++)
7903ad3fb45Sjoris 		b[i] = a[i];
791397ddb8aSnicm 	free(a);
7923ad3fb45Sjoris }
7933ad3fb45Sjoris 
7943ad3fb45Sjoris static int
skipline(FILE * f)7953ad3fb45Sjoris skipline(FILE *f)
7963ad3fb45Sjoris {
7973ad3fb45Sjoris 	int i, c;
7983ad3fb45Sjoris 
7993ad3fb45Sjoris 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
8003ad3fb45Sjoris 		continue;
8013ad3fb45Sjoris 	return (i);
8023ad3fb45Sjoris }
8033ad3fb45Sjoris 
8043ad3fb45Sjoris static void
output(FILE * f1,FILE * f2,int flags)805219c50abSray output(FILE *f1, FILE *f2, int flags)
8063ad3fb45Sjoris {
8073ad3fb45Sjoris 	int m, i0, i1, j0, j1;
8083ad3fb45Sjoris 
8093ad3fb45Sjoris 	rewind(f1);
8103ad3fb45Sjoris 	rewind(f2);
811eea21b3cSray 	m = len[0];
8123ad3fb45Sjoris 	J[0] = 0;
813eea21b3cSray 	J[m + 1] = len[1] + 1;
8143ad3fb45Sjoris 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
8153ad3fb45Sjoris 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
8163ad3fb45Sjoris 			i0++;
8173ad3fb45Sjoris 		j0 = J[i0 - 1] + 1;
8183ad3fb45Sjoris 		i1 = i0 - 1;
8193ad3fb45Sjoris 		while (i1 < m && J[i1 + 1] == 0)
8203ad3fb45Sjoris 			i1++;
8213ad3fb45Sjoris 		j1 = J[i1 + 1] - 1;
8223ad3fb45Sjoris 		J[i1] = j1;
823219c50abSray 		change(f1, f2, i0, i1, j0, j1, flags);
8243ad3fb45Sjoris 	}
8253ad3fb45Sjoris 	if (m == 0)
826219c50abSray 		change(f1, f2, 1, 0, 1, len[1], flags);
8273ad3fb45Sjoris 	if (diff_format == D_IFDEF) {
8283ad3fb45Sjoris 		for (;;) {
8293ad3fb45Sjoris #define	c i0
8303ad3fb45Sjoris 			if ((c = getc(f1)) == EOF)
8313ad3fb45Sjoris 				return;
8323ad3fb45Sjoris 			diff_output("%c", c);
8333ad3fb45Sjoris 		}
8343ad3fb45Sjoris #undef c
8353ad3fb45Sjoris 	}
8363ad3fb45Sjoris 	if (anychange != 0) {
8373ad3fb45Sjoris 		if (diff_format == D_CONTEXT)
838219c50abSray 			dump_context_vec(f1, f2, flags);
8393ad3fb45Sjoris 		else if (diff_format == D_UNIFIED)
840219c50abSray 			dump_unified_vec(f1, f2, flags);
8413ad3fb45Sjoris 	}
8423ad3fb45Sjoris }
8433ad3fb45Sjoris 
844a304c0f4Sray static void
range(int a,int b,char * separator)8453ad3fb45Sjoris range(int a, int b, char *separator)
8463ad3fb45Sjoris {
8473ad3fb45Sjoris 	diff_output("%d", a > b ? b : a);
8483ad3fb45Sjoris 	if (a < b)
8493ad3fb45Sjoris 		diff_output("%s%d", separator, b);
8503ad3fb45Sjoris }
8513ad3fb45Sjoris 
852a304c0f4Sray static void
uni_range(int a,int b)8533ad3fb45Sjoris uni_range(int a, int b)
8543ad3fb45Sjoris {
8553ad3fb45Sjoris 	if (a < b)
8563ad3fb45Sjoris 		diff_output("%d,%d", a, b - a + 1);
8573ad3fb45Sjoris 	else if (a == b)
8583ad3fb45Sjoris 		diff_output("%d", b);
8593ad3fb45Sjoris 	else
8603ad3fb45Sjoris 		diff_output("%d,0", b);
8613ad3fb45Sjoris }
8623ad3fb45Sjoris 
8633ad3fb45Sjoris static char *
preadline(int fd,size_t rlen,off_t off)8643ad3fb45Sjoris preadline(int fd, size_t rlen, off_t off)
8653ad3fb45Sjoris {
8663ad3fb45Sjoris 	char *line;
8673ad3fb45Sjoris 	ssize_t nr;
8683ad3fb45Sjoris 
8693ad3fb45Sjoris 	line = xmalloc(rlen + 1);
870*3aaa63ebSderaadt 	if ((nr = pread(fd, line, rlen, off)) == -1)
8717a6efebcSray 		fatal("preadline: %s", strerror(errno));
8723ad3fb45Sjoris 	line[nr] = '\0';
8733ad3fb45Sjoris 	return (line);
8743ad3fb45Sjoris }
8753ad3fb45Sjoris 
8763ad3fb45Sjoris static int
ignoreline(char * line)8773ad3fb45Sjoris ignoreline(char *line)
8783ad3fb45Sjoris {
8793ad3fb45Sjoris 	int ret;
8803ad3fb45Sjoris 
881a304c0f4Sray 	ret = regexec(&ignore_re, line, 0, NULL, 0);
882397ddb8aSnicm 	free(line);
8833ad3fb45Sjoris 	return (ret == 0);	/* if it matched, it should be ignored. */
8843ad3fb45Sjoris }
8853ad3fb45Sjoris 
886fd660bf2Stobias static void
diff_head(void)887fd660bf2Stobias diff_head(void)
888fd660bf2Stobias {
889fd660bf2Stobias 	char buf[64];
8906534056aStobias 	struct tm t;
891fd660bf2Stobias 	time_t curr_time;
892fd660bf2Stobias 
893fd660bf2Stobias 	if (diff_rev1 != NULL) {
8946534056aStobias 		gmtime_r(&stb1.st_mtime, &t);
895fd660bf2Stobias 	} else {
896fd660bf2Stobias 		time(&curr_time);
8976534056aStobias 		localtime_r(&curr_time, &t);
898fd660bf2Stobias 	}
899fd660bf2Stobias 
9006534056aStobias 	(void)strftime(buf, sizeof(buf), "%b %G %H:%M:%S -0000", &t);
901d00c0f9eStobias 	diff_output("%s %s	%d %s", diff_format == D_CONTEXT ?
9026534056aStobias 	    "***" : "---", diff_file1, t.tm_mday, buf);
903fd660bf2Stobias 
904fd660bf2Stobias 	if (diff_rev1 != NULL) {
905fd660bf2Stobias 		rcsnum_tostr(diff_rev1, buf, sizeof(buf));
906fd660bf2Stobias 		diff_output("\t%s", buf);
907fd660bf2Stobias 	}
908fd660bf2Stobias 
909fd660bf2Stobias 	diff_output("\n");
910fd660bf2Stobias 
9116534056aStobias 	gmtime_r(&stb2.st_mtime, &t);
912fd660bf2Stobias 
9136534056aStobias 	(void)strftime(buf, sizeof(buf), "%b %G %H:%M:%S -0000", &t);
914d00c0f9eStobias 	diff_output("%s %s	%d %s", diff_format == D_CONTEXT ?
9156534056aStobias 	    "---" : "+++", diff_file2, t.tm_mday, buf);
916fd660bf2Stobias 
917fd660bf2Stobias 	if (diff_rev2 != NULL) {
918fd660bf2Stobias 		rcsnum_tostr(diff_rev2, buf, sizeof(buf));
919fd660bf2Stobias 		diff_output("\t%s", buf);
920fd660bf2Stobias 	}
921fd660bf2Stobias 
922fd660bf2Stobias 	diff_output("\n");
923fd660bf2Stobias }
924fd660bf2Stobias 
925fd660bf2Stobias static void
rdiff_head(void)926fd660bf2Stobias rdiff_head(void)
927fd660bf2Stobias {
928fd660bf2Stobias 	char buf[64];
9296534056aStobias 	struct tm t;
930fd660bf2Stobias 	time_t curr_time;
931fd660bf2Stobias 
932fd660bf2Stobias 	diff_output("%s ", diff_format == D_CONTEXT ? "***" : "---");
933fd660bf2Stobias 
934fd660bf2Stobias 	if (diff_rev1 == NULL) {
935fd660bf2Stobias 		diff_output("%s", CVS_PATH_DEVNULL);
9366534056aStobias 		gmtime_r(&stb1.st_atime, &t);
937fd660bf2Stobias 	} else {
938fd660bf2Stobias 		rcsnum_tostr(diff_rev1, buf, sizeof(buf));
939be756b91Stobias 		diff_output("%s:%s", diff_file1, buf);
94045ea4f19Stobias 		localtime_r(&stb1.st_mtime, &t);
941fd660bf2Stobias 	}
942fd660bf2Stobias 
9436534056aStobias 	(void)strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %G", &t);
944fd660bf2Stobias 	diff_output("\t%s\n", buf);
945fd660bf2Stobias 
946fd660bf2Stobias 	if (diff_rev2 != NULL) {
9476534056aStobias 		localtime_r(&stb2.st_mtime, &t);
948fd660bf2Stobias 	} else {
949fd660bf2Stobias 		time(&curr_time);
9506534056aStobias 		localtime_r(&curr_time, &t);
951fd660bf2Stobias 	}
952fd660bf2Stobias 
9536534056aStobias 	(void)strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %G", &t);
954fd660bf2Stobias 
955fd660bf2Stobias 	diff_output("%s %s	%s\n", diff_format == D_CONTEXT ? "---" : "+++",
956be756b91Stobias 	    diff_file2, buf);
957fd660bf2Stobias }
958fd660bf2Stobias 
9593ad3fb45Sjoris /*
9603ad3fb45Sjoris  * Indicate that there is a difference between lines a and b of the from file
9613ad3fb45Sjoris  * to get to lines c to d of the to file.  If a is greater then b then there
9623ad3fb45Sjoris  * are no lines in the from file involved and this means that there were
9633ad3fb45Sjoris  * lines appended (beginning at b).  If c is greater than d then there are
9643ad3fb45Sjoris  * lines missing from the to file.
9653ad3fb45Sjoris  */
9663ad3fb45Sjoris static void
change(FILE * f1,FILE * f2,int a,int b,int c,int d,int flags)967219c50abSray change(FILE *f1, FILE *f2, int a, int b, int c, int d, int flags)
9683ad3fb45Sjoris {
9693ad3fb45Sjoris 	static size_t max_context = 64;
970eea21b3cSray 	int i;
9713ad3fb45Sjoris 
9723ad3fb45Sjoris 	if (diff_format != D_IFDEF && a > b && c > d)
9733ad3fb45Sjoris 		return;
9743ad3fb45Sjoris 	if (ignore_pats != NULL) {
9753ad3fb45Sjoris 		char *line;
9763ad3fb45Sjoris 		/*
9773ad3fb45Sjoris 		 * All lines in the change, insert, or delete must
9783ad3fb45Sjoris 		 * match an ignore pattern for the change to be
9793ad3fb45Sjoris 		 * ignored.
9803ad3fb45Sjoris 		 */
9813ad3fb45Sjoris 		if (a <= b) {		/* Changes and deletes. */
9823ad3fb45Sjoris 			for (i = a; i <= b; i++) {
9833ad3fb45Sjoris 				line = preadline(fileno(f1),
9843ad3fb45Sjoris 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
9853ad3fb45Sjoris 				if (!ignoreline(line))
9863ad3fb45Sjoris 					goto proceed;
9873ad3fb45Sjoris 			}
9883ad3fb45Sjoris 		}
9893ad3fb45Sjoris 		if (a > b || c <= d) {	/* Changes and inserts. */
9903ad3fb45Sjoris 			for (i = c; i <= d; i++) {
9913ad3fb45Sjoris 				line = preadline(fileno(f2),
9923ad3fb45Sjoris 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
9933ad3fb45Sjoris 				if (!ignoreline(line))
9943ad3fb45Sjoris 					goto proceed;
9953ad3fb45Sjoris 			}
9963ad3fb45Sjoris 		}
9973ad3fb45Sjoris 		return;
9983ad3fb45Sjoris 	}
9993ad3fb45Sjoris proceed:
10003ad3fb45Sjoris 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
10013ad3fb45Sjoris 		/*
10023ad3fb45Sjoris 		 * Allocate change records as needed.
10033ad3fb45Sjoris 		 */
10043ad3fb45Sjoris 		if (context_vec_ptr == context_vec_end - 1) {
10053ad3fb45Sjoris 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
10063ad3fb45Sjoris 			max_context <<= 1;
1007caa2ffb0Sderaadt 			context_vec_start = xreallocarray(context_vec_start,
100880566be2Sray 			    max_context, sizeof(*context_vec_start));
10093ad3fb45Sjoris 			context_vec_end = context_vec_start + max_context;
10103ad3fb45Sjoris 			context_vec_ptr = context_vec_start + offset;
10113ad3fb45Sjoris 		}
10123ad3fb45Sjoris 		if (anychange == 0) {
10133ad3fb45Sjoris 			/*
10143ad3fb45Sjoris 			 * Print the context/unidiff header first time through.
10153ad3fb45Sjoris 			 */
1016fd660bf2Stobias 			if (cvs_cmdop == CVS_OP_RDIFF)
1017fd660bf2Stobias 				rdiff_head();
1018fd660bf2Stobias 			else
1019fd660bf2Stobias 				diff_head();
10203ad3fb45Sjoris 
10213ad3fb45Sjoris 			anychange = 1;
102272026f1aSnicm 		} else if (a > context_vec_ptr->b + (2 * diff_context) + 1 &&
102372026f1aSnicm 		    c > context_vec_ptr->d + (2 * diff_context) + 1) {
10243ad3fb45Sjoris 			/*
10253ad3fb45Sjoris 			 * If this change is more than 'context' lines from the
10263ad3fb45Sjoris 			 * previous change, dump the record and reset it.
10273ad3fb45Sjoris 			 */
10283ad3fb45Sjoris 			if (diff_format == D_CONTEXT)
1029219c50abSray 				dump_context_vec(f1, f2, flags);
10303ad3fb45Sjoris 			else
1031219c50abSray 				dump_unified_vec(f1, f2, flags);
10323ad3fb45Sjoris 		}
10333ad3fb45Sjoris 		context_vec_ptr++;
10343ad3fb45Sjoris 		context_vec_ptr->a = a;
10353ad3fb45Sjoris 		context_vec_ptr->b = b;
10363ad3fb45Sjoris 		context_vec_ptr->c = c;
10373ad3fb45Sjoris 		context_vec_ptr->d = d;
10383ad3fb45Sjoris 		return;
10393ad3fb45Sjoris 	}
10403ad3fb45Sjoris 	if (anychange == 0)
10413ad3fb45Sjoris 		anychange = 1;
10423ad3fb45Sjoris 	switch (diff_format) {
10433ad3fb45Sjoris 	case D_BRIEF:
10443ad3fb45Sjoris 		return;
10453ad3fb45Sjoris 	case D_NORMAL:
10463ad3fb45Sjoris 		range(a, b, ",");
10473ad3fb45Sjoris 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
10483ad3fb45Sjoris 		if (diff_format == D_NORMAL)
10493ad3fb45Sjoris 			range(c, d, ",");
10503ad3fb45Sjoris 		diff_output("\n");
10513ad3fb45Sjoris 		break;
10523ad3fb45Sjoris 	case D_RCSDIFF:
10533ad3fb45Sjoris 		if (a > b)
10543ad3fb45Sjoris 			diff_output("a%d %d\n", b, d - c + 1);
10553ad3fb45Sjoris 		else {
10563ad3fb45Sjoris 			diff_output("d%d %d\n", a, b - a + 1);
10573ad3fb45Sjoris 
10583ad3fb45Sjoris 			if (!(c > d))	/* add changed lines */
10593ad3fb45Sjoris 				diff_output("a%d %d\n", b, d - c + 1);
10603ad3fb45Sjoris 		}
10613ad3fb45Sjoris 		break;
10623ad3fb45Sjoris 	}
10633ad3fb45Sjoris 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
1064219c50abSray 		fetch(ixold, a, b, f1, '<', 1, flags);
10653ad3fb45Sjoris 		if (a <= b && c <= d && diff_format == D_NORMAL)
10663ad3fb45Sjoris 			diff_output("---\n");
10673ad3fb45Sjoris 	}
1068219c50abSray 	fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0, flags);
10693ad3fb45Sjoris 	if (inifdef) {
10703ad3fb45Sjoris 		diff_output("#endif /* %s */\n", ifdefname);
10713ad3fb45Sjoris 		inifdef = 0;
10723ad3fb45Sjoris 	}
10733ad3fb45Sjoris }
10743ad3fb45Sjoris 
10753ad3fb45Sjoris static void
fetch(long * f,int a,int b,FILE * lb,int ch,int oldfile,int flags)1076219c50abSray fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags)
10773ad3fb45Sjoris {
10783ad3fb45Sjoris 	long j, nc;
10793ad3fb45Sjoris 	int i, c, col;
10803ad3fb45Sjoris 
10813ad3fb45Sjoris 	/*
10823ad3fb45Sjoris 	 * When doing #ifdef's, copy down to current line
10833ad3fb45Sjoris 	 * if this is the first file, so that stuff makes it to output.
10843ad3fb45Sjoris 	 */
10853ad3fb45Sjoris 	if (diff_format == D_IFDEF && oldfile) {
10863ad3fb45Sjoris 		long curpos = ftell(lb);
10873ad3fb45Sjoris 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
10883ad3fb45Sjoris 		nc = f[a > b ? b : a - 1] - curpos;
10893ad3fb45Sjoris 		for (i = 0; i < nc; i++)
10903ad3fb45Sjoris 			diff_output("%c", getc(lb));
10913ad3fb45Sjoris 	}
10923ad3fb45Sjoris 	if (a > b)
10933ad3fb45Sjoris 		return;
10943ad3fb45Sjoris 	if (diff_format == D_IFDEF) {
10953ad3fb45Sjoris 		if (inifdef) {
10963ad3fb45Sjoris 			diff_output("#else /* %s%s */\n",
10973ad3fb45Sjoris 			    oldfile == 1 ? "!" : "", ifdefname);
10983ad3fb45Sjoris 		} else {
10993ad3fb45Sjoris 			if (oldfile)
11003ad3fb45Sjoris 				diff_output("#ifndef %s\n", ifdefname);
11013ad3fb45Sjoris 			else
11023ad3fb45Sjoris 				diff_output("#ifdef %s\n", ifdefname);
11033ad3fb45Sjoris 		}
11043ad3fb45Sjoris 		inifdef = 1 + oldfile;
11053ad3fb45Sjoris 	}
11063ad3fb45Sjoris 	for (i = a; i <= b; i++) {
11073ad3fb45Sjoris 		fseek(lb, f[i - 1], SEEK_SET);
11083ad3fb45Sjoris 		nc = f[i] - f[i - 1];
11093ad3fb45Sjoris 		if (diff_format != D_IFDEF && ch != '\0') {
11103ad3fb45Sjoris 			diff_output("%c", ch);
11113ad3fb45Sjoris 			if (Tflag == 1 && (diff_format == D_NORMAL ||
11123ad3fb45Sjoris 			    diff_format == D_CONTEXT ||
11133ad3fb45Sjoris 			    diff_format == D_UNIFIED))
11143ad3fb45Sjoris 				diff_output("\t");
11153ad3fb45Sjoris 			else if (diff_format != D_UNIFIED)
11163ad3fb45Sjoris 				diff_output(" ");
11173ad3fb45Sjoris 		}
11183ad3fb45Sjoris 		col = 0;
11193ad3fb45Sjoris 		for (j = 0; j < nc; j++) {
11203ad3fb45Sjoris 			if ((c = getc(lb)) == EOF) {
11213ad3fb45Sjoris 				if (diff_format == D_RCSDIFF)
11223ad3fb45Sjoris 					cvs_log(LP_ERR,
11233ad3fb45Sjoris 					    "No newline at end of file");
11243ad3fb45Sjoris 				else
11253ad3fb45Sjoris 					diff_output("\n\\ No newline at end of "
112625d5ee6bSjoris 					    "file\n");
11273ad3fb45Sjoris 				return;
11283ad3fb45Sjoris 			}
1129219c50abSray 			if (c == '\t' && (flags & D_EXPANDTABS)) {
11303ad3fb45Sjoris 				do {
11313ad3fb45Sjoris 					diff_output(" ");
11323ad3fb45Sjoris 				} while (++col & 7);
11333ad3fb45Sjoris 			} else {
11343ad3fb45Sjoris 				diff_output("%c", c);
11353ad3fb45Sjoris 				col++;
11363ad3fb45Sjoris 			}
11373ad3fb45Sjoris 		}
11383ad3fb45Sjoris 	}
11393ad3fb45Sjoris }
11403ad3fb45Sjoris 
11413ad3fb45Sjoris /*
11423ad3fb45Sjoris  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
11433ad3fb45Sjoris  */
11443ad3fb45Sjoris static int
readhash(FILE * f,int flags)1145219c50abSray readhash(FILE *f, int flags)
11463ad3fb45Sjoris {
11473ad3fb45Sjoris 	int i, t, space;
11483ad3fb45Sjoris 	int sum;
11493ad3fb45Sjoris 
11503ad3fb45Sjoris 	sum = 1;
11513ad3fb45Sjoris 	space = 0;
1152219c50abSray 	if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) == 0) {
1153219c50abSray 		if (flags & D_IGNORECASE)
11543ad3fb45Sjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
11553ad3fb45Sjoris 				if (t == EOF) {
11563ad3fb45Sjoris 					if (i == 0)
11573ad3fb45Sjoris 						return (0);
11583ad3fb45Sjoris 					break;
11593ad3fb45Sjoris 				}
11603ad3fb45Sjoris 				sum = sum * 127 + chrtran[t];
11613ad3fb45Sjoris 			}
11623ad3fb45Sjoris 		else
11633ad3fb45Sjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
11643ad3fb45Sjoris 				if (t == EOF) {
11653ad3fb45Sjoris 					if (i == 0)
11663ad3fb45Sjoris 						return (0);
11673ad3fb45Sjoris 					break;
11683ad3fb45Sjoris 				}
11693ad3fb45Sjoris 				sum = sum * 127 + t;
11703ad3fb45Sjoris 			}
11713ad3fb45Sjoris 	} else {
11723ad3fb45Sjoris 		for (i = 0;;) {
11733ad3fb45Sjoris 			switch (t = getc(f)) {
11743ad3fb45Sjoris 			case '\t':
1175eea21b3cSray 			case '\r':
1176eea21b3cSray 			case '\v':
1177eea21b3cSray 			case '\f':
11783ad3fb45Sjoris 			case ' ':
11793ad3fb45Sjoris 				space++;
11803ad3fb45Sjoris 				continue;
11813ad3fb45Sjoris 			default:
1182219c50abSray 				if (space && (flags & D_IGNOREBLANKS) == 0) {
11833ad3fb45Sjoris 					i++;
11843ad3fb45Sjoris 					space = 0;
11853ad3fb45Sjoris 				}
11863ad3fb45Sjoris 				sum = sum * 127 + chrtran[t];
11873ad3fb45Sjoris 				i++;
11883ad3fb45Sjoris 				continue;
11893ad3fb45Sjoris 			case EOF:
11903ad3fb45Sjoris 				if (i == 0)
11913ad3fb45Sjoris 					return (0);
11923ad3fb45Sjoris 				/* FALLTHROUGH */
11933ad3fb45Sjoris 			case '\n':
11943ad3fb45Sjoris 				break;
11953ad3fb45Sjoris 			}
11963ad3fb45Sjoris 			break;
11973ad3fb45Sjoris 		}
11983ad3fb45Sjoris 	}
11993ad3fb45Sjoris 	/*
12003ad3fb45Sjoris 	 * There is a remote possibility that we end up with a zero sum.
12013ad3fb45Sjoris 	 * Zero is used as an EOF marker, so return 1 instead.
12023ad3fb45Sjoris 	 */
12033ad3fb45Sjoris 	return (sum == 0 ? 1 : sum);
12043ad3fb45Sjoris }
12053ad3fb45Sjoris 
12063ad3fb45Sjoris static int
asciifile(FILE * f)12073ad3fb45Sjoris asciifile(FILE *f)
12083ad3fb45Sjoris {
1209eea21b3cSray 	unsigned char buf[BUFSIZ];
12103ad3fb45Sjoris 	size_t i, cnt;
12113ad3fb45Sjoris 
1212219c50abSray 	if (f == NULL)
12133ad3fb45Sjoris 		return (1);
12143ad3fb45Sjoris 
12153ad3fb45Sjoris 	rewind(f);
1216a304c0f4Sray 	cnt = fread(buf, 1, sizeof(buf), f);
12173ad3fb45Sjoris 	for (i = 0; i < cnt; i++)
12183ad3fb45Sjoris 		if (!isprint(buf[i]) && !isspace(buf[i]))
12193ad3fb45Sjoris 			return (0);
12203ad3fb45Sjoris 	return (1);
12213ad3fb45Sjoris }
12223ad3fb45Sjoris 
1223e22e6974Sxsa #define begins_with(s, pre) (strncmp(s, pre, sizeof(pre)-1) == 0)
1224e22e6974Sxsa 
12253ad3fb45Sjoris static char *
match_function(const long * f,int pos,FILE * fp)12263ad3fb45Sjoris match_function(const long *f, int pos, FILE *fp)
12273ad3fb45Sjoris {
12283ad3fb45Sjoris 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
12293ad3fb45Sjoris 	size_t nc;
12303ad3fb45Sjoris 	int last = lastline;
1231e22e6974Sxsa 	char *state = NULL;
12323ad3fb45Sjoris 
12333ad3fb45Sjoris 	lastline = pos;
12343ad3fb45Sjoris 	while (pos > last) {
12353ad3fb45Sjoris 		fseek(fp, f[pos - 1], SEEK_SET);
12363ad3fb45Sjoris 		nc = f[pos] - f[pos - 1];
12373ad3fb45Sjoris 		if (nc >= sizeof(buf))
12383ad3fb45Sjoris 			nc = sizeof(buf) - 1;
1239a304c0f4Sray 		nc = fread(buf, 1, nc, fp);
12403ad3fb45Sjoris 		if (nc > 0) {
12413ad3fb45Sjoris 			buf[nc] = '\0';
124257003866Sray 			buf[strcspn(buf, "\n")] = '\0';
12433ad3fb45Sjoris 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
1244e22e6974Sxsa 				if (begins_with(buf, "private:")) {
1245e22e6974Sxsa 					if (!state)
1246e22e6974Sxsa 						state = " (private)";
1247e22e6974Sxsa 				} else if (begins_with(buf, "protected:")) {
1248e22e6974Sxsa 					if (!state)
1249e22e6974Sxsa 						state = " (protected)";
1250e22e6974Sxsa 				} else if (begins_with(buf, "public:")) {
1251e22e6974Sxsa 					if (!state)
1252e22e6974Sxsa 						state = " (public)";
1253e22e6974Sxsa 				} else {
125457003866Sray 					strlcpy(lastbuf, buf, sizeof lastbuf);
1255e22e6974Sxsa 					if (state)
1256e22e6974Sxsa 						strlcat(lastbuf, state,
1257e22e6974Sxsa 						    sizeof lastbuf);
12583ad3fb45Sjoris 					lastmatchline = pos;
12593ad3fb45Sjoris 					return lastbuf;
12603ad3fb45Sjoris 				}
12613ad3fb45Sjoris 			}
1262e22e6974Sxsa 		}
12633ad3fb45Sjoris 		pos--;
12643ad3fb45Sjoris 	}
1265eea21b3cSray 	return lastmatchline > 0 ? lastbuf : NULL;
12663ad3fb45Sjoris }
12673ad3fb45Sjoris 
12683ad3fb45Sjoris /* dump accumulated "context" diff changes */
12693ad3fb45Sjoris static void
dump_context_vec(FILE * f1,FILE * f2,int flags)1270219c50abSray dump_context_vec(FILE *f1, FILE *f2, int flags)
12713ad3fb45Sjoris {
12723ad3fb45Sjoris 	struct context_vec *cvp = context_vec_start;
12733ad3fb45Sjoris 	int lowa, upb, lowc, upd, do_output;
12743ad3fb45Sjoris 	int a, b, c, d;
12753ad3fb45Sjoris 	char ch, *f;
12763ad3fb45Sjoris 
12773ad3fb45Sjoris 	if (context_vec_start > context_vec_ptr)
12783ad3fb45Sjoris 		return;
12793ad3fb45Sjoris 
12803ad3fb45Sjoris 	b = d = 0;		/* gcc */
1281b9fc9a72Sderaadt 	lowa = MAXIMUM(1, cvp->a - diff_context);
1282b9fc9a72Sderaadt 	upb = MINIMUM(len[0], context_vec_ptr->b + diff_context);
1283b9fc9a72Sderaadt 	lowc = MAXIMUM(1, cvp->c - diff_context);
1284b9fc9a72Sderaadt 	upd = MINIMUM(len[1], context_vec_ptr->d + diff_context);
12853ad3fb45Sjoris 
12863ad3fb45Sjoris 	diff_output("***************");
1287219c50abSray 	if ((flags & D_PROTOTYPE)) {
12883ad3fb45Sjoris 		f = match_function(ixold, lowa-1, f1);
1289d5d01609Sray 		if (f != NULL)
12903ad3fb45Sjoris 			diff_output(" %s", f);
12913ad3fb45Sjoris 	}
12923ad3fb45Sjoris 	diff_output("\n*** ");
12933ad3fb45Sjoris 	range(lowa, upb, ",");
12943ad3fb45Sjoris 	diff_output(" ****\n");
12953ad3fb45Sjoris 
12963ad3fb45Sjoris 	/*
12973ad3fb45Sjoris 	 * Output changes to the "old" file.  The first loop suppresses
12983ad3fb45Sjoris 	 * output if there were no changes to the "old" file (we'll see
12993ad3fb45Sjoris 	 * the "old" lines as context in the "new" list).
13003ad3fb45Sjoris 	 */
13013ad3fb45Sjoris 	do_output = 0;
13023ad3fb45Sjoris 	for (; cvp <= context_vec_ptr; cvp++)
13033ad3fb45Sjoris 		if (cvp->a <= cvp->b) {
13043ad3fb45Sjoris 			cvp = context_vec_start;
13053ad3fb45Sjoris 			do_output++;
13063ad3fb45Sjoris 			break;
13073ad3fb45Sjoris 		}
1308eea21b3cSray 	if (do_output) {
13093ad3fb45Sjoris 		while (cvp <= context_vec_ptr) {
13103ad3fb45Sjoris 			a = cvp->a;
13113ad3fb45Sjoris 			b = cvp->b;
13123ad3fb45Sjoris 			c = cvp->c;
13133ad3fb45Sjoris 			d = cvp->d;
13143ad3fb45Sjoris 
13153ad3fb45Sjoris 			if (a <= b && c <= d)
13163ad3fb45Sjoris 				ch = 'c';
13173ad3fb45Sjoris 			else
13183ad3fb45Sjoris 				ch = (a <= b) ? 'd' : 'a';
13193ad3fb45Sjoris 
13203ad3fb45Sjoris 			if (ch == 'a')
1321219c50abSray 				fetch(ixold, lowa, b, f1, ' ', 0, flags);
13223ad3fb45Sjoris 			else {
1323219c50abSray 				fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
13243ad3fb45Sjoris 				fetch(ixold, a, b, f1,
1325219c50abSray 				    ch == 'c' ? '!' : '-', 0, flags);
13263ad3fb45Sjoris 			}
13273ad3fb45Sjoris 			lowa = b + 1;
13283ad3fb45Sjoris 			cvp++;
13293ad3fb45Sjoris 		}
1330219c50abSray 		fetch(ixold, b + 1, upb, f1, ' ', 0, flags);
13313ad3fb45Sjoris 	}
13323ad3fb45Sjoris 	/* output changes to the "new" file */
13333ad3fb45Sjoris 	diff_output("--- ");
13343ad3fb45Sjoris 	range(lowc, upd, ",");
13353ad3fb45Sjoris 	diff_output(" ----\n");
13363ad3fb45Sjoris 
13373ad3fb45Sjoris 	do_output = 0;
13383ad3fb45Sjoris 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
13393ad3fb45Sjoris 		if (cvp->c <= cvp->d) {
13403ad3fb45Sjoris 			cvp = context_vec_start;
13413ad3fb45Sjoris 			do_output++;
13423ad3fb45Sjoris 			break;
13433ad3fb45Sjoris 		}
1344eea21b3cSray 	if (do_output) {
13453ad3fb45Sjoris 		while (cvp <= context_vec_ptr) {
13463ad3fb45Sjoris 			a = cvp->a;
13473ad3fb45Sjoris 			b = cvp->b;
13483ad3fb45Sjoris 			c = cvp->c;
13493ad3fb45Sjoris 			d = cvp->d;
13503ad3fb45Sjoris 
13513ad3fb45Sjoris 			if (a <= b && c <= d)
13523ad3fb45Sjoris 				ch = 'c';
13533ad3fb45Sjoris 			else
13543ad3fb45Sjoris 				ch = (a <= b) ? 'd' : 'a';
13553ad3fb45Sjoris 
13563ad3fb45Sjoris 			if (ch == 'd')
1357219c50abSray 				fetch(ixnew, lowc, d, f2, ' ', 0, flags);
13583ad3fb45Sjoris 			else {
1359219c50abSray 				fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
13603ad3fb45Sjoris 				fetch(ixnew, c, d, f2,
1361219c50abSray 				    ch == 'c' ? '!' : '+', 0, flags);
13623ad3fb45Sjoris 			}
13633ad3fb45Sjoris 			lowc = d + 1;
13643ad3fb45Sjoris 			cvp++;
13653ad3fb45Sjoris 		}
1366219c50abSray 		fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
13673ad3fb45Sjoris 	}
13683ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
13693ad3fb45Sjoris }
13703ad3fb45Sjoris 
13713ad3fb45Sjoris /* dump accumulated "unified" diff changes */
13723ad3fb45Sjoris static void
dump_unified_vec(FILE * f1,FILE * f2,int flags)1373219c50abSray dump_unified_vec(FILE *f1, FILE *f2, int flags)
13743ad3fb45Sjoris {
13753ad3fb45Sjoris 	struct context_vec *cvp = context_vec_start;
13763ad3fb45Sjoris 	int lowa, upb, lowc, upd;
13773ad3fb45Sjoris 	int a, b, c, d;
13783ad3fb45Sjoris 	char ch, *f;
13793ad3fb45Sjoris 
13803ad3fb45Sjoris 	if (context_vec_start > context_vec_ptr)
13813ad3fb45Sjoris 		return;
13823ad3fb45Sjoris 
13833ad3fb45Sjoris 	b = d = 0;		/* gcc */
1384b9fc9a72Sderaadt 	lowa = MAXIMUM(1, cvp->a - diff_context);
1385b9fc9a72Sderaadt 	upb = MINIMUM(len[0], context_vec_ptr->b + diff_context);
1386b9fc9a72Sderaadt 	lowc = MAXIMUM(1, cvp->c - diff_context);
1387b9fc9a72Sderaadt 	upd = MINIMUM(len[1], context_vec_ptr->d + diff_context);
13883ad3fb45Sjoris 
13893ad3fb45Sjoris 	diff_output("@@ -");
13903ad3fb45Sjoris 	uni_range(lowa, upb);
13913ad3fb45Sjoris 	diff_output(" +");
13923ad3fb45Sjoris 	uni_range(lowc, upd);
13933ad3fb45Sjoris 	diff_output(" @@");
1394219c50abSray 	if ((flags & D_PROTOTYPE)) {
13953ad3fb45Sjoris 		f = match_function(ixold, lowa-1, f1);
1396d5d01609Sray 		if (f != NULL)
13973ad3fb45Sjoris 			diff_output(" %s", f);
13983ad3fb45Sjoris 	}
13993ad3fb45Sjoris 	diff_output("\n");
14003ad3fb45Sjoris 
14013ad3fb45Sjoris 	/*
14023ad3fb45Sjoris 	 * Output changes in "unified" diff format--the old and new lines
14033ad3fb45Sjoris 	 * are printed together.
14043ad3fb45Sjoris 	 */
14053ad3fb45Sjoris 	for (; cvp <= context_vec_ptr; cvp++) {
14063ad3fb45Sjoris 		a = cvp->a;
14073ad3fb45Sjoris 		b = cvp->b;
14083ad3fb45Sjoris 		c = cvp->c;
14093ad3fb45Sjoris 		d = cvp->d;
14103ad3fb45Sjoris 
14113ad3fb45Sjoris 		/*
14123ad3fb45Sjoris 		 * c: both new and old changes
14133ad3fb45Sjoris 		 * d: only changes in the old file
14143ad3fb45Sjoris 		 * a: only changes in the new file
14153ad3fb45Sjoris 		 */
14163ad3fb45Sjoris 		if (a <= b && c <= d)
14173ad3fb45Sjoris 			ch = 'c';
14183ad3fb45Sjoris 		else
14193ad3fb45Sjoris 			ch = (a <= b) ? 'd' : 'a';
14203ad3fb45Sjoris 
14213ad3fb45Sjoris 		switch (ch) {
14223ad3fb45Sjoris 		case 'c':
1423219c50abSray 			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1424219c50abSray 			fetch(ixold, a, b, f1, '-', 0, flags);
1425219c50abSray 			fetch(ixnew, c, d, f2, '+', 0, flags);
14263ad3fb45Sjoris 			break;
14273ad3fb45Sjoris 		case 'd':
1428219c50abSray 			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1429219c50abSray 			fetch(ixold, a, b, f1, '-', 0, flags);
14303ad3fb45Sjoris 			break;
14313ad3fb45Sjoris 		case 'a':
1432219c50abSray 			fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
1433219c50abSray 			fetch(ixnew, c, d, f2, '+', 0, flags);
14343ad3fb45Sjoris 			break;
14353ad3fb45Sjoris 		}
14363ad3fb45Sjoris 		lowa = b + 1;
14373ad3fb45Sjoris 		lowc = d + 1;
14383ad3fb45Sjoris 	}
1439219c50abSray 	fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
14403ad3fb45Sjoris 
14413ad3fb45Sjoris 	context_vec_ptr = context_vec_start - 1;
14423ad3fb45Sjoris }
14433ad3fb45Sjoris 
14443ad3fb45Sjoris void
diff_output(const char * fmt,...)14453ad3fb45Sjoris diff_output(const char *fmt, ...)
14463ad3fb45Sjoris {
14473ad3fb45Sjoris 	va_list vap;
14483ad3fb45Sjoris 	int i;
14493ad3fb45Sjoris 	char *str;
14503ad3fb45Sjoris 
14513ad3fb45Sjoris 	va_start(vap, fmt);
14523ad3fb45Sjoris 	i = vasprintf(&str, fmt, vap);
14533ad3fb45Sjoris 	va_end(vap);
14543ad3fb45Sjoris 	if (i == -1)
14559a0ecc80Stobias 		fatal("diff_output: could not allocate memory");
14563ad3fb45Sjoris 	if (diffbuf != NULL)
14577bb3ddb0Sray 		buf_puts(diffbuf, str);
14583ad3fb45Sjoris 	else
14593ad3fb45Sjoris 		cvs_printf("%s", str);
1460397ddb8aSnicm 	free(str);
14613ad3fb45Sjoris }
1462