xref: /openbsd-src/usr.bin/cvs/diff.c (revision 895e6cf67c8fdf98f134f74ffd1d862147d98cdd)
1*895e6cf6Sjfb /*	$OpenBSD: diff.c,v 1.7 2004/08/12 18:37:27 jfb Exp $	*/
208f90673Sjfb /*
308f90673Sjfb  * Copyright (C) Caldera International Inc.  2001-2002.
408f90673Sjfb  * All rights reserved.
508f90673Sjfb  *
608f90673Sjfb  * Redistribution and use in source and binary forms, with or without
708f90673Sjfb  * modification, are permitted provided that the following conditions
808f90673Sjfb  * are met:
908f90673Sjfb  * 1. Redistributions of source code and documentation must retain the above
1008f90673Sjfb  *    copyright notice, this list of conditions and the following disclaimer.
1108f90673Sjfb  * 2. Redistributions in binary form must reproduce the above copyright
1208f90673Sjfb  *    notice, this list of conditions and the following disclaimer in the
1308f90673Sjfb  *    documentation and/or other materials provided with the distribution.
1408f90673Sjfb  * 3. All advertising materials mentioning features or use of this software
1508f90673Sjfb  *    must display the following acknowledgement:
1608f90673Sjfb  *	This product includes software developed or owned by Caldera
1708f90673Sjfb  *	International, Inc.
1808f90673Sjfb  * 4. Neither the name of Caldera International, Inc. nor the names of other
1908f90673Sjfb  *    contributors may be used to endorse or promote products derived from
2008f90673Sjfb  *    this software without specific prior written permission.
2108f90673Sjfb  *
2208f90673Sjfb  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
2308f90673Sjfb  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
2408f90673Sjfb  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2508f90673Sjfb  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2608f90673Sjfb  * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
2708f90673Sjfb  * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2808f90673Sjfb  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2908f90673Sjfb  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3008f90673Sjfb  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3108f90673Sjfb  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
3208f90673Sjfb  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3308f90673Sjfb  * POSSIBILITY OF SUCH DAMAGE.
3408f90673Sjfb  */
3508f90673Sjfb /*-
3608f90673Sjfb  * Copyright (c) 1991, 1993
3708f90673Sjfb  *	The Regents of the University of California.  All rights reserved.
3808f90673Sjfb  * Copyright (c) 2004 Jean-Francois Brousseau.  All rights reserved.
3908f90673Sjfb  *
4008f90673Sjfb  * Redistribution and use in source and binary forms, with or without
4108f90673Sjfb  * modification, are permitted provided that the following conditions
4208f90673Sjfb  * are met:
4308f90673Sjfb  * 1. Redistributions of source code must retain the above copyright
4408f90673Sjfb  *    notice, this list of conditions and the following disclaimer.
4508f90673Sjfb  * 2. Redistributions in binary form must reproduce the above copyright
4608f90673Sjfb  *    notice, this list of conditions and the following disclaimer in the
4708f90673Sjfb  *    documentation and/or other materials provided with the distribution.
4808f90673Sjfb  * 3. Neither the name of the University nor the names of its contributors
4908f90673Sjfb  *    may be used to endorse or promote products derived from this software
5008f90673Sjfb  *    without specific prior written permission.
5108f90673Sjfb  *
5208f90673Sjfb  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5308f90673Sjfb  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5408f90673Sjfb  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5508f90673Sjfb  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5608f90673Sjfb  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5708f90673Sjfb  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5808f90673Sjfb  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5908f90673Sjfb  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6008f90673Sjfb  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6108f90673Sjfb  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6208f90673Sjfb  * SUCH DAMAGE.
6308f90673Sjfb  *
6408f90673Sjfb  *	@(#)diffreg.c   8.1 (Berkeley) 6/6/93
6508f90673Sjfb  */
6608f90673Sjfb /*
6708f90673Sjfb  *	Uses an algorithm due to Harold Stone, which finds
6808f90673Sjfb  *	a pair of longest identical subsequences in the two
6908f90673Sjfb  *	files.
7008f90673Sjfb  *
7108f90673Sjfb  *	The major goal is to generate the match vector J.
7208f90673Sjfb  *	J[i] is the index of the line in file1 corresponding
7308f90673Sjfb  *	to line i file0. J[i] = 0 if there is no
7408f90673Sjfb  *	such line in file1.
7508f90673Sjfb  *
7608f90673Sjfb  *	Lines are hashed so as to work in core. All potential
7708f90673Sjfb  *	matches are located by sorting the lines of each file
7808f90673Sjfb  *	on the hash (called ``value''). In particular, this
7908f90673Sjfb  *	collects the equivalence classes in file1 together.
8008f90673Sjfb  *	Subroutine equiv replaces the value of each line in
8108f90673Sjfb  *	file0 by the index of the first element of its
8208f90673Sjfb  *	matching equivalence in (the reordered) file1.
8308f90673Sjfb  *	To save space equiv squeezes file1 into a single
8408f90673Sjfb  *	array member in which the equivalence classes
8508f90673Sjfb  *	are simply concatenated, except that their first
8608f90673Sjfb  *	members are flagged by changing sign.
8708f90673Sjfb  *
8808f90673Sjfb  *	Next the indices that point into member are unsorted into
8908f90673Sjfb  *	array class according to the original order of file0.
9008f90673Sjfb  *
9108f90673Sjfb  *	The cleverness lies in routine stone. This marches
9208f90673Sjfb  *	through the lines of file0, developing a vector klist
9308f90673Sjfb  *	of "k-candidates". At step i a k-candidate is a matched
9408f90673Sjfb  *	pair of lines x,y (x in file0 y in file1) such that
9508f90673Sjfb  *	there is a common subsequence of length k
9608f90673Sjfb  *	between the first i lines of file0 and the first y
9708f90673Sjfb  *	lines of file1, but there is no such subsequence for
9808f90673Sjfb  *	any smaller y. x is the earliest possible mate to y
9908f90673Sjfb  *	that occurs in such a subsequence.
10008f90673Sjfb  *
10108f90673Sjfb  *	Whenever any of the members of the equivalence class of
10208f90673Sjfb  *	lines in file1 matable to a line in file0 has serial number
10308f90673Sjfb  *	less than the y of some k-candidate, that k-candidate
10408f90673Sjfb  *	with the smallest such y is replaced. The new
10508f90673Sjfb  *	k-candidate is chained (via pred) to the current
10608f90673Sjfb  *	k-1 candidate so that the actual subsequence can
10708f90673Sjfb  *	be recovered. When a member has serial number greater
10808f90673Sjfb  *	that the y of all k-candidates, the klist is extended.
10908f90673Sjfb  *	At the end, the longest subsequence is pulled out
11008f90673Sjfb  *	and placed in the array J by unravel
11108f90673Sjfb  *
11208f90673Sjfb  *	With J in hand, the matches there recorded are
11308f90673Sjfb  *	check'ed against reality to assure that no spurious
11408f90673Sjfb  *	matches have crept in due to hashing. If they have,
11508f90673Sjfb  *	they are broken, and "jackpot" is recorded--a harmless
11608f90673Sjfb  *	matter except that a true match for a spuriously
11708f90673Sjfb  *	mated line may now be unnecessarily reported as a change.
11808f90673Sjfb  *
11908f90673Sjfb  *	Much of the complexity of the program comes simply
12008f90673Sjfb  *	from trying to minimize core utilization and
12108f90673Sjfb  *	maximize the range of doable problems by dynamically
12208f90673Sjfb  *	allocating what is needed and reusing what is not.
12308f90673Sjfb  *	The core requirements for problems larger than somewhat
12408f90673Sjfb  *	are (in words) 2*length(file0) + length(file1) +
12508f90673Sjfb  *	3*(number of k-candidates installed),  typically about
12608f90673Sjfb  *	6n words for files of length n.
12708f90673Sjfb  */
12808f90673Sjfb 
12908f90673Sjfb #include <sys/param.h>
13008f90673Sjfb #include <sys/stat.h>
13108f90673Sjfb #include <sys/wait.h>
13208f90673Sjfb 
13308f90673Sjfb #include <errno.h>
13408f90673Sjfb #include <ctype.h>
13508f90673Sjfb #include <stdio.h>
13608f90673Sjfb #include <fcntl.h>
13708f90673Sjfb #include <paths.h>
13808f90673Sjfb #include <regex.h>
13908f90673Sjfb #include <dirent.h>
14008f90673Sjfb #include <stdlib.h>
14108f90673Sjfb #include <stddef.h>
14208f90673Sjfb #include <unistd.h>
14308f90673Sjfb #include <string.h>
14408f90673Sjfb #include <sysexits.h>
14508f90673Sjfb 
14608f90673Sjfb #include "cvs.h"
14708f90673Sjfb #include "log.h"
14808f90673Sjfb #include "buf.h"
149dc6a6879Sjfb #include "proto.h"
15008f90673Sjfb 
15108f90673Sjfb 
15208f90673Sjfb #define CVS_DIFF_DEFCTX    3   /* default context length */
15308f90673Sjfb 
15408f90673Sjfb 
15508f90673Sjfb /*
15608f90673Sjfb  * Output format options
15708f90673Sjfb  */
15808f90673Sjfb #define	D_NORMAL	0	/* Normal output */
15908f90673Sjfb #define	D_CONTEXT	1	/* Diff with context */
16008f90673Sjfb #define	D_UNIFIED	2	/* Unified context diff */
16108f90673Sjfb #define	D_IFDEF		3	/* Diff with merged #ifdef's */
16208f90673Sjfb #define	D_BRIEF		4	/* Say if the files differ */
16308f90673Sjfb 
16408f90673Sjfb /*
16508f90673Sjfb  * Status values for print_status() and diffreg() return values
16608f90673Sjfb  */
16708f90673Sjfb #define	D_SAME		0	/* Files are the same */
16808f90673Sjfb #define	D_DIFFER	1	/* Files are different */
16908f90673Sjfb #define	D_BINARY	2	/* Binary files are different */
17008f90673Sjfb #define	D_COMMON	3	/* Subdirectory common to both dirs */
17108f90673Sjfb #define	D_ONLY		4	/* Only exists in one directory */
17208f90673Sjfb #define	D_MISMATCH1	5	/* path1 was a dir, path2 a file */
17308f90673Sjfb #define	D_MISMATCH2	6	/* path1 was a file, path2 a dir */
17408f90673Sjfb #define	D_ERROR		7	/* An error occurred */
17508f90673Sjfb #define	D_SKIPPED1	8	/* path1 was a special file */
17608f90673Sjfb #define	D_SKIPPED2	9	/* path2 was a special file */
17708f90673Sjfb 
17808f90673Sjfb struct cand {
17908f90673Sjfb 	int x;
18008f90673Sjfb 	int y;
18108f90673Sjfb 	int pred;
18208f90673Sjfb } cand;
18308f90673Sjfb 
18408f90673Sjfb struct line {
18508f90673Sjfb 	int serial;
18608f90673Sjfb 	int value;
18708f90673Sjfb } *file[2];
18808f90673Sjfb 
18908f90673Sjfb /*
19008f90673Sjfb  * The following struct is used to record change information when
19108f90673Sjfb  * doing a "context" or "unified" diff.  (see routine "change" to
19208f90673Sjfb  * understand the highly mnemonic field names)
19308f90673Sjfb  */
19408f90673Sjfb struct context_vec {
19508f90673Sjfb 	int a;			/* start line in old file */
19608f90673Sjfb 	int b;			/* end line in old file */
19708f90673Sjfb 	int c;			/* start line in new file */
19808f90673Sjfb 	int d;			/* end line in new file */
19908f90673Sjfb };
20008f90673Sjfb 
201dc6a6879Sjfb struct diff_arg {
202dc6a6879Sjfb 	char  *rev1;
203dc6a6879Sjfb 	char  *rev2;
204dc6a6879Sjfb 	char  *date1;
205dc6a6879Sjfb 	char  *date2;
206dc6a6879Sjfb };
207dc6a6879Sjfb 
20808f90673Sjfb 
20908f90673Sjfb struct excludes {
21008f90673Sjfb 	char *pattern;
21108f90673Sjfb 	struct excludes *next;
21208f90673Sjfb };
21308f90673Sjfb 
21408f90673Sjfb 
21508f90673Sjfb char	*splice(char *, char *);
21608f90673Sjfb int  cvs_diffreg(const char *, const char *);
217dc6a6879Sjfb int  cvs_diff_file  (struct cvs_file *, void *);
21808f90673Sjfb static void output(const char *, FILE *, const char *, FILE *);
21908f90673Sjfb static void check(FILE *, FILE *);
22008f90673Sjfb static void range(int, int, char *);
22108f90673Sjfb static void uni_range(int, int);
22208f90673Sjfb static void dump_context_vec(FILE *, FILE *);
22308f90673Sjfb static void dump_unified_vec(FILE *, FILE *);
22408f90673Sjfb static void prepare(int, FILE *, off_t);
22508f90673Sjfb static void prune(void);
22608f90673Sjfb static void equiv(struct line *, int, struct line *, int, int *);
22708f90673Sjfb static void unravel(int);
22808f90673Sjfb static void unsort(struct line *, int, int *);
22908f90673Sjfb static void change(const char *, FILE *, const char *, FILE *, int, int, int, int);
23008f90673Sjfb static void sort(struct line *, int);
23108f90673Sjfb static int  ignoreline(char *);
23208f90673Sjfb static int  asciifile(FILE *);
23308f90673Sjfb static int  fetch(long *, int, int, FILE *, int, int);
23408f90673Sjfb static int  newcand(int, int, int);
23508f90673Sjfb static int  search(int *, int, int);
23608f90673Sjfb static int  skipline(FILE *);
23708f90673Sjfb static int  isqrt(int);
23808f90673Sjfb static int  stone(int *, int, int *, int *);
23908f90673Sjfb static int  readhash(FILE *);
24008f90673Sjfb static int  files_differ(FILE *, FILE *);
24108f90673Sjfb static char *preadline(int, size_t, off_t);
24208f90673Sjfb 
24308f90673Sjfb 
24408f90673Sjfb 
24508f90673Sjfb extern int cvs_client;
24608f90673Sjfb 
24708f90673Sjfb static int aflag, bflag, dflag, iflag, tflag, Tflag, wflag;
24808f90673Sjfb static int context, status;
24908f90673Sjfb static int format = D_NORMAL;
25008f90673Sjfb static struct stat stb1, stb2;
251f5638424Sjfb static char *ifdefname, *ignore_pats, diffargs[128];
252f5638424Sjfb static const char *diff_file;
25308f90673Sjfb regex_t ignore_re;
25408f90673Sjfb 
25508f90673Sjfb static int  *J;			/* will be overlaid on class */
25608f90673Sjfb static int  *class;		/* will be overlaid on file[0] */
25708f90673Sjfb static int  *klist;		/* will be overlaid on file[0] after class */
25808f90673Sjfb static int  *member;		/* will be overlaid on file[1] */
25908f90673Sjfb static int   clen;
26008f90673Sjfb static int   inifdef;		/* whether or not we are in a #ifdef block */
26108f90673Sjfb static int   len[2];
26208f90673Sjfb static int   pref, suff;	/* length of prefix and suffix */
26308f90673Sjfb static int   slen[2];
26408f90673Sjfb static int   anychange;
26508f90673Sjfb static long *ixnew;		/* will be overlaid on file[1] */
26608f90673Sjfb static long *ixold;		/* will be overlaid on klist */
26708f90673Sjfb static struct cand *clist;	/* merely a free storage pot for candidates */
26808f90673Sjfb static int   clistlen;		/* the length of clist */
26908f90673Sjfb static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
27008f90673Sjfb static u_char *chrtran;		/* translation table for case-folding */
27108f90673Sjfb static struct context_vec *context_vec_start;
27208f90673Sjfb static struct context_vec *context_vec_end;
27308f90673Sjfb static struct context_vec *context_vec_ptr;
27408f90673Sjfb 
27508f90673Sjfb #define FUNCTION_CONTEXT_SIZE	41
27608f90673Sjfb static int lastline;
27708f90673Sjfb static int lastmatchline;
27808f90673Sjfb 
27908f90673Sjfb 
28008f90673Sjfb 
28108f90673Sjfb 
28208f90673Sjfb /*
28308f90673Sjfb  * chrtran points to one of 2 translation tables: cup2low if folding upper to
28408f90673Sjfb  * lower case clow2low if not folding case
28508f90673Sjfb  */
28608f90673Sjfb u_char clow2low[256] = {
28708f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
28808f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
28908f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
29008f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
29108f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
29208f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
29308f90673Sjfb 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
29408f90673Sjfb 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
29508f90673Sjfb 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
29608f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
29708f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
29808f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
29908f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
30008f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
30108f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
30208f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
30308f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
30408f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
30508f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
30608f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
30708f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
30808f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
30908f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
31008f90673Sjfb 	0xfd, 0xfe, 0xff
31108f90673Sjfb };
31208f90673Sjfb 
31308f90673Sjfb u_char cup2low[256] = {
31408f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
31508f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
31608f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
31708f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
31808f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
31908f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
32008f90673Sjfb 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
32108f90673Sjfb 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
32208f90673Sjfb 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
32308f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
32408f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
32508f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
32608f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
32708f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
32808f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
32908f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
33008f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
33108f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
33208f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
33308f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
33408f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
33508f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
33608f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
33708f90673Sjfb 	0xfd, 0xfe, 0xff
33808f90673Sjfb };
33908f90673Sjfb 
34008f90673Sjfb 
34108f90673Sjfb 
34208f90673Sjfb /*
34308f90673Sjfb  * cvs_diff()
34408f90673Sjfb  *
34508f90673Sjfb  * Handler for the `cvs diff' command.
34608f90673Sjfb  *
34708f90673Sjfb  * SYNOPSIS: cvs [args] diff [-clipu] [-D date] [-r rev]
34808f90673Sjfb  */
34908f90673Sjfb 
35008f90673Sjfb int
35108f90673Sjfb cvs_diff(int argc, char **argv)
35208f90673Sjfb {
353dc6a6879Sjfb 	int ch, recurse, flags;
354dc6a6879Sjfb 	struct diff_arg darg;
355dc6a6879Sjfb 	struct cvsroot *root;
35608f90673Sjfb 
35708f90673Sjfb 	context = CVS_DIFF_DEFCTX;
358dc6a6879Sjfb 	flags = CF_RECURSE|CF_IGNORE|CF_SORT|CF_KNOWN;
35908f90673Sjfb 	recurse = 1;
36008f90673Sjfb 
361dc6a6879Sjfb 	memset(&darg, 0, sizeof(darg));
362dc6a6879Sjfb 	strlcpy(diffargs, argv[0], sizeof(diffargs));
363dc6a6879Sjfb 
36408f90673Sjfb 	while ((ch = getopt(argc, argv, "cD:lir:u")) != -1) {
36508f90673Sjfb 		switch (ch) {
36608f90673Sjfb 		case 'c':
367f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
36808f90673Sjfb 			format = D_CONTEXT;
36908f90673Sjfb 			break;
37008f90673Sjfb 		case 'D':
371dc6a6879Sjfb 			if (darg.date1 == NULL && darg.rev1 == NULL)
372dc6a6879Sjfb 				darg.date1 = optarg;
373dc6a6879Sjfb 			else if (darg.date2 == NULL && darg.rev2 == NULL)
374dc6a6879Sjfb 				darg.date2 = optarg;
37508f90673Sjfb 			else {
37608f90673Sjfb 				cvs_log(LP_ERR,
37708f90673Sjfb 				    "no more than two revisions/dates can "
37808f90673Sjfb 				    "be specified");
37908f90673Sjfb 			}
38008f90673Sjfb 			break;
38108f90673Sjfb 		case 'l':
382f5638424Sjfb 			strlcat(diffargs, " -l", sizeof(diffargs));
38308f90673Sjfb 			recurse = 0;
384dc6a6879Sjfb 			flags &= ~CF_RECURSE;
38508f90673Sjfb 			break;
38608f90673Sjfb 		case 'i':
387f5638424Sjfb 			strlcat(diffargs, " -i", sizeof(diffargs));
38808f90673Sjfb 			iflag = 1;
38908f90673Sjfb 			break;
39008f90673Sjfb 		case 'r':
391dc6a6879Sjfb 			if ((darg.rev1 == NULL) && (darg.date1 == NULL))
392dc6a6879Sjfb 				darg.rev1 = optarg;
393dc6a6879Sjfb 			else if ((darg.rev2 == NULL) && (darg.date2 == NULL))
394dc6a6879Sjfb 				darg.rev2 = optarg;
39508f90673Sjfb 			else {
39608f90673Sjfb 				cvs_log(LP_ERR,
39708f90673Sjfb 				    "no more than two revisions/dates can "
39808f90673Sjfb 				    "be specified");
39908f90673Sjfb 				return (EX_USAGE);
40008f90673Sjfb 			}
40108f90673Sjfb 			break;
40208f90673Sjfb 		case 'u':
403f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
40408f90673Sjfb 			format = D_UNIFIED;
40508f90673Sjfb 			break;
40608f90673Sjfb 		default:
40708f90673Sjfb 			return (EX_USAGE);
40808f90673Sjfb 		}
40908f90673Sjfb 	}
41008f90673Sjfb 
41108f90673Sjfb 	argc -= optind;
41208f90673Sjfb 	argv += optind;
41308f90673Sjfb 
41408f90673Sjfb 	if (argc == 0) {
415b33893e8Sjfb 		cvs_files = cvs_file_get(".", flags);
416dc6a6879Sjfb 	}
417dc6a6879Sjfb 	else
418b33893e8Sjfb 		cvs_files = cvs_file_getspec(argv, argc, 0);
419*895e6cf6Sjfb 	if (cvs_files == NULL)
420*895e6cf6Sjfb 		return (EX_DATAERR);
42108f90673Sjfb 
422b33893e8Sjfb 	cvs_file_examine(cvs_files, cvs_diff_file, &darg);
42308f90673Sjfb 
424b33893e8Sjfb 	root = cvs_files->cf_ddat->cd_root;
425dd2c5b0dSjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
426b33893e8Sjfb 		cvs_senddir(root, cvs_files);
427dc6a6879Sjfb 		cvs_sendreq(root, CVS_REQ_DIFF, NULL);
428dd2c5b0dSjfb 	}
42908f90673Sjfb 
430dc6a6879Sjfb 	return (0);
431dc6a6879Sjfb }
432dc6a6879Sjfb 
433dc6a6879Sjfb 
434dc6a6879Sjfb /*
435dc6a6879Sjfb  * cvs_diff_sendflags()
436dc6a6879Sjfb  *
437dc6a6879Sjfb  */
438dc6a6879Sjfb 
439dc6a6879Sjfb int
440dc6a6879Sjfb cvs_diff_sendflags(struct cvsroot *root, struct diff_arg *dap)
441dc6a6879Sjfb {
44208f90673Sjfb 	/* send the flags */
44308f90673Sjfb 	if (format == D_CONTEXT)
444dc6a6879Sjfb 		cvs_sendarg(root, "-c", 0);
44508f90673Sjfb 	else if (format == D_UNIFIED)
446dc6a6879Sjfb 		cvs_sendarg(root, "-u", 0);
44708f90673Sjfb 
448dc6a6879Sjfb 	if (dap->rev1 != NULL) {
449dc6a6879Sjfb 		cvs_sendarg(root, "-r", 0);
450dc6a6879Sjfb 		cvs_sendarg(root, dap->rev1, 1);
45108f90673Sjfb 	}
452dc6a6879Sjfb 	else if (dap->date1 != NULL) {
453dc6a6879Sjfb 		cvs_sendarg(root, "-D", 0);
454dc6a6879Sjfb 		cvs_sendarg(root, dap->date1, 1);
45508f90673Sjfb 	}
456dc6a6879Sjfb 	if (dap->rev2 != NULL) {
457dc6a6879Sjfb 		cvs_sendarg(root, "-r", 0);
458dc6a6879Sjfb 		cvs_sendarg(root, dap->rev2, 1);
45908f90673Sjfb 	}
460dc6a6879Sjfb 	else if (dap->date2 != NULL) {
461dc6a6879Sjfb 		cvs_sendarg(root, "-D", 0);
462dc6a6879Sjfb 		cvs_sendarg(root, dap->date2, 1);
46308f90673Sjfb 	}
46408f90673Sjfb 
46508f90673Sjfb 	return (0);
46608f90673Sjfb }
46708f90673Sjfb 
46808f90673Sjfb 
46908f90673Sjfb /*
47008f90673Sjfb  * cvs_diff_file()
47108f90673Sjfb  *
47208f90673Sjfb  * Diff a single file.
47308f90673Sjfb  */
47408f90673Sjfb 
47508f90673Sjfb int
476dc6a6879Sjfb cvs_diff_file(struct cvs_file *cfp, void *arg)
47708f90673Sjfb {
478dc6a6879Sjfb 	char *dir, *repo, rcspath[MAXPATHLEN], buf[64];
47908f90673Sjfb 	BUF *b1, *b2;
48008f90673Sjfb 	RCSNUM *r1, *r2;
48108f90673Sjfb 	RCSFILE *rf;
482dc6a6879Sjfb 	struct diff_arg *dap;
48308f90673Sjfb 	struct cvs_ent *entp;
484dc6a6879Sjfb 	struct cvsroot *root;
485dc6a6879Sjfb 
486dc6a6879Sjfb 	dap = (struct diff_arg *)arg;
487dc6a6879Sjfb 
488dc6a6879Sjfb 	if (cfp->cf_type == DT_DIR) {
489*895e6cf6Sjfb 		if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
490*895e6cf6Sjfb 			root = cfp->cf_parent->cf_ddat->cd_root;
491*895e6cf6Sjfb 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
492*895e6cf6Sjfb 		}
493*895e6cf6Sjfb 		else {
494dc6a6879Sjfb 			root = cfp->cf_ddat->cd_root;
495dc6a6879Sjfb 			if ((cfp->cf_parent == NULL) ||
496dc6a6879Sjfb 			    (root != cfp->cf_parent->cf_ddat->cd_root)) {
497dc6a6879Sjfb 				cvs_connect(root);
498dc6a6879Sjfb 				cvs_diff_sendflags(root, dap);
499dc6a6879Sjfb 			}
500dc6a6879Sjfb 
501dc6a6879Sjfb 			cvs_senddir(root, cfp);
502*895e6cf6Sjfb 		}
503*895e6cf6Sjfb 
504dc6a6879Sjfb 		return (0);
505dc6a6879Sjfb 	}
50608f90673Sjfb 
50708f90673Sjfb 	rf = NULL;
508dc6a6879Sjfb 	diff_file = cfp->cf_path;
509*895e6cf6Sjfb 
510dc6a6879Sjfb 	if (cfp->cf_parent != NULL) {
511dc6a6879Sjfb 		dir = cfp->cf_parent->cf_path;
512*895e6cf6Sjfb 		root = cfp->cf_parent->cf_ddat->cd_root;
513dc6a6879Sjfb 		repo = cfp->cf_parent->cf_ddat->cd_repo;
514dc6a6879Sjfb 	}
515dc6a6879Sjfb 	else {
516dc6a6879Sjfb 		dir = ".";
517*895e6cf6Sjfb 		root = NULL;
518dc6a6879Sjfb 		repo = NULL;
51908f90673Sjfb 	}
52008f90673Sjfb 
521dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
522dc6a6879Sjfb 		if (root->cr_method == CVS_METHOD_LOCAL)
523dc6a6879Sjfb 			cvs_log(LP_WARN, "I know nothing about %s", diff_file);
524dc6a6879Sjfb 		else
525dc6a6879Sjfb 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
526dc6a6879Sjfb 		return (0);
52708f90673Sjfb 	}
52808f90673Sjfb 
529dc6a6879Sjfb 	entp = cvs_ent_getent(diff_file);
530dc6a6879Sjfb 	if (entp == NULL)
531dc6a6879Sjfb 		return (-1);
532dc6a6879Sjfb 
533dc6a6879Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
534dc6a6879Sjfb 		if (cvs_sendentry(root, entp) < 0) {
535dc6a6879Sjfb 			cvs_ent_free(entp);
53608f90673Sjfb 			return (-1);
53708f90673Sjfb 		}
53808f90673Sjfb 	}
53908f90673Sjfb 
540dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UPTODATE) {
541dc6a6879Sjfb 		if (root->cr_method != CVS_METHOD_LOCAL)
542dc6a6879Sjfb 			cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name);
543dc6a6879Sjfb 		cvs_ent_free(entp);
54408f90673Sjfb 		return (0);
54508f90673Sjfb 	}
54608f90673Sjfb 
54708f90673Sjfb 	/* at this point, the file is modified */
548dc6a6879Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
549dc6a6879Sjfb 		cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name);
550dc6a6879Sjfb 		cvs_sendfile(root, diff_file);
55108f90673Sjfb 	}
55208f90673Sjfb 	else {
55308f90673Sjfb 		snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
554dc6a6879Sjfb 		    root->cr_dir, repo, diff_file, RCS_FILE_EXT);
55508f90673Sjfb 
55608f90673Sjfb 		rf = rcs_open(rcspath, RCS_MODE_READ);
557dc6a6879Sjfb 		if (rf == NULL) {
558dc6a6879Sjfb 			cvs_ent_free(entp);
55908f90673Sjfb 			return (-1);
560dc6a6879Sjfb 		}
56108f90673Sjfb 
562dc6a6879Sjfb 		cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file,
56308f90673Sjfb 		    RCS_DIFF_DIV, rcspath);
56408f90673Sjfb 
565dc6a6879Sjfb 		if (dap->rev1 == NULL)
56608f90673Sjfb 			r1 = entp->ce_rev;
56708f90673Sjfb 		else {
56808f90673Sjfb 			r1 = rcsnum_alloc();
569dc6a6879Sjfb 			rcsnum_aton(dap->rev1, NULL, r1);
57008f90673Sjfb 		}
57108f90673Sjfb 
572dc6a6879Sjfb 		cvs_printf("retrieving revision %s\n",
57308f90673Sjfb 		    rcsnum_tostr(r1, buf, sizeof(buf)));
57408f90673Sjfb 		b1 = rcs_getrev(rf, r1);
57508f90673Sjfb 
576dc6a6879Sjfb 		if (dap->rev2 != NULL) {
577dc6a6879Sjfb 			cvs_printf("retrieving revision %s\n", dap->rev2);
57808f90673Sjfb 			r2 = rcsnum_alloc();
579dc6a6879Sjfb 			rcsnum_aton(dap->rev2, NULL, r2);
58008f90673Sjfb 			b2 = rcs_getrev(rf, r2);
58108f90673Sjfb 		}
58208f90673Sjfb 		else {
583dc6a6879Sjfb 			b2 = cvs_buf_load(diff_file, BUF_AUTOEXT);
58408f90673Sjfb 		}
58508f90673Sjfb 
586dc6a6879Sjfb 		rcs_close(rf);
587dc6a6879Sjfb 
588f5638424Sjfb 		printf("%s", diffargs);
589f5638424Sjfb 		printf(" -r%s", buf);
590dc6a6879Sjfb 		if (dap->rev2 != NULL)
591dc6a6879Sjfb 			printf(" -r%s", dap->rev2);
592dc6a6879Sjfb 		printf(" %s\n", diff_file);
59308f90673Sjfb 		cvs_buf_write(b1, "/tmp/diff1", 0600);
59408f90673Sjfb 		cvs_buf_write(b2, "/tmp/diff2", 0600);
59508f90673Sjfb 		cvs_diffreg("/tmp/diff1", "/tmp/diff2");
59608f90673Sjfb 	}
59708f90673Sjfb 
598dc6a6879Sjfb 	cvs_ent_free(entp);
59908f90673Sjfb 	return (0);
60008f90673Sjfb }
60108f90673Sjfb 
60208f90673Sjfb 
60308f90673Sjfb int
60408f90673Sjfb cvs_diffreg(const char *file1, const char *file2)
60508f90673Sjfb {
60608f90673Sjfb 	FILE *f1, *f2;
60708f90673Sjfb 	int i, rval;
60808f90673Sjfb 
60908f90673Sjfb 	f1 = f2 = NULL;
61008f90673Sjfb 	rval = D_SAME;
61108f90673Sjfb 	anychange = 0;
61208f90673Sjfb 	lastline = 0;
61308f90673Sjfb 	lastmatchline = 0;
61408f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
61508f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
61608f90673Sjfb 
61708f90673Sjfb 	f1 = fopen(file1, "r");
61808f90673Sjfb 	if (f1 == NULL) {
61908f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
62008f90673Sjfb 		status |= 2;
62108f90673Sjfb 		goto closem;
62208f90673Sjfb 	}
62308f90673Sjfb 
62408f90673Sjfb 	f2 = fopen(file2, "r");
62508f90673Sjfb 	if (f2 == NULL) {
62608f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
62708f90673Sjfb 		status |= 2;
62808f90673Sjfb 		goto closem;
62908f90673Sjfb 	}
63008f90673Sjfb 
63108f90673Sjfb 	switch (files_differ(f1, f2)) {
63208f90673Sjfb 	case 0:
63308f90673Sjfb 		goto closem;
63408f90673Sjfb 	case 1:
63508f90673Sjfb 		break;
63608f90673Sjfb 	default:
63708f90673Sjfb 		/* error */
63808f90673Sjfb 		status |= 2;
63908f90673Sjfb 		goto closem;
64008f90673Sjfb 	}
64108f90673Sjfb 
64208f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
64308f90673Sjfb 		rval = D_BINARY;
64408f90673Sjfb 		status |= 1;
64508f90673Sjfb 		goto closem;
64608f90673Sjfb 	}
64708f90673Sjfb 	prepare(0, f1, stb1.st_size);
64808f90673Sjfb 	prepare(1, f2, stb2.st_size);
64908f90673Sjfb 	prune();
65008f90673Sjfb 	sort(sfile[0], slen[0]);
65108f90673Sjfb 	sort(sfile[1], slen[1]);
65208f90673Sjfb 
65308f90673Sjfb 	member = (int *)file[1];
65408f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
65508f90673Sjfb 	member = realloc(member, (slen[1] + 2) * sizeof(int));
65608f90673Sjfb 
65708f90673Sjfb 	class = (int *)file[0];
65808f90673Sjfb 	unsort(sfile[0], slen[0], class);
65908f90673Sjfb 	class = realloc(class, (slen[0] + 2) * sizeof(int));
66008f90673Sjfb 
66108f90673Sjfb 	klist = malloc((slen[0] + 2) * sizeof(int));
66208f90673Sjfb 	clen = 0;
66308f90673Sjfb 	clistlen = 100;
66408f90673Sjfb 	clist = malloc(clistlen * sizeof(cand));
66508f90673Sjfb 	i = stone(class, slen[0], member, klist);
66608f90673Sjfb 	free(member);
66708f90673Sjfb 	free(class);
66808f90673Sjfb 
66908f90673Sjfb 	J = realloc(J, (len[0] + 2) * sizeof(int));
67008f90673Sjfb 	unravel(klist[i]);
67108f90673Sjfb 	free(clist);
67208f90673Sjfb 	free(klist);
67308f90673Sjfb 
67408f90673Sjfb 	ixold = realloc(ixold, (len[0] + 2) * sizeof(long));
67508f90673Sjfb 	ixnew = realloc(ixnew, (len[1] + 2) * sizeof(long));
67608f90673Sjfb 	check(f1, f2);
67708f90673Sjfb 	output(file1, f1, file2, f2);
67808f90673Sjfb 
67908f90673Sjfb closem:
68008f90673Sjfb 	if (anychange) {
68108f90673Sjfb 		status |= 1;
68208f90673Sjfb 		if (rval == D_SAME)
68308f90673Sjfb 			rval = D_DIFFER;
68408f90673Sjfb 	}
68508f90673Sjfb 	if (f1 != NULL)
68608f90673Sjfb 		fclose(f1);
68708f90673Sjfb 	if (f2 != NULL)
68808f90673Sjfb 		fclose(f2);
68908f90673Sjfb 	return (rval);
69008f90673Sjfb }
69108f90673Sjfb 
69208f90673Sjfb /*
69308f90673Sjfb  * Check to see if the given files differ.
69408f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
69508f90673Sjfb  * XXX - could use code from cmp(1) [faster]
69608f90673Sjfb  */
69708f90673Sjfb static int
69808f90673Sjfb files_differ(FILE *f1, FILE *f2)
69908f90673Sjfb {
70008f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
70108f90673Sjfb 	size_t i, j;
70208f90673Sjfb 
70308f90673Sjfb 	if (stb1.st_size != stb2.st_size)
70408f90673Sjfb 		return (1);
70508f90673Sjfb 	for (;;) {
70608f90673Sjfb 		i = fread(buf1, 1, sizeof(buf1), f1);
70708f90673Sjfb 		j = fread(buf2, 1, sizeof(buf2), f2);
70808f90673Sjfb 		if (i != j)
70908f90673Sjfb 			return (1);
71008f90673Sjfb 		if (i == 0 && j == 0) {
71108f90673Sjfb 			if (ferror(f1) || ferror(f2))
71208f90673Sjfb 				return (1);
71308f90673Sjfb 			return (0);
71408f90673Sjfb 		}
71508f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
71608f90673Sjfb 			return (1);
71708f90673Sjfb 	}
71808f90673Sjfb }
71908f90673Sjfb 
72008f90673Sjfb 
72108f90673Sjfb char *
72208f90673Sjfb splice(char *dir, char *file)
72308f90673Sjfb {
72408f90673Sjfb 	char *tail, *buf;
72508f90673Sjfb 
72608f90673Sjfb 	if ((tail = strrchr(file, '/')) == NULL)
72708f90673Sjfb 		tail = file;
72808f90673Sjfb 	else
72908f90673Sjfb 		tail++;
73008f90673Sjfb 	asprintf(&buf, "%s/%s", dir, tail);
73108f90673Sjfb 	return (buf);
73208f90673Sjfb }
73308f90673Sjfb 
73408f90673Sjfb static void
73508f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
73608f90673Sjfb {
73708f90673Sjfb 	struct line *p;
73808f90673Sjfb 	int j, h;
73908f90673Sjfb 	size_t sz;
74008f90673Sjfb 
74108f90673Sjfb 	rewind(fd);
74208f90673Sjfb 
74308f90673Sjfb 	sz = (filesize <= SIZE_MAX ? filesize : SIZE_MAX) / 25;
74408f90673Sjfb 	if (sz < 100)
74508f90673Sjfb 		sz = 100;
74608f90673Sjfb 
74708f90673Sjfb 	p = malloc((sz + 3) * sizeof(struct line));
74808f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
74908f90673Sjfb 		if (j == (int)sz) {
75008f90673Sjfb 			sz = sz * 3 / 2;
75108f90673Sjfb 			p = realloc(p, (sz + 3) * sizeof(struct line));
75208f90673Sjfb 		}
75308f90673Sjfb 		p[++j].value = h;
75408f90673Sjfb 	}
75508f90673Sjfb 	len[i] = j;
75608f90673Sjfb 	file[i] = p;
75708f90673Sjfb }
75808f90673Sjfb 
75908f90673Sjfb static void
76008f90673Sjfb prune(void)
76108f90673Sjfb {
76208f90673Sjfb 	int i, j;
76308f90673Sjfb 
76408f90673Sjfb 	for (pref = 0; pref < len[0] && pref < len[1] &&
76508f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
76608f90673Sjfb 	    pref++)
76708f90673Sjfb 		;
76808f90673Sjfb 	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
76908f90673Sjfb 	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
77008f90673Sjfb 	    suff++)
77108f90673Sjfb 		;
77208f90673Sjfb 	for (j = 0; j < 2; j++) {
77308f90673Sjfb 		sfile[j] = file[j] + pref;
77408f90673Sjfb 		slen[j] = len[j] - pref - suff;
77508f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
77608f90673Sjfb 			sfile[j][i].serial = i;
77708f90673Sjfb 	}
77808f90673Sjfb }
77908f90673Sjfb 
78008f90673Sjfb static void
78108f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
78208f90673Sjfb {
78308f90673Sjfb 	int i, j;
78408f90673Sjfb 
78508f90673Sjfb 	i = j = 1;
78608f90673Sjfb 	while (i <= n && j <= m) {
78708f90673Sjfb 		if (a[i].value < b[j].value)
78808f90673Sjfb 			a[i++].value = 0;
78908f90673Sjfb 		else if (a[i].value == b[j].value)
79008f90673Sjfb 			a[i++].value = j;
79108f90673Sjfb 		else
79208f90673Sjfb 			j++;
79308f90673Sjfb 	}
79408f90673Sjfb 	while (i <= n)
79508f90673Sjfb 		a[i++].value = 0;
79608f90673Sjfb 	b[m + 1].value = 0;
79708f90673Sjfb 	j = 0;
79808f90673Sjfb 	while (++j <= m) {
79908f90673Sjfb 		c[j] = -b[j].serial;
80008f90673Sjfb 		while (b[j + 1].value == b[j].value) {
80108f90673Sjfb 			j++;
80208f90673Sjfb 			c[j] = b[j].serial;
80308f90673Sjfb 		}
80408f90673Sjfb 	}
80508f90673Sjfb 	c[j] = -1;
80608f90673Sjfb }
80708f90673Sjfb 
80808f90673Sjfb /* Code taken from ping.c */
80908f90673Sjfb static int
81008f90673Sjfb isqrt(int n)
81108f90673Sjfb {
81208f90673Sjfb 	int y, x = 1;
81308f90673Sjfb 
81408f90673Sjfb 	if (n == 0)
81508f90673Sjfb 		return(0);
81608f90673Sjfb 
81708f90673Sjfb 	do { /* newton was a stinker */
81808f90673Sjfb 		y = x;
81908f90673Sjfb 		x = n / x;
82008f90673Sjfb 		x += y;
82108f90673Sjfb 		x /= 2;
82208f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
82308f90673Sjfb 
82408f90673Sjfb 	return (x);
82508f90673Sjfb }
82608f90673Sjfb 
82708f90673Sjfb static int
82808f90673Sjfb stone(int *a, int n, int *b, int *c)
82908f90673Sjfb {
83008f90673Sjfb 	int i, k, y, j, l;
83108f90673Sjfb 	int oldc, tc, oldl;
83208f90673Sjfb 	u_int numtries;
83308f90673Sjfb 
834dc6a6879Sjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, isqrt(n));
83508f90673Sjfb 
83608f90673Sjfb 	k = 0;
83708f90673Sjfb 	c[0] = newcand(0, 0, 0);
83808f90673Sjfb 	for (i = 1; i <= n; i++) {
83908f90673Sjfb 		j = a[i];
84008f90673Sjfb 		if (j == 0)
84108f90673Sjfb 			continue;
84208f90673Sjfb 		y = -b[j];
84308f90673Sjfb 		oldl = 0;
84408f90673Sjfb 		oldc = c[0];
84508f90673Sjfb 		numtries = 0;
84608f90673Sjfb 		do {
84708f90673Sjfb 			if (y <= clist[oldc].y)
84808f90673Sjfb 				continue;
84908f90673Sjfb 			l = search(c, k, y);
85008f90673Sjfb 			if (l != oldl + 1)
85108f90673Sjfb 				oldc = c[l - 1];
85208f90673Sjfb 			if (l <= k) {
85308f90673Sjfb 				if (clist[c[l]].y <= y)
85408f90673Sjfb 					continue;
85508f90673Sjfb 				tc = c[l];
85608f90673Sjfb 				c[l] = newcand(i, y, oldc);
85708f90673Sjfb 				oldc = tc;
85808f90673Sjfb 				oldl = l;
85908f90673Sjfb 				numtries++;
86008f90673Sjfb 			} else {
86108f90673Sjfb 				c[l] = newcand(i, y, oldc);
86208f90673Sjfb 				k++;
86308f90673Sjfb 				break;
86408f90673Sjfb 			}
86508f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
86608f90673Sjfb 	}
86708f90673Sjfb 	return (k);
86808f90673Sjfb }
86908f90673Sjfb 
87008f90673Sjfb static int
87108f90673Sjfb newcand(int x, int y, int pred)
87208f90673Sjfb {
87308f90673Sjfb 	struct cand *q;
87408f90673Sjfb 
87508f90673Sjfb 	if (clen == clistlen) {
87608f90673Sjfb 		clistlen = clistlen * 11 / 10;
87708f90673Sjfb 		clist = realloc(clist, clistlen * sizeof(cand));
87808f90673Sjfb 	}
87908f90673Sjfb 	q = clist + clen;
88008f90673Sjfb 	q->x = x;
88108f90673Sjfb 	q->y = y;
88208f90673Sjfb 	q->pred = pred;
88308f90673Sjfb 	return (clen++);
88408f90673Sjfb }
88508f90673Sjfb 
88608f90673Sjfb static int
88708f90673Sjfb search(int *c, int k, int y)
88808f90673Sjfb {
88908f90673Sjfb 	int i, j, l, t;
89008f90673Sjfb 
89108f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
89208f90673Sjfb 		return (k + 1);
89308f90673Sjfb 	i = 0;
89408f90673Sjfb 	j = k + 1;
89508f90673Sjfb 	while (1) {
89608f90673Sjfb 		l = i + j;
89708f90673Sjfb 		if ((l >>= 1) <= i)
89808f90673Sjfb 			break;
89908f90673Sjfb 		t = clist[c[l]].y;
90008f90673Sjfb 		if (t > y)
90108f90673Sjfb 			j = l;
90208f90673Sjfb 		else if (t < y)
90308f90673Sjfb 			i = l;
90408f90673Sjfb 		else
90508f90673Sjfb 			return (l);
90608f90673Sjfb 	}
90708f90673Sjfb 	return (l + 1);
90808f90673Sjfb }
90908f90673Sjfb 
91008f90673Sjfb static void
91108f90673Sjfb unravel(int p)
91208f90673Sjfb {
91308f90673Sjfb 	struct cand *q;
91408f90673Sjfb 	int i;
91508f90673Sjfb 
91608f90673Sjfb 	for (i = 0; i <= len[0]; i++)
91708f90673Sjfb 		J[i] = i <= pref ? i :
91808f90673Sjfb 		    i > len[0] - suff ? i + len[1] - len[0] : 0;
91908f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
92008f90673Sjfb 		J[q->x + pref] = q->y + pref;
92108f90673Sjfb }
92208f90673Sjfb 
92308f90673Sjfb /*
92408f90673Sjfb  * Check does double duty:
92508f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
92608f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
92708f90673Sjfb  *  2.  collect random access indexes to the two files
92808f90673Sjfb  */
92908f90673Sjfb static void
93008f90673Sjfb check(FILE *f1, FILE *f2)
93108f90673Sjfb {
93208f90673Sjfb 	int i, j, jackpot, c, d;
93308f90673Sjfb 	long ctold, ctnew;
93408f90673Sjfb 
93508f90673Sjfb 	rewind(f1);
93608f90673Sjfb 	rewind(f2);
93708f90673Sjfb 	j = 1;
93808f90673Sjfb 	ixold[0] = ixnew[0] = 0;
93908f90673Sjfb 	jackpot = 0;
94008f90673Sjfb 	ctold = ctnew = 0;
94108f90673Sjfb 	for (i = 1; i <= len[0]; i++) {
94208f90673Sjfb 		if (J[i] == 0) {
94308f90673Sjfb 			ixold[i] = ctold += skipline(f1);
94408f90673Sjfb 			continue;
94508f90673Sjfb 		}
94608f90673Sjfb 		while (j < J[i]) {
94708f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
94808f90673Sjfb 			j++;
94908f90673Sjfb 		}
95008f90673Sjfb 		if (bflag || wflag || iflag) {
95108f90673Sjfb 			for (;;) {
95208f90673Sjfb 				c = getc(f1);
95308f90673Sjfb 				d = getc(f2);
95408f90673Sjfb 				/*
95508f90673Sjfb 				 * GNU diff ignores a missing newline
95608f90673Sjfb 				 * in one file if bflag || wflag.
95708f90673Sjfb 				 */
95808f90673Sjfb 				if ((bflag || wflag) &&
95908f90673Sjfb 				    ((c == EOF && d == '\n') ||
96008f90673Sjfb 				    (c == '\n' && d == EOF))) {
96108f90673Sjfb 					break;
96208f90673Sjfb 				}
96308f90673Sjfb 				ctold++;
96408f90673Sjfb 				ctnew++;
96508f90673Sjfb 				if (bflag && isspace(c) && isspace(d)) {
96608f90673Sjfb 					do {
96708f90673Sjfb 						if (c == '\n')
96808f90673Sjfb 							break;
96908f90673Sjfb 						ctold++;
97008f90673Sjfb 					} while (isspace(c = getc(f1)));
97108f90673Sjfb 					do {
97208f90673Sjfb 						if (d == '\n')
97308f90673Sjfb 							break;
97408f90673Sjfb 						ctnew++;
97508f90673Sjfb 					} while (isspace(d = getc(f2)));
97608f90673Sjfb 				} else if (wflag) {
97708f90673Sjfb 					while (isspace(c) && c != '\n') {
97808f90673Sjfb 						c = getc(f1);
97908f90673Sjfb 						ctold++;
98008f90673Sjfb 					}
98108f90673Sjfb 					while (isspace(d) && d != '\n') {
98208f90673Sjfb 						d = getc(f2);
98308f90673Sjfb 						ctnew++;
98408f90673Sjfb 					}
98508f90673Sjfb 				}
98608f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
98708f90673Sjfb 					jackpot++;
98808f90673Sjfb 					J[i] = 0;
98908f90673Sjfb 					if (c != '\n' && c != EOF)
99008f90673Sjfb 						ctold += skipline(f1);
99108f90673Sjfb 					if (d != '\n' && c != EOF)
99208f90673Sjfb 						ctnew += skipline(f2);
99308f90673Sjfb 					break;
99408f90673Sjfb 				}
99508f90673Sjfb 				if (c == '\n' || c == EOF)
99608f90673Sjfb 					break;
99708f90673Sjfb 			}
99808f90673Sjfb 		} else {
99908f90673Sjfb 			for (;;) {
100008f90673Sjfb 				ctold++;
100108f90673Sjfb 				ctnew++;
100208f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
100308f90673Sjfb 					/* jackpot++; */
100408f90673Sjfb 					J[i] = 0;
100508f90673Sjfb 					if (c != '\n' && c != EOF)
100608f90673Sjfb 						ctold += skipline(f1);
100708f90673Sjfb 					if (d != '\n' && c != EOF)
100808f90673Sjfb 						ctnew += skipline(f2);
100908f90673Sjfb 					break;
101008f90673Sjfb 				}
101108f90673Sjfb 				if (c == '\n' || c == EOF)
101208f90673Sjfb 					break;
101308f90673Sjfb 			}
101408f90673Sjfb 		}
101508f90673Sjfb 		ixold[i] = ctold;
101608f90673Sjfb 		ixnew[j] = ctnew;
101708f90673Sjfb 		j++;
101808f90673Sjfb 	}
101908f90673Sjfb 	for (; j <= len[1]; j++)
102008f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
102108f90673Sjfb 	/*
102208f90673Sjfb 	 * if (jackpot)
102308f90673Sjfb 	 *	fprintf(stderr, "jackpot\n");
102408f90673Sjfb 	 */
102508f90673Sjfb }
102608f90673Sjfb 
102708f90673Sjfb /* shellsort CACM #201 */
102808f90673Sjfb static void
102908f90673Sjfb sort(struct line *a, int n)
103008f90673Sjfb {
103108f90673Sjfb 	struct line *ai, *aim, w;
103208f90673Sjfb 	int j, m = 0, k;
103308f90673Sjfb 
103408f90673Sjfb 	if (n == 0)
103508f90673Sjfb 		return;
103608f90673Sjfb 	for (j = 1; j <= n; j *= 2)
103708f90673Sjfb 		m = 2 * j - 1;
103808f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
103908f90673Sjfb 		k = n - m;
104008f90673Sjfb 		for (j = 1; j <= k; j++) {
104108f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
104208f90673Sjfb 				aim = &ai[m];
104308f90673Sjfb 				if (aim < ai)
104408f90673Sjfb 					break;	/* wraparound */
104508f90673Sjfb 				if (aim->value > ai[0].value ||
104608f90673Sjfb 				    (aim->value == ai[0].value &&
104708f90673Sjfb 					aim->serial > ai[0].serial))
104808f90673Sjfb 					break;
104908f90673Sjfb 				w.value = ai[0].value;
105008f90673Sjfb 				ai[0].value = aim->value;
105108f90673Sjfb 				aim->value = w.value;
105208f90673Sjfb 				w.serial = ai[0].serial;
105308f90673Sjfb 				ai[0].serial = aim->serial;
105408f90673Sjfb 				aim->serial = w.serial;
105508f90673Sjfb 			}
105608f90673Sjfb 		}
105708f90673Sjfb 	}
105808f90673Sjfb }
105908f90673Sjfb 
106008f90673Sjfb static void
106108f90673Sjfb unsort(struct line *f, int l, int *b)
106208f90673Sjfb {
106308f90673Sjfb 	int *a, i;
106408f90673Sjfb 
106508f90673Sjfb 	a = malloc((l + 1) * sizeof(int));
106608f90673Sjfb 	for (i = 1; i <= l; i++)
106708f90673Sjfb 		a[f[i].serial] = f[i].value;
106808f90673Sjfb 	for (i = 1; i <= l; i++)
106908f90673Sjfb 		b[i] = a[i];
107008f90673Sjfb 	free(a);
107108f90673Sjfb }
107208f90673Sjfb 
107308f90673Sjfb static int
107408f90673Sjfb skipline(FILE *f)
107508f90673Sjfb {
107608f90673Sjfb 	int i, c;
107708f90673Sjfb 
107808f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
107908f90673Sjfb 		continue;
108008f90673Sjfb 	return (i);
108108f90673Sjfb }
108208f90673Sjfb 
108308f90673Sjfb static void
108408f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2)
108508f90673Sjfb {
108608f90673Sjfb 	int m, i0, i1, j0, j1;
108708f90673Sjfb 
108808f90673Sjfb 	rewind(f1);
108908f90673Sjfb 	rewind(f2);
109008f90673Sjfb 	m = len[0];
109108f90673Sjfb 	J[0] = 0;
109208f90673Sjfb 	J[m + 1] = len[1] + 1;
109308f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
109408f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
109508f90673Sjfb 			i0++;
109608f90673Sjfb 		j0 = J[i0 - 1] + 1;
109708f90673Sjfb 		i1 = i0 - 1;
109808f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
109908f90673Sjfb 			i1++;
110008f90673Sjfb 		j1 = J[i1 + 1] - 1;
110108f90673Sjfb 		J[i1] = j1;
110208f90673Sjfb 		change(file1, f1, file2, f2, i0, i1, j0, j1);
110308f90673Sjfb 	}
110408f90673Sjfb 	if (m == 0)
110508f90673Sjfb 		change(file1, f1, file2, f2, 1, 0, 1, len[1]);
110608f90673Sjfb 	if (format == D_IFDEF) {
110708f90673Sjfb 		for (;;) {
110808f90673Sjfb #define	c i0
110908f90673Sjfb 			if ((c = getc(f1)) == EOF)
111008f90673Sjfb 				return;
111108f90673Sjfb 			putchar(c);
111208f90673Sjfb 		}
111308f90673Sjfb #undef c
111408f90673Sjfb 	}
111508f90673Sjfb 	if (anychange != 0) {
111608f90673Sjfb 		if (format == D_CONTEXT)
111708f90673Sjfb 			dump_context_vec(f1, f2);
111808f90673Sjfb 		else if (format == D_UNIFIED)
111908f90673Sjfb 			dump_unified_vec(f1, f2);
112008f90673Sjfb 	}
112108f90673Sjfb }
112208f90673Sjfb 
112308f90673Sjfb static __inline void
112408f90673Sjfb range(int a, int b, char *separator)
112508f90673Sjfb {
112608f90673Sjfb 	printf("%d", a > b ? b : a);
112708f90673Sjfb 	if (a < b)
112808f90673Sjfb 		printf("%s%d", separator, b);
112908f90673Sjfb }
113008f90673Sjfb 
113108f90673Sjfb static __inline void
113208f90673Sjfb uni_range(int a, int b)
113308f90673Sjfb {
113408f90673Sjfb 	if (a < b)
113508f90673Sjfb 		printf("%d,%d", a, b - a + 1);
113608f90673Sjfb 	else if (a == b)
113708f90673Sjfb 		printf("%d", b);
113808f90673Sjfb 	else
113908f90673Sjfb 		printf("%d,0", b);
114008f90673Sjfb }
114108f90673Sjfb 
114208f90673Sjfb static char *
114308f90673Sjfb preadline(int fd, size_t len, off_t off)
114408f90673Sjfb {
114508f90673Sjfb 	char *line;
114608f90673Sjfb 	ssize_t nr;
114708f90673Sjfb 
114808f90673Sjfb 	line = malloc(len + 1);
114908f90673Sjfb 	if (line == NULL) {
115008f90673Sjfb 		cvs_log(LP_ERRNO, "failed to allocate line");
115108f90673Sjfb 		return (NULL);
115208f90673Sjfb 	}
115308f90673Sjfb 	if ((nr = pread(fd, line, len, off)) < 0) {
115408f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
115508f90673Sjfb 		return (NULL);
115608f90673Sjfb 	}
115708f90673Sjfb 	line[nr] = '\0';
115808f90673Sjfb 	return (line);
115908f90673Sjfb }
116008f90673Sjfb 
116108f90673Sjfb static int
116208f90673Sjfb ignoreline(char *line)
116308f90673Sjfb {
116408f90673Sjfb 	int ret;
116508f90673Sjfb 
116608f90673Sjfb 	ret = regexec(&ignore_re, line, 0, NULL, 0);
116708f90673Sjfb 	free(line);
116808f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
116908f90673Sjfb }
117008f90673Sjfb 
117108f90673Sjfb /*
117208f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
117308f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
117408f90673Sjfb  * are no lines in the from file involved and this means that there were
117508f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
117608f90673Sjfb  * lines missing from the to file.
117708f90673Sjfb  */
117808f90673Sjfb static void
117908f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2,
118008f90673Sjfb 	int a, int b, int c, int d)
118108f90673Sjfb {
118208f90673Sjfb 	static size_t max_context = 64;
118308f90673Sjfb 	int i;
118408f90673Sjfb 
118508f90673Sjfb 	if (format != D_IFDEF && a > b && c > d)
118608f90673Sjfb 		return;
118708f90673Sjfb 	if (ignore_pats != NULL) {
118808f90673Sjfb 		char *line;
118908f90673Sjfb 		/*
119008f90673Sjfb 		 * All lines in the change, insert, or delete must
119108f90673Sjfb 		 * match an ignore pattern for the change to be
119208f90673Sjfb 		 * ignored.
119308f90673Sjfb 		 */
119408f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
119508f90673Sjfb 			for (i = a; i <= b; i++) {
119608f90673Sjfb 				line = preadline(fileno(f1),
119708f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
119808f90673Sjfb 				if (!ignoreline(line))
119908f90673Sjfb 					goto proceed;
120008f90673Sjfb 			}
120108f90673Sjfb 		}
120208f90673Sjfb 		if (a > b || c <= d) {	/* Changes and inserts. */
120308f90673Sjfb 			for (i = c; i <= d; i++) {
120408f90673Sjfb 				line = preadline(fileno(f2),
120508f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
120608f90673Sjfb 				if (!ignoreline(line))
120708f90673Sjfb 					goto proceed;
120808f90673Sjfb 			}
120908f90673Sjfb 		}
121008f90673Sjfb 		return;
121108f90673Sjfb 	}
121208f90673Sjfb proceed:
121308f90673Sjfb 	if (format == D_CONTEXT || format == D_UNIFIED) {
121408f90673Sjfb 		/*
121508f90673Sjfb 		 * Allocate change records as needed.
121608f90673Sjfb 		 */
121708f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
121808f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
121908f90673Sjfb 			max_context <<= 1;
122008f90673Sjfb 			context_vec_start = realloc(context_vec_start,
122108f90673Sjfb 			    max_context * sizeof(struct context_vec));
122208f90673Sjfb 			context_vec_end = context_vec_start + max_context;
122308f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
122408f90673Sjfb 		}
122508f90673Sjfb 		if (anychange == 0) {
122608f90673Sjfb 			/*
122708f90673Sjfb 			 * Print the context/unidiff header first time through.
122808f90673Sjfb 			 */
122908f90673Sjfb 			printf("%s %s	%s",
123008f90673Sjfb 			    format == D_CONTEXT ? "***" : "---", diff_file,
123108f90673Sjfb 			    ctime(&stb1.st_mtime));
123208f90673Sjfb 			printf("%s %s	%s",
123308f90673Sjfb 			    format == D_CONTEXT ? "---" : "+++", diff_file,
123408f90673Sjfb 			    ctime(&stb2.st_mtime));
123508f90673Sjfb 			anychange = 1;
123608f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
123708f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
123808f90673Sjfb 			/*
123908f90673Sjfb 			 * If this change is more than 'context' lines from the
124008f90673Sjfb 			 * previous change, dump the record and reset it.
124108f90673Sjfb 			 */
124208f90673Sjfb 			if (format == D_CONTEXT)
124308f90673Sjfb 				dump_context_vec(f1, f2);
124408f90673Sjfb 			else
124508f90673Sjfb 				dump_unified_vec(f1, f2);
124608f90673Sjfb 		}
124708f90673Sjfb 		context_vec_ptr++;
124808f90673Sjfb 		context_vec_ptr->a = a;
124908f90673Sjfb 		context_vec_ptr->b = b;
125008f90673Sjfb 		context_vec_ptr->c = c;
125108f90673Sjfb 		context_vec_ptr->d = d;
125208f90673Sjfb 		return;
125308f90673Sjfb 	}
125408f90673Sjfb 	if (anychange == 0)
125508f90673Sjfb 		anychange = 1;
125608f90673Sjfb 	switch (format) {
125708f90673Sjfb 	case D_BRIEF:
125808f90673Sjfb 		return;
125908f90673Sjfb 	case D_NORMAL:
126008f90673Sjfb 		range(a, b, ",");
126108f90673Sjfb 		putchar(a > b ? 'a' : c > d ? 'd' : 'c');
126208f90673Sjfb 		if (format == D_NORMAL)
126308f90673Sjfb 			range(c, d, ",");
126408f90673Sjfb 		putchar('\n');
126508f90673Sjfb 		break;
126608f90673Sjfb 	}
126708f90673Sjfb 	if (format == D_NORMAL || format == D_IFDEF) {
126808f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
126908f90673Sjfb 		if (a <= b && c <= d && format == D_NORMAL)
127008f90673Sjfb 			puts("---");
127108f90673Sjfb 	}
127208f90673Sjfb 	i = fetch(ixnew, c, d, f2, format == D_NORMAL ? '>' : '\0', 0);
127308f90673Sjfb 	if (inifdef) {
127408f90673Sjfb 		printf("#endif /* %s */\n", ifdefname);
127508f90673Sjfb 		inifdef = 0;
127608f90673Sjfb 	}
127708f90673Sjfb }
127808f90673Sjfb 
127908f90673Sjfb static int
128008f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
128108f90673Sjfb {
128208f90673Sjfb 	int i, j, c, lastc, col, nc;
128308f90673Sjfb 
128408f90673Sjfb 	/*
128508f90673Sjfb 	 * When doing #ifdef's, copy down to current line
128608f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
128708f90673Sjfb 	 */
128808f90673Sjfb 	if (format == D_IFDEF && oldfile) {
128908f90673Sjfb 		long curpos = ftell(lb);
129008f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
129108f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
129208f90673Sjfb 		for (i = 0; i < nc; i++)
129308f90673Sjfb 			putchar(getc(lb));
129408f90673Sjfb 	}
129508f90673Sjfb 	if (a > b)
129608f90673Sjfb 		return (0);
129708f90673Sjfb 	if (format == D_IFDEF) {
129808f90673Sjfb 		if (inifdef) {
129908f90673Sjfb 			printf("#else /* %s%s */\n",
130008f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
130108f90673Sjfb 		} else {
130208f90673Sjfb 			if (oldfile)
130308f90673Sjfb 				printf("#ifndef %s\n", ifdefname);
130408f90673Sjfb 			else
130508f90673Sjfb 				printf("#ifdef %s\n", ifdefname);
130608f90673Sjfb 		}
130708f90673Sjfb 		inifdef = 1 + oldfile;
130808f90673Sjfb 	}
130908f90673Sjfb 	for (i = a; i <= b; i++) {
131008f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
131108f90673Sjfb 		nc = f[i] - f[i - 1];
131208f90673Sjfb 		if (format != D_IFDEF && ch != '\0') {
131308f90673Sjfb 			putchar(ch);
131408f90673Sjfb 			if (Tflag && (format == D_NORMAL || format == D_CONTEXT
131508f90673Sjfb 			    || format == D_UNIFIED))
131608f90673Sjfb 				putchar('\t');
131708f90673Sjfb 			else if (format != D_UNIFIED)
131808f90673Sjfb 				putchar(' ');
131908f90673Sjfb 		}
132008f90673Sjfb 		col = 0;
132108f90673Sjfb 		for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
132208f90673Sjfb 			if ((c = getc(lb)) == EOF) {
132308f90673Sjfb 				puts("\n\\ No newline at end of file");
132408f90673Sjfb 				return (0);
132508f90673Sjfb 			}
132608f90673Sjfb 			if (c == '\t' && tflag) {
132708f90673Sjfb 				do {
132808f90673Sjfb 					putchar(' ');
132908f90673Sjfb 				} while (++col & 7);
133008f90673Sjfb 			} else {
133108f90673Sjfb 				putchar(c);
133208f90673Sjfb 				col++;
133308f90673Sjfb 			}
133408f90673Sjfb 		}
133508f90673Sjfb 	}
133608f90673Sjfb 	return (0);
133708f90673Sjfb }
133808f90673Sjfb 
133908f90673Sjfb /*
134008f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
134108f90673Sjfb  */
134208f90673Sjfb static int
134308f90673Sjfb readhash(FILE *f)
134408f90673Sjfb {
134508f90673Sjfb 	int i, t, space;
134608f90673Sjfb 	int sum;
134708f90673Sjfb 
134808f90673Sjfb 	sum = 1;
134908f90673Sjfb 	space = 0;
135008f90673Sjfb 	if (!bflag && !wflag) {
135108f90673Sjfb 		if (iflag)
135208f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
135308f90673Sjfb 				if (t == EOF) {
135408f90673Sjfb 					if (i == 0)
135508f90673Sjfb 						return (0);
135608f90673Sjfb 					break;
135708f90673Sjfb 				}
135808f90673Sjfb 				sum = sum * 127 + chrtran[t];
135908f90673Sjfb 			}
136008f90673Sjfb 		else
136108f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
136208f90673Sjfb 				if (t == EOF) {
136308f90673Sjfb 					if (i == 0)
136408f90673Sjfb 						return (0);
136508f90673Sjfb 					break;
136608f90673Sjfb 				}
136708f90673Sjfb 				sum = sum * 127 + t;
136808f90673Sjfb 			}
136908f90673Sjfb 	} else {
137008f90673Sjfb 		for (i = 0;;) {
137108f90673Sjfb 			switch (t = getc(f)) {
137208f90673Sjfb 			case '\t':
137308f90673Sjfb 			case ' ':
137408f90673Sjfb 				space++;
137508f90673Sjfb 				continue;
137608f90673Sjfb 			default:
137708f90673Sjfb 				if (space && !wflag) {
137808f90673Sjfb 					i++;
137908f90673Sjfb 					space = 0;
138008f90673Sjfb 				}
138108f90673Sjfb 				sum = sum * 127 + chrtran[t];
138208f90673Sjfb 				i++;
138308f90673Sjfb 				continue;
138408f90673Sjfb 			case EOF:
138508f90673Sjfb 				if (i == 0)
138608f90673Sjfb 					return (0);
138708f90673Sjfb 				/* FALLTHROUGH */
138808f90673Sjfb 			case '\n':
138908f90673Sjfb 				break;
139008f90673Sjfb 			}
139108f90673Sjfb 			break;
139208f90673Sjfb 		}
139308f90673Sjfb 	}
139408f90673Sjfb 	/*
139508f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
139608f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
139708f90673Sjfb 	 */
139808f90673Sjfb 	return (sum == 0 ? 1 : sum);
139908f90673Sjfb }
140008f90673Sjfb 
140108f90673Sjfb static int
140208f90673Sjfb asciifile(FILE *f)
140308f90673Sjfb {
140408f90673Sjfb 	char buf[BUFSIZ];
140508f90673Sjfb 	int i, cnt;
140608f90673Sjfb 
140708f90673Sjfb 	if (aflag || f == NULL)
140808f90673Sjfb 		return (1);
140908f90673Sjfb 
141008f90673Sjfb 	rewind(f);
141108f90673Sjfb 	cnt = fread(buf, 1, sizeof(buf), f);
141208f90673Sjfb 	for (i = 0; i < cnt; i++)
141308f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
141408f90673Sjfb 			return (0);
141508f90673Sjfb 	return (1);
141608f90673Sjfb }
141708f90673Sjfb 
141808f90673Sjfb 
141908f90673Sjfb /* dump accumulated "context" diff changes */
142008f90673Sjfb static void
142108f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
142208f90673Sjfb {
142308f90673Sjfb 	struct context_vec *cvp = context_vec_start;
142408f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
142508f90673Sjfb 	int a, b, c, d;
1426dc6a6879Sjfb 	char ch;
142708f90673Sjfb 
142808f90673Sjfb 	if (context_vec_start > context_vec_ptr)
142908f90673Sjfb 		return;
143008f90673Sjfb 
143108f90673Sjfb 	b = d = 0;		/* gcc */
1432dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1433dc6a6879Sjfb 	upb = MIN(len[0], context_vec_ptr->b + context);
1434dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1435dc6a6879Sjfb 	upd = MIN(len[1], context_vec_ptr->d + context);
143608f90673Sjfb 
143708f90673Sjfb 	printf("***************");
143808f90673Sjfb 	printf("\n*** ");
143908f90673Sjfb 	range(lowa, upb, ",");
144008f90673Sjfb 	printf(" ****\n");
144108f90673Sjfb 
144208f90673Sjfb 	/*
144308f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
144408f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
144508f90673Sjfb 	 * the "old" lines as context in the "new" list).
144608f90673Sjfb 	 */
144708f90673Sjfb 	do_output = 0;
144808f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
144908f90673Sjfb 		if (cvp->a <= cvp->b) {
145008f90673Sjfb 			cvp = context_vec_start;
145108f90673Sjfb 			do_output++;
145208f90673Sjfb 			break;
145308f90673Sjfb 		}
145408f90673Sjfb 	if (do_output) {
145508f90673Sjfb 		while (cvp <= context_vec_ptr) {
145608f90673Sjfb 			a = cvp->a;
145708f90673Sjfb 			b = cvp->b;
145808f90673Sjfb 			c = cvp->c;
145908f90673Sjfb 			d = cvp->d;
146008f90673Sjfb 
146108f90673Sjfb 			if (a <= b && c <= d)
146208f90673Sjfb 				ch = 'c';
146308f90673Sjfb 			else
146408f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
146508f90673Sjfb 
146608f90673Sjfb 			if (ch == 'a')
146708f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
146808f90673Sjfb 			else {
146908f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
147008f90673Sjfb 				fetch(ixold, a, b, f1,
147108f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
147208f90673Sjfb 			}
147308f90673Sjfb 			lowa = b + 1;
147408f90673Sjfb 			cvp++;
147508f90673Sjfb 		}
147608f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
147708f90673Sjfb 	}
147808f90673Sjfb 	/* output changes to the "new" file */
147908f90673Sjfb 	printf("--- ");
148008f90673Sjfb 	range(lowc, upd, ",");
148108f90673Sjfb 	printf(" ----\n");
148208f90673Sjfb 
148308f90673Sjfb 	do_output = 0;
148408f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
148508f90673Sjfb 		if (cvp->c <= cvp->d) {
148608f90673Sjfb 			cvp = context_vec_start;
148708f90673Sjfb 			do_output++;
148808f90673Sjfb 			break;
148908f90673Sjfb 		}
149008f90673Sjfb 	if (do_output) {
149108f90673Sjfb 		while (cvp <= context_vec_ptr) {
149208f90673Sjfb 			a = cvp->a;
149308f90673Sjfb 			b = cvp->b;
149408f90673Sjfb 			c = cvp->c;
149508f90673Sjfb 			d = cvp->d;
149608f90673Sjfb 
149708f90673Sjfb 			if (a <= b && c <= d)
149808f90673Sjfb 				ch = 'c';
149908f90673Sjfb 			else
150008f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
150108f90673Sjfb 
150208f90673Sjfb 			if (ch == 'd')
150308f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
150408f90673Sjfb 			else {
150508f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
150608f90673Sjfb 				fetch(ixnew, c, d, f2,
150708f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
150808f90673Sjfb 			}
150908f90673Sjfb 			lowc = d + 1;
151008f90673Sjfb 			cvp++;
151108f90673Sjfb 		}
151208f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
151308f90673Sjfb 	}
151408f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
151508f90673Sjfb }
151608f90673Sjfb 
151708f90673Sjfb /* dump accumulated "unified" diff changes */
151808f90673Sjfb static void
151908f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
152008f90673Sjfb {
152108f90673Sjfb 	struct context_vec *cvp = context_vec_start;
152208f90673Sjfb 	int lowa, upb, lowc, upd;
152308f90673Sjfb 	int a, b, c, d;
1524dc6a6879Sjfb 	char ch;
152508f90673Sjfb 
152608f90673Sjfb 	if (context_vec_start > context_vec_ptr)
152708f90673Sjfb 		return;
152808f90673Sjfb 
152908f90673Sjfb 	b = d = 0;		/* gcc */
1530dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1531dc6a6879Sjfb 	upb = MIN(len[0], context_vec_ptr->b + context);
1532dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1533dc6a6879Sjfb 	upd = MIN(len[1], context_vec_ptr->d + context);
153408f90673Sjfb 
153508f90673Sjfb 	fputs("@@ -", stdout);
153608f90673Sjfb 	uni_range(lowa, upb);
153708f90673Sjfb 	fputs(" +", stdout);
153808f90673Sjfb 	uni_range(lowc, upd);
153908f90673Sjfb 	fputs(" @@", stdout);
154008f90673Sjfb 	putchar('\n');
154108f90673Sjfb 
154208f90673Sjfb 	/*
154308f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
154408f90673Sjfb 	 * are printed together.
154508f90673Sjfb 	 */
154608f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
154708f90673Sjfb 		a = cvp->a;
154808f90673Sjfb 		b = cvp->b;
154908f90673Sjfb 		c = cvp->c;
155008f90673Sjfb 		d = cvp->d;
155108f90673Sjfb 
155208f90673Sjfb 		/*
155308f90673Sjfb 		 * c: both new and old changes
155408f90673Sjfb 		 * d: only changes in the old file
155508f90673Sjfb 		 * a: only changes in the new file
155608f90673Sjfb 		 */
155708f90673Sjfb 		if (a <= b && c <= d)
155808f90673Sjfb 			ch = 'c';
155908f90673Sjfb 		else
156008f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
156108f90673Sjfb 
156208f90673Sjfb 		switch (ch) {
156308f90673Sjfb 		case 'c':
156408f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
156508f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
156608f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
156708f90673Sjfb 			break;
156808f90673Sjfb 		case 'd':
156908f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
157008f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
157108f90673Sjfb 			break;
157208f90673Sjfb 		case 'a':
157308f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
157408f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
157508f90673Sjfb 			break;
157608f90673Sjfb 		}
157708f90673Sjfb 		lowa = b + 1;
157808f90673Sjfb 		lowc = d + 1;
157908f90673Sjfb 	}
158008f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
158108f90673Sjfb 
158208f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
158308f90673Sjfb }
1584