xref: /openbsd-src/usr.bin/rcs/diff.c (revision f74aa433d7838ae53d3a7e1b45b0fff26ae00ea3)
1*f74aa433Sray /*	$OpenBSD: diff.c,v 1.18 2007/05/29 08:02:59 ray 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  */
662dc36bedSjoris /*
672dc36bedSjoris  *	Uses an algorithm due to Harold Stone, which finds
682dc36bedSjoris  *	a pair of longest identical subsequences in the two
692dc36bedSjoris  *	files.
702dc36bedSjoris  *
712dc36bedSjoris  *	The major goal is to generate the match vector J.
722dc36bedSjoris  *	J[i] is the index of the line in file1 corresponding
732dc36bedSjoris  *	to line i file0. J[i] = 0 if there is no
742dc36bedSjoris  *	such line in file1.
752dc36bedSjoris  *
762dc36bedSjoris  *	Lines are hashed so as to work in core. All potential
772dc36bedSjoris  *	matches are located by sorting the lines of each file
782dc36bedSjoris  *	on the hash (called ``value''). In particular, this
792dc36bedSjoris  *	collects the equivalence classes in file1 together.
802dc36bedSjoris  *	Subroutine equiv replaces the value of each line in
812dc36bedSjoris  *	file0 by the index of the first element of its
822dc36bedSjoris  *	matching equivalence in (the reordered) file1.
832dc36bedSjoris  *	To save space equiv squeezes file1 into a single
842dc36bedSjoris  *	array member in which the equivalence classes
852dc36bedSjoris  *	are simply concatenated, except that their first
862dc36bedSjoris  *	members are flagged by changing sign.
872dc36bedSjoris  *
882dc36bedSjoris  *	Next the indices that point into member are unsorted into
892dc36bedSjoris  *	array class according to the original order of file0.
902dc36bedSjoris  *
912dc36bedSjoris  *	The cleverness lies in routine stone. This marches
922dc36bedSjoris  *	through the lines of file0, developing a vector klist
932dc36bedSjoris  *	of "k-candidates". At step i a k-candidate is a matched
942dc36bedSjoris  *	pair of lines x,y (x in file0 y in file1) such that
952dc36bedSjoris  *	there is a common subsequence of length k
962dc36bedSjoris  *	between the first i lines of file0 and the first y
972dc36bedSjoris  *	lines of file1, but there is no such subsequence for
982dc36bedSjoris  *	any smaller y. x is the earliest possible mate to y
992dc36bedSjoris  *	that occurs in such a subsequence.
1002dc36bedSjoris  *
1012dc36bedSjoris  *	Whenever any of the members of the equivalence class of
1022dc36bedSjoris  *	lines in file1 matable to a line in file0 has serial number
1032dc36bedSjoris  *	less than the y of some k-candidate, that k-candidate
1042dc36bedSjoris  *	with the smallest such y is replaced. The new
1052dc36bedSjoris  *	k-candidate is chained (via pred) to the current
1062dc36bedSjoris  *	k-1 candidate so that the actual subsequence can
1072dc36bedSjoris  *	be recovered. When a member has serial number greater
1082dc36bedSjoris  *	that the y of all k-candidates, the klist is extended.
1092dc36bedSjoris  *	At the end, the longest subsequence is pulled out
1102dc36bedSjoris  *	and placed in the array J by unravel
1112dc36bedSjoris  *
1122dc36bedSjoris  *	With J in hand, the matches there recorded are
1132dc36bedSjoris  *	check'ed against reality to assure that no spurious
1142dc36bedSjoris  *	matches have crept in due to hashing. If they have,
1152dc36bedSjoris  *	they are broken, and "jackpot" is recorded--a harmless
1162dc36bedSjoris  *	matter except that a true match for a spuriously
1172dc36bedSjoris  *	mated line may now be unnecessarily reported as a change.
1182dc36bedSjoris  *
1192dc36bedSjoris  *	Much of the complexity of the program comes simply
1202dc36bedSjoris  *	from trying to minimize core utilization and
1212dc36bedSjoris  *	maximize the range of doable problems by dynamically
1222dc36bedSjoris  *	allocating what is needed and reusing what is not.
1232dc36bedSjoris  *	The core requirements for problems larger than somewhat
1242dc36bedSjoris  *	are (in words) 2*length(file0) + length(file1) +
1252dc36bedSjoris  *	3*(number of k-candidates installed),  typically about
1262dc36bedSjoris  *	6n words for files of length n.
1272dc36bedSjoris  */
1282dc36bedSjoris 
1294781e2faSxsa #include <sys/param.h>
1304781e2faSxsa #include <sys/stat.h>
1314781e2faSxsa 
1324781e2faSxsa #include <ctype.h>
1334781e2faSxsa #include <err.h>
1344781e2faSxsa #include <limits.h>
1354781e2faSxsa #include <stdarg.h>
1364781e2faSxsa #include <stddef.h>
1374781e2faSxsa #include <stdio.h>
1384781e2faSxsa #include <string.h>
1394781e2faSxsa #include <unistd.h>
1402dc36bedSjoris 
1412dc36bedSjoris #include "buf.h"
1422dc36bedSjoris #include "diff.h"
1432dc36bedSjoris #include "xmalloc.h"
1442dc36bedSjoris 
1452dc36bedSjoris struct cand {
1462dc36bedSjoris 	int	x;
1472dc36bedSjoris 	int	y;
1482dc36bedSjoris 	int	pred;
1492dc36bedSjoris } cand;
1502dc36bedSjoris 
1512dc36bedSjoris struct line {
1522dc36bedSjoris 	int	serial;
1532dc36bedSjoris 	int	value;
1542dc36bedSjoris } *file[2];
1552dc36bedSjoris 
1562dc36bedSjoris /*
1572dc36bedSjoris  * The following struct is used to record change information when
1582dc36bedSjoris  * doing a "context" or "unified" diff.  (see routine "change" to
1592dc36bedSjoris  * understand the highly mnemonic field names)
1602dc36bedSjoris  */
1612dc36bedSjoris struct context_vec {
1622dc36bedSjoris 	int	a;		/* start line in old file */
1632dc36bedSjoris 	int	b;		/* end line in old file */
1642dc36bedSjoris 	int	c;		/* start line in new file */
1652dc36bedSjoris 	int	d;		/* end line in new file */
1662dc36bedSjoris };
1672dc36bedSjoris 
1682dc36bedSjoris struct diff_arg {
1692dc36bedSjoris 	char	*rev1;
1702dc36bedSjoris 	char	*rev2;
1712dc36bedSjoris 	char	*date1;
1722dc36bedSjoris 	char	*date2;
1732dc36bedSjoris };
1742dc36bedSjoris 
175f5f501bbSmillert static void	 output(FILE *, FILE *, int);
176f5f501bbSmillert static void	 check(FILE *, FILE *, int);
1772dc36bedSjoris static void	 range(int, int, char *);
1782dc36bedSjoris static void	 uni_range(int, int);
179f5f501bbSmillert static void	 dump_context_vec(FILE *, FILE *, int);
180f5f501bbSmillert static void	 dump_unified_vec(FILE *, FILE *, int);
181f5f501bbSmillert static int	 prepare(int, FILE *, off_t, int);
1822dc36bedSjoris static void	 prune(void);
1832dc36bedSjoris static void	 equiv(struct line *, int, struct line *, int, int *);
1842dc36bedSjoris static void	 unravel(int);
1852dc36bedSjoris static void	 unsort(struct line *, int, int *);
186f5f501bbSmillert static void	 change(FILE *, FILE *, int, int, int, int, int);
1872dc36bedSjoris static void	 sort(struct line *, int);
1882dc36bedSjoris static int	 ignoreline(char *);
1892dc36bedSjoris static int	 asciifile(FILE *);
190f5f501bbSmillert static void	 fetch(long *, int, int, FILE *, int, int, int);
1912dc36bedSjoris static int	 newcand(int, int, int);
1922dc36bedSjoris static int	 search(int *, int, int);
1932dc36bedSjoris static int	 skipline(FILE *);
1942dc36bedSjoris static int	 isqrt(int);
195f5f501bbSmillert static int	 stone(int *, int, int *, int *, int);
196f5f501bbSmillert static int	 readhash(FILE *, int);
1972dc36bedSjoris static int	 files_differ(FILE *, FILE *);
1982dc36bedSjoris static char	*match_function(const long *, int, FILE *);
1992dc36bedSjoris static char	*preadline(int, size_t, off_t);
2002dc36bedSjoris 
2012dc36bedSjoris 
202f5f501bbSmillert int diff_context = 3;
2032dc36bedSjoris int diff_format = D_NORMAL;
2042dc36bedSjoris char *diff_file = NULL;
2052dc36bedSjoris RCSNUM *diff_rev1 = NULL;
2062dc36bedSjoris RCSNUM *diff_rev2 = NULL;
207f5f501bbSmillert char diffargs[512]; /* XXX */
2082dc36bedSjoris static struct stat stb1, stb2;
209f5f501bbSmillert static char *ifdefname;
210f5f501bbSmillert regex_t *diff_ignore_re;
2112dc36bedSjoris 
2122dc36bedSjoris static int  *J;			/* will be overlaid on class */
2132dc36bedSjoris static int  *class;		/* will be overlaid on file[0] */
2142dc36bedSjoris static int  *klist;		/* will be overlaid on file[0] after class */
2152dc36bedSjoris static int  *member;		/* will be overlaid on file[1] */
2162dc36bedSjoris static int   clen;
2172dc36bedSjoris static int   inifdef;		/* whether or not we are in a #ifdef block */
2182dc36bedSjoris static int   diff_len[2];
2192dc36bedSjoris static int   pref, suff;	/* length of prefix and suffix */
2202dc36bedSjoris static int   slen[2];
2212dc36bedSjoris static int   anychange;
2222dc36bedSjoris static long *ixnew;		/* will be overlaid on file[1] */
2232dc36bedSjoris static long *ixold;		/* will be overlaid on klist */
2242dc36bedSjoris static struct cand *clist;	/* merely a free storage pot for candidates */
2252dc36bedSjoris static int   clistlen;		/* the length of clist */
2262dc36bedSjoris static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
2272dc36bedSjoris static u_char *chrtran;		/* translation table for case-folding */
2282dc36bedSjoris static struct context_vec *context_vec_start;
2292dc36bedSjoris static struct context_vec *context_vec_end;
2302dc36bedSjoris static struct context_vec *context_vec_ptr;
2312dc36bedSjoris 
232e22e6974Sxsa #define FUNCTION_CONTEXT_SIZE	55
2332dc36bedSjoris static char lastbuf[FUNCTION_CONTEXT_SIZE];
2342dc36bedSjoris static int lastline;
2352dc36bedSjoris static int lastmatchline;
2362dc36bedSjoris BUF  *diffbuf = NULL;
2372dc36bedSjoris 
2382dc36bedSjoris /*
2392dc36bedSjoris  * chrtran points to one of 2 translation tables: cup2low if folding upper to
2402dc36bedSjoris  * lower case clow2low if not folding case
2412dc36bedSjoris  */
2422dc36bedSjoris u_char clow2low[256] = {
2432dc36bedSjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2442dc36bedSjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2452dc36bedSjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2462dc36bedSjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2472dc36bedSjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2482dc36bedSjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
2492dc36bedSjoris 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
2502dc36bedSjoris 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
2512dc36bedSjoris 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
2522dc36bedSjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2532dc36bedSjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2542dc36bedSjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2552dc36bedSjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2562dc36bedSjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2572dc36bedSjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2582dc36bedSjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2592dc36bedSjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2602dc36bedSjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2612dc36bedSjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2622dc36bedSjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2632dc36bedSjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2642dc36bedSjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2652dc36bedSjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2662dc36bedSjoris 	0xfd, 0xfe, 0xff
2672dc36bedSjoris };
2682dc36bedSjoris 
2692dc36bedSjoris u_char cup2low[256] = {
2702dc36bedSjoris 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
2712dc36bedSjoris 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
2722dc36bedSjoris 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
2732dc36bedSjoris 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2742dc36bedSjoris 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
2752dc36bedSjoris 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
2762dc36bedSjoris 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
2772dc36bedSjoris 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
2782dc36bedSjoris 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
2792dc36bedSjoris 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
2802dc36bedSjoris 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
2812dc36bedSjoris 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
2822dc36bedSjoris 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
2832dc36bedSjoris 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
2842dc36bedSjoris 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
2852dc36bedSjoris 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
2862dc36bedSjoris 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
2872dc36bedSjoris 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2882dc36bedSjoris 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
2892dc36bedSjoris 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
2902dc36bedSjoris 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
2912dc36bedSjoris 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
2922dc36bedSjoris 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
2932dc36bedSjoris 	0xfd, 0xfe, 0xff
2942dc36bedSjoris };
2952dc36bedSjoris 
2962dc36bedSjoris int
297f5f501bbSmillert rcs_diffreg(const char *file1, const char *file2, BUF *out, int flags)
2982dc36bedSjoris {
2992dc36bedSjoris 	FILE *f1, *f2;
3002dc36bedSjoris 	int i, rval;
3012dc36bedSjoris 
3022dc36bedSjoris 	f1 = f2 = NULL;
3032dc36bedSjoris 	rval = D_SAME;
3042dc36bedSjoris 	anychange = 0;
3052dc36bedSjoris 	lastline = 0;
3062dc36bedSjoris 	lastmatchline = 0;
3072dc36bedSjoris 	context_vec_ptr = context_vec_start - 1;
308f5f501bbSmillert 	if (flags & D_IGNORECASE)
309f5f501bbSmillert 		chrtran = cup2low;
310f5f501bbSmillert 	else
311f5f501bbSmillert 		chrtran = clow2low;
3122dc36bedSjoris 	if (out != NULL)
3132dc36bedSjoris 		diffbuf = out;
3142dc36bedSjoris 
3152dc36bedSjoris 	f1 = fopen(file1, "r");
3162dc36bedSjoris 	if (f1 == NULL) {
3172dc36bedSjoris 		warn("%s", file1);
3182dc36bedSjoris 		goto closem;
3192dc36bedSjoris 	}
3202dc36bedSjoris 
3212dc36bedSjoris 	f2 = fopen(file2, "r");
3222dc36bedSjoris 	if (f2 == NULL) {
3232dc36bedSjoris 		warn("%s", file2);
3242dc36bedSjoris 		goto closem;
3252dc36bedSjoris 	}
3262dc36bedSjoris 
327ccac42f6Sray 	if (fstat(fileno(f1), &stb1) < 0) {
3282dc36bedSjoris 		warn("%s", file1);
3292dc36bedSjoris 		goto closem;
3302dc36bedSjoris 	}
3312dc36bedSjoris 
332ccac42f6Sray 	if (fstat(fileno(f2), &stb2) < 0) {
3332dc36bedSjoris 		warn("%s", file2);
3342dc36bedSjoris 		goto closem;
3352dc36bedSjoris 	}
3362dc36bedSjoris 
337616468a8Sray 	switch (files_differ(f1, f2)) {
338616468a8Sray 	case 1:
339616468a8Sray 		break;
340616468a8Sray 	case -1:
341616468a8Sray 		rval = D_ERROR;
342616468a8Sray 		/* FALLTHROUGH */
343616468a8Sray 	case 0:
3442dc36bedSjoris 		goto closem;
345616468a8Sray 	default:
346616468a8Sray 		errx(D_ERROR, "files_differ: invalid case");
347616468a8Sray 	}
3482dc36bedSjoris 
349f5f501bbSmillert 	if ((flags & D_FORCEASCII) == 0 &&
350f5f501bbSmillert 	    (!asciifile(f1) || !asciifile(f2))) {
3511fb625bfSxsa 		rval = D_ERROR;
3522dc36bedSjoris 		goto closem;
3532dc36bedSjoris 	}
3542dc36bedSjoris 
355f5f501bbSmillert 	if (prepare(0, f1, stb1.st_size, flags) < 0 ||
356f5f501bbSmillert 	    prepare(1, f2, stb2.st_size, flags) < 0) {
3572dc36bedSjoris 		goto closem;
3582dc36bedSjoris 	}
3592dc36bedSjoris 
3602dc36bedSjoris 	prune();
3612dc36bedSjoris 	sort(sfile[0], slen[0]);
3622dc36bedSjoris 	sort(sfile[1], slen[1]);
3632dc36bedSjoris 
3642dc36bedSjoris 	member = (int *)file[1];
3652dc36bedSjoris 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
36680566be2Sray 	member = xrealloc(member, slen[1] + 2, sizeof(*member));
3672dc36bedSjoris 
3682dc36bedSjoris 	class = (int *)file[0];
3692dc36bedSjoris 	unsort(sfile[0], slen[0], class);
37080566be2Sray 	class = xrealloc(class, slen[0] + 2, sizeof(*class));
3712dc36bedSjoris 
3722dc36bedSjoris 	klist = xcalloc(slen[0] + 2, sizeof(*klist));
3732dc36bedSjoris 	clen = 0;
3742dc36bedSjoris 	clistlen = 100;
3752dc36bedSjoris 	clist = xcalloc(clistlen, sizeof(*clist));
3762dc36bedSjoris 
377f5f501bbSmillert 	if ((i = stone(class, slen[0], member, klist, flags)) < 0)
3782dc36bedSjoris 		goto closem;
3792dc36bedSjoris 
3802dc36bedSjoris 	xfree(member);
3812dc36bedSjoris 	xfree(class);
3822dc36bedSjoris 
38380566be2Sray 	J = xrealloc(J, diff_len[0] + 2, sizeof(*J));
3842dc36bedSjoris 	unravel(klist[i]);
3852dc36bedSjoris 	xfree(clist);
3862dc36bedSjoris 	xfree(klist);
3872dc36bedSjoris 
38880566be2Sray 	ixold = xrealloc(ixold, diff_len[0] + 2, sizeof(*ixold));
3892dc36bedSjoris 
39080566be2Sray 	ixnew = xrealloc(ixnew, diff_len[1] + 2, sizeof(*ixnew));
391f5f501bbSmillert 	check(f1, f2, flags);
392f5f501bbSmillert 	output(f1, f2, flags);
3932dc36bedSjoris 
3942dc36bedSjoris closem:
3952dc36bedSjoris 	if (anychange == 1) {
3962dc36bedSjoris 		if (rval == D_SAME)
3972dc36bedSjoris 			rval = D_DIFFER;
3982dc36bedSjoris 	}
3992dc36bedSjoris 	if (f1 != NULL)
4002dc36bedSjoris 		fclose(f1);
4012dc36bedSjoris 	if (f2 != NULL)
4022dc36bedSjoris 		fclose(f2);
4032dc36bedSjoris 
4042dc36bedSjoris 	return (rval);
4052dc36bedSjoris }
4062dc36bedSjoris 
4072dc36bedSjoris /*
4082dc36bedSjoris  * Check to see if the given files differ.
4092dc36bedSjoris  * Returns 0 if they are the same, 1 if different, and -1 on error.
4102dc36bedSjoris  * XXX - could use code from cmp(1) [faster]
4112dc36bedSjoris  */
4122dc36bedSjoris static int
4132dc36bedSjoris files_differ(FILE *f1, FILE *f2)
4142dc36bedSjoris {
4152dc36bedSjoris 	char buf1[BUFSIZ], buf2[BUFSIZ];
4162dc36bedSjoris 	size_t i, j;
4172dc36bedSjoris 
4182dc36bedSjoris 	if (stb1.st_size != stb2.st_size)
4192dc36bedSjoris 		return (1);
4202dc36bedSjoris 	for (;;) {
42185ea658bSray 		i = fread(buf1, 1, sizeof(buf1), f1);
42285ea658bSray 		j = fread(buf2, 1, sizeof(buf2), f2);
4232dc36bedSjoris 		if (i != j)
4242dc36bedSjoris 			return (1);
4252dc36bedSjoris 		if (i == 0 && j == 0) {
4262dc36bedSjoris 			if (ferror(f1) || ferror(f2))
427616468a8Sray 				return (-1);
4282dc36bedSjoris 			return (0);
4292dc36bedSjoris 		}
4302dc36bedSjoris 		if (memcmp(buf1, buf2, i) != 0)
4312dc36bedSjoris 			return (1);
4322dc36bedSjoris 	}
4332dc36bedSjoris }
4342dc36bedSjoris 
4352dc36bedSjoris static int
436f5f501bbSmillert prepare(int i, FILE *fd, off_t filesize, int flags)
4372dc36bedSjoris {
4382dc36bedSjoris 	struct line *p;
4392dc36bedSjoris 	int j, h;
4402dc36bedSjoris 	size_t sz;
4412dc36bedSjoris 
4422dc36bedSjoris 	rewind(fd);
4432dc36bedSjoris 
4442dc36bedSjoris 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
4452dc36bedSjoris 	if (sz < 100)
4462dc36bedSjoris 		sz = 100;
4472dc36bedSjoris 
4482dc36bedSjoris 	p = xcalloc(sz + 3, sizeof(*p));
449f5f501bbSmillert 	for (j = 0; (h = readhash(fd, flags));) {
4502dc36bedSjoris 		if (j == (int)sz) {
4512dc36bedSjoris 			sz = sz * 3 / 2;
45280566be2Sray 			p = xrealloc(p, sz + 3, sizeof(*p));
4532dc36bedSjoris 		}
4542dc36bedSjoris 		p[++j].value = h;
4552dc36bedSjoris 	}
4562dc36bedSjoris 	diff_len[i] = j;
4572dc36bedSjoris 	file[i] = p;
4582dc36bedSjoris 
4592dc36bedSjoris 	return (0);
4602dc36bedSjoris }
4612dc36bedSjoris 
4622dc36bedSjoris static void
4632dc36bedSjoris prune(void)
4642dc36bedSjoris {
4652dc36bedSjoris 	int i, j;
4662dc36bedSjoris 
4672dc36bedSjoris 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
4682dc36bedSjoris 	    file[0][pref + 1].value == file[1][pref + 1].value;
4692dc36bedSjoris 	    pref++)
4702dc36bedSjoris 		;
4712dc36bedSjoris 	for (suff = 0;
4722dc36bedSjoris 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
4732dc36bedSjoris 	    (file[0][diff_len[0] - suff].value ==
4742dc36bedSjoris 	    file[1][diff_len[1] - suff].value);
4752dc36bedSjoris 	    suff++)
4762dc36bedSjoris 		;
4772dc36bedSjoris 	for (j = 0; j < 2; j++) {
4782dc36bedSjoris 		sfile[j] = file[j] + pref;
4792dc36bedSjoris 		slen[j] = diff_len[j] - pref - suff;
4802dc36bedSjoris 		for (i = 0; i <= slen[j]; i++)
4812dc36bedSjoris 			sfile[j][i].serial = i;
4822dc36bedSjoris 	}
4832dc36bedSjoris }
4842dc36bedSjoris 
4852dc36bedSjoris static void
4862dc36bedSjoris equiv(struct line *a, int n, struct line *b, int m, int *c)
4872dc36bedSjoris {
4882dc36bedSjoris 	int i, j;
4892dc36bedSjoris 
4902dc36bedSjoris 	i = j = 1;
4912dc36bedSjoris 	while (i <= n && j <= m) {
4922dc36bedSjoris 		if (a[i].value < b[j].value)
4932dc36bedSjoris 			a[i++].value = 0;
4942dc36bedSjoris 		else if (a[i].value == b[j].value)
4952dc36bedSjoris 			a[i++].value = j;
4962dc36bedSjoris 		else
4972dc36bedSjoris 			j++;
4982dc36bedSjoris 	}
4992dc36bedSjoris 	while (i <= n)
5002dc36bedSjoris 		a[i++].value = 0;
5012dc36bedSjoris 	b[m + 1].value = 0;
5022dc36bedSjoris 	j = 0;
5032dc36bedSjoris 	while (++j <= m) {
5042dc36bedSjoris 		c[j] = -b[j].serial;
5052dc36bedSjoris 		while (b[j + 1].value == b[j].value) {
5062dc36bedSjoris 			j++;
5072dc36bedSjoris 			c[j] = b[j].serial;
5082dc36bedSjoris 		}
5092dc36bedSjoris 	}
5102dc36bedSjoris 	c[j] = -1;
5112dc36bedSjoris }
5122dc36bedSjoris 
5132dc36bedSjoris /* Code taken from ping.c */
5142dc36bedSjoris static int
5152dc36bedSjoris isqrt(int n)
5162dc36bedSjoris {
5172dc36bedSjoris 	int y, x = 1;
5182dc36bedSjoris 
5192dc36bedSjoris 	if (n == 0)
5202dc36bedSjoris 		return (0);
5212dc36bedSjoris 
5222dc36bedSjoris 	do { /* newton was a stinker */
5232dc36bedSjoris 		y = x;
5242dc36bedSjoris 		x = n / x;
5252dc36bedSjoris 		x += y;
5262dc36bedSjoris 		x /= 2;
5272dc36bedSjoris 	} while (x - y > 1 || x - y < -1);
5282dc36bedSjoris 
5292dc36bedSjoris 	return (x);
5302dc36bedSjoris }
5312dc36bedSjoris 
5322dc36bedSjoris static int
533f5f501bbSmillert stone(int *a, int n, int *b, int *c, int flags)
5342dc36bedSjoris {
5352dc36bedSjoris 	int ret;
5362dc36bedSjoris 	int i, k, y, j, l;
5372dc36bedSjoris 	int oldc, tc, oldl;
5382dc36bedSjoris 	u_int numtries;
5392dc36bedSjoris 
5402dc36bedSjoris 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
541c2ac0f30Sxsa 	const u_int bound = (flags & D_MINIMAL) ? UINT_MAX :
542c2ac0f30Sxsa 	    MAX(256, (u_int)isqrt(n));
5432dc36bedSjoris 
5442dc36bedSjoris 	k = 0;
5452dc36bedSjoris 	if ((ret = newcand(0, 0, 0)) < 0)
5462dc36bedSjoris 		return (-1);
5472dc36bedSjoris 	c[0] = ret;
5482dc36bedSjoris 	for (i = 1; i <= n; i++) {
5492dc36bedSjoris 		j = a[i];
5502dc36bedSjoris 		if (j == 0)
5512dc36bedSjoris 			continue;
5522dc36bedSjoris 		y = -b[j];
5532dc36bedSjoris 		oldl = 0;
5542dc36bedSjoris 		oldc = c[0];
5552dc36bedSjoris 		numtries = 0;
5562dc36bedSjoris 		do {
5572dc36bedSjoris 			if (y <= clist[oldc].y)
5582dc36bedSjoris 				continue;
5592dc36bedSjoris 			l = search(c, k, y);
5602dc36bedSjoris 			if (l != oldl + 1)
5612dc36bedSjoris 				oldc = c[l - 1];
5622dc36bedSjoris 			if (l <= k) {
5632dc36bedSjoris 				if (clist[c[l]].y <= y)
5642dc36bedSjoris 					continue;
5652dc36bedSjoris 				tc = c[l];
5662dc36bedSjoris 				if ((ret = newcand(i, y, oldc)) < 0)
5672dc36bedSjoris 					return (-1);
5682dc36bedSjoris 				c[l] = ret;
5692dc36bedSjoris 				oldc = tc;
5702dc36bedSjoris 				oldl = l;
5712dc36bedSjoris 				numtries++;
5722dc36bedSjoris 			} else {
5732dc36bedSjoris 				if ((ret = newcand(i, y, oldc)) < 0)
5742dc36bedSjoris 					return (-1);
5752dc36bedSjoris 				c[l] = ret;
5762dc36bedSjoris 				k++;
5772dc36bedSjoris 				break;
5782dc36bedSjoris 			}
5792dc36bedSjoris 		} while ((y = b[++j]) > 0 && numtries < bound);
5802dc36bedSjoris 	}
5812dc36bedSjoris 	return (k);
5822dc36bedSjoris }
5832dc36bedSjoris 
5842dc36bedSjoris static int
5852dc36bedSjoris newcand(int x, int y, int pred)
5862dc36bedSjoris {
58780566be2Sray 	struct cand *q;
5882dc36bedSjoris 
5892dc36bedSjoris 	if (clen == clistlen) {
590*f74aa433Sray 		clistlen = clistlen * 11 / 10;
591*f74aa433Sray 		clist = xrealloc(clist, clistlen, sizeof(*clist));
5922dc36bedSjoris 	}
5932dc36bedSjoris 	q = clist + clen;
5942dc36bedSjoris 	q->x = x;
5952dc36bedSjoris 	q->y = y;
5962dc36bedSjoris 	q->pred = pred;
5972dc36bedSjoris 	return (clen++);
5982dc36bedSjoris }
5992dc36bedSjoris 
6002dc36bedSjoris static int
6012dc36bedSjoris search(int *c, int k, int y)
6022dc36bedSjoris {
6032dc36bedSjoris 	int i, j, l, t;
6042dc36bedSjoris 
6052dc36bedSjoris 	if (clist[c[k]].y < y)	/* quick look for typical case */
6062dc36bedSjoris 		return (k + 1);
6072dc36bedSjoris 	i = 0;
6082dc36bedSjoris 	j = k + 1;
6092dc36bedSjoris 	for (;;) {
6102dc36bedSjoris 		l = (i + j) / 2;
6112dc36bedSjoris 		if (l <= i)
6122dc36bedSjoris 			break;
6132dc36bedSjoris 		t = clist[c[l]].y;
6142dc36bedSjoris 		if (t > y)
6152dc36bedSjoris 			j = l;
6162dc36bedSjoris 		else if (t < y)
6172dc36bedSjoris 			i = l;
6182dc36bedSjoris 		else
6192dc36bedSjoris 			return (l);
6202dc36bedSjoris 	}
6212dc36bedSjoris 	return (l + 1);
6222dc36bedSjoris }
6232dc36bedSjoris 
6242dc36bedSjoris static void
6252dc36bedSjoris unravel(int p)
6262dc36bedSjoris {
6272dc36bedSjoris 	struct cand *q;
6282dc36bedSjoris 	int i;
6292dc36bedSjoris 
6302dc36bedSjoris 	for (i = 0; i <= diff_len[0]; i++)
6312dc36bedSjoris 		J[i] = i <= pref ? i :
6322dc36bedSjoris 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
6332dc36bedSjoris 	for (q = clist + p; q->y != 0; q = clist + q->pred)
6342dc36bedSjoris 		J[q->x + pref] = q->y + pref;
6352dc36bedSjoris }
6362dc36bedSjoris 
6372dc36bedSjoris /*
6382dc36bedSjoris  * Check does double duty:
6392dc36bedSjoris  *  1.	ferret out any fortuitous correspondences due
6402dc36bedSjoris  *	to confounding by hashing (which result in "jackpot")
6412dc36bedSjoris  *  2.  collect random access indexes to the two files
6422dc36bedSjoris  */
6432dc36bedSjoris static void
644f5f501bbSmillert check(FILE *f1, FILE *f2, int flags)
6452dc36bedSjoris {
6462dc36bedSjoris 	int i, j, jackpot, c, d;
6472dc36bedSjoris 	long ctold, ctnew;
6482dc36bedSjoris 
6492dc36bedSjoris 	rewind(f1);
6502dc36bedSjoris 	rewind(f2);
6512dc36bedSjoris 	j = 1;
6522dc36bedSjoris 	ixold[0] = ixnew[0] = 0;
6532dc36bedSjoris 	jackpot = 0;
6542dc36bedSjoris 	ctold = ctnew = 0;
6552dc36bedSjoris 	for (i = 1; i <= diff_len[0]; i++) {
6562dc36bedSjoris 		if (J[i] == 0) {
6572dc36bedSjoris 			ixold[i] = ctold += skipline(f1);
6582dc36bedSjoris 			continue;
6592dc36bedSjoris 		}
6602dc36bedSjoris 		while (j < J[i]) {
6612dc36bedSjoris 			ixnew[j] = ctnew += skipline(f2);
6622dc36bedSjoris 			j++;
6632dc36bedSjoris 		}
664f5f501bbSmillert 		if (flags & (D_FOLDBLANKS|D_IGNOREBLANKS|D_IGNORECASE)) {
6652dc36bedSjoris 			for (;;) {
6662dc36bedSjoris 				c = getc(f1);
6672dc36bedSjoris 				d = getc(f2);
6682dc36bedSjoris 				/*
6692dc36bedSjoris 				 * GNU diff ignores a missing newline
670f5f501bbSmillert 				 * in one file for -b or -w.
6712dc36bedSjoris 				 */
672f5f501bbSmillert 				if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) &&
6732dc36bedSjoris 				    ((c == EOF && d == '\n') ||
6742dc36bedSjoris 				    (c == '\n' && d == EOF))) {
6752dc36bedSjoris 					break;
6762dc36bedSjoris 				}
6772dc36bedSjoris 				ctold++;
6782dc36bedSjoris 				ctnew++;
679f5f501bbSmillert 				if ((flags & D_FOLDBLANKS) && isspace(c) &&
680f5f501bbSmillert 				    isspace(d)) {
6812dc36bedSjoris 					do {
6822dc36bedSjoris 						if (c == '\n')
6832dc36bedSjoris 							break;
6842dc36bedSjoris 						ctold++;
6852dc36bedSjoris 					} while (isspace(c = getc(f1)));
6862dc36bedSjoris 					do {
6872dc36bedSjoris 						if (d == '\n')
6882dc36bedSjoris 							break;
6892dc36bedSjoris 						ctnew++;
6902dc36bedSjoris 					} while (isspace(d = getc(f2)));
691f5f501bbSmillert 				} else if ((flags & D_IGNOREBLANKS)) {
6922dc36bedSjoris 					while (isspace(c) && c != '\n') {
6932dc36bedSjoris 						c = getc(f1);
6942dc36bedSjoris 						ctold++;
6952dc36bedSjoris 					}
6962dc36bedSjoris 					while (isspace(d) && d != '\n') {
6972dc36bedSjoris 						d = getc(f2);
6982dc36bedSjoris 						ctnew++;
6992dc36bedSjoris 					}
7002dc36bedSjoris 				}
7012dc36bedSjoris 				if (chrtran[c] != chrtran[d]) {
7022dc36bedSjoris 					jackpot++;
7032dc36bedSjoris 					J[i] = 0;
7042dc36bedSjoris 					if (c != '\n' && c != EOF)
7052dc36bedSjoris 						ctold += skipline(f1);
7062dc36bedSjoris 					if (d != '\n' && c != EOF)
7072dc36bedSjoris 						ctnew += skipline(f2);
7082dc36bedSjoris 					break;
7092dc36bedSjoris 				}
7102dc36bedSjoris 				if (c == '\n' || c == EOF)
7112dc36bedSjoris 					break;
7122dc36bedSjoris 			}
7132dc36bedSjoris 		} else {
7142dc36bedSjoris 			for (;;) {
7152dc36bedSjoris 				ctold++;
7162dc36bedSjoris 				ctnew++;
7172dc36bedSjoris 				if ((c = getc(f1)) != (d = getc(f2))) {
7182dc36bedSjoris 					/* jackpot++; */
7192dc36bedSjoris 					J[i] = 0;
7202dc36bedSjoris 					if (c != '\n' && c != EOF)
7212dc36bedSjoris 						ctold += skipline(f1);
7222dc36bedSjoris 					if (d != '\n' && c != EOF)
7232dc36bedSjoris 						ctnew += skipline(f2);
7242dc36bedSjoris 					break;
7252dc36bedSjoris 				}
7262dc36bedSjoris 				if (c == '\n' || c == EOF)
7272dc36bedSjoris 					break;
7282dc36bedSjoris 			}
7292dc36bedSjoris 		}
7302dc36bedSjoris 		ixold[i] = ctold;
7312dc36bedSjoris 		ixnew[j] = ctnew;
7322dc36bedSjoris 		j++;
7332dc36bedSjoris 	}
7342dc36bedSjoris 	for (; j <= diff_len[1]; j++)
7352dc36bedSjoris 		ixnew[j] = ctnew += skipline(f2);
7362dc36bedSjoris 	/*
7372dc36bedSjoris 	 * if (jackpot != 0)
7382dc36bedSjoris 	 *	printf("jackpot\n");
7392dc36bedSjoris 	 */
7402dc36bedSjoris }
7412dc36bedSjoris 
7422dc36bedSjoris /* shellsort CACM #201 */
7432dc36bedSjoris static void
7442dc36bedSjoris sort(struct line *a, int n)
7452dc36bedSjoris {
7462dc36bedSjoris 	struct line *ai, *aim, w;
7472dc36bedSjoris 	int j, m = 0, k;
7482dc36bedSjoris 
7492dc36bedSjoris 	if (n == 0)
7502dc36bedSjoris 		return;
7512dc36bedSjoris 	for (j = 1; j <= n; j *= 2)
7522dc36bedSjoris 		m = 2 * j - 1;
7532dc36bedSjoris 	for (m /= 2; m != 0; m /= 2) {
7542dc36bedSjoris 		k = n - m;
7552dc36bedSjoris 		for (j = 1; j <= k; j++) {
7562dc36bedSjoris 			for (ai = &a[j]; ai > a; ai -= m) {
7572dc36bedSjoris 				aim = &ai[m];
7582dc36bedSjoris 				if (aim < ai)
7592dc36bedSjoris 					break;	/* wraparound */
7602dc36bedSjoris 				if (aim->value > ai[0].value ||
7612dc36bedSjoris 				    (aim->value == ai[0].value &&
7622dc36bedSjoris 					aim->serial > ai[0].serial))
7632dc36bedSjoris 					break;
7642dc36bedSjoris 				w.value = ai[0].value;
7652dc36bedSjoris 				ai[0].value = aim->value;
7662dc36bedSjoris 				aim->value = w.value;
7672dc36bedSjoris 				w.serial = ai[0].serial;
7682dc36bedSjoris 				ai[0].serial = aim->serial;
7692dc36bedSjoris 				aim->serial = w.serial;
7702dc36bedSjoris 			}
7712dc36bedSjoris 		}
7722dc36bedSjoris 	}
7732dc36bedSjoris }
7742dc36bedSjoris 
7752dc36bedSjoris static void
7762dc36bedSjoris unsort(struct line *f, int l, int *b)
7772dc36bedSjoris {
7782dc36bedSjoris 	int *a, i;
7792dc36bedSjoris 
7802dc36bedSjoris 	a = xcalloc(l + 1, sizeof(*a));
7812dc36bedSjoris 	for (i = 1; i <= l; i++)
7822dc36bedSjoris 		a[f[i].serial] = f[i].value;
7832dc36bedSjoris 	for (i = 1; i <= l; i++)
7842dc36bedSjoris 		b[i] = a[i];
7852dc36bedSjoris 	xfree(a);
7862dc36bedSjoris }
7872dc36bedSjoris 
7882dc36bedSjoris static int
7892dc36bedSjoris skipline(FILE *f)
7902dc36bedSjoris {
7912dc36bedSjoris 	int i, c;
7922dc36bedSjoris 
7932dc36bedSjoris 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
7942dc36bedSjoris 		continue;
7952dc36bedSjoris 	return (i);
7962dc36bedSjoris }
7972dc36bedSjoris 
7982dc36bedSjoris static void
799f5f501bbSmillert output(FILE *f1, FILE *f2, int flags)
8002dc36bedSjoris {
8012dc36bedSjoris 	int m, i0, i1, j0, j1;
8022dc36bedSjoris 
8032dc36bedSjoris 	rewind(f1);
8042dc36bedSjoris 	rewind(f2);
8052dc36bedSjoris 	m = diff_len[0];
8062dc36bedSjoris 	J[0] = 0;
8072dc36bedSjoris 	J[m + 1] = diff_len[1] + 1;
8082dc36bedSjoris 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
8092dc36bedSjoris 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
8102dc36bedSjoris 			i0++;
8112dc36bedSjoris 		j0 = J[i0 - 1] + 1;
8122dc36bedSjoris 		i1 = i0 - 1;
8132dc36bedSjoris 		while (i1 < m && J[i1 + 1] == 0)
8142dc36bedSjoris 			i1++;
8152dc36bedSjoris 		j1 = J[i1 + 1] - 1;
8162dc36bedSjoris 		J[i1] = j1;
817f5f501bbSmillert 		change(f1, f2, i0, i1, j0, j1, flags);
8182dc36bedSjoris 	}
8192dc36bedSjoris 	if (m == 0)
820f5f501bbSmillert 		change(f1, f2, 1, 0, 1, diff_len[1], flags);
8212dc36bedSjoris 	if (diff_format == D_IFDEF) {
8222dc36bedSjoris 		for (;;) {
8232dc36bedSjoris #define	c i0
8242dc36bedSjoris 			if ((c = getc(f1)) == EOF)
8252dc36bedSjoris 				return;
8262dc36bedSjoris 			diff_output("%c", c);
8272dc36bedSjoris 		}
8282dc36bedSjoris #undef c
8292dc36bedSjoris 	}
8302dc36bedSjoris 	if (anychange != 0) {
8312dc36bedSjoris 		if (diff_format == D_CONTEXT)
832f5f501bbSmillert 			dump_context_vec(f1, f2, flags);
8332dc36bedSjoris 		else if (diff_format == D_UNIFIED)
834f5f501bbSmillert 			dump_unified_vec(f1, f2, flags);
8352dc36bedSjoris 	}
8362dc36bedSjoris }
8372dc36bedSjoris 
838ccac42f6Sray static void
8392dc36bedSjoris range(int a, int b, char *separator)
8402dc36bedSjoris {
8412dc36bedSjoris 	diff_output("%d", a > b ? b : a);
8422dc36bedSjoris 	if (a < b)
8432dc36bedSjoris 		diff_output("%s%d", separator, b);
8442dc36bedSjoris }
8452dc36bedSjoris 
846ccac42f6Sray static void
8472dc36bedSjoris uni_range(int a, int b)
8482dc36bedSjoris {
8492dc36bedSjoris 	if (a < b)
8502dc36bedSjoris 		diff_output("%d,%d", a, b - a + 1);
8512dc36bedSjoris 	else if (a == b)
8522dc36bedSjoris 		diff_output("%d", b);
8532dc36bedSjoris 	else
8542dc36bedSjoris 		diff_output("%d,0", b);
8552dc36bedSjoris }
8562dc36bedSjoris 
8572dc36bedSjoris static char *
8582dc36bedSjoris preadline(int fd, size_t rlen, off_t off)
8592dc36bedSjoris {
8602dc36bedSjoris 	char *line;
8612dc36bedSjoris 	ssize_t nr;
8622dc36bedSjoris 
8632dc36bedSjoris 	line = xmalloc(rlen + 1);
8642dc36bedSjoris 	if ((nr = pread(fd, line, rlen, off)) < 0) {
8652dc36bedSjoris 		warn("preadline failed");
86610e28a1fSray 		xfree(line);
8672dc36bedSjoris 		return (NULL);
8682dc36bedSjoris 	}
8692dc36bedSjoris 	line[nr] = '\0';
8702dc36bedSjoris 	return (line);
8712dc36bedSjoris }
8722dc36bedSjoris 
8732dc36bedSjoris static int
8742dc36bedSjoris ignoreline(char *line)
8752dc36bedSjoris {
8762dc36bedSjoris 	int ret;
8772dc36bedSjoris 
878f5f501bbSmillert 	ret = regexec(diff_ignore_re, line, 0, NULL, 0);
8792dc36bedSjoris 	xfree(line);
8802dc36bedSjoris 	return (ret == 0);	/* if it matched, it should be ignored. */
8812dc36bedSjoris }
8822dc36bedSjoris 
8832dc36bedSjoris /*
8842dc36bedSjoris  * Indicate that there is a difference between lines a and b of the from file
8852dc36bedSjoris  * to get to lines c to d of the to file.  If a is greater then b then there
8862dc36bedSjoris  * are no lines in the from file involved and this means that there were
8872dc36bedSjoris  * lines appended (beginning at b).  If c is greater than d then there are
8882dc36bedSjoris  * lines missing from the to file.
8892dc36bedSjoris  */
8902dc36bedSjoris static void
891f5f501bbSmillert change(FILE *f1, FILE *f2, int a, int b, int c, int d, int flags)
8922dc36bedSjoris {
8932dc36bedSjoris 	int i;
8942dc36bedSjoris 	static size_t max_context = 64;
8952dc36bedSjoris 	char buf[64];
8962dc36bedSjoris 	struct tm *t;
8972dc36bedSjoris 
8982dc36bedSjoris 	if (diff_format != D_IFDEF && a > b && c > d)
8992dc36bedSjoris 		return;
900f5f501bbSmillert 	if (diff_ignore_re != NULL) {
9012dc36bedSjoris 		char *line;
9022dc36bedSjoris 		/*
9032dc36bedSjoris 		 * All lines in the change, insert, or delete must
9042dc36bedSjoris 		 * match an ignore pattern for the change to be
9052dc36bedSjoris 		 * ignored.
9062dc36bedSjoris 		 */
9072dc36bedSjoris 		if (a <= b) {		/* Changes and deletes. */
9082dc36bedSjoris 			for (i = a; i <= b; i++) {
9092dc36bedSjoris 				line = preadline(fileno(f1),
9102dc36bedSjoris 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
9112dc36bedSjoris 				if (!ignoreline(line))
9122dc36bedSjoris 					goto proceed;
9132dc36bedSjoris 			}
9142dc36bedSjoris 		}
9152dc36bedSjoris 		if (a > b || c <= d) {	/* Changes and inserts. */
9162dc36bedSjoris 			for (i = c; i <= d; i++) {
9172dc36bedSjoris 				line = preadline(fileno(f2),
9182dc36bedSjoris 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
9192dc36bedSjoris 				if (!ignoreline(line))
9202dc36bedSjoris 					goto proceed;
9212dc36bedSjoris 			}
9222dc36bedSjoris 		}
9232dc36bedSjoris 		return;
9242dc36bedSjoris 	}
9252dc36bedSjoris proceed:
9262dc36bedSjoris 	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
9272dc36bedSjoris 		/*
9282dc36bedSjoris 		 * Allocate change records as needed.
9292dc36bedSjoris 		 */
9302dc36bedSjoris 		if (context_vec_ptr == context_vec_end - 1) {
9312dc36bedSjoris 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
9322dc36bedSjoris 			max_context <<= 1;
93380566be2Sray 			context_vec_start = xrealloc(context_vec_start,
93480566be2Sray 			    max_context, sizeof(*context_vec_start));
9352dc36bedSjoris 			context_vec_end = context_vec_start + max_context;
9362dc36bedSjoris 			context_vec_ptr = context_vec_start + offset;
9372dc36bedSjoris 		}
9382dc36bedSjoris 		if (anychange == 0) {
9392dc36bedSjoris 			/*
9402dc36bedSjoris 			 * Print the context/unidiff header first time through.
9412dc36bedSjoris 			 */
9422dc36bedSjoris 			t = localtime(&stb1.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_rev1 != NULL) {
9512dc36bedSjoris 				rcsnum_tostr(diff_rev1, buf, sizeof(buf));
9522dc36bedSjoris 				diff_output("\t%s", buf);
9532dc36bedSjoris 			}
9542dc36bedSjoris 
9552dc36bedSjoris 			printf("\n");
9562dc36bedSjoris 
9572dc36bedSjoris 			t = localtime(&stb2.st_mtime);
9582dc36bedSjoris 			(void)strftime(buf, sizeof(buf),
9592dc36bedSjoris 			    "%Y/%m/%d %H:%M:%S", t);
9602dc36bedSjoris 
9612dc36bedSjoris 			diff_output("%s %s	%s",
9622dc36bedSjoris 			    diff_format == D_CONTEXT ? "---" : "+++", diff_file,
9632dc36bedSjoris 			    buf);
9642dc36bedSjoris 
9652dc36bedSjoris 			if (diff_rev2 != NULL) {
9662dc36bedSjoris 				rcsnum_tostr(diff_rev2, buf, sizeof(buf));
9672dc36bedSjoris 				diff_output("\t%s", buf);
9682dc36bedSjoris 			}
9692dc36bedSjoris 
9702dc36bedSjoris 			printf("\n");
9712dc36bedSjoris 			anychange = 1;
972f5f501bbSmillert 		} else if (a > context_vec_ptr->b + (2 * diff_context) + 1 &&
973f5f501bbSmillert 		    c > context_vec_ptr->d + (2 * diff_context) + 1) {
9742dc36bedSjoris 			/*
975f5f501bbSmillert 			 * If this change is more than 'diff_context' lines
976f5f501bbSmillert 			 * from the previous change, dump the record and reset it.
9772dc36bedSjoris 			 */
9782dc36bedSjoris 			if (diff_format == D_CONTEXT)
979f5f501bbSmillert 				dump_context_vec(f1, f2, flags);
9802dc36bedSjoris 			else
981f5f501bbSmillert 				dump_unified_vec(f1, f2, flags);
9822dc36bedSjoris 		}
9832dc36bedSjoris 		context_vec_ptr++;
9842dc36bedSjoris 		context_vec_ptr->a = a;
9852dc36bedSjoris 		context_vec_ptr->b = b;
9862dc36bedSjoris 		context_vec_ptr->c = c;
9872dc36bedSjoris 		context_vec_ptr->d = d;
9882dc36bedSjoris 		return;
9892dc36bedSjoris 	}
9902dc36bedSjoris 	if (anychange == 0)
9912dc36bedSjoris 		anychange = 1;
9922dc36bedSjoris 	switch (diff_format) {
9932dc36bedSjoris 	case D_BRIEF:
9942dc36bedSjoris 		return;
9952dc36bedSjoris 	case D_NORMAL:
9962dc36bedSjoris 		range(a, b, ",");
9972dc36bedSjoris 		diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c');
9982dc36bedSjoris 		if (diff_format == D_NORMAL)
9992dc36bedSjoris 			range(c, d, ",");
10002dc36bedSjoris 		diff_output("\n");
10012dc36bedSjoris 		break;
10022dc36bedSjoris 	case D_RCSDIFF:
10032dc36bedSjoris 		if (a > b)
10042dc36bedSjoris 			diff_output("a%d %d\n", b, d - c + 1);
10052dc36bedSjoris 		else {
10062dc36bedSjoris 			diff_output("d%d %d\n", a, b - a + 1);
10072dc36bedSjoris 
10082dc36bedSjoris 			if (!(c > d))	/* add changed lines */
10092dc36bedSjoris 				diff_output("a%d %d\n", b, d - c + 1);
10102dc36bedSjoris 		}
10112dc36bedSjoris 		break;
10122dc36bedSjoris 	}
10132dc36bedSjoris 	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
1014f5f501bbSmillert 		fetch(ixold, a, b, f1, '<', 1, flags);
10152dc36bedSjoris 		if (a <= b && c <= d && diff_format == D_NORMAL)
10162dc36bedSjoris 			diff_output("---\n");
10172dc36bedSjoris 	}
1018f5f501bbSmillert 	fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0, flags);
10192dc36bedSjoris 	if (inifdef) {
10202dc36bedSjoris 		diff_output("#endif /* %s */\n", ifdefname);
10212dc36bedSjoris 		inifdef = 0;
10222dc36bedSjoris 	}
10232dc36bedSjoris }
10242dc36bedSjoris 
10252dc36bedSjoris static void
1026f5f501bbSmillert fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags)
10272dc36bedSjoris {
10282dc36bedSjoris 	long j, nc;
10292dc36bedSjoris 	int i, c, col;
10302dc36bedSjoris 
10312dc36bedSjoris 	/*
10322dc36bedSjoris 	 * When doing #ifdef's, copy down to current line
10332dc36bedSjoris 	 * if this is the first file, so that stuff makes it to output.
10342dc36bedSjoris 	 */
10352dc36bedSjoris 	if (diff_format == D_IFDEF && oldfile) {
10362dc36bedSjoris 		long curpos = ftell(lb);
10372dc36bedSjoris 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
10382dc36bedSjoris 		nc = f[a > b ? b : a - 1] - curpos;
10392dc36bedSjoris 		for (i = 0; i < nc; i++)
10402dc36bedSjoris 			diff_output("%c", getc(lb));
10412dc36bedSjoris 	}
10422dc36bedSjoris 	if (a > b)
10432dc36bedSjoris 		return;
10442dc36bedSjoris 	if (diff_format == D_IFDEF) {
10452dc36bedSjoris 		if (inifdef) {
10462dc36bedSjoris 			diff_output("#else /* %s%s */\n",
10472dc36bedSjoris 			    oldfile == 1 ? "!" : "", ifdefname);
10482dc36bedSjoris 		} else {
10492dc36bedSjoris 			if (oldfile)
10502dc36bedSjoris 				diff_output("#ifndef %s\n", ifdefname);
10512dc36bedSjoris 			else
10522dc36bedSjoris 				diff_output("#ifdef %s\n", ifdefname);
10532dc36bedSjoris 		}
10542dc36bedSjoris 		inifdef = 1 + oldfile;
10552dc36bedSjoris 	}
10562dc36bedSjoris 	for (i = a; i <= b; i++) {
10572dc36bedSjoris 		fseek(lb, f[i - 1], SEEK_SET);
10582dc36bedSjoris 		nc = f[i] - f[i - 1];
10592dc36bedSjoris 		if (diff_format != D_IFDEF && ch != '\0') {
10602dc36bedSjoris 			diff_output("%c", ch);
1061f5f501bbSmillert 			if (diff_format != D_UNIFIED)
10622dc36bedSjoris 				diff_output(" ");
10632dc36bedSjoris 		}
10642dc36bedSjoris 		col = 0;
10652dc36bedSjoris 		for (j = 0; j < nc; j++) {
10662dc36bedSjoris 			if ((c = getc(lb)) == EOF) {
10672dc36bedSjoris 				if (diff_format == D_RCSDIFF)
10682dc36bedSjoris 					warn("No newline at end of file");
10692dc36bedSjoris 				else
10702dc36bedSjoris 					diff_output("\n\\ No newline at end of "
10712dc36bedSjoris 					    "file");
10722dc36bedSjoris 				return;
10732dc36bedSjoris 			}
1074f5f501bbSmillert 			if (c == '\t' && (flags & D_EXPANDTABS)) {
10752dc36bedSjoris 				do {
10762dc36bedSjoris 					diff_output(" ");
10772dc36bedSjoris 				} while (++col & 7);
10782dc36bedSjoris 			} else {
10792dc36bedSjoris 				diff_output("%c", c);
10802dc36bedSjoris 				col++;
10812dc36bedSjoris 			}
10822dc36bedSjoris 		}
10832dc36bedSjoris 	}
10842dc36bedSjoris }
10852dc36bedSjoris 
10862dc36bedSjoris /*
10872dc36bedSjoris  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
10882dc36bedSjoris  */
10892dc36bedSjoris static int
1090f5f501bbSmillert readhash(FILE *f, int flags)
10912dc36bedSjoris {
10922dc36bedSjoris 	int i, t, space;
10932dc36bedSjoris 	int sum;
10942dc36bedSjoris 
10952dc36bedSjoris 	sum = 1;
10962dc36bedSjoris 	space = 0;
1097f5f501bbSmillert 	if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) == 0) {
1098f5f501bbSmillert 		if (flags & D_IGNORECASE)
10992dc36bedSjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
11002dc36bedSjoris 				if (t == EOF) {
11012dc36bedSjoris 					if (i == 0)
11022dc36bedSjoris 						return (0);
11032dc36bedSjoris 					break;
11042dc36bedSjoris 				}
11052dc36bedSjoris 				sum = sum * 127 + chrtran[t];
11062dc36bedSjoris 			}
11072dc36bedSjoris 		else
11082dc36bedSjoris 			for (i = 0; (t = getc(f)) != '\n'; i++) {
11092dc36bedSjoris 				if (t == EOF) {
11102dc36bedSjoris 					if (i == 0)
11112dc36bedSjoris 						return (0);
11122dc36bedSjoris 					break;
11132dc36bedSjoris 				}
11142dc36bedSjoris 				sum = sum * 127 + t;
11152dc36bedSjoris 			}
11162dc36bedSjoris 	} else {
11172dc36bedSjoris 		for (i = 0;;) {
11182dc36bedSjoris 			switch (t = getc(f)) {
11192dc36bedSjoris 			case '\t':
11202dc36bedSjoris 			case ' ':
11212dc36bedSjoris 				space++;
11222dc36bedSjoris 				continue;
11232dc36bedSjoris 			default:
1124f5f501bbSmillert 				if (space && (flags & D_IGNOREBLANKS) == 0) {
11252dc36bedSjoris 					i++;
11262dc36bedSjoris 					space = 0;
11272dc36bedSjoris 				}
11282dc36bedSjoris 				sum = sum * 127 + chrtran[t];
11292dc36bedSjoris 				i++;
11302dc36bedSjoris 				continue;
11312dc36bedSjoris 			case EOF:
11322dc36bedSjoris 				if (i == 0)
11332dc36bedSjoris 					return (0);
11342dc36bedSjoris 				/* FALLTHROUGH */
11352dc36bedSjoris 			case '\n':
11362dc36bedSjoris 				break;
11372dc36bedSjoris 			}
11382dc36bedSjoris 			break;
11392dc36bedSjoris 		}
11402dc36bedSjoris 	}
11412dc36bedSjoris 	/*
11422dc36bedSjoris 	 * There is a remote possibility that we end up with a zero sum.
11432dc36bedSjoris 	 * Zero is used as an EOF marker, so return 1 instead.
11442dc36bedSjoris 	 */
11452dc36bedSjoris 	return (sum == 0 ? 1 : sum);
11462dc36bedSjoris }
11472dc36bedSjoris 
11482dc36bedSjoris static int
11492dc36bedSjoris asciifile(FILE *f)
11502dc36bedSjoris {
11512dc36bedSjoris 	char buf[BUFSIZ];
11522dc36bedSjoris 	size_t i, cnt;
11532dc36bedSjoris 
1154f5f501bbSmillert 	if (f == NULL)
11552dc36bedSjoris 		return (1);
11562dc36bedSjoris 
11572dc36bedSjoris 	rewind(f);
115885ea658bSray 	cnt = fread(buf, 1, sizeof(buf), f);
11592dc36bedSjoris 	for (i = 0; i < cnt; i++)
11602dc36bedSjoris 		if (!isprint(buf[i]) && !isspace(buf[i]))
11612dc36bedSjoris 			return (0);
11622dc36bedSjoris 	return (1);
11632dc36bedSjoris }
11642dc36bedSjoris 
1165e22e6974Sxsa #define begins_with(s, pre) (strncmp(s, pre, sizeof(pre)-1) == 0)
1166e22e6974Sxsa 
11672dc36bedSjoris static char*
11682dc36bedSjoris match_function(const long *f, int pos, FILE *fp)
11692dc36bedSjoris {
11702dc36bedSjoris 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
11712dc36bedSjoris 	size_t nc;
11722dc36bedSjoris 	int last = lastline;
11732dc36bedSjoris 	char *p;
1174e22e6974Sxsa 	char *state = NULL;
11752dc36bedSjoris 
11762dc36bedSjoris 	lastline = pos;
11772dc36bedSjoris 	while (pos > last) {
11782dc36bedSjoris 		fseek(fp, f[pos - 1], SEEK_SET);
11792dc36bedSjoris 		nc = f[pos] - f[pos - 1];
11802dc36bedSjoris 		if (nc >= sizeof(buf))
11812dc36bedSjoris 			nc = sizeof(buf) - 1;
118285ea658bSray 		nc = fread(buf, 1, nc, fp);
11832dc36bedSjoris 		if (nc > 0) {
11842dc36bedSjoris 			buf[nc] = '\0';
11852dc36bedSjoris 			p = strchr((const char *)buf, '\n');
11862dc36bedSjoris 			if (p != NULL)
11872dc36bedSjoris 				*p = '\0';
11882dc36bedSjoris 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
1189e22e6974Sxsa 				if (begins_with(buf, "private:")) {
1190e22e6974Sxsa 					if (!state)
1191e22e6974Sxsa 						state = " (private)";
1192e22e6974Sxsa 				} else if (begins_with(buf, "protected:")) {
1193e22e6974Sxsa 					if (!state)
1194e22e6974Sxsa 						state = " (protected)";
1195e22e6974Sxsa 				} else if (begins_with(buf, "public:")) {
1196e22e6974Sxsa 					if (!state)
1197e22e6974Sxsa 						state = " (public)";
1198e22e6974Sxsa 				} else {
11996b99bb86Sray 					if (strlcpy(lastbuf, (const char *)buf,
12006b99bb86Sray 					    sizeof(lastbuf)) >= sizeof(lastbuf))
1201e22e6974Sxsa 						errx(1,
1202e22e6974Sxsa 						    "match_function: strlcpy");
12032dc36bedSjoris 					lastmatchline = pos;
12042dc36bedSjoris 					return lastbuf;
12052dc36bedSjoris 				}
12062dc36bedSjoris 			}
1207e22e6974Sxsa 		}
12082dc36bedSjoris 		pos--;
12092dc36bedSjoris 	}
12102dc36bedSjoris 	return (lastmatchline > 0) ? lastbuf : NULL;
12112dc36bedSjoris }
12122dc36bedSjoris 
12132dc36bedSjoris 
12142dc36bedSjoris /* dump accumulated "context" diff changes */
12152dc36bedSjoris static void
1216f5f501bbSmillert dump_context_vec(FILE *f1, FILE *f2, int flags)
12172dc36bedSjoris {
12182dc36bedSjoris 	struct context_vec *cvp = context_vec_start;
12192dc36bedSjoris 	int lowa, upb, lowc, upd, do_output;
12202dc36bedSjoris 	int a, b, c, d;
12212dc36bedSjoris 	char ch, *f;
12222dc36bedSjoris 
12232dc36bedSjoris 	if (context_vec_start > context_vec_ptr)
12242dc36bedSjoris 		return;
12252dc36bedSjoris 
12262dc36bedSjoris 	b = d = 0;		/* gcc */
1227f5f501bbSmillert 	lowa = MAX(1, cvp->a - diff_context);
1228f5f501bbSmillert 	upb = MIN(diff_len[0], context_vec_ptr->b + diff_context);
1229f5f501bbSmillert 	lowc = MAX(1, cvp->c - diff_context);
1230f5f501bbSmillert 	upd = MIN(diff_len[1], context_vec_ptr->d + diff_context);
12312dc36bedSjoris 
12322dc36bedSjoris 	diff_output("***************");
1233f5f501bbSmillert 	if ((flags & D_PROTOTYPE)) {
12342dc36bedSjoris 		f = match_function(ixold, lowa - 1, f1);
12352dc36bedSjoris 		if (f != NULL) {
12362dc36bedSjoris 			diff_output(" ");
12372dc36bedSjoris 			diff_output("%s", f);
12382dc36bedSjoris 		}
12392dc36bedSjoris 	}
12402dc36bedSjoris 	diff_output("\n*** ");
12412dc36bedSjoris 	range(lowa, upb, ",");
12422dc36bedSjoris 	diff_output(" ****\n");
12432dc36bedSjoris 
12442dc36bedSjoris 	/*
12452dc36bedSjoris 	 * Output changes to the "old" file.  The first loop suppresses
12462dc36bedSjoris 	 * output if there were no changes to the "old" file (we'll see
12472dc36bedSjoris 	 * the "old" lines as context in the "new" list).
12482dc36bedSjoris 	 */
12492dc36bedSjoris 	do_output = 0;
12502dc36bedSjoris 	for (; cvp <= context_vec_ptr; cvp++)
12512dc36bedSjoris 		if (cvp->a <= cvp->b) {
12522dc36bedSjoris 			cvp = context_vec_start;
12532dc36bedSjoris 			do_output++;
12542dc36bedSjoris 			break;
12552dc36bedSjoris 		}
12562dc36bedSjoris 	if (do_output != 0) {
12572dc36bedSjoris 		while (cvp <= context_vec_ptr) {
12582dc36bedSjoris 			a = cvp->a;
12592dc36bedSjoris 			b = cvp->b;
12602dc36bedSjoris 			c = cvp->c;
12612dc36bedSjoris 			d = cvp->d;
12622dc36bedSjoris 
12632dc36bedSjoris 			if (a <= b && c <= d)
12642dc36bedSjoris 				ch = 'c';
12652dc36bedSjoris 			else
12662dc36bedSjoris 				ch = (a <= b) ? 'd' : 'a';
12672dc36bedSjoris 
12682dc36bedSjoris 			if (ch == 'a')
1269f5f501bbSmillert 				fetch(ixold, lowa, b, f1, ' ', 0, flags);
12702dc36bedSjoris 			else {
1271f5f501bbSmillert 				fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
12722dc36bedSjoris 				fetch(ixold, a, b, f1,
1273f5f501bbSmillert 				    ch == 'c' ? '!' : '-', 0, flags);
12742dc36bedSjoris 			}
12752dc36bedSjoris 			lowa = b + 1;
12762dc36bedSjoris 			cvp++;
12772dc36bedSjoris 		}
1278f5f501bbSmillert 		fetch(ixold, b + 1, upb, f1, ' ', 0, flags);
12792dc36bedSjoris 	}
12802dc36bedSjoris 	/* output changes to the "new" file */
12812dc36bedSjoris 	diff_output("--- ");
12822dc36bedSjoris 	range(lowc, upd, ",");
12832dc36bedSjoris 	diff_output(" ----\n");
12842dc36bedSjoris 
12852dc36bedSjoris 	do_output = 0;
12862dc36bedSjoris 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
12872dc36bedSjoris 		if (cvp->c <= cvp->d) {
12882dc36bedSjoris 			cvp = context_vec_start;
12892dc36bedSjoris 			do_output++;
12902dc36bedSjoris 			break;
12912dc36bedSjoris 		}
12922dc36bedSjoris 	if (do_output != 0) {
12932dc36bedSjoris 		while (cvp <= context_vec_ptr) {
12942dc36bedSjoris 			a = cvp->a;
12952dc36bedSjoris 			b = cvp->b;
12962dc36bedSjoris 			c = cvp->c;
12972dc36bedSjoris 			d = cvp->d;
12982dc36bedSjoris 
12992dc36bedSjoris 			if (a <= b && c <= d)
13002dc36bedSjoris 				ch = 'c';
13012dc36bedSjoris 			else
13022dc36bedSjoris 				ch = (a <= b) ? 'd' : 'a';
13032dc36bedSjoris 
13042dc36bedSjoris 			if (ch == 'd')
1305f5f501bbSmillert 				fetch(ixnew, lowc, d, f2, ' ', 0, flags);
13062dc36bedSjoris 			else {
1307f5f501bbSmillert 				fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
13082dc36bedSjoris 				fetch(ixnew, c, d, f2,
1309f5f501bbSmillert 				    ch == 'c' ? '!' : '+', 0, flags);
13102dc36bedSjoris 			}
13112dc36bedSjoris 			lowc = d + 1;
13122dc36bedSjoris 			cvp++;
13132dc36bedSjoris 		}
1314f5f501bbSmillert 		fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
13152dc36bedSjoris 	}
13162dc36bedSjoris 	context_vec_ptr = context_vec_start - 1;
13172dc36bedSjoris }
13182dc36bedSjoris 
13192dc36bedSjoris /* dump accumulated "unified" diff changes */
13202dc36bedSjoris static void
1321f5f501bbSmillert dump_unified_vec(FILE *f1, FILE *f2, int flags)
13222dc36bedSjoris {
13232dc36bedSjoris 	struct context_vec *cvp = context_vec_start;
13242dc36bedSjoris 	int lowa, upb, lowc, upd;
13252dc36bedSjoris 	int a, b, c, d;
13262dc36bedSjoris 	char ch, *f;
13272dc36bedSjoris 
13282dc36bedSjoris 	if (context_vec_start > context_vec_ptr)
13292dc36bedSjoris 		return;
13302dc36bedSjoris 
13312dc36bedSjoris 	b = d = 0;		/* gcc */
1332f5f501bbSmillert 	lowa = MAX(1, cvp->a - diff_context);
1333f5f501bbSmillert 	upb = MIN(diff_len[0], context_vec_ptr->b + diff_context);
1334f5f501bbSmillert 	lowc = MAX(1, cvp->c - diff_context);
1335f5f501bbSmillert 	upd = MIN(diff_len[1], context_vec_ptr->d + diff_context);
13362dc36bedSjoris 
13372dc36bedSjoris 	diff_output("@@ -");
13382dc36bedSjoris 	uni_range(lowa, upb);
13392dc36bedSjoris 	diff_output(" +");
13402dc36bedSjoris 	uni_range(lowc, upd);
13412dc36bedSjoris 	diff_output(" @@");
1342f5f501bbSmillert 	if ((flags & D_PROTOTYPE)) {
13432dc36bedSjoris 		f = match_function(ixold, lowa - 1, f1);
13442dc36bedSjoris 		if (f != NULL) {
13452dc36bedSjoris 			diff_output(" ");
13462dc36bedSjoris 			diff_output("%s", f);
13472dc36bedSjoris 		}
13482dc36bedSjoris 	}
13492dc36bedSjoris 	diff_output("\n");
13502dc36bedSjoris 
13512dc36bedSjoris 	/*
13522dc36bedSjoris 	 * Output changes in "unified" diff format--the old and new lines
13532dc36bedSjoris 	 * are printed together.
13542dc36bedSjoris 	 */
13552dc36bedSjoris 	for (; cvp <= context_vec_ptr; cvp++) {
13562dc36bedSjoris 		a = cvp->a;
13572dc36bedSjoris 		b = cvp->b;
13582dc36bedSjoris 		c = cvp->c;
13592dc36bedSjoris 		d = cvp->d;
13602dc36bedSjoris 
13612dc36bedSjoris 		/*
13622dc36bedSjoris 		 * c: both new and old changes
13632dc36bedSjoris 		 * d: only changes in the old file
13642dc36bedSjoris 		 * a: only changes in the new file
13652dc36bedSjoris 		 */
13662dc36bedSjoris 		if (a <= b && c <= d)
13672dc36bedSjoris 			ch = 'c';
13682dc36bedSjoris 		else
13692dc36bedSjoris 			ch = (a <= b) ? 'd' : 'a';
13702dc36bedSjoris 
13712dc36bedSjoris 		switch (ch) {
13722dc36bedSjoris 		case 'c':
1373f5f501bbSmillert 			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1374f5f501bbSmillert 			fetch(ixold, a, b, f1, '-', 0, flags);
1375f5f501bbSmillert 			fetch(ixnew, c, d, f2, '+', 0, flags);
13762dc36bedSjoris 			break;
13772dc36bedSjoris 		case 'd':
1378f5f501bbSmillert 			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1379f5f501bbSmillert 			fetch(ixold, a, b, f1, '-', 0, flags);
13802dc36bedSjoris 			break;
13812dc36bedSjoris 		case 'a':
1382f5f501bbSmillert 			fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
1383f5f501bbSmillert 			fetch(ixnew, c, d, f2, '+', 0, flags);
13842dc36bedSjoris 			break;
13852dc36bedSjoris 		}
13862dc36bedSjoris 		lowa = b + 1;
13872dc36bedSjoris 		lowc = d + 1;
13882dc36bedSjoris 	}
1389f5f501bbSmillert 	fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
13902dc36bedSjoris 
13912dc36bedSjoris 	context_vec_ptr = context_vec_start - 1;
13922dc36bedSjoris }
13932dc36bedSjoris 
13942dc36bedSjoris void
13952dc36bedSjoris diff_output(const char *fmt, ...)
13962dc36bedSjoris {
13972dc36bedSjoris 	va_list vap;
13982dc36bedSjoris 	int i;
13992dc36bedSjoris 	char *str;
14002dc36bedSjoris 
14012dc36bedSjoris 	va_start(vap, fmt);
14022dc36bedSjoris 	i = vasprintf(&str, fmt, vap);
14032dc36bedSjoris 	va_end(vap);
14042dc36bedSjoris 	if (i == -1)
1405d4625ebbSxsa 		err(1, "diff_output");
14062dc36bedSjoris 	if (diffbuf != NULL)
14072dc36bedSjoris 		rcs_buf_append(diffbuf, str, strlen(str));
14082dc36bedSjoris 	else
14092dc36bedSjoris 		printf("%s", str);
14102dc36bedSjoris 	xfree(str);
14112dc36bedSjoris }
1412