xref: /openbsd-src/usr.bin/cvs/diff.c (revision 7f535ec4f820f752ff9329a57d7ef04f5f1c36f4)
1*7f535ec4Sjfb /*	$OpenBSD: diff.c,v 1.20 2005/02/01 18:03:32 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 *);
218cc811931Sjfb int  cvs_diff_sendflags(struct cvsroot *, struct diff_arg *);
21908f90673Sjfb static void output(const char *, FILE *, const char *, FILE *);
22008f90673Sjfb static void check(FILE *, FILE *);
22108f90673Sjfb static void range(int, int, char *);
22208f90673Sjfb static void uni_range(int, int);
22308f90673Sjfb static void dump_context_vec(FILE *, FILE *);
22408f90673Sjfb static void dump_unified_vec(FILE *, FILE *);
225*7f535ec4Sjfb static int  prepare(int, FILE *, off_t);
22608f90673Sjfb static void prune(void);
22708f90673Sjfb static void equiv(struct line *, int, struct line *, int, int *);
22808f90673Sjfb static void unravel(int);
22908f90673Sjfb static void unsort(struct line *, int, int *);
23008f90673Sjfb static void change(const char *, FILE *, const char *, FILE *, int, int, int, int);
23108f90673Sjfb static void sort(struct line *, int);
23208f90673Sjfb static int  ignoreline(char *);
23308f90673Sjfb static int  asciifile(FILE *);
23408f90673Sjfb static int  fetch(long *, int, int, FILE *, int, int);
23508f90673Sjfb static int  newcand(int, int, int);
23608f90673Sjfb static int  search(int *, int, int);
23708f90673Sjfb static int  skipline(FILE *);
23808f90673Sjfb static int  isqrt(int);
23908f90673Sjfb static int  stone(int *, int, int *, int *);
24008f90673Sjfb static int  readhash(FILE *);
24108f90673Sjfb static int  files_differ(FILE *, FILE *);
2425e78344dSjfb static char *match_function(const long *, int, FILE *);
24308f90673Sjfb static char *preadline(int, size_t, off_t);
24408f90673Sjfb 
24508f90673Sjfb 
24608f90673Sjfb extern int cvs_client;
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 
34008f90673Sjfb 
34108f90673Sjfb /*
34208f90673Sjfb  * cvs_diff()
34308f90673Sjfb  *
34408f90673Sjfb  * Handler for the `cvs diff' command.
34508f90673Sjfb  *
34608f90673Sjfb  * SYNOPSIS: cvs [args] diff [-clipu] [-D date] [-r rev]
34708f90673Sjfb  */
34808f90673Sjfb int
34908f90673Sjfb cvs_diff(int argc, char **argv)
35008f90673Sjfb {
351dc6a6879Sjfb 	int ch, recurse, flags;
352dc6a6879Sjfb 	struct diff_arg darg;
353dc6a6879Sjfb 	struct cvsroot *root;
35408f90673Sjfb 
35508f90673Sjfb 	context = CVS_DIFF_DEFCTX;
356dc6a6879Sjfb 	flags = CF_RECURSE|CF_IGNORE|CF_SORT|CF_KNOWN;
35708f90673Sjfb 	recurse = 1;
35808f90673Sjfb 
359dc6a6879Sjfb 	memset(&darg, 0, sizeof(darg));
360dc6a6879Sjfb 	strlcpy(diffargs, argv[0], sizeof(diffargs));
361dc6a6879Sjfb 
3625e78344dSjfb 	while ((ch = getopt(argc, argv, "cD:liNpr:u")) != -1) {
36308f90673Sjfb 		switch (ch) {
36408f90673Sjfb 		case 'c':
365f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
36608f90673Sjfb 			format = D_CONTEXT;
36708f90673Sjfb 			break;
36808f90673Sjfb 		case 'D':
369dc6a6879Sjfb 			if (darg.date1 == NULL && darg.rev1 == NULL)
370dc6a6879Sjfb 				darg.date1 = optarg;
371dc6a6879Sjfb 			else if (darg.date2 == NULL && darg.rev2 == NULL)
372dc6a6879Sjfb 				darg.date2 = optarg;
37308f90673Sjfb 			else {
37408f90673Sjfb 				cvs_log(LP_ERR,
37508f90673Sjfb 				    "no more than two revisions/dates can "
37608f90673Sjfb 				    "be specified");
37708f90673Sjfb 			}
37808f90673Sjfb 			break;
37908f90673Sjfb 		case 'l':
380f5638424Sjfb 			strlcat(diffargs, " -l", sizeof(diffargs));
38108f90673Sjfb 			recurse = 0;
382dc6a6879Sjfb 			flags &= ~CF_RECURSE;
38308f90673Sjfb 			break;
38408f90673Sjfb 		case 'i':
385f5638424Sjfb 			strlcat(diffargs, " -i", sizeof(diffargs));
38608f90673Sjfb 			iflag = 1;
38708f90673Sjfb 			break;
388c710bc5aSjfb 		case 'N':
389c710bc5aSjfb 			strlcat(diffargs, " -N", sizeof(diffargs));
390c710bc5aSjfb 			Nflag = 1;
391c710bc5aSjfb 			break;
3925e78344dSjfb 		case 'p':
3935e78344dSjfb 			strlcat(diffargs, " -p", sizeof(diffargs));
3945e78344dSjfb 			pflag = 1;
3955e78344dSjfb 			break;
39608f90673Sjfb 		case 'r':
397dc6a6879Sjfb 			if ((darg.rev1 == NULL) && (darg.date1 == NULL))
398dc6a6879Sjfb 				darg.rev1 = optarg;
399dc6a6879Sjfb 			else if ((darg.rev2 == NULL) && (darg.date2 == NULL))
400dc6a6879Sjfb 				darg.rev2 = optarg;
40108f90673Sjfb 			else {
40208f90673Sjfb 				cvs_log(LP_ERR,
40308f90673Sjfb 				    "no more than two revisions/dates can "
40408f90673Sjfb 				    "be specified");
40508f90673Sjfb 				return (EX_USAGE);
40608f90673Sjfb 			}
40708f90673Sjfb 			break;
40808f90673Sjfb 		case 'u':
409f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
41008f90673Sjfb 			format = D_UNIFIED;
41108f90673Sjfb 			break;
41208f90673Sjfb 		default:
41308f90673Sjfb 			return (EX_USAGE);
41408f90673Sjfb 		}
41508f90673Sjfb 	}
41608f90673Sjfb 
41708f90673Sjfb 	argc -= optind;
41808f90673Sjfb 	argv += optind;
41908f90673Sjfb 
42008f90673Sjfb 	if (argc == 0) {
421b33893e8Sjfb 		cvs_files = cvs_file_get(".", flags);
4223917c9bfSderaadt 	} else
423b33893e8Sjfb 		cvs_files = cvs_file_getspec(argv, argc, 0);
424895e6cf6Sjfb 	if (cvs_files == NULL)
425895e6cf6Sjfb 		return (EX_DATAERR);
42608f90673Sjfb 
427b33893e8Sjfb 	cvs_file_examine(cvs_files, cvs_diff_file, &darg);
42808f90673Sjfb 
429b33893e8Sjfb 	root = cvs_files->cf_ddat->cd_root;
430dd2c5b0dSjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
431b33893e8Sjfb 		cvs_senddir(root, cvs_files);
432dc6a6879Sjfb 		cvs_sendreq(root, CVS_REQ_DIFF, NULL);
433dd2c5b0dSjfb 	}
43408f90673Sjfb 
435dc6a6879Sjfb 	return (0);
436dc6a6879Sjfb }
437dc6a6879Sjfb 
438dc6a6879Sjfb 
439dc6a6879Sjfb /*
440dc6a6879Sjfb  * cvs_diff_sendflags()
441dc6a6879Sjfb  *
442dc6a6879Sjfb  */
443dc6a6879Sjfb int
444dc6a6879Sjfb cvs_diff_sendflags(struct cvsroot *root, struct diff_arg *dap)
445dc6a6879Sjfb {
44608f90673Sjfb 	/* send the flags */
4475e78344dSjfb 	if (Nflag && (cvs_sendarg(root, "-N", 0) < 0))
4485e78344dSjfb 		return (-1);
4495e78344dSjfb 	if (pflag && (cvs_sendarg(root, "-p", 0) < 0))
4505e78344dSjfb 		return (-1);
4515e78344dSjfb 
45208f90673Sjfb 	if (format == D_CONTEXT)
453dc6a6879Sjfb 		cvs_sendarg(root, "-c", 0);
45408f90673Sjfb 	else if (format == D_UNIFIED)
455dc6a6879Sjfb 		cvs_sendarg(root, "-u", 0);
45608f90673Sjfb 
457dc6a6879Sjfb 	if (dap->rev1 != NULL) {
458dc6a6879Sjfb 		cvs_sendarg(root, "-r", 0);
45943579e5bSjfb 		cvs_sendarg(root, dap->rev1, 0);
4603917c9bfSderaadt 	} else if (dap->date1 != NULL) {
461dc6a6879Sjfb 		cvs_sendarg(root, "-D", 0);
46243579e5bSjfb 		cvs_sendarg(root, dap->date1, 0);
46308f90673Sjfb 	}
464dc6a6879Sjfb 	if (dap->rev2 != NULL) {
465dc6a6879Sjfb 		cvs_sendarg(root, "-r", 0);
46643579e5bSjfb 		cvs_sendarg(root, dap->rev2, 0);
4673917c9bfSderaadt 	} else if (dap->date2 != NULL) {
468dc6a6879Sjfb 		cvs_sendarg(root, "-D", 0);
46943579e5bSjfb 		cvs_sendarg(root, dap->date2, 0);
47008f90673Sjfb 	}
47108f90673Sjfb 
47208f90673Sjfb 	return (0);
47308f90673Sjfb }
47408f90673Sjfb 
47508f90673Sjfb 
47608f90673Sjfb /*
47708f90673Sjfb  * cvs_diff_file()
47808f90673Sjfb  *
47908f90673Sjfb  * Diff a single file.
48008f90673Sjfb  */
48108f90673Sjfb int
482dc6a6879Sjfb cvs_diff_file(struct cvs_file *cfp, void *arg)
48308f90673Sjfb {
484c710bc5aSjfb 	char *dir, *repo, buf[64];
485c710bc5aSjfb 	char fpath[MAXPATHLEN], dfpath[MAXPATHLEN], rcspath[MAXPATHLEN];
486946f6157Sdjm 	char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN];
48708f90673Sjfb 	BUF *b1, *b2;
48808f90673Sjfb 	RCSNUM *r1, *r2;
48908f90673Sjfb 	RCSFILE *rf;
490dc6a6879Sjfb 	struct diff_arg *dap;
49108f90673Sjfb 	struct cvs_ent *entp;
492dc6a6879Sjfb 	struct cvsroot *root;
493dc6a6879Sjfb 
494dc6a6879Sjfb 	dap = (struct diff_arg *)arg;
495dc6a6879Sjfb 
496dc6a6879Sjfb 	if (cfp->cf_type == DT_DIR) {
497895e6cf6Sjfb 		if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
498895e6cf6Sjfb 			root = cfp->cf_parent->cf_ddat->cd_root;
499c710bc5aSjfb 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE,
500c710bc5aSjfb 			    CVS_FILE_NAME(cfp));
5013917c9bfSderaadt 		} else {
502dc6a6879Sjfb 			root = cfp->cf_ddat->cd_root;
503dc6a6879Sjfb 			if ((cfp->cf_parent == NULL) ||
504dc6a6879Sjfb 			    (root != cfp->cf_parent->cf_ddat->cd_root)) {
505dc6a6879Sjfb 				cvs_connect(root);
506dc6a6879Sjfb 				cvs_diff_sendflags(root, dap);
507dc6a6879Sjfb 			}
508dc6a6879Sjfb 
509dc6a6879Sjfb 			cvs_senddir(root, cfp);
510895e6cf6Sjfb 		}
511895e6cf6Sjfb 
512dc6a6879Sjfb 		return (0);
513dc6a6879Sjfb 	}
51408f90673Sjfb 
5152d5b8b1dSjfb 	if (cfp->cf_cvstat == CVS_FST_LOST) {
5162d5b8b1dSjfb 		cvs_log(LP_WARN, "cannot find file %s", CVS_FILE_NAME(cfp));
5172d5b8b1dSjfb 		return (0);
5182d5b8b1dSjfb 	}
5192d5b8b1dSjfb 
52008f90673Sjfb 	rf = NULL;
521c710bc5aSjfb 	diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath));
522895e6cf6Sjfb 
523dc6a6879Sjfb 	if (cfp->cf_parent != NULL) {
524c710bc5aSjfb 		dir = cvs_file_getpath(cfp->cf_parent, dfpath, sizeof(dfpath));
525895e6cf6Sjfb 		root = cfp->cf_parent->cf_ddat->cd_root;
526dc6a6879Sjfb 		repo = cfp->cf_parent->cf_ddat->cd_repo;
5273917c9bfSderaadt 	} else {
528dc6a6879Sjfb 		dir = ".";
529895e6cf6Sjfb 		root = NULL;
530dc6a6879Sjfb 		repo = NULL;
53108f90673Sjfb 	}
53208f90673Sjfb 
533dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
534dc6a6879Sjfb 		if (root->cr_method == CVS_METHOD_LOCAL)
535dc6a6879Sjfb 			cvs_log(LP_WARN, "I know nothing about %s", diff_file);
536dc6a6879Sjfb 		else
537c710bc5aSjfb 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE,
538c710bc5aSjfb 			    CVS_FILE_NAME(cfp));
539dc6a6879Sjfb 		return (0);
54008f90673Sjfb 	}
54108f90673Sjfb 
542dc6a6879Sjfb 	entp = cvs_ent_getent(diff_file);
543dc6a6879Sjfb 	if (entp == NULL)
544dc6a6879Sjfb 		return (-1);
545dc6a6879Sjfb 
546dc6a6879Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
547dc6a6879Sjfb 		if (cvs_sendentry(root, entp) < 0) {
548dc6a6879Sjfb 			cvs_ent_free(entp);
54908f90673Sjfb 			return (-1);
55008f90673Sjfb 		}
55108f90673Sjfb 	}
55208f90673Sjfb 
553dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UPTODATE) {
554dc6a6879Sjfb 		if (root->cr_method != CVS_METHOD_LOCAL)
555c710bc5aSjfb 			cvs_sendreq(root, CVS_REQ_UNCHANGED,
556c710bc5aSjfb 			    CVS_FILE_NAME(cfp));
557dc6a6879Sjfb 		cvs_ent_free(entp);
55808f90673Sjfb 		return (0);
55908f90673Sjfb 	}
56008f90673Sjfb 
56108f90673Sjfb 	/* at this point, the file is modified */
562dc6a6879Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
563c710bc5aSjfb 		cvs_sendreq(root, CVS_REQ_MODIFIED, CVS_FILE_NAME(cfp));
564dc6a6879Sjfb 		cvs_sendfile(root, diff_file);
5653917c9bfSderaadt 	} else {
56608f90673Sjfb 		snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
567dc6a6879Sjfb 		    root->cr_dir, repo, diff_file, RCS_FILE_EXT);
56808f90673Sjfb 
56908f90673Sjfb 		rf = rcs_open(rcspath, RCS_MODE_READ);
570dc6a6879Sjfb 		if (rf == NULL) {
571dc6a6879Sjfb 			cvs_ent_free(entp);
57208f90673Sjfb 			return (-1);
573dc6a6879Sjfb 		}
57408f90673Sjfb 
575dc6a6879Sjfb 		cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file,
57608f90673Sjfb 		    RCS_DIFF_DIV, rcspath);
57708f90673Sjfb 
578dc6a6879Sjfb 		if (dap->rev1 == NULL)
57908f90673Sjfb 			r1 = entp->ce_rev;
58008f90673Sjfb 		else {
581*7f535ec4Sjfb 			if ((r1 = rcsnum_alloc()) == NULL) {
582*7f535ec4Sjfb 				cvs_ent_free(entp);
583*7f535ec4Sjfb 				return (-1);
584*7f535ec4Sjfb 			}
585dc6a6879Sjfb 			rcsnum_aton(dap->rev1, NULL, r1);
58608f90673Sjfb 		}
58708f90673Sjfb 
588dc6a6879Sjfb 		cvs_printf("retrieving revision %s\n",
58908f90673Sjfb 		    rcsnum_tostr(r1, buf, sizeof(buf)));
59008f90673Sjfb 		b1 = rcs_getrev(rf, r1);
59108f90673Sjfb 
592*7f535ec4Sjfb 		if (r1 != entp->ce_rev)
593*7f535ec4Sjfb 			rcsnum_free(r1);
594*7f535ec4Sjfb 
595dc6a6879Sjfb 		if (dap->rev2 != NULL) {
596dc6a6879Sjfb 			cvs_printf("retrieving revision %s\n", dap->rev2);
597*7f535ec4Sjfb 			if ((r2 = rcsnum_alloc()) == NULL) {
598*7f535ec4Sjfb 				cvs_ent_free(entp);
599*7f535ec4Sjfb 				return (-1);
600*7f535ec4Sjfb 			}
601dc6a6879Sjfb 			rcsnum_aton(dap->rev2, NULL, r2);
60208f90673Sjfb 			b2 = rcs_getrev(rf, r2);
603*7f535ec4Sjfb 			rcsnum_free(r2);
6043917c9bfSderaadt 		} else {
605dc6a6879Sjfb 			b2 = cvs_buf_load(diff_file, BUF_AUTOEXT);
60608f90673Sjfb 		}
60708f90673Sjfb 
608dc6a6879Sjfb 		rcs_close(rf);
609dc6a6879Sjfb 
610f5638424Sjfb 		printf("%s", diffargs);
611f5638424Sjfb 		printf(" -r%s", buf);
612dc6a6879Sjfb 		if (dap->rev2 != NULL)
613dc6a6879Sjfb 			printf(" -r%s", dap->rev2);
614dc6a6879Sjfb 		printf(" %s\n", diff_file);
615946f6157Sdjm 		strlcpy(path_tmp1, "/tmp/diff1.XXXXXXXXXX", sizeof(path_tmp1));
616*7f535ec4Sjfb 		if (cvs_buf_write_stmp(b1, path_tmp1, 0600) == -1) {
617*7f535ec4Sjfb 			cvs_buf_free(b1);
618*7f535ec4Sjfb 			cvs_buf_free(b2);
619946f6157Sdjm 			return (-1);
620*7f535ec4Sjfb 		}
621*7f535ec4Sjfb 		cvs_buf_free(b1);
622*7f535ec4Sjfb 
62369853e40Sjfb 		strlcpy(path_tmp2, "/tmp/diff2.XXXXXXXXXX", sizeof(path_tmp2));
624946f6157Sdjm 		if (cvs_buf_write_stmp(b2, path_tmp2, 0600) == -1) {
625*7f535ec4Sjfb 			cvs_buf_free(b2);
626946f6157Sdjm 			(void)unlink(path_tmp1);
627946f6157Sdjm 			return (-1);
628946f6157Sdjm 		}
629*7f535ec4Sjfb 		cvs_buf_free(b2);
630*7f535ec4Sjfb 
631946f6157Sdjm 		cvs_diffreg(path_tmp1, path_tmp2);
632946f6157Sdjm 		(void)unlink(path_tmp1);
633946f6157Sdjm 		(void)unlink(path_tmp2);
63408f90673Sjfb 	}
63508f90673Sjfb 
636dc6a6879Sjfb 	cvs_ent_free(entp);
63708f90673Sjfb 	return (0);
63808f90673Sjfb }
63908f90673Sjfb 
64008f90673Sjfb 
64108f90673Sjfb int
64208f90673Sjfb cvs_diffreg(const char *file1, const char *file2)
64308f90673Sjfb {
64408f90673Sjfb 	FILE *f1, *f2;
64508f90673Sjfb 	int i, rval;
646*7f535ec4Sjfb 	void *tmp;
64708f90673Sjfb 
64808f90673Sjfb 	f1 = f2 = NULL;
64908f90673Sjfb 	rval = D_SAME;
65008f90673Sjfb 	anychange = 0;
65108f90673Sjfb 	lastline = 0;
65208f90673Sjfb 	lastmatchline = 0;
65308f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
65408f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
65508f90673Sjfb 
65608f90673Sjfb 	f1 = fopen(file1, "r");
65708f90673Sjfb 	if (f1 == NULL) {
65808f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
65908f90673Sjfb 		status |= 2;
66008f90673Sjfb 		goto closem;
66108f90673Sjfb 	}
66208f90673Sjfb 
66308f90673Sjfb 	f2 = fopen(file2, "r");
66408f90673Sjfb 	if (f2 == NULL) {
66508f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
66608f90673Sjfb 		status |= 2;
66708f90673Sjfb 		goto closem;
66808f90673Sjfb 	}
66908f90673Sjfb 
67008f90673Sjfb 	switch (files_differ(f1, f2)) {
67108f90673Sjfb 	case 0:
67208f90673Sjfb 		goto closem;
67308f90673Sjfb 	case 1:
67408f90673Sjfb 		break;
67508f90673Sjfb 	default:
67608f90673Sjfb 		/* error */
67708f90673Sjfb 		status |= 2;
67808f90673Sjfb 		goto closem;
67908f90673Sjfb 	}
68008f90673Sjfb 
68108f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
68208f90673Sjfb 		rval = D_BINARY;
68308f90673Sjfb 		status |= 1;
68408f90673Sjfb 		goto closem;
68508f90673Sjfb 	}
686*7f535ec4Sjfb 	if ((prepare(0, f1, stb1.st_size) < 0) ||
687*7f535ec4Sjfb 	    (prepare(1, f2, stb2.st_size) < 0)) {
688*7f535ec4Sjfb 		status |= 2;
689*7f535ec4Sjfb 		goto closem;
690*7f535ec4Sjfb 	}
69108f90673Sjfb 	prune();
69208f90673Sjfb 	sort(sfile[0], slen[0]);
69308f90673Sjfb 	sort(sfile[1], slen[1]);
69408f90673Sjfb 
69508f90673Sjfb 	member = (int *)file[1];
69608f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
697*7f535ec4Sjfb 	if ((tmp = realloc(member, (slen[1] + 2) * sizeof(int))) == NULL) {
698*7f535ec4Sjfb 		status |= 2;
699*7f535ec4Sjfb 		goto closem;
700*7f535ec4Sjfb 	}
701*7f535ec4Sjfb 	member = (int *)tmp;
70208f90673Sjfb 
70308f90673Sjfb 	class = (int *)file[0];
70408f90673Sjfb 	unsort(sfile[0], slen[0], class);
705*7f535ec4Sjfb 	if ((tmp = realloc(class, (slen[0] + 2) * sizeof(int))) == NULL) {
706*7f535ec4Sjfb 		status |= 2;
707*7f535ec4Sjfb 		goto closem;
708*7f535ec4Sjfb 	}
709*7f535ec4Sjfb 	class = (int *)tmp;
71008f90673Sjfb 
711*7f535ec4Sjfb 	if ((klist = malloc((slen[0] + 2) * sizeof(int))) == NULL) {
712*7f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate klist");
713*7f535ec4Sjfb 		status |= 2;
714*7f535ec4Sjfb 		goto closem;
715*7f535ec4Sjfb 	}
71608f90673Sjfb 	clen = 0;
71708f90673Sjfb 	clistlen = 100;
718*7f535ec4Sjfb 	if ((clist = malloc(clistlen * sizeof(cand))) == NULL) {
719*7f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate clist");
720*7f535ec4Sjfb 		status |= 2;
721*7f535ec4Sjfb 		goto closem;
722*7f535ec4Sjfb 	}
72308f90673Sjfb 	i = stone(class, slen[0], member, klist);
72408f90673Sjfb 	free(member);
72508f90673Sjfb 	free(class);
72608f90673Sjfb 
72708f90673Sjfb 	J = realloc(J, (len[0] + 2) * sizeof(int));
72808f90673Sjfb 	unravel(klist[i]);
72908f90673Sjfb 	free(clist);
73008f90673Sjfb 	free(klist);
73108f90673Sjfb 
73208f90673Sjfb 	ixold = realloc(ixold, (len[0] + 2) * sizeof(long));
73308f90673Sjfb 	ixnew = realloc(ixnew, (len[1] + 2) * sizeof(long));
73408f90673Sjfb 	check(f1, f2);
73508f90673Sjfb 	output(file1, f1, file2, f2);
73608f90673Sjfb 
73708f90673Sjfb closem:
73808f90673Sjfb 	if (anychange) {
73908f90673Sjfb 		status |= 1;
74008f90673Sjfb 		if (rval == D_SAME)
74108f90673Sjfb 			rval = D_DIFFER;
74208f90673Sjfb 	}
74308f90673Sjfb 	if (f1 != NULL)
74408f90673Sjfb 		fclose(f1);
74508f90673Sjfb 	if (f2 != NULL)
74608f90673Sjfb 		fclose(f2);
747*7f535ec4Sjfb 
74808f90673Sjfb 	return (rval);
74908f90673Sjfb }
75008f90673Sjfb 
75108f90673Sjfb /*
75208f90673Sjfb  * Check to see if the given files differ.
75308f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
75408f90673Sjfb  * XXX - could use code from cmp(1) [faster]
75508f90673Sjfb  */
75608f90673Sjfb static int
75708f90673Sjfb files_differ(FILE *f1, FILE *f2)
75808f90673Sjfb {
75908f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
76008f90673Sjfb 	size_t i, j;
76108f90673Sjfb 
76208f90673Sjfb 	if (stb1.st_size != stb2.st_size)
76308f90673Sjfb 		return (1);
76408f90673Sjfb 	for (;;) {
76508f90673Sjfb 		i = fread(buf1, 1, sizeof(buf1), f1);
76608f90673Sjfb 		j = fread(buf2, 1, sizeof(buf2), f2);
76708f90673Sjfb 		if (i != j)
76808f90673Sjfb 			return (1);
76908f90673Sjfb 		if (i == 0 && j == 0) {
77008f90673Sjfb 			if (ferror(f1) || ferror(f2))
77108f90673Sjfb 				return (1);
77208f90673Sjfb 			return (0);
77308f90673Sjfb 		}
77408f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
77508f90673Sjfb 			return (1);
77608f90673Sjfb 	}
77708f90673Sjfb }
77808f90673Sjfb 
77908f90673Sjfb 
78008f90673Sjfb char *
7812a0de57dSjfb splice(char *dir, char *filename)
78208f90673Sjfb {
78308f90673Sjfb 	char *tail, *buf;
78408f90673Sjfb 
7852a0de57dSjfb 	if ((tail = strrchr(filename, '/')) == NULL)
7862a0de57dSjfb 		tail = filename;
78708f90673Sjfb 	else
78808f90673Sjfb 		tail++;
78908f90673Sjfb 	asprintf(&buf, "%s/%s", dir, tail);
79008f90673Sjfb 	return (buf);
79108f90673Sjfb }
79208f90673Sjfb 
793*7f535ec4Sjfb static int
79408f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
79508f90673Sjfb {
796*7f535ec4Sjfb 	void *tmp;
79708f90673Sjfb 	struct line *p;
79808f90673Sjfb 	int j, h;
79908f90673Sjfb 	size_t sz;
80008f90673Sjfb 
80108f90673Sjfb 	rewind(fd);
80208f90673Sjfb 
803c48da046Sxsa 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
80408f90673Sjfb 	if (sz < 100)
80508f90673Sjfb 		sz = 100;
80608f90673Sjfb 
807*7f535ec4Sjfb 	p = (struct line *)malloc((sz + 3) * sizeof(struct line));
808*7f535ec4Sjfb 	if (p == NULL) {
809*7f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to prepare line array");
810*7f535ec4Sjfb 		return (-1);
811*7f535ec4Sjfb 	}
81208f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
81308f90673Sjfb 		if (j == (int)sz) {
81408f90673Sjfb 			sz = sz * 3 / 2;
815*7f535ec4Sjfb 			tmp = realloc(p, (sz + 3) * sizeof(struct line));
816*7f535ec4Sjfb 			if (tmp == NULL) {
817*7f535ec4Sjfb 				cvs_log(LP_ERRNO, "failed to grow line array");
818*7f535ec4Sjfb 				free(p);
819*7f535ec4Sjfb 				return (-1);
820*7f535ec4Sjfb 			}
821*7f535ec4Sjfb 			p = (struct line *)tmp;
82208f90673Sjfb 		}
82308f90673Sjfb 		p[++j].value = h;
82408f90673Sjfb 	}
82508f90673Sjfb 	len[i] = j;
82608f90673Sjfb 	file[i] = p;
827*7f535ec4Sjfb 
828*7f535ec4Sjfb 	return (0);
82908f90673Sjfb }
83008f90673Sjfb 
83108f90673Sjfb static void
83208f90673Sjfb prune(void)
83308f90673Sjfb {
83408f90673Sjfb 	int i, j;
83508f90673Sjfb 
83608f90673Sjfb 	for (pref = 0; pref < len[0] && pref < len[1] &&
83708f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
83808f90673Sjfb 	    pref++)
83908f90673Sjfb 		;
84008f90673Sjfb 	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
84108f90673Sjfb 	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
84208f90673Sjfb 	    suff++)
84308f90673Sjfb 		;
84408f90673Sjfb 	for (j = 0; j < 2; j++) {
84508f90673Sjfb 		sfile[j] = file[j] + pref;
84608f90673Sjfb 		slen[j] = len[j] - pref - suff;
84708f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
84808f90673Sjfb 			sfile[j][i].serial = i;
84908f90673Sjfb 	}
85008f90673Sjfb }
85108f90673Sjfb 
85208f90673Sjfb static void
85308f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
85408f90673Sjfb {
85508f90673Sjfb 	int i, j;
85608f90673Sjfb 
85708f90673Sjfb 	i = j = 1;
85808f90673Sjfb 	while (i <= n && j <= m) {
85908f90673Sjfb 		if (a[i].value < b[j].value)
86008f90673Sjfb 			a[i++].value = 0;
86108f90673Sjfb 		else if (a[i].value == b[j].value)
86208f90673Sjfb 			a[i++].value = j;
86308f90673Sjfb 		else
86408f90673Sjfb 			j++;
86508f90673Sjfb 	}
86608f90673Sjfb 	while (i <= n)
86708f90673Sjfb 		a[i++].value = 0;
86808f90673Sjfb 	b[m + 1].value = 0;
86908f90673Sjfb 	j = 0;
87008f90673Sjfb 	while (++j <= m) {
87108f90673Sjfb 		c[j] = -b[j].serial;
87208f90673Sjfb 		while (b[j + 1].value == b[j].value) {
87308f90673Sjfb 			j++;
87408f90673Sjfb 			c[j] = b[j].serial;
87508f90673Sjfb 		}
87608f90673Sjfb 	}
87708f90673Sjfb 	c[j] = -1;
87808f90673Sjfb }
87908f90673Sjfb 
88008f90673Sjfb /* Code taken from ping.c */
88108f90673Sjfb static int
88208f90673Sjfb isqrt(int n)
88308f90673Sjfb {
88408f90673Sjfb 	int y, x = 1;
88508f90673Sjfb 
88608f90673Sjfb 	if (n == 0)
88708f90673Sjfb 		return (0);
88808f90673Sjfb 
88908f90673Sjfb 	do { /* newton was a stinker */
89008f90673Sjfb 		y = x;
89108f90673Sjfb 		x = n / x;
89208f90673Sjfb 		x += y;
89308f90673Sjfb 		x /= 2;
89408f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
89508f90673Sjfb 
89608f90673Sjfb 	return (x);
89708f90673Sjfb }
89808f90673Sjfb 
89908f90673Sjfb static int
90008f90673Sjfb stone(int *a, int n, int *b, int *c)
90108f90673Sjfb {
90208f90673Sjfb 	int i, k, y, j, l;
90308f90673Sjfb 	int oldc, tc, oldl;
90408f90673Sjfb 	u_int numtries;
90508f90673Sjfb 
906cc649edbSjfb 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
907cc649edbSjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
90808f90673Sjfb 
90908f90673Sjfb 	k = 0;
91008f90673Sjfb 	c[0] = newcand(0, 0, 0);
91108f90673Sjfb 	for (i = 1; i <= n; i++) {
91208f90673Sjfb 		j = a[i];
91308f90673Sjfb 		if (j == 0)
91408f90673Sjfb 			continue;
91508f90673Sjfb 		y = -b[j];
91608f90673Sjfb 		oldl = 0;
91708f90673Sjfb 		oldc = c[0];
91808f90673Sjfb 		numtries = 0;
91908f90673Sjfb 		do {
92008f90673Sjfb 			if (y <= clist[oldc].y)
92108f90673Sjfb 				continue;
92208f90673Sjfb 			l = search(c, k, y);
92308f90673Sjfb 			if (l != oldl + 1)
92408f90673Sjfb 				oldc = c[l - 1];
92508f90673Sjfb 			if (l <= k) {
92608f90673Sjfb 				if (clist[c[l]].y <= y)
92708f90673Sjfb 					continue;
92808f90673Sjfb 				tc = c[l];
92908f90673Sjfb 				c[l] = newcand(i, y, oldc);
93008f90673Sjfb 				oldc = tc;
93108f90673Sjfb 				oldl = l;
93208f90673Sjfb 				numtries++;
93308f90673Sjfb 			} else {
93408f90673Sjfb 				c[l] = newcand(i, y, oldc);
93508f90673Sjfb 				k++;
93608f90673Sjfb 				break;
93708f90673Sjfb 			}
93808f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
93908f90673Sjfb 	}
94008f90673Sjfb 	return (k);
94108f90673Sjfb }
94208f90673Sjfb 
94308f90673Sjfb static int
94408f90673Sjfb newcand(int x, int y, int pred)
94508f90673Sjfb {
94608f90673Sjfb 	struct cand *q;
94708f90673Sjfb 
94808f90673Sjfb 	if (clen == clistlen) {
94908f90673Sjfb 		clistlen = clistlen * 11 / 10;
95008f90673Sjfb 		clist = realloc(clist, clistlen * sizeof(cand));
951*7f535ec4Sjfb 		if (clist == NULL) {
952*7f535ec4Sjfb 			cvs_log(LP_ERRNO, "failed to resize clist");
953*7f535ec4Sjfb 			return (-1);
954*7f535ec4Sjfb 		}
95508f90673Sjfb 	}
95608f90673Sjfb 	q = clist + clen;
95708f90673Sjfb 	q->x = x;
95808f90673Sjfb 	q->y = y;
95908f90673Sjfb 	q->pred = pred;
96008f90673Sjfb 	return (clen++);
96108f90673Sjfb }
96208f90673Sjfb 
96308f90673Sjfb static int
96408f90673Sjfb search(int *c, int k, int y)
96508f90673Sjfb {
96608f90673Sjfb 	int i, j, l, t;
96708f90673Sjfb 
96808f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
96908f90673Sjfb 		return (k + 1);
97008f90673Sjfb 	i = 0;
97108f90673Sjfb 	j = k + 1;
97208f90673Sjfb 	while (1) {
97308f90673Sjfb 		l = i + j;
97408f90673Sjfb 		if ((l >>= 1) <= i)
97508f90673Sjfb 			break;
97608f90673Sjfb 		t = clist[c[l]].y;
97708f90673Sjfb 		if (t > y)
97808f90673Sjfb 			j = l;
97908f90673Sjfb 		else if (t < y)
98008f90673Sjfb 			i = l;
98108f90673Sjfb 		else
98208f90673Sjfb 			return (l);
98308f90673Sjfb 	}
98408f90673Sjfb 	return (l + 1);
98508f90673Sjfb }
98608f90673Sjfb 
98708f90673Sjfb static void
98808f90673Sjfb unravel(int p)
98908f90673Sjfb {
99008f90673Sjfb 	struct cand *q;
99108f90673Sjfb 	int i;
99208f90673Sjfb 
99308f90673Sjfb 	for (i = 0; i <= len[0]; i++)
99408f90673Sjfb 		J[i] = i <= pref ? i :
99508f90673Sjfb 		    i > len[0] - suff ? i + len[1] - len[0] : 0;
99608f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
99708f90673Sjfb 		J[q->x + pref] = q->y + pref;
99808f90673Sjfb }
99908f90673Sjfb 
100008f90673Sjfb /*
100108f90673Sjfb  * Check does double duty:
100208f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
100308f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
100408f90673Sjfb  *  2.  collect random access indexes to the two files
100508f90673Sjfb  */
100608f90673Sjfb static void
100708f90673Sjfb check(FILE *f1, FILE *f2)
100808f90673Sjfb {
100908f90673Sjfb 	int i, j, jackpot, c, d;
101008f90673Sjfb 	long ctold, ctnew;
101108f90673Sjfb 
101208f90673Sjfb 	rewind(f1);
101308f90673Sjfb 	rewind(f2);
101408f90673Sjfb 	j = 1;
101508f90673Sjfb 	ixold[0] = ixnew[0] = 0;
101608f90673Sjfb 	jackpot = 0;
101708f90673Sjfb 	ctold = ctnew = 0;
101808f90673Sjfb 	for (i = 1; i <= len[0]; i++) {
101908f90673Sjfb 		if (J[i] == 0) {
102008f90673Sjfb 			ixold[i] = ctold += skipline(f1);
102108f90673Sjfb 			continue;
102208f90673Sjfb 		}
102308f90673Sjfb 		while (j < J[i]) {
102408f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
102508f90673Sjfb 			j++;
102608f90673Sjfb 		}
102708f90673Sjfb 		if (bflag || wflag || iflag) {
102808f90673Sjfb 			for (;;) {
102908f90673Sjfb 				c = getc(f1);
103008f90673Sjfb 				d = getc(f2);
103108f90673Sjfb 				/*
103208f90673Sjfb 				 * GNU diff ignores a missing newline
103308f90673Sjfb 				 * in one file if bflag || wflag.
103408f90673Sjfb 				 */
103508f90673Sjfb 				if ((bflag || wflag) &&
103608f90673Sjfb 				    ((c == EOF && d == '\n') ||
103708f90673Sjfb 				    (c == '\n' && d == EOF))) {
103808f90673Sjfb 					break;
103908f90673Sjfb 				}
104008f90673Sjfb 				ctold++;
104108f90673Sjfb 				ctnew++;
104208f90673Sjfb 				if (bflag && isspace(c) && isspace(d)) {
104308f90673Sjfb 					do {
104408f90673Sjfb 						if (c == '\n')
104508f90673Sjfb 							break;
104608f90673Sjfb 						ctold++;
104708f90673Sjfb 					} while (isspace(c = getc(f1)));
104808f90673Sjfb 					do {
104908f90673Sjfb 						if (d == '\n')
105008f90673Sjfb 							break;
105108f90673Sjfb 						ctnew++;
105208f90673Sjfb 					} while (isspace(d = getc(f2)));
105308f90673Sjfb 				} else if (wflag) {
105408f90673Sjfb 					while (isspace(c) && c != '\n') {
105508f90673Sjfb 						c = getc(f1);
105608f90673Sjfb 						ctold++;
105708f90673Sjfb 					}
105808f90673Sjfb 					while (isspace(d) && d != '\n') {
105908f90673Sjfb 						d = getc(f2);
106008f90673Sjfb 						ctnew++;
106108f90673Sjfb 					}
106208f90673Sjfb 				}
106308f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
106408f90673Sjfb 					jackpot++;
106508f90673Sjfb 					J[i] = 0;
106608f90673Sjfb 					if (c != '\n' && c != EOF)
106708f90673Sjfb 						ctold += skipline(f1);
106808f90673Sjfb 					if (d != '\n' && c != EOF)
106908f90673Sjfb 						ctnew += skipline(f2);
107008f90673Sjfb 					break;
107108f90673Sjfb 				}
107208f90673Sjfb 				if (c == '\n' || c == EOF)
107308f90673Sjfb 					break;
107408f90673Sjfb 			}
107508f90673Sjfb 		} else {
107608f90673Sjfb 			for (;;) {
107708f90673Sjfb 				ctold++;
107808f90673Sjfb 				ctnew++;
107908f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
108008f90673Sjfb 					/* jackpot++; */
108108f90673Sjfb 					J[i] = 0;
108208f90673Sjfb 					if (c != '\n' && c != EOF)
108308f90673Sjfb 						ctold += skipline(f1);
108408f90673Sjfb 					if (d != '\n' && c != EOF)
108508f90673Sjfb 						ctnew += skipline(f2);
108608f90673Sjfb 					break;
108708f90673Sjfb 				}
108808f90673Sjfb 				if (c == '\n' || c == EOF)
108908f90673Sjfb 					break;
109008f90673Sjfb 			}
109108f90673Sjfb 		}
109208f90673Sjfb 		ixold[i] = ctold;
109308f90673Sjfb 		ixnew[j] = ctnew;
109408f90673Sjfb 		j++;
109508f90673Sjfb 	}
109608f90673Sjfb 	for (; j <= len[1]; j++)
109708f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
109808f90673Sjfb 	/*
109908f90673Sjfb 	 * if (jackpot)
110008f90673Sjfb 	 *	fprintf(stderr, "jackpot\n");
110108f90673Sjfb 	 */
110208f90673Sjfb }
110308f90673Sjfb 
110408f90673Sjfb /* shellsort CACM #201 */
110508f90673Sjfb static void
110608f90673Sjfb sort(struct line *a, int n)
110708f90673Sjfb {
110808f90673Sjfb 	struct line *ai, *aim, w;
110908f90673Sjfb 	int j, m = 0, k;
111008f90673Sjfb 
111108f90673Sjfb 	if (n == 0)
111208f90673Sjfb 		return;
111308f90673Sjfb 	for (j = 1; j <= n; j *= 2)
111408f90673Sjfb 		m = 2 * j - 1;
111508f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
111608f90673Sjfb 		k = n - m;
111708f90673Sjfb 		for (j = 1; j <= k; j++) {
111808f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
111908f90673Sjfb 				aim = &ai[m];
112008f90673Sjfb 				if (aim < ai)
112108f90673Sjfb 					break;	/* wraparound */
112208f90673Sjfb 				if (aim->value > ai[0].value ||
112308f90673Sjfb 				    (aim->value == ai[0].value &&
112408f90673Sjfb 					aim->serial > ai[0].serial))
112508f90673Sjfb 					break;
112608f90673Sjfb 				w.value = ai[0].value;
112708f90673Sjfb 				ai[0].value = aim->value;
112808f90673Sjfb 				aim->value = w.value;
112908f90673Sjfb 				w.serial = ai[0].serial;
113008f90673Sjfb 				ai[0].serial = aim->serial;
113108f90673Sjfb 				aim->serial = w.serial;
113208f90673Sjfb 			}
113308f90673Sjfb 		}
113408f90673Sjfb 	}
113508f90673Sjfb }
113608f90673Sjfb 
113708f90673Sjfb static void
113808f90673Sjfb unsort(struct line *f, int l, int *b)
113908f90673Sjfb {
114008f90673Sjfb 	int *a, i;
114108f90673Sjfb 
1142*7f535ec4Sjfb 	if ((a = (int *)malloc((l + 1) * sizeof(int))) == NULL) {
1143*7f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate sort array");
1144*7f535ec4Sjfb 		return;
1145*7f535ec4Sjfb 	}
114608f90673Sjfb 	for (i = 1; i <= l; i++)
114708f90673Sjfb 		a[f[i].serial] = f[i].value;
114808f90673Sjfb 	for (i = 1; i <= l; i++)
114908f90673Sjfb 		b[i] = a[i];
115008f90673Sjfb 	free(a);
115108f90673Sjfb }
115208f90673Sjfb 
115308f90673Sjfb static int
115408f90673Sjfb skipline(FILE *f)
115508f90673Sjfb {
115608f90673Sjfb 	int i, c;
115708f90673Sjfb 
115808f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
115908f90673Sjfb 		continue;
116008f90673Sjfb 	return (i);
116108f90673Sjfb }
116208f90673Sjfb 
116308f90673Sjfb static void
116408f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2)
116508f90673Sjfb {
116608f90673Sjfb 	int m, i0, i1, j0, j1;
116708f90673Sjfb 
116808f90673Sjfb 	rewind(f1);
116908f90673Sjfb 	rewind(f2);
117008f90673Sjfb 	m = len[0];
117108f90673Sjfb 	J[0] = 0;
117208f90673Sjfb 	J[m + 1] = len[1] + 1;
117308f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
117408f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
117508f90673Sjfb 			i0++;
117608f90673Sjfb 		j0 = J[i0 - 1] + 1;
117708f90673Sjfb 		i1 = i0 - 1;
117808f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
117908f90673Sjfb 			i1++;
118008f90673Sjfb 		j1 = J[i1 + 1] - 1;
118108f90673Sjfb 		J[i1] = j1;
118208f90673Sjfb 		change(file1, f1, file2, f2, i0, i1, j0, j1);
118308f90673Sjfb 	}
118408f90673Sjfb 	if (m == 0)
118508f90673Sjfb 		change(file1, f1, file2, f2, 1, 0, 1, len[1]);
118608f90673Sjfb 	if (format == D_IFDEF) {
118708f90673Sjfb 		for (;;) {
118808f90673Sjfb #define	c i0
118908f90673Sjfb 			if ((c = getc(f1)) == EOF)
119008f90673Sjfb 				return;
119108f90673Sjfb 			putchar(c);
119208f90673Sjfb 		}
119308f90673Sjfb #undef c
119408f90673Sjfb 	}
119508f90673Sjfb 	if (anychange != 0) {
119608f90673Sjfb 		if (format == D_CONTEXT)
119708f90673Sjfb 			dump_context_vec(f1, f2);
119808f90673Sjfb 		else if (format == D_UNIFIED)
119908f90673Sjfb 			dump_unified_vec(f1, f2);
120008f90673Sjfb 	}
120108f90673Sjfb }
120208f90673Sjfb 
120308f90673Sjfb static __inline void
120408f90673Sjfb range(int a, int b, char *separator)
120508f90673Sjfb {
120608f90673Sjfb 	printf("%d", a > b ? b : a);
120708f90673Sjfb 	if (a < b)
120808f90673Sjfb 		printf("%s%d", separator, b);
120908f90673Sjfb }
121008f90673Sjfb 
121108f90673Sjfb static __inline void
121208f90673Sjfb uni_range(int a, int b)
121308f90673Sjfb {
121408f90673Sjfb 	if (a < b)
121508f90673Sjfb 		printf("%d,%d", a, b - a + 1);
121608f90673Sjfb 	else if (a == b)
121708f90673Sjfb 		printf("%d", b);
121808f90673Sjfb 	else
121908f90673Sjfb 		printf("%d,0", b);
122008f90673Sjfb }
122108f90673Sjfb 
122208f90673Sjfb static char *
12232a0de57dSjfb preadline(int fd, size_t rlen, off_t off)
122408f90673Sjfb {
122508f90673Sjfb 	char *line;
122608f90673Sjfb 	ssize_t nr;
122708f90673Sjfb 
12282a0de57dSjfb 	line = malloc(rlen + 1);
122908f90673Sjfb 	if (line == NULL) {
123008f90673Sjfb 		cvs_log(LP_ERRNO, "failed to allocate line");
123108f90673Sjfb 		return (NULL);
123208f90673Sjfb 	}
12332a0de57dSjfb 	if ((nr = pread(fd, line, rlen, off)) < 0) {
123408f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
123508f90673Sjfb 		return (NULL);
123608f90673Sjfb 	}
123708f90673Sjfb 	line[nr] = '\0';
123808f90673Sjfb 	return (line);
123908f90673Sjfb }
124008f90673Sjfb 
124108f90673Sjfb static int
124208f90673Sjfb ignoreline(char *line)
124308f90673Sjfb {
124408f90673Sjfb 	int ret;
124508f90673Sjfb 
124608f90673Sjfb 	ret = regexec(&ignore_re, line, 0, NULL, 0);
124708f90673Sjfb 	free(line);
124808f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
124908f90673Sjfb }
125008f90673Sjfb 
125108f90673Sjfb /*
125208f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
125308f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
125408f90673Sjfb  * are no lines in the from file involved and this means that there were
125508f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
125608f90673Sjfb  * lines missing from the to file.
125708f90673Sjfb  */
125808f90673Sjfb static void
125908f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2,
126008f90673Sjfb 	int a, int b, int c, int d)
126108f90673Sjfb {
126208f90673Sjfb 	static size_t max_context = 64;
126308f90673Sjfb 	int i;
126408f90673Sjfb 
126508f90673Sjfb 	if (format != D_IFDEF && a > b && c > d)
126608f90673Sjfb 		return;
126708f90673Sjfb 	if (ignore_pats != NULL) {
126808f90673Sjfb 		char *line;
126908f90673Sjfb 		/*
127008f90673Sjfb 		 * All lines in the change, insert, or delete must
127108f90673Sjfb 		 * match an ignore pattern for the change to be
127208f90673Sjfb 		 * ignored.
127308f90673Sjfb 		 */
127408f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
127508f90673Sjfb 			for (i = a; i <= b; i++) {
127608f90673Sjfb 				line = preadline(fileno(f1),
127708f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
127808f90673Sjfb 				if (!ignoreline(line))
127908f90673Sjfb 					goto proceed;
128008f90673Sjfb 			}
128108f90673Sjfb 		}
128208f90673Sjfb 		if (a > b || c <= d) {	/* Changes and inserts. */
128308f90673Sjfb 			for (i = c; i <= d; i++) {
128408f90673Sjfb 				line = preadline(fileno(f2),
128508f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
128608f90673Sjfb 				if (!ignoreline(line))
128708f90673Sjfb 					goto proceed;
128808f90673Sjfb 			}
128908f90673Sjfb 		}
129008f90673Sjfb 		return;
129108f90673Sjfb 	}
129208f90673Sjfb proceed:
129308f90673Sjfb 	if (format == D_CONTEXT || format == D_UNIFIED) {
129408f90673Sjfb 		/*
129508f90673Sjfb 		 * Allocate change records as needed.
129608f90673Sjfb 		 */
129708f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
129808f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
129908f90673Sjfb 			max_context <<= 1;
130008f90673Sjfb 			context_vec_start = realloc(context_vec_start,
130108f90673Sjfb 			    max_context * sizeof(struct context_vec));
130208f90673Sjfb 			context_vec_end = context_vec_start + max_context;
130308f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
130408f90673Sjfb 		}
130508f90673Sjfb 		if (anychange == 0) {
130608f90673Sjfb 			/*
130708f90673Sjfb 			 * Print the context/unidiff header first time through.
130808f90673Sjfb 			 */
130908f90673Sjfb 			printf("%s %s	%s",
131008f90673Sjfb 			    format == D_CONTEXT ? "***" : "---", diff_file,
131108f90673Sjfb 			    ctime(&stb1.st_mtime));
131208f90673Sjfb 			printf("%s %s	%s",
131308f90673Sjfb 			    format == D_CONTEXT ? "---" : "+++", diff_file,
131408f90673Sjfb 			    ctime(&stb2.st_mtime));
131508f90673Sjfb 			anychange = 1;
131608f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
131708f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
131808f90673Sjfb 			/*
131908f90673Sjfb 			 * If this change is more than 'context' lines from the
132008f90673Sjfb 			 * previous change, dump the record and reset it.
132108f90673Sjfb 			 */
132208f90673Sjfb 			if (format == D_CONTEXT)
132308f90673Sjfb 				dump_context_vec(f1, f2);
132408f90673Sjfb 			else
132508f90673Sjfb 				dump_unified_vec(f1, f2);
132608f90673Sjfb 		}
132708f90673Sjfb 		context_vec_ptr++;
132808f90673Sjfb 		context_vec_ptr->a = a;
132908f90673Sjfb 		context_vec_ptr->b = b;
133008f90673Sjfb 		context_vec_ptr->c = c;
133108f90673Sjfb 		context_vec_ptr->d = d;
133208f90673Sjfb 		return;
133308f90673Sjfb 	}
133408f90673Sjfb 	if (anychange == 0)
133508f90673Sjfb 		anychange = 1;
133608f90673Sjfb 	switch (format) {
133708f90673Sjfb 	case D_BRIEF:
133808f90673Sjfb 		return;
133908f90673Sjfb 	case D_NORMAL:
134008f90673Sjfb 		range(a, b, ",");
134108f90673Sjfb 		putchar(a > b ? 'a' : c > d ? 'd' : 'c');
134208f90673Sjfb 		if (format == D_NORMAL)
134308f90673Sjfb 			range(c, d, ",");
134408f90673Sjfb 		putchar('\n');
134508f90673Sjfb 		break;
134608f90673Sjfb 	}
134708f90673Sjfb 	if (format == D_NORMAL || format == D_IFDEF) {
134808f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
134908f90673Sjfb 		if (a <= b && c <= d && format == D_NORMAL)
135008f90673Sjfb 			puts("---");
135108f90673Sjfb 	}
135208f90673Sjfb 	i = fetch(ixnew, c, d, f2, format == D_NORMAL ? '>' : '\0', 0);
135308f90673Sjfb 	if (inifdef) {
135408f90673Sjfb 		printf("#endif /* %s */\n", ifdefname);
135508f90673Sjfb 		inifdef = 0;
135608f90673Sjfb 	}
135708f90673Sjfb }
135808f90673Sjfb 
135908f90673Sjfb static int
136008f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
136108f90673Sjfb {
136208f90673Sjfb 	int i, j, c, lastc, col, nc;
136308f90673Sjfb 
136408f90673Sjfb 	/*
136508f90673Sjfb 	 * When doing #ifdef's, copy down to current line
136608f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
136708f90673Sjfb 	 */
136808f90673Sjfb 	if (format == D_IFDEF && oldfile) {
136908f90673Sjfb 		long curpos = ftell(lb);
137008f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
137108f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
137208f90673Sjfb 		for (i = 0; i < nc; i++)
137308f90673Sjfb 			putchar(getc(lb));
137408f90673Sjfb 	}
137508f90673Sjfb 	if (a > b)
137608f90673Sjfb 		return (0);
137708f90673Sjfb 	if (format == D_IFDEF) {
137808f90673Sjfb 		if (inifdef) {
137908f90673Sjfb 			printf("#else /* %s%s */\n",
138008f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
138108f90673Sjfb 		} else {
138208f90673Sjfb 			if (oldfile)
138308f90673Sjfb 				printf("#ifndef %s\n", ifdefname);
138408f90673Sjfb 			else
138508f90673Sjfb 				printf("#ifdef %s\n", ifdefname);
138608f90673Sjfb 		}
138708f90673Sjfb 		inifdef = 1 + oldfile;
138808f90673Sjfb 	}
138908f90673Sjfb 	for (i = a; i <= b; i++) {
139008f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
139108f90673Sjfb 		nc = f[i] - f[i - 1];
139208f90673Sjfb 		if (format != D_IFDEF && ch != '\0') {
139308f90673Sjfb 			putchar(ch);
139408f90673Sjfb 			if (Tflag && (format == D_NORMAL || format == D_CONTEXT
139508f90673Sjfb 			    || format == D_UNIFIED))
139608f90673Sjfb 				putchar('\t');
139708f90673Sjfb 			else if (format != D_UNIFIED)
139808f90673Sjfb 				putchar(' ');
139908f90673Sjfb 		}
140008f90673Sjfb 		col = 0;
140108f90673Sjfb 		for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
140208f90673Sjfb 			if ((c = getc(lb)) == EOF) {
140308f90673Sjfb 				puts("\n\\ No newline at end of file");
140408f90673Sjfb 				return (0);
140508f90673Sjfb 			}
140608f90673Sjfb 			if (c == '\t' && tflag) {
140708f90673Sjfb 				do {
140808f90673Sjfb 					putchar(' ');
140908f90673Sjfb 				} while (++col & 7);
141008f90673Sjfb 			} else {
141108f90673Sjfb 				putchar(c);
141208f90673Sjfb 				col++;
141308f90673Sjfb 			}
141408f90673Sjfb 		}
141508f90673Sjfb 	}
141608f90673Sjfb 	return (0);
141708f90673Sjfb }
141808f90673Sjfb 
141908f90673Sjfb /*
142008f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
142108f90673Sjfb  */
142208f90673Sjfb static int
142308f90673Sjfb readhash(FILE *f)
142408f90673Sjfb {
142508f90673Sjfb 	int i, t, space;
142608f90673Sjfb 	int sum;
142708f90673Sjfb 
142808f90673Sjfb 	sum = 1;
142908f90673Sjfb 	space = 0;
143008f90673Sjfb 	if (!bflag && !wflag) {
143108f90673Sjfb 		if (iflag)
143208f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
143308f90673Sjfb 				if (t == EOF) {
143408f90673Sjfb 					if (i == 0)
143508f90673Sjfb 						return (0);
143608f90673Sjfb 					break;
143708f90673Sjfb 				}
143808f90673Sjfb 				sum = sum * 127 + chrtran[t];
143908f90673Sjfb 			}
144008f90673Sjfb 		else
144108f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
144208f90673Sjfb 				if (t == EOF) {
144308f90673Sjfb 					if (i == 0)
144408f90673Sjfb 						return (0);
144508f90673Sjfb 					break;
144608f90673Sjfb 				}
144708f90673Sjfb 				sum = sum * 127 + t;
144808f90673Sjfb 			}
144908f90673Sjfb 	} else {
145008f90673Sjfb 		for (i = 0;;) {
145108f90673Sjfb 			switch (t = getc(f)) {
145208f90673Sjfb 			case '\t':
145308f90673Sjfb 			case ' ':
145408f90673Sjfb 				space++;
145508f90673Sjfb 				continue;
145608f90673Sjfb 			default:
145708f90673Sjfb 				if (space && !wflag) {
145808f90673Sjfb 					i++;
145908f90673Sjfb 					space = 0;
146008f90673Sjfb 				}
146108f90673Sjfb 				sum = sum * 127 + chrtran[t];
146208f90673Sjfb 				i++;
146308f90673Sjfb 				continue;
146408f90673Sjfb 			case EOF:
146508f90673Sjfb 				if (i == 0)
146608f90673Sjfb 					return (0);
146708f90673Sjfb 				/* FALLTHROUGH */
146808f90673Sjfb 			case '\n':
146908f90673Sjfb 				break;
147008f90673Sjfb 			}
147108f90673Sjfb 			break;
147208f90673Sjfb 		}
147308f90673Sjfb 	}
147408f90673Sjfb 	/*
147508f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
147608f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
147708f90673Sjfb 	 */
147808f90673Sjfb 	return (sum == 0 ? 1 : sum);
147908f90673Sjfb }
148008f90673Sjfb 
148108f90673Sjfb static int
148208f90673Sjfb asciifile(FILE *f)
148308f90673Sjfb {
148408f90673Sjfb 	char buf[BUFSIZ];
148508f90673Sjfb 	int i, cnt;
148608f90673Sjfb 
148708f90673Sjfb 	if (aflag || f == NULL)
148808f90673Sjfb 		return (1);
148908f90673Sjfb 
149008f90673Sjfb 	rewind(f);
149108f90673Sjfb 	cnt = fread(buf, 1, sizeof(buf), f);
149208f90673Sjfb 	for (i = 0; i < cnt; i++)
149308f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
149408f90673Sjfb 			return (0);
149508f90673Sjfb 	return (1);
149608f90673Sjfb }
149708f90673Sjfb 
14985e78344dSjfb static char*
14995e78344dSjfb match_function(const long *f, int pos, FILE *fp)
15005e78344dSjfb {
15015e78344dSjfb 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
15025e78344dSjfb 	size_t nc;
15035e78344dSjfb 	int last = lastline;
15045e78344dSjfb 	char *p;
15055e78344dSjfb 
15065e78344dSjfb 	lastline = pos;
15075e78344dSjfb 	while (pos > last) {
15085e78344dSjfb 		fseek(fp, f[pos - 1], SEEK_SET);
15095e78344dSjfb 		nc = f[pos] - f[pos - 1];
15105e78344dSjfb 		if (nc >= sizeof(buf))
15115e78344dSjfb 			nc = sizeof(buf) - 1;
15125e78344dSjfb 		nc = fread(buf, 1, nc, fp);
15135e78344dSjfb 		if (nc > 0) {
15145e78344dSjfb 			buf[nc] = '\0';
15155e78344dSjfb 			p = strchr(buf, '\n');
15165e78344dSjfb 			if (p != NULL)
15175e78344dSjfb 				*p = '\0';
15185e78344dSjfb 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
15195e78344dSjfb 				strlcpy(lastbuf, buf, sizeof lastbuf);
15205e78344dSjfb 				lastmatchline = pos;
15215e78344dSjfb 				return lastbuf;
15225e78344dSjfb 			}
15235e78344dSjfb 		}
15245e78344dSjfb 		pos--;
15255e78344dSjfb 	}
15265e78344dSjfb 	return (lastmatchline > 0) ? lastbuf : NULL;
15275e78344dSjfb }
15285e78344dSjfb 
152908f90673Sjfb 
153008f90673Sjfb /* dump accumulated "context" diff changes */
153108f90673Sjfb static void
153208f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
153308f90673Sjfb {
153408f90673Sjfb 	struct context_vec *cvp = context_vec_start;
153508f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
153608f90673Sjfb 	int a, b, c, d;
15375e78344dSjfb 	char ch, *f;
153808f90673Sjfb 
153908f90673Sjfb 	if (context_vec_start > context_vec_ptr)
154008f90673Sjfb 		return;
154108f90673Sjfb 
154208f90673Sjfb 	b = d = 0;		/* gcc */
1543dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1544dc6a6879Sjfb 	upb = MIN(len[0], context_vec_ptr->b + context);
1545dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1546dc6a6879Sjfb 	upd = MIN(len[1], context_vec_ptr->d + context);
154708f90673Sjfb 
154808f90673Sjfb 	printf("***************");
15495e78344dSjfb 	if (pflag) {
15505e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
15515e78344dSjfb 		if (f != NULL) {
15525e78344dSjfb 			putchar(' ');
15535e78344dSjfb 			fputs(f, stdout);
15545e78344dSjfb 		}
15555e78344dSjfb 	}
155608f90673Sjfb 	printf("\n*** ");
155708f90673Sjfb 	range(lowa, upb, ",");
155808f90673Sjfb 	printf(" ****\n");
155908f90673Sjfb 
156008f90673Sjfb 	/*
156108f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
156208f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
156308f90673Sjfb 	 * the "old" lines as context in the "new" list).
156408f90673Sjfb 	 */
156508f90673Sjfb 	do_output = 0;
156608f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
156708f90673Sjfb 		if (cvp->a <= cvp->b) {
156808f90673Sjfb 			cvp = context_vec_start;
156908f90673Sjfb 			do_output++;
157008f90673Sjfb 			break;
157108f90673Sjfb 		}
157208f90673Sjfb 	if (do_output) {
157308f90673Sjfb 		while (cvp <= context_vec_ptr) {
157408f90673Sjfb 			a = cvp->a;
157508f90673Sjfb 			b = cvp->b;
157608f90673Sjfb 			c = cvp->c;
157708f90673Sjfb 			d = cvp->d;
157808f90673Sjfb 
157908f90673Sjfb 			if (a <= b && c <= d)
158008f90673Sjfb 				ch = 'c';
158108f90673Sjfb 			else
158208f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
158308f90673Sjfb 
158408f90673Sjfb 			if (ch == 'a')
158508f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
158608f90673Sjfb 			else {
158708f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
158808f90673Sjfb 				fetch(ixold, a, b, f1,
158908f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
159008f90673Sjfb 			}
159108f90673Sjfb 			lowa = b + 1;
159208f90673Sjfb 			cvp++;
159308f90673Sjfb 		}
159408f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
159508f90673Sjfb 	}
159608f90673Sjfb 	/* output changes to the "new" file */
159708f90673Sjfb 	printf("--- ");
159808f90673Sjfb 	range(lowc, upd, ",");
159908f90673Sjfb 	printf(" ----\n");
160008f90673Sjfb 
160108f90673Sjfb 	do_output = 0;
160208f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
160308f90673Sjfb 		if (cvp->c <= cvp->d) {
160408f90673Sjfb 			cvp = context_vec_start;
160508f90673Sjfb 			do_output++;
160608f90673Sjfb 			break;
160708f90673Sjfb 		}
160808f90673Sjfb 	if (do_output) {
160908f90673Sjfb 		while (cvp <= context_vec_ptr) {
161008f90673Sjfb 			a = cvp->a;
161108f90673Sjfb 			b = cvp->b;
161208f90673Sjfb 			c = cvp->c;
161308f90673Sjfb 			d = cvp->d;
161408f90673Sjfb 
161508f90673Sjfb 			if (a <= b && c <= d)
161608f90673Sjfb 				ch = 'c';
161708f90673Sjfb 			else
161808f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
161908f90673Sjfb 
162008f90673Sjfb 			if (ch == 'd')
162108f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
162208f90673Sjfb 			else {
162308f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
162408f90673Sjfb 				fetch(ixnew, c, d, f2,
162508f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
162608f90673Sjfb 			}
162708f90673Sjfb 			lowc = d + 1;
162808f90673Sjfb 			cvp++;
162908f90673Sjfb 		}
163008f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
163108f90673Sjfb 	}
163208f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
163308f90673Sjfb }
163408f90673Sjfb 
163508f90673Sjfb /* dump accumulated "unified" diff changes */
163608f90673Sjfb static void
163708f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
163808f90673Sjfb {
163908f90673Sjfb 	struct context_vec *cvp = context_vec_start;
164008f90673Sjfb 	int lowa, upb, lowc, upd;
164108f90673Sjfb 	int a, b, c, d;
16425e78344dSjfb 	char ch, *f;
164308f90673Sjfb 
164408f90673Sjfb 	if (context_vec_start > context_vec_ptr)
164508f90673Sjfb 		return;
164608f90673Sjfb 
164708f90673Sjfb 	b = d = 0;		/* gcc */
1648dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1649dc6a6879Sjfb 	upb = MIN(len[0], context_vec_ptr->b + context);
1650dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1651dc6a6879Sjfb 	upd = MIN(len[1], context_vec_ptr->d + context);
165208f90673Sjfb 
165308f90673Sjfb 	fputs("@@ -", stdout);
165408f90673Sjfb 	uni_range(lowa, upb);
165508f90673Sjfb 	fputs(" +", stdout);
165608f90673Sjfb 	uni_range(lowc, upd);
165708f90673Sjfb 	fputs(" @@", stdout);
16585e78344dSjfb 	if (pflag) {
16595e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
16605e78344dSjfb 		if (f != NULL) {
16615e78344dSjfb 			putchar(' ');
16625e78344dSjfb 			fputs(f, stdout);
16635e78344dSjfb 		}
16645e78344dSjfb 	}
166508f90673Sjfb 	putchar('\n');
166608f90673Sjfb 
166708f90673Sjfb 	/*
166808f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
166908f90673Sjfb 	 * are printed together.
167008f90673Sjfb 	 */
167108f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
167208f90673Sjfb 		a = cvp->a;
167308f90673Sjfb 		b = cvp->b;
167408f90673Sjfb 		c = cvp->c;
167508f90673Sjfb 		d = cvp->d;
167608f90673Sjfb 
167708f90673Sjfb 		/*
167808f90673Sjfb 		 * c: both new and old changes
167908f90673Sjfb 		 * d: only changes in the old file
168008f90673Sjfb 		 * a: only changes in the new file
168108f90673Sjfb 		 */
168208f90673Sjfb 		if (a <= b && c <= d)
168308f90673Sjfb 			ch = 'c';
168408f90673Sjfb 		else
168508f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
168608f90673Sjfb 
168708f90673Sjfb 		switch (ch) {
168808f90673Sjfb 		case 'c':
168908f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
169008f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
169108f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
169208f90673Sjfb 			break;
169308f90673Sjfb 		case 'd':
169408f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
169508f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
169608f90673Sjfb 			break;
169708f90673Sjfb 		case 'a':
169808f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
169908f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
170008f90673Sjfb 			break;
170108f90673Sjfb 		}
170208f90673Sjfb 		lowa = b + 1;
170308f90673Sjfb 		lowc = d + 1;
170408f90673Sjfb 	}
170508f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
170608f90673Sjfb 
170708f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
170808f90673Sjfb }
1709