xref: /openbsd-src/usr.bin/cvs/diff.c (revision 9225b0cabd2bb268c53d45a280478779b86ee7bb)
1*9225b0caSxsa /*	$OpenBSD: diff.c,v 1.41 2005/05/31 08:58:47 xsa 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 
132*9225b0caSxsa #include <ctype.h>
133*9225b0caSxsa #include <dirent.h>
134394180a4Sjfb #include <err.h>
13508f90673Sjfb #include <errno.h>
13608f90673Sjfb #include <fcntl.h>
13708f90673Sjfb #include <paths.h>
13808f90673Sjfb #include <regex.h>
13908f90673Sjfb #include <stddef.h>
140*9225b0caSxsa #include <stdio.h>
141*9225b0caSxsa #include <stdlib.h>
14208f90673Sjfb #include <string.h>
143*9225b0caSxsa #include <unistd.h>
14408f90673Sjfb 
145*9225b0caSxsa #include "buf.h"
14608f90673Sjfb #include "cvs.h"
14708f90673Sjfb #include "log.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 
209e4276007Sjfb static int  cvs_diff_init      (struct cvs_cmd *, int, char **, int *);
210e4276007Sjfb static int  cvs_diff_remote    (CVSFILE *, void *);
211e4276007Sjfb static int  cvs_diff_local     (CVSFILE *, void *);
212e4276007Sjfb static int  cvs_diff_pre_exec (struct cvsroot *);
213e4276007Sjfb 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 */
257e4276007Sjfb 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 
335e4276007Sjfb 
336e4276007Sjfb struct cvs_cmd cvs_cmd_diff = {
337e4276007Sjfb 	CVS_OP_DIFF, CVS_REQ_DIFF, "diff",
338e4276007Sjfb 	{ "di", "dif" },
339e4276007Sjfb 	"Show differences between revisions",
340e4276007Sjfb 	"[-cilNnpu] [-D date] [-r rev] ...",
341e4276007Sjfb 	"cD:ilNnpr:Ru",
34216cfc147Sjoris 	NULL,
34316cfc147Sjoris 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
344e4276007Sjfb 	cvs_diff_init,
345e4276007Sjfb 	cvs_diff_pre_exec,
346e4276007Sjfb 	cvs_diff_remote,
347e4276007Sjfb 	cvs_diff_local,
348e4276007Sjfb 	NULL,
349e4276007Sjfb 	cvs_diff_cleanup,
350e4276007Sjfb 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
351e4276007Sjfb };
352e4276007Sjfb 
353e4276007Sjfb 
354e4276007Sjfb struct cvs_cmd cvs_cmd_rdiff = {
355e4276007Sjfb 	CVS_OP_RDIFF, CVS_REQ_DIFF, "rdiff",
356e4276007Sjfb 	{ "di", "dif" },
357e4276007Sjfb 	"Create 'patch' format diffs between releases",
358e4276007Sjfb 	"",
359e4276007Sjfb 	"",
360e4276007Sjfb 	NULL,
361e4276007Sjfb 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
362e4276007Sjfb 	cvs_diff_init,
363e4276007Sjfb 	cvs_diff_pre_exec,
364e4276007Sjfb 	cvs_diff_remote,
365e4276007Sjfb 	cvs_diff_local,
366e4276007Sjfb 	NULL,
367e4276007Sjfb 	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 
374e4276007Sjfb static int
375e4276007Sjfb 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 
385e4276007Sjfb 	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;
405e4276007Sjfb 			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':
437e4276007Sjfb 			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 {
455e4276007Sjfb 	if (dap != NULL) {
45616cfc147Sjoris 		free(dap);
457e4276007Sjfb 		dap = NULL;
458e4276007Sjfb 	}
45916cfc147Sjoris 	return (0);
46016cfc147Sjoris }
461dc6a6879Sjfb 
462dc6a6879Sjfb /*
463e4276007Sjfb  * cvs_diff_pre_exec()
464dc6a6879Sjfb  *
465dc6a6879Sjfb  */
466dc6a6879Sjfb int
467e4276007Sjfb cvs_diff_pre_exec(struct cvsroot *root)
468dc6a6879Sjfb {
469e4276007Sjfb 	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 
476610801e8Sxsa 		if (format == D_CONTEXT) {
477610801e8Sxsa 			if (cvs_sendarg(root, "-c", 0) < 0)
478610801e8Sxsa 				return (CVS_EX_PROTO);
479610801e8Sxsa 		} else if (format == D_UNIFIED) {
480610801e8Sxsa 			if (cvs_sendarg(root, "-u", 0) < 0)
481610801e8Sxsa 				return (CVS_EX_PROTO);
482610801e8Sxsa 		}
48308f90673Sjfb 
484dc6a6879Sjfb 		if (dap->rev1 != NULL) {
485610801e8Sxsa 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
486610801e8Sxsa 			    (cvs_sendarg(root, dap->rev1, 0) < 0))
487610801e8Sxsa 				return (CVS_EX_PROTO);
4883917c9bfSderaadt 		} else if (dap->date1 != NULL) {
489610801e8Sxsa 			if ((cvs_sendarg(root, "-D", 0) < 0) ||
490610801e8Sxsa 			    (cvs_sendarg(root, dap->date1, 0) < 0))
491610801e8Sxsa 				return (CVS_EX_PROTO);
49208f90673Sjfb 		}
493dc6a6879Sjfb 		if (dap->rev2 != NULL) {
494610801e8Sxsa 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
495610801e8Sxsa 			    (cvs_sendarg(root, dap->rev2, 0) < 0))
496610801e8Sxsa 				return (CVS_EX_PROTO);
4973917c9bfSderaadt 		} else if (dap->date2 != NULL) {
498610801e8Sxsa 			if ((cvs_sendarg(root, "-D", 0) < 0) ||
499610801e8Sxsa 			    (cvs_sendarg(root, dap->date2, 0) < 0))
500610801e8Sxsa 				return  (CVS_EX_PROTO);
50108f90673Sjfb 		}
502e4276007Sjfb 	}
50308f90673Sjfb 
50408f90673Sjfb 	return (0);
50508f90673Sjfb }
50608f90673Sjfb 
50708f90673Sjfb 
50808f90673Sjfb /*
50908f90673Sjfb  * cvs_diff_file()
51008f90673Sjfb  *
51108f90673Sjfb  * Diff a single file.
51208f90673Sjfb  */
513e4276007Sjfb static int
514e4276007Sjfb cvs_diff_remote(struct cvs_file *cfp, void *arg)
51508f90673Sjfb {
516e4276007Sjfb 	char *dir, *repo;
517e4276007Sjfb 	char fpath[MAXPATHLEN], dfpath[MAXPATHLEN];
518dc6a6879Sjfb 	struct cvsroot *root;
519dc6a6879Sjfb 
520dc6a6879Sjfb 	if (cfp->cf_type == DT_DIR) {
521895e6cf6Sjfb 		if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
522bb029937Sjfb 			root = cfp->cf_parent->cf_root;
523c710bc5aSjfb 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE,
524c710bc5aSjfb 			    CVS_FILE_NAME(cfp));
5253917c9bfSderaadt 		} else {
526bb029937Sjfb 			root = cfp->cf_root;
52716cfc147Sjoris #if 0
528dc6a6879Sjfb 			if ((cfp->cf_parent == NULL) ||
529bb029937Sjfb 			    (root != cfp->cf_parent->cf_root)) {
530dc6a6879Sjfb 				cvs_connect(root);
531e4276007Sjfb 				cvs_diff_pre_exec(root);
532dc6a6879Sjfb 			}
53316cfc147Sjoris #endif
534dc6a6879Sjfb 
535dc6a6879Sjfb 			cvs_senddir(root, cfp);
536895e6cf6Sjfb 		}
537895e6cf6Sjfb 
538dc6a6879Sjfb 		return (0);
539dc6a6879Sjfb 	}
54008f90673Sjfb 
5412d5b8b1dSjfb 	if (cfp->cf_cvstat == CVS_FST_LOST) {
5422d5b8b1dSjfb 		cvs_log(LP_WARN, "cannot find file %s", CVS_FILE_NAME(cfp));
5432d5b8b1dSjfb 		return (0);
5442d5b8b1dSjfb 	}
5452d5b8b1dSjfb 
546c710bc5aSjfb 	diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath));
547895e6cf6Sjfb 
548dc6a6879Sjfb 	if (cfp->cf_parent != NULL) {
549c710bc5aSjfb 		dir = cvs_file_getpath(cfp->cf_parent, dfpath, sizeof(dfpath));
550bb029937Sjfb 		root = cfp->cf_parent->cf_root;
551bb029937Sjfb 		repo = cfp->cf_parent->cf_repo;
5523917c9bfSderaadt 	} else {
553dc6a6879Sjfb 		dir = ".";
554895e6cf6Sjfb 		root = NULL;
555dc6a6879Sjfb 		repo = NULL;
55608f90673Sjfb 	}
55708f90673Sjfb 
558dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
559e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
560dc6a6879Sjfb 		return (0);
56108f90673Sjfb 	}
56208f90673Sjfb 
563e4276007Sjfb 	if (cvs_sendentry(root, cfp) < 0)
56431274bbfSjoris 		return (CVS_EX_PROTO);
56508f90673Sjfb 
566dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UPTODATE) {
567e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name);
56808f90673Sjfb 		return (0);
56908f90673Sjfb 	}
57008f90673Sjfb 
57108f90673Sjfb 	/* at this point, the file is modified */
572e4276007Sjfb 	if ((cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name) < 0) ||
573e4276007Sjfb 	    (cvs_sendfile(root, diff_file) < 0))
574e4276007Sjfb 		return (CVS_EX_PROTO);
575e4276007Sjfb 
576e4276007Sjfb 	return (0);
577e4276007Sjfb }
578e4276007Sjfb 
579e4276007Sjfb static int
580e4276007Sjfb cvs_diff_local(CVSFILE *cf, void *arg)
581e4276007Sjfb {
582e4276007Sjfb 	int len;
583e4276007Sjfb 	char *repo, buf[64];
584e4276007Sjfb 	char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
585e4276007Sjfb 	char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN];
586e4276007Sjfb 	BUF *b1, *b2;
587e4276007Sjfb 	RCSNUM *r1, *r2;
588e4276007Sjfb 	RCSFILE *rf;
589e4276007Sjfb 	struct cvsroot *root;
590e4276007Sjfb 
591e4276007Sjfb 	rf = NULL;
592e4276007Sjfb 	root = CVS_DIR_ROOT(cf);
593e4276007Sjfb 	repo = CVS_DIR_REPO(cf);
594e4276007Sjfb 	diff_file = cvs_file_getpath(cf, fpath, sizeof(fpath));
595e4276007Sjfb 
596e4276007Sjfb 	if (cf->cf_type == DT_DIR) {
597e4276007Sjfb 		cvs_log(LP_INFO, "Diffing %s", fpath);
598e4276007Sjfb 		return (0);
599e4276007Sjfb 	}
600e4276007Sjfb 
601e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_LOST) {
602e4276007Sjfb 		cvs_log(LP_WARN, "cannot find file %s", cf->cf_name);
603e4276007Sjfb 		return (0);
604e4276007Sjfb 	}
605e4276007Sjfb 
606e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_UNKNOWN) {
607e4276007Sjfb 		cvs_log(LP_WARN, "I know nothing about %s", diff_file);
608e4276007Sjfb 		return (0);
6094bd38884Sjoris 	} else if (cf->cf_cvstat == CVS_FST_UPTODATE && r1 == NULL)
610e4276007Sjfb 		return (0);
611e4276007Sjfb 
612e4276007Sjfb 	/* at this point, the file is modified */
613e4276007Sjfb 	len = snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
614dc6a6879Sjfb 	    root->cr_dir, repo, diff_file, RCS_FILE_EXT);
615e4276007Sjfb 	if (len == -1 || len >= (int)sizeof(rcspath)) {
61627b85f85Sxsa 		errno = ENAMETOOLONG;
61727b85f85Sxsa 		cvs_log(LP_ERRNO, "%s", rcspath);
61801b3d77aSjoris 		return (CVS_EX_DATA);
61927b85f85Sxsa 	}
62008f90673Sjfb 
6211b6534b8Sjfb 	rf = rcs_open(rcspath, RCS_READ);
622dc6a6879Sjfb 	if (rf == NULL) {
62331274bbfSjoris 		return (CVS_EX_DATA);
624dc6a6879Sjfb 	}
62508f90673Sjfb 
626dc6a6879Sjfb 	cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file,
62708f90673Sjfb 	    RCS_DIFF_DIV, rcspath);
62808f90673Sjfb 
629dc6a6879Sjfb 	if (dap->rev1 == NULL)
630e4276007Sjfb 		r1 = cf->cf_lrev;
63108f90673Sjfb 	else {
63225b74b48Sjfb 		if ((r1 = rcsnum_parse(dap->rev1)) == NULL) {
63331274bbfSjoris 			return (CVS_EX_DATA);
6347f535ec4Sjfb 		}
63508f90673Sjfb 	}
63608f90673Sjfb 
637dc6a6879Sjfb 	cvs_printf("retrieving revision %s\n",
63808f90673Sjfb 	    rcsnum_tostr(r1, buf, sizeof(buf)));
63908f90673Sjfb 	b1 = rcs_getrev(rf, r1);
64008f90673Sjfb 
64195ae1173Sjoris 	if (b1 == NULL) {
642289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
64395ae1173Sjoris 		    rcsnum_tostr(r1, buf, sizeof(buf)));
64495ae1173Sjoris 		if (r1 != cf->cf_lrev)
64595ae1173Sjoris 			rcsnum_free(r1);
64695ae1173Sjoris 		return (CVS_EX_DATA);
64795ae1173Sjoris 	}
64895ae1173Sjoris 
649e4276007Sjfb 	if (r1 != cf->cf_lrev)
6507f535ec4Sjfb 		rcsnum_free(r1);
6517f535ec4Sjfb 
652dc6a6879Sjfb 	if (dap->rev2 != NULL) {
653dc6a6879Sjfb 		cvs_printf("retrieving revision %s\n", dap->rev2);
65425b74b48Sjfb 		if ((r2 = rcsnum_parse(dap->rev2)) == NULL) {
65531274bbfSjoris 			return (CVS_EX_DATA);
6567f535ec4Sjfb 		}
65708f90673Sjfb 		b2 = rcs_getrev(rf, r2);
6587f535ec4Sjfb 		rcsnum_free(r2);
6593917c9bfSderaadt 	} else {
660dc6a6879Sjfb 		b2 = cvs_buf_load(diff_file, BUF_AUTOEXT);
66108f90673Sjfb 	}
66208f90673Sjfb 
663dc6a6879Sjfb 	rcs_close(rf);
664dc6a6879Sjfb 
66595ae1173Sjoris 	if (b2 == NULL) {
666289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
66795ae1173Sjoris 		    dap->rev2);
66895ae1173Sjoris 		cvs_buf_free(b1);
66995ae1173Sjoris 		return (CVS_EX_DATA);
67095ae1173Sjoris 	}
67195ae1173Sjoris 
672f5638424Sjfb 	printf("%s", diffargs);
673f5638424Sjfb 	printf(" -r%s", buf);
674dc6a6879Sjfb 	if (dap->rev2 != NULL)
675dc6a6879Sjfb 		printf(" -r%s", dap->rev2);
676dc6a6879Sjfb 	printf(" %s\n", diff_file);
677946f6157Sdjm 	strlcpy(path_tmp1, "/tmp/diff1.XXXXXXXXXX", sizeof(path_tmp1));
6787f535ec4Sjfb 	if (cvs_buf_write_stmp(b1, path_tmp1, 0600) == -1) {
6797f535ec4Sjfb 		cvs_buf_free(b1);
6807f535ec4Sjfb 		cvs_buf_free(b2);
68131274bbfSjoris 		return (CVS_EX_DATA);
6827f535ec4Sjfb 	}
6837f535ec4Sjfb 	cvs_buf_free(b1);
6847f535ec4Sjfb 
68569853e40Sjfb 	strlcpy(path_tmp2, "/tmp/diff2.XXXXXXXXXX", sizeof(path_tmp2));
686946f6157Sdjm 	if (cvs_buf_write_stmp(b2, path_tmp2, 0600) == -1) {
6877f535ec4Sjfb 		cvs_buf_free(b2);
688946f6157Sdjm 		(void)unlink(path_tmp1);
68931274bbfSjoris 		return (CVS_EX_DATA);
690946f6157Sdjm 	}
6917f535ec4Sjfb 	cvs_buf_free(b2);
6927f535ec4Sjfb 
693946f6157Sdjm 	cvs_diffreg(path_tmp1, path_tmp2);
694946f6157Sdjm 	(void)unlink(path_tmp1);
695946f6157Sdjm 	(void)unlink(path_tmp2);
69608f90673Sjfb 
69708f90673Sjfb 	return (0);
69808f90673Sjfb }
69908f90673Sjfb 
70008f90673Sjfb 
70108f90673Sjfb int
70208f90673Sjfb cvs_diffreg(const char *file1, const char *file2)
70308f90673Sjfb {
70408f90673Sjfb 	FILE *f1, *f2;
70508f90673Sjfb 	int i, rval;
7067f535ec4Sjfb 	void *tmp;
70708f90673Sjfb 
70808f90673Sjfb 	f1 = f2 = NULL;
70908f90673Sjfb 	rval = D_SAME;
71008f90673Sjfb 	anychange = 0;
71108f90673Sjfb 	lastline = 0;
71208f90673Sjfb 	lastmatchline = 0;
71308f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
71408f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
71508f90673Sjfb 
71608f90673Sjfb 	f1 = fopen(file1, "r");
71708f90673Sjfb 	if (f1 == NULL) {
71808f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
71908f90673Sjfb 		status |= 2;
72008f90673Sjfb 		goto closem;
72108f90673Sjfb 	}
72208f90673Sjfb 
72308f90673Sjfb 	f2 = fopen(file2, "r");
72408f90673Sjfb 	if (f2 == NULL) {
72508f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
72608f90673Sjfb 		status |= 2;
72708f90673Sjfb 		goto closem;
72808f90673Sjfb 	}
72908f90673Sjfb 
73008f90673Sjfb 	switch (files_differ(f1, f2)) {
73108f90673Sjfb 	case 0:
73208f90673Sjfb 		goto closem;
73308f90673Sjfb 	case 1:
73408f90673Sjfb 		break;
73508f90673Sjfb 	default:
73608f90673Sjfb 		/* error */
73708f90673Sjfb 		status |= 2;
73808f90673Sjfb 		goto closem;
73908f90673Sjfb 	}
74008f90673Sjfb 
74108f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
74208f90673Sjfb 		rval = D_BINARY;
74308f90673Sjfb 		status |= 1;
74408f90673Sjfb 		goto closem;
74508f90673Sjfb 	}
7467f535ec4Sjfb 	if ((prepare(0, f1, stb1.st_size) < 0) ||
7477f535ec4Sjfb 	    (prepare(1, f2, stb2.st_size) < 0)) {
7487f535ec4Sjfb 		status |= 2;
7497f535ec4Sjfb 		goto closem;
7507f535ec4Sjfb 	}
75108f90673Sjfb 	prune();
75208f90673Sjfb 	sort(sfile[0], slen[0]);
75308f90673Sjfb 	sort(sfile[1], slen[1]);
75408f90673Sjfb 
75508f90673Sjfb 	member = (int *)file[1];
75608f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
7577f535ec4Sjfb 	if ((tmp = realloc(member, (slen[1] + 2) * sizeof(int))) == NULL) {
7587f535ec4Sjfb 		status |= 2;
7597f535ec4Sjfb 		goto closem;
7607f535ec4Sjfb 	}
7617f535ec4Sjfb 	member = (int *)tmp;
76208f90673Sjfb 
76308f90673Sjfb 	class = (int *)file[0];
76408f90673Sjfb 	unsort(sfile[0], slen[0], class);
7657f535ec4Sjfb 	if ((tmp = realloc(class, (slen[0] + 2) * sizeof(int))) == NULL) {
7667f535ec4Sjfb 		status |= 2;
7677f535ec4Sjfb 		goto closem;
7687f535ec4Sjfb 	}
7697f535ec4Sjfb 	class = (int *)tmp;
77008f90673Sjfb 
7717f535ec4Sjfb 	if ((klist = malloc((slen[0] + 2) * sizeof(int))) == NULL) {
7727f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate klist");
7737f535ec4Sjfb 		status |= 2;
7747f535ec4Sjfb 		goto closem;
7757f535ec4Sjfb 	}
77608f90673Sjfb 	clen = 0;
77708f90673Sjfb 	clistlen = 100;
7787f535ec4Sjfb 	if ((clist = malloc(clistlen * sizeof(cand))) == NULL) {
7797f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate clist");
7807f535ec4Sjfb 		status |= 2;
7817f535ec4Sjfb 		goto closem;
7827f535ec4Sjfb 	}
78308f90673Sjfb 	i = stone(class, slen[0], member, klist);
78408f90673Sjfb 	free(member);
78508f90673Sjfb 	free(class);
78608f90673Sjfb 
787e4276007Sjfb 	J = realloc(J, (diff_len[0] + 2) * sizeof(int));
78808f90673Sjfb 	unravel(klist[i]);
78908f90673Sjfb 	free(clist);
79008f90673Sjfb 	free(klist);
79108f90673Sjfb 
792e4276007Sjfb 	ixold = realloc(ixold, (diff_len[0] + 2) * sizeof(long));
793e4276007Sjfb 	ixnew = realloc(ixnew, (diff_len[1] + 2) * sizeof(long));
79408f90673Sjfb 	check(f1, f2);
79508f90673Sjfb 	output(file1, f1, file2, f2);
79608f90673Sjfb 
79708f90673Sjfb closem:
79808f90673Sjfb 	if (anychange) {
79908f90673Sjfb 		status |= 1;
80008f90673Sjfb 		if (rval == D_SAME)
80108f90673Sjfb 			rval = D_DIFFER;
80208f90673Sjfb 	}
80308f90673Sjfb 	if (f1 != NULL)
80408f90673Sjfb 		fclose(f1);
80508f90673Sjfb 	if (f2 != NULL)
80608f90673Sjfb 		fclose(f2);
8077f535ec4Sjfb 
80808f90673Sjfb 	return (rval);
80908f90673Sjfb }
81008f90673Sjfb 
81108f90673Sjfb /*
81208f90673Sjfb  * Check to see if the given files differ.
81308f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
81408f90673Sjfb  * XXX - could use code from cmp(1) [faster]
81508f90673Sjfb  */
81608f90673Sjfb static int
81708f90673Sjfb files_differ(FILE *f1, FILE *f2)
81808f90673Sjfb {
81908f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
82008f90673Sjfb 	size_t i, j;
82108f90673Sjfb 
82208f90673Sjfb 	if (stb1.st_size != stb2.st_size)
82308f90673Sjfb 		return (1);
82408f90673Sjfb 	for (;;) {
82508f90673Sjfb 		i = fread(buf1, 1, sizeof(buf1), f1);
82608f90673Sjfb 		j = fread(buf2, 1, sizeof(buf2), f2);
82708f90673Sjfb 		if (i != j)
82808f90673Sjfb 			return (1);
82908f90673Sjfb 		if (i == 0 && j == 0) {
83008f90673Sjfb 			if (ferror(f1) || ferror(f2))
83108f90673Sjfb 				return (1);
83208f90673Sjfb 			return (0);
83308f90673Sjfb 		}
83408f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
83508f90673Sjfb 			return (1);
83608f90673Sjfb 	}
83708f90673Sjfb }
83808f90673Sjfb 
8397f535ec4Sjfb static int
84008f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
84108f90673Sjfb {
8427f535ec4Sjfb 	void *tmp;
84308f90673Sjfb 	struct line *p;
84408f90673Sjfb 	int j, h;
84508f90673Sjfb 	size_t sz;
84608f90673Sjfb 
84708f90673Sjfb 	rewind(fd);
84808f90673Sjfb 
849c48da046Sxsa 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
85008f90673Sjfb 	if (sz < 100)
85108f90673Sjfb 		sz = 100;
85208f90673Sjfb 
8537f535ec4Sjfb 	p = (struct line *)malloc((sz + 3) * sizeof(struct line));
8547f535ec4Sjfb 	if (p == NULL) {
8557f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to prepare line array");
8567f535ec4Sjfb 		return (-1);
8577f535ec4Sjfb 	}
85808f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
85908f90673Sjfb 		if (j == (int)sz) {
86008f90673Sjfb 			sz = sz * 3 / 2;
8617f535ec4Sjfb 			tmp = realloc(p, (sz + 3) * sizeof(struct line));
8627f535ec4Sjfb 			if (tmp == NULL) {
8637f535ec4Sjfb 				cvs_log(LP_ERRNO, "failed to grow line array");
8647f535ec4Sjfb 				free(p);
8657f535ec4Sjfb 				return (-1);
8667f535ec4Sjfb 			}
8677f535ec4Sjfb 			p = (struct line *)tmp;
86808f90673Sjfb 		}
86908f90673Sjfb 		p[++j].value = h;
87008f90673Sjfb 	}
871e4276007Sjfb 	diff_len[i] = j;
87208f90673Sjfb 	file[i] = p;
8737f535ec4Sjfb 
8747f535ec4Sjfb 	return (0);
87508f90673Sjfb }
87608f90673Sjfb 
87708f90673Sjfb static void
87808f90673Sjfb prune(void)
87908f90673Sjfb {
88008f90673Sjfb 	int i, j;
88108f90673Sjfb 
882e4276007Sjfb 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
88308f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
88408f90673Sjfb 	    pref++)
88508f90673Sjfb 		;
886e4276007Sjfb 	for (suff = 0;
887e4276007Sjfb 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
888e4276007Sjfb 	    (file[0][diff_len[0] - suff].value ==
889e4276007Sjfb 	    file[1][diff_len[1] - suff].value);
89008f90673Sjfb 	    suff++)
89108f90673Sjfb 		;
89208f90673Sjfb 	for (j = 0; j < 2; j++) {
89308f90673Sjfb 		sfile[j] = file[j] + pref;
894e4276007Sjfb 		slen[j] = diff_len[j] - pref - suff;
89508f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
89608f90673Sjfb 			sfile[j][i].serial = i;
89708f90673Sjfb 	}
89808f90673Sjfb }
89908f90673Sjfb 
90008f90673Sjfb static void
90108f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
90208f90673Sjfb {
90308f90673Sjfb 	int i, j;
90408f90673Sjfb 
90508f90673Sjfb 	i = j = 1;
90608f90673Sjfb 	while (i <= n && j <= m) {
90708f90673Sjfb 		if (a[i].value < b[j].value)
90808f90673Sjfb 			a[i++].value = 0;
90908f90673Sjfb 		else if (a[i].value == b[j].value)
91008f90673Sjfb 			a[i++].value = j;
91108f90673Sjfb 		else
91208f90673Sjfb 			j++;
91308f90673Sjfb 	}
91408f90673Sjfb 	while (i <= n)
91508f90673Sjfb 		a[i++].value = 0;
91608f90673Sjfb 	b[m + 1].value = 0;
91708f90673Sjfb 	j = 0;
91808f90673Sjfb 	while (++j <= m) {
91908f90673Sjfb 		c[j] = -b[j].serial;
92008f90673Sjfb 		while (b[j + 1].value == b[j].value) {
92108f90673Sjfb 			j++;
92208f90673Sjfb 			c[j] = b[j].serial;
92308f90673Sjfb 		}
92408f90673Sjfb 	}
92508f90673Sjfb 	c[j] = -1;
92608f90673Sjfb }
92708f90673Sjfb 
92808f90673Sjfb /* Code taken from ping.c */
92908f90673Sjfb static int
93008f90673Sjfb isqrt(int n)
93108f90673Sjfb {
93208f90673Sjfb 	int y, x = 1;
93308f90673Sjfb 
93408f90673Sjfb 	if (n == 0)
93508f90673Sjfb 		return (0);
93608f90673Sjfb 
93708f90673Sjfb 	do { /* newton was a stinker */
93808f90673Sjfb 		y = x;
93908f90673Sjfb 		x = n / x;
94008f90673Sjfb 		x += y;
94108f90673Sjfb 		x /= 2;
94208f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
94308f90673Sjfb 
94408f90673Sjfb 	return (x);
94508f90673Sjfb }
94608f90673Sjfb 
94708f90673Sjfb static int
94808f90673Sjfb stone(int *a, int n, int *b, int *c)
94908f90673Sjfb {
95008f90673Sjfb 	int i, k, y, j, l;
95108f90673Sjfb 	int oldc, tc, oldl;
95208f90673Sjfb 	u_int numtries;
95308f90673Sjfb 
954cc649edbSjfb 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
955cc649edbSjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
95608f90673Sjfb 
95708f90673Sjfb 	k = 0;
95808f90673Sjfb 	c[0] = newcand(0, 0, 0);
95908f90673Sjfb 	for (i = 1; i <= n; i++) {
96008f90673Sjfb 		j = a[i];
96108f90673Sjfb 		if (j == 0)
96208f90673Sjfb 			continue;
96308f90673Sjfb 		y = -b[j];
96408f90673Sjfb 		oldl = 0;
96508f90673Sjfb 		oldc = c[0];
96608f90673Sjfb 		numtries = 0;
96708f90673Sjfb 		do {
96808f90673Sjfb 			if (y <= clist[oldc].y)
96908f90673Sjfb 				continue;
97008f90673Sjfb 			l = search(c, k, y);
97108f90673Sjfb 			if (l != oldl + 1)
97208f90673Sjfb 				oldc = c[l - 1];
97308f90673Sjfb 			if (l <= k) {
97408f90673Sjfb 				if (clist[c[l]].y <= y)
97508f90673Sjfb 					continue;
97608f90673Sjfb 				tc = c[l];
97708f90673Sjfb 				c[l] = newcand(i, y, oldc);
97808f90673Sjfb 				oldc = tc;
97908f90673Sjfb 				oldl = l;
98008f90673Sjfb 				numtries++;
98108f90673Sjfb 			} else {
98208f90673Sjfb 				c[l] = newcand(i, y, oldc);
98308f90673Sjfb 				k++;
98408f90673Sjfb 				break;
98508f90673Sjfb 			}
98608f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
98708f90673Sjfb 	}
98808f90673Sjfb 	return (k);
98908f90673Sjfb }
99008f90673Sjfb 
99108f90673Sjfb static int
99208f90673Sjfb newcand(int x, int y, int pred)
99308f90673Sjfb {
99408f90673Sjfb 	struct cand *q;
99508f90673Sjfb 
99608f90673Sjfb 	if (clen == clistlen) {
99708f90673Sjfb 		clistlen = clistlen * 11 / 10;
99808f90673Sjfb 		clist = realloc(clist, clistlen * sizeof(cand));
9997f535ec4Sjfb 		if (clist == NULL) {
10007f535ec4Sjfb 			cvs_log(LP_ERRNO, "failed to resize clist");
10017f535ec4Sjfb 			return (-1);
10027f535ec4Sjfb 		}
100308f90673Sjfb 	}
100408f90673Sjfb 	q = clist + clen;
100508f90673Sjfb 	q->x = x;
100608f90673Sjfb 	q->y = y;
100708f90673Sjfb 	q->pred = pred;
100808f90673Sjfb 	return (clen++);
100908f90673Sjfb }
101008f90673Sjfb 
101108f90673Sjfb static int
101208f90673Sjfb search(int *c, int k, int y)
101308f90673Sjfb {
101408f90673Sjfb 	int i, j, l, t;
101508f90673Sjfb 
101608f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
101708f90673Sjfb 		return (k + 1);
101808f90673Sjfb 	i = 0;
101908f90673Sjfb 	j = k + 1;
102008f90673Sjfb 	while (1) {
102108f90673Sjfb 		l = i + j;
102208f90673Sjfb 		if ((l >>= 1) <= i)
102308f90673Sjfb 			break;
102408f90673Sjfb 		t = clist[c[l]].y;
102508f90673Sjfb 		if (t > y)
102608f90673Sjfb 			j = l;
102708f90673Sjfb 		else if (t < y)
102808f90673Sjfb 			i = l;
102908f90673Sjfb 		else
103008f90673Sjfb 			return (l);
103108f90673Sjfb 	}
103208f90673Sjfb 	return (l + 1);
103308f90673Sjfb }
103408f90673Sjfb 
103508f90673Sjfb static void
103608f90673Sjfb unravel(int p)
103708f90673Sjfb {
103808f90673Sjfb 	struct cand *q;
103908f90673Sjfb 	int i;
104008f90673Sjfb 
1041e4276007Sjfb 	for (i = 0; i <= diff_len[0]; i++)
104208f90673Sjfb 		J[i] = i <= pref ? i :
1043e4276007Sjfb 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
104408f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
104508f90673Sjfb 		J[q->x + pref] = q->y + pref;
104608f90673Sjfb }
104708f90673Sjfb 
104808f90673Sjfb /*
104908f90673Sjfb  * Check does double duty:
105008f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
105108f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
105208f90673Sjfb  *  2.  collect random access indexes to the two files
105308f90673Sjfb  */
105408f90673Sjfb static void
105508f90673Sjfb check(FILE *f1, FILE *f2)
105608f90673Sjfb {
105708f90673Sjfb 	int i, j, jackpot, c, d;
105808f90673Sjfb 	long ctold, ctnew;
105908f90673Sjfb 
106008f90673Sjfb 	rewind(f1);
106108f90673Sjfb 	rewind(f2);
106208f90673Sjfb 	j = 1;
106308f90673Sjfb 	ixold[0] = ixnew[0] = 0;
106408f90673Sjfb 	jackpot = 0;
106508f90673Sjfb 	ctold = ctnew = 0;
1066e4276007Sjfb 	for (i = 1; i <= diff_len[0]; i++) {
106708f90673Sjfb 		if (J[i] == 0) {
106808f90673Sjfb 			ixold[i] = ctold += skipline(f1);
106908f90673Sjfb 			continue;
107008f90673Sjfb 		}
107108f90673Sjfb 		while (j < J[i]) {
107208f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
107308f90673Sjfb 			j++;
107408f90673Sjfb 		}
107508f90673Sjfb 		if (bflag || wflag || iflag) {
107608f90673Sjfb 			for (;;) {
107708f90673Sjfb 				c = getc(f1);
107808f90673Sjfb 				d = getc(f2);
107908f90673Sjfb 				/*
108008f90673Sjfb 				 * GNU diff ignores a missing newline
108108f90673Sjfb 				 * in one file if bflag || wflag.
108208f90673Sjfb 				 */
108308f90673Sjfb 				if ((bflag || wflag) &&
108408f90673Sjfb 				    ((c == EOF && d == '\n') ||
108508f90673Sjfb 				    (c == '\n' && d == EOF))) {
108608f90673Sjfb 					break;
108708f90673Sjfb 				}
108808f90673Sjfb 				ctold++;
108908f90673Sjfb 				ctnew++;
109008f90673Sjfb 				if (bflag && isspace(c) && isspace(d)) {
109108f90673Sjfb 					do {
109208f90673Sjfb 						if (c == '\n')
109308f90673Sjfb 							break;
109408f90673Sjfb 						ctold++;
109508f90673Sjfb 					} while (isspace(c = getc(f1)));
109608f90673Sjfb 					do {
109708f90673Sjfb 						if (d == '\n')
109808f90673Sjfb 							break;
109908f90673Sjfb 						ctnew++;
110008f90673Sjfb 					} while (isspace(d = getc(f2)));
110108f90673Sjfb 				} else if (wflag) {
110208f90673Sjfb 					while (isspace(c) && c != '\n') {
110308f90673Sjfb 						c = getc(f1);
110408f90673Sjfb 						ctold++;
110508f90673Sjfb 					}
110608f90673Sjfb 					while (isspace(d) && d != '\n') {
110708f90673Sjfb 						d = getc(f2);
110808f90673Sjfb 						ctnew++;
110908f90673Sjfb 					}
111008f90673Sjfb 				}
111108f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
111208f90673Sjfb 					jackpot++;
111308f90673Sjfb 					J[i] = 0;
111408f90673Sjfb 					if (c != '\n' && c != EOF)
111508f90673Sjfb 						ctold += skipline(f1);
111608f90673Sjfb 					if (d != '\n' && c != EOF)
111708f90673Sjfb 						ctnew += skipline(f2);
111808f90673Sjfb 					break;
111908f90673Sjfb 				}
112008f90673Sjfb 				if (c == '\n' || c == EOF)
112108f90673Sjfb 					break;
112208f90673Sjfb 			}
112308f90673Sjfb 		} else {
112408f90673Sjfb 			for (;;) {
112508f90673Sjfb 				ctold++;
112608f90673Sjfb 				ctnew++;
112708f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
112808f90673Sjfb 					/* jackpot++; */
112908f90673Sjfb 					J[i] = 0;
113008f90673Sjfb 					if (c != '\n' && c != EOF)
113108f90673Sjfb 						ctold += skipline(f1);
113208f90673Sjfb 					if (d != '\n' && c != EOF)
113308f90673Sjfb 						ctnew += skipline(f2);
113408f90673Sjfb 					break;
113508f90673Sjfb 				}
113608f90673Sjfb 				if (c == '\n' || c == EOF)
113708f90673Sjfb 					break;
113808f90673Sjfb 			}
113908f90673Sjfb 		}
114008f90673Sjfb 		ixold[i] = ctold;
114108f90673Sjfb 		ixnew[j] = ctnew;
114208f90673Sjfb 		j++;
114308f90673Sjfb 	}
1144e4276007Sjfb 	for (; j <= diff_len[1]; j++)
114508f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
114608f90673Sjfb 	/*
114708f90673Sjfb 	 * if (jackpot)
114808f90673Sjfb 	 *	fprintf(stderr, "jackpot\n");
114908f90673Sjfb 	 */
115008f90673Sjfb }
115108f90673Sjfb 
115208f90673Sjfb /* shellsort CACM #201 */
115308f90673Sjfb static void
115408f90673Sjfb sort(struct line *a, int n)
115508f90673Sjfb {
115608f90673Sjfb 	struct line *ai, *aim, w;
115708f90673Sjfb 	int j, m = 0, k;
115808f90673Sjfb 
115908f90673Sjfb 	if (n == 0)
116008f90673Sjfb 		return;
116108f90673Sjfb 	for (j = 1; j <= n; j *= 2)
116208f90673Sjfb 		m = 2 * j - 1;
116308f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
116408f90673Sjfb 		k = n - m;
116508f90673Sjfb 		for (j = 1; j <= k; j++) {
116608f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
116708f90673Sjfb 				aim = &ai[m];
116808f90673Sjfb 				if (aim < ai)
116908f90673Sjfb 					break;	/* wraparound */
117008f90673Sjfb 				if (aim->value > ai[0].value ||
117108f90673Sjfb 				    (aim->value == ai[0].value &&
117208f90673Sjfb 					aim->serial > ai[0].serial))
117308f90673Sjfb 					break;
117408f90673Sjfb 				w.value = ai[0].value;
117508f90673Sjfb 				ai[0].value = aim->value;
117608f90673Sjfb 				aim->value = w.value;
117708f90673Sjfb 				w.serial = ai[0].serial;
117808f90673Sjfb 				ai[0].serial = aim->serial;
117908f90673Sjfb 				aim->serial = w.serial;
118008f90673Sjfb 			}
118108f90673Sjfb 		}
118208f90673Sjfb 	}
118308f90673Sjfb }
118408f90673Sjfb 
118508f90673Sjfb static void
118608f90673Sjfb unsort(struct line *f, int l, int *b)
118708f90673Sjfb {
118808f90673Sjfb 	int *a, i;
118908f90673Sjfb 
11907f535ec4Sjfb 	if ((a = (int *)malloc((l + 1) * sizeof(int))) == NULL) {
11917f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate sort array");
11927f535ec4Sjfb 		return;
11937f535ec4Sjfb 	}
119408f90673Sjfb 	for (i = 1; i <= l; i++)
119508f90673Sjfb 		a[f[i].serial] = f[i].value;
119608f90673Sjfb 	for (i = 1; i <= l; i++)
119708f90673Sjfb 		b[i] = a[i];
119808f90673Sjfb 	free(a);
119908f90673Sjfb }
120008f90673Sjfb 
120108f90673Sjfb static int
120208f90673Sjfb skipline(FILE *f)
120308f90673Sjfb {
120408f90673Sjfb 	int i, c;
120508f90673Sjfb 
120608f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
120708f90673Sjfb 		continue;
120808f90673Sjfb 	return (i);
120908f90673Sjfb }
121008f90673Sjfb 
121108f90673Sjfb static void
121208f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2)
121308f90673Sjfb {
121408f90673Sjfb 	int m, i0, i1, j0, j1;
121508f90673Sjfb 
121608f90673Sjfb 	rewind(f1);
121708f90673Sjfb 	rewind(f2);
1218e4276007Sjfb 	m = diff_len[0];
121908f90673Sjfb 	J[0] = 0;
1220e4276007Sjfb 	J[m + 1] = diff_len[1] + 1;
122108f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
122208f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
122308f90673Sjfb 			i0++;
122408f90673Sjfb 		j0 = J[i0 - 1] + 1;
122508f90673Sjfb 		i1 = i0 - 1;
122608f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
122708f90673Sjfb 			i1++;
122808f90673Sjfb 		j1 = J[i1 + 1] - 1;
122908f90673Sjfb 		J[i1] = j1;
123008f90673Sjfb 		change(file1, f1, file2, f2, i0, i1, j0, j1);
123108f90673Sjfb 	}
123208f90673Sjfb 	if (m == 0)
1233e4276007Sjfb 		change(file1, f1, file2, f2, 1, 0, 1, diff_len[1]);
123408f90673Sjfb 	if (format == D_IFDEF) {
123508f90673Sjfb 		for (;;) {
123608f90673Sjfb #define	c i0
123708f90673Sjfb 			if ((c = getc(f1)) == EOF)
123808f90673Sjfb 				return;
123908f90673Sjfb 			putchar(c);
124008f90673Sjfb 		}
124108f90673Sjfb #undef c
124208f90673Sjfb 	}
124308f90673Sjfb 	if (anychange != 0) {
124408f90673Sjfb 		if (format == D_CONTEXT)
124508f90673Sjfb 			dump_context_vec(f1, f2);
124608f90673Sjfb 		else if (format == D_UNIFIED)
124708f90673Sjfb 			dump_unified_vec(f1, f2);
124808f90673Sjfb 	}
124908f90673Sjfb }
125008f90673Sjfb 
125108f90673Sjfb static __inline void
125208f90673Sjfb range(int a, int b, char *separator)
125308f90673Sjfb {
125408f90673Sjfb 	printf("%d", a > b ? b : a);
125508f90673Sjfb 	if (a < b)
125608f90673Sjfb 		printf("%s%d", separator, b);
125708f90673Sjfb }
125808f90673Sjfb 
125908f90673Sjfb static __inline void
126008f90673Sjfb uni_range(int a, int b)
126108f90673Sjfb {
126208f90673Sjfb 	if (a < b)
126308f90673Sjfb 		printf("%d,%d", a, b - a + 1);
126408f90673Sjfb 	else if (a == b)
126508f90673Sjfb 		printf("%d", b);
126608f90673Sjfb 	else
126708f90673Sjfb 		printf("%d,0", b);
126808f90673Sjfb }
126908f90673Sjfb 
127008f90673Sjfb static char *
12712a0de57dSjfb preadline(int fd, size_t rlen, off_t off)
127208f90673Sjfb {
127308f90673Sjfb 	char *line;
127408f90673Sjfb 	ssize_t nr;
127508f90673Sjfb 
12762a0de57dSjfb 	line = malloc(rlen + 1);
127708f90673Sjfb 	if (line == NULL) {
127808f90673Sjfb 		cvs_log(LP_ERRNO, "failed to allocate line");
127908f90673Sjfb 		return (NULL);
128008f90673Sjfb 	}
12812a0de57dSjfb 	if ((nr = pread(fd, line, rlen, off)) < 0) {
128208f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
128308f90673Sjfb 		return (NULL);
128408f90673Sjfb 	}
128508f90673Sjfb 	line[nr] = '\0';
128608f90673Sjfb 	return (line);
128708f90673Sjfb }
128808f90673Sjfb 
128908f90673Sjfb static int
129008f90673Sjfb ignoreline(char *line)
129108f90673Sjfb {
129208f90673Sjfb 	int ret;
129308f90673Sjfb 
129408f90673Sjfb 	ret = regexec(&ignore_re, line, 0, NULL, 0);
129508f90673Sjfb 	free(line);
129608f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
129708f90673Sjfb }
129808f90673Sjfb 
129908f90673Sjfb /*
130008f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
130108f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
130208f90673Sjfb  * are no lines in the from file involved and this means that there were
130308f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
130408f90673Sjfb  * lines missing from the to file.
130508f90673Sjfb  */
130608f90673Sjfb static void
130708f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2,
130808f90673Sjfb 	int a, int b, int c, int d)
130908f90673Sjfb {
131008f90673Sjfb 	static size_t max_context = 64;
131108f90673Sjfb 	int i;
131208f90673Sjfb 
131308f90673Sjfb 	if (format != D_IFDEF && a > b && c > d)
131408f90673Sjfb 		return;
131508f90673Sjfb 	if (ignore_pats != NULL) {
131608f90673Sjfb 		char *line;
131708f90673Sjfb 		/*
131808f90673Sjfb 		 * All lines in the change, insert, or delete must
131908f90673Sjfb 		 * match an ignore pattern for the change to be
132008f90673Sjfb 		 * ignored.
132108f90673Sjfb 		 */
132208f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
132308f90673Sjfb 			for (i = a; i <= b; i++) {
132408f90673Sjfb 				line = preadline(fileno(f1),
132508f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
132608f90673Sjfb 				if (!ignoreline(line))
132708f90673Sjfb 					goto proceed;
132808f90673Sjfb 			}
132908f90673Sjfb 		}
133008f90673Sjfb 		if (a > b || c <= d) {	/* Changes and inserts. */
133108f90673Sjfb 			for (i = c; i <= d; i++) {
133208f90673Sjfb 				line = preadline(fileno(f2),
133308f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
133408f90673Sjfb 				if (!ignoreline(line))
133508f90673Sjfb 					goto proceed;
133608f90673Sjfb 			}
133708f90673Sjfb 		}
133808f90673Sjfb 		return;
133908f90673Sjfb 	}
134008f90673Sjfb proceed:
134108f90673Sjfb 	if (format == D_CONTEXT || format == D_UNIFIED) {
134208f90673Sjfb 		/*
134308f90673Sjfb 		 * Allocate change records as needed.
134408f90673Sjfb 		 */
134508f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
134608f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
134708f90673Sjfb 			max_context <<= 1;
134808f90673Sjfb 			context_vec_start = realloc(context_vec_start,
134908f90673Sjfb 			    max_context * sizeof(struct context_vec));
135008f90673Sjfb 			context_vec_end = context_vec_start + max_context;
135108f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
135208f90673Sjfb 		}
135308f90673Sjfb 		if (anychange == 0) {
135408f90673Sjfb 			/*
135508f90673Sjfb 			 * Print the context/unidiff header first time through.
135608f90673Sjfb 			 */
135708f90673Sjfb 			printf("%s %s	%s",
135808f90673Sjfb 			    format == D_CONTEXT ? "***" : "---", diff_file,
135908f90673Sjfb 			    ctime(&stb1.st_mtime));
136008f90673Sjfb 			printf("%s %s	%s",
136108f90673Sjfb 			    format == D_CONTEXT ? "---" : "+++", diff_file,
136208f90673Sjfb 			    ctime(&stb2.st_mtime));
136308f90673Sjfb 			anychange = 1;
136408f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
136508f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
136608f90673Sjfb 			/*
136708f90673Sjfb 			 * If this change is more than 'context' lines from the
136808f90673Sjfb 			 * previous change, dump the record and reset it.
136908f90673Sjfb 			 */
137008f90673Sjfb 			if (format == D_CONTEXT)
137108f90673Sjfb 				dump_context_vec(f1, f2);
137208f90673Sjfb 			else
137308f90673Sjfb 				dump_unified_vec(f1, f2);
137408f90673Sjfb 		}
137508f90673Sjfb 		context_vec_ptr++;
137608f90673Sjfb 		context_vec_ptr->a = a;
137708f90673Sjfb 		context_vec_ptr->b = b;
137808f90673Sjfb 		context_vec_ptr->c = c;
137908f90673Sjfb 		context_vec_ptr->d = d;
138008f90673Sjfb 		return;
138108f90673Sjfb 	}
138208f90673Sjfb 	if (anychange == 0)
138308f90673Sjfb 		anychange = 1;
138408f90673Sjfb 	switch (format) {
138508f90673Sjfb 	case D_BRIEF:
138608f90673Sjfb 		return;
138708f90673Sjfb 	case D_NORMAL:
138808f90673Sjfb 		range(a, b, ",");
138908f90673Sjfb 		putchar(a > b ? 'a' : c > d ? 'd' : 'c');
139008f90673Sjfb 		if (format == D_NORMAL)
139108f90673Sjfb 			range(c, d, ",");
139208f90673Sjfb 		putchar('\n');
139308f90673Sjfb 		break;
1394394180a4Sjfb 	case D_RCSDIFF:
1395394180a4Sjfb 		if (a > b)
1396394180a4Sjfb 			printf("a%d %d\n", b, d - c + 1);
1397394180a4Sjfb 		else {
1398394180a4Sjfb 			printf("d%d %d\n", a, b - a + 1);
1399394180a4Sjfb 
1400394180a4Sjfb 			if (!(c > d))	/* add changed lines */
1401394180a4Sjfb 				printf("a%d %d\n", b, d - c + 1);
1402394180a4Sjfb 		}
1403394180a4Sjfb 		break;
140408f90673Sjfb 	}
140508f90673Sjfb 	if (format == D_NORMAL || format == D_IFDEF) {
140608f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
140708f90673Sjfb 		if (a <= b && c <= d && format == D_NORMAL)
140808f90673Sjfb 			puts("---");
140908f90673Sjfb 	}
141008f90673Sjfb 	i = fetch(ixnew, c, d, f2, format == D_NORMAL ? '>' : '\0', 0);
141108f90673Sjfb 	if (inifdef) {
141208f90673Sjfb 		printf("#endif /* %s */\n", ifdefname);
141308f90673Sjfb 		inifdef = 0;
141408f90673Sjfb 	}
141508f90673Sjfb }
141608f90673Sjfb 
141708f90673Sjfb static int
141808f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
141908f90673Sjfb {
142008f90673Sjfb 	int i, j, c, lastc, col, nc;
142108f90673Sjfb 
142208f90673Sjfb 	/*
142308f90673Sjfb 	 * When doing #ifdef's, copy down to current line
142408f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
142508f90673Sjfb 	 */
142608f90673Sjfb 	if (format == D_IFDEF && oldfile) {
142708f90673Sjfb 		long curpos = ftell(lb);
142808f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
142908f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
143008f90673Sjfb 		for (i = 0; i < nc; i++)
143108f90673Sjfb 			putchar(getc(lb));
143208f90673Sjfb 	}
143308f90673Sjfb 	if (a > b)
143408f90673Sjfb 		return (0);
143508f90673Sjfb 	if (format == D_IFDEF) {
143608f90673Sjfb 		if (inifdef) {
143708f90673Sjfb 			printf("#else /* %s%s */\n",
143808f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
143908f90673Sjfb 		} else {
144008f90673Sjfb 			if (oldfile)
144108f90673Sjfb 				printf("#ifndef %s\n", ifdefname);
144208f90673Sjfb 			else
144308f90673Sjfb 				printf("#ifdef %s\n", ifdefname);
144408f90673Sjfb 		}
144508f90673Sjfb 		inifdef = 1 + oldfile;
144608f90673Sjfb 	}
144708f90673Sjfb 	for (i = a; i <= b; i++) {
144808f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
144908f90673Sjfb 		nc = f[i] - f[i - 1];
145008f90673Sjfb 		if (format != D_IFDEF && ch != '\0') {
145108f90673Sjfb 			putchar(ch);
145208f90673Sjfb 			if (Tflag && (format == D_NORMAL || format == D_CONTEXT
145308f90673Sjfb 			    || format == D_UNIFIED))
145408f90673Sjfb 				putchar('\t');
145508f90673Sjfb 			else if (format != D_UNIFIED)
145608f90673Sjfb 				putchar(' ');
145708f90673Sjfb 		}
145808f90673Sjfb 		col = 0;
145908f90673Sjfb 		for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
146008f90673Sjfb 			if ((c = getc(lb)) == EOF) {
1461394180a4Sjfb 				if (format == D_RCSDIFF)
1462394180a4Sjfb 					warnx("No newline at end of file");
1463394180a4Sjfb 				else
146408f90673Sjfb 					puts("\n\\ No newline at end of file");
146508f90673Sjfb 				return (0);
146608f90673Sjfb 			}
146708f90673Sjfb 			if (c == '\t' && tflag) {
146808f90673Sjfb 				do {
146908f90673Sjfb 					putchar(' ');
147008f90673Sjfb 				} while (++col & 7);
147108f90673Sjfb 			} else {
147208f90673Sjfb 				putchar(c);
147308f90673Sjfb 				col++;
147408f90673Sjfb 			}
147508f90673Sjfb 		}
147608f90673Sjfb 	}
147708f90673Sjfb 	return (0);
147808f90673Sjfb }
147908f90673Sjfb 
148008f90673Sjfb /*
148108f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
148208f90673Sjfb  */
148308f90673Sjfb static int
148408f90673Sjfb readhash(FILE *f)
148508f90673Sjfb {
148608f90673Sjfb 	int i, t, space;
148708f90673Sjfb 	int sum;
148808f90673Sjfb 
148908f90673Sjfb 	sum = 1;
149008f90673Sjfb 	space = 0;
149108f90673Sjfb 	if (!bflag && !wflag) {
149208f90673Sjfb 		if (iflag)
149308f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
149408f90673Sjfb 				if (t == EOF) {
149508f90673Sjfb 					if (i == 0)
149608f90673Sjfb 						return (0);
149708f90673Sjfb 					break;
149808f90673Sjfb 				}
149908f90673Sjfb 				sum = sum * 127 + chrtran[t];
150008f90673Sjfb 			}
150108f90673Sjfb 		else
150208f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
150308f90673Sjfb 				if (t == EOF) {
150408f90673Sjfb 					if (i == 0)
150508f90673Sjfb 						return (0);
150608f90673Sjfb 					break;
150708f90673Sjfb 				}
150808f90673Sjfb 				sum = sum * 127 + t;
150908f90673Sjfb 			}
151008f90673Sjfb 	} else {
151108f90673Sjfb 		for (i = 0;;) {
151208f90673Sjfb 			switch (t = getc(f)) {
151308f90673Sjfb 			case '\t':
151408f90673Sjfb 			case ' ':
151508f90673Sjfb 				space++;
151608f90673Sjfb 				continue;
151708f90673Sjfb 			default:
151808f90673Sjfb 				if (space && !wflag) {
151908f90673Sjfb 					i++;
152008f90673Sjfb 					space = 0;
152108f90673Sjfb 				}
152208f90673Sjfb 				sum = sum * 127 + chrtran[t];
152308f90673Sjfb 				i++;
152408f90673Sjfb 				continue;
152508f90673Sjfb 			case EOF:
152608f90673Sjfb 				if (i == 0)
152708f90673Sjfb 					return (0);
152808f90673Sjfb 				/* FALLTHROUGH */
152908f90673Sjfb 			case '\n':
153008f90673Sjfb 				break;
153108f90673Sjfb 			}
153208f90673Sjfb 			break;
153308f90673Sjfb 		}
153408f90673Sjfb 	}
153508f90673Sjfb 	/*
153608f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
153708f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
153808f90673Sjfb 	 */
153908f90673Sjfb 	return (sum == 0 ? 1 : sum);
154008f90673Sjfb }
154108f90673Sjfb 
154208f90673Sjfb static int
154308f90673Sjfb asciifile(FILE *f)
154408f90673Sjfb {
154508f90673Sjfb 	char buf[BUFSIZ];
154608f90673Sjfb 	int i, cnt;
154708f90673Sjfb 
154808f90673Sjfb 	if (aflag || f == NULL)
154908f90673Sjfb 		return (1);
155008f90673Sjfb 
155108f90673Sjfb 	rewind(f);
155208f90673Sjfb 	cnt = fread(buf, 1, sizeof(buf), f);
155308f90673Sjfb 	for (i = 0; i < cnt; i++)
155408f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
155508f90673Sjfb 			return (0);
155608f90673Sjfb 	return (1);
155708f90673Sjfb }
155808f90673Sjfb 
15595e78344dSjfb static char*
15605e78344dSjfb match_function(const long *f, int pos, FILE *fp)
15615e78344dSjfb {
15625e78344dSjfb 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
15635e78344dSjfb 	size_t nc;
15645e78344dSjfb 	int last = lastline;
15655e78344dSjfb 	char *p;
15665e78344dSjfb 
15675e78344dSjfb 	lastline = pos;
15685e78344dSjfb 	while (pos > last) {
15695e78344dSjfb 		fseek(fp, f[pos - 1], SEEK_SET);
15705e78344dSjfb 		nc = f[pos] - f[pos - 1];
15715e78344dSjfb 		if (nc >= sizeof(buf))
15725e78344dSjfb 			nc = sizeof(buf) - 1;
15735e78344dSjfb 		nc = fread(buf, 1, nc, fp);
15745e78344dSjfb 		if (nc > 0) {
15755e78344dSjfb 			buf[nc] = '\0';
15765e78344dSjfb 			p = strchr(buf, '\n');
15775e78344dSjfb 			if (p != NULL)
15785e78344dSjfb 				*p = '\0';
15795e78344dSjfb 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
15805e78344dSjfb 				strlcpy(lastbuf, buf, sizeof lastbuf);
15815e78344dSjfb 				lastmatchline = pos;
15825e78344dSjfb 				return lastbuf;
15835e78344dSjfb 			}
15845e78344dSjfb 		}
15855e78344dSjfb 		pos--;
15865e78344dSjfb 	}
15875e78344dSjfb 	return (lastmatchline > 0) ? lastbuf : NULL;
15885e78344dSjfb }
15895e78344dSjfb 
159008f90673Sjfb 
159108f90673Sjfb /* dump accumulated "context" diff changes */
159208f90673Sjfb static void
159308f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
159408f90673Sjfb {
159508f90673Sjfb 	struct context_vec *cvp = context_vec_start;
159608f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
159708f90673Sjfb 	int a, b, c, d;
15985e78344dSjfb 	char ch, *f;
159908f90673Sjfb 
160008f90673Sjfb 	if (context_vec_start > context_vec_ptr)
160108f90673Sjfb 		return;
160208f90673Sjfb 
160308f90673Sjfb 	b = d = 0;		/* gcc */
1604dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1605e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1606dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1607e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
160808f90673Sjfb 
160908f90673Sjfb 	printf("***************");
16105e78344dSjfb 	if (pflag) {
16115e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
16125e78344dSjfb 		if (f != NULL) {
16135e78344dSjfb 			putchar(' ');
16145e78344dSjfb 			fputs(f, stdout);
16155e78344dSjfb 		}
16165e78344dSjfb 	}
161708f90673Sjfb 	printf("\n*** ");
161808f90673Sjfb 	range(lowa, upb, ",");
161908f90673Sjfb 	printf(" ****\n");
162008f90673Sjfb 
162108f90673Sjfb 	/*
162208f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
162308f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
162408f90673Sjfb 	 * the "old" lines as context in the "new" list).
162508f90673Sjfb 	 */
162608f90673Sjfb 	do_output = 0;
162708f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
162808f90673Sjfb 		if (cvp->a <= cvp->b) {
162908f90673Sjfb 			cvp = context_vec_start;
163008f90673Sjfb 			do_output++;
163108f90673Sjfb 			break;
163208f90673Sjfb 		}
163308f90673Sjfb 	if (do_output) {
163408f90673Sjfb 		while (cvp <= context_vec_ptr) {
163508f90673Sjfb 			a = cvp->a;
163608f90673Sjfb 			b = cvp->b;
163708f90673Sjfb 			c = cvp->c;
163808f90673Sjfb 			d = cvp->d;
163908f90673Sjfb 
164008f90673Sjfb 			if (a <= b && c <= d)
164108f90673Sjfb 				ch = 'c';
164208f90673Sjfb 			else
164308f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
164408f90673Sjfb 
164508f90673Sjfb 			if (ch == 'a')
164608f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
164708f90673Sjfb 			else {
164808f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
164908f90673Sjfb 				fetch(ixold, a, b, f1,
165008f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
165108f90673Sjfb 			}
165208f90673Sjfb 			lowa = b + 1;
165308f90673Sjfb 			cvp++;
165408f90673Sjfb 		}
165508f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
165608f90673Sjfb 	}
165708f90673Sjfb 	/* output changes to the "new" file */
165808f90673Sjfb 	printf("--- ");
165908f90673Sjfb 	range(lowc, upd, ",");
166008f90673Sjfb 	printf(" ----\n");
166108f90673Sjfb 
166208f90673Sjfb 	do_output = 0;
166308f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
166408f90673Sjfb 		if (cvp->c <= cvp->d) {
166508f90673Sjfb 			cvp = context_vec_start;
166608f90673Sjfb 			do_output++;
166708f90673Sjfb 			break;
166808f90673Sjfb 		}
166908f90673Sjfb 	if (do_output) {
167008f90673Sjfb 		while (cvp <= context_vec_ptr) {
167108f90673Sjfb 			a = cvp->a;
167208f90673Sjfb 			b = cvp->b;
167308f90673Sjfb 			c = cvp->c;
167408f90673Sjfb 			d = cvp->d;
167508f90673Sjfb 
167608f90673Sjfb 			if (a <= b && c <= d)
167708f90673Sjfb 				ch = 'c';
167808f90673Sjfb 			else
167908f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
168008f90673Sjfb 
168108f90673Sjfb 			if (ch == 'd')
168208f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
168308f90673Sjfb 			else {
168408f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
168508f90673Sjfb 				fetch(ixnew, c, d, f2,
168608f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
168708f90673Sjfb 			}
168808f90673Sjfb 			lowc = d + 1;
168908f90673Sjfb 			cvp++;
169008f90673Sjfb 		}
169108f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
169208f90673Sjfb 	}
169308f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
169408f90673Sjfb }
169508f90673Sjfb 
169608f90673Sjfb /* dump accumulated "unified" diff changes */
169708f90673Sjfb static void
169808f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
169908f90673Sjfb {
170008f90673Sjfb 	struct context_vec *cvp = context_vec_start;
170108f90673Sjfb 	int lowa, upb, lowc, upd;
170208f90673Sjfb 	int a, b, c, d;
17035e78344dSjfb 	char ch, *f;
170408f90673Sjfb 
170508f90673Sjfb 	if (context_vec_start > context_vec_ptr)
170608f90673Sjfb 		return;
170708f90673Sjfb 
170808f90673Sjfb 	b = d = 0;		/* gcc */
1709dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1710e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1711dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1712e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
171308f90673Sjfb 
171408f90673Sjfb 	fputs("@@ -", stdout);
171508f90673Sjfb 	uni_range(lowa, upb);
171608f90673Sjfb 	fputs(" +", stdout);
171708f90673Sjfb 	uni_range(lowc, upd);
171808f90673Sjfb 	fputs(" @@", stdout);
17195e78344dSjfb 	if (pflag) {
17205e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
17215e78344dSjfb 		if (f != NULL) {
17225e78344dSjfb 			putchar(' ');
17235e78344dSjfb 			fputs(f, stdout);
17245e78344dSjfb 		}
17255e78344dSjfb 	}
172608f90673Sjfb 	putchar('\n');
172708f90673Sjfb 
172808f90673Sjfb 	/*
172908f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
173008f90673Sjfb 	 * are printed together.
173108f90673Sjfb 	 */
173208f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
173308f90673Sjfb 		a = cvp->a;
173408f90673Sjfb 		b = cvp->b;
173508f90673Sjfb 		c = cvp->c;
173608f90673Sjfb 		d = cvp->d;
173708f90673Sjfb 
173808f90673Sjfb 		/*
173908f90673Sjfb 		 * c: both new and old changes
174008f90673Sjfb 		 * d: only changes in the old file
174108f90673Sjfb 		 * a: only changes in the new file
174208f90673Sjfb 		 */
174308f90673Sjfb 		if (a <= b && c <= d)
174408f90673Sjfb 			ch = 'c';
174508f90673Sjfb 		else
174608f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
174708f90673Sjfb 
174808f90673Sjfb 		switch (ch) {
174908f90673Sjfb 		case 'c':
175008f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
175108f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
175208f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
175308f90673Sjfb 			break;
175408f90673Sjfb 		case 'd':
175508f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
175608f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
175708f90673Sjfb 			break;
175808f90673Sjfb 		case 'a':
175908f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
176008f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
176108f90673Sjfb 			break;
176208f90673Sjfb 		}
176308f90673Sjfb 		lowa = b + 1;
176408f90673Sjfb 		lowc = d + 1;
176508f90673Sjfb 	}
176608f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
176708f90673Sjfb 
176808f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
176908f90673Sjfb }
1770