xref: /openbsd-src/usr.bin/cvs/diff.c (revision bb02993716ad42bd207d749bbca5daefffd99503)
1*bb029937Sjfb /*	$OpenBSD: diff.c,v 1.31 2005/04/18 21:02:49 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 
14508f90673Sjfb #include "cvs.h"
14608f90673Sjfb #include "log.h"
14708f90673Sjfb #include "buf.h"
148dc6a6879Sjfb #include "proto.h"
14908f90673Sjfb 
15008f90673Sjfb 
15108f90673Sjfb #define CVS_DIFF_DEFCTX    3   /* default context length */
15208f90673Sjfb 
15308f90673Sjfb 
15408f90673Sjfb /*
15508f90673Sjfb  * Output format options
15608f90673Sjfb  */
15708f90673Sjfb #define	D_NORMAL	0	/* Normal output */
15808f90673Sjfb #define	D_CONTEXT	1	/* Diff with context */
15908f90673Sjfb #define	D_UNIFIED	2	/* Unified context diff */
16008f90673Sjfb #define	D_IFDEF		3	/* Diff with merged #ifdef's */
16108f90673Sjfb #define	D_BRIEF		4	/* Say if the files differ */
16208f90673Sjfb 
16308f90673Sjfb /*
16408f90673Sjfb  * Status values for print_status() and diffreg() return values
16508f90673Sjfb  */
16608f90673Sjfb #define	D_SAME		0	/* Files are the same */
16708f90673Sjfb #define	D_DIFFER	1	/* Files are different */
16808f90673Sjfb #define	D_BINARY	2	/* Binary files are different */
16908f90673Sjfb #define	D_COMMON	3	/* Subdirectory common to both dirs */
17008f90673Sjfb #define	D_ONLY		4	/* Only exists in one directory */
17108f90673Sjfb #define	D_MISMATCH1	5	/* path1 was a dir, path2 a file */
17208f90673Sjfb #define	D_MISMATCH2	6	/* path1 was a file, path2 a dir */
17308f90673Sjfb #define	D_ERROR		7	/* An error occurred */
17408f90673Sjfb #define	D_SKIPPED1	8	/* path1 was a special file */
17508f90673Sjfb #define	D_SKIPPED2	9	/* path2 was a special file */
17608f90673Sjfb 
17708f90673Sjfb struct cand {
17808f90673Sjfb 	int x;
17908f90673Sjfb 	int y;
18008f90673Sjfb 	int pred;
18108f90673Sjfb } cand;
18208f90673Sjfb 
18308f90673Sjfb struct line {
18408f90673Sjfb 	int serial;
18508f90673Sjfb 	int value;
18608f90673Sjfb } *file[2];
18708f90673Sjfb 
18808f90673Sjfb /*
18908f90673Sjfb  * The following struct is used to record change information when
19008f90673Sjfb  * doing a "context" or "unified" diff.  (see routine "change" to
19108f90673Sjfb  * understand the highly mnemonic field names)
19208f90673Sjfb  */
19308f90673Sjfb struct context_vec {
19408f90673Sjfb 	int a;			/* start line in old file */
19508f90673Sjfb 	int b;			/* end line in old file */
19608f90673Sjfb 	int c;			/* start line in new file */
19708f90673Sjfb 	int d;			/* end line in new file */
19808f90673Sjfb };
19908f90673Sjfb 
200dc6a6879Sjfb struct diff_arg {
201dc6a6879Sjfb 	char  *rev1;
202dc6a6879Sjfb 	char  *rev2;
203dc6a6879Sjfb 	char  *date1;
204dc6a6879Sjfb 	char  *date2;
205dc6a6879Sjfb };
206dc6a6879Sjfb 
20708f90673Sjfb 
20808f90673Sjfb struct excludes {
20908f90673Sjfb 	char *pattern;
21008f90673Sjfb 	struct excludes *next;
21108f90673Sjfb };
21208f90673Sjfb 
21308f90673Sjfb 
21408f90673Sjfb char	*splice(char *, char *);
21516cfc147Sjoris int  cvs_diff_options(char *, int, char **, int *);
21608f90673Sjfb int  cvs_diffreg(const char *, const char *);
217dc6a6879Sjfb int  cvs_diff_file(struct cvs_file *, void *);
21816cfc147Sjoris int  cvs_diff_sendflags(struct cvsroot *);
21916cfc147Sjoris int  cvs_diff_cleanup(void);
22016cfc147Sjoris 
22108f90673Sjfb static void output(const char *, FILE *, const char *, FILE *);
22208f90673Sjfb static void check(FILE *, FILE *);
22308f90673Sjfb static void range(int, int, char *);
22408f90673Sjfb static void uni_range(int, int);
22508f90673Sjfb static void dump_context_vec(FILE *, FILE *);
22608f90673Sjfb static void dump_unified_vec(FILE *, FILE *);
2277f535ec4Sjfb static int  prepare(int, FILE *, off_t);
22808f90673Sjfb static void prune(void);
22908f90673Sjfb static void equiv(struct line *, int, struct line *, int, int *);
23008f90673Sjfb static void unravel(int);
23108f90673Sjfb static void unsort(struct line *, int, int *);
23208f90673Sjfb static void change(const char *, FILE *, const char *, FILE *, int, int, int, int);
23308f90673Sjfb static void sort(struct line *, int);
23408f90673Sjfb static int  ignoreline(char *);
23508f90673Sjfb static int  asciifile(FILE *);
23608f90673Sjfb static int  fetch(long *, int, int, FILE *, int, int);
23708f90673Sjfb static int  newcand(int, int, int);
23808f90673Sjfb static int  search(int *, int, int);
23908f90673Sjfb static int  skipline(FILE *);
24008f90673Sjfb static int  isqrt(int);
24108f90673Sjfb static int  stone(int *, int, int *, int *);
24208f90673Sjfb static int  readhash(FILE *);
24308f90673Sjfb static int  files_differ(FILE *, FILE *);
2445e78344dSjfb static char *match_function(const long *, int, FILE *);
24508f90673Sjfb static char *preadline(int, size_t, off_t);
24608f90673Sjfb 
24708f90673Sjfb 
2485e78344dSjfb static int aflag, bflag, dflag, iflag, Nflag, pflag, tflag, Tflag, wflag;
24908f90673Sjfb static int context, status;
25008f90673Sjfb static int format = D_NORMAL;
25108f90673Sjfb static struct stat stb1, stb2;
252f5638424Sjfb static char *ifdefname, *ignore_pats, diffargs[128];
253f5638424Sjfb static const char *diff_file;
25408f90673Sjfb regex_t ignore_re;
25508f90673Sjfb 
25608f90673Sjfb static int  *J;			/* will be overlaid on class */
25708f90673Sjfb static int  *class;		/* will be overlaid on file[0] */
25808f90673Sjfb static int  *klist;		/* will be overlaid on file[0] after class */
25908f90673Sjfb static int  *member;		/* will be overlaid on file[1] */
26008f90673Sjfb static int   clen;
26108f90673Sjfb static int   inifdef;		/* whether or not we are in a #ifdef block */
26208f90673Sjfb static int   len[2];
26308f90673Sjfb static int   pref, suff;	/* length of prefix and suffix */
26408f90673Sjfb static int   slen[2];
26508f90673Sjfb static int   anychange;
26608f90673Sjfb static long *ixnew;		/* will be overlaid on file[1] */
26708f90673Sjfb static long *ixold;		/* will be overlaid on klist */
26808f90673Sjfb static struct cand *clist;	/* merely a free storage pot for candidates */
26908f90673Sjfb static int   clistlen;		/* the length of clist */
27008f90673Sjfb static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
27108f90673Sjfb static u_char *chrtran;		/* translation table for case-folding */
27208f90673Sjfb static struct context_vec *context_vec_start;
27308f90673Sjfb static struct context_vec *context_vec_end;
27408f90673Sjfb static struct context_vec *context_vec_ptr;
27508f90673Sjfb 
27608f90673Sjfb #define FUNCTION_CONTEXT_SIZE	41
2775e78344dSjfb static char lastbuf[FUNCTION_CONTEXT_SIZE];
27808f90673Sjfb static int  lastline;
27908f90673Sjfb static int  lastmatchline;
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 
34016cfc147Sjoris struct cvs_cmd_info cvs_diff = {
34116cfc147Sjoris 	cvs_diff_options,
34216cfc147Sjoris 	cvs_diff_sendflags,
34316cfc147Sjoris 	cvs_diff_file,
34416cfc147Sjoris 	cvs_diff_cleanup,
34516cfc147Sjoris 	NULL,
34616cfc147Sjoris 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
34716cfc147Sjoris 	CVS_REQ_DIFF,
34844381dcbSjoris 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
34916cfc147Sjoris };
35008f90673Sjfb 
35116cfc147Sjoris static struct diff_arg *dap = NULL;
35216cfc147Sjoris static int recurse;
35316cfc147Sjoris 
35408f90673Sjfb int
35516cfc147Sjoris cvs_diff_options(char *opt, int argc, char **argv, int *arg)
35608f90673Sjfb {
35716cfc147Sjoris 	int ch;
35808f90673Sjfb 
35916cfc147Sjoris 	dap = (struct diff_arg *)malloc(sizeof(*dap));
36016cfc147Sjoris 	if (dap == NULL)
36131274bbfSjoris 		return (CVS_EX_DATA);
36216cfc147Sjoris 	dap->date1 = dap->date2 = dap->rev1 = dap->rev2 = NULL;
363dc6a6879Sjfb 	strlcpy(diffargs, argv[0], sizeof(diffargs));
364dc6a6879Sjfb 
36516cfc147Sjoris 	while ((ch = getopt(argc, argv, opt)) != -1) {
36608f90673Sjfb 		switch (ch) {
36708f90673Sjfb 		case 'c':
368f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
36908f90673Sjfb 			format = D_CONTEXT;
37008f90673Sjfb 			break;
37108f90673Sjfb 		case 'D':
37216cfc147Sjoris 			if (dap->date1 == NULL && dap->rev1 == NULL) {
37316cfc147Sjoris 				dap->date1 = optarg;
37416cfc147Sjoris 			} else if (dap->date2 == NULL && dap->rev2 == NULL) {
37516cfc147Sjoris 				dap->date2 = optarg;
37616cfc147Sjoris 			} else {
37708f90673Sjfb 				cvs_log(LP_ERR,
37808f90673Sjfb 				    "no more than two revisions/dates can "
37908f90673Sjfb 				    "be specified");
38008f90673Sjfb 			}
38108f90673Sjfb 			break;
38208f90673Sjfb 		case 'l':
383f5638424Sjfb 			strlcat(diffargs, " -l", sizeof(diffargs));
38408f90673Sjfb 			recurse = 0;
38516cfc147Sjoris 			cvs_diff.file_flags &= ~CF_RECURSE;
38608f90673Sjfb 			break;
38708f90673Sjfb 		case 'i':
388f5638424Sjfb 			strlcat(diffargs, " -i", sizeof(diffargs));
38908f90673Sjfb 			iflag = 1;
39008f90673Sjfb 			break;
391c710bc5aSjfb 		case 'N':
392c710bc5aSjfb 			strlcat(diffargs, " -N", sizeof(diffargs));
393c710bc5aSjfb 			Nflag = 1;
394c710bc5aSjfb 			break;
3955e78344dSjfb 		case 'p':
3965e78344dSjfb 			strlcat(diffargs, " -p", sizeof(diffargs));
3975e78344dSjfb 			pflag = 1;
3985e78344dSjfb 			break;
39908f90673Sjfb 		case 'r':
40016cfc147Sjoris 			if ((dap->rev1 == NULL) && (dap->date1 == NULL)) {
40116cfc147Sjoris 				dap->rev1 = optarg;
40216cfc147Sjoris 			} else if ((dap->rev2 == NULL) &&
40316cfc147Sjoris 			    (dap->date2 == NULL)) {
40416cfc147Sjoris 				dap->rev2 = optarg;
40516cfc147Sjoris 			} else {
40608f90673Sjfb 				cvs_log(LP_ERR,
40708f90673Sjfb 				    "no more than two revisions/dates can "
40808f90673Sjfb 				    "be specified");
40931274bbfSjoris 				return (CVS_EX_USAGE);
41008f90673Sjfb 			}
41108f90673Sjfb 			break;
41208f90673Sjfb 		case 'u':
413f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
41408f90673Sjfb 			format = D_UNIFIED;
41508f90673Sjfb 			break;
41608f90673Sjfb 		default:
41731274bbfSjoris 			return (CVS_EX_USAGE);
41808f90673Sjfb 		}
41908f90673Sjfb 	}
42008f90673Sjfb 
42116cfc147Sjoris 	*arg = optind;
422dc6a6879Sjfb 	return (0);
423dc6a6879Sjfb }
424dc6a6879Sjfb 
42516cfc147Sjoris int
42616cfc147Sjoris cvs_diff_cleanup(void)
42716cfc147Sjoris {
42816cfc147Sjoris 	if (dap != NULL)
42916cfc147Sjoris 		free(dap);
43016cfc147Sjoris 	return (0);
43116cfc147Sjoris }
432dc6a6879Sjfb 
433dc6a6879Sjfb /*
434dc6a6879Sjfb  * cvs_diff_sendflags()
435dc6a6879Sjfb  *
436dc6a6879Sjfb  */
437dc6a6879Sjfb int
43816cfc147Sjoris cvs_diff_sendflags(struct cvsroot *root)
439dc6a6879Sjfb {
44008f90673Sjfb 	/* send the flags */
4415e78344dSjfb 	if (Nflag && (cvs_sendarg(root, "-N", 0) < 0))
44231274bbfSjoris 		return (CVS_EX_PROTO);
4435e78344dSjfb 	if (pflag && (cvs_sendarg(root, "-p", 0) < 0))
44431274bbfSjoris 		return (CVS_EX_PROTO);
4455e78344dSjfb 
44608f90673Sjfb 	if (format == D_CONTEXT)
447dc6a6879Sjfb 		cvs_sendarg(root, "-c", 0);
44808f90673Sjfb 	else if (format == D_UNIFIED)
449dc6a6879Sjfb 		cvs_sendarg(root, "-u", 0);
45008f90673Sjfb 
451dc6a6879Sjfb 	if (dap->rev1 != NULL) {
452dc6a6879Sjfb 		cvs_sendarg(root, "-r", 0);
45343579e5bSjfb 		cvs_sendarg(root, dap->rev1, 0);
4543917c9bfSderaadt 	} else if (dap->date1 != NULL) {
455dc6a6879Sjfb 		cvs_sendarg(root, "-D", 0);
45643579e5bSjfb 		cvs_sendarg(root, dap->date1, 0);
45708f90673Sjfb 	}
458dc6a6879Sjfb 	if (dap->rev2 != NULL) {
459dc6a6879Sjfb 		cvs_sendarg(root, "-r", 0);
46043579e5bSjfb 		cvs_sendarg(root, dap->rev2, 0);
4613917c9bfSderaadt 	} else if (dap->date2 != NULL) {
462dc6a6879Sjfb 		cvs_sendarg(root, "-D", 0);
46343579e5bSjfb 		cvs_sendarg(root, dap->date2, 0);
46408f90673Sjfb 	}
46508f90673Sjfb 
46608f90673Sjfb 	return (0);
46708f90673Sjfb }
46808f90673Sjfb 
46908f90673Sjfb 
47008f90673Sjfb /*
47108f90673Sjfb  * cvs_diff_file()
47208f90673Sjfb  *
47308f90673Sjfb  * Diff a single file.
47408f90673Sjfb  */
47508f90673Sjfb int
476dc6a6879Sjfb cvs_diff_file(struct cvs_file *cfp, void *arg)
47708f90673Sjfb {
47827b85f85Sxsa 	int l;
479c710bc5aSjfb 	char *dir, *repo, buf[64];
480c710bc5aSjfb 	char fpath[MAXPATHLEN], dfpath[MAXPATHLEN], rcspath[MAXPATHLEN];
481946f6157Sdjm 	char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN];
48208f90673Sjfb 	BUF *b1, *b2;
48308f90673Sjfb 	RCSNUM *r1, *r2;
48408f90673Sjfb 	RCSFILE *rf;
485dc6a6879Sjfb 	struct cvsroot *root;
486dc6a6879Sjfb 
487dc6a6879Sjfb 	if (cfp->cf_type == DT_DIR) {
488895e6cf6Sjfb 		if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
489*bb029937Sjfb 			root = cfp->cf_parent->cf_root;
490c710bc5aSjfb 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE,
491c710bc5aSjfb 			    CVS_FILE_NAME(cfp));
4923917c9bfSderaadt 		} else {
493*bb029937Sjfb 			root = cfp->cf_root;
49416cfc147Sjoris #if 0
495dc6a6879Sjfb 			if ((cfp->cf_parent == NULL) ||
496*bb029937Sjfb 			    (root != cfp->cf_parent->cf_root)) {
497dc6a6879Sjfb 				cvs_connect(root);
49816cfc147Sjoris 				cvs_diff_sendflags(root);
499dc6a6879Sjfb 			}
50016cfc147Sjoris #endif
501dc6a6879Sjfb 
502dc6a6879Sjfb 			cvs_senddir(root, cfp);
503895e6cf6Sjfb 		}
504895e6cf6Sjfb 
505dc6a6879Sjfb 		return (0);
506dc6a6879Sjfb 	}
50708f90673Sjfb 
5082d5b8b1dSjfb 	if (cfp->cf_cvstat == CVS_FST_LOST) {
5092d5b8b1dSjfb 		cvs_log(LP_WARN, "cannot find file %s", CVS_FILE_NAME(cfp));
5102d5b8b1dSjfb 		return (0);
5112d5b8b1dSjfb 	}
5122d5b8b1dSjfb 
51308f90673Sjfb 	rf = NULL;
514c710bc5aSjfb 	diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath));
515895e6cf6Sjfb 
516dc6a6879Sjfb 	if (cfp->cf_parent != NULL) {
517c710bc5aSjfb 		dir = cvs_file_getpath(cfp->cf_parent, dfpath, sizeof(dfpath));
518*bb029937Sjfb 		root = cfp->cf_parent->cf_root;
519*bb029937Sjfb 		repo = cfp->cf_parent->cf_repo;
5203917c9bfSderaadt 	} else {
521dc6a6879Sjfb 		dir = ".";
522895e6cf6Sjfb 		root = NULL;
523dc6a6879Sjfb 		repo = NULL;
52408f90673Sjfb 	}
52508f90673Sjfb 
526dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
527dc6a6879Sjfb 		if (root->cr_method == CVS_METHOD_LOCAL)
528dc6a6879Sjfb 			cvs_log(LP_WARN, "I know nothing about %s", diff_file);
529dc6a6879Sjfb 		else
530c710bc5aSjfb 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE,
531c710bc5aSjfb 			    CVS_FILE_NAME(cfp));
532dc6a6879Sjfb 		return (0);
53308f90673Sjfb 	}
53408f90673Sjfb 
535dc6a6879Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
536*bb029937Sjfb 		if (cvs_sendentry(root, cfp) < 0) {
53731274bbfSjoris 			return (CVS_EX_PROTO);
53808f90673Sjfb 		}
53908f90673Sjfb 	}
54008f90673Sjfb 
541dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UPTODATE) {
542dc6a6879Sjfb 		if (root->cr_method != CVS_METHOD_LOCAL)
543c710bc5aSjfb 			cvs_sendreq(root, CVS_REQ_UNCHANGED,
544c710bc5aSjfb 			    CVS_FILE_NAME(cfp));
54508f90673Sjfb 		return (0);
54608f90673Sjfb 	}
54708f90673Sjfb 
54808f90673Sjfb 	/* at this point, the file is modified */
549dc6a6879Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
550c710bc5aSjfb 		cvs_sendreq(root, CVS_REQ_MODIFIED, CVS_FILE_NAME(cfp));
551dc6a6879Sjfb 		cvs_sendfile(root, diff_file);
5523917c9bfSderaadt 	} else {
55327b85f85Sxsa 		l = snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
554dc6a6879Sjfb 		    root->cr_dir, repo, diff_file, RCS_FILE_EXT);
55527b85f85Sxsa 		if (l == -1 || l >= (int)sizeof(rcspath)) {
55627b85f85Sxsa 			errno = ENAMETOOLONG;
55727b85f85Sxsa 			cvs_log(LP_ERRNO, "%s", rcspath);
55827b85f85Sxsa 			return (-1);
55927b85f85Sxsa 		}
56008f90673Sjfb 
5611b6534b8Sjfb 		rf = rcs_open(rcspath, RCS_READ);
562dc6a6879Sjfb 		if (rf == NULL) {
56331274bbfSjoris 			return (CVS_EX_DATA);
564dc6a6879Sjfb 		}
56508f90673Sjfb 
566dc6a6879Sjfb 		cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file,
56708f90673Sjfb 		    RCS_DIFF_DIV, rcspath);
56808f90673Sjfb 
569dc6a6879Sjfb 		if (dap->rev1 == NULL)
570*bb029937Sjfb 			r1 = cfp->cf_lrev;
57108f90673Sjfb 		else {
57225b74b48Sjfb 			if ((r1 = rcsnum_parse(dap->rev1)) == NULL) {
57331274bbfSjoris 				return (CVS_EX_DATA);
5747f535ec4Sjfb 			}
57508f90673Sjfb 		}
57608f90673Sjfb 
577dc6a6879Sjfb 		cvs_printf("retrieving revision %s\n",
57808f90673Sjfb 		    rcsnum_tostr(r1, buf, sizeof(buf)));
57908f90673Sjfb 		b1 = rcs_getrev(rf, r1);
58008f90673Sjfb 
581*bb029937Sjfb 		if (r1 != cfp->cf_lrev)
5827f535ec4Sjfb 			rcsnum_free(r1);
5837f535ec4Sjfb 
584dc6a6879Sjfb 		if (dap->rev2 != NULL) {
585dc6a6879Sjfb 			cvs_printf("retrieving revision %s\n", dap->rev2);
58625b74b48Sjfb 			if ((r2 = rcsnum_parse(dap->rev2)) == NULL) {
58731274bbfSjoris 				return (CVS_EX_DATA);
5887f535ec4Sjfb 			}
58908f90673Sjfb 			b2 = rcs_getrev(rf, r2);
5907f535ec4Sjfb 			rcsnum_free(r2);
5913917c9bfSderaadt 		} else {
592dc6a6879Sjfb 			b2 = cvs_buf_load(diff_file, BUF_AUTOEXT);
59308f90673Sjfb 		}
59408f90673Sjfb 
595dc6a6879Sjfb 		rcs_close(rf);
596dc6a6879Sjfb 
597f5638424Sjfb 		printf("%s", diffargs);
598f5638424Sjfb 		printf(" -r%s", buf);
599dc6a6879Sjfb 		if (dap->rev2 != NULL)
600dc6a6879Sjfb 			printf(" -r%s", dap->rev2);
601dc6a6879Sjfb 		printf(" %s\n", diff_file);
602946f6157Sdjm 		strlcpy(path_tmp1, "/tmp/diff1.XXXXXXXXXX", sizeof(path_tmp1));
6037f535ec4Sjfb 		if (cvs_buf_write_stmp(b1, path_tmp1, 0600) == -1) {
6047f535ec4Sjfb 			cvs_buf_free(b1);
6057f535ec4Sjfb 			cvs_buf_free(b2);
60631274bbfSjoris 			return (CVS_EX_DATA);
6077f535ec4Sjfb 		}
6087f535ec4Sjfb 		cvs_buf_free(b1);
6097f535ec4Sjfb 
61069853e40Sjfb 		strlcpy(path_tmp2, "/tmp/diff2.XXXXXXXXXX", sizeof(path_tmp2));
611946f6157Sdjm 		if (cvs_buf_write_stmp(b2, path_tmp2, 0600) == -1) {
6127f535ec4Sjfb 			cvs_buf_free(b2);
613946f6157Sdjm 			(void)unlink(path_tmp1);
61431274bbfSjoris 			return (CVS_EX_DATA);
615946f6157Sdjm 		}
6167f535ec4Sjfb 		cvs_buf_free(b2);
6177f535ec4Sjfb 
618946f6157Sdjm 		cvs_diffreg(path_tmp1, path_tmp2);
619946f6157Sdjm 		(void)unlink(path_tmp1);
620946f6157Sdjm 		(void)unlink(path_tmp2);
62108f90673Sjfb 	}
62208f90673Sjfb 
62308f90673Sjfb 	return (0);
62408f90673Sjfb }
62508f90673Sjfb 
62608f90673Sjfb 
62708f90673Sjfb int
62808f90673Sjfb cvs_diffreg(const char *file1, const char *file2)
62908f90673Sjfb {
63008f90673Sjfb 	FILE *f1, *f2;
63108f90673Sjfb 	int i, rval;
6327f535ec4Sjfb 	void *tmp;
63308f90673Sjfb 
63408f90673Sjfb 	f1 = f2 = NULL;
63508f90673Sjfb 	rval = D_SAME;
63608f90673Sjfb 	anychange = 0;
63708f90673Sjfb 	lastline = 0;
63808f90673Sjfb 	lastmatchline = 0;
63908f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
64008f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
64108f90673Sjfb 
64208f90673Sjfb 	f1 = fopen(file1, "r");
64308f90673Sjfb 	if (f1 == NULL) {
64408f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
64508f90673Sjfb 		status |= 2;
64608f90673Sjfb 		goto closem;
64708f90673Sjfb 	}
64808f90673Sjfb 
64908f90673Sjfb 	f2 = fopen(file2, "r");
65008f90673Sjfb 	if (f2 == NULL) {
65108f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
65208f90673Sjfb 		status |= 2;
65308f90673Sjfb 		goto closem;
65408f90673Sjfb 	}
65508f90673Sjfb 
65608f90673Sjfb 	switch (files_differ(f1, f2)) {
65708f90673Sjfb 	case 0:
65808f90673Sjfb 		goto closem;
65908f90673Sjfb 	case 1:
66008f90673Sjfb 		break;
66108f90673Sjfb 	default:
66208f90673Sjfb 		/* error */
66308f90673Sjfb 		status |= 2;
66408f90673Sjfb 		goto closem;
66508f90673Sjfb 	}
66608f90673Sjfb 
66708f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
66808f90673Sjfb 		rval = D_BINARY;
66908f90673Sjfb 		status |= 1;
67008f90673Sjfb 		goto closem;
67108f90673Sjfb 	}
6727f535ec4Sjfb 	if ((prepare(0, f1, stb1.st_size) < 0) ||
6737f535ec4Sjfb 	    (prepare(1, f2, stb2.st_size) < 0)) {
6747f535ec4Sjfb 		status |= 2;
6757f535ec4Sjfb 		goto closem;
6767f535ec4Sjfb 	}
67708f90673Sjfb 	prune();
67808f90673Sjfb 	sort(sfile[0], slen[0]);
67908f90673Sjfb 	sort(sfile[1], slen[1]);
68008f90673Sjfb 
68108f90673Sjfb 	member = (int *)file[1];
68208f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
6837f535ec4Sjfb 	if ((tmp = realloc(member, (slen[1] + 2) * sizeof(int))) == NULL) {
6847f535ec4Sjfb 		status |= 2;
6857f535ec4Sjfb 		goto closem;
6867f535ec4Sjfb 	}
6877f535ec4Sjfb 	member = (int *)tmp;
68808f90673Sjfb 
68908f90673Sjfb 	class = (int *)file[0];
69008f90673Sjfb 	unsort(sfile[0], slen[0], class);
6917f535ec4Sjfb 	if ((tmp = realloc(class, (slen[0] + 2) * sizeof(int))) == NULL) {
6927f535ec4Sjfb 		status |= 2;
6937f535ec4Sjfb 		goto closem;
6947f535ec4Sjfb 	}
6957f535ec4Sjfb 	class = (int *)tmp;
69608f90673Sjfb 
6977f535ec4Sjfb 	if ((klist = malloc((slen[0] + 2) * sizeof(int))) == NULL) {
6987f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate klist");
6997f535ec4Sjfb 		status |= 2;
7007f535ec4Sjfb 		goto closem;
7017f535ec4Sjfb 	}
70208f90673Sjfb 	clen = 0;
70308f90673Sjfb 	clistlen = 100;
7047f535ec4Sjfb 	if ((clist = malloc(clistlen * sizeof(cand))) == NULL) {
7057f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate clist");
7067f535ec4Sjfb 		status |= 2;
7077f535ec4Sjfb 		goto closem;
7087f535ec4Sjfb 	}
70908f90673Sjfb 	i = stone(class, slen[0], member, klist);
71008f90673Sjfb 	free(member);
71108f90673Sjfb 	free(class);
71208f90673Sjfb 
71308f90673Sjfb 	J = realloc(J, (len[0] + 2) * sizeof(int));
71408f90673Sjfb 	unravel(klist[i]);
71508f90673Sjfb 	free(clist);
71608f90673Sjfb 	free(klist);
71708f90673Sjfb 
71808f90673Sjfb 	ixold = realloc(ixold, (len[0] + 2) * sizeof(long));
71908f90673Sjfb 	ixnew = realloc(ixnew, (len[1] + 2) * sizeof(long));
72008f90673Sjfb 	check(f1, f2);
72108f90673Sjfb 	output(file1, f1, file2, f2);
72208f90673Sjfb 
72308f90673Sjfb closem:
72408f90673Sjfb 	if (anychange) {
72508f90673Sjfb 		status |= 1;
72608f90673Sjfb 		if (rval == D_SAME)
72708f90673Sjfb 			rval = D_DIFFER;
72808f90673Sjfb 	}
72908f90673Sjfb 	if (f1 != NULL)
73008f90673Sjfb 		fclose(f1);
73108f90673Sjfb 	if (f2 != NULL)
73208f90673Sjfb 		fclose(f2);
7337f535ec4Sjfb 
73408f90673Sjfb 	return (rval);
73508f90673Sjfb }
73608f90673Sjfb 
73708f90673Sjfb /*
73808f90673Sjfb  * Check to see if the given files differ.
73908f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
74008f90673Sjfb  * XXX - could use code from cmp(1) [faster]
74108f90673Sjfb  */
74208f90673Sjfb static int
74308f90673Sjfb files_differ(FILE *f1, FILE *f2)
74408f90673Sjfb {
74508f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
74608f90673Sjfb 	size_t i, j;
74708f90673Sjfb 
74808f90673Sjfb 	if (stb1.st_size != stb2.st_size)
74908f90673Sjfb 		return (1);
75008f90673Sjfb 	for (;;) {
75108f90673Sjfb 		i = fread(buf1, 1, sizeof(buf1), f1);
75208f90673Sjfb 		j = fread(buf2, 1, sizeof(buf2), f2);
75308f90673Sjfb 		if (i != j)
75408f90673Sjfb 			return (1);
75508f90673Sjfb 		if (i == 0 && j == 0) {
75608f90673Sjfb 			if (ferror(f1) || ferror(f2))
75708f90673Sjfb 				return (1);
75808f90673Sjfb 			return (0);
75908f90673Sjfb 		}
76008f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
76108f90673Sjfb 			return (1);
76208f90673Sjfb 	}
76308f90673Sjfb }
76408f90673Sjfb 
76508f90673Sjfb 
76608f90673Sjfb char *
7672a0de57dSjfb splice(char *dir, char *filename)
76808f90673Sjfb {
76908f90673Sjfb 	char *tail, *buf;
77008f90673Sjfb 
7712a0de57dSjfb 	if ((tail = strrchr(filename, '/')) == NULL)
7722a0de57dSjfb 		tail = filename;
77308f90673Sjfb 	else
77408f90673Sjfb 		tail++;
77508f90673Sjfb 	asprintf(&buf, "%s/%s", dir, tail);
77608f90673Sjfb 	return (buf);
77708f90673Sjfb }
77808f90673Sjfb 
7797f535ec4Sjfb static int
78008f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
78108f90673Sjfb {
7827f535ec4Sjfb 	void *tmp;
78308f90673Sjfb 	struct line *p;
78408f90673Sjfb 	int j, h;
78508f90673Sjfb 	size_t sz;
78608f90673Sjfb 
78708f90673Sjfb 	rewind(fd);
78808f90673Sjfb 
789c48da046Sxsa 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
79008f90673Sjfb 	if (sz < 100)
79108f90673Sjfb 		sz = 100;
79208f90673Sjfb 
7937f535ec4Sjfb 	p = (struct line *)malloc((sz + 3) * sizeof(struct line));
7947f535ec4Sjfb 	if (p == NULL) {
7957f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to prepare line array");
7967f535ec4Sjfb 		return (-1);
7977f535ec4Sjfb 	}
79808f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
79908f90673Sjfb 		if (j == (int)sz) {
80008f90673Sjfb 			sz = sz * 3 / 2;
8017f535ec4Sjfb 			tmp = realloc(p, (sz + 3) * sizeof(struct line));
8027f535ec4Sjfb 			if (tmp == NULL) {
8037f535ec4Sjfb 				cvs_log(LP_ERRNO, "failed to grow line array");
8047f535ec4Sjfb 				free(p);
8057f535ec4Sjfb 				return (-1);
8067f535ec4Sjfb 			}
8077f535ec4Sjfb 			p = (struct line *)tmp;
80808f90673Sjfb 		}
80908f90673Sjfb 		p[++j].value = h;
81008f90673Sjfb 	}
81108f90673Sjfb 	len[i] = j;
81208f90673Sjfb 	file[i] = p;
8137f535ec4Sjfb 
8147f535ec4Sjfb 	return (0);
81508f90673Sjfb }
81608f90673Sjfb 
81708f90673Sjfb static void
81808f90673Sjfb prune(void)
81908f90673Sjfb {
82008f90673Sjfb 	int i, j;
82108f90673Sjfb 
82208f90673Sjfb 	for (pref = 0; pref < len[0] && pref < len[1] &&
82308f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
82408f90673Sjfb 	    pref++)
82508f90673Sjfb 		;
82608f90673Sjfb 	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
82708f90673Sjfb 	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
82808f90673Sjfb 	    suff++)
82908f90673Sjfb 		;
83008f90673Sjfb 	for (j = 0; j < 2; j++) {
83108f90673Sjfb 		sfile[j] = file[j] + pref;
83208f90673Sjfb 		slen[j] = len[j] - pref - suff;
83308f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
83408f90673Sjfb 			sfile[j][i].serial = i;
83508f90673Sjfb 	}
83608f90673Sjfb }
83708f90673Sjfb 
83808f90673Sjfb static void
83908f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
84008f90673Sjfb {
84108f90673Sjfb 	int i, j;
84208f90673Sjfb 
84308f90673Sjfb 	i = j = 1;
84408f90673Sjfb 	while (i <= n && j <= m) {
84508f90673Sjfb 		if (a[i].value < b[j].value)
84608f90673Sjfb 			a[i++].value = 0;
84708f90673Sjfb 		else if (a[i].value == b[j].value)
84808f90673Sjfb 			a[i++].value = j;
84908f90673Sjfb 		else
85008f90673Sjfb 			j++;
85108f90673Sjfb 	}
85208f90673Sjfb 	while (i <= n)
85308f90673Sjfb 		a[i++].value = 0;
85408f90673Sjfb 	b[m + 1].value = 0;
85508f90673Sjfb 	j = 0;
85608f90673Sjfb 	while (++j <= m) {
85708f90673Sjfb 		c[j] = -b[j].serial;
85808f90673Sjfb 		while (b[j + 1].value == b[j].value) {
85908f90673Sjfb 			j++;
86008f90673Sjfb 			c[j] = b[j].serial;
86108f90673Sjfb 		}
86208f90673Sjfb 	}
86308f90673Sjfb 	c[j] = -1;
86408f90673Sjfb }
86508f90673Sjfb 
86608f90673Sjfb /* Code taken from ping.c */
86708f90673Sjfb static int
86808f90673Sjfb isqrt(int n)
86908f90673Sjfb {
87008f90673Sjfb 	int y, x = 1;
87108f90673Sjfb 
87208f90673Sjfb 	if (n == 0)
87308f90673Sjfb 		return (0);
87408f90673Sjfb 
87508f90673Sjfb 	do { /* newton was a stinker */
87608f90673Sjfb 		y = x;
87708f90673Sjfb 		x = n / x;
87808f90673Sjfb 		x += y;
87908f90673Sjfb 		x /= 2;
88008f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
88108f90673Sjfb 
88208f90673Sjfb 	return (x);
88308f90673Sjfb }
88408f90673Sjfb 
88508f90673Sjfb static int
88608f90673Sjfb stone(int *a, int n, int *b, int *c)
88708f90673Sjfb {
88808f90673Sjfb 	int i, k, y, j, l;
88908f90673Sjfb 	int oldc, tc, oldl;
89008f90673Sjfb 	u_int numtries;
89108f90673Sjfb 
892cc649edbSjfb 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
893cc649edbSjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
89408f90673Sjfb 
89508f90673Sjfb 	k = 0;
89608f90673Sjfb 	c[0] = newcand(0, 0, 0);
89708f90673Sjfb 	for (i = 1; i <= n; i++) {
89808f90673Sjfb 		j = a[i];
89908f90673Sjfb 		if (j == 0)
90008f90673Sjfb 			continue;
90108f90673Sjfb 		y = -b[j];
90208f90673Sjfb 		oldl = 0;
90308f90673Sjfb 		oldc = c[0];
90408f90673Sjfb 		numtries = 0;
90508f90673Sjfb 		do {
90608f90673Sjfb 			if (y <= clist[oldc].y)
90708f90673Sjfb 				continue;
90808f90673Sjfb 			l = search(c, k, y);
90908f90673Sjfb 			if (l != oldl + 1)
91008f90673Sjfb 				oldc = c[l - 1];
91108f90673Sjfb 			if (l <= k) {
91208f90673Sjfb 				if (clist[c[l]].y <= y)
91308f90673Sjfb 					continue;
91408f90673Sjfb 				tc = c[l];
91508f90673Sjfb 				c[l] = newcand(i, y, oldc);
91608f90673Sjfb 				oldc = tc;
91708f90673Sjfb 				oldl = l;
91808f90673Sjfb 				numtries++;
91908f90673Sjfb 			} else {
92008f90673Sjfb 				c[l] = newcand(i, y, oldc);
92108f90673Sjfb 				k++;
92208f90673Sjfb 				break;
92308f90673Sjfb 			}
92408f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
92508f90673Sjfb 	}
92608f90673Sjfb 	return (k);
92708f90673Sjfb }
92808f90673Sjfb 
92908f90673Sjfb static int
93008f90673Sjfb newcand(int x, int y, int pred)
93108f90673Sjfb {
93208f90673Sjfb 	struct cand *q;
93308f90673Sjfb 
93408f90673Sjfb 	if (clen == clistlen) {
93508f90673Sjfb 		clistlen = clistlen * 11 / 10;
93608f90673Sjfb 		clist = realloc(clist, clistlen * sizeof(cand));
9377f535ec4Sjfb 		if (clist == NULL) {
9387f535ec4Sjfb 			cvs_log(LP_ERRNO, "failed to resize clist");
9397f535ec4Sjfb 			return (-1);
9407f535ec4Sjfb 		}
94108f90673Sjfb 	}
94208f90673Sjfb 	q = clist + clen;
94308f90673Sjfb 	q->x = x;
94408f90673Sjfb 	q->y = y;
94508f90673Sjfb 	q->pred = pred;
94608f90673Sjfb 	return (clen++);
94708f90673Sjfb }
94808f90673Sjfb 
94908f90673Sjfb static int
95008f90673Sjfb search(int *c, int k, int y)
95108f90673Sjfb {
95208f90673Sjfb 	int i, j, l, t;
95308f90673Sjfb 
95408f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
95508f90673Sjfb 		return (k + 1);
95608f90673Sjfb 	i = 0;
95708f90673Sjfb 	j = k + 1;
95808f90673Sjfb 	while (1) {
95908f90673Sjfb 		l = i + j;
96008f90673Sjfb 		if ((l >>= 1) <= i)
96108f90673Sjfb 			break;
96208f90673Sjfb 		t = clist[c[l]].y;
96308f90673Sjfb 		if (t > y)
96408f90673Sjfb 			j = l;
96508f90673Sjfb 		else if (t < y)
96608f90673Sjfb 			i = l;
96708f90673Sjfb 		else
96808f90673Sjfb 			return (l);
96908f90673Sjfb 	}
97008f90673Sjfb 	return (l + 1);
97108f90673Sjfb }
97208f90673Sjfb 
97308f90673Sjfb static void
97408f90673Sjfb unravel(int p)
97508f90673Sjfb {
97608f90673Sjfb 	struct cand *q;
97708f90673Sjfb 	int i;
97808f90673Sjfb 
97908f90673Sjfb 	for (i = 0; i <= len[0]; i++)
98008f90673Sjfb 		J[i] = i <= pref ? i :
98108f90673Sjfb 		    i > len[0] - suff ? i + len[1] - len[0] : 0;
98208f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
98308f90673Sjfb 		J[q->x + pref] = q->y + pref;
98408f90673Sjfb }
98508f90673Sjfb 
98608f90673Sjfb /*
98708f90673Sjfb  * Check does double duty:
98808f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
98908f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
99008f90673Sjfb  *  2.  collect random access indexes to the two files
99108f90673Sjfb  */
99208f90673Sjfb static void
99308f90673Sjfb check(FILE *f1, FILE *f2)
99408f90673Sjfb {
99508f90673Sjfb 	int i, j, jackpot, c, d;
99608f90673Sjfb 	long ctold, ctnew;
99708f90673Sjfb 
99808f90673Sjfb 	rewind(f1);
99908f90673Sjfb 	rewind(f2);
100008f90673Sjfb 	j = 1;
100108f90673Sjfb 	ixold[0] = ixnew[0] = 0;
100208f90673Sjfb 	jackpot = 0;
100308f90673Sjfb 	ctold = ctnew = 0;
100408f90673Sjfb 	for (i = 1; i <= len[0]; i++) {
100508f90673Sjfb 		if (J[i] == 0) {
100608f90673Sjfb 			ixold[i] = ctold += skipline(f1);
100708f90673Sjfb 			continue;
100808f90673Sjfb 		}
100908f90673Sjfb 		while (j < J[i]) {
101008f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
101108f90673Sjfb 			j++;
101208f90673Sjfb 		}
101308f90673Sjfb 		if (bflag || wflag || iflag) {
101408f90673Sjfb 			for (;;) {
101508f90673Sjfb 				c = getc(f1);
101608f90673Sjfb 				d = getc(f2);
101708f90673Sjfb 				/*
101808f90673Sjfb 				 * GNU diff ignores a missing newline
101908f90673Sjfb 				 * in one file if bflag || wflag.
102008f90673Sjfb 				 */
102108f90673Sjfb 				if ((bflag || wflag) &&
102208f90673Sjfb 				    ((c == EOF && d == '\n') ||
102308f90673Sjfb 				    (c == '\n' && d == EOF))) {
102408f90673Sjfb 					break;
102508f90673Sjfb 				}
102608f90673Sjfb 				ctold++;
102708f90673Sjfb 				ctnew++;
102808f90673Sjfb 				if (bflag && isspace(c) && isspace(d)) {
102908f90673Sjfb 					do {
103008f90673Sjfb 						if (c == '\n')
103108f90673Sjfb 							break;
103208f90673Sjfb 						ctold++;
103308f90673Sjfb 					} while (isspace(c = getc(f1)));
103408f90673Sjfb 					do {
103508f90673Sjfb 						if (d == '\n')
103608f90673Sjfb 							break;
103708f90673Sjfb 						ctnew++;
103808f90673Sjfb 					} while (isspace(d = getc(f2)));
103908f90673Sjfb 				} else if (wflag) {
104008f90673Sjfb 					while (isspace(c) && c != '\n') {
104108f90673Sjfb 						c = getc(f1);
104208f90673Sjfb 						ctold++;
104308f90673Sjfb 					}
104408f90673Sjfb 					while (isspace(d) && d != '\n') {
104508f90673Sjfb 						d = getc(f2);
104608f90673Sjfb 						ctnew++;
104708f90673Sjfb 					}
104808f90673Sjfb 				}
104908f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
105008f90673Sjfb 					jackpot++;
105108f90673Sjfb 					J[i] = 0;
105208f90673Sjfb 					if (c != '\n' && c != EOF)
105308f90673Sjfb 						ctold += skipline(f1);
105408f90673Sjfb 					if (d != '\n' && c != EOF)
105508f90673Sjfb 						ctnew += skipline(f2);
105608f90673Sjfb 					break;
105708f90673Sjfb 				}
105808f90673Sjfb 				if (c == '\n' || c == EOF)
105908f90673Sjfb 					break;
106008f90673Sjfb 			}
106108f90673Sjfb 		} else {
106208f90673Sjfb 			for (;;) {
106308f90673Sjfb 				ctold++;
106408f90673Sjfb 				ctnew++;
106508f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
106608f90673Sjfb 					/* jackpot++; */
106708f90673Sjfb 					J[i] = 0;
106808f90673Sjfb 					if (c != '\n' && c != EOF)
106908f90673Sjfb 						ctold += skipline(f1);
107008f90673Sjfb 					if (d != '\n' && c != EOF)
107108f90673Sjfb 						ctnew += skipline(f2);
107208f90673Sjfb 					break;
107308f90673Sjfb 				}
107408f90673Sjfb 				if (c == '\n' || c == EOF)
107508f90673Sjfb 					break;
107608f90673Sjfb 			}
107708f90673Sjfb 		}
107808f90673Sjfb 		ixold[i] = ctold;
107908f90673Sjfb 		ixnew[j] = ctnew;
108008f90673Sjfb 		j++;
108108f90673Sjfb 	}
108208f90673Sjfb 	for (; j <= len[1]; j++)
108308f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
108408f90673Sjfb 	/*
108508f90673Sjfb 	 * if (jackpot)
108608f90673Sjfb 	 *	fprintf(stderr, "jackpot\n");
108708f90673Sjfb 	 */
108808f90673Sjfb }
108908f90673Sjfb 
109008f90673Sjfb /* shellsort CACM #201 */
109108f90673Sjfb static void
109208f90673Sjfb sort(struct line *a, int n)
109308f90673Sjfb {
109408f90673Sjfb 	struct line *ai, *aim, w;
109508f90673Sjfb 	int j, m = 0, k;
109608f90673Sjfb 
109708f90673Sjfb 	if (n == 0)
109808f90673Sjfb 		return;
109908f90673Sjfb 	for (j = 1; j <= n; j *= 2)
110008f90673Sjfb 		m = 2 * j - 1;
110108f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
110208f90673Sjfb 		k = n - m;
110308f90673Sjfb 		for (j = 1; j <= k; j++) {
110408f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
110508f90673Sjfb 				aim = &ai[m];
110608f90673Sjfb 				if (aim < ai)
110708f90673Sjfb 					break;	/* wraparound */
110808f90673Sjfb 				if (aim->value > ai[0].value ||
110908f90673Sjfb 				    (aim->value == ai[0].value &&
111008f90673Sjfb 					aim->serial > ai[0].serial))
111108f90673Sjfb 					break;
111208f90673Sjfb 				w.value = ai[0].value;
111308f90673Sjfb 				ai[0].value = aim->value;
111408f90673Sjfb 				aim->value = w.value;
111508f90673Sjfb 				w.serial = ai[0].serial;
111608f90673Sjfb 				ai[0].serial = aim->serial;
111708f90673Sjfb 				aim->serial = w.serial;
111808f90673Sjfb 			}
111908f90673Sjfb 		}
112008f90673Sjfb 	}
112108f90673Sjfb }
112208f90673Sjfb 
112308f90673Sjfb static void
112408f90673Sjfb unsort(struct line *f, int l, int *b)
112508f90673Sjfb {
112608f90673Sjfb 	int *a, i;
112708f90673Sjfb 
11287f535ec4Sjfb 	if ((a = (int *)malloc((l + 1) * sizeof(int))) == NULL) {
11297f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate sort array");
11307f535ec4Sjfb 		return;
11317f535ec4Sjfb 	}
113208f90673Sjfb 	for (i = 1; i <= l; i++)
113308f90673Sjfb 		a[f[i].serial] = f[i].value;
113408f90673Sjfb 	for (i = 1; i <= l; i++)
113508f90673Sjfb 		b[i] = a[i];
113608f90673Sjfb 	free(a);
113708f90673Sjfb }
113808f90673Sjfb 
113908f90673Sjfb static int
114008f90673Sjfb skipline(FILE *f)
114108f90673Sjfb {
114208f90673Sjfb 	int i, c;
114308f90673Sjfb 
114408f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
114508f90673Sjfb 		continue;
114608f90673Sjfb 	return (i);
114708f90673Sjfb }
114808f90673Sjfb 
114908f90673Sjfb static void
115008f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2)
115108f90673Sjfb {
115208f90673Sjfb 	int m, i0, i1, j0, j1;
115308f90673Sjfb 
115408f90673Sjfb 	rewind(f1);
115508f90673Sjfb 	rewind(f2);
115608f90673Sjfb 	m = len[0];
115708f90673Sjfb 	J[0] = 0;
115808f90673Sjfb 	J[m + 1] = len[1] + 1;
115908f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
116008f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
116108f90673Sjfb 			i0++;
116208f90673Sjfb 		j0 = J[i0 - 1] + 1;
116308f90673Sjfb 		i1 = i0 - 1;
116408f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
116508f90673Sjfb 			i1++;
116608f90673Sjfb 		j1 = J[i1 + 1] - 1;
116708f90673Sjfb 		J[i1] = j1;
116808f90673Sjfb 		change(file1, f1, file2, f2, i0, i1, j0, j1);
116908f90673Sjfb 	}
117008f90673Sjfb 	if (m == 0)
117108f90673Sjfb 		change(file1, f1, file2, f2, 1, 0, 1, len[1]);
117208f90673Sjfb 	if (format == D_IFDEF) {
117308f90673Sjfb 		for (;;) {
117408f90673Sjfb #define	c i0
117508f90673Sjfb 			if ((c = getc(f1)) == EOF)
117608f90673Sjfb 				return;
117708f90673Sjfb 			putchar(c);
117808f90673Sjfb 		}
117908f90673Sjfb #undef c
118008f90673Sjfb 	}
118108f90673Sjfb 	if (anychange != 0) {
118208f90673Sjfb 		if (format == D_CONTEXT)
118308f90673Sjfb 			dump_context_vec(f1, f2);
118408f90673Sjfb 		else if (format == D_UNIFIED)
118508f90673Sjfb 			dump_unified_vec(f1, f2);
118608f90673Sjfb 	}
118708f90673Sjfb }
118808f90673Sjfb 
118908f90673Sjfb static __inline void
119008f90673Sjfb range(int a, int b, char *separator)
119108f90673Sjfb {
119208f90673Sjfb 	printf("%d", a > b ? b : a);
119308f90673Sjfb 	if (a < b)
119408f90673Sjfb 		printf("%s%d", separator, b);
119508f90673Sjfb }
119608f90673Sjfb 
119708f90673Sjfb static __inline void
119808f90673Sjfb uni_range(int a, int b)
119908f90673Sjfb {
120008f90673Sjfb 	if (a < b)
120108f90673Sjfb 		printf("%d,%d", a, b - a + 1);
120208f90673Sjfb 	else if (a == b)
120308f90673Sjfb 		printf("%d", b);
120408f90673Sjfb 	else
120508f90673Sjfb 		printf("%d,0", b);
120608f90673Sjfb }
120708f90673Sjfb 
120808f90673Sjfb static char *
12092a0de57dSjfb preadline(int fd, size_t rlen, off_t off)
121008f90673Sjfb {
121108f90673Sjfb 	char *line;
121208f90673Sjfb 	ssize_t nr;
121308f90673Sjfb 
12142a0de57dSjfb 	line = malloc(rlen + 1);
121508f90673Sjfb 	if (line == NULL) {
121608f90673Sjfb 		cvs_log(LP_ERRNO, "failed to allocate line");
121708f90673Sjfb 		return (NULL);
121808f90673Sjfb 	}
12192a0de57dSjfb 	if ((nr = pread(fd, line, rlen, off)) < 0) {
122008f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
122108f90673Sjfb 		return (NULL);
122208f90673Sjfb 	}
122308f90673Sjfb 	line[nr] = '\0';
122408f90673Sjfb 	return (line);
122508f90673Sjfb }
122608f90673Sjfb 
122708f90673Sjfb static int
122808f90673Sjfb ignoreline(char *line)
122908f90673Sjfb {
123008f90673Sjfb 	int ret;
123108f90673Sjfb 
123208f90673Sjfb 	ret = regexec(&ignore_re, line, 0, NULL, 0);
123308f90673Sjfb 	free(line);
123408f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
123508f90673Sjfb }
123608f90673Sjfb 
123708f90673Sjfb /*
123808f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
123908f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
124008f90673Sjfb  * are no lines in the from file involved and this means that there were
124108f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
124208f90673Sjfb  * lines missing from the to file.
124308f90673Sjfb  */
124408f90673Sjfb static void
124508f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2,
124608f90673Sjfb 	int a, int b, int c, int d)
124708f90673Sjfb {
124808f90673Sjfb 	static size_t max_context = 64;
124908f90673Sjfb 	int i;
125008f90673Sjfb 
125108f90673Sjfb 	if (format != D_IFDEF && a > b && c > d)
125208f90673Sjfb 		return;
125308f90673Sjfb 	if (ignore_pats != NULL) {
125408f90673Sjfb 		char *line;
125508f90673Sjfb 		/*
125608f90673Sjfb 		 * All lines in the change, insert, or delete must
125708f90673Sjfb 		 * match an ignore pattern for the change to be
125808f90673Sjfb 		 * ignored.
125908f90673Sjfb 		 */
126008f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
126108f90673Sjfb 			for (i = a; i <= b; i++) {
126208f90673Sjfb 				line = preadline(fileno(f1),
126308f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
126408f90673Sjfb 				if (!ignoreline(line))
126508f90673Sjfb 					goto proceed;
126608f90673Sjfb 			}
126708f90673Sjfb 		}
126808f90673Sjfb 		if (a > b || c <= d) {	/* Changes and inserts. */
126908f90673Sjfb 			for (i = c; i <= d; i++) {
127008f90673Sjfb 				line = preadline(fileno(f2),
127108f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
127208f90673Sjfb 				if (!ignoreline(line))
127308f90673Sjfb 					goto proceed;
127408f90673Sjfb 			}
127508f90673Sjfb 		}
127608f90673Sjfb 		return;
127708f90673Sjfb 	}
127808f90673Sjfb proceed:
127908f90673Sjfb 	if (format == D_CONTEXT || format == D_UNIFIED) {
128008f90673Sjfb 		/*
128108f90673Sjfb 		 * Allocate change records as needed.
128208f90673Sjfb 		 */
128308f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
128408f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
128508f90673Sjfb 			max_context <<= 1;
128608f90673Sjfb 			context_vec_start = realloc(context_vec_start,
128708f90673Sjfb 			    max_context * sizeof(struct context_vec));
128808f90673Sjfb 			context_vec_end = context_vec_start + max_context;
128908f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
129008f90673Sjfb 		}
129108f90673Sjfb 		if (anychange == 0) {
129208f90673Sjfb 			/*
129308f90673Sjfb 			 * Print the context/unidiff header first time through.
129408f90673Sjfb 			 */
129508f90673Sjfb 			printf("%s %s	%s",
129608f90673Sjfb 			    format == D_CONTEXT ? "***" : "---", diff_file,
129708f90673Sjfb 			    ctime(&stb1.st_mtime));
129808f90673Sjfb 			printf("%s %s	%s",
129908f90673Sjfb 			    format == D_CONTEXT ? "---" : "+++", diff_file,
130008f90673Sjfb 			    ctime(&stb2.st_mtime));
130108f90673Sjfb 			anychange = 1;
130208f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
130308f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
130408f90673Sjfb 			/*
130508f90673Sjfb 			 * If this change is more than 'context' lines from the
130608f90673Sjfb 			 * previous change, dump the record and reset it.
130708f90673Sjfb 			 */
130808f90673Sjfb 			if (format == D_CONTEXT)
130908f90673Sjfb 				dump_context_vec(f1, f2);
131008f90673Sjfb 			else
131108f90673Sjfb 				dump_unified_vec(f1, f2);
131208f90673Sjfb 		}
131308f90673Sjfb 		context_vec_ptr++;
131408f90673Sjfb 		context_vec_ptr->a = a;
131508f90673Sjfb 		context_vec_ptr->b = b;
131608f90673Sjfb 		context_vec_ptr->c = c;
131708f90673Sjfb 		context_vec_ptr->d = d;
131808f90673Sjfb 		return;
131908f90673Sjfb 	}
132008f90673Sjfb 	if (anychange == 0)
132108f90673Sjfb 		anychange = 1;
132208f90673Sjfb 	switch (format) {
132308f90673Sjfb 	case D_BRIEF:
132408f90673Sjfb 		return;
132508f90673Sjfb 	case D_NORMAL:
132608f90673Sjfb 		range(a, b, ",");
132708f90673Sjfb 		putchar(a > b ? 'a' : c > d ? 'd' : 'c');
132808f90673Sjfb 		if (format == D_NORMAL)
132908f90673Sjfb 			range(c, d, ",");
133008f90673Sjfb 		putchar('\n');
133108f90673Sjfb 		break;
133208f90673Sjfb 	}
133308f90673Sjfb 	if (format == D_NORMAL || format == D_IFDEF) {
133408f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
133508f90673Sjfb 		if (a <= b && c <= d && format == D_NORMAL)
133608f90673Sjfb 			puts("---");
133708f90673Sjfb 	}
133808f90673Sjfb 	i = fetch(ixnew, c, d, f2, format == D_NORMAL ? '>' : '\0', 0);
133908f90673Sjfb 	if (inifdef) {
134008f90673Sjfb 		printf("#endif /* %s */\n", ifdefname);
134108f90673Sjfb 		inifdef = 0;
134208f90673Sjfb 	}
134308f90673Sjfb }
134408f90673Sjfb 
134508f90673Sjfb static int
134608f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
134708f90673Sjfb {
134808f90673Sjfb 	int i, j, c, lastc, col, nc;
134908f90673Sjfb 
135008f90673Sjfb 	/*
135108f90673Sjfb 	 * When doing #ifdef's, copy down to current line
135208f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
135308f90673Sjfb 	 */
135408f90673Sjfb 	if (format == D_IFDEF && oldfile) {
135508f90673Sjfb 		long curpos = ftell(lb);
135608f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
135708f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
135808f90673Sjfb 		for (i = 0; i < nc; i++)
135908f90673Sjfb 			putchar(getc(lb));
136008f90673Sjfb 	}
136108f90673Sjfb 	if (a > b)
136208f90673Sjfb 		return (0);
136308f90673Sjfb 	if (format == D_IFDEF) {
136408f90673Sjfb 		if (inifdef) {
136508f90673Sjfb 			printf("#else /* %s%s */\n",
136608f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
136708f90673Sjfb 		} else {
136808f90673Sjfb 			if (oldfile)
136908f90673Sjfb 				printf("#ifndef %s\n", ifdefname);
137008f90673Sjfb 			else
137108f90673Sjfb 				printf("#ifdef %s\n", ifdefname);
137208f90673Sjfb 		}
137308f90673Sjfb 		inifdef = 1 + oldfile;
137408f90673Sjfb 	}
137508f90673Sjfb 	for (i = a; i <= b; i++) {
137608f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
137708f90673Sjfb 		nc = f[i] - f[i - 1];
137808f90673Sjfb 		if (format != D_IFDEF && ch != '\0') {
137908f90673Sjfb 			putchar(ch);
138008f90673Sjfb 			if (Tflag && (format == D_NORMAL || format == D_CONTEXT
138108f90673Sjfb 			    || format == D_UNIFIED))
138208f90673Sjfb 				putchar('\t');
138308f90673Sjfb 			else if (format != D_UNIFIED)
138408f90673Sjfb 				putchar(' ');
138508f90673Sjfb 		}
138608f90673Sjfb 		col = 0;
138708f90673Sjfb 		for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
138808f90673Sjfb 			if ((c = getc(lb)) == EOF) {
138908f90673Sjfb 				puts("\n\\ No newline at end of file");
139008f90673Sjfb 				return (0);
139108f90673Sjfb 			}
139208f90673Sjfb 			if (c == '\t' && tflag) {
139308f90673Sjfb 				do {
139408f90673Sjfb 					putchar(' ');
139508f90673Sjfb 				} while (++col & 7);
139608f90673Sjfb 			} else {
139708f90673Sjfb 				putchar(c);
139808f90673Sjfb 				col++;
139908f90673Sjfb 			}
140008f90673Sjfb 		}
140108f90673Sjfb 	}
140208f90673Sjfb 	return (0);
140308f90673Sjfb }
140408f90673Sjfb 
140508f90673Sjfb /*
140608f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
140708f90673Sjfb  */
140808f90673Sjfb static int
140908f90673Sjfb readhash(FILE *f)
141008f90673Sjfb {
141108f90673Sjfb 	int i, t, space;
141208f90673Sjfb 	int sum;
141308f90673Sjfb 
141408f90673Sjfb 	sum = 1;
141508f90673Sjfb 	space = 0;
141608f90673Sjfb 	if (!bflag && !wflag) {
141708f90673Sjfb 		if (iflag)
141808f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
141908f90673Sjfb 				if (t == EOF) {
142008f90673Sjfb 					if (i == 0)
142108f90673Sjfb 						return (0);
142208f90673Sjfb 					break;
142308f90673Sjfb 				}
142408f90673Sjfb 				sum = sum * 127 + chrtran[t];
142508f90673Sjfb 			}
142608f90673Sjfb 		else
142708f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
142808f90673Sjfb 				if (t == EOF) {
142908f90673Sjfb 					if (i == 0)
143008f90673Sjfb 						return (0);
143108f90673Sjfb 					break;
143208f90673Sjfb 				}
143308f90673Sjfb 				sum = sum * 127 + t;
143408f90673Sjfb 			}
143508f90673Sjfb 	} else {
143608f90673Sjfb 		for (i = 0;;) {
143708f90673Sjfb 			switch (t = getc(f)) {
143808f90673Sjfb 			case '\t':
143908f90673Sjfb 			case ' ':
144008f90673Sjfb 				space++;
144108f90673Sjfb 				continue;
144208f90673Sjfb 			default:
144308f90673Sjfb 				if (space && !wflag) {
144408f90673Sjfb 					i++;
144508f90673Sjfb 					space = 0;
144608f90673Sjfb 				}
144708f90673Sjfb 				sum = sum * 127 + chrtran[t];
144808f90673Sjfb 				i++;
144908f90673Sjfb 				continue;
145008f90673Sjfb 			case EOF:
145108f90673Sjfb 				if (i == 0)
145208f90673Sjfb 					return (0);
145308f90673Sjfb 				/* FALLTHROUGH */
145408f90673Sjfb 			case '\n':
145508f90673Sjfb 				break;
145608f90673Sjfb 			}
145708f90673Sjfb 			break;
145808f90673Sjfb 		}
145908f90673Sjfb 	}
146008f90673Sjfb 	/*
146108f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
146208f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
146308f90673Sjfb 	 */
146408f90673Sjfb 	return (sum == 0 ? 1 : sum);
146508f90673Sjfb }
146608f90673Sjfb 
146708f90673Sjfb static int
146808f90673Sjfb asciifile(FILE *f)
146908f90673Sjfb {
147008f90673Sjfb 	char buf[BUFSIZ];
147108f90673Sjfb 	int i, cnt;
147208f90673Sjfb 
147308f90673Sjfb 	if (aflag || f == NULL)
147408f90673Sjfb 		return (1);
147508f90673Sjfb 
147608f90673Sjfb 	rewind(f);
147708f90673Sjfb 	cnt = fread(buf, 1, sizeof(buf), f);
147808f90673Sjfb 	for (i = 0; i < cnt; i++)
147908f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
148008f90673Sjfb 			return (0);
148108f90673Sjfb 	return (1);
148208f90673Sjfb }
148308f90673Sjfb 
14845e78344dSjfb static char*
14855e78344dSjfb match_function(const long *f, int pos, FILE *fp)
14865e78344dSjfb {
14875e78344dSjfb 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
14885e78344dSjfb 	size_t nc;
14895e78344dSjfb 	int last = lastline;
14905e78344dSjfb 	char *p;
14915e78344dSjfb 
14925e78344dSjfb 	lastline = pos;
14935e78344dSjfb 	while (pos > last) {
14945e78344dSjfb 		fseek(fp, f[pos - 1], SEEK_SET);
14955e78344dSjfb 		nc = f[pos] - f[pos - 1];
14965e78344dSjfb 		if (nc >= sizeof(buf))
14975e78344dSjfb 			nc = sizeof(buf) - 1;
14985e78344dSjfb 		nc = fread(buf, 1, nc, fp);
14995e78344dSjfb 		if (nc > 0) {
15005e78344dSjfb 			buf[nc] = '\0';
15015e78344dSjfb 			p = strchr(buf, '\n');
15025e78344dSjfb 			if (p != NULL)
15035e78344dSjfb 				*p = '\0';
15045e78344dSjfb 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
15055e78344dSjfb 				strlcpy(lastbuf, buf, sizeof lastbuf);
15065e78344dSjfb 				lastmatchline = pos;
15075e78344dSjfb 				return lastbuf;
15085e78344dSjfb 			}
15095e78344dSjfb 		}
15105e78344dSjfb 		pos--;
15115e78344dSjfb 	}
15125e78344dSjfb 	return (lastmatchline > 0) ? lastbuf : NULL;
15135e78344dSjfb }
15145e78344dSjfb 
151508f90673Sjfb 
151608f90673Sjfb /* dump accumulated "context" diff changes */
151708f90673Sjfb static void
151808f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
151908f90673Sjfb {
152008f90673Sjfb 	struct context_vec *cvp = context_vec_start;
152108f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
152208f90673Sjfb 	int a, b, c, d;
15235e78344dSjfb 	char ch, *f;
152408f90673Sjfb 
152508f90673Sjfb 	if (context_vec_start > context_vec_ptr)
152608f90673Sjfb 		return;
152708f90673Sjfb 
152808f90673Sjfb 	b = d = 0;		/* gcc */
1529dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1530dc6a6879Sjfb 	upb = MIN(len[0], context_vec_ptr->b + context);
1531dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1532dc6a6879Sjfb 	upd = MIN(len[1], context_vec_ptr->d + context);
153308f90673Sjfb 
153408f90673Sjfb 	printf("***************");
15355e78344dSjfb 	if (pflag) {
15365e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
15375e78344dSjfb 		if (f != NULL) {
15385e78344dSjfb 			putchar(' ');
15395e78344dSjfb 			fputs(f, stdout);
15405e78344dSjfb 		}
15415e78344dSjfb 	}
154208f90673Sjfb 	printf("\n*** ");
154308f90673Sjfb 	range(lowa, upb, ",");
154408f90673Sjfb 	printf(" ****\n");
154508f90673Sjfb 
154608f90673Sjfb 	/*
154708f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
154808f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
154908f90673Sjfb 	 * the "old" lines as context in the "new" list).
155008f90673Sjfb 	 */
155108f90673Sjfb 	do_output = 0;
155208f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
155308f90673Sjfb 		if (cvp->a <= cvp->b) {
155408f90673Sjfb 			cvp = context_vec_start;
155508f90673Sjfb 			do_output++;
155608f90673Sjfb 			break;
155708f90673Sjfb 		}
155808f90673Sjfb 	if (do_output) {
155908f90673Sjfb 		while (cvp <= context_vec_ptr) {
156008f90673Sjfb 			a = cvp->a;
156108f90673Sjfb 			b = cvp->b;
156208f90673Sjfb 			c = cvp->c;
156308f90673Sjfb 			d = cvp->d;
156408f90673Sjfb 
156508f90673Sjfb 			if (a <= b && c <= d)
156608f90673Sjfb 				ch = 'c';
156708f90673Sjfb 			else
156808f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
156908f90673Sjfb 
157008f90673Sjfb 			if (ch == 'a')
157108f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
157208f90673Sjfb 			else {
157308f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
157408f90673Sjfb 				fetch(ixold, a, b, f1,
157508f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
157608f90673Sjfb 			}
157708f90673Sjfb 			lowa = b + 1;
157808f90673Sjfb 			cvp++;
157908f90673Sjfb 		}
158008f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
158108f90673Sjfb 	}
158208f90673Sjfb 	/* output changes to the "new" file */
158308f90673Sjfb 	printf("--- ");
158408f90673Sjfb 	range(lowc, upd, ",");
158508f90673Sjfb 	printf(" ----\n");
158608f90673Sjfb 
158708f90673Sjfb 	do_output = 0;
158808f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
158908f90673Sjfb 		if (cvp->c <= cvp->d) {
159008f90673Sjfb 			cvp = context_vec_start;
159108f90673Sjfb 			do_output++;
159208f90673Sjfb 			break;
159308f90673Sjfb 		}
159408f90673Sjfb 	if (do_output) {
159508f90673Sjfb 		while (cvp <= context_vec_ptr) {
159608f90673Sjfb 			a = cvp->a;
159708f90673Sjfb 			b = cvp->b;
159808f90673Sjfb 			c = cvp->c;
159908f90673Sjfb 			d = cvp->d;
160008f90673Sjfb 
160108f90673Sjfb 			if (a <= b && c <= d)
160208f90673Sjfb 				ch = 'c';
160308f90673Sjfb 			else
160408f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
160508f90673Sjfb 
160608f90673Sjfb 			if (ch == 'd')
160708f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
160808f90673Sjfb 			else {
160908f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
161008f90673Sjfb 				fetch(ixnew, c, d, f2,
161108f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
161208f90673Sjfb 			}
161308f90673Sjfb 			lowc = d + 1;
161408f90673Sjfb 			cvp++;
161508f90673Sjfb 		}
161608f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
161708f90673Sjfb 	}
161808f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
161908f90673Sjfb }
162008f90673Sjfb 
162108f90673Sjfb /* dump accumulated "unified" diff changes */
162208f90673Sjfb static void
162308f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
162408f90673Sjfb {
162508f90673Sjfb 	struct context_vec *cvp = context_vec_start;
162608f90673Sjfb 	int lowa, upb, lowc, upd;
162708f90673Sjfb 	int a, b, c, d;
16285e78344dSjfb 	char ch, *f;
162908f90673Sjfb 
163008f90673Sjfb 	if (context_vec_start > context_vec_ptr)
163108f90673Sjfb 		return;
163208f90673Sjfb 
163308f90673Sjfb 	b = d = 0;		/* gcc */
1634dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1635dc6a6879Sjfb 	upb = MIN(len[0], context_vec_ptr->b + context);
1636dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1637dc6a6879Sjfb 	upd = MIN(len[1], context_vec_ptr->d + context);
163808f90673Sjfb 
163908f90673Sjfb 	fputs("@@ -", stdout);
164008f90673Sjfb 	uni_range(lowa, upb);
164108f90673Sjfb 	fputs(" +", stdout);
164208f90673Sjfb 	uni_range(lowc, upd);
164308f90673Sjfb 	fputs(" @@", stdout);
16445e78344dSjfb 	if (pflag) {
16455e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
16465e78344dSjfb 		if (f != NULL) {
16475e78344dSjfb 			putchar(' ');
16485e78344dSjfb 			fputs(f, stdout);
16495e78344dSjfb 		}
16505e78344dSjfb 	}
165108f90673Sjfb 	putchar('\n');
165208f90673Sjfb 
165308f90673Sjfb 	/*
165408f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
165508f90673Sjfb 	 * are printed together.
165608f90673Sjfb 	 */
165708f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
165808f90673Sjfb 		a = cvp->a;
165908f90673Sjfb 		b = cvp->b;
166008f90673Sjfb 		c = cvp->c;
166108f90673Sjfb 		d = cvp->d;
166208f90673Sjfb 
166308f90673Sjfb 		/*
166408f90673Sjfb 		 * c: both new and old changes
166508f90673Sjfb 		 * d: only changes in the old file
166608f90673Sjfb 		 * a: only changes in the new file
166708f90673Sjfb 		 */
166808f90673Sjfb 		if (a <= b && c <= d)
166908f90673Sjfb 			ch = 'c';
167008f90673Sjfb 		else
167108f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
167208f90673Sjfb 
167308f90673Sjfb 		switch (ch) {
167408f90673Sjfb 		case 'c':
167508f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
167608f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
167708f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
167808f90673Sjfb 			break;
167908f90673Sjfb 		case 'd':
168008f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
168108f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
168208f90673Sjfb 			break;
168308f90673Sjfb 		case 'a':
168408f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
168508f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
168608f90673Sjfb 			break;
168708f90673Sjfb 		}
168808f90673Sjfb 		lowa = b + 1;
168908f90673Sjfb 		lowc = d + 1;
169008f90673Sjfb 	}
169108f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
169208f90673Sjfb 
169308f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
169408f90673Sjfb }
1695