xref: /openbsd-src/usr.bin/cvs/diff.c (revision e4276007dfc80452049f621e2fe571403c1cc0b2)
1*e4276007Sjfb /*	$OpenBSD: diff.c,v 1.36 2005/05/24 04:12:25 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 
132394180a4Sjfb #include <err.h>
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 */
162394180a4Sjfb #define	D_RCSDIFF	5       /* Reverse editor output: RCS format */
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 
209*e4276007Sjfb static int  cvs_diff_init      (struct cvs_cmd *, int, char **, int *);
210*e4276007Sjfb static int  cvs_diff_remote    (CVSFILE *, void *);
211*e4276007Sjfb static int  cvs_diff_local     (CVSFILE *, void *);
212*e4276007Sjfb static int  cvs_diff_pre_exec (struct cvsroot *);
213*e4276007Sjfb static int  cvs_diff_cleanup   (void);
21408f90673Sjfb int  cvs_diffreg        (const char *, const char *);
21516cfc147Sjoris 
21608f90673Sjfb static void output(const char *, FILE *, const char *, FILE *);
21708f90673Sjfb static void check(FILE *, FILE *);
21808f90673Sjfb static void range(int, int, char *);
21908f90673Sjfb static void uni_range(int, int);
22008f90673Sjfb static void dump_context_vec(FILE *, FILE *);
22108f90673Sjfb static void dump_unified_vec(FILE *, FILE *);
2227f535ec4Sjfb static int  prepare(int, FILE *, off_t);
22308f90673Sjfb static void prune(void);
22408f90673Sjfb static void equiv(struct line *, int, struct line *, int, int *);
22508f90673Sjfb static void unravel(int);
22608f90673Sjfb static void unsort(struct line *, int, int *);
22708f90673Sjfb static void change(const char *, FILE *, const char *, FILE *, int, int, int, int);
22808f90673Sjfb static void sort(struct line *, int);
22908f90673Sjfb static int  ignoreline(char *);
23008f90673Sjfb static int  asciifile(FILE *);
23108f90673Sjfb static int  fetch(long *, int, int, FILE *, int, int);
23208f90673Sjfb static int  newcand(int, int, int);
23308f90673Sjfb static int  search(int *, int, int);
23408f90673Sjfb static int  skipline(FILE *);
23508f90673Sjfb static int  isqrt(int);
23608f90673Sjfb static int  stone(int *, int, int *, int *);
23708f90673Sjfb static int  readhash(FILE *);
23808f90673Sjfb static int  files_differ(FILE *, FILE *);
2395e78344dSjfb static char *match_function(const long *, int, FILE *);
24008f90673Sjfb static char *preadline(int, size_t, off_t);
24108f90673Sjfb 
24208f90673Sjfb 
2435e78344dSjfb static int aflag, bflag, dflag, iflag, Nflag, pflag, tflag, Tflag, wflag;
24408f90673Sjfb static int context, status;
24508f90673Sjfb static int format = D_NORMAL;
24608f90673Sjfb static struct stat stb1, stb2;
247f5638424Sjfb static char *ifdefname, *ignore_pats, diffargs[128];
248f5638424Sjfb static const char *diff_file;
24908f90673Sjfb regex_t ignore_re;
25008f90673Sjfb 
25108f90673Sjfb static int  *J;			/* will be overlaid on class */
25208f90673Sjfb static int  *class;		/* will be overlaid on file[0] */
25308f90673Sjfb static int  *klist;		/* will be overlaid on file[0] after class */
25408f90673Sjfb static int  *member;		/* will be overlaid on file[1] */
25508f90673Sjfb static int   clen;
25608f90673Sjfb static int   inifdef;		/* whether or not we are in a #ifdef block */
257*e4276007Sjfb static int   diff_len[2];
25808f90673Sjfb static int   pref, suff;	/* length of prefix and suffix */
25908f90673Sjfb static int   slen[2];
26008f90673Sjfb static int   anychange;
26108f90673Sjfb static long *ixnew;		/* will be overlaid on file[1] */
26208f90673Sjfb static long *ixold;		/* will be overlaid on klist */
26308f90673Sjfb static struct cand *clist;	/* merely a free storage pot for candidates */
26408f90673Sjfb static int   clistlen;		/* the length of clist */
26508f90673Sjfb static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
26608f90673Sjfb static u_char *chrtran;		/* translation table for case-folding */
26708f90673Sjfb static struct context_vec *context_vec_start;
26808f90673Sjfb static struct context_vec *context_vec_end;
26908f90673Sjfb static struct context_vec *context_vec_ptr;
27008f90673Sjfb 
27108f90673Sjfb #define FUNCTION_CONTEXT_SIZE	41
2725e78344dSjfb static char lastbuf[FUNCTION_CONTEXT_SIZE];
27308f90673Sjfb static int  lastline;
27408f90673Sjfb static int  lastmatchline;
27508f90673Sjfb 
27608f90673Sjfb 
27708f90673Sjfb /*
27808f90673Sjfb  * chrtran points to one of 2 translation tables: cup2low if folding upper to
27908f90673Sjfb  * lower case clow2low if not folding case
28008f90673Sjfb  */
28108f90673Sjfb u_char clow2low[256] = {
28208f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
28308f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
28408f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
28508f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
28608f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
28708f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
28808f90673Sjfb 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
28908f90673Sjfb 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
29008f90673Sjfb 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
29108f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
29208f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
29308f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
29408f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
29508f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
29608f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
29708f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
29808f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
29908f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
30008f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
30108f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
30208f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
30308f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
30408f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
30508f90673Sjfb 	0xfd, 0xfe, 0xff
30608f90673Sjfb };
30708f90673Sjfb 
30808f90673Sjfb u_char cup2low[256] = {
30908f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
31008f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
31108f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
31208f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
31308f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
31408f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
31508f90673Sjfb 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
31608f90673Sjfb 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
31708f90673Sjfb 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
31808f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
31908f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
32008f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
32108f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
32208f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
32308f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
32408f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
32508f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
32608f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
32708f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
32808f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
32908f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
33008f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
33108f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
33208f90673Sjfb 	0xfd, 0xfe, 0xff
33308f90673Sjfb };
33408f90673Sjfb 
335*e4276007Sjfb 
336*e4276007Sjfb struct cvs_cmd cvs_cmd_diff = {
337*e4276007Sjfb 	CVS_OP_DIFF, CVS_REQ_DIFF, "diff",
338*e4276007Sjfb 	{ "di", "dif" },
339*e4276007Sjfb 	"Show differences between revisions",
340*e4276007Sjfb 	"[-cilNnpu] [-D date] [-r rev] ...",
341*e4276007Sjfb 	"cD:ilNnpr:Ru",
34216cfc147Sjoris 	NULL,
34316cfc147Sjoris 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
344*e4276007Sjfb 	cvs_diff_init,
345*e4276007Sjfb 	cvs_diff_pre_exec,
346*e4276007Sjfb 	cvs_diff_remote,
347*e4276007Sjfb 	cvs_diff_local,
348*e4276007Sjfb 	NULL,
349*e4276007Sjfb 	cvs_diff_cleanup,
350*e4276007Sjfb 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
351*e4276007Sjfb };
352*e4276007Sjfb 
353*e4276007Sjfb 
354*e4276007Sjfb struct cvs_cmd cvs_cmd_rdiff = {
355*e4276007Sjfb 	CVS_OP_RDIFF, CVS_REQ_DIFF, "rdiff",
356*e4276007Sjfb 	{ "di", "dif" },
357*e4276007Sjfb 	"Create 'patch' format diffs between releases",
358*e4276007Sjfb 	"",
359*e4276007Sjfb 	"",
360*e4276007Sjfb 	NULL,
361*e4276007Sjfb 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
362*e4276007Sjfb 	cvs_diff_init,
363*e4276007Sjfb 	cvs_diff_pre_exec,
364*e4276007Sjfb 	cvs_diff_remote,
365*e4276007Sjfb 	cvs_diff_local,
366*e4276007Sjfb 	NULL,
367*e4276007Sjfb 	cvs_diff_cleanup,
36844381dcbSjoris 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
36916cfc147Sjoris };
37008f90673Sjfb 
37116cfc147Sjoris static struct diff_arg *dap = NULL;
37216cfc147Sjoris static int recurse;
37316cfc147Sjoris 
374*e4276007Sjfb static int
375*e4276007Sjfb cvs_diff_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg)
37608f90673Sjfb {
37716cfc147Sjoris 	int ch;
37808f90673Sjfb 
37916cfc147Sjoris 	dap = (struct diff_arg *)malloc(sizeof(*dap));
38016cfc147Sjoris 	if (dap == NULL)
38131274bbfSjoris 		return (CVS_EX_DATA);
38216cfc147Sjoris 	dap->date1 = dap->date2 = dap->rev1 = dap->rev2 = NULL;
383dc6a6879Sjfb 	strlcpy(diffargs, argv[0], sizeof(diffargs));
384dc6a6879Sjfb 
385*e4276007Sjfb 	while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) {
38608f90673Sjfb 		switch (ch) {
38708f90673Sjfb 		case 'c':
388f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
38908f90673Sjfb 			format = D_CONTEXT;
39008f90673Sjfb 			break;
39108f90673Sjfb 		case 'D':
39216cfc147Sjoris 			if (dap->date1 == NULL && dap->rev1 == NULL) {
39316cfc147Sjoris 				dap->date1 = optarg;
39416cfc147Sjoris 			} else if (dap->date2 == NULL && dap->rev2 == NULL) {
39516cfc147Sjoris 				dap->date2 = optarg;
39616cfc147Sjoris 			} else {
39708f90673Sjfb 				cvs_log(LP_ERR,
39808f90673Sjfb 				    "no more than two revisions/dates can "
39908f90673Sjfb 				    "be specified");
40008f90673Sjfb 			}
40108f90673Sjfb 			break;
40208f90673Sjfb 		case 'l':
403f5638424Sjfb 			strlcat(diffargs, " -l", sizeof(diffargs));
40408f90673Sjfb 			recurse = 0;
405*e4276007Sjfb 			cvs_cmd_diff.file_flags &= ~CF_RECURSE;
40608f90673Sjfb 			break;
40708f90673Sjfb 		case 'i':
408f5638424Sjfb 			strlcat(diffargs, " -i", sizeof(diffargs));
40908f90673Sjfb 			iflag = 1;
41008f90673Sjfb 			break;
411c710bc5aSjfb 		case 'N':
412c710bc5aSjfb 			strlcat(diffargs, " -N", sizeof(diffargs));
413c710bc5aSjfb 			Nflag = 1;
414c710bc5aSjfb 			break;
415394180a4Sjfb 		case 'n':
416394180a4Sjfb 			strlcat(diffargs, " -n", sizeof(diffargs));
417394180a4Sjfb 			format = D_RCSDIFF;
418394180a4Sjfb 			break;
4195e78344dSjfb 		case 'p':
4205e78344dSjfb 			strlcat(diffargs, " -p", sizeof(diffargs));
4215e78344dSjfb 			pflag = 1;
4225e78344dSjfb 			break;
42308f90673Sjfb 		case 'r':
42416cfc147Sjoris 			if ((dap->rev1 == NULL) && (dap->date1 == NULL)) {
42516cfc147Sjoris 				dap->rev1 = optarg;
42616cfc147Sjoris 			} else if ((dap->rev2 == NULL) &&
42716cfc147Sjoris 			    (dap->date2 == NULL)) {
42816cfc147Sjoris 				dap->rev2 = optarg;
42916cfc147Sjoris 			} else {
43008f90673Sjfb 				cvs_log(LP_ERR,
43108f90673Sjfb 				    "no more than two revisions/dates can "
43208f90673Sjfb 				    "be specified");
43331274bbfSjoris 				return (CVS_EX_USAGE);
43408f90673Sjfb 			}
43508f90673Sjfb 			break;
436f203c484Sjoris 		case 'R':
437*e4276007Sjfb 			cvs_cmd_diff.file_flags |= CF_RECURSE;
438f203c484Sjoris 			break;
43908f90673Sjfb 		case 'u':
440f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
44108f90673Sjfb 			format = D_UNIFIED;
44208f90673Sjfb 			break;
44308f90673Sjfb 		default:
44431274bbfSjoris 			return (CVS_EX_USAGE);
44508f90673Sjfb 		}
44608f90673Sjfb 	}
44708f90673Sjfb 
44816cfc147Sjoris 	*arg = optind;
449dc6a6879Sjfb 	return (0);
450dc6a6879Sjfb }
451dc6a6879Sjfb 
45216cfc147Sjoris int
45316cfc147Sjoris cvs_diff_cleanup(void)
45416cfc147Sjoris {
455*e4276007Sjfb 	if (dap != NULL) {
45616cfc147Sjoris 		free(dap);
457*e4276007Sjfb 		dap = NULL;
458*e4276007Sjfb 	}
45916cfc147Sjoris 	return (0);
46016cfc147Sjoris }
461dc6a6879Sjfb 
462dc6a6879Sjfb /*
463*e4276007Sjfb  * cvs_diff_pre_exec()
464dc6a6879Sjfb  *
465dc6a6879Sjfb  */
466dc6a6879Sjfb int
467*e4276007Sjfb cvs_diff_pre_exec(struct cvsroot *root)
468dc6a6879Sjfb {
469*e4276007Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
47008f90673Sjfb 		/* send the flags */
4715e78344dSjfb 		if (Nflag && (cvs_sendarg(root, "-N", 0) < 0))
47231274bbfSjoris 			return (CVS_EX_PROTO);
4735e78344dSjfb 		if (pflag && (cvs_sendarg(root, "-p", 0) < 0))
47431274bbfSjoris 			return (CVS_EX_PROTO);
4755e78344dSjfb 
47608f90673Sjfb 		if (format == D_CONTEXT)
477dc6a6879Sjfb 			cvs_sendarg(root, "-c", 0);
47808f90673Sjfb 		else if (format == D_UNIFIED)
479dc6a6879Sjfb 			cvs_sendarg(root, "-u", 0);
48008f90673Sjfb 
481dc6a6879Sjfb 		if (dap->rev1 != NULL) {
482dc6a6879Sjfb 			cvs_sendarg(root, "-r", 0);
48343579e5bSjfb 			cvs_sendarg(root, dap->rev1, 0);
4843917c9bfSderaadt 		} else if (dap->date1 != NULL) {
485dc6a6879Sjfb 			cvs_sendarg(root, "-D", 0);
48643579e5bSjfb 			cvs_sendarg(root, dap->date1, 0);
48708f90673Sjfb 		}
488dc6a6879Sjfb 		if (dap->rev2 != NULL) {
489dc6a6879Sjfb 			cvs_sendarg(root, "-r", 0);
49043579e5bSjfb 			cvs_sendarg(root, dap->rev2, 0);
4913917c9bfSderaadt 		} else if (dap->date2 != NULL) {
492dc6a6879Sjfb 			cvs_sendarg(root, "-D", 0);
49343579e5bSjfb 			cvs_sendarg(root, dap->date2, 0);
49408f90673Sjfb 		}
495*e4276007Sjfb 	}
49608f90673Sjfb 
49708f90673Sjfb 	return (0);
49808f90673Sjfb }
49908f90673Sjfb 
50008f90673Sjfb 
50108f90673Sjfb /*
50208f90673Sjfb  * cvs_diff_file()
50308f90673Sjfb  *
50408f90673Sjfb  * Diff a single file.
50508f90673Sjfb  */
506*e4276007Sjfb static int
507*e4276007Sjfb cvs_diff_remote(struct cvs_file *cfp, void *arg)
50808f90673Sjfb {
509*e4276007Sjfb 	char *dir, *repo;
510*e4276007Sjfb 	char fpath[MAXPATHLEN], dfpath[MAXPATHLEN];
511dc6a6879Sjfb 	struct cvsroot *root;
512dc6a6879Sjfb 
513dc6a6879Sjfb 	if (cfp->cf_type == DT_DIR) {
514895e6cf6Sjfb 		if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
515bb029937Sjfb 			root = cfp->cf_parent->cf_root;
516c710bc5aSjfb 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE,
517c710bc5aSjfb 			    CVS_FILE_NAME(cfp));
5183917c9bfSderaadt 		} else {
519bb029937Sjfb 			root = cfp->cf_root;
52016cfc147Sjoris #if 0
521dc6a6879Sjfb 			if ((cfp->cf_parent == NULL) ||
522bb029937Sjfb 			    (root != cfp->cf_parent->cf_root)) {
523dc6a6879Sjfb 				cvs_connect(root);
524*e4276007Sjfb 				cvs_diff_pre_exec(root);
525dc6a6879Sjfb 			}
52616cfc147Sjoris #endif
527dc6a6879Sjfb 
528dc6a6879Sjfb 			cvs_senddir(root, cfp);
529895e6cf6Sjfb 		}
530895e6cf6Sjfb 
531dc6a6879Sjfb 		return (0);
532dc6a6879Sjfb 	}
53308f90673Sjfb 
5342d5b8b1dSjfb 	if (cfp->cf_cvstat == CVS_FST_LOST) {
5352d5b8b1dSjfb 		cvs_log(LP_WARN, "cannot find file %s", CVS_FILE_NAME(cfp));
5362d5b8b1dSjfb 		return (0);
5372d5b8b1dSjfb 	}
5382d5b8b1dSjfb 
539c710bc5aSjfb 	diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath));
540895e6cf6Sjfb 
541dc6a6879Sjfb 	if (cfp->cf_parent != NULL) {
542c710bc5aSjfb 		dir = cvs_file_getpath(cfp->cf_parent, dfpath, sizeof(dfpath));
543bb029937Sjfb 		root = cfp->cf_parent->cf_root;
544bb029937Sjfb 		repo = cfp->cf_parent->cf_repo;
5453917c9bfSderaadt 	} else {
546dc6a6879Sjfb 		dir = ".";
547895e6cf6Sjfb 		root = NULL;
548dc6a6879Sjfb 		repo = NULL;
54908f90673Sjfb 	}
55008f90673Sjfb 
551dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
552*e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
553dc6a6879Sjfb 		return (0);
55408f90673Sjfb 	}
55508f90673Sjfb 
556*e4276007Sjfb 	if (cvs_sendentry(root, cfp) < 0)
55731274bbfSjoris 		return (CVS_EX_PROTO);
55808f90673Sjfb 
559dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UPTODATE) {
560*e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name);
56108f90673Sjfb 		return (0);
56208f90673Sjfb 	}
56308f90673Sjfb 
56408f90673Sjfb 	/* at this point, the file is modified */
565*e4276007Sjfb 	if ((cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name) < 0) ||
566*e4276007Sjfb 	    (cvs_sendfile(root, diff_file) < 0))
567*e4276007Sjfb 		return (CVS_EX_PROTO);
568*e4276007Sjfb 
569*e4276007Sjfb 	return (0);
570*e4276007Sjfb }
571*e4276007Sjfb 
572*e4276007Sjfb static int
573*e4276007Sjfb cvs_diff_local(CVSFILE *cf, void *arg)
574*e4276007Sjfb {
575*e4276007Sjfb 	int len;
576*e4276007Sjfb 	char *repo, buf[64];
577*e4276007Sjfb 	char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
578*e4276007Sjfb 	char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN];
579*e4276007Sjfb 	BUF *b1, *b2;
580*e4276007Sjfb 	RCSNUM *r1, *r2;
581*e4276007Sjfb 	RCSFILE *rf;
582*e4276007Sjfb 	struct cvsroot *root;
583*e4276007Sjfb 
584*e4276007Sjfb 	rf = NULL;
585*e4276007Sjfb 	root = CVS_DIR_ROOT(cf);
586*e4276007Sjfb 	repo = CVS_DIR_REPO(cf);
587*e4276007Sjfb 	diff_file = cvs_file_getpath(cf, fpath, sizeof(fpath));
588*e4276007Sjfb 
589*e4276007Sjfb 	if (cf->cf_type == DT_DIR) {
590*e4276007Sjfb 		cvs_log(LP_INFO, "Diffing %s", fpath);
591*e4276007Sjfb 		return (0);
592*e4276007Sjfb 	}
593*e4276007Sjfb 
594*e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_LOST) {
595*e4276007Sjfb 		cvs_log(LP_WARN, "cannot find file %s", cf->cf_name);
596*e4276007Sjfb 		return (0);
597*e4276007Sjfb 	}
598*e4276007Sjfb 
599*e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_UNKNOWN) {
600*e4276007Sjfb 		cvs_log(LP_WARN, "I know nothing about %s", diff_file);
601*e4276007Sjfb 		return (0);
602*e4276007Sjfb 	} else if (cf->cf_cvstat == CVS_FST_UPTODATE)
603*e4276007Sjfb 		return (0);
604*e4276007Sjfb 
605*e4276007Sjfb 	/* at this point, the file is modified */
606*e4276007Sjfb 	len = snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
607dc6a6879Sjfb 	    root->cr_dir, repo, diff_file, RCS_FILE_EXT);
608*e4276007Sjfb 	if (len == -1 || len >= (int)sizeof(rcspath)) {
60927b85f85Sxsa 		errno = ENAMETOOLONG;
61027b85f85Sxsa 		cvs_log(LP_ERRNO, "%s", rcspath);
61101b3d77aSjoris 		return (CVS_EX_DATA);
61227b85f85Sxsa 	}
61308f90673Sjfb 
6141b6534b8Sjfb 	rf = rcs_open(rcspath, RCS_READ);
615dc6a6879Sjfb 	if (rf == NULL) {
61631274bbfSjoris 		return (CVS_EX_DATA);
617dc6a6879Sjfb 	}
61808f90673Sjfb 
619dc6a6879Sjfb 	cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file,
62008f90673Sjfb 	    RCS_DIFF_DIV, rcspath);
62108f90673Sjfb 
622dc6a6879Sjfb 	if (dap->rev1 == NULL)
623*e4276007Sjfb 		r1 = cf->cf_lrev;
62408f90673Sjfb 	else {
62525b74b48Sjfb 		if ((r1 = rcsnum_parse(dap->rev1)) == NULL) {
62631274bbfSjoris 			return (CVS_EX_DATA);
6277f535ec4Sjfb 		}
62808f90673Sjfb 	}
62908f90673Sjfb 
630dc6a6879Sjfb 	cvs_printf("retrieving revision %s\n",
63108f90673Sjfb 	    rcsnum_tostr(r1, buf, sizeof(buf)));
63208f90673Sjfb 	b1 = rcs_getrev(rf, r1);
63308f90673Sjfb 
634*e4276007Sjfb 	if (r1 != cf->cf_lrev)
6357f535ec4Sjfb 		rcsnum_free(r1);
6367f535ec4Sjfb 
637dc6a6879Sjfb 	if (dap->rev2 != NULL) {
638dc6a6879Sjfb 		cvs_printf("retrieving revision %s\n", dap->rev2);
63925b74b48Sjfb 		if ((r2 = rcsnum_parse(dap->rev2)) == NULL) {
64031274bbfSjoris 			return (CVS_EX_DATA);
6417f535ec4Sjfb 		}
64208f90673Sjfb 		b2 = rcs_getrev(rf, r2);
6437f535ec4Sjfb 		rcsnum_free(r2);
6443917c9bfSderaadt 	} else {
645dc6a6879Sjfb 		b2 = cvs_buf_load(diff_file, BUF_AUTOEXT);
64608f90673Sjfb 	}
64708f90673Sjfb 
648dc6a6879Sjfb 	rcs_close(rf);
649dc6a6879Sjfb 
650f5638424Sjfb 	printf("%s", diffargs);
651f5638424Sjfb 	printf(" -r%s", buf);
652dc6a6879Sjfb 	if (dap->rev2 != NULL)
653dc6a6879Sjfb 		printf(" -r%s", dap->rev2);
654dc6a6879Sjfb 	printf(" %s\n", diff_file);
655946f6157Sdjm 	strlcpy(path_tmp1, "/tmp/diff1.XXXXXXXXXX", sizeof(path_tmp1));
6567f535ec4Sjfb 	if (cvs_buf_write_stmp(b1, path_tmp1, 0600) == -1) {
6577f535ec4Sjfb 		cvs_buf_free(b1);
6587f535ec4Sjfb 		cvs_buf_free(b2);
65931274bbfSjoris 		return (CVS_EX_DATA);
6607f535ec4Sjfb 	}
6617f535ec4Sjfb 	cvs_buf_free(b1);
6627f535ec4Sjfb 
66369853e40Sjfb 	strlcpy(path_tmp2, "/tmp/diff2.XXXXXXXXXX", sizeof(path_tmp2));
664946f6157Sdjm 	if (cvs_buf_write_stmp(b2, path_tmp2, 0600) == -1) {
6657f535ec4Sjfb 		cvs_buf_free(b2);
666946f6157Sdjm 		(void)unlink(path_tmp1);
66731274bbfSjoris 		return (CVS_EX_DATA);
668946f6157Sdjm 	}
6697f535ec4Sjfb 	cvs_buf_free(b2);
6707f535ec4Sjfb 
671946f6157Sdjm 	cvs_diffreg(path_tmp1, path_tmp2);
672946f6157Sdjm 	(void)unlink(path_tmp1);
673946f6157Sdjm 	(void)unlink(path_tmp2);
67408f90673Sjfb 
67508f90673Sjfb 	return (0);
67608f90673Sjfb }
67708f90673Sjfb 
67808f90673Sjfb 
67908f90673Sjfb int
68008f90673Sjfb cvs_diffreg(const char *file1, const char *file2)
68108f90673Sjfb {
68208f90673Sjfb 	FILE *f1, *f2;
68308f90673Sjfb 	int i, rval;
6847f535ec4Sjfb 	void *tmp;
68508f90673Sjfb 
68608f90673Sjfb 	f1 = f2 = NULL;
68708f90673Sjfb 	rval = D_SAME;
68808f90673Sjfb 	anychange = 0;
68908f90673Sjfb 	lastline = 0;
69008f90673Sjfb 	lastmatchline = 0;
69108f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
69208f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
69308f90673Sjfb 
69408f90673Sjfb 	f1 = fopen(file1, "r");
69508f90673Sjfb 	if (f1 == NULL) {
69608f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
69708f90673Sjfb 		status |= 2;
69808f90673Sjfb 		goto closem;
69908f90673Sjfb 	}
70008f90673Sjfb 
70108f90673Sjfb 	f2 = fopen(file2, "r");
70208f90673Sjfb 	if (f2 == NULL) {
70308f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
70408f90673Sjfb 		status |= 2;
70508f90673Sjfb 		goto closem;
70608f90673Sjfb 	}
70708f90673Sjfb 
70808f90673Sjfb 	switch (files_differ(f1, f2)) {
70908f90673Sjfb 	case 0:
71008f90673Sjfb 		goto closem;
71108f90673Sjfb 	case 1:
71208f90673Sjfb 		break;
71308f90673Sjfb 	default:
71408f90673Sjfb 		/* error */
71508f90673Sjfb 		status |= 2;
71608f90673Sjfb 		goto closem;
71708f90673Sjfb 	}
71808f90673Sjfb 
71908f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
72008f90673Sjfb 		rval = D_BINARY;
72108f90673Sjfb 		status |= 1;
72208f90673Sjfb 		goto closem;
72308f90673Sjfb 	}
7247f535ec4Sjfb 	if ((prepare(0, f1, stb1.st_size) < 0) ||
7257f535ec4Sjfb 	    (prepare(1, f2, stb2.st_size) < 0)) {
7267f535ec4Sjfb 		status |= 2;
7277f535ec4Sjfb 		goto closem;
7287f535ec4Sjfb 	}
72908f90673Sjfb 	prune();
73008f90673Sjfb 	sort(sfile[0], slen[0]);
73108f90673Sjfb 	sort(sfile[1], slen[1]);
73208f90673Sjfb 
73308f90673Sjfb 	member = (int *)file[1];
73408f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
7357f535ec4Sjfb 	if ((tmp = realloc(member, (slen[1] + 2) * sizeof(int))) == NULL) {
7367f535ec4Sjfb 		status |= 2;
7377f535ec4Sjfb 		goto closem;
7387f535ec4Sjfb 	}
7397f535ec4Sjfb 	member = (int *)tmp;
74008f90673Sjfb 
74108f90673Sjfb 	class = (int *)file[0];
74208f90673Sjfb 	unsort(sfile[0], slen[0], class);
7437f535ec4Sjfb 	if ((tmp = realloc(class, (slen[0] + 2) * sizeof(int))) == NULL) {
7447f535ec4Sjfb 		status |= 2;
7457f535ec4Sjfb 		goto closem;
7467f535ec4Sjfb 	}
7477f535ec4Sjfb 	class = (int *)tmp;
74808f90673Sjfb 
7497f535ec4Sjfb 	if ((klist = malloc((slen[0] + 2) * sizeof(int))) == NULL) {
7507f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate klist");
7517f535ec4Sjfb 		status |= 2;
7527f535ec4Sjfb 		goto closem;
7537f535ec4Sjfb 	}
75408f90673Sjfb 	clen = 0;
75508f90673Sjfb 	clistlen = 100;
7567f535ec4Sjfb 	if ((clist = malloc(clistlen * sizeof(cand))) == NULL) {
7577f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate clist");
7587f535ec4Sjfb 		status |= 2;
7597f535ec4Sjfb 		goto closem;
7607f535ec4Sjfb 	}
76108f90673Sjfb 	i = stone(class, slen[0], member, klist);
76208f90673Sjfb 	free(member);
76308f90673Sjfb 	free(class);
76408f90673Sjfb 
765*e4276007Sjfb 	J = realloc(J, (diff_len[0] + 2) * sizeof(int));
76608f90673Sjfb 	unravel(klist[i]);
76708f90673Sjfb 	free(clist);
76808f90673Sjfb 	free(klist);
76908f90673Sjfb 
770*e4276007Sjfb 	ixold = realloc(ixold, (diff_len[0] + 2) * sizeof(long));
771*e4276007Sjfb 	ixnew = realloc(ixnew, (diff_len[1] + 2) * sizeof(long));
77208f90673Sjfb 	check(f1, f2);
77308f90673Sjfb 	output(file1, f1, file2, f2);
77408f90673Sjfb 
77508f90673Sjfb closem:
77608f90673Sjfb 	if (anychange) {
77708f90673Sjfb 		status |= 1;
77808f90673Sjfb 		if (rval == D_SAME)
77908f90673Sjfb 			rval = D_DIFFER;
78008f90673Sjfb 	}
78108f90673Sjfb 	if (f1 != NULL)
78208f90673Sjfb 		fclose(f1);
78308f90673Sjfb 	if (f2 != NULL)
78408f90673Sjfb 		fclose(f2);
7857f535ec4Sjfb 
78608f90673Sjfb 	return (rval);
78708f90673Sjfb }
78808f90673Sjfb 
78908f90673Sjfb /*
79008f90673Sjfb  * Check to see if the given files differ.
79108f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
79208f90673Sjfb  * XXX - could use code from cmp(1) [faster]
79308f90673Sjfb  */
79408f90673Sjfb static int
79508f90673Sjfb files_differ(FILE *f1, FILE *f2)
79608f90673Sjfb {
79708f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
79808f90673Sjfb 	size_t i, j;
79908f90673Sjfb 
80008f90673Sjfb 	if (stb1.st_size != stb2.st_size)
80108f90673Sjfb 		return (1);
80208f90673Sjfb 	for (;;) {
80308f90673Sjfb 		i = fread(buf1, 1, sizeof(buf1), f1);
80408f90673Sjfb 		j = fread(buf2, 1, sizeof(buf2), f2);
80508f90673Sjfb 		if (i != j)
80608f90673Sjfb 			return (1);
80708f90673Sjfb 		if (i == 0 && j == 0) {
80808f90673Sjfb 			if (ferror(f1) || ferror(f2))
80908f90673Sjfb 				return (1);
81008f90673Sjfb 			return (0);
81108f90673Sjfb 		}
81208f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
81308f90673Sjfb 			return (1);
81408f90673Sjfb 	}
81508f90673Sjfb }
81608f90673Sjfb 
8177f535ec4Sjfb static int
81808f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
81908f90673Sjfb {
8207f535ec4Sjfb 	void *tmp;
82108f90673Sjfb 	struct line *p;
82208f90673Sjfb 	int j, h;
82308f90673Sjfb 	size_t sz;
82408f90673Sjfb 
82508f90673Sjfb 	rewind(fd);
82608f90673Sjfb 
827c48da046Sxsa 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
82808f90673Sjfb 	if (sz < 100)
82908f90673Sjfb 		sz = 100;
83008f90673Sjfb 
8317f535ec4Sjfb 	p = (struct line *)malloc((sz + 3) * sizeof(struct line));
8327f535ec4Sjfb 	if (p == NULL) {
8337f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to prepare line array");
8347f535ec4Sjfb 		return (-1);
8357f535ec4Sjfb 	}
83608f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
83708f90673Sjfb 		if (j == (int)sz) {
83808f90673Sjfb 			sz = sz * 3 / 2;
8397f535ec4Sjfb 			tmp = realloc(p, (sz + 3) * sizeof(struct line));
8407f535ec4Sjfb 			if (tmp == NULL) {
8417f535ec4Sjfb 				cvs_log(LP_ERRNO, "failed to grow line array");
8427f535ec4Sjfb 				free(p);
8437f535ec4Sjfb 				return (-1);
8447f535ec4Sjfb 			}
8457f535ec4Sjfb 			p = (struct line *)tmp;
84608f90673Sjfb 		}
84708f90673Sjfb 		p[++j].value = h;
84808f90673Sjfb 	}
849*e4276007Sjfb 	diff_len[i] = j;
85008f90673Sjfb 	file[i] = p;
8517f535ec4Sjfb 
8527f535ec4Sjfb 	return (0);
85308f90673Sjfb }
85408f90673Sjfb 
85508f90673Sjfb static void
85608f90673Sjfb prune(void)
85708f90673Sjfb {
85808f90673Sjfb 	int i, j;
85908f90673Sjfb 
860*e4276007Sjfb 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
86108f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
86208f90673Sjfb 	    pref++)
86308f90673Sjfb 		;
864*e4276007Sjfb 	for (suff = 0;
865*e4276007Sjfb 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
866*e4276007Sjfb 	    (file[0][diff_len[0] - suff].value ==
867*e4276007Sjfb 	    file[1][diff_len[1] - suff].value);
86808f90673Sjfb 	    suff++)
86908f90673Sjfb 		;
87008f90673Sjfb 	for (j = 0; j < 2; j++) {
87108f90673Sjfb 		sfile[j] = file[j] + pref;
872*e4276007Sjfb 		slen[j] = diff_len[j] - pref - suff;
87308f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
87408f90673Sjfb 			sfile[j][i].serial = i;
87508f90673Sjfb 	}
87608f90673Sjfb }
87708f90673Sjfb 
87808f90673Sjfb static void
87908f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
88008f90673Sjfb {
88108f90673Sjfb 	int i, j;
88208f90673Sjfb 
88308f90673Sjfb 	i = j = 1;
88408f90673Sjfb 	while (i <= n && j <= m) {
88508f90673Sjfb 		if (a[i].value < b[j].value)
88608f90673Sjfb 			a[i++].value = 0;
88708f90673Sjfb 		else if (a[i].value == b[j].value)
88808f90673Sjfb 			a[i++].value = j;
88908f90673Sjfb 		else
89008f90673Sjfb 			j++;
89108f90673Sjfb 	}
89208f90673Sjfb 	while (i <= n)
89308f90673Sjfb 		a[i++].value = 0;
89408f90673Sjfb 	b[m + 1].value = 0;
89508f90673Sjfb 	j = 0;
89608f90673Sjfb 	while (++j <= m) {
89708f90673Sjfb 		c[j] = -b[j].serial;
89808f90673Sjfb 		while (b[j + 1].value == b[j].value) {
89908f90673Sjfb 			j++;
90008f90673Sjfb 			c[j] = b[j].serial;
90108f90673Sjfb 		}
90208f90673Sjfb 	}
90308f90673Sjfb 	c[j] = -1;
90408f90673Sjfb }
90508f90673Sjfb 
90608f90673Sjfb /* Code taken from ping.c */
90708f90673Sjfb static int
90808f90673Sjfb isqrt(int n)
90908f90673Sjfb {
91008f90673Sjfb 	int y, x = 1;
91108f90673Sjfb 
91208f90673Sjfb 	if (n == 0)
91308f90673Sjfb 		return (0);
91408f90673Sjfb 
91508f90673Sjfb 	do { /* newton was a stinker */
91608f90673Sjfb 		y = x;
91708f90673Sjfb 		x = n / x;
91808f90673Sjfb 		x += y;
91908f90673Sjfb 		x /= 2;
92008f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
92108f90673Sjfb 
92208f90673Sjfb 	return (x);
92308f90673Sjfb }
92408f90673Sjfb 
92508f90673Sjfb static int
92608f90673Sjfb stone(int *a, int n, int *b, int *c)
92708f90673Sjfb {
92808f90673Sjfb 	int i, k, y, j, l;
92908f90673Sjfb 	int oldc, tc, oldl;
93008f90673Sjfb 	u_int numtries;
93108f90673Sjfb 
932cc649edbSjfb 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
933cc649edbSjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
93408f90673Sjfb 
93508f90673Sjfb 	k = 0;
93608f90673Sjfb 	c[0] = newcand(0, 0, 0);
93708f90673Sjfb 	for (i = 1; i <= n; i++) {
93808f90673Sjfb 		j = a[i];
93908f90673Sjfb 		if (j == 0)
94008f90673Sjfb 			continue;
94108f90673Sjfb 		y = -b[j];
94208f90673Sjfb 		oldl = 0;
94308f90673Sjfb 		oldc = c[0];
94408f90673Sjfb 		numtries = 0;
94508f90673Sjfb 		do {
94608f90673Sjfb 			if (y <= clist[oldc].y)
94708f90673Sjfb 				continue;
94808f90673Sjfb 			l = search(c, k, y);
94908f90673Sjfb 			if (l != oldl + 1)
95008f90673Sjfb 				oldc = c[l - 1];
95108f90673Sjfb 			if (l <= k) {
95208f90673Sjfb 				if (clist[c[l]].y <= y)
95308f90673Sjfb 					continue;
95408f90673Sjfb 				tc = c[l];
95508f90673Sjfb 				c[l] = newcand(i, y, oldc);
95608f90673Sjfb 				oldc = tc;
95708f90673Sjfb 				oldl = l;
95808f90673Sjfb 				numtries++;
95908f90673Sjfb 			} else {
96008f90673Sjfb 				c[l] = newcand(i, y, oldc);
96108f90673Sjfb 				k++;
96208f90673Sjfb 				break;
96308f90673Sjfb 			}
96408f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
96508f90673Sjfb 	}
96608f90673Sjfb 	return (k);
96708f90673Sjfb }
96808f90673Sjfb 
96908f90673Sjfb static int
97008f90673Sjfb newcand(int x, int y, int pred)
97108f90673Sjfb {
97208f90673Sjfb 	struct cand *q;
97308f90673Sjfb 
97408f90673Sjfb 	if (clen == clistlen) {
97508f90673Sjfb 		clistlen = clistlen * 11 / 10;
97608f90673Sjfb 		clist = realloc(clist, clistlen * sizeof(cand));
9777f535ec4Sjfb 		if (clist == NULL) {
9787f535ec4Sjfb 			cvs_log(LP_ERRNO, "failed to resize clist");
9797f535ec4Sjfb 			return (-1);
9807f535ec4Sjfb 		}
98108f90673Sjfb 	}
98208f90673Sjfb 	q = clist + clen;
98308f90673Sjfb 	q->x = x;
98408f90673Sjfb 	q->y = y;
98508f90673Sjfb 	q->pred = pred;
98608f90673Sjfb 	return (clen++);
98708f90673Sjfb }
98808f90673Sjfb 
98908f90673Sjfb static int
99008f90673Sjfb search(int *c, int k, int y)
99108f90673Sjfb {
99208f90673Sjfb 	int i, j, l, t;
99308f90673Sjfb 
99408f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
99508f90673Sjfb 		return (k + 1);
99608f90673Sjfb 	i = 0;
99708f90673Sjfb 	j = k + 1;
99808f90673Sjfb 	while (1) {
99908f90673Sjfb 		l = i + j;
100008f90673Sjfb 		if ((l >>= 1) <= i)
100108f90673Sjfb 			break;
100208f90673Sjfb 		t = clist[c[l]].y;
100308f90673Sjfb 		if (t > y)
100408f90673Sjfb 			j = l;
100508f90673Sjfb 		else if (t < y)
100608f90673Sjfb 			i = l;
100708f90673Sjfb 		else
100808f90673Sjfb 			return (l);
100908f90673Sjfb 	}
101008f90673Sjfb 	return (l + 1);
101108f90673Sjfb }
101208f90673Sjfb 
101308f90673Sjfb static void
101408f90673Sjfb unravel(int p)
101508f90673Sjfb {
101608f90673Sjfb 	struct cand *q;
101708f90673Sjfb 	int i;
101808f90673Sjfb 
1019*e4276007Sjfb 	for (i = 0; i <= diff_len[0]; i++)
102008f90673Sjfb 		J[i] = i <= pref ? i :
1021*e4276007Sjfb 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
102208f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
102308f90673Sjfb 		J[q->x + pref] = q->y + pref;
102408f90673Sjfb }
102508f90673Sjfb 
102608f90673Sjfb /*
102708f90673Sjfb  * Check does double duty:
102808f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
102908f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
103008f90673Sjfb  *  2.  collect random access indexes to the two files
103108f90673Sjfb  */
103208f90673Sjfb static void
103308f90673Sjfb check(FILE *f1, FILE *f2)
103408f90673Sjfb {
103508f90673Sjfb 	int i, j, jackpot, c, d;
103608f90673Sjfb 	long ctold, ctnew;
103708f90673Sjfb 
103808f90673Sjfb 	rewind(f1);
103908f90673Sjfb 	rewind(f2);
104008f90673Sjfb 	j = 1;
104108f90673Sjfb 	ixold[0] = ixnew[0] = 0;
104208f90673Sjfb 	jackpot = 0;
104308f90673Sjfb 	ctold = ctnew = 0;
1044*e4276007Sjfb 	for (i = 1; i <= diff_len[0]; i++) {
104508f90673Sjfb 		if (J[i] == 0) {
104608f90673Sjfb 			ixold[i] = ctold += skipline(f1);
104708f90673Sjfb 			continue;
104808f90673Sjfb 		}
104908f90673Sjfb 		while (j < J[i]) {
105008f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
105108f90673Sjfb 			j++;
105208f90673Sjfb 		}
105308f90673Sjfb 		if (bflag || wflag || iflag) {
105408f90673Sjfb 			for (;;) {
105508f90673Sjfb 				c = getc(f1);
105608f90673Sjfb 				d = getc(f2);
105708f90673Sjfb 				/*
105808f90673Sjfb 				 * GNU diff ignores a missing newline
105908f90673Sjfb 				 * in one file if bflag || wflag.
106008f90673Sjfb 				 */
106108f90673Sjfb 				if ((bflag || wflag) &&
106208f90673Sjfb 				    ((c == EOF && d == '\n') ||
106308f90673Sjfb 				    (c == '\n' && d == EOF))) {
106408f90673Sjfb 					break;
106508f90673Sjfb 				}
106608f90673Sjfb 				ctold++;
106708f90673Sjfb 				ctnew++;
106808f90673Sjfb 				if (bflag && isspace(c) && isspace(d)) {
106908f90673Sjfb 					do {
107008f90673Sjfb 						if (c == '\n')
107108f90673Sjfb 							break;
107208f90673Sjfb 						ctold++;
107308f90673Sjfb 					} while (isspace(c = getc(f1)));
107408f90673Sjfb 					do {
107508f90673Sjfb 						if (d == '\n')
107608f90673Sjfb 							break;
107708f90673Sjfb 						ctnew++;
107808f90673Sjfb 					} while (isspace(d = getc(f2)));
107908f90673Sjfb 				} else if (wflag) {
108008f90673Sjfb 					while (isspace(c) && c != '\n') {
108108f90673Sjfb 						c = getc(f1);
108208f90673Sjfb 						ctold++;
108308f90673Sjfb 					}
108408f90673Sjfb 					while (isspace(d) && d != '\n') {
108508f90673Sjfb 						d = getc(f2);
108608f90673Sjfb 						ctnew++;
108708f90673Sjfb 					}
108808f90673Sjfb 				}
108908f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
109008f90673Sjfb 					jackpot++;
109108f90673Sjfb 					J[i] = 0;
109208f90673Sjfb 					if (c != '\n' && c != EOF)
109308f90673Sjfb 						ctold += skipline(f1);
109408f90673Sjfb 					if (d != '\n' && c != EOF)
109508f90673Sjfb 						ctnew += skipline(f2);
109608f90673Sjfb 					break;
109708f90673Sjfb 				}
109808f90673Sjfb 				if (c == '\n' || c == EOF)
109908f90673Sjfb 					break;
110008f90673Sjfb 			}
110108f90673Sjfb 		} else {
110208f90673Sjfb 			for (;;) {
110308f90673Sjfb 				ctold++;
110408f90673Sjfb 				ctnew++;
110508f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
110608f90673Sjfb 					/* jackpot++; */
110708f90673Sjfb 					J[i] = 0;
110808f90673Sjfb 					if (c != '\n' && c != EOF)
110908f90673Sjfb 						ctold += skipline(f1);
111008f90673Sjfb 					if (d != '\n' && c != EOF)
111108f90673Sjfb 						ctnew += skipline(f2);
111208f90673Sjfb 					break;
111308f90673Sjfb 				}
111408f90673Sjfb 				if (c == '\n' || c == EOF)
111508f90673Sjfb 					break;
111608f90673Sjfb 			}
111708f90673Sjfb 		}
111808f90673Sjfb 		ixold[i] = ctold;
111908f90673Sjfb 		ixnew[j] = ctnew;
112008f90673Sjfb 		j++;
112108f90673Sjfb 	}
1122*e4276007Sjfb 	for (; j <= diff_len[1]; j++)
112308f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
112408f90673Sjfb 	/*
112508f90673Sjfb 	 * if (jackpot)
112608f90673Sjfb 	 *	fprintf(stderr, "jackpot\n");
112708f90673Sjfb 	 */
112808f90673Sjfb }
112908f90673Sjfb 
113008f90673Sjfb /* shellsort CACM #201 */
113108f90673Sjfb static void
113208f90673Sjfb sort(struct line *a, int n)
113308f90673Sjfb {
113408f90673Sjfb 	struct line *ai, *aim, w;
113508f90673Sjfb 	int j, m = 0, k;
113608f90673Sjfb 
113708f90673Sjfb 	if (n == 0)
113808f90673Sjfb 		return;
113908f90673Sjfb 	for (j = 1; j <= n; j *= 2)
114008f90673Sjfb 		m = 2 * j - 1;
114108f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
114208f90673Sjfb 		k = n - m;
114308f90673Sjfb 		for (j = 1; j <= k; j++) {
114408f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
114508f90673Sjfb 				aim = &ai[m];
114608f90673Sjfb 				if (aim < ai)
114708f90673Sjfb 					break;	/* wraparound */
114808f90673Sjfb 				if (aim->value > ai[0].value ||
114908f90673Sjfb 				    (aim->value == ai[0].value &&
115008f90673Sjfb 					aim->serial > ai[0].serial))
115108f90673Sjfb 					break;
115208f90673Sjfb 				w.value = ai[0].value;
115308f90673Sjfb 				ai[0].value = aim->value;
115408f90673Sjfb 				aim->value = w.value;
115508f90673Sjfb 				w.serial = ai[0].serial;
115608f90673Sjfb 				ai[0].serial = aim->serial;
115708f90673Sjfb 				aim->serial = w.serial;
115808f90673Sjfb 			}
115908f90673Sjfb 		}
116008f90673Sjfb 	}
116108f90673Sjfb }
116208f90673Sjfb 
116308f90673Sjfb static void
116408f90673Sjfb unsort(struct line *f, int l, int *b)
116508f90673Sjfb {
116608f90673Sjfb 	int *a, i;
116708f90673Sjfb 
11687f535ec4Sjfb 	if ((a = (int *)malloc((l + 1) * sizeof(int))) == NULL) {
11697f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate sort array");
11707f535ec4Sjfb 		return;
11717f535ec4Sjfb 	}
117208f90673Sjfb 	for (i = 1; i <= l; i++)
117308f90673Sjfb 		a[f[i].serial] = f[i].value;
117408f90673Sjfb 	for (i = 1; i <= l; i++)
117508f90673Sjfb 		b[i] = a[i];
117608f90673Sjfb 	free(a);
117708f90673Sjfb }
117808f90673Sjfb 
117908f90673Sjfb static int
118008f90673Sjfb skipline(FILE *f)
118108f90673Sjfb {
118208f90673Sjfb 	int i, c;
118308f90673Sjfb 
118408f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
118508f90673Sjfb 		continue;
118608f90673Sjfb 	return (i);
118708f90673Sjfb }
118808f90673Sjfb 
118908f90673Sjfb static void
119008f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2)
119108f90673Sjfb {
119208f90673Sjfb 	int m, i0, i1, j0, j1;
119308f90673Sjfb 
119408f90673Sjfb 	rewind(f1);
119508f90673Sjfb 	rewind(f2);
1196*e4276007Sjfb 	m = diff_len[0];
119708f90673Sjfb 	J[0] = 0;
1198*e4276007Sjfb 	J[m + 1] = diff_len[1] + 1;
119908f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
120008f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
120108f90673Sjfb 			i0++;
120208f90673Sjfb 		j0 = J[i0 - 1] + 1;
120308f90673Sjfb 		i1 = i0 - 1;
120408f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
120508f90673Sjfb 			i1++;
120608f90673Sjfb 		j1 = J[i1 + 1] - 1;
120708f90673Sjfb 		J[i1] = j1;
120808f90673Sjfb 		change(file1, f1, file2, f2, i0, i1, j0, j1);
120908f90673Sjfb 	}
121008f90673Sjfb 	if (m == 0)
1211*e4276007Sjfb 		change(file1, f1, file2, f2, 1, 0, 1, diff_len[1]);
121208f90673Sjfb 	if (format == D_IFDEF) {
121308f90673Sjfb 		for (;;) {
121408f90673Sjfb #define	c i0
121508f90673Sjfb 			if ((c = getc(f1)) == EOF)
121608f90673Sjfb 				return;
121708f90673Sjfb 			putchar(c);
121808f90673Sjfb 		}
121908f90673Sjfb #undef c
122008f90673Sjfb 	}
122108f90673Sjfb 	if (anychange != 0) {
122208f90673Sjfb 		if (format == D_CONTEXT)
122308f90673Sjfb 			dump_context_vec(f1, f2);
122408f90673Sjfb 		else if (format == D_UNIFIED)
122508f90673Sjfb 			dump_unified_vec(f1, f2);
122608f90673Sjfb 	}
122708f90673Sjfb }
122808f90673Sjfb 
122908f90673Sjfb static __inline void
123008f90673Sjfb range(int a, int b, char *separator)
123108f90673Sjfb {
123208f90673Sjfb 	printf("%d", a > b ? b : a);
123308f90673Sjfb 	if (a < b)
123408f90673Sjfb 		printf("%s%d", separator, b);
123508f90673Sjfb }
123608f90673Sjfb 
123708f90673Sjfb static __inline void
123808f90673Sjfb uni_range(int a, int b)
123908f90673Sjfb {
124008f90673Sjfb 	if (a < b)
124108f90673Sjfb 		printf("%d,%d", a, b - a + 1);
124208f90673Sjfb 	else if (a == b)
124308f90673Sjfb 		printf("%d", b);
124408f90673Sjfb 	else
124508f90673Sjfb 		printf("%d,0", b);
124608f90673Sjfb }
124708f90673Sjfb 
124808f90673Sjfb static char *
12492a0de57dSjfb preadline(int fd, size_t rlen, off_t off)
125008f90673Sjfb {
125108f90673Sjfb 	char *line;
125208f90673Sjfb 	ssize_t nr;
125308f90673Sjfb 
12542a0de57dSjfb 	line = malloc(rlen + 1);
125508f90673Sjfb 	if (line == NULL) {
125608f90673Sjfb 		cvs_log(LP_ERRNO, "failed to allocate line");
125708f90673Sjfb 		return (NULL);
125808f90673Sjfb 	}
12592a0de57dSjfb 	if ((nr = pread(fd, line, rlen, off)) < 0) {
126008f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
126108f90673Sjfb 		return (NULL);
126208f90673Sjfb 	}
126308f90673Sjfb 	line[nr] = '\0';
126408f90673Sjfb 	return (line);
126508f90673Sjfb }
126608f90673Sjfb 
126708f90673Sjfb static int
126808f90673Sjfb ignoreline(char *line)
126908f90673Sjfb {
127008f90673Sjfb 	int ret;
127108f90673Sjfb 
127208f90673Sjfb 	ret = regexec(&ignore_re, line, 0, NULL, 0);
127308f90673Sjfb 	free(line);
127408f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
127508f90673Sjfb }
127608f90673Sjfb 
127708f90673Sjfb /*
127808f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
127908f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
128008f90673Sjfb  * are no lines in the from file involved and this means that there were
128108f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
128208f90673Sjfb  * lines missing from the to file.
128308f90673Sjfb  */
128408f90673Sjfb static void
128508f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2,
128608f90673Sjfb 	int a, int b, int c, int d)
128708f90673Sjfb {
128808f90673Sjfb 	static size_t max_context = 64;
128908f90673Sjfb 	int i;
129008f90673Sjfb 
129108f90673Sjfb 	if (format != D_IFDEF && a > b && c > d)
129208f90673Sjfb 		return;
129308f90673Sjfb 	if (ignore_pats != NULL) {
129408f90673Sjfb 		char *line;
129508f90673Sjfb 		/*
129608f90673Sjfb 		 * All lines in the change, insert, or delete must
129708f90673Sjfb 		 * match an ignore pattern for the change to be
129808f90673Sjfb 		 * ignored.
129908f90673Sjfb 		 */
130008f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
130108f90673Sjfb 			for (i = a; i <= b; i++) {
130208f90673Sjfb 				line = preadline(fileno(f1),
130308f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
130408f90673Sjfb 				if (!ignoreline(line))
130508f90673Sjfb 					goto proceed;
130608f90673Sjfb 			}
130708f90673Sjfb 		}
130808f90673Sjfb 		if (a > b || c <= d) {	/* Changes and inserts. */
130908f90673Sjfb 			for (i = c; i <= d; i++) {
131008f90673Sjfb 				line = preadline(fileno(f2),
131108f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
131208f90673Sjfb 				if (!ignoreline(line))
131308f90673Sjfb 					goto proceed;
131408f90673Sjfb 			}
131508f90673Sjfb 		}
131608f90673Sjfb 		return;
131708f90673Sjfb 	}
131808f90673Sjfb proceed:
131908f90673Sjfb 	if (format == D_CONTEXT || format == D_UNIFIED) {
132008f90673Sjfb 		/*
132108f90673Sjfb 		 * Allocate change records as needed.
132208f90673Sjfb 		 */
132308f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
132408f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
132508f90673Sjfb 			max_context <<= 1;
132608f90673Sjfb 			context_vec_start = realloc(context_vec_start,
132708f90673Sjfb 			    max_context * sizeof(struct context_vec));
132808f90673Sjfb 			context_vec_end = context_vec_start + max_context;
132908f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
133008f90673Sjfb 		}
133108f90673Sjfb 		if (anychange == 0) {
133208f90673Sjfb 			/*
133308f90673Sjfb 			 * Print the context/unidiff header first time through.
133408f90673Sjfb 			 */
133508f90673Sjfb 			printf("%s %s	%s",
133608f90673Sjfb 			    format == D_CONTEXT ? "***" : "---", diff_file,
133708f90673Sjfb 			    ctime(&stb1.st_mtime));
133808f90673Sjfb 			printf("%s %s	%s",
133908f90673Sjfb 			    format == D_CONTEXT ? "---" : "+++", diff_file,
134008f90673Sjfb 			    ctime(&stb2.st_mtime));
134108f90673Sjfb 			anychange = 1;
134208f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
134308f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
134408f90673Sjfb 			/*
134508f90673Sjfb 			 * If this change is more than 'context' lines from the
134608f90673Sjfb 			 * previous change, dump the record and reset it.
134708f90673Sjfb 			 */
134808f90673Sjfb 			if (format == D_CONTEXT)
134908f90673Sjfb 				dump_context_vec(f1, f2);
135008f90673Sjfb 			else
135108f90673Sjfb 				dump_unified_vec(f1, f2);
135208f90673Sjfb 		}
135308f90673Sjfb 		context_vec_ptr++;
135408f90673Sjfb 		context_vec_ptr->a = a;
135508f90673Sjfb 		context_vec_ptr->b = b;
135608f90673Sjfb 		context_vec_ptr->c = c;
135708f90673Sjfb 		context_vec_ptr->d = d;
135808f90673Sjfb 		return;
135908f90673Sjfb 	}
136008f90673Sjfb 	if (anychange == 0)
136108f90673Sjfb 		anychange = 1;
136208f90673Sjfb 	switch (format) {
136308f90673Sjfb 	case D_BRIEF:
136408f90673Sjfb 		return;
136508f90673Sjfb 	case D_NORMAL:
136608f90673Sjfb 		range(a, b, ",");
136708f90673Sjfb 		putchar(a > b ? 'a' : c > d ? 'd' : 'c');
136808f90673Sjfb 		if (format == D_NORMAL)
136908f90673Sjfb 			range(c, d, ",");
137008f90673Sjfb 		putchar('\n');
137108f90673Sjfb 		break;
1372394180a4Sjfb 	case D_RCSDIFF:
1373394180a4Sjfb 		if (a > b)
1374394180a4Sjfb 			printf("a%d %d\n", b, d - c + 1);
1375394180a4Sjfb 		else {
1376394180a4Sjfb 			printf("d%d %d\n", a, b - a + 1);
1377394180a4Sjfb 
1378394180a4Sjfb 			if (!(c > d))	/* add changed lines */
1379394180a4Sjfb 				printf("a%d %d\n", b, d - c + 1);
1380394180a4Sjfb 		}
1381394180a4Sjfb 		break;
138208f90673Sjfb 	}
138308f90673Sjfb 	if (format == D_NORMAL || format == D_IFDEF) {
138408f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
138508f90673Sjfb 		if (a <= b && c <= d && format == D_NORMAL)
138608f90673Sjfb 			puts("---");
138708f90673Sjfb 	}
138808f90673Sjfb 	i = fetch(ixnew, c, d, f2, format == D_NORMAL ? '>' : '\0', 0);
138908f90673Sjfb 	if (inifdef) {
139008f90673Sjfb 		printf("#endif /* %s */\n", ifdefname);
139108f90673Sjfb 		inifdef = 0;
139208f90673Sjfb 	}
139308f90673Sjfb }
139408f90673Sjfb 
139508f90673Sjfb static int
139608f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
139708f90673Sjfb {
139808f90673Sjfb 	int i, j, c, lastc, col, nc;
139908f90673Sjfb 
140008f90673Sjfb 	/*
140108f90673Sjfb 	 * When doing #ifdef's, copy down to current line
140208f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
140308f90673Sjfb 	 */
140408f90673Sjfb 	if (format == D_IFDEF && oldfile) {
140508f90673Sjfb 		long curpos = ftell(lb);
140608f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
140708f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
140808f90673Sjfb 		for (i = 0; i < nc; i++)
140908f90673Sjfb 			putchar(getc(lb));
141008f90673Sjfb 	}
141108f90673Sjfb 	if (a > b)
141208f90673Sjfb 		return (0);
141308f90673Sjfb 	if (format == D_IFDEF) {
141408f90673Sjfb 		if (inifdef) {
141508f90673Sjfb 			printf("#else /* %s%s */\n",
141608f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
141708f90673Sjfb 		} else {
141808f90673Sjfb 			if (oldfile)
141908f90673Sjfb 				printf("#ifndef %s\n", ifdefname);
142008f90673Sjfb 			else
142108f90673Sjfb 				printf("#ifdef %s\n", ifdefname);
142208f90673Sjfb 		}
142308f90673Sjfb 		inifdef = 1 + oldfile;
142408f90673Sjfb 	}
142508f90673Sjfb 	for (i = a; i <= b; i++) {
142608f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
142708f90673Sjfb 		nc = f[i] - f[i - 1];
142808f90673Sjfb 		if (format != D_IFDEF && ch != '\0') {
142908f90673Sjfb 			putchar(ch);
143008f90673Sjfb 			if (Tflag && (format == D_NORMAL || format == D_CONTEXT
143108f90673Sjfb 			    || format == D_UNIFIED))
143208f90673Sjfb 				putchar('\t');
143308f90673Sjfb 			else if (format != D_UNIFIED)
143408f90673Sjfb 				putchar(' ');
143508f90673Sjfb 		}
143608f90673Sjfb 		col = 0;
143708f90673Sjfb 		for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
143808f90673Sjfb 			if ((c = getc(lb)) == EOF) {
1439394180a4Sjfb 				if (format == D_RCSDIFF)
1440394180a4Sjfb 					warnx("No newline at end of file");
1441394180a4Sjfb 				else
144208f90673Sjfb 					puts("\n\\ No newline at end of file");
144308f90673Sjfb 				return (0);
144408f90673Sjfb 			}
144508f90673Sjfb 			if (c == '\t' && tflag) {
144608f90673Sjfb 				do {
144708f90673Sjfb 					putchar(' ');
144808f90673Sjfb 				} while (++col & 7);
144908f90673Sjfb 			} else {
145008f90673Sjfb 				putchar(c);
145108f90673Sjfb 				col++;
145208f90673Sjfb 			}
145308f90673Sjfb 		}
145408f90673Sjfb 	}
145508f90673Sjfb 	return (0);
145608f90673Sjfb }
145708f90673Sjfb 
145808f90673Sjfb /*
145908f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
146008f90673Sjfb  */
146108f90673Sjfb static int
146208f90673Sjfb readhash(FILE *f)
146308f90673Sjfb {
146408f90673Sjfb 	int i, t, space;
146508f90673Sjfb 	int sum;
146608f90673Sjfb 
146708f90673Sjfb 	sum = 1;
146808f90673Sjfb 	space = 0;
146908f90673Sjfb 	if (!bflag && !wflag) {
147008f90673Sjfb 		if (iflag)
147108f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
147208f90673Sjfb 				if (t == EOF) {
147308f90673Sjfb 					if (i == 0)
147408f90673Sjfb 						return (0);
147508f90673Sjfb 					break;
147608f90673Sjfb 				}
147708f90673Sjfb 				sum = sum * 127 + chrtran[t];
147808f90673Sjfb 			}
147908f90673Sjfb 		else
148008f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
148108f90673Sjfb 				if (t == EOF) {
148208f90673Sjfb 					if (i == 0)
148308f90673Sjfb 						return (0);
148408f90673Sjfb 					break;
148508f90673Sjfb 				}
148608f90673Sjfb 				sum = sum * 127 + t;
148708f90673Sjfb 			}
148808f90673Sjfb 	} else {
148908f90673Sjfb 		for (i = 0;;) {
149008f90673Sjfb 			switch (t = getc(f)) {
149108f90673Sjfb 			case '\t':
149208f90673Sjfb 			case ' ':
149308f90673Sjfb 				space++;
149408f90673Sjfb 				continue;
149508f90673Sjfb 			default:
149608f90673Sjfb 				if (space && !wflag) {
149708f90673Sjfb 					i++;
149808f90673Sjfb 					space = 0;
149908f90673Sjfb 				}
150008f90673Sjfb 				sum = sum * 127 + chrtran[t];
150108f90673Sjfb 				i++;
150208f90673Sjfb 				continue;
150308f90673Sjfb 			case EOF:
150408f90673Sjfb 				if (i == 0)
150508f90673Sjfb 					return (0);
150608f90673Sjfb 				/* FALLTHROUGH */
150708f90673Sjfb 			case '\n':
150808f90673Sjfb 				break;
150908f90673Sjfb 			}
151008f90673Sjfb 			break;
151108f90673Sjfb 		}
151208f90673Sjfb 	}
151308f90673Sjfb 	/*
151408f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
151508f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
151608f90673Sjfb 	 */
151708f90673Sjfb 	return (sum == 0 ? 1 : sum);
151808f90673Sjfb }
151908f90673Sjfb 
152008f90673Sjfb static int
152108f90673Sjfb asciifile(FILE *f)
152208f90673Sjfb {
152308f90673Sjfb 	char buf[BUFSIZ];
152408f90673Sjfb 	int i, cnt;
152508f90673Sjfb 
152608f90673Sjfb 	if (aflag || f == NULL)
152708f90673Sjfb 		return (1);
152808f90673Sjfb 
152908f90673Sjfb 	rewind(f);
153008f90673Sjfb 	cnt = fread(buf, 1, sizeof(buf), f);
153108f90673Sjfb 	for (i = 0; i < cnt; i++)
153208f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
153308f90673Sjfb 			return (0);
153408f90673Sjfb 	return (1);
153508f90673Sjfb }
153608f90673Sjfb 
15375e78344dSjfb static char*
15385e78344dSjfb match_function(const long *f, int pos, FILE *fp)
15395e78344dSjfb {
15405e78344dSjfb 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
15415e78344dSjfb 	size_t nc;
15425e78344dSjfb 	int last = lastline;
15435e78344dSjfb 	char *p;
15445e78344dSjfb 
15455e78344dSjfb 	lastline = pos;
15465e78344dSjfb 	while (pos > last) {
15475e78344dSjfb 		fseek(fp, f[pos - 1], SEEK_SET);
15485e78344dSjfb 		nc = f[pos] - f[pos - 1];
15495e78344dSjfb 		if (nc >= sizeof(buf))
15505e78344dSjfb 			nc = sizeof(buf) - 1;
15515e78344dSjfb 		nc = fread(buf, 1, nc, fp);
15525e78344dSjfb 		if (nc > 0) {
15535e78344dSjfb 			buf[nc] = '\0';
15545e78344dSjfb 			p = strchr(buf, '\n');
15555e78344dSjfb 			if (p != NULL)
15565e78344dSjfb 				*p = '\0';
15575e78344dSjfb 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
15585e78344dSjfb 				strlcpy(lastbuf, buf, sizeof lastbuf);
15595e78344dSjfb 				lastmatchline = pos;
15605e78344dSjfb 				return lastbuf;
15615e78344dSjfb 			}
15625e78344dSjfb 		}
15635e78344dSjfb 		pos--;
15645e78344dSjfb 	}
15655e78344dSjfb 	return (lastmatchline > 0) ? lastbuf : NULL;
15665e78344dSjfb }
15675e78344dSjfb 
156808f90673Sjfb 
156908f90673Sjfb /* dump accumulated "context" diff changes */
157008f90673Sjfb static void
157108f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
157208f90673Sjfb {
157308f90673Sjfb 	struct context_vec *cvp = context_vec_start;
157408f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
157508f90673Sjfb 	int a, b, c, d;
15765e78344dSjfb 	char ch, *f;
157708f90673Sjfb 
157808f90673Sjfb 	if (context_vec_start > context_vec_ptr)
157908f90673Sjfb 		return;
158008f90673Sjfb 
158108f90673Sjfb 	b = d = 0;		/* gcc */
1582dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1583*e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1584dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1585*e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
158608f90673Sjfb 
158708f90673Sjfb 	printf("***************");
15885e78344dSjfb 	if (pflag) {
15895e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
15905e78344dSjfb 		if (f != NULL) {
15915e78344dSjfb 			putchar(' ');
15925e78344dSjfb 			fputs(f, stdout);
15935e78344dSjfb 		}
15945e78344dSjfb 	}
159508f90673Sjfb 	printf("\n*** ");
159608f90673Sjfb 	range(lowa, upb, ",");
159708f90673Sjfb 	printf(" ****\n");
159808f90673Sjfb 
159908f90673Sjfb 	/*
160008f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
160108f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
160208f90673Sjfb 	 * the "old" lines as context in the "new" list).
160308f90673Sjfb 	 */
160408f90673Sjfb 	do_output = 0;
160508f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
160608f90673Sjfb 		if (cvp->a <= cvp->b) {
160708f90673Sjfb 			cvp = context_vec_start;
160808f90673Sjfb 			do_output++;
160908f90673Sjfb 			break;
161008f90673Sjfb 		}
161108f90673Sjfb 	if (do_output) {
161208f90673Sjfb 		while (cvp <= context_vec_ptr) {
161308f90673Sjfb 			a = cvp->a;
161408f90673Sjfb 			b = cvp->b;
161508f90673Sjfb 			c = cvp->c;
161608f90673Sjfb 			d = cvp->d;
161708f90673Sjfb 
161808f90673Sjfb 			if (a <= b && c <= d)
161908f90673Sjfb 				ch = 'c';
162008f90673Sjfb 			else
162108f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
162208f90673Sjfb 
162308f90673Sjfb 			if (ch == 'a')
162408f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
162508f90673Sjfb 			else {
162608f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
162708f90673Sjfb 				fetch(ixold, a, b, f1,
162808f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
162908f90673Sjfb 			}
163008f90673Sjfb 			lowa = b + 1;
163108f90673Sjfb 			cvp++;
163208f90673Sjfb 		}
163308f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
163408f90673Sjfb 	}
163508f90673Sjfb 	/* output changes to the "new" file */
163608f90673Sjfb 	printf("--- ");
163708f90673Sjfb 	range(lowc, upd, ",");
163808f90673Sjfb 	printf(" ----\n");
163908f90673Sjfb 
164008f90673Sjfb 	do_output = 0;
164108f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
164208f90673Sjfb 		if (cvp->c <= cvp->d) {
164308f90673Sjfb 			cvp = context_vec_start;
164408f90673Sjfb 			do_output++;
164508f90673Sjfb 			break;
164608f90673Sjfb 		}
164708f90673Sjfb 	if (do_output) {
164808f90673Sjfb 		while (cvp <= context_vec_ptr) {
164908f90673Sjfb 			a = cvp->a;
165008f90673Sjfb 			b = cvp->b;
165108f90673Sjfb 			c = cvp->c;
165208f90673Sjfb 			d = cvp->d;
165308f90673Sjfb 
165408f90673Sjfb 			if (a <= b && c <= d)
165508f90673Sjfb 				ch = 'c';
165608f90673Sjfb 			else
165708f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
165808f90673Sjfb 
165908f90673Sjfb 			if (ch == 'd')
166008f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
166108f90673Sjfb 			else {
166208f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
166308f90673Sjfb 				fetch(ixnew, c, d, f2,
166408f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
166508f90673Sjfb 			}
166608f90673Sjfb 			lowc = d + 1;
166708f90673Sjfb 			cvp++;
166808f90673Sjfb 		}
166908f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
167008f90673Sjfb 	}
167108f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
167208f90673Sjfb }
167308f90673Sjfb 
167408f90673Sjfb /* dump accumulated "unified" diff changes */
167508f90673Sjfb static void
167608f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
167708f90673Sjfb {
167808f90673Sjfb 	struct context_vec *cvp = context_vec_start;
167908f90673Sjfb 	int lowa, upb, lowc, upd;
168008f90673Sjfb 	int a, b, c, d;
16815e78344dSjfb 	char ch, *f;
168208f90673Sjfb 
168308f90673Sjfb 	if (context_vec_start > context_vec_ptr)
168408f90673Sjfb 		return;
168508f90673Sjfb 
168608f90673Sjfb 	b = d = 0;		/* gcc */
1687dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1688*e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1689dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1690*e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
169108f90673Sjfb 
169208f90673Sjfb 	fputs("@@ -", stdout);
169308f90673Sjfb 	uni_range(lowa, upb);
169408f90673Sjfb 	fputs(" +", stdout);
169508f90673Sjfb 	uni_range(lowc, upd);
169608f90673Sjfb 	fputs(" @@", stdout);
16975e78344dSjfb 	if (pflag) {
16985e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
16995e78344dSjfb 		if (f != NULL) {
17005e78344dSjfb 			putchar(' ');
17015e78344dSjfb 			fputs(f, stdout);
17025e78344dSjfb 		}
17035e78344dSjfb 	}
170408f90673Sjfb 	putchar('\n');
170508f90673Sjfb 
170608f90673Sjfb 	/*
170708f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
170808f90673Sjfb 	 * are printed together.
170908f90673Sjfb 	 */
171008f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
171108f90673Sjfb 		a = cvp->a;
171208f90673Sjfb 		b = cvp->b;
171308f90673Sjfb 		c = cvp->c;
171408f90673Sjfb 		d = cvp->d;
171508f90673Sjfb 
171608f90673Sjfb 		/*
171708f90673Sjfb 		 * c: both new and old changes
171808f90673Sjfb 		 * d: only changes in the old file
171908f90673Sjfb 		 * a: only changes in the new file
172008f90673Sjfb 		 */
172108f90673Sjfb 		if (a <= b && c <= d)
172208f90673Sjfb 			ch = 'c';
172308f90673Sjfb 		else
172408f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
172508f90673Sjfb 
172608f90673Sjfb 		switch (ch) {
172708f90673Sjfb 		case 'c':
172808f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
172908f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
173008f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
173108f90673Sjfb 			break;
173208f90673Sjfb 		case 'd':
173308f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
173408f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
173508f90673Sjfb 			break;
173608f90673Sjfb 		case 'a':
173708f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
173808f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
173908f90673Sjfb 			break;
174008f90673Sjfb 		}
174108f90673Sjfb 		lowa = b + 1;
174208f90673Sjfb 		lowc = d + 1;
174308f90673Sjfb 	}
174408f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
174508f90673Sjfb 
174608f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
174708f90673Sjfb }
1748