xref: /openbsd-src/usr.bin/cvs/diff.c (revision b87c59bd45eab9a75b7770e4bb0e0639adc28d28)
1*b87c59bdSxsa /*	$OpenBSD: diff.c,v 1.46 2005/07/10 13:53:51 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 
1329225b0caSxsa #include <ctype.h>
1339225b0caSxsa #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>
1409225b0caSxsa #include <stdio.h>
1419225b0caSxsa #include <stdlib.h>
14208f90673Sjfb #include <string.h>
1439225b0caSxsa #include <unistd.h>
14408f90673Sjfb 
1459225b0caSxsa #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 
208*b87c59bdSxsa extern int verbosity;
209*b87c59bdSxsa 
21008f90673Sjfb 
211e4276007Sjfb static int  cvs_diff_init      (struct cvs_cmd *, int, char **, int *);
212e4276007Sjfb static int  cvs_diff_remote    (CVSFILE *, void *);
213e4276007Sjfb static int  cvs_diff_local     (CVSFILE *, void *);
214e4276007Sjfb static int  cvs_diff_pre_exec (struct cvsroot *);
215e4276007Sjfb static int  cvs_diff_cleanup   (void);
21608f90673Sjfb int  cvs_diffreg        (const char *, const char *);
21716cfc147Sjoris 
21808f90673Sjfb static void output(const char *, FILE *, const char *, FILE *);
21908f90673Sjfb static void check(FILE *, FILE *);
22008f90673Sjfb static void range(int, int, char *);
22108f90673Sjfb static void uni_range(int, int);
22208f90673Sjfb static void dump_context_vec(FILE *, FILE *);
22308f90673Sjfb static void dump_unified_vec(FILE *, FILE *);
2247f535ec4Sjfb static int  prepare(int, FILE *, off_t);
22508f90673Sjfb static void prune(void);
22608f90673Sjfb static void equiv(struct line *, int, struct line *, int, int *);
22708f90673Sjfb static void unravel(int);
22808f90673Sjfb static void unsort(struct line *, int, int *);
22908f90673Sjfb static void change(const char *, FILE *, const char *, FILE *, int, int, int, int);
23008f90673Sjfb static void sort(struct line *, int);
23108f90673Sjfb static int  ignoreline(char *);
23208f90673Sjfb static int  asciifile(FILE *);
23308f90673Sjfb static int  fetch(long *, int, int, FILE *, int, int);
23408f90673Sjfb static int  newcand(int, int, int);
23508f90673Sjfb static int  search(int *, int, int);
23608f90673Sjfb static int  skipline(FILE *);
23708f90673Sjfb static int  isqrt(int);
23808f90673Sjfb static int  stone(int *, int, int *, int *);
23908f90673Sjfb static int  readhash(FILE *);
24008f90673Sjfb static int  files_differ(FILE *, FILE *);
2415e78344dSjfb static char *match_function(const long *, int, FILE *);
24208f90673Sjfb static char *preadline(int, size_t, off_t);
24308f90673Sjfb 
24408f90673Sjfb 
2455e78344dSjfb static int aflag, bflag, dflag, iflag, Nflag, pflag, tflag, Tflag, wflag;
24608f90673Sjfb static int context, status;
24708f90673Sjfb static int format = D_NORMAL;
24808f90673Sjfb static struct stat stb1, stb2;
249f5638424Sjfb static char *ifdefname, *ignore_pats, diffargs[128];
250f5638424Sjfb static const char *diff_file;
25108f90673Sjfb regex_t ignore_re;
25208f90673Sjfb 
25308f90673Sjfb static int  *J;			/* will be overlaid on class */
25408f90673Sjfb static int  *class;		/* will be overlaid on file[0] */
25508f90673Sjfb static int  *klist;		/* will be overlaid on file[0] after class */
25608f90673Sjfb static int  *member;		/* will be overlaid on file[1] */
25708f90673Sjfb static int   clen;
25808f90673Sjfb static int   inifdef;		/* whether or not we are in a #ifdef block */
259e4276007Sjfb static int   diff_len[2];
26008f90673Sjfb static int   pref, suff;	/* length of prefix and suffix */
26108f90673Sjfb static int   slen[2];
26208f90673Sjfb static int   anychange;
26308f90673Sjfb static long *ixnew;		/* will be overlaid on file[1] */
26408f90673Sjfb static long *ixold;		/* will be overlaid on klist */
26508f90673Sjfb static struct cand *clist;	/* merely a free storage pot for candidates */
26608f90673Sjfb static int   clistlen;		/* the length of clist */
26708f90673Sjfb static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
26808f90673Sjfb static u_char *chrtran;		/* translation table for case-folding */
26908f90673Sjfb static struct context_vec *context_vec_start;
27008f90673Sjfb static struct context_vec *context_vec_end;
27108f90673Sjfb static struct context_vec *context_vec_ptr;
27208f90673Sjfb 
27308f90673Sjfb #define FUNCTION_CONTEXT_SIZE	41
2745e78344dSjfb static char lastbuf[FUNCTION_CONTEXT_SIZE];
27508f90673Sjfb static int  lastline;
27608f90673Sjfb static int  lastmatchline;
27708f90673Sjfb 
27808f90673Sjfb 
27908f90673Sjfb /*
28008f90673Sjfb  * chrtran points to one of 2 translation tables: cup2low if folding upper to
28108f90673Sjfb  * lower case clow2low if not folding case
28208f90673Sjfb  */
28308f90673Sjfb u_char clow2low[256] = {
28408f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
28508f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
28608f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
28708f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
28808f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
28908f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
29008f90673Sjfb 	0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
29108f90673Sjfb 	0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
29208f90673Sjfb 	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
29308f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
29408f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
29508f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
29608f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
29708f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
29808f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
29908f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
30008f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
30108f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
30208f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
30308f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
30408f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
30508f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
30608f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
30708f90673Sjfb 	0xfd, 0xfe, 0xff
30808f90673Sjfb };
30908f90673Sjfb 
31008f90673Sjfb u_char cup2low[256] = {
31108f90673Sjfb 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
31208f90673Sjfb 	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
31308f90673Sjfb 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
31408f90673Sjfb 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
31508f90673Sjfb 	0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
31608f90673Sjfb 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
31708f90673Sjfb 	0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
31808f90673Sjfb 	0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
31908f90673Sjfb 	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
32008f90673Sjfb 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
32108f90673Sjfb 	0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
32208f90673Sjfb 	0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
32308f90673Sjfb 	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
32408f90673Sjfb 	0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
32508f90673Sjfb 	0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
32608f90673Sjfb 	0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
32708f90673Sjfb 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
32808f90673Sjfb 	0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
32908f90673Sjfb 	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
33008f90673Sjfb 	0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
33108f90673Sjfb 	0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
33208f90673Sjfb 	0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
33308f90673Sjfb 	0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
33408f90673Sjfb 	0xfd, 0xfe, 0xff
33508f90673Sjfb };
33608f90673Sjfb 
337e4276007Sjfb 
338e4276007Sjfb struct cvs_cmd cvs_cmd_diff = {
339e4276007Sjfb 	CVS_OP_DIFF, CVS_REQ_DIFF, "diff",
340e4276007Sjfb 	{ "di", "dif" },
341e4276007Sjfb 	"Show differences between revisions",
342c9150269Sxsa 	"[-cilNnpu] [[-D date] [-r rev] [-D date2 | -r rev2]] "
343c9150269Sxsa 	"[-k mode] [file ...]",
344c9150269Sxsa 	"cD:iklNnpr:Ru",
34516cfc147Sjoris 	NULL,
34616cfc147Sjoris 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
347e4276007Sjfb 	cvs_diff_init,
348e4276007Sjfb 	cvs_diff_pre_exec,
349e4276007Sjfb 	cvs_diff_remote,
350e4276007Sjfb 	cvs_diff_local,
351e4276007Sjfb 	NULL,
352e4276007Sjfb 	cvs_diff_cleanup,
353e4276007Sjfb 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
354e4276007Sjfb };
355e4276007Sjfb 
356e4276007Sjfb 
357e4276007Sjfb struct cvs_cmd cvs_cmd_rdiff = {
358e4276007Sjfb 	CVS_OP_RDIFF, CVS_REQ_DIFF, "rdiff",
359c9150269Sxsa 	{ "pa", "patch" },
360e4276007Sjfb 	"Create 'patch' format diffs between releases",
361c9150269Sxsa 	"[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev "
362c9150269Sxsa 	"[-D date2 | -rev2] module ...",
363c9150269Sxsa 	"cD:flRr:stuV:",
364e4276007Sjfb 	NULL,
365e4276007Sjfb 	CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN,
366e4276007Sjfb 	cvs_diff_init,
367e4276007Sjfb 	cvs_diff_pre_exec,
368e4276007Sjfb 	cvs_diff_remote,
369e4276007Sjfb 	cvs_diff_local,
370e4276007Sjfb 	NULL,
371e4276007Sjfb 	cvs_diff_cleanup,
37244381dcbSjoris 	CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
37316cfc147Sjoris };
37408f90673Sjfb 
37516cfc147Sjoris static struct diff_arg *dap = NULL;
37616cfc147Sjoris static int recurse;
37716cfc147Sjoris 
378e4276007Sjfb static int
379e4276007Sjfb cvs_diff_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg)
38008f90673Sjfb {
38116cfc147Sjoris 	int ch;
38208f90673Sjfb 
38316cfc147Sjoris 	dap = (struct diff_arg *)malloc(sizeof(*dap));
38416cfc147Sjoris 	if (dap == NULL)
38531274bbfSjoris 		return (CVS_EX_DATA);
38616cfc147Sjoris 	dap->date1 = dap->date2 = dap->rev1 = dap->rev2 = NULL;
387dc6a6879Sjfb 	strlcpy(diffargs, argv[0], sizeof(diffargs));
388dc6a6879Sjfb 
389e4276007Sjfb 	while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) {
39008f90673Sjfb 		switch (ch) {
39108f90673Sjfb 		case 'c':
392f5638424Sjfb 			strlcat(diffargs, " -c", sizeof(diffargs));
39308f90673Sjfb 			format = D_CONTEXT;
39408f90673Sjfb 			break;
39508f90673Sjfb 		case 'D':
39616cfc147Sjoris 			if (dap->date1 == NULL && dap->rev1 == NULL) {
39716cfc147Sjoris 				dap->date1 = optarg;
39816cfc147Sjoris 			} else if (dap->date2 == NULL && dap->rev2 == NULL) {
39916cfc147Sjoris 				dap->date2 = optarg;
40016cfc147Sjoris 			} else {
40108f90673Sjfb 				cvs_log(LP_ERR,
40208f90673Sjfb 				    "no more than two revisions/dates can "
40308f90673Sjfb 				    "be specified");
40408f90673Sjfb 			}
40508f90673Sjfb 			break;
40608f90673Sjfb 		case 'l':
407f5638424Sjfb 			strlcat(diffargs, " -l", sizeof(diffargs));
40808f90673Sjfb 			recurse = 0;
409e4276007Sjfb 			cvs_cmd_diff.file_flags &= ~CF_RECURSE;
41008f90673Sjfb 			break;
41108f90673Sjfb 		case 'i':
412f5638424Sjfb 			strlcat(diffargs, " -i", sizeof(diffargs));
41308f90673Sjfb 			iflag = 1;
41408f90673Sjfb 			break;
415c710bc5aSjfb 		case 'N':
416c710bc5aSjfb 			strlcat(diffargs, " -N", sizeof(diffargs));
417c710bc5aSjfb 			Nflag = 1;
418c710bc5aSjfb 			break;
419394180a4Sjfb 		case 'n':
420394180a4Sjfb 			strlcat(diffargs, " -n", sizeof(diffargs));
421394180a4Sjfb 			format = D_RCSDIFF;
422394180a4Sjfb 			break;
4235e78344dSjfb 		case 'p':
4245e78344dSjfb 			strlcat(diffargs, " -p", sizeof(diffargs));
4255e78344dSjfb 			pflag = 1;
4265e78344dSjfb 			break;
42708f90673Sjfb 		case 'r':
42816cfc147Sjoris 			if ((dap->rev1 == NULL) && (dap->date1 == NULL)) {
42916cfc147Sjoris 				dap->rev1 = optarg;
43016cfc147Sjoris 			} else if ((dap->rev2 == NULL) &&
43116cfc147Sjoris 			    (dap->date2 == NULL)) {
43216cfc147Sjoris 				dap->rev2 = optarg;
43316cfc147Sjoris 			} else {
43408f90673Sjfb 				cvs_log(LP_ERR,
43508f90673Sjfb 				    "no more than two revisions/dates can "
43608f90673Sjfb 				    "be specified");
43731274bbfSjoris 				return (CVS_EX_USAGE);
43808f90673Sjfb 			}
43908f90673Sjfb 			break;
440f203c484Sjoris 		case 'R':
441e4276007Sjfb 			cvs_cmd_diff.file_flags |= CF_RECURSE;
442f203c484Sjoris 			break;
44308f90673Sjfb 		case 'u':
444f5638424Sjfb 			strlcat(diffargs, " -u", sizeof(diffargs));
44508f90673Sjfb 			format = D_UNIFIED;
44608f90673Sjfb 			break;
44708f90673Sjfb 		default:
44831274bbfSjoris 			return (CVS_EX_USAGE);
44908f90673Sjfb 		}
45008f90673Sjfb 	}
45108f90673Sjfb 
45216cfc147Sjoris 	*arg = optind;
453dc6a6879Sjfb 	return (0);
454dc6a6879Sjfb }
455dc6a6879Sjfb 
45616cfc147Sjoris int
45716cfc147Sjoris cvs_diff_cleanup(void)
45816cfc147Sjoris {
459e4276007Sjfb 	if (dap != NULL) {
46016cfc147Sjoris 		free(dap);
461e4276007Sjfb 		dap = NULL;
462e4276007Sjfb 	}
46316cfc147Sjoris 	return (0);
46416cfc147Sjoris }
465dc6a6879Sjfb 
466dc6a6879Sjfb /*
467e4276007Sjfb  * cvs_diff_pre_exec()
468dc6a6879Sjfb  *
469dc6a6879Sjfb  */
470dc6a6879Sjfb int
471e4276007Sjfb cvs_diff_pre_exec(struct cvsroot *root)
472dc6a6879Sjfb {
473e4276007Sjfb 	if (root->cr_method != CVS_METHOD_LOCAL) {
47408f90673Sjfb 		/* send the flags */
4755e78344dSjfb 		if (Nflag && (cvs_sendarg(root, "-N", 0) < 0))
47631274bbfSjoris 			return (CVS_EX_PROTO);
4775e78344dSjfb 		if (pflag && (cvs_sendarg(root, "-p", 0) < 0))
47831274bbfSjoris 			return (CVS_EX_PROTO);
4795e78344dSjfb 
480610801e8Sxsa 		if (format == D_CONTEXT) {
481610801e8Sxsa 			if (cvs_sendarg(root, "-c", 0) < 0)
482610801e8Sxsa 				return (CVS_EX_PROTO);
483610801e8Sxsa 		} else if (format == D_UNIFIED) {
484610801e8Sxsa 			if (cvs_sendarg(root, "-u", 0) < 0)
485610801e8Sxsa 				return (CVS_EX_PROTO);
486610801e8Sxsa 		}
48708f90673Sjfb 
488dc6a6879Sjfb 		if (dap->rev1 != NULL) {
489610801e8Sxsa 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
490610801e8Sxsa 			    (cvs_sendarg(root, dap->rev1, 0) < 0))
491610801e8Sxsa 				return (CVS_EX_PROTO);
4923917c9bfSderaadt 		} else if (dap->date1 != NULL) {
493610801e8Sxsa 			if ((cvs_sendarg(root, "-D", 0) < 0) ||
494610801e8Sxsa 			    (cvs_sendarg(root, dap->date1, 0) < 0))
495610801e8Sxsa 				return (CVS_EX_PROTO);
49608f90673Sjfb 		}
497dc6a6879Sjfb 		if (dap->rev2 != NULL) {
498610801e8Sxsa 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
499610801e8Sxsa 			    (cvs_sendarg(root, dap->rev2, 0) < 0))
500610801e8Sxsa 				return (CVS_EX_PROTO);
5013917c9bfSderaadt 		} else if (dap->date2 != NULL) {
502610801e8Sxsa 			if ((cvs_sendarg(root, "-D", 0) < 0) ||
503610801e8Sxsa 			    (cvs_sendarg(root, dap->date2, 0) < 0))
504610801e8Sxsa 				return  (CVS_EX_PROTO);
50508f90673Sjfb 		}
506e4276007Sjfb 	}
50708f90673Sjfb 
50808f90673Sjfb 	return (0);
50908f90673Sjfb }
51008f90673Sjfb 
51108f90673Sjfb 
51208f90673Sjfb /*
51308f90673Sjfb  * cvs_diff_file()
51408f90673Sjfb  *
51508f90673Sjfb  * Diff a single file.
51608f90673Sjfb  */
517e4276007Sjfb static int
518e4276007Sjfb cvs_diff_remote(struct cvs_file *cfp, void *arg)
51908f90673Sjfb {
520e4276007Sjfb 	char *dir, *repo;
521e4276007Sjfb 	char fpath[MAXPATHLEN], dfpath[MAXPATHLEN];
522dc6a6879Sjfb 	struct cvsroot *root;
523dc6a6879Sjfb 
524dc6a6879Sjfb 	if (cfp->cf_type == DT_DIR) {
525895e6cf6Sjfb 		if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
526bb029937Sjfb 			root = cfp->cf_parent->cf_root;
527c710bc5aSjfb 			cvs_sendreq(root, CVS_REQ_QUESTIONABLE,
528c710bc5aSjfb 			    CVS_FILE_NAME(cfp));
5293917c9bfSderaadt 		} else {
530bb029937Sjfb 			root = cfp->cf_root;
53116cfc147Sjoris #if 0
532dc6a6879Sjfb 			if ((cfp->cf_parent == NULL) ||
533bb029937Sjfb 			    (root != cfp->cf_parent->cf_root)) {
534dc6a6879Sjfb 				cvs_connect(root);
535e4276007Sjfb 				cvs_diff_pre_exec(root);
536dc6a6879Sjfb 			}
53716cfc147Sjoris #endif
538dc6a6879Sjfb 
539dc6a6879Sjfb 			cvs_senddir(root, cfp);
540895e6cf6Sjfb 		}
541895e6cf6Sjfb 
542dc6a6879Sjfb 		return (0);
543dc6a6879Sjfb 	}
54408f90673Sjfb 
5452d5b8b1dSjfb 	if (cfp->cf_cvstat == CVS_FST_LOST) {
5462d5b8b1dSjfb 		cvs_log(LP_WARN, "cannot find file %s", CVS_FILE_NAME(cfp));
5472d5b8b1dSjfb 		return (0);
5482d5b8b1dSjfb 	}
5492d5b8b1dSjfb 
550c710bc5aSjfb 	diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath));
551895e6cf6Sjfb 
552dc6a6879Sjfb 	if (cfp->cf_parent != NULL) {
553c710bc5aSjfb 		dir = cvs_file_getpath(cfp->cf_parent, dfpath, sizeof(dfpath));
554bb029937Sjfb 		root = cfp->cf_parent->cf_root;
555bb029937Sjfb 		repo = cfp->cf_parent->cf_repo;
5563917c9bfSderaadt 	} else {
557dc6a6879Sjfb 		dir = ".";
558895e6cf6Sjfb 		root = NULL;
559dc6a6879Sjfb 		repo = NULL;
56008f90673Sjfb 	}
56108f90673Sjfb 
562dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UNKNOWN) {
563e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name);
564dc6a6879Sjfb 		return (0);
56508f90673Sjfb 	}
56608f90673Sjfb 
567e4276007Sjfb 	if (cvs_sendentry(root, cfp) < 0)
56831274bbfSjoris 		return (CVS_EX_PROTO);
56908f90673Sjfb 
570dc6a6879Sjfb 	if (cfp->cf_cvstat == CVS_FST_UPTODATE) {
571e4276007Sjfb 		cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name);
57208f90673Sjfb 		return (0);
57308f90673Sjfb 	}
57408f90673Sjfb 
57508f90673Sjfb 	/* at this point, the file is modified */
576e4276007Sjfb 	if ((cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name) < 0) ||
577e4276007Sjfb 	    (cvs_sendfile(root, diff_file) < 0))
578e4276007Sjfb 		return (CVS_EX_PROTO);
579e4276007Sjfb 
580e4276007Sjfb 	return (0);
581e4276007Sjfb }
582e4276007Sjfb 
583e4276007Sjfb static int
584e4276007Sjfb cvs_diff_local(CVSFILE *cf, void *arg)
585e4276007Sjfb {
586e4276007Sjfb 	int len;
587e4276007Sjfb 	char *repo, buf[64];
588e4276007Sjfb 	char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
589e4276007Sjfb 	char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN];
590e4276007Sjfb 	BUF *b1, *b2;
591e4276007Sjfb 	RCSNUM *r1, *r2;
592e4276007Sjfb 	RCSFILE *rf;
593e4276007Sjfb 	struct cvsroot *root;
594e4276007Sjfb 
595e4276007Sjfb 	rf = NULL;
596e4276007Sjfb 	root = CVS_DIR_ROOT(cf);
597e4276007Sjfb 	repo = CVS_DIR_REPO(cf);
598e4276007Sjfb 	diff_file = cvs_file_getpath(cf, fpath, sizeof(fpath));
599e4276007Sjfb 
600e4276007Sjfb 	if (cf->cf_type == DT_DIR) {
601*b87c59bdSxsa 		if (verbosity > 1)
602e4276007Sjfb 			cvs_log(LP_INFO, "Diffing %s", fpath);
603e4276007Sjfb 		return (0);
604e4276007Sjfb 	}
605e4276007Sjfb 
606e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_LOST) {
607e4276007Sjfb 		cvs_log(LP_WARN, "cannot find file %s", cf->cf_name);
608e4276007Sjfb 		return (0);
609e4276007Sjfb 	}
610e4276007Sjfb 
611e4276007Sjfb 	if (cf->cf_cvstat == CVS_FST_UNKNOWN) {
612e4276007Sjfb 		cvs_log(LP_WARN, "I know nothing about %s", diff_file);
613e4276007Sjfb 		return (0);
6145c0cd766Sniallo 	} else if (cf->cf_cvstat == CVS_FST_UPTODATE)
615e4276007Sjfb 		return (0);
616e4276007Sjfb 
617e4276007Sjfb 	/* at this point, the file is modified */
618e4276007Sjfb 	len = snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
619dc6a6879Sjfb 	    root->cr_dir, repo, diff_file, RCS_FILE_EXT);
620e4276007Sjfb 	if (len == -1 || len >= (int)sizeof(rcspath)) {
62127b85f85Sxsa 		errno = ENAMETOOLONG;
62227b85f85Sxsa 		cvs_log(LP_ERRNO, "%s", rcspath);
62301b3d77aSjoris 		return (CVS_EX_DATA);
62427b85f85Sxsa 	}
62508f90673Sjfb 
6261b6534b8Sjfb 	rf = rcs_open(rcspath, RCS_READ);
627dc6a6879Sjfb 	if (rf == NULL) {
62831274bbfSjoris 		return (CVS_EX_DATA);
629dc6a6879Sjfb 	}
63008f90673Sjfb 
631dc6a6879Sjfb 	cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file,
63208f90673Sjfb 	    RCS_DIFF_DIV, rcspath);
63308f90673Sjfb 
634dc6a6879Sjfb 	if (dap->rev1 == NULL)
635e4276007Sjfb 		r1 = cf->cf_lrev;
63608f90673Sjfb 	else {
63725b74b48Sjfb 		if ((r1 = rcsnum_parse(dap->rev1)) == NULL) {
63831274bbfSjoris 			return (CVS_EX_DATA);
6397f535ec4Sjfb 		}
64008f90673Sjfb 	}
64108f90673Sjfb 
642dc6a6879Sjfb 	cvs_printf("retrieving revision %s\n",
64308f90673Sjfb 	    rcsnum_tostr(r1, buf, sizeof(buf)));
64408f90673Sjfb 	b1 = rcs_getrev(rf, r1);
64508f90673Sjfb 
64695ae1173Sjoris 	if (b1 == NULL) {
647289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
64895ae1173Sjoris 		    rcsnum_tostr(r1, buf, sizeof(buf)));
64995ae1173Sjoris 		if (r1 != cf->cf_lrev)
65095ae1173Sjoris 			rcsnum_free(r1);
65195ae1173Sjoris 		return (CVS_EX_DATA);
65295ae1173Sjoris 	}
65395ae1173Sjoris 
654e4276007Sjfb 	if (r1 != cf->cf_lrev)
6557f535ec4Sjfb 		rcsnum_free(r1);
6567f535ec4Sjfb 
657dc6a6879Sjfb 	if (dap->rev2 != NULL) {
658dc6a6879Sjfb 		cvs_printf("retrieving revision %s\n", dap->rev2);
65925b74b48Sjfb 		if ((r2 = rcsnum_parse(dap->rev2)) == NULL) {
66031274bbfSjoris 			return (CVS_EX_DATA);
6617f535ec4Sjfb 		}
66208f90673Sjfb 		b2 = rcs_getrev(rf, r2);
6637f535ec4Sjfb 		rcsnum_free(r2);
6643917c9bfSderaadt 	} else {
665dc6a6879Sjfb 		b2 = cvs_buf_load(diff_file, BUF_AUTOEXT);
66608f90673Sjfb 	}
66708f90673Sjfb 
668dc6a6879Sjfb 	rcs_close(rf);
669dc6a6879Sjfb 
67095ae1173Sjoris 	if (b2 == NULL) {
671289b97f4Sxsa 		cvs_log(LP_ERR, "failed to retrieve revision %s\n",
67295ae1173Sjoris 		    dap->rev2);
67395ae1173Sjoris 		cvs_buf_free(b1);
67495ae1173Sjoris 		return (CVS_EX_DATA);
67595ae1173Sjoris 	}
67695ae1173Sjoris 
6775ac8b1e7Sjoris 	cvs_printf("%s", diffargs);
6785ac8b1e7Sjoris 	cvs_printf(" -r%s", buf);
679dc6a6879Sjfb 	if (dap->rev2 != NULL)
6805ac8b1e7Sjoris 		cvs_printf(" -r%s", dap->rev2);
6815ac8b1e7Sjoris 	cvs_printf(" %s\n", diff_file);
682946f6157Sdjm 	strlcpy(path_tmp1, "/tmp/diff1.XXXXXXXXXX", sizeof(path_tmp1));
6837f535ec4Sjfb 	if (cvs_buf_write_stmp(b1, path_tmp1, 0600) == -1) {
6847f535ec4Sjfb 		cvs_buf_free(b1);
6857f535ec4Sjfb 		cvs_buf_free(b2);
68631274bbfSjoris 		return (CVS_EX_DATA);
6877f535ec4Sjfb 	}
6887f535ec4Sjfb 	cvs_buf_free(b1);
6897f535ec4Sjfb 
69069853e40Sjfb 	strlcpy(path_tmp2, "/tmp/diff2.XXXXXXXXXX", sizeof(path_tmp2));
691946f6157Sdjm 	if (cvs_buf_write_stmp(b2, path_tmp2, 0600) == -1) {
6927f535ec4Sjfb 		cvs_buf_free(b2);
693946f6157Sdjm 		(void)unlink(path_tmp1);
69431274bbfSjoris 		return (CVS_EX_DATA);
695946f6157Sdjm 	}
6967f535ec4Sjfb 	cvs_buf_free(b2);
6977f535ec4Sjfb 
698946f6157Sdjm 	cvs_diffreg(path_tmp1, path_tmp2);
699946f6157Sdjm 	(void)unlink(path_tmp1);
700946f6157Sdjm 	(void)unlink(path_tmp2);
70108f90673Sjfb 
70208f90673Sjfb 	return (0);
70308f90673Sjfb }
70408f90673Sjfb 
70508f90673Sjfb 
70608f90673Sjfb int
70708f90673Sjfb cvs_diffreg(const char *file1, const char *file2)
70808f90673Sjfb {
70908f90673Sjfb 	FILE *f1, *f2;
71008f90673Sjfb 	int i, rval;
7117f535ec4Sjfb 	void *tmp;
71208f90673Sjfb 
71308f90673Sjfb 	f1 = f2 = NULL;
71408f90673Sjfb 	rval = D_SAME;
71508f90673Sjfb 	anychange = 0;
71608f90673Sjfb 	lastline = 0;
71708f90673Sjfb 	lastmatchline = 0;
71808f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
71908f90673Sjfb 	chrtran = (iflag ? cup2low : clow2low);
72008f90673Sjfb 
72108f90673Sjfb 	f1 = fopen(file1, "r");
72208f90673Sjfb 	if (f1 == NULL) {
72308f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file1);
72408f90673Sjfb 		status |= 2;
72508f90673Sjfb 		goto closem;
72608f90673Sjfb 	}
72708f90673Sjfb 
72808f90673Sjfb 	f2 = fopen(file2, "r");
72908f90673Sjfb 	if (f2 == NULL) {
73008f90673Sjfb 		cvs_log(LP_ERRNO, "%s", file2);
73108f90673Sjfb 		status |= 2;
73208f90673Sjfb 		goto closem;
73308f90673Sjfb 	}
73408f90673Sjfb 
73508f90673Sjfb 	switch (files_differ(f1, f2)) {
73608f90673Sjfb 	case 0:
73708f90673Sjfb 		goto closem;
73808f90673Sjfb 	case 1:
73908f90673Sjfb 		break;
74008f90673Sjfb 	default:
74108f90673Sjfb 		/* error */
74208f90673Sjfb 		status |= 2;
74308f90673Sjfb 		goto closem;
74408f90673Sjfb 	}
74508f90673Sjfb 
74608f90673Sjfb 	if (!asciifile(f1) || !asciifile(f2)) {
74708f90673Sjfb 		rval = D_BINARY;
74808f90673Sjfb 		status |= 1;
74908f90673Sjfb 		goto closem;
75008f90673Sjfb 	}
7517f535ec4Sjfb 	if ((prepare(0, f1, stb1.st_size) < 0) ||
7527f535ec4Sjfb 	    (prepare(1, f2, stb2.st_size) < 0)) {
7537f535ec4Sjfb 		status |= 2;
7547f535ec4Sjfb 		goto closem;
7557f535ec4Sjfb 	}
75608f90673Sjfb 	prune();
75708f90673Sjfb 	sort(sfile[0], slen[0]);
75808f90673Sjfb 	sort(sfile[1], slen[1]);
75908f90673Sjfb 
76008f90673Sjfb 	member = (int *)file[1];
76108f90673Sjfb 	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
7627f535ec4Sjfb 	if ((tmp = realloc(member, (slen[1] + 2) * sizeof(int))) == NULL) {
7637f535ec4Sjfb 		status |= 2;
7647f535ec4Sjfb 		goto closem;
7657f535ec4Sjfb 	}
7667f535ec4Sjfb 	member = (int *)tmp;
76708f90673Sjfb 
76808f90673Sjfb 	class = (int *)file[0];
76908f90673Sjfb 	unsort(sfile[0], slen[0], class);
7707f535ec4Sjfb 	if ((tmp = realloc(class, (slen[0] + 2) * sizeof(int))) == NULL) {
7717f535ec4Sjfb 		status |= 2;
7727f535ec4Sjfb 		goto closem;
7737f535ec4Sjfb 	}
7747f535ec4Sjfb 	class = (int *)tmp;
77508f90673Sjfb 
7767f535ec4Sjfb 	if ((klist = malloc((slen[0] + 2) * sizeof(int))) == NULL) {
7777f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate klist");
7787f535ec4Sjfb 		status |= 2;
7797f535ec4Sjfb 		goto closem;
7807f535ec4Sjfb 	}
78108f90673Sjfb 	clen = 0;
78208f90673Sjfb 	clistlen = 100;
7837f535ec4Sjfb 	if ((clist = malloc(clistlen * sizeof(cand))) == NULL) {
7847f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate clist");
7857f535ec4Sjfb 		status |= 2;
7867f535ec4Sjfb 		goto closem;
7877f535ec4Sjfb 	}
78808f90673Sjfb 	i = stone(class, slen[0], member, klist);
78908f90673Sjfb 	free(member);
79008f90673Sjfb 	free(class);
79108f90673Sjfb 
792e4276007Sjfb 	J = realloc(J, (diff_len[0] + 2) * sizeof(int));
79308f90673Sjfb 	unravel(klist[i]);
79408f90673Sjfb 	free(clist);
79508f90673Sjfb 	free(klist);
79608f90673Sjfb 
797e4276007Sjfb 	ixold = realloc(ixold, (diff_len[0] + 2) * sizeof(long));
798e4276007Sjfb 	ixnew = realloc(ixnew, (diff_len[1] + 2) * sizeof(long));
79908f90673Sjfb 	check(f1, f2);
80008f90673Sjfb 	output(file1, f1, file2, f2);
80108f90673Sjfb 
80208f90673Sjfb closem:
80308f90673Sjfb 	if (anychange) {
80408f90673Sjfb 		status |= 1;
80508f90673Sjfb 		if (rval == D_SAME)
80608f90673Sjfb 			rval = D_DIFFER;
80708f90673Sjfb 	}
80808f90673Sjfb 	if (f1 != NULL)
80908f90673Sjfb 		fclose(f1);
81008f90673Sjfb 	if (f2 != NULL)
81108f90673Sjfb 		fclose(f2);
8127f535ec4Sjfb 
81308f90673Sjfb 	return (rval);
81408f90673Sjfb }
81508f90673Sjfb 
81608f90673Sjfb /*
81708f90673Sjfb  * Check to see if the given files differ.
81808f90673Sjfb  * Returns 0 if they are the same, 1 if different, and -1 on error.
81908f90673Sjfb  * XXX - could use code from cmp(1) [faster]
82008f90673Sjfb  */
82108f90673Sjfb static int
82208f90673Sjfb files_differ(FILE *f1, FILE *f2)
82308f90673Sjfb {
82408f90673Sjfb 	char buf1[BUFSIZ], buf2[BUFSIZ];
82508f90673Sjfb 	size_t i, j;
82608f90673Sjfb 
82708f90673Sjfb 	if (stb1.st_size != stb2.st_size)
82808f90673Sjfb 		return (1);
82908f90673Sjfb 	for (;;) {
83008f90673Sjfb 		i = fread(buf1, 1, sizeof(buf1), f1);
83108f90673Sjfb 		j = fread(buf2, 1, sizeof(buf2), f2);
83208f90673Sjfb 		if (i != j)
83308f90673Sjfb 			return (1);
83408f90673Sjfb 		if (i == 0 && j == 0) {
83508f90673Sjfb 			if (ferror(f1) || ferror(f2))
83608f90673Sjfb 				return (1);
83708f90673Sjfb 			return (0);
83808f90673Sjfb 		}
83908f90673Sjfb 		if (memcmp(buf1, buf2, i) != 0)
84008f90673Sjfb 			return (1);
84108f90673Sjfb 	}
84208f90673Sjfb }
84308f90673Sjfb 
8447f535ec4Sjfb static int
84508f90673Sjfb prepare(int i, FILE *fd, off_t filesize)
84608f90673Sjfb {
8477f535ec4Sjfb 	void *tmp;
84808f90673Sjfb 	struct line *p;
84908f90673Sjfb 	int j, h;
85008f90673Sjfb 	size_t sz;
85108f90673Sjfb 
85208f90673Sjfb 	rewind(fd);
85308f90673Sjfb 
854c48da046Sxsa 	sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25;
85508f90673Sjfb 	if (sz < 100)
85608f90673Sjfb 		sz = 100;
85708f90673Sjfb 
8587f535ec4Sjfb 	p = (struct line *)malloc((sz + 3) * sizeof(struct line));
8597f535ec4Sjfb 	if (p == NULL) {
8607f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to prepare line array");
8617f535ec4Sjfb 		return (-1);
8627f535ec4Sjfb 	}
86308f90673Sjfb 	for (j = 0; (h = readhash(fd));) {
86408f90673Sjfb 		if (j == (int)sz) {
86508f90673Sjfb 			sz = sz * 3 / 2;
8667f535ec4Sjfb 			tmp = realloc(p, (sz + 3) * sizeof(struct line));
8677f535ec4Sjfb 			if (tmp == NULL) {
8687f535ec4Sjfb 				cvs_log(LP_ERRNO, "failed to grow line array");
8697f535ec4Sjfb 				free(p);
8707f535ec4Sjfb 				return (-1);
8717f535ec4Sjfb 			}
8727f535ec4Sjfb 			p = (struct line *)tmp;
87308f90673Sjfb 		}
87408f90673Sjfb 		p[++j].value = h;
87508f90673Sjfb 	}
876e4276007Sjfb 	diff_len[i] = j;
87708f90673Sjfb 	file[i] = p;
8787f535ec4Sjfb 
8797f535ec4Sjfb 	return (0);
88008f90673Sjfb }
88108f90673Sjfb 
88208f90673Sjfb static void
88308f90673Sjfb prune(void)
88408f90673Sjfb {
88508f90673Sjfb 	int i, j;
88608f90673Sjfb 
887e4276007Sjfb 	for (pref = 0; pref < diff_len[0] && pref < diff_len[1] &&
88808f90673Sjfb 	    file[0][pref + 1].value == file[1][pref + 1].value;
88908f90673Sjfb 	    pref++)
89008f90673Sjfb 		;
891e4276007Sjfb 	for (suff = 0;
892e4276007Sjfb 	    (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) &&
893e4276007Sjfb 	    (file[0][diff_len[0] - suff].value ==
894e4276007Sjfb 	    file[1][diff_len[1] - suff].value);
89508f90673Sjfb 	    suff++)
89608f90673Sjfb 		;
89708f90673Sjfb 	for (j = 0; j < 2; j++) {
89808f90673Sjfb 		sfile[j] = file[j] + pref;
899e4276007Sjfb 		slen[j] = diff_len[j] - pref - suff;
90008f90673Sjfb 		for (i = 0; i <= slen[j]; i++)
90108f90673Sjfb 			sfile[j][i].serial = i;
90208f90673Sjfb 	}
90308f90673Sjfb }
90408f90673Sjfb 
90508f90673Sjfb static void
90608f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c)
90708f90673Sjfb {
90808f90673Sjfb 	int i, j;
90908f90673Sjfb 
91008f90673Sjfb 	i = j = 1;
91108f90673Sjfb 	while (i <= n && j <= m) {
91208f90673Sjfb 		if (a[i].value < b[j].value)
91308f90673Sjfb 			a[i++].value = 0;
91408f90673Sjfb 		else if (a[i].value == b[j].value)
91508f90673Sjfb 			a[i++].value = j;
91608f90673Sjfb 		else
91708f90673Sjfb 			j++;
91808f90673Sjfb 	}
91908f90673Sjfb 	while (i <= n)
92008f90673Sjfb 		a[i++].value = 0;
92108f90673Sjfb 	b[m + 1].value = 0;
92208f90673Sjfb 	j = 0;
92308f90673Sjfb 	while (++j <= m) {
92408f90673Sjfb 		c[j] = -b[j].serial;
92508f90673Sjfb 		while (b[j + 1].value == b[j].value) {
92608f90673Sjfb 			j++;
92708f90673Sjfb 			c[j] = b[j].serial;
92808f90673Sjfb 		}
92908f90673Sjfb 	}
93008f90673Sjfb 	c[j] = -1;
93108f90673Sjfb }
93208f90673Sjfb 
93308f90673Sjfb /* Code taken from ping.c */
93408f90673Sjfb static int
93508f90673Sjfb isqrt(int n)
93608f90673Sjfb {
93708f90673Sjfb 	int y, x = 1;
93808f90673Sjfb 
93908f90673Sjfb 	if (n == 0)
94008f90673Sjfb 		return (0);
94108f90673Sjfb 
94208f90673Sjfb 	do { /* newton was a stinker */
94308f90673Sjfb 		y = x;
94408f90673Sjfb 		x = n / x;
94508f90673Sjfb 		x += y;
94608f90673Sjfb 		x /= 2;
94708f90673Sjfb 	} while ((x - y) > 1 || (x - y) < -1);
94808f90673Sjfb 
94908f90673Sjfb 	return (x);
95008f90673Sjfb }
95108f90673Sjfb 
95208f90673Sjfb static int
95308f90673Sjfb stone(int *a, int n, int *b, int *c)
95408f90673Sjfb {
95508f90673Sjfb 	int i, k, y, j, l;
95608f90673Sjfb 	int oldc, tc, oldl;
95708f90673Sjfb 	u_int numtries;
95808f90673Sjfb 
959cc649edbSjfb 	/* XXX move the isqrt() out of the macro to avoid multiple calls */
960cc649edbSjfb 	const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n));
96108f90673Sjfb 
96208f90673Sjfb 	k = 0;
96308f90673Sjfb 	c[0] = newcand(0, 0, 0);
96408f90673Sjfb 	for (i = 1; i <= n; i++) {
96508f90673Sjfb 		j = a[i];
96608f90673Sjfb 		if (j == 0)
96708f90673Sjfb 			continue;
96808f90673Sjfb 		y = -b[j];
96908f90673Sjfb 		oldl = 0;
97008f90673Sjfb 		oldc = c[0];
97108f90673Sjfb 		numtries = 0;
97208f90673Sjfb 		do {
97308f90673Sjfb 			if (y <= clist[oldc].y)
97408f90673Sjfb 				continue;
97508f90673Sjfb 			l = search(c, k, y);
97608f90673Sjfb 			if (l != oldl + 1)
97708f90673Sjfb 				oldc = c[l - 1];
97808f90673Sjfb 			if (l <= k) {
97908f90673Sjfb 				if (clist[c[l]].y <= y)
98008f90673Sjfb 					continue;
98108f90673Sjfb 				tc = c[l];
98208f90673Sjfb 				c[l] = newcand(i, y, oldc);
98308f90673Sjfb 				oldc = tc;
98408f90673Sjfb 				oldl = l;
98508f90673Sjfb 				numtries++;
98608f90673Sjfb 			} else {
98708f90673Sjfb 				c[l] = newcand(i, y, oldc);
98808f90673Sjfb 				k++;
98908f90673Sjfb 				break;
99008f90673Sjfb 			}
99108f90673Sjfb 		} while ((y = b[++j]) > 0 && numtries < bound);
99208f90673Sjfb 	}
99308f90673Sjfb 	return (k);
99408f90673Sjfb }
99508f90673Sjfb 
99608f90673Sjfb static int
99708f90673Sjfb newcand(int x, int y, int pred)
99808f90673Sjfb {
99908f90673Sjfb 	struct cand *q;
100008f90673Sjfb 
100108f90673Sjfb 	if (clen == clistlen) {
100208f90673Sjfb 		clistlen = clistlen * 11 / 10;
100308f90673Sjfb 		clist = realloc(clist, clistlen * sizeof(cand));
10047f535ec4Sjfb 		if (clist == NULL) {
10057f535ec4Sjfb 			cvs_log(LP_ERRNO, "failed to resize clist");
10067f535ec4Sjfb 			return (-1);
10077f535ec4Sjfb 		}
100808f90673Sjfb 	}
100908f90673Sjfb 	q = clist + clen;
101008f90673Sjfb 	q->x = x;
101108f90673Sjfb 	q->y = y;
101208f90673Sjfb 	q->pred = pred;
101308f90673Sjfb 	return (clen++);
101408f90673Sjfb }
101508f90673Sjfb 
101608f90673Sjfb static int
101708f90673Sjfb search(int *c, int k, int y)
101808f90673Sjfb {
101908f90673Sjfb 	int i, j, l, t;
102008f90673Sjfb 
102108f90673Sjfb 	if (clist[c[k]].y < y)	/* quick look for typical case */
102208f90673Sjfb 		return (k + 1);
102308f90673Sjfb 	i = 0;
102408f90673Sjfb 	j = k + 1;
102508f90673Sjfb 	while (1) {
102608f90673Sjfb 		l = i + j;
102708f90673Sjfb 		if ((l >>= 1) <= i)
102808f90673Sjfb 			break;
102908f90673Sjfb 		t = clist[c[l]].y;
103008f90673Sjfb 		if (t > y)
103108f90673Sjfb 			j = l;
103208f90673Sjfb 		else if (t < y)
103308f90673Sjfb 			i = l;
103408f90673Sjfb 		else
103508f90673Sjfb 			return (l);
103608f90673Sjfb 	}
103708f90673Sjfb 	return (l + 1);
103808f90673Sjfb }
103908f90673Sjfb 
104008f90673Sjfb static void
104108f90673Sjfb unravel(int p)
104208f90673Sjfb {
104308f90673Sjfb 	struct cand *q;
104408f90673Sjfb 	int i;
104508f90673Sjfb 
1046e4276007Sjfb 	for (i = 0; i <= diff_len[0]; i++)
104708f90673Sjfb 		J[i] = i <= pref ? i :
1048e4276007Sjfb 		    i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0;
104908f90673Sjfb 	for (q = clist + p; q->y != 0; q = clist + q->pred)
105008f90673Sjfb 		J[q->x + pref] = q->y + pref;
105108f90673Sjfb }
105208f90673Sjfb 
105308f90673Sjfb /*
105408f90673Sjfb  * Check does double duty:
105508f90673Sjfb  *  1.	ferret out any fortuitous correspondences due
105608f90673Sjfb  *	to confounding by hashing (which result in "jackpot")
105708f90673Sjfb  *  2.  collect random access indexes to the two files
105808f90673Sjfb  */
105908f90673Sjfb static void
106008f90673Sjfb check(FILE *f1, FILE *f2)
106108f90673Sjfb {
106208f90673Sjfb 	int i, j, jackpot, c, d;
106308f90673Sjfb 	long ctold, ctnew;
106408f90673Sjfb 
106508f90673Sjfb 	rewind(f1);
106608f90673Sjfb 	rewind(f2);
106708f90673Sjfb 	j = 1;
106808f90673Sjfb 	ixold[0] = ixnew[0] = 0;
106908f90673Sjfb 	jackpot = 0;
107008f90673Sjfb 	ctold = ctnew = 0;
1071e4276007Sjfb 	for (i = 1; i <= diff_len[0]; i++) {
107208f90673Sjfb 		if (J[i] == 0) {
107308f90673Sjfb 			ixold[i] = ctold += skipline(f1);
107408f90673Sjfb 			continue;
107508f90673Sjfb 		}
107608f90673Sjfb 		while (j < J[i]) {
107708f90673Sjfb 			ixnew[j] = ctnew += skipline(f2);
107808f90673Sjfb 			j++;
107908f90673Sjfb 		}
108008f90673Sjfb 		if (bflag || wflag || iflag) {
108108f90673Sjfb 			for (;;) {
108208f90673Sjfb 				c = getc(f1);
108308f90673Sjfb 				d = getc(f2);
108408f90673Sjfb 				/*
108508f90673Sjfb 				 * GNU diff ignores a missing newline
108608f90673Sjfb 				 * in one file if bflag || wflag.
108708f90673Sjfb 				 */
108808f90673Sjfb 				if ((bflag || wflag) &&
108908f90673Sjfb 				    ((c == EOF && d == '\n') ||
109008f90673Sjfb 				    (c == '\n' && d == EOF))) {
109108f90673Sjfb 					break;
109208f90673Sjfb 				}
109308f90673Sjfb 				ctold++;
109408f90673Sjfb 				ctnew++;
109508f90673Sjfb 				if (bflag && isspace(c) && isspace(d)) {
109608f90673Sjfb 					do {
109708f90673Sjfb 						if (c == '\n')
109808f90673Sjfb 							break;
109908f90673Sjfb 						ctold++;
110008f90673Sjfb 					} while (isspace(c = getc(f1)));
110108f90673Sjfb 					do {
110208f90673Sjfb 						if (d == '\n')
110308f90673Sjfb 							break;
110408f90673Sjfb 						ctnew++;
110508f90673Sjfb 					} while (isspace(d = getc(f2)));
110608f90673Sjfb 				} else if (wflag) {
110708f90673Sjfb 					while (isspace(c) && c != '\n') {
110808f90673Sjfb 						c = getc(f1);
110908f90673Sjfb 						ctold++;
111008f90673Sjfb 					}
111108f90673Sjfb 					while (isspace(d) && d != '\n') {
111208f90673Sjfb 						d = getc(f2);
111308f90673Sjfb 						ctnew++;
111408f90673Sjfb 					}
111508f90673Sjfb 				}
111608f90673Sjfb 				if (chrtran[c] != chrtran[d]) {
111708f90673Sjfb 					jackpot++;
111808f90673Sjfb 					J[i] = 0;
111908f90673Sjfb 					if (c != '\n' && c != EOF)
112008f90673Sjfb 						ctold += skipline(f1);
112108f90673Sjfb 					if (d != '\n' && c != EOF)
112208f90673Sjfb 						ctnew += skipline(f2);
112308f90673Sjfb 					break;
112408f90673Sjfb 				}
112508f90673Sjfb 				if (c == '\n' || c == EOF)
112608f90673Sjfb 					break;
112708f90673Sjfb 			}
112808f90673Sjfb 		} else {
112908f90673Sjfb 			for (;;) {
113008f90673Sjfb 				ctold++;
113108f90673Sjfb 				ctnew++;
113208f90673Sjfb 				if ((c = getc(f1)) != (d = getc(f2))) {
113308f90673Sjfb 					/* jackpot++; */
113408f90673Sjfb 					J[i] = 0;
113508f90673Sjfb 					if (c != '\n' && c != EOF)
113608f90673Sjfb 						ctold += skipline(f1);
113708f90673Sjfb 					if (d != '\n' && c != EOF)
113808f90673Sjfb 						ctnew += skipline(f2);
113908f90673Sjfb 					break;
114008f90673Sjfb 				}
114108f90673Sjfb 				if (c == '\n' || c == EOF)
114208f90673Sjfb 					break;
114308f90673Sjfb 			}
114408f90673Sjfb 		}
114508f90673Sjfb 		ixold[i] = ctold;
114608f90673Sjfb 		ixnew[j] = ctnew;
114708f90673Sjfb 		j++;
114808f90673Sjfb 	}
1149e4276007Sjfb 	for (; j <= diff_len[1]; j++)
115008f90673Sjfb 		ixnew[j] = ctnew += skipline(f2);
115108f90673Sjfb 	/*
115208f90673Sjfb 	 * if (jackpot)
11535ac8b1e7Sjoris 	 *	cvs_printf("jackpot\n");
115408f90673Sjfb 	 */
115508f90673Sjfb }
115608f90673Sjfb 
115708f90673Sjfb /* shellsort CACM #201 */
115808f90673Sjfb static void
115908f90673Sjfb sort(struct line *a, int n)
116008f90673Sjfb {
116108f90673Sjfb 	struct line *ai, *aim, w;
116208f90673Sjfb 	int j, m = 0, k;
116308f90673Sjfb 
116408f90673Sjfb 	if (n == 0)
116508f90673Sjfb 		return;
116608f90673Sjfb 	for (j = 1; j <= n; j *= 2)
116708f90673Sjfb 		m = 2 * j - 1;
116808f90673Sjfb 	for (m /= 2; m != 0; m /= 2) {
116908f90673Sjfb 		k = n - m;
117008f90673Sjfb 		for (j = 1; j <= k; j++) {
117108f90673Sjfb 			for (ai = &a[j]; ai > a; ai -= m) {
117208f90673Sjfb 				aim = &ai[m];
117308f90673Sjfb 				if (aim < ai)
117408f90673Sjfb 					break;	/* wraparound */
117508f90673Sjfb 				if (aim->value > ai[0].value ||
117608f90673Sjfb 				    (aim->value == ai[0].value &&
117708f90673Sjfb 					aim->serial > ai[0].serial))
117808f90673Sjfb 					break;
117908f90673Sjfb 				w.value = ai[0].value;
118008f90673Sjfb 				ai[0].value = aim->value;
118108f90673Sjfb 				aim->value = w.value;
118208f90673Sjfb 				w.serial = ai[0].serial;
118308f90673Sjfb 				ai[0].serial = aim->serial;
118408f90673Sjfb 				aim->serial = w.serial;
118508f90673Sjfb 			}
118608f90673Sjfb 		}
118708f90673Sjfb 	}
118808f90673Sjfb }
118908f90673Sjfb 
119008f90673Sjfb static void
119108f90673Sjfb unsort(struct line *f, int l, int *b)
119208f90673Sjfb {
119308f90673Sjfb 	int *a, i;
119408f90673Sjfb 
11957f535ec4Sjfb 	if ((a = (int *)malloc((l + 1) * sizeof(int))) == NULL) {
11967f535ec4Sjfb 		cvs_log(LP_ERRNO, "failed to allocate sort array");
11977f535ec4Sjfb 		return;
11987f535ec4Sjfb 	}
119908f90673Sjfb 	for (i = 1; i <= l; i++)
120008f90673Sjfb 		a[f[i].serial] = f[i].value;
120108f90673Sjfb 	for (i = 1; i <= l; i++)
120208f90673Sjfb 		b[i] = a[i];
120308f90673Sjfb 	free(a);
120408f90673Sjfb }
120508f90673Sjfb 
120608f90673Sjfb static int
120708f90673Sjfb skipline(FILE *f)
120808f90673Sjfb {
120908f90673Sjfb 	int i, c;
121008f90673Sjfb 
121108f90673Sjfb 	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
121208f90673Sjfb 		continue;
121308f90673Sjfb 	return (i);
121408f90673Sjfb }
121508f90673Sjfb 
121608f90673Sjfb static void
121708f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2)
121808f90673Sjfb {
121908f90673Sjfb 	int m, i0, i1, j0, j1;
122008f90673Sjfb 
122108f90673Sjfb 	rewind(f1);
122208f90673Sjfb 	rewind(f2);
1223e4276007Sjfb 	m = diff_len[0];
122408f90673Sjfb 	J[0] = 0;
1225e4276007Sjfb 	J[m + 1] = diff_len[1] + 1;
122608f90673Sjfb 	for (i0 = 1; i0 <= m; i0 = i1 + 1) {
122708f90673Sjfb 		while (i0 <= m && J[i0] == J[i0 - 1] + 1)
122808f90673Sjfb 			i0++;
122908f90673Sjfb 		j0 = J[i0 - 1] + 1;
123008f90673Sjfb 		i1 = i0 - 1;
123108f90673Sjfb 		while (i1 < m && J[i1 + 1] == 0)
123208f90673Sjfb 			i1++;
123308f90673Sjfb 		j1 = J[i1 + 1] - 1;
123408f90673Sjfb 		J[i1] = j1;
123508f90673Sjfb 		change(file1, f1, file2, f2, i0, i1, j0, j1);
123608f90673Sjfb 	}
123708f90673Sjfb 	if (m == 0)
1238e4276007Sjfb 		change(file1, f1, file2, f2, 1, 0, 1, diff_len[1]);
123908f90673Sjfb 	if (format == D_IFDEF) {
124008f90673Sjfb 		for (;;) {
124108f90673Sjfb #define	c i0
124208f90673Sjfb 			if ((c = getc(f1)) == EOF)
124308f90673Sjfb 				return;
12445ac8b1e7Sjoris 			cvs_putchar(c);
124508f90673Sjfb 		}
124608f90673Sjfb #undef c
124708f90673Sjfb 	}
124808f90673Sjfb 	if (anychange != 0) {
124908f90673Sjfb 		if (format == D_CONTEXT)
125008f90673Sjfb 			dump_context_vec(f1, f2);
125108f90673Sjfb 		else if (format == D_UNIFIED)
125208f90673Sjfb 			dump_unified_vec(f1, f2);
125308f90673Sjfb 	}
125408f90673Sjfb }
125508f90673Sjfb 
125608f90673Sjfb static __inline void
125708f90673Sjfb range(int a, int b, char *separator)
125808f90673Sjfb {
12595ac8b1e7Sjoris 	cvs_printf("%d", a > b ? b : a);
126008f90673Sjfb 	if (a < b)
12615ac8b1e7Sjoris 		cvs_printf("%s%d", separator, b);
126208f90673Sjfb }
126308f90673Sjfb 
126408f90673Sjfb static __inline void
126508f90673Sjfb uni_range(int a, int b)
126608f90673Sjfb {
126708f90673Sjfb 	if (a < b)
12685ac8b1e7Sjoris 		cvs_printf("%d,%d", a, b - a + 1);
126908f90673Sjfb 	else if (a == b)
12705ac8b1e7Sjoris 		cvs_printf("%d", b);
127108f90673Sjfb 	else
12725ac8b1e7Sjoris 		cvs_printf("%d,0", b);
127308f90673Sjfb }
127408f90673Sjfb 
127508f90673Sjfb static char *
12762a0de57dSjfb preadline(int fd, size_t rlen, off_t off)
127708f90673Sjfb {
127808f90673Sjfb 	char *line;
127908f90673Sjfb 	ssize_t nr;
128008f90673Sjfb 
12812a0de57dSjfb 	line = malloc(rlen + 1);
128208f90673Sjfb 	if (line == NULL) {
128308f90673Sjfb 		cvs_log(LP_ERRNO, "failed to allocate line");
128408f90673Sjfb 		return (NULL);
128508f90673Sjfb 	}
12862a0de57dSjfb 	if ((nr = pread(fd, line, rlen, off)) < 0) {
128708f90673Sjfb 		cvs_log(LP_ERRNO, "preadline failed");
128808f90673Sjfb 		return (NULL);
128908f90673Sjfb 	}
129008f90673Sjfb 	line[nr] = '\0';
129108f90673Sjfb 	return (line);
129208f90673Sjfb }
129308f90673Sjfb 
129408f90673Sjfb static int
129508f90673Sjfb ignoreline(char *line)
129608f90673Sjfb {
129708f90673Sjfb 	int ret;
129808f90673Sjfb 
129908f90673Sjfb 	ret = regexec(&ignore_re, line, 0, NULL, 0);
130008f90673Sjfb 	free(line);
130108f90673Sjfb 	return (ret == 0);	/* if it matched, it should be ignored. */
130208f90673Sjfb }
130308f90673Sjfb 
130408f90673Sjfb /*
130508f90673Sjfb  * Indicate that there is a difference between lines a and b of the from file
130608f90673Sjfb  * to get to lines c to d of the to file.  If a is greater then b then there
130708f90673Sjfb  * are no lines in the from file involved and this means that there were
130808f90673Sjfb  * lines appended (beginning at b).  If c is greater than d then there are
130908f90673Sjfb  * lines missing from the to file.
131008f90673Sjfb  */
131108f90673Sjfb static void
131208f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2,
131308f90673Sjfb 	int a, int b, int c, int d)
131408f90673Sjfb {
131508f90673Sjfb 	static size_t max_context = 64;
131608f90673Sjfb 	int i;
131708f90673Sjfb 
131808f90673Sjfb 	if (format != D_IFDEF && a > b && c > d)
131908f90673Sjfb 		return;
132008f90673Sjfb 	if (ignore_pats != NULL) {
132108f90673Sjfb 		char *line;
132208f90673Sjfb 		/*
132308f90673Sjfb 		 * All lines in the change, insert, or delete must
132408f90673Sjfb 		 * match an ignore pattern for the change to be
132508f90673Sjfb 		 * ignored.
132608f90673Sjfb 		 */
132708f90673Sjfb 		if (a <= b) {		/* Changes and deletes. */
132808f90673Sjfb 			for (i = a; i <= b; i++) {
132908f90673Sjfb 				line = preadline(fileno(f1),
133008f90673Sjfb 				    ixold[i] - ixold[i - 1], ixold[i - 1]);
133108f90673Sjfb 				if (!ignoreline(line))
133208f90673Sjfb 					goto proceed;
133308f90673Sjfb 			}
133408f90673Sjfb 		}
133508f90673Sjfb 		if (a > b || c <= d) {	/* Changes and inserts. */
133608f90673Sjfb 			for (i = c; i <= d; i++) {
133708f90673Sjfb 				line = preadline(fileno(f2),
133808f90673Sjfb 				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
133908f90673Sjfb 				if (!ignoreline(line))
134008f90673Sjfb 					goto proceed;
134108f90673Sjfb 			}
134208f90673Sjfb 		}
134308f90673Sjfb 		return;
134408f90673Sjfb 	}
134508f90673Sjfb proceed:
134608f90673Sjfb 	if (format == D_CONTEXT || format == D_UNIFIED) {
134708f90673Sjfb 		/*
134808f90673Sjfb 		 * Allocate change records as needed.
134908f90673Sjfb 		 */
135008f90673Sjfb 		if (context_vec_ptr == context_vec_end - 1) {
135108f90673Sjfb 			ptrdiff_t offset = context_vec_ptr - context_vec_start;
135208f90673Sjfb 			max_context <<= 1;
135308f90673Sjfb 			context_vec_start = realloc(context_vec_start,
135408f90673Sjfb 			    max_context * sizeof(struct context_vec));
135508f90673Sjfb 			context_vec_end = context_vec_start + max_context;
135608f90673Sjfb 			context_vec_ptr = context_vec_start + offset;
135708f90673Sjfb 		}
135808f90673Sjfb 		if (anychange == 0) {
135908f90673Sjfb 			/*
136008f90673Sjfb 			 * Print the context/unidiff header first time through.
136108f90673Sjfb 			 */
13625ac8b1e7Sjoris 			cvs_printf("%s %s	%s",
136308f90673Sjfb 			    format == D_CONTEXT ? "***" : "---", diff_file,
136408f90673Sjfb 			    ctime(&stb1.st_mtime));
13655ac8b1e7Sjoris 			cvs_printf("%s %s	%s",
136608f90673Sjfb 			    format == D_CONTEXT ? "---" : "+++", diff_file,
136708f90673Sjfb 			    ctime(&stb2.st_mtime));
136808f90673Sjfb 			anychange = 1;
136908f90673Sjfb 		} else if (a > context_vec_ptr->b + (2 * context) + 1 &&
137008f90673Sjfb 		    c > context_vec_ptr->d + (2 * context) + 1) {
137108f90673Sjfb 			/*
137208f90673Sjfb 			 * If this change is more than 'context' lines from the
137308f90673Sjfb 			 * previous change, dump the record and reset it.
137408f90673Sjfb 			 */
137508f90673Sjfb 			if (format == D_CONTEXT)
137608f90673Sjfb 				dump_context_vec(f1, f2);
137708f90673Sjfb 			else
137808f90673Sjfb 				dump_unified_vec(f1, f2);
137908f90673Sjfb 		}
138008f90673Sjfb 		context_vec_ptr++;
138108f90673Sjfb 		context_vec_ptr->a = a;
138208f90673Sjfb 		context_vec_ptr->b = b;
138308f90673Sjfb 		context_vec_ptr->c = c;
138408f90673Sjfb 		context_vec_ptr->d = d;
138508f90673Sjfb 		return;
138608f90673Sjfb 	}
138708f90673Sjfb 	if (anychange == 0)
138808f90673Sjfb 		anychange = 1;
138908f90673Sjfb 	switch (format) {
139008f90673Sjfb 	case D_BRIEF:
139108f90673Sjfb 		return;
139208f90673Sjfb 	case D_NORMAL:
139308f90673Sjfb 		range(a, b, ",");
13945ac8b1e7Sjoris 		cvs_putchar(a > b ? 'a' : c > d ? 'd' : 'c');
139508f90673Sjfb 		if (format == D_NORMAL)
139608f90673Sjfb 			range(c, d, ",");
13975ac8b1e7Sjoris 		cvs_putchar('\n');
139808f90673Sjfb 		break;
1399394180a4Sjfb 	case D_RCSDIFF:
1400394180a4Sjfb 		if (a > b)
14015ac8b1e7Sjoris 			cvs_printf("a%d %d\n", b, d - c + 1);
1402394180a4Sjfb 		else {
14035ac8b1e7Sjoris 			cvs_printf("d%d %d\n", a, b - a + 1);
1404394180a4Sjfb 
1405394180a4Sjfb 			if (!(c > d))	/* add changed lines */
14065ac8b1e7Sjoris 				cvs_printf("a%d %d\n", b, d - c + 1);
1407394180a4Sjfb 		}
1408394180a4Sjfb 		break;
140908f90673Sjfb 	}
141008f90673Sjfb 	if (format == D_NORMAL || format == D_IFDEF) {
141108f90673Sjfb 		fetch(ixold, a, b, f1, '<', 1);
141208f90673Sjfb 		if (a <= b && c <= d && format == D_NORMAL)
141308f90673Sjfb 			puts("---");
141408f90673Sjfb 	}
141508f90673Sjfb 	i = fetch(ixnew, c, d, f2, format == D_NORMAL ? '>' : '\0', 0);
141608f90673Sjfb 	if (inifdef) {
14175ac8b1e7Sjoris 		cvs_printf("#endif /* %s */\n", ifdefname);
141808f90673Sjfb 		inifdef = 0;
141908f90673Sjfb 	}
142008f90673Sjfb }
142108f90673Sjfb 
142208f90673Sjfb static int
142308f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
142408f90673Sjfb {
142508f90673Sjfb 	int i, j, c, lastc, col, nc;
142608f90673Sjfb 
142708f90673Sjfb 	/*
142808f90673Sjfb 	 * When doing #ifdef's, copy down to current line
142908f90673Sjfb 	 * if this is the first file, so that stuff makes it to output.
143008f90673Sjfb 	 */
143108f90673Sjfb 	if (format == D_IFDEF && oldfile) {
143208f90673Sjfb 		long curpos = ftell(lb);
143308f90673Sjfb 		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
143408f90673Sjfb 		nc = f[a > b ? b : a - 1] - curpos;
143508f90673Sjfb 		for (i = 0; i < nc; i++)
14365ac8b1e7Sjoris 			cvs_putchar(getc(lb));
143708f90673Sjfb 	}
143808f90673Sjfb 	if (a > b)
143908f90673Sjfb 		return (0);
144008f90673Sjfb 	if (format == D_IFDEF) {
144108f90673Sjfb 		if (inifdef) {
14425ac8b1e7Sjoris 			cvs_printf("#else /* %s%s */\n",
144308f90673Sjfb 			    oldfile == 1 ? "!" : "", ifdefname);
144408f90673Sjfb 		} else {
144508f90673Sjfb 			if (oldfile)
14465ac8b1e7Sjoris 				cvs_printf("#ifndef %s\n", ifdefname);
144708f90673Sjfb 			else
14485ac8b1e7Sjoris 				cvs_printf("#ifdef %s\n", ifdefname);
144908f90673Sjfb 		}
145008f90673Sjfb 		inifdef = 1 + oldfile;
145108f90673Sjfb 	}
145208f90673Sjfb 	for (i = a; i <= b; i++) {
145308f90673Sjfb 		fseek(lb, f[i - 1], SEEK_SET);
145408f90673Sjfb 		nc = f[i] - f[i - 1];
145508f90673Sjfb 		if (format != D_IFDEF && ch != '\0') {
14565ac8b1e7Sjoris 			cvs_putchar(ch);
145708f90673Sjfb 			if (Tflag && (format == D_NORMAL || format == D_CONTEXT
145808f90673Sjfb 			    || format == D_UNIFIED))
14595ac8b1e7Sjoris 				cvs_putchar('\t');
146008f90673Sjfb 			else if (format != D_UNIFIED)
14615ac8b1e7Sjoris 				cvs_putchar(' ');
146208f90673Sjfb 		}
146308f90673Sjfb 		col = 0;
146408f90673Sjfb 		for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
146508f90673Sjfb 			if ((c = getc(lb)) == EOF) {
1466394180a4Sjfb 				if (format == D_RCSDIFF)
1467394180a4Sjfb 					warnx("No newline at end of file");
1468394180a4Sjfb 				else
146908f90673Sjfb 					puts("\n\\ No newline at end of file");
147008f90673Sjfb 				return (0);
147108f90673Sjfb 			}
147208f90673Sjfb 			if (c == '\t' && tflag) {
147308f90673Sjfb 				do {
14745ac8b1e7Sjoris 					cvs_putchar(' ');
147508f90673Sjfb 				} while (++col & 7);
147608f90673Sjfb 			} else {
14775ac8b1e7Sjoris 				cvs_putchar(c);
147808f90673Sjfb 				col++;
147908f90673Sjfb 			}
148008f90673Sjfb 		}
148108f90673Sjfb 	}
148208f90673Sjfb 	return (0);
148308f90673Sjfb }
148408f90673Sjfb 
148508f90673Sjfb /*
148608f90673Sjfb  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
148708f90673Sjfb  */
148808f90673Sjfb static int
148908f90673Sjfb readhash(FILE *f)
149008f90673Sjfb {
149108f90673Sjfb 	int i, t, space;
149208f90673Sjfb 	int sum;
149308f90673Sjfb 
149408f90673Sjfb 	sum = 1;
149508f90673Sjfb 	space = 0;
149608f90673Sjfb 	if (!bflag && !wflag) {
149708f90673Sjfb 		if (iflag)
149808f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
149908f90673Sjfb 				if (t == EOF) {
150008f90673Sjfb 					if (i == 0)
150108f90673Sjfb 						return (0);
150208f90673Sjfb 					break;
150308f90673Sjfb 				}
150408f90673Sjfb 				sum = sum * 127 + chrtran[t];
150508f90673Sjfb 			}
150608f90673Sjfb 		else
150708f90673Sjfb 			for (i = 0; (t = getc(f)) != '\n'; i++) {
150808f90673Sjfb 				if (t == EOF) {
150908f90673Sjfb 					if (i == 0)
151008f90673Sjfb 						return (0);
151108f90673Sjfb 					break;
151208f90673Sjfb 				}
151308f90673Sjfb 				sum = sum * 127 + t;
151408f90673Sjfb 			}
151508f90673Sjfb 	} else {
151608f90673Sjfb 		for (i = 0;;) {
151708f90673Sjfb 			switch (t = getc(f)) {
151808f90673Sjfb 			case '\t':
151908f90673Sjfb 			case ' ':
152008f90673Sjfb 				space++;
152108f90673Sjfb 				continue;
152208f90673Sjfb 			default:
152308f90673Sjfb 				if (space && !wflag) {
152408f90673Sjfb 					i++;
152508f90673Sjfb 					space = 0;
152608f90673Sjfb 				}
152708f90673Sjfb 				sum = sum * 127 + chrtran[t];
152808f90673Sjfb 				i++;
152908f90673Sjfb 				continue;
153008f90673Sjfb 			case EOF:
153108f90673Sjfb 				if (i == 0)
153208f90673Sjfb 					return (0);
153308f90673Sjfb 				/* FALLTHROUGH */
153408f90673Sjfb 			case '\n':
153508f90673Sjfb 				break;
153608f90673Sjfb 			}
153708f90673Sjfb 			break;
153808f90673Sjfb 		}
153908f90673Sjfb 	}
154008f90673Sjfb 	/*
154108f90673Sjfb 	 * There is a remote possibility that we end up with a zero sum.
154208f90673Sjfb 	 * Zero is used as an EOF marker, so return 1 instead.
154308f90673Sjfb 	 */
154408f90673Sjfb 	return (sum == 0 ? 1 : sum);
154508f90673Sjfb }
154608f90673Sjfb 
154708f90673Sjfb static int
154808f90673Sjfb asciifile(FILE *f)
154908f90673Sjfb {
155008f90673Sjfb 	char buf[BUFSIZ];
155108f90673Sjfb 	int i, cnt;
155208f90673Sjfb 
155308f90673Sjfb 	if (aflag || f == NULL)
155408f90673Sjfb 		return (1);
155508f90673Sjfb 
155608f90673Sjfb 	rewind(f);
155708f90673Sjfb 	cnt = fread(buf, 1, sizeof(buf), f);
155808f90673Sjfb 	for (i = 0; i < cnt; i++)
155908f90673Sjfb 		if (!isprint(buf[i]) && !isspace(buf[i]))
156008f90673Sjfb 			return (0);
156108f90673Sjfb 	return (1);
156208f90673Sjfb }
156308f90673Sjfb 
15645e78344dSjfb static char*
15655e78344dSjfb match_function(const long *f, int pos, FILE *fp)
15665e78344dSjfb {
15675e78344dSjfb 	unsigned char buf[FUNCTION_CONTEXT_SIZE];
15685e78344dSjfb 	size_t nc;
15695e78344dSjfb 	int last = lastline;
15705e78344dSjfb 	char *p;
15715e78344dSjfb 
15725e78344dSjfb 	lastline = pos;
15735e78344dSjfb 	while (pos > last) {
15745e78344dSjfb 		fseek(fp, f[pos - 1], SEEK_SET);
15755e78344dSjfb 		nc = f[pos] - f[pos - 1];
15765e78344dSjfb 		if (nc >= sizeof(buf))
15775e78344dSjfb 			nc = sizeof(buf) - 1;
15785e78344dSjfb 		nc = fread(buf, 1, nc, fp);
15795e78344dSjfb 		if (nc > 0) {
15805e78344dSjfb 			buf[nc] = '\0';
1581634926d6Sniallo 			p = strchr((const char *)buf, '\n');
15825e78344dSjfb 			if (p != NULL)
15835e78344dSjfb 				*p = '\0';
15845e78344dSjfb 			if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
1585634926d6Sniallo 				strlcpy(lastbuf, (const char *)buf, sizeof lastbuf);
15865e78344dSjfb 				lastmatchline = pos;
15875e78344dSjfb 				return lastbuf;
15885e78344dSjfb 			}
15895e78344dSjfb 		}
15905e78344dSjfb 		pos--;
15915e78344dSjfb 	}
15925e78344dSjfb 	return (lastmatchline > 0) ? lastbuf : NULL;
15935e78344dSjfb }
15945e78344dSjfb 
159508f90673Sjfb 
159608f90673Sjfb /* dump accumulated "context" diff changes */
159708f90673Sjfb static void
159808f90673Sjfb dump_context_vec(FILE *f1, FILE *f2)
159908f90673Sjfb {
160008f90673Sjfb 	struct context_vec *cvp = context_vec_start;
160108f90673Sjfb 	int lowa, upb, lowc, upd, do_output;
160208f90673Sjfb 	int a, b, c, d;
16035e78344dSjfb 	char ch, *f;
160408f90673Sjfb 
160508f90673Sjfb 	if (context_vec_start > context_vec_ptr)
160608f90673Sjfb 		return;
160708f90673Sjfb 
160808f90673Sjfb 	b = d = 0;		/* gcc */
1609dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1610e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1611dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1612e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
161308f90673Sjfb 
16145ac8b1e7Sjoris 	cvs_printf("***************");
16155e78344dSjfb 	if (pflag) {
16165e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
16175e78344dSjfb 		if (f != NULL) {
16185ac8b1e7Sjoris 			cvs_putchar(' ');
16195ac8b1e7Sjoris 			cvs_printf("%s", f);
16205e78344dSjfb 		}
16215e78344dSjfb 	}
16225ac8b1e7Sjoris 	cvs_printf("\n*** ");
162308f90673Sjfb 	range(lowa, upb, ",");
16245ac8b1e7Sjoris 	cvs_printf(" ****\n");
162508f90673Sjfb 
162608f90673Sjfb 	/*
162708f90673Sjfb 	 * Output changes to the "old" file.  The first loop suppresses
162808f90673Sjfb 	 * output if there were no changes to the "old" file (we'll see
162908f90673Sjfb 	 * the "old" lines as context in the "new" list).
163008f90673Sjfb 	 */
163108f90673Sjfb 	do_output = 0;
163208f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++)
163308f90673Sjfb 		if (cvp->a <= cvp->b) {
163408f90673Sjfb 			cvp = context_vec_start;
163508f90673Sjfb 			do_output++;
163608f90673Sjfb 			break;
163708f90673Sjfb 		}
163808f90673Sjfb 	if (do_output) {
163908f90673Sjfb 		while (cvp <= context_vec_ptr) {
164008f90673Sjfb 			a = cvp->a;
164108f90673Sjfb 			b = cvp->b;
164208f90673Sjfb 			c = cvp->c;
164308f90673Sjfb 			d = cvp->d;
164408f90673Sjfb 
164508f90673Sjfb 			if (a <= b && c <= d)
164608f90673Sjfb 				ch = 'c';
164708f90673Sjfb 			else
164808f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
164908f90673Sjfb 
165008f90673Sjfb 			if (ch == 'a')
165108f90673Sjfb 				fetch(ixold, lowa, b, f1, ' ', 0);
165208f90673Sjfb 			else {
165308f90673Sjfb 				fetch(ixold, lowa, a - 1, f1, ' ', 0);
165408f90673Sjfb 				fetch(ixold, a, b, f1,
165508f90673Sjfb 				    ch == 'c' ? '!' : '-', 0);
165608f90673Sjfb 			}
165708f90673Sjfb 			lowa = b + 1;
165808f90673Sjfb 			cvp++;
165908f90673Sjfb 		}
166008f90673Sjfb 		fetch(ixold, b + 1, upb, f1, ' ', 0);
166108f90673Sjfb 	}
166208f90673Sjfb 	/* output changes to the "new" file */
16635ac8b1e7Sjoris 	cvs_printf("--- ");
166408f90673Sjfb 	range(lowc, upd, ",");
16655ac8b1e7Sjoris 	cvs_printf(" ----\n");
166608f90673Sjfb 
166708f90673Sjfb 	do_output = 0;
166808f90673Sjfb 	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
166908f90673Sjfb 		if (cvp->c <= cvp->d) {
167008f90673Sjfb 			cvp = context_vec_start;
167108f90673Sjfb 			do_output++;
167208f90673Sjfb 			break;
167308f90673Sjfb 		}
167408f90673Sjfb 	if (do_output) {
167508f90673Sjfb 		while (cvp <= context_vec_ptr) {
167608f90673Sjfb 			a = cvp->a;
167708f90673Sjfb 			b = cvp->b;
167808f90673Sjfb 			c = cvp->c;
167908f90673Sjfb 			d = cvp->d;
168008f90673Sjfb 
168108f90673Sjfb 			if (a <= b && c <= d)
168208f90673Sjfb 				ch = 'c';
168308f90673Sjfb 			else
168408f90673Sjfb 				ch = (a <= b) ? 'd' : 'a';
168508f90673Sjfb 
168608f90673Sjfb 			if (ch == 'd')
168708f90673Sjfb 				fetch(ixnew, lowc, d, f2, ' ', 0);
168808f90673Sjfb 			else {
168908f90673Sjfb 				fetch(ixnew, lowc, c - 1, f2, ' ', 0);
169008f90673Sjfb 				fetch(ixnew, c, d, f2,
169108f90673Sjfb 				    ch == 'c' ? '!' : '+', 0);
169208f90673Sjfb 			}
169308f90673Sjfb 			lowc = d + 1;
169408f90673Sjfb 			cvp++;
169508f90673Sjfb 		}
169608f90673Sjfb 		fetch(ixnew, d + 1, upd, f2, ' ', 0);
169708f90673Sjfb 	}
169808f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
169908f90673Sjfb }
170008f90673Sjfb 
170108f90673Sjfb /* dump accumulated "unified" diff changes */
170208f90673Sjfb static void
170308f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2)
170408f90673Sjfb {
170508f90673Sjfb 	struct context_vec *cvp = context_vec_start;
170608f90673Sjfb 	int lowa, upb, lowc, upd;
170708f90673Sjfb 	int a, b, c, d;
17085e78344dSjfb 	char ch, *f;
170908f90673Sjfb 
171008f90673Sjfb 	if (context_vec_start > context_vec_ptr)
171108f90673Sjfb 		return;
171208f90673Sjfb 
171308f90673Sjfb 	b = d = 0;		/* gcc */
1714dc6a6879Sjfb 	lowa = MAX(1, cvp->a - context);
1715e4276007Sjfb 	upb = MIN(diff_len[0], context_vec_ptr->b + context);
1716dc6a6879Sjfb 	lowc = MAX(1, cvp->c - context);
1717e4276007Sjfb 	upd = MIN(diff_len[1], context_vec_ptr->d + context);
171808f90673Sjfb 
17195ac8b1e7Sjoris 	cvs_printf("@@ -");
172008f90673Sjfb 	uni_range(lowa, upb);
17215ac8b1e7Sjoris 	cvs_printf(" +");
172208f90673Sjfb 	uni_range(lowc, upd);
17235ac8b1e7Sjoris 	cvs_printf(" @@");
17245e78344dSjfb 	if (pflag) {
17255e78344dSjfb 		f = match_function(ixold, lowa - 1, f1);
17265e78344dSjfb 		if (f != NULL) {
17275ac8b1e7Sjoris 			cvs_putchar(' ');
17285ac8b1e7Sjoris 			cvs_printf("%s", f);
17295e78344dSjfb 		}
17305e78344dSjfb 	}
17315ac8b1e7Sjoris 	cvs_putchar('\n');
173208f90673Sjfb 
173308f90673Sjfb 	/*
173408f90673Sjfb 	 * Output changes in "unified" diff format--the old and new lines
173508f90673Sjfb 	 * are printed together.
173608f90673Sjfb 	 */
173708f90673Sjfb 	for (; cvp <= context_vec_ptr; cvp++) {
173808f90673Sjfb 		a = cvp->a;
173908f90673Sjfb 		b = cvp->b;
174008f90673Sjfb 		c = cvp->c;
174108f90673Sjfb 		d = cvp->d;
174208f90673Sjfb 
174308f90673Sjfb 		/*
174408f90673Sjfb 		 * c: both new and old changes
174508f90673Sjfb 		 * d: only changes in the old file
174608f90673Sjfb 		 * a: only changes in the new file
174708f90673Sjfb 		 */
174808f90673Sjfb 		if (a <= b && c <= d)
174908f90673Sjfb 			ch = 'c';
175008f90673Sjfb 		else
175108f90673Sjfb 			ch = (a <= b) ? 'd' : 'a';
175208f90673Sjfb 
175308f90673Sjfb 		switch (ch) {
175408f90673Sjfb 		case 'c':
175508f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
175608f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
175708f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
175808f90673Sjfb 			break;
175908f90673Sjfb 		case 'd':
176008f90673Sjfb 			fetch(ixold, lowa, a - 1, f1, ' ', 0);
176108f90673Sjfb 			fetch(ixold, a, b, f1, '-', 0);
176208f90673Sjfb 			break;
176308f90673Sjfb 		case 'a':
176408f90673Sjfb 			fetch(ixnew, lowc, c - 1, f2, ' ', 0);
176508f90673Sjfb 			fetch(ixnew, c, d, f2, '+', 0);
176608f90673Sjfb 			break;
176708f90673Sjfb 		}
176808f90673Sjfb 		lowa = b + 1;
176908f90673Sjfb 		lowc = d + 1;
177008f90673Sjfb 	}
177108f90673Sjfb 	fetch(ixnew, d + 1, upd, f2, ' ', 0);
177208f90673Sjfb 
177308f90673Sjfb 	context_vec_ptr = context_vec_start - 1;
177408f90673Sjfb }
1775