xref: /openbsd-src/usr.bin/rcs/diff.c (revision 3aaa63eb46949490a39db9c6d82aacc8ee5d8551)
1*3aaa63ebSderaadt /*	$OpenBSD: diff.c,v 1.40 2019/06/28 13:35:03 deraadt Exp $	*/
22dc36bedSjoris /*
32dc36bedSjoris  * Copyright (C) Caldera International Inc.  2001-2002.
42dc36bedSjoris  * All rights reserved.
52dc36bedSjoris  *
62dc36bedSjoris  * Redistribution and use in source and binary forms, with or without
72dc36bedSjoris  * modification, are permitted provided that the following conditions
82dc36bedSjoris  * are met:
92dc36bedSjoris  * 1. Redistributions of source code and documentation must retain the above
102dc36bedSjoris  *    copyright notice, this list of conditions and the following disclaimer.
112dc36bedSjoris  * 2. Redistributions in binary form must reproduce the above copyright
122dc36bedSjoris  *    notice, this list of conditions and the following disclaimer in the
132dc36bedSjoris  *    documentation and/or other materials provided with the distribution.
142dc36bedSjoris  * 3. All advertising materials mentioning features or use of this software
152dc36bedSjoris  *    must display the following acknowledgement:
162dc36bedSjoris  *	This product includes software developed or owned by Caldera
172dc36bedSjoris  *	International, Inc.
182dc36bedSjoris  * 4. Neither the name of Caldera International, Inc. nor the names of other
192dc36bedSjoris  *    contributors may be used to endorse or promote products derived from
202dc36bedSjoris  *    this software without specific prior written permission.
212dc36bedSjoris  *
222dc36bedSjoris  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
232dc36bedSjoris  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
242dc36bedSjoris  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
252dc36bedSjoris  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
262dc36bedSjoris  * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
272dc36bedSjoris  * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
282dc36bedSjoris  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
292dc36bedSjoris  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
302dc36bedSjoris  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
312dc36bedSjoris  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
322dc36bedSjoris  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
332dc36bedSjoris  * POSSIBILITY OF SUCH DAMAGE.
342dc36bedSjoris  */
352dc36bedSjoris /*-
362dc36bedSjoris  * Copyright (c) 1991, 1993
372dc36bedSjoris  *	The Regents of the University of California.  All rights reserved.
382dc36bedSjoris  * Copyright (c) 2004 Jean-Francois Brousseau.  All rights reserved.
392dc36bedSjoris  *
402dc36bedSjoris  * Redistribution and use in source and binary forms, with or without
412dc36bedSjoris  * modification, are permitted provided that the following conditions
422dc36bedSjoris  * are met:
432dc36bedSjoris  * 1. Redistributions of source code must retain the above copyright
442dc36bedSjoris  *    notice, this list of conditions and the following disclaimer.
452dc36bedSjoris  * 2. Redistributions in binary form must reproduce the above copyright
462dc36bedSjoris  *    notice, this list of conditions and the following disclaimer in the
472dc36bedSjoris  *    documentation and/or other materials provided with the distribution.
482dc36bedSjoris  * 3. Neither the name of the University nor the names of its contributors
492dc36bedSjoris  *    may be used to endorse or promote products derived from this software
502dc36bedSjoris  *    without specific prior written permission.
512dc36bedSjoris  *
522dc36bedSjoris  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
532dc36bedSjoris  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
542dc36bedSjoris  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
552dc36bedSjoris  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
562dc36bedSjoris  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
572dc36bedSjoris  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
582dc36bedSjoris  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
592dc36bedSjoris  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
602dc36bedSjoris  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
612dc36bedSjoris  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
622dc36bedSjoris  * SUCH DAMAGE.
632dc36bedSjoris  *
642dc36bedSjoris  *	@(#)diffreg.c   8.1 (Berkeley) 6/6/93
652dc36bedSjoris  */
66af90d6d1Sray 
67af90d6d1Sray #include <sys/stat.h>
68af90d6d1Sray 
69af90d6d1Sray #include <ctype.h>
70af90d6d1Sray #include <err.h>
71af90d6d1Sray #include <stdarg.h>
72b9fc9a72Sderaadt #include <stdint.h>
73af90d6d1Sray #include <stddef.h>
74af90d6d1Sray #include <stdio.h>
758ac837e5Snicm #include <stdlib.h>
76af90d6d1Sray #include <string.h>
77af90d6d1Sray #include <unistd.h>
78b9fc9a72Sderaadt #include <limits.h>
79af90d6d1Sray 
80af90d6d1Sray #include "buf.h"
81af90d6d1Sray #include "diff.h"
82af90d6d1Sray #include "xmalloc.h"
83af90d6d1Sray 
84b9fc9a72Sderaadt #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
85b9fc9a72Sderaadt #define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
86b9fc9a72Sderaadt 
87af90d6d1Sray /*
88af90d6d1Sray  * diff - compare two files.
89af90d6d1Sray  */
90af90d6d1Sray 
912dc36bedSjoris /*
922dc36bedSjoris  *	Uses an algorithm due to Harold Stone, which finds
932dc36bedSjoris  *	a pair of longest identical subsequences in the two
942dc36bedSjoris  *	files.
952dc36bedSjoris  *
962dc36bedSjoris  *	The major goal is to generate the match vector J.
972dc36bedSjoris  *	J[i] is the index of the line in file1 corresponding
982dc36bedSjoris  *	to line i file0. J[i] = 0 if there is no
992dc36bedSjoris  *	such line in file1.
1002dc36bedSjoris  *
1012dc36bedSjoris  *	Lines are hashed so as to work in core. All potential
1022dc36bedSjoris  *	matches are located by sorting the lines of each file
1032dc36bedSjoris  *	on the hash (called ``value''). In particular, this
1042dc36bedSjoris  *	collects the equivalence classes in file1 together.
1052dc36bedSjoris  *	Subroutine equiv replaces the value of each line in
1062dc36bedSjoris  *	file0 by the index of the first element of its
1072dc36bedSjoris  *	matching equivalence in (the reordered) file1.
1082dc36bedSjoris  *	To save space equiv squeezes file1 into a single
1092dc36bedSjoris  *	array member in which the equivalence classes
1102dc36bedSjoris  *	are simply concatenated, except that their first
1112dc36bedSjoris  *	members are flagged by changing sign.
1122dc36bedSjoris  *
1132dc36bedSjoris  *	Next the indices that point into member are unsorted into
1142dc36bedSjoris  *	array class according to the original order of file0.
1152dc36bedSjoris  *
1162dc36bedSjoris  *	The cleverness lies in routine stone. This marches
1172dc36bedSjoris  *	through the lines of file0, developing a vector klist
1182dc36bedSjoris  *	of "k-candidates". At step i a k-candidate is a matched
1192dc36bedSjoris  *	pair of lines x,y (x in file0 y in file1) such that
1202dc36bedSjoris  *	there is a common subsequence of length k
1212dc36bedSjoris  *	between the first i lines of file0 and the first y
1222dc36bedSjoris  *	lines of file1, but there is no such subsequence for
1232dc36bedSjoris  *	any smaller y. x is the earliest possible mate to y
1242dc36bedSjoris  *	that occurs in such a subsequence.
1252dc36bedSjoris  *
1262dc36bedSjoris  *	Whenever any of the members of the equivalence class of
1272dc36bedSjoris  *	lines in file1 matable to a line in file0 has serial number
1282dc36bedSjoris  *	less than the y of some k-candidate, that k-candidate
1292dc36bedSjoris  *	with the smallest such y is replaced. The new
1302dc36bedSjoris  *	k-candidate is chained (via pred) to the current
1312dc36bedSjoris  *	k-1 candidate so that the actual subsequence can
1322dc36bedSjoris  *	be recovered. When a member has serial number greater
1332dc36bedSjoris  *	that the y of all k-candidates, the klist is extended.
1342dc36bedSjoris  *	At the end, the longest subsequence is pulled out
1352dc36bedSjoris  *	and placed in the array J by unravel
1362dc36bedSjoris  *
1372dc36bedSjoris  *	With J in hand, the matches there recorded are
1382dc36bedSjoris  *	check'ed against reality to assure that no spurious
1392dc36bedSjoris  *	matches have crept in due to hashing. If they have,
1402dc36bedSjoris  *	they are broken, and "jackpot" is recorded--a harmless
1412dc36bedSjoris  *	matter except that a true match for a spuriously
1422dc36bedSjoris  *	mated line may now be unnecessarily reported as a change.
1432dc36bedSjoris  *
1442dc36bedSjoris  *	Much of the complexity of the program comes simply
1452dc36bedSjoris  *	from trying to minimize core utilization and
1462dc36bedSjoris  *	maximize the range of doable problems by dynamically
1472dc36bedSjoris  *	allocating what is needed and reusing what is not.
1482dc36bedSjoris  *	The core requirements for problems larger than somewhat
1492dc36bedSjoris  *	are (in words) 2*length(file0) + length(file1) +
1502dc36bedSjoris  *	3*(number of k-candidates installed),  typically about
1512dc36bedSjoris  *	6n words for files of length n.
1522dc36bedSjoris  */
1532dc36bedSjoris 
1542dc36bedSjoris struct cand {
1552dc36bedSjoris 	int	x;
1562dc36bedSjoris 	int	y;
1572dc36bedSjoris 	int	pred;
158ccf8fb4cSray };
1592dc36bedSjoris 
1602dc36bedSjoris struct line {
1612dc36bedSjoris 	int	serial;
1622dc36bedSjoris 	int	value;
1632dc36bedSjoris } *file[2];
1642dc36bedSjoris 
1652dc36bedSjoris /*
1662dc36bedSjoris  * The following struct is used to record change information when
1672dc36bedSjoris  * doing a "context" or "unified" diff.  (see routine "change" to
1682dc36bedSjoris  * understand the highly mnemonic field names)
1692dc36bedSjoris  */
1702dc36bedSjoris struct context_vec {
1712dc36bedSjoris 	int	a;		/* start line in old file */
1722dc36bedSjoris 	int	b;		/* end line in old file */
1732dc36bedSjoris 	int	c;		/* start line in new file */
1742dc36bedSjoris 	int	d;		/* end line in new file */
1752dc36bedSjoris };
1762dc36bedSjoris 
177f5f501bbSmillert static void	 output(FILE *, FILE *, int);
178f5f501bbSmillert static void	 check(FILE *, FILE *, int);
1792dc36bedSjoris static void	 range(int, int, char *);
1802dc36bedSjoris static void	 uni_range(int, int);
181f5f501bbSmillert static void	 dump_context_vec(FILE *, FILE *, int);
182f5f501bbSmillert static void	 dump_unified_vec(FILE *, FILE *, int);
183d2c2f9b1Sray static void	 prepare(int, FILE *, off_t, int);
1842dc36bedSjoris static void	 prune(void);
1852dc36bedSjoris static void	 equiv(struct line *, int, struct line *, int, int *);
1862dc36bedSjoris static void	 unravel(int);
1872dc36bedSjoris static void	 unsort(struct line *, int, int *);
188f5f501bbSmillert static void	 change(FILE *, FILE *, int, int, int, int, int);
1892dc36bedSjoris static void	 sort(struct line *, int);
1902dc36bedSjoris static int	 ignoreline(char *);
1912dc36bedSjoris static int	 asciifile(FILE *);
192f5f501bbSmillert static void	 fetch(long *, int, int, FILE *, int, int, int);
1932dc36bedSjoris static int	 newcand(int, int, int);
1942dc36bedSjoris static int	 search(int *, int, int);
1952dc36bedSjoris static int	 skipline(FILE *);
1962dc36bedSjoris static int	 isqrt(int);
197f5f501bbSmillert static int	 stone(int *, int, int *, int *, int);
198f5f501bbSmillert static int	 readhash(FILE *, int);
1992dc36bedSjoris static int	 files_differ(FILE *, FILE *);
2002dc36bedSjoris static char	*match_function(const long *, int, FILE *);
2012dc36bedSjoris static char	*preadline(int, size_t, off_t);
2022dc36bedSjoris 
2032dc36bedSjoris 
204f5f501bbSmillert int diff_context = 3;
2052dc36bedSjoris int diff_format = D_NORMAL;
2062dc36bedSjoris char *diff_file = NULL;
2072dc36bedSjoris RCSNUM *diff_rev1 = NULL;
2082dc36bedSjoris RCSNUM *diff_rev2 = NULL;
209f5f501bbSmillert char diffargs[512]; /* XXX */
2102dc36bedSjoris static struct stat stb1, stb2;
211f5f501bbSmillert static char *ifdefname;
212f5f501bbSmillert regex_t *diff_ignore_re;
2132dc36bedSjoris 
2142dc36bedSjoris static int  *J;			/* will be overlaid on class */
2152dc36bedSjoris static int  *class;		/* will be overlaid on file[0] */
2162dc36bedSjoris static int  *klist;		/* will be overlaid on file[0] after class */
2172dc36bedSjoris static int  *member;		/* will be overlaid on file[1] */
2182dc36bedSjoris static int   clen;
2192dc36bedSjoris static int   inifdef;		/* whether or not we are in a #ifdef block */
220d2c2f9b1Sray static int   len[2];
2212dc36bedSjoris static int   pref, suff;	/* length of prefix and suffix */
2222dc36bedSjoris static int   slen[2];
2232dc36bedSjoris static int   anychange;
2242dc36bedSjoris static long *ixnew;		/* will be overlaid on file[1] */
2252dc36bedSjoris static long *ixold;		/* will be overlaid on klist */
2262dc36bedSjoris static struct cand *clist;	/* merely a free storage pot for candidates */
2272dc36bedSjoris static int   clistlen;		/* the length of clist */
2282dc36bedSjoris static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
2292dc36bedSjoris static u_char *chrtran;		/* translation table for case-folding */
2302dc36bedSjoris static struct context_vec *context_vec_start;
2312dc36bedSjoris static struct context_vec *context_vec_end;
2322dc36bedSjoris static struct context_vec *context_vec_ptr;
2332dc36bedSjoris 
234e22e6974Sxsa #define FUNCTION_CONTEXT_SIZE	55
2352dc36bedSjoris static char lastbuf[FUNCTION_CONTEXT_SIZE];
2362dc36bedSjoris static int lastline;
2372dc36bedSjoris static int lastmatchline;
2382dc36bedSjoris BUF  *diffbuf = NULL;
2392dc36bedSjoris 
240af90d6d1Sray 
2412dc36bedSjoris /*
2422dc36bedSjoris  * chrtran points to one of 2 translation tables: cup2low if folding upper to
2432dc36bedSjoris  * lower case clow2low if not folding case
2442dc36bedSjoris  */
2452dc36bedSjoris u_char clow2low[256] = {
2462dc36bedSjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2472dc36bedSjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2482dc36bedSjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2492dc36bedSjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2502dc36bedSjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2512dc36bedSjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
2522dc36bedSjoris 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
2532dc36bedSjoris 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
2542dc36bedSjoris 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
2552dc36bedSjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2562dc36bedSjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2572dc36bedSjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2582dc36bedSjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2592dc36bedSjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2602dc36bedSjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2612dc36bedSjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2622dc36bedSjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2632dc36bedSjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2642dc36bedSjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2652dc36bedSjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2662dc36bedSjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2672dc36bedSjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2682dc36bedSjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2692dc36bedSjoris 	0xfd, 0xfe, 0xff
2702dc36bedSjoris };
2712dc36bedSjoris 
2722dc36bedSjoris u_char cup2low[256] = {
2732dc36bedSjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2742dc36bedSjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2752dc36bedSjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2762dc36bedSjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2772dc36bedSjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2782dc36bedSjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
2792dc36bedSjoris 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
2802dc36bedSjoris 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
2812dc36bedSjoris 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
2822dc36bedSjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2832dc36bedSjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2842dc36bedSjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2852dc36bedSjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2862dc36bedSjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2872dc36bedSjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2882dc36bedSjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2892dc36bedSjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2902dc36bedSjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2912dc36bedSjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2922dc36bedSjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2932dc36bedSjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2942dc36bedSjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2952dc36bedSjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2962dc36bedSjoris 	0xfd, 0xfe, 0xff
2972dc36bedSjoris };
2982dc36bedSjoris 
2992dc36bedSjoris int
diffreg(const char * file1,const char * file2,BUF * out,int flags)300f049f428Sray diffreg(const char *file1, const char *file2, BUF *out, int flags)
3012dc36bedSjoris {
3022dc36bedSjoris 	FILE *f1, *f2;
3032dc36bedSjoris 	int i, rval;
3042dc36bedSjoris 
3052dc36bedSjoris 	f1 = f2 = NULL;
3062dc36bedSjoris 	rval = D_SAME;
3072dc36bedSjoris 	anychange = 0;
3082dc36bedSjoris 	lastline = 0;
3092dc36bedSjoris 	lastmatchline = 0;
3102dc36bedSjoris 	context_vec_ptr = context_vec_start - 1;
311f5f501bbSmillert 	if (flags & D_IGNORECASE)
312f5f501bbSmillert 		chrtran = cup2low;
313f5f501bbSmillert 	else
314f5f501bbSmillert 		chrtran = clow2low;
3152dc36bedSjoris 	if (out != NULL)
3162dc36bedSjoris 		diffbuf = out;
3172dc36bedSjoris 
3182dc36bedSjoris 	f1 = fopen(file1, "r");
3192dc36bedSjoris 	if (f1 == NULL) {
3202dc36bedSjoris 		warn("%s", file1);
3212dc36bedSjoris 		goto closem;
3222dc36bedSjoris 	}
3232dc36bedSjoris 
3242dc36bedSjoris 	f2 = fopen(file2, "r");
3252dc36bedSjoris 	if (f2 == NULL) {
3262dc36bedSjoris 		warn("%s", file2);
3272dc36bedSjoris 		goto closem;
3282dc36bedSjoris 	}
3292dc36bedSjoris 
330*3aaa63ebSderaadt 	if (fstat(fileno(f1), &stb1) == -1) {
3312dc36bedSjoris 		warn("%s", file1);
3322dc36bedSjoris 		goto closem;
3332dc36bedSjoris 	}
3342dc36bedSjoris 
335*3aaa63ebSderaadt 	if (fstat(fileno(f2), &stb2) == -1) {
3362dc36bedSjoris 		warn("%s", file2);
3372dc36bedSjoris 		goto closem;
3382dc36bedSjoris 	}
3392dc36bedSjoris 
340616468a8Sray 	switch (files_differ(f1, f2)) {
341616468a8Sray 	case 1:
342616468a8Sray 		break;
343616468a8Sray 	case -1:
344616468a8Sray 		rval = D_ERROR;
345616468a8Sray 		/* FALLTHROUGH */
346616468a8Sray 	case 0:
3472dc36bedSjoris 		goto closem;
348616468a8Sray 	default:
349616468a8Sray 		errx(D_ERROR, "files_differ: invalid case");
350616468a8Sray 	}
3512dc36bedSjoris 
352f5f501bbSmillert 	if ((flags & D_FORCEASCII) == 0 &&
353f5f501bbSmillert 	    (!asciifile(f1) || !asciifile(f2))) {
3541fb625bfSxsa 		rval = D_ERROR;
3552dc36bedSjoris 		goto closem;
3562dc36bedSjoris 	}
3572dc36bedSjoris 
358d2c2f9b1Sray 	prepare(0, f1, stb1.st_size, flags);
359d2c2f9b1Sray 	prepare(1, f2, stb2.st_size, flags);
3602dc36bedSjoris 
3612dc36bedSjoris 	prune();
3622dc36bedSjoris 	sort(sfile[0], slen[0]);
3632dc36bedSjoris 	sort(sfile[1], slen[1]);
3642dc36bedSjoris 
3652dc36bedSjoris 	member = (int *)file[1];
3662dc36bedSjoris 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
367caa2ffb0Sderaadt 	member = xreallocarray(member, slen[1] + 2, sizeof(*member));
3682dc36bedSjoris 
3692dc36bedSjoris 	class = (int *)file[0];
3702dc36bedSjoris 	unsort(sfile[0], slen[0], class);
371caa2ffb0Sderaadt 	class = xreallocarray(class, slen[0] + 2, sizeof(*class));
3722dc36bedSjoris 
3732dc36bedSjoris 	klist = xcalloc(slen[0] + 2, sizeof(*klist));
3742dc36bedSjoris 	clen = 0;
3752dc36bedSjoris 	clistlen = 100;
3762dc36bedSjoris 	clist = xcalloc(clistlen, sizeof(*clist));
377af90d6d1Sray 	i = stone(class, slen[0], member, klist, flags);
3788ac837e5Snicm 	free(member);
3798ac837e5Snicm 	free(class);
3802dc36bedSjoris 
381caa2ffb0Sderaadt 	J = xreallocarray(J, len[0] + 2, sizeof(*J));
3822dc36bedSjoris 	unravel(klist[i]);
3838ac837e5Snicm 	free(clist);
3848ac837e5Snicm 	free(klist);
3852dc36bedSjoris 
386caa2ffb0Sderaadt 	ixold = xreallocarray(ixold, len[0] + 2, sizeof(*ixold));
387caa2ffb0Sderaadt 	ixnew = xreallocarray(ixnew, len[1] + 2, sizeof(*ixnew));
388f5f501bbSmillert 	check(f1, f2, flags);
389f5f501bbSmillert 	output(f1, f2, flags);
3902dc36bedSjoris 
3912dc36bedSjoris closem:
392af90d6d1Sray 	if (anychange) {
3932dc36bedSjoris 		if (rval == D_SAME)
3942dc36bedSjoris 			rval = D_DIFFER;
3952dc36bedSjoris 	}
3962dc36bedSjoris 	if (f1 != NULL)
3972dc36bedSjoris 		fclose(f1);
3982dc36bedSjoris 	if (f2 != NULL)
3992dc36bedSjoris 		fclose(f2);
4002dc36bedSjoris 
4012dc36bedSjoris 	return (rval);
4022dc36bedSjoris }
4032dc36bedSjoris 
4042dc36bedSjoris /*
4052dc36bedSjoris  * Check to see if the given files differ.
4062dc36bedSjoris  * Returns 0 if they are the same, 1 if different, and -1 on error.
4072dc36bedSjoris  * XXX - could use code from cmp(1) [faster]
4082dc36bedSjoris  */
4092dc36bedSjoris static int
files_differ(FILE * f1,FILE * f2)4102dc36bedSjoris files_differ(FILE *f1, FILE *f2)
4112dc36bedSjoris {
4122dc36bedSjoris 	char buf1[BUFSIZ], buf2[BUFSIZ];
4132dc36bedSjoris 	size_t i, j;
4142dc36bedSjoris 
4152dc36bedSjoris 	if (stb1.st_size != stb2.st_size)
4162dc36bedSjoris 		return (1);
4172dc36bedSjoris 	for (;;) {
41885ea658bSray 		i = fread(buf1, 1, sizeof(buf1), f1);
41985ea658bSray 		j = fread(buf2, 1, sizeof(buf2), f2);
42009523d6fSray 		if ((!i && ferror(f1)) || (!j && ferror(f2)))
42109523d6fSray 			return (-1);
4222dc36bedSjoris 		if (i != j)
4232dc36bedSjoris 			return (1);
42409523d6fSray 		if (i == 0)
4252dc36bedSjoris 			return (0);
4262dc36bedSjoris 		if (memcmp(buf1, buf2, i) != 0)
4272dc36bedSjoris 			return (1);
4282dc36bedSjoris 	}
4292dc36bedSjoris }
4302dc36bedSjoris 
431d2c2f9b1Sray static void
prepare(int i,FILE * fd,off_t filesize,int flags)432f5f501bbSmillert prepare(int i, FILE *fd, off_t filesize, int flags)
4332dc36bedSjoris {
4342dc36bedSjoris 	struct line *p;
4352dc36bedSjoris 	int j, h;
4362dc36bedSjoris 	size_t sz;
4372dc36bedSjoris 
4382dc36bedSjoris 	rewind(fd);
4392dc36bedSjoris 
440257be878Sokan 	sz = ((uintmax_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
4412dc36bedSjoris 	if (sz < 100)
4422dc36bedSjoris 		sz = 100;
4432dc36bedSjoris 
4442dc36bedSjoris 	p = xcalloc(sz + 3, sizeof(*p));
445f5f501bbSmillert 	for (j = 0; (h = readhash(fd, flags));) {
446257be878Sokan 		if ((size_t)j == sz) {
4472dc36bedSjoris 			sz = sz * 3 / 2;
448caa2ffb0Sderaadt 			p = xreallocarray(p, sz + 3, sizeof(*p));
4492dc36bedSjoris 		}
4502dc36bedSjoris 		p[++j].value = h;
4512dc36bedSjoris 	}
452d2c2f9b1Sray 	len[i] = j;
4532dc36bedSjoris 	file[i] = p;
4542dc36bedSjoris }
4552dc36bedSjoris 
4562dc36bedSjoris static void
prune(void)4572dc36bedSjoris prune(void)
4582dc36bedSjoris {
4592dc36bedSjoris 	int i, j;
4602dc36bedSjoris 
461d2c2f9b1Sray 	for (pref = 0; pref < len[0] && pref < len[1] &&
4622dc36bedSjoris 	    file[0][pref + 1].value == file[1][pref + 1].value;
4632dc36bedSjoris 	    pref++)
4642dc36bedSjoris 		;
465af90d6d1Sray 	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
466af90d6d1Sray 	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
4672dc36bedSjoris 	    suff++)
4682dc36bedSjoris 		;
4692dc36bedSjoris 	for (j = 0; j < 2; j++) {
4702dc36bedSjoris 		sfile[j] = file[j] + pref;
471d2c2f9b1Sray 		slen[j] = len[j] - pref - suff;
4722dc36bedSjoris 		for (i = 0; i <= slen[j]; i++)
4732dc36bedSjoris 			sfile[j][i].serial = i;
4742dc36bedSjoris 	}
4752dc36bedSjoris }
4762dc36bedSjoris 
4772dc36bedSjoris static void
equiv(struct line * a,int n,struct line * b,int m,int * c)4782dc36bedSjoris equiv(struct line *a, int n, struct line *b, int m, int *c)
4792dc36bedSjoris {
4802dc36bedSjoris 	int i, j;
4812dc36bedSjoris 
4822dc36bedSjoris 	i = j = 1;
4832dc36bedSjoris 	while (i <= n && j <= m) {
4842dc36bedSjoris 		if (a[i].value < b[j].value)
4852dc36bedSjoris 			a[i++].value = 0;
4862dc36bedSjoris 		else if (a[i].value == b[j].value)
4872dc36bedSjoris 			a[i++].value = j;
4882dc36bedSjoris 		else
4892dc36bedSjoris 			j++;
4902dc36bedSjoris 	}
4912dc36bedSjoris 	while (i <= n)
4922dc36bedSjoris 		a[i++].value = 0;
4932dc36bedSjoris 	b[m + 1].value = 0;
4942dc36bedSjoris 	j = 0;
4952dc36bedSjoris 	while (++j <= m) {
4962dc36bedSjoris 		c[j] = -b[j].serial;
4972dc36bedSjoris 		while (b[j + 1].value == b[j].value) {
4982dc36bedSjoris 			j++;
4992dc36bedSjoris 			c[j] = b[j].serial;
5002dc36bedSjoris 		}
5012dc36bedSjoris 	}
5022dc36bedSjoris 	c[j] = -1;
5032dc36bedSjoris }
5042dc36bedSjoris 
5052dc36bedSjoris /* Code taken from ping.c */
5062dc36bedSjoris static int
isqrt(int n)5072dc36bedSjoris isqrt(int n)
5082dc36bedSjoris {
5092dc36bedSjoris 	int y, x = 1;
5102dc36bedSjoris 
5112dc36bedSjoris 	if (n == 0)
5122dc36bedSjoris 		return (0);
5132dc36bedSjoris 
5142dc36bedSjoris 	do { /* newton was a stinker */
5152dc36bedSjoris 		y = x;
5162dc36bedSjoris 		x = n / x;
5172dc36bedSjoris 		x += y;
5182dc36bedSjoris 		x /= 2;
519af90d6d1Sray 	} while ((x - y) > 1 || (x - y) < -1);
5202dc36bedSjoris 
5212dc36bedSjoris 	return (x);
5222dc36bedSjoris }
5232dc36bedSjoris 
5242dc36bedSjoris static int
stone(int * a,int n,int * b,int * c,int flags)525f5f501bbSmillert stone(int *a, int n, int *b, int *c, int flags)
5262dc36bedSjoris {
5272dc36bedSjoris 	int i, k, y, j, l;
528df890a16Snicm 	int oldc, tc, oldl, sq;
529df890a16Snicm 	u_int numtries, bound;
5302dc36bedSjoris 
531df890a16Snicm 	if (flags & D_MINIMAL)
532df890a16Snicm 		bound = UINT_MAX;
533df890a16Snicm 	else {
534df890a16Snicm 		sq = isqrt(n);
535b9fc9a72Sderaadt 		bound = MAXIMUM(256, sq);
536df890a16Snicm 	}
5372dc36bedSjoris 
5382dc36bedSjoris 	k = 0;
539af90d6d1Sray 	c[0] = newcand(0, 0, 0);
5402dc36bedSjoris 	for (i = 1; i <= n; i++) {
5412dc36bedSjoris 		j = a[i];
5422dc36bedSjoris 		if (j == 0)
5432dc36bedSjoris 			continue;
5442dc36bedSjoris 		y = -b[j];
5452dc36bedSjoris 		oldl = 0;
5462dc36bedSjoris 		oldc = c[0];
5472dc36bedSjoris 		numtries = 0;
5482dc36bedSjoris 		do {
5492dc36bedSjoris 			if (y <= clist[oldc].y)
5502dc36bedSjoris 				continue;
5512dc36bedSjoris 			l = search(c, k, y);
5522dc36bedSjoris 			if (l != oldl + 1)
5532dc36bedSjoris 				oldc = c[l - 1];
5542dc36bedSjoris 			if (l <= k) {
5552dc36bedSjoris 				if (clist[c[l]].y <= y)
5562dc36bedSjoris 					continue;
5572dc36bedSjoris 				tc = c[l];
558af90d6d1Sray 				c[l] = newcand(i, y, oldc);
5592dc36bedSjoris 				oldc = tc;
5602dc36bedSjoris 				oldl = l;
5612dc36bedSjoris 				numtries++;
5622dc36bedSjoris 			} else {
563af90d6d1Sray 				c[l] = newcand(i, y, oldc);
5642dc36bedSjoris 				k++;
5652dc36bedSjoris 				break;
5662dc36bedSjoris 			}
5672dc36bedSjoris 		} while ((y = b[++j]) > 0 && numtries < bound);
5682dc36bedSjoris 	}
5692dc36bedSjoris 	return (k);
5702dc36bedSjoris }
5712dc36bedSjoris 
5722dc36bedSjoris static int
newcand(int x,int y,int pred)5732dc36bedSjoris newcand(int x, int y, int pred)
5742dc36bedSjoris {
57580566be2Sray 	struct cand *q;
5762dc36bedSjoris 
5772dc36bedSjoris 	if (clen == clistlen) {
578f74aa433Sray 		clistlen = clistlen * 11 / 10;
579caa2ffb0Sderaadt 		clist = xreallocarray(clist, clistlen, sizeof(*clist));
5802dc36bedSjoris 	}
5812dc36bedSjoris 	q = clist + clen;
5822dc36bedSjoris 	q->x = x;
5832dc36bedSjoris 	q->y = y;
5842dc36bedSjoris 	q->pred = pred;
5852dc36bedSjoris 	return (clen++);
5862dc36bedSjoris }
5872dc36bedSjoris 
5882dc36bedSjoris static int
search(int * c,int k,int y)5892dc36bedSjoris search(int *c, int k, int y)
5902dc36bedSjoris {
5912dc36bedSjoris 	int i, j, l, t;
5922dc36bedSjoris 
5932dc36bedSjoris 	if (clist[c[k]].y < y)	/* quick look for typical case */
5942dc36bedSjoris 		return (k + 1);
5952dc36bedSjoris 	i = 0;
5962dc36bedSjoris 	j = k + 1;
5972dc36bedSjoris 	for (;;) {
5982dc36bedSjoris 		l = (i + j) / 2;
5992dc36bedSjoris 		if (l <= i)
6002dc36bedSjoris 			break;
6012dc36bedSjoris 		t = clist[c[l]].y;
6022dc36bedSjoris 		if (t > y)
6032dc36bedSjoris 			j = l;
6042dc36bedSjoris 		else if (t < y)
6052dc36bedSjoris 			i = l;
6062dc36bedSjoris 		else
6072dc36bedSjoris 			return (l);
6082dc36bedSjoris 	}
6092dc36bedSjoris 	return (l + 1);
6102dc36bedSjoris }
6112dc36bedSjoris 
6122dc36bedSjoris static void
unravel(int p)6132dc36bedSjoris unravel(int p)
6142dc36bedSjoris {
6152dc36bedSjoris 	struct cand *q;
6162dc36bedSjoris 	int i;
6172dc36bedSjoris 
618d2c2f9b1Sray 	for (i = 0; i <= len[0]; i++)
6192dc36bedSjoris 		J[i] = i <= pref ? i :
620d2c2f9b1Sray 		    i > len[0] - suff ? i + len[1] - len[0] : 0;
6212dc36bedSjoris 	for (q = clist + p; q->y != 0; q = clist + q->pred)
6222dc36bedSjoris 		J[q->x + pref] = q->y + pref;
6232dc36bedSjoris }
6242dc36bedSjoris 
6252dc36bedSjoris /*
6262dc36bedSjoris  * Check does double duty:
6272dc36bedSjoris  *  1.	ferret out any fortuitous correspondences due
6282dc36bedSjoris  *	to confounding by hashing (which result in "jackpot")
6292dc36bedSjoris  *  2.  collect random access indexes to the two files
6302dc36bedSjoris  */
6312dc36bedSjoris static void
check(FILE * f1,FILE * f2,int flags)632f5f501bbSmillert check(FILE *f1, FILE *f2, int flags)
6332dc36bedSjoris {
6342dc36bedSjoris 	int i, j, jackpot, c, d;
6352dc36bedSjoris 	long ctold, ctnew;
6362dc36bedSjoris 
6372dc36bedSjoris 	rewind(f1);
6382dc36bedSjoris 	rewind(f2);
6392dc36bedSjoris 	j = 1;
6402dc36bedSjoris 	ixold[0] = ixnew[0] = 0;
6412dc36bedSjoris 	jackpot = 0;
6422dc36bedSjoris 	ctold = ctnew = 0;
643d2c2f9b1Sray 	for (i = 1; i <= len[0]; i++) {
6442dc36bedSjoris 		if (J[i] == 0) {
6452dc36bedSjoris 			ixold[i] = ctold += skipline(f1);
6462dc36bedSjoris 			continue;
6472dc36bedSjoris 		}
6482dc36bedSjoris 		while (j < J[i]) {
6492dc36bedSjoris 			ixnew[j] = ctnew += skipline(f2);
6502dc36bedSjoris 			j++;
6512dc36bedSjoris 		}
652f5f501bbSmillert 		if (flags & (D_FOLDBLANKS|D_IGNOREBLANKS|D_IGNORECASE)) {
6532dc36bedSjoris 			for (;;) {
6542dc36bedSjoris 				c = getc(f1);
6552dc36bedSjoris 				d = getc(f2);
6562dc36bedSjoris 				/*
6572dc36bedSjoris 				 * GNU diff ignores a missing newline
658f5f501bbSmillert 				 * in one file for -b or -w.
6592dc36bedSjoris 				 */
660f5f501bbSmillert 				if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) &&
6612dc36bedSjoris 				    ((c == EOF && d == '\n') ||
6622dc36bedSjoris 				    (c == '\n' && d == EOF))) {
6632dc36bedSjoris 					break;
6642dc36bedSjoris 				}
6652dc36bedSjoris 				ctold++;
6662dc36bedSjoris 				ctnew++;
667f5f501bbSmillert 				if ((flags & D_FOLDBLANKS) && isspace(c) &&
668f5f501bbSmillert 				    isspace(d)) {
6692dc36bedSjoris 					do {
6702dc36bedSjoris 						if (c == '\n')
6712dc36bedSjoris 							break;
6722dc36bedSjoris 						ctold++;
6732dc36bedSjoris 					} while (isspace(c = getc(f1)));
6742dc36bedSjoris 					do {
6752dc36bedSjoris 						if (d == '\n')
6762dc36bedSjoris 							break;
6772dc36bedSjoris 						ctnew++;
6782dc36bedSjoris 					} while (isspace(d = getc(f2)));
679f5f501bbSmillert 				} else if ((flags & D_IGNOREBLANKS)) {
6802dc36bedSjoris 					while (isspace(c) && c != '\n') {
6812dc36bedSjoris 						c = getc(f1);
6822dc36bedSjoris 						ctold++;
6832dc36bedSjoris 					}
6842dc36bedSjoris 					while (isspace(d) && d != '\n') {
6852dc36bedSjoris 						d = getc(f2);
6862dc36bedSjoris 						ctnew++;
6872dc36bedSjoris 					}
6882dc36bedSjoris 				}
6892dc36bedSjoris 				if (chrtran[c] != chrtran[d]) {
6902dc36bedSjoris 					jackpot++;
6912dc36bedSjoris 					J[i] = 0;
6922dc36bedSjoris 					if (c != '\n' && c != EOF)
6932dc36bedSjoris 						ctold += skipline(f1);
6942dc36bedSjoris 					if (d != '\n' && c != EOF)
6952dc36bedSjoris 						ctnew += skipline(f2);
6962dc36bedSjoris 					break;
6972dc36bedSjoris 				}
6982dc36bedSjoris 				if (c == '\n' || c == EOF)
6992dc36bedSjoris 					break;
7002dc36bedSjoris 			}
7012dc36bedSjoris 		} else {
7022dc36bedSjoris 			for (;;) {
7032dc36bedSjoris 				ctold++;
7042dc36bedSjoris 				ctnew++;
7052dc36bedSjoris 				if ((c = getc(f1)) != (d = getc(f2))) {
7062dc36bedSjoris 					/* jackpot++; */
7072dc36bedSjoris 					J[i] = 0;
7082dc36bedSjoris 					if (c != '\n' && c != EOF)
7092dc36bedSjoris 						ctold += skipline(f1);
7102dc36bedSjoris 					if (d != '\n' && c != EOF)
7112dc36bedSjoris 						ctnew += skipline(f2);
7122dc36bedSjoris 					break;
7132dc36bedSjoris 				}
7142dc36bedSjoris 				if (c == '\n' || c == EOF)
7152dc36bedSjoris 					break;
7162dc36bedSjoris 			}
7172dc36bedSjoris 		}
7182dc36bedSjoris 		ixold[i] = ctold;
7192dc36bedSjoris 		ixnew[j] = ctnew;
7202dc36bedSjoris 		j++;
7212dc36bedSjoris 	}
722d2c2f9b1Sray 	for (; j <= len[1]; j++)
7232dc36bedSjoris 		ixnew[j] = ctnew += skipline(f2);
7242dc36bedSjoris 	/*
725af90d6d1Sray 	 * if (jackpot)
726af90d6d1Sray 	 *	fprintf(stderr, "jackpot\n");
7272dc36bedSjoris 	 */
7282dc36bedSjoris }
7292dc36bedSjoris 
7302dc36bedSjoris /* shellsort CACM #201 */
7312dc36bedSjoris static void
sort(struct line * a,int n)7322dc36bedSjoris sort(struct line *a, int n)
7332dc36bedSjoris {
7342dc36bedSjoris 	struct line *ai, *aim, w;
7352dc36bedSjoris 	int j, m = 0, k;
7362dc36bedSjoris 
7372dc36bedSjoris 	if (n == 0)
7382dc36bedSjoris 		return;
7392dc36bedSjoris 	for (j = 1; j <= n; j *= 2)
7402dc36bedSjoris 		m = 2 * j - 1;
7412dc36bedSjoris 	for (m /= 2; m != 0; m /= 2) {
7422dc36bedSjoris 		k = n - m;
7432dc36bedSjoris 		for (j = 1; j <= k; j++) {
7442dc36bedSjoris 			for (ai = &a[j]; ai > a; ai -= m) {
7452dc36bedSjoris 				aim = &ai[m];
7462dc36bedSjoris 				if (aim < ai)
7472dc36bedSjoris 					break;	/* wraparound */
7482dc36bedSjoris 				if (aim->value > ai[0].value ||
7492dc36bedSjoris 				    (aim->value == ai[0].value &&
7502dc36bedSjoris 					aim->serial > ai[0].serial))
7512dc36bedSjoris 					break;
7522dc36bedSjoris 				w.value = ai[0].value;
7532dc36bedSjoris 				ai[0].value = aim->value;
7542dc36bedSjoris 				aim->value = w.value;
7552dc36bedSjoris 				w.serial = ai[0].serial;
7562dc36bedSjoris 				ai[0].serial = aim->serial;
7572dc36bedSjoris 				aim->serial = w.serial;
7582dc36bedSjoris 			}
7592dc36bedSjoris 		}
7602dc36bedSjoris 	}
7612dc36bedSjoris }
7622dc36bedSjoris 
7632dc36bedSjoris static void
unsort(struct line * f,int l,int * b)7642dc36bedSjoris unsort(struct line *f, int l, int *b)
7652dc36bedSjoris {
7662dc36bedSjoris 	int *a, i;
7672dc36bedSjoris 
7682dc36bedSjoris 	a = xcalloc(l + 1, sizeof(*a));
7692dc36bedSjoris 	for (i = 1; i <= l; i++)
7702dc36bedSjoris 		a[f[i].serial] = f[i].value;
7712dc36bedSjoris 	for (i = 1; i <= l; i++)
7722dc36bedSjoris 		b[i] = a[i];
7738ac837e5Snicm 	free(a);
7742dc36bedSjoris }
7752dc36bedSjoris 
7762dc36bedSjoris static int
skipline(FILE * f)7772dc36bedSjoris skipline(FILE *f)
7782dc36bedSjoris {
7792dc36bedSjoris 	int i, c;
7802dc36bedSjoris 
7812dc36bedSjoris 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
7822dc36bedSjoris 		continue;
7832dc36bedSjoris 	return (i);
7842dc36bedSjoris }
7852dc36bedSjoris 
7862dc36bedSjoris static void
output(FILE * f1,FILE * f2,int flags)787f5f501bbSmillert output(FILE *f1, FILE *f2, int flags)
7882dc36bedSjoris {
7892dc36bedSjoris 	int m, i0, i1, j0, j1;
7902dc36bedSjoris 
7912dc36bedSjoris 	rewind(f1);
7922dc36bedSjoris 	rewind(f2);
793d2c2f9b1Sray 	m = len[0];
7942dc36bedSjoris 	J[0] = 0;
795d2c2f9b1Sray 	J[m + 1] = len[1] + 1;
7962dc36bedSjoris 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
7972dc36bedSjoris 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
7982dc36bedSjoris 			i0++;
7992dc36bedSjoris 		j0 = J[i0 - 1] + 1;
8002dc36bedSjoris 		i1 = i0 - 1;
8012dc36bedSjoris 		while (i1 < m && J[i1 + 1] == 0)
8022dc36bedSjoris 			i1++;
8032dc36bedSjoris 		j1 = J[i1 + 1] - 1;
8042dc36bedSjoris 		J[i1] = j1;
805f5f501bbSmillert 		change(f1, f2, i0, i1, j0, j1, flags);
8062dc36bedSjoris 	}
8072dc36bedSjoris 	if (m == 0)
808d2c2f9b1Sray 		change(f1, f2, 1, 0, 1, len[1], flags);
8092dc36bedSjoris 	if (diff_format == D_IFDEF) {
8102dc36bedSjoris 		for (;;) {
8112dc36bedSjoris #define	c i0
8122dc36bedSjoris 			if ((c = getc(f1)) == EOF)
8132dc36bedSjoris 				return;
8142dc36bedSjoris 			diff_output("%c", c);
8152dc36bedSjoris 		}
8162dc36bedSjoris #undef c
8172dc36bedSjoris 	}
8182dc36bedSjoris 	if (anychange != 0) {
8192dc36bedSjoris 		if (diff_format == D_CONTEXT)
820f5f501bbSmillert 			dump_context_vec(f1, f2, flags);
8212dc36bedSjoris 		else if (diff_format == D_UNIFIED)
822f5f501bbSmillert 			dump_unified_vec(f1, f2, flags);
8232dc36bedSjoris 	}
8242dc36bedSjoris }
8252dc36bedSjoris 
826ccac42f6Sray static void
range(int a,int b,char * separator)8272dc36bedSjoris range(int a, int b, char *separator)
8282dc36bedSjoris {
8292dc36bedSjoris 	diff_output("%d", a > b ? b : a);
8302dc36bedSjoris 	if (a < b)
8312dc36bedSjoris 		diff_output("%s%d", separator, b);
8322dc36bedSjoris }
8332dc36bedSjoris 
834ccac42f6Sray static void
uni_range(int a,int b)8352dc36bedSjoris uni_range(int a, int b)
8362dc36bedSjoris {
8372dc36bedSjoris 	if (a < b)
8382dc36bedSjoris 		diff_output("%d,%d", a, b - a + 1);
8392dc36bedSjoris 	else if (a == b)
8402dc36bedSjoris 		diff_output("%d", b);
8412dc36bedSjoris 	else
8422dc36bedSjoris 		diff_output("%d,0", b);
8432dc36bedSjoris }
8442dc36bedSjoris 
8452dc36bedSjoris static char *
preadline(int fd,size_t rlen,off_t off)8462dc36bedSjoris preadline(int fd, size_t rlen, off_t off)
8472dc36bedSjoris {
8482dc36bedSjoris 	char *line;
8492dc36bedSjoris 	ssize_t nr;
8502dc36bedSjoris 
8512dc36bedSjoris 	line = xmalloc(rlen + 1);
852*3aaa63ebSderaadt 	if ((nr = pread(fd, line, rlen, off)) == -1)
8537a6efebcSray 		err(D_ERROR, "preadline");
8542dc36bedSjoris 	line[nr] = '\0';
8552dc36bedSjoris 	return (line);
8562dc36bedSjoris }
8572dc36bedSjoris 
8582dc36bedSjoris static int
ignoreline(char * line)8592dc36bedSjoris ignoreline(char *line)
8602dc36bedSjoris {
8612dc36bedSjoris 	int ret;
8622dc36bedSjoris 
863f5f501bbSmillert 	ret = regexec(diff_ignore_re, line, 0, NULL, 0);
8648ac837e5Snicm 	free(line);
8652dc36bedSjoris 	return (ret == 0);	/* if it matched, it should be ignored. */
8662dc36bedSjoris }
8672dc36bedSjoris 
8682dc36bedSjoris /*
8692dc36bedSjoris  * Indicate that there is a difference between lines a and b of the from file
8702dc36bedSjoris  * to get to lines c to d of the to file.  If a is greater then b then there
8712dc36bedSjoris  * are no lines in the from file involved and this means that there were
8722dc36bedSjoris  * lines appended (beginning at b).  If c is greater than d then there are
8732dc36bedSjoris  * lines missing from the to file.
8742dc36bedSjoris  */
8752dc36bedSjoris static void
change(FILE * f1,FILE * f2,int a,int b,int c,int d,int flags)876f5f501bbSmillert change(FILE *f1, FILE *f2, int a, int b, int c, int d, int flags)
8772dc36bedSjoris {
8782dc36bedSjoris 	static size_t max_context = 64;
8792dc36bedSjoris 	char buf[64];
8802dc36bedSjoris 	struct tm *t;
881af90d6d1Sray 	int i;
8822dc36bedSjoris 
8832dc36bedSjoris 	if (diff_format != D_IFDEF && a > b && c > d)
8842dc36bedSjoris 		return;
885f5f501bbSmillert 	if (diff_ignore_re != NULL) {
8862dc36bedSjoris 		char *line;
8872dc36bedSjoris 		/*
8882dc36bedSjoris 		 * All lines in the change, insert, or delete must
8892dc36bedSjoris 		 * match an ignore pattern for the change to be
8902dc36bedSjoris 		 * ignored.
8912dc36bedSjoris 		 */
8922dc36bedSjoris 		if (a <= b) {		/* Changes and deletes. */
8932dc36bedSjoris 			for (i = a; i <= b; i++) {
8942dc36bedSjoris 				line = preadline(fileno(f1),
8952dc36bedSjoris 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
8962dc36bedSjoris 				if (!ignoreline(line))
8972dc36bedSjoris 					goto proceed;
8982dc36bedSjoris 			}
8992dc36bedSjoris 		}
9002dc36bedSjoris 		if (a > b || c <= d) {	/* Changes and inserts. */
9012dc36bedSjoris 			for (i = c; i <= d; i++) {
9022dc36bedSjoris 				line = preadline(fileno(f2),
9032dc36bedSjoris 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
9042dc36bedSjoris 				if (!ignoreline(line))
9052dc36bedSjoris 					goto proceed;
9062dc36bedSjoris 			}
9072dc36bedSjoris 		}
9082dc36bedSjoris 		return;
9092dc36bedSjoris 	}
9102dc36bedSjoris proceed:
9112dc36bedSjoris 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
9122dc36bedSjoris 		/*
9132dc36bedSjoris 		 * Allocate change records as needed.
9142dc36bedSjoris 		 */
9152dc36bedSjoris 		if (context_vec_ptr == context_vec_end - 1) {
9162dc36bedSjoris 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
9172dc36bedSjoris 			max_context <<= 1;
918caa2ffb0Sderaadt 			context_vec_start = xreallocarray(context_vec_start,
91980566be2Sray 			    max_context, sizeof(*context_vec_start));
9202dc36bedSjoris 			context_vec_end = context_vec_start + max_context;
9212dc36bedSjoris 			context_vec_ptr = context_vec_start + offset;
9222dc36bedSjoris 		}
9232dc36bedSjoris 		if (anychange == 0) {
9242dc36bedSjoris 			/*
9252dc36bedSjoris 			 * Print the context/unidiff header first time through.
9262dc36bedSjoris 			 */
9272dc36bedSjoris 			t = localtime(&stb1.st_mtime);
9282dc36bedSjoris 			(void)strftime(buf, sizeof(buf),
9292dc36bedSjoris 			    "%Y/%m/%d %H:%M:%S", t);
9302dc36bedSjoris 
9312dc36bedSjoris 			diff_output("%s %s	%s",
9322dc36bedSjoris 			    diff_format == D_CONTEXT ? "***" : "---", diff_file,
9332dc36bedSjoris 			    buf);
9342dc36bedSjoris 
9352dc36bedSjoris 			if (diff_rev1 != NULL) {
9362dc36bedSjoris 				rcsnum_tostr(diff_rev1, buf, sizeof(buf));
9372dc36bedSjoris 				diff_output("\t%s", buf);
9382dc36bedSjoris 			}
9392dc36bedSjoris 
9402dc36bedSjoris 			printf("\n");
9412dc36bedSjoris 
9422dc36bedSjoris 			t = localtime(&stb2.st_mtime);
9432dc36bedSjoris 			(void)strftime(buf, sizeof(buf),
9442dc36bedSjoris 			    "%Y/%m/%d %H:%M:%S", t);
9452dc36bedSjoris 
9462dc36bedSjoris 			diff_output("%s %s	%s",
9472dc36bedSjoris 			    diff_format == D_CONTEXT ? "---" : "+++", diff_file,
9482dc36bedSjoris 			    buf);
9492dc36bedSjoris 
9502dc36bedSjoris 			if (diff_rev2 != NULL) {
9512dc36bedSjoris 				rcsnum_tostr(diff_rev2, buf, sizeof(buf));
9522dc36bedSjoris 				diff_output("\t%s", buf);
9532dc36bedSjoris 			}
9542dc36bedSjoris 
9552dc36bedSjoris 			printf("\n");
9562dc36bedSjoris 			anychange = 1;
957f5f501bbSmillert 		} else if (a > context_vec_ptr->b + (2 * diff_context) + 1 &&
958f5f501bbSmillert 		    c > context_vec_ptr->d + (2 * diff_context) + 1) {
9592dc36bedSjoris 			/*
960af90d6d1Sray 			 * If this change is more than 'diff_context' lines from the
961af90d6d1Sray 			 * previous change, dump the record and reset it.
9622dc36bedSjoris 			 */
9632dc36bedSjoris 			if (diff_format == D_CONTEXT)
964f5f501bbSmillert 				dump_context_vec(f1, f2, flags);
9652dc36bedSjoris 			else
966f5f501bbSmillert 				dump_unified_vec(f1, f2, flags);
9672dc36bedSjoris 		}
9682dc36bedSjoris 		context_vec_ptr++;
9692dc36bedSjoris 		context_vec_ptr->a = a;
9702dc36bedSjoris 		context_vec_ptr->b = b;
9712dc36bedSjoris 		context_vec_ptr->c = c;
9722dc36bedSjoris 		context_vec_ptr->d = d;
9732dc36bedSjoris 		return;
9742dc36bedSjoris 	}
9752dc36bedSjoris 	if (anychange == 0)
9762dc36bedSjoris 		anychange = 1;
9772dc36bedSjoris 	switch (diff_format) {
9782dc36bedSjoris 	case D_BRIEF:
9792dc36bedSjoris 		return;
9802dc36bedSjoris 	case D_NORMAL:
9812dc36bedSjoris 		range(a, b, ",");
9822dc36bedSjoris 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
9832dc36bedSjoris 		if (diff_format == D_NORMAL)
9842dc36bedSjoris 			range(c, d, ",");
9852dc36bedSjoris 		diff_output("\n");
9862dc36bedSjoris 		break;
9872dc36bedSjoris 	case D_RCSDIFF:
9882dc36bedSjoris 		if (a > b)
9892dc36bedSjoris 			diff_output("a%d %d\n", b, d - c + 1);
9902dc36bedSjoris 		else {
9912dc36bedSjoris 			diff_output("d%d %d\n", a, b - a + 1);
9922dc36bedSjoris 
9932dc36bedSjoris 			if (!(c > d))	/* add changed lines */
9942dc36bedSjoris 				diff_output("a%d %d\n", b, d - c + 1);
9952dc36bedSjoris 		}
9962dc36bedSjoris 		break;
9972dc36bedSjoris 	}
9982dc36bedSjoris 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
999f5f501bbSmillert 		fetch(ixold, a, b, f1, '<', 1, flags);
10002dc36bedSjoris 		if (a <= b && c <= d && diff_format == D_NORMAL)
10012dc36bedSjoris 			diff_output("---\n");
10022dc36bedSjoris 	}
1003f5f501bbSmillert 	fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0, flags);
10042dc36bedSjoris 	if (inifdef) {
10052dc36bedSjoris 		diff_output("#endif /* %s */\n", ifdefname);
10062dc36bedSjoris 		inifdef = 0;
10072dc36bedSjoris 	}
10082dc36bedSjoris }
10092dc36bedSjoris 
10102dc36bedSjoris static void
fetch(long * f,int a,int b,FILE * lb,int ch,int oldfile,int flags)1011f5f501bbSmillert fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags)
10122dc36bedSjoris {
10132dc36bedSjoris 	long j, nc;
10142dc36bedSjoris 	int i, c, col;
10152dc36bedSjoris 
10162dc36bedSjoris 	/*
10172dc36bedSjoris 	 * When doing #ifdef's, copy down to current line
10182dc36bedSjoris 	 * if this is the first file, so that stuff makes it to output.
10192dc36bedSjoris 	 */
10202dc36bedSjoris 	if (diff_format == D_IFDEF && oldfile) {
10212dc36bedSjoris 		long curpos = ftell(lb);
10222dc36bedSjoris 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
10232dc36bedSjoris 		nc = f[a > b ? b : a - 1] - curpos;
10242dc36bedSjoris 		for (i = 0; i < nc; i++)
10252dc36bedSjoris 			diff_output("%c", getc(lb));
10262dc36bedSjoris 	}
10272dc36bedSjoris 	if (a > b)
10282dc36bedSjoris 		return;
10292dc36bedSjoris 	if (diff_format == D_IFDEF) {
10302dc36bedSjoris 		if (inifdef) {
10312dc36bedSjoris 			diff_output("#else /* %s%s */\n",
10322dc36bedSjoris 			    oldfile == 1 ? "!" : "", ifdefname);
10332dc36bedSjoris 		} else {
10342dc36bedSjoris 			if (oldfile)
10352dc36bedSjoris 				diff_output("#ifndef %s\n", ifdefname);
10362dc36bedSjoris 			else
10372dc36bedSjoris 				diff_output("#ifdef %s\n", ifdefname);
10382dc36bedSjoris 		}
10392dc36bedSjoris 		inifdef = 1 + oldfile;
10402dc36bedSjoris 	}
10412dc36bedSjoris 	for (i = a; i <= b; i++) {
10422dc36bedSjoris 		fseek(lb, f[i - 1], SEEK_SET);
10432dc36bedSjoris 		nc = f[i] - f[i - 1];
10442dc36bedSjoris 		if (diff_format != D_IFDEF && ch != '\0') {
10452dc36bedSjoris 			diff_output("%c", ch);
1046f5f501bbSmillert 			if (diff_format != D_UNIFIED)
10472dc36bedSjoris 				diff_output(" ");
10482dc36bedSjoris 		}
10492dc36bedSjoris 		col = 0;
10502dc36bedSjoris 		for (j = 0; j < nc; j++) {
10512dc36bedSjoris 			if ((c = getc(lb)) == EOF) {
10522dc36bedSjoris 				if (diff_format == D_RCSDIFF)
1053300eb659Sray 					warnx("No newline at end of file");
10542dc36bedSjoris 				else
10552dc36bedSjoris 					diff_output("\n\\ No newline at end of "
10562dc36bedSjoris 					    "file");
10572dc36bedSjoris 				return;
10582dc36bedSjoris 			}
1059f5f501bbSmillert 			if (c == '\t' && (flags & D_EXPANDTABS)) {
10602dc36bedSjoris 				do {
10612dc36bedSjoris 					diff_output(" ");
10622dc36bedSjoris 				} while (++col & 7);
10632dc36bedSjoris 			} else {
10642dc36bedSjoris 				diff_output("%c", c);
10652dc36bedSjoris 				col++;
10662dc36bedSjoris 			}
10672dc36bedSjoris 		}
10682dc36bedSjoris 	}
10692dc36bedSjoris }
10702dc36bedSjoris 
10712dc36bedSjoris /*
10722dc36bedSjoris  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
10732dc36bedSjoris  */
10742dc36bedSjoris static int
readhash(FILE * f,int flags)1075f5f501bbSmillert readhash(FILE *f, int flags)
10762dc36bedSjoris {
10772dc36bedSjoris 	int i, t, space;
10782dc36bedSjoris 	int sum;
10792dc36bedSjoris 
10802dc36bedSjoris 	sum = 1;
10812dc36bedSjoris 	space = 0;
1082f5f501bbSmillert 	if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) == 0) {
1083f5f501bbSmillert 		if (flags & D_IGNORECASE)
10842dc36bedSjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
10852dc36bedSjoris 				if (t == EOF) {
10862dc36bedSjoris 					if (i == 0)
10872dc36bedSjoris 						return (0);
10882dc36bedSjoris 					break;
10892dc36bedSjoris 				}
10902dc36bedSjoris 				sum = sum * 127 + chrtran[t];
10912dc36bedSjoris 			}
10922dc36bedSjoris 		else
10932dc36bedSjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
10942dc36bedSjoris 				if (t == EOF) {
10952dc36bedSjoris 					if (i == 0)
10962dc36bedSjoris 						return (0);
10972dc36bedSjoris 					break;
10982dc36bedSjoris 				}
10992dc36bedSjoris 				sum = sum * 127 + t;
11002dc36bedSjoris 			}
11012dc36bedSjoris 	} else {
11022dc36bedSjoris 		for (i = 0;;) {
11032dc36bedSjoris 			switch (t = getc(f)) {
11042dc36bedSjoris 			case '\t':
1105af90d6d1Sray 			case '\r':
1106af90d6d1Sray 			case '\v':
1107af90d6d1Sray 			case '\f':
11082dc36bedSjoris 			case ' ':
11092dc36bedSjoris 				space++;
11102dc36bedSjoris 				continue;
11112dc36bedSjoris 			default:
1112f5f501bbSmillert 				if (space && (flags & D_IGNOREBLANKS) == 0) {
11132dc36bedSjoris 					i++;
11142dc36bedSjoris 					space = 0;
11152dc36bedSjoris 				}
11162dc36bedSjoris 				sum = sum * 127 + chrtran[t];
11172dc36bedSjoris 				i++;
11182dc36bedSjoris 				continue;
11192dc36bedSjoris 			case EOF:
11202dc36bedSjoris 				if (i == 0)
11212dc36bedSjoris 					return (0);
11222dc36bedSjoris 				/* FALLTHROUGH */
11232dc36bedSjoris 			case '\n':
11242dc36bedSjoris 				break;
11252dc36bedSjoris 			}
11262dc36bedSjoris 			break;
11272dc36bedSjoris 		}
11282dc36bedSjoris 	}
11292dc36bedSjoris 	/*
11302dc36bedSjoris 	 * There is a remote possibility that we end up with a zero sum.
11312dc36bedSjoris 	 * Zero is used as an EOF marker, so return 1 instead.
11322dc36bedSjoris 	 */
11332dc36bedSjoris 	return (sum == 0 ? 1 : sum);
11342dc36bedSjoris }
11352dc36bedSjoris 
11362dc36bedSjoris static int
asciifile(FILE * f)11372dc36bedSjoris asciifile(FILE *f)
11382dc36bedSjoris {
1139af90d6d1Sray 	unsigned char buf[BUFSIZ];
11400a91b02cSstsp 	size_t cnt;
11412dc36bedSjoris 
1142f5f501bbSmillert 	if (f == NULL)
11432dc36bedSjoris 		return (1);
11442dc36bedSjoris 
11452dc36bedSjoris 	rewind(f);
114685ea658bSray 	cnt = fread(buf, 1, sizeof(buf), f);
11470a91b02cSstsp 	return (memchr(buf, '\0', cnt) == NULL);
11482dc36bedSjoris }
11492dc36bedSjoris 
1150e22e6974Sxsa #define begins_with(s, pre) (strncmp(s, pre, sizeof(pre)-1) == 0)
1151e22e6974Sxsa 
11522dc36bedSjoris static char *
match_function(const long * f,int pos,FILE * fp)11532dc36bedSjoris match_function(const long *f, int pos, FILE *fp)
11542dc36bedSjoris {
11552dc36bedSjoris 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
11562dc36bedSjoris 	size_t nc;
11572dc36bedSjoris 	int last = lastline;
1158e22e6974Sxsa 	char *state = NULL;
11592dc36bedSjoris 
11602dc36bedSjoris 	lastline = pos;
11612dc36bedSjoris 	while (pos > last) {
11622dc36bedSjoris 		fseek(fp, f[pos - 1], SEEK_SET);
11632dc36bedSjoris 		nc = f[pos] - f[pos - 1];
11642dc36bedSjoris 		if (nc >= sizeof(buf))
11652dc36bedSjoris 			nc = sizeof(buf) - 1;
116685ea658bSray 		nc = fread(buf, 1, nc, fp);
11672dc36bedSjoris 		if (nc > 0) {
11682dc36bedSjoris 			buf[nc] = '\0';
11694fd6ed32Sgilles 			buf[strcspn(buf, "\n")] = '\0';
11702dc36bedSjoris 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
1171e22e6974Sxsa 				if (begins_with(buf, "private:")) {
1172e22e6974Sxsa 					if (!state)
1173e22e6974Sxsa 						state = " (private)";
1174e22e6974Sxsa 				} else if (begins_with(buf, "protected:")) {
1175e22e6974Sxsa 					if (!state)
1176e22e6974Sxsa 						state = " (protected)";
1177e22e6974Sxsa 				} else if (begins_with(buf, "public:")) {
1178e22e6974Sxsa 					if (!state)
1179e22e6974Sxsa 						state = " (public)";
1180e22e6974Sxsa 				} else {
1181eea21b3cSray 					if (strlcpy(lastbuf, buf,
11826b99bb86Sray 					    sizeof(lastbuf)) >= sizeof(lastbuf))
1183e22e6974Sxsa 						errx(1,
1184e22e6974Sxsa 						    "match_function: strlcpy");
11852dc36bedSjoris 					lastmatchline = pos;
11862dc36bedSjoris 					return lastbuf;
11872dc36bedSjoris 				}
11882dc36bedSjoris 			}
1189e22e6974Sxsa 		}
11902dc36bedSjoris 		pos--;
11912dc36bedSjoris 	}
1192af90d6d1Sray 	return lastmatchline > 0 ? lastbuf : NULL;
11932dc36bedSjoris }
11942dc36bedSjoris 
11952dc36bedSjoris /* dump accumulated "context" diff changes */
11962dc36bedSjoris static void
dump_context_vec(FILE * f1,FILE * f2,int flags)1197f5f501bbSmillert dump_context_vec(FILE *f1, FILE *f2, int flags)
11982dc36bedSjoris {
11992dc36bedSjoris 	struct context_vec *cvp = context_vec_start;
12002dc36bedSjoris 	int lowa, upb, lowc, upd, do_output;
12012dc36bedSjoris 	int a, b, c, d;
12022dc36bedSjoris 	char ch, *f;
12032dc36bedSjoris 
12042dc36bedSjoris 	if (context_vec_start > context_vec_ptr)
12052dc36bedSjoris 		return;
12062dc36bedSjoris 
12072dc36bedSjoris 	b = d = 0;		/* gcc */
1208b9fc9a72Sderaadt 	lowa = MAXIMUM(1, cvp->a - diff_context);
1209b9fc9a72Sderaadt 	upb = MINIMUM(len[0], context_vec_ptr->b + diff_context);
1210b9fc9a72Sderaadt 	lowc = MAXIMUM(1, cvp->c - diff_context);
1211b9fc9a72Sderaadt 	upd = MINIMUM(len[1], context_vec_ptr->d + diff_context);
12122dc36bedSjoris 
12132dc36bedSjoris 	diff_output("***************");
1214f5f501bbSmillert 	if ((flags & D_PROTOTYPE)) {
12152dc36bedSjoris 		f = match_function(ixold, lowa-1, f1);
1216d5d01609Sray 		if (f != NULL)
12172dc36bedSjoris 			diff_output(" %s", f);
12182dc36bedSjoris 	}
12192dc36bedSjoris 	diff_output("\n*** ");
12202dc36bedSjoris 	range(lowa, upb, ",");
12212dc36bedSjoris 	diff_output(" ****\n");
12222dc36bedSjoris 
12232dc36bedSjoris 	/*
12242dc36bedSjoris 	 * Output changes to the "old" file.  The first loop suppresses
12252dc36bedSjoris 	 * output if there were no changes to the "old" file (we'll see
12262dc36bedSjoris 	 * the "old" lines as context in the "new" list).
12272dc36bedSjoris 	 */
12282dc36bedSjoris 	do_output = 0;
12292dc36bedSjoris 	for (; cvp <= context_vec_ptr; cvp++)
12302dc36bedSjoris 		if (cvp->a <= cvp->b) {
12312dc36bedSjoris 			cvp = context_vec_start;
12322dc36bedSjoris 			do_output++;
12332dc36bedSjoris 			break;
12342dc36bedSjoris 		}
1235af90d6d1Sray 	if (do_output) {
12362dc36bedSjoris 		while (cvp <= context_vec_ptr) {
12372dc36bedSjoris 			a = cvp->a;
12382dc36bedSjoris 			b = cvp->b;
12392dc36bedSjoris 			c = cvp->c;
12402dc36bedSjoris 			d = cvp->d;
12412dc36bedSjoris 
12422dc36bedSjoris 			if (a <= b && c <= d)
12432dc36bedSjoris 				ch = 'c';
12442dc36bedSjoris 			else
12452dc36bedSjoris 				ch = (a <= b) ? 'd' : 'a';
12462dc36bedSjoris 
12472dc36bedSjoris 			if (ch == 'a')
1248f5f501bbSmillert 				fetch(ixold, lowa, b, f1, ' ', 0, flags);
12492dc36bedSjoris 			else {
1250f5f501bbSmillert 				fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
12512dc36bedSjoris 				fetch(ixold, a, b, f1,
1252f5f501bbSmillert 				    ch == 'c' ? '!' : '-', 0, flags);
12532dc36bedSjoris 			}
12542dc36bedSjoris 			lowa = b + 1;
12552dc36bedSjoris 			cvp++;
12562dc36bedSjoris 		}
1257f5f501bbSmillert 		fetch(ixold, b + 1, upb, f1, ' ', 0, flags);
12582dc36bedSjoris 	}
12592dc36bedSjoris 	/* output changes to the "new" file */
12602dc36bedSjoris 	diff_output("--- ");
12612dc36bedSjoris 	range(lowc, upd, ",");
12622dc36bedSjoris 	diff_output(" ----\n");
12632dc36bedSjoris 
12642dc36bedSjoris 	do_output = 0;
12652dc36bedSjoris 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
12662dc36bedSjoris 		if (cvp->c <= cvp->d) {
12672dc36bedSjoris 			cvp = context_vec_start;
12682dc36bedSjoris 			do_output++;
12692dc36bedSjoris 			break;
12702dc36bedSjoris 		}
1271af90d6d1Sray 	if (do_output) {
12722dc36bedSjoris 		while (cvp <= context_vec_ptr) {
12732dc36bedSjoris 			a = cvp->a;
12742dc36bedSjoris 			b = cvp->b;
12752dc36bedSjoris 			c = cvp->c;
12762dc36bedSjoris 			d = cvp->d;
12772dc36bedSjoris 
12782dc36bedSjoris 			if (a <= b && c <= d)
12792dc36bedSjoris 				ch = 'c';
12802dc36bedSjoris 			else
12812dc36bedSjoris 				ch = (a <= b) ? 'd' : 'a';
12822dc36bedSjoris 
12832dc36bedSjoris 			if (ch == 'd')
1284f5f501bbSmillert 				fetch(ixnew, lowc, d, f2, ' ', 0, flags);
12852dc36bedSjoris 			else {
1286f5f501bbSmillert 				fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
12872dc36bedSjoris 				fetch(ixnew, c, d, f2,
1288f5f501bbSmillert 				    ch == 'c' ? '!' : '+', 0, flags);
12892dc36bedSjoris 			}
12902dc36bedSjoris 			lowc = d + 1;
12912dc36bedSjoris 			cvp++;
12922dc36bedSjoris 		}
1293f5f501bbSmillert 		fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
12942dc36bedSjoris 	}
12952dc36bedSjoris 	context_vec_ptr = context_vec_start - 1;
12962dc36bedSjoris }
12972dc36bedSjoris 
12982dc36bedSjoris /* dump accumulated "unified" diff changes */
12992dc36bedSjoris static void
dump_unified_vec(FILE * f1,FILE * f2,int flags)1300f5f501bbSmillert dump_unified_vec(FILE *f1, FILE *f2, int flags)
13012dc36bedSjoris {
13022dc36bedSjoris 	struct context_vec *cvp = context_vec_start;
13032dc36bedSjoris 	int lowa, upb, lowc, upd;
13042dc36bedSjoris 	int a, b, c, d;
13052dc36bedSjoris 	char ch, *f;
13062dc36bedSjoris 
13072dc36bedSjoris 	if (context_vec_start > context_vec_ptr)
13082dc36bedSjoris 		return;
13092dc36bedSjoris 
131093443395Sotto 	d = 0; /* gcc */
1311b9fc9a72Sderaadt 	lowa = MAXIMUM(1, cvp->a - diff_context);
1312b9fc9a72Sderaadt 	upb = MINIMUM(len[0], context_vec_ptr->b + diff_context);
1313b9fc9a72Sderaadt 	lowc = MAXIMUM(1, cvp->c - diff_context);
1314b9fc9a72Sderaadt 	upd = MINIMUM(len[1], context_vec_ptr->d + diff_context);
13152dc36bedSjoris 
13162dc36bedSjoris 	diff_output("@@ -");
13172dc36bedSjoris 	uni_range(lowa, upb);
13182dc36bedSjoris 	diff_output(" +");
13192dc36bedSjoris 	uni_range(lowc, upd);
13202dc36bedSjoris 	diff_output(" @@");
1321f5f501bbSmillert 	if ((flags & D_PROTOTYPE)) {
13222dc36bedSjoris 		f = match_function(ixold, lowa-1, f1);
1323d5d01609Sray 		if (f != NULL)
13242dc36bedSjoris 			diff_output(" %s", f);
13252dc36bedSjoris 	}
13262dc36bedSjoris 	diff_output("\n");
13272dc36bedSjoris 
13282dc36bedSjoris 	/*
13292dc36bedSjoris 	 * Output changes in "unified" diff format--the old and new lines
13302dc36bedSjoris 	 * are printed together.
13312dc36bedSjoris 	 */
13322dc36bedSjoris 	for (; cvp <= context_vec_ptr; cvp++) {
13332dc36bedSjoris 		a = cvp->a;
13342dc36bedSjoris 		b = cvp->b;
13352dc36bedSjoris 		c = cvp->c;
13362dc36bedSjoris 		d = cvp->d;
13372dc36bedSjoris 
13382dc36bedSjoris 		/*
13392dc36bedSjoris 		 * c: both new and old changes
13402dc36bedSjoris 		 * d: only changes in the old file
13412dc36bedSjoris 		 * a: only changes in the new file
13422dc36bedSjoris 		 */
13432dc36bedSjoris 		if (a <= b && c <= d)
13442dc36bedSjoris 			ch = 'c';
13452dc36bedSjoris 		else
13462dc36bedSjoris 			ch = (a <= b) ? 'd' : 'a';
13472dc36bedSjoris 
13482dc36bedSjoris 		switch (ch) {
13492dc36bedSjoris 		case 'c':
1350f5f501bbSmillert 			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1351f5f501bbSmillert 			fetch(ixold, a, b, f1, '-', 0, flags);
1352f5f501bbSmillert 			fetch(ixnew, c, d, f2, '+', 0, flags);
13532dc36bedSjoris 			break;
13542dc36bedSjoris 		case 'd':
1355f5f501bbSmillert 			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1356f5f501bbSmillert 			fetch(ixold, a, b, f1, '-', 0, flags);
13572dc36bedSjoris 			break;
13582dc36bedSjoris 		case 'a':
1359f5f501bbSmillert 			fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
1360f5f501bbSmillert 			fetch(ixnew, c, d, f2, '+', 0, flags);
13612dc36bedSjoris 			break;
13622dc36bedSjoris 		}
13632dc36bedSjoris 		lowa = b + 1;
13642dc36bedSjoris 		lowc = d + 1;
13652dc36bedSjoris 	}
1366f5f501bbSmillert 	fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
13672dc36bedSjoris 
13682dc36bedSjoris 	context_vec_ptr = context_vec_start - 1;
13692dc36bedSjoris }
13702dc36bedSjoris 
13712dc36bedSjoris void
diff_output(const char * fmt,...)13722dc36bedSjoris diff_output(const char *fmt, ...)
13732dc36bedSjoris {
13742dc36bedSjoris 	va_list vap;
13752dc36bedSjoris 	int i;
13762dc36bedSjoris 	char *str;
13772dc36bedSjoris 
13782dc36bedSjoris 	va_start(vap, fmt);
13792dc36bedSjoris 	i = vasprintf(&str, fmt, vap);
13802dc36bedSjoris 	va_end(vap);
13812dc36bedSjoris 	if (i == -1)
1382d4625ebbSxsa 		err(1, "diff_output");
13832dc36bedSjoris 	if (diffbuf != NULL)
13847bb3ddb0Sray 		buf_append(diffbuf, str, strlen(str));
13852dc36bedSjoris 	else
13862dc36bedSjoris 		printf("%s", str);
13878ac837e5Snicm 	free(str);
13882dc36bedSjoris }
1389