1*cb9f80caSxsa /* $OpenBSD: diff.c,v 1.77 2006/01/25 11:13:18 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 129ac41f80cSxsa #include "includes.h" 13008f90673Sjfb 1319225b0caSxsa #include "buf.h" 13208f90673Sjfb #include "cvs.h" 133af5bb824Sniallo #include "diff.h" 13408f90673Sjfb #include "log.h" 135dc6a6879Sjfb #include "proto.h" 13608f90673Sjfb 13708f90673Sjfb struct cand { 13808f90673Sjfb int x; 13908f90673Sjfb int y; 14008f90673Sjfb int pred; 14108f90673Sjfb } cand; 14208f90673Sjfb 14308f90673Sjfb struct line { 14408f90673Sjfb int serial; 14508f90673Sjfb int value; 14608f90673Sjfb } *file[2]; 14708f90673Sjfb 14808f90673Sjfb /* 14908f90673Sjfb * The following struct is used to record change in formation when 15008f90673Sjfb * doing a "context" or "unified" diff. (see routine "change" to 15108f90673Sjfb * understand the highly mnemonic field names) 15208f90673Sjfb */ 15308f90673Sjfb struct context_vec { 15408f90673Sjfb int a; /* start line in old file */ 15508f90673Sjfb int b; /* end line in old file */ 15608f90673Sjfb int c; /* start line in new file */ 15708f90673Sjfb int d; /* end line in new file */ 15808f90673Sjfb }; 15908f90673Sjfb 160dc6a6879Sjfb struct diff_arg { 161dc6a6879Sjfb char *rev1; 162dc6a6879Sjfb char *rev2; 163dc6a6879Sjfb char *date1; 164dc6a6879Sjfb char *date2; 165dc6a6879Sjfb }; 166dc6a6879Sjfb 167af5bb824Sniallo #if !defined(RCSPROG) 168e4276007Sjfb static int cvs_diff_init(struct cvs_cmd *, int, char **, int *); 169e4276007Sjfb static int cvs_diff_remote(CVSFILE *, void *); 170e4276007Sjfb static int cvs_diff_local(CVSFILE *, void *); 171e4276007Sjfb static int cvs_diff_pre_exec(struct cvsroot *); 172e4276007Sjfb static int cvs_diff_cleanup(void); 173af5bb824Sniallo #endif 17416cfc147Sjoris 17508f90673Sjfb static void output(const char *, FILE *, const char *, FILE *); 17608f90673Sjfb static void check(FILE *, FILE *); 17708f90673Sjfb static void range(int, int, char *); 17808f90673Sjfb static void uni_range(int, int); 17908f90673Sjfb static void dump_context_vec(FILE *, FILE *); 18008f90673Sjfb static void dump_unified_vec(FILE *, FILE *); 1817f535ec4Sjfb static int prepare(int, FILE *, off_t); 18208f90673Sjfb static void prune(void); 18308f90673Sjfb static void equiv(struct line *, int, struct line *, int, int *); 18408f90673Sjfb static void unravel(int); 18508f90673Sjfb static void unsort(struct line *, int, int *); 186ff1f7a8eSxsa static void change(const char *, FILE *, const char *, FILE *, int, 187ff1f7a8eSxsa int, int, int); 18808f90673Sjfb static void sort(struct line *, int); 18908f90673Sjfb static int ignoreline(char *); 19008f90673Sjfb static int asciifile(FILE *); 19108f90673Sjfb static int fetch(long *, int, int, FILE *, int, int); 19208f90673Sjfb static int newcand(int, int, int); 19308f90673Sjfb static int search(int *, int, int); 19408f90673Sjfb static int skipline(FILE *); 19508f90673Sjfb static int isqrt(int); 19608f90673Sjfb static int stone(int *, int, int *, int *); 19708f90673Sjfb static int readhash(FILE *); 19808f90673Sjfb static int files_differ(FILE *, FILE *); 1995e78344dSjfb static char *match_function(const long *, int, FILE *); 20008f90673Sjfb static char *preadline(int, size_t, off_t); 20108f90673Sjfb 20208f90673Sjfb 203af5bb824Sniallo #if !defined(RCSPROG) 204af5bb824Sniallo static int Nflag; 205af5bb824Sniallo #endif 206af5bb824Sniallo static int aflag, bflag, dflag, iflag, pflag, tflag, Tflag, wflag; 20778de3304Sniallo static int context = 3; 208f9b67873Sniallo int diff_format = D_NORMAL; 2091ff4dac1Sjoris char *diff_file = NULL; 210c60b216dSxsa char diffargs[128]; 21108f90673Sjfb static struct stat stb1, stb2; 212af5bb824Sniallo static char *ifdefname, *ignore_pats; 21308f90673Sjfb regex_t ignore_re; 21408f90673Sjfb 21508f90673Sjfb static int *J; /* will be overlaid on class */ 21608f90673Sjfb static int *class; /* will be overlaid on file[0] */ 21708f90673Sjfb static int *klist; /* will be overlaid on file[0] after class */ 21808f90673Sjfb static int *member; /* will be overlaid on file[1] */ 21908f90673Sjfb static int clen; 22008f90673Sjfb static int inifdef; /* whether or not we are in a #ifdef block */ 221e4276007Sjfb static int diff_len[2]; 22208f90673Sjfb static int pref, suff; /* length of prefix and suffix */ 22308f90673Sjfb static int slen[2]; 22408f90673Sjfb static int anychange; 22508f90673Sjfb static long *ixnew; /* will be overlaid on file[1] */ 22608f90673Sjfb static long *ixold; /* will be overlaid on klist */ 22708f90673Sjfb static struct cand *clist; /* merely a free storage pot for candidates */ 22808f90673Sjfb static int clistlen; /* the length of clist */ 22908f90673Sjfb static struct line *sfile[2]; /* shortened by pruning common prefix/suffix */ 23008f90673Sjfb static u_char *chrtran; /* translation table for case-folding */ 23108f90673Sjfb static struct context_vec *context_vec_start; 23208f90673Sjfb static struct context_vec *context_vec_end; 23308f90673Sjfb static struct context_vec *context_vec_ptr; 23408f90673Sjfb 23508f90673Sjfb #define FUNCTION_CONTEXT_SIZE 41 2365e78344dSjfb static char lastbuf[FUNCTION_CONTEXT_SIZE]; 23708f90673Sjfb static int lastline; 23808f90673Sjfb static int lastmatchline; 23901af718aSjoris BUF *diffbuf = NULL; 240f9b67873Sniallo 24108f90673Sjfb /* 24208f90673Sjfb * chrtran points to one of 2 translation tables: cup2low if folding upper to 24308f90673Sjfb * lower case clow2low if not folding case 24408f90673Sjfb */ 24508f90673Sjfb u_char clow2low[256] = { 24608f90673Sjfb 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 24708f90673Sjfb 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 24808f90673Sjfb 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 24908f90673Sjfb 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 25008f90673Sjfb 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 25108f90673Sjfb 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 25208f90673Sjfb 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 25308f90673Sjfb 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 25408f90673Sjfb 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 25508f90673Sjfb 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 25608f90673Sjfb 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 25708f90673Sjfb 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 25808f90673Sjfb 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 25908f90673Sjfb 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 26008f90673Sjfb 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 26108f90673Sjfb 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 26208f90673Sjfb 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 26308f90673Sjfb 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 26408f90673Sjfb 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 26508f90673Sjfb 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 26608f90673Sjfb 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 26708f90673Sjfb 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 26808f90673Sjfb 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 26908f90673Sjfb 0xfd, 0xfe, 0xff 27008f90673Sjfb }; 27108f90673Sjfb 27208f90673Sjfb u_char cup2low[256] = { 27308f90673Sjfb 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 27408f90673Sjfb 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 27508f90673Sjfb 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 27608f90673Sjfb 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 27708f90673Sjfb 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 27808f90673Sjfb 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61, 27908f90673Sjfb 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 28008f90673Sjfb 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 28108f90673Sjfb 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62, 28208f90673Sjfb 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 28308f90673Sjfb 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 28408f90673Sjfb 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 28508f90673Sjfb 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 28608f90673Sjfb 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 28708f90673Sjfb 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 28808f90673Sjfb 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 28908f90673Sjfb 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 29008f90673Sjfb 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 29108f90673Sjfb 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 29208f90673Sjfb 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 29308f90673Sjfb 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 29408f90673Sjfb 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 29508f90673Sjfb 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 29608f90673Sjfb 0xfd, 0xfe, 0xff 29708f90673Sjfb }; 29808f90673Sjfb 299af5bb824Sniallo #if !defined(RCSPROG) 300e4276007Sjfb struct cvs_cmd cvs_cmd_diff = { 301e4276007Sjfb CVS_OP_DIFF, CVS_REQ_DIFF, "diff", 302e4276007Sjfb { "di", "dif" }, 303e4276007Sjfb "Show differences between revisions", 304c9150269Sxsa "[-cilNnpu] [[-D date] [-r rev] [-D date2 | -r rev2]] " 305c9150269Sxsa "[-k mode] [file ...]", 306c9150269Sxsa "cD:iklNnpr:Ru", 30716cfc147Sjoris NULL, 30816cfc147Sjoris CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN, 309e4276007Sjfb cvs_diff_init, 310e4276007Sjfb cvs_diff_pre_exec, 311e4276007Sjfb cvs_diff_remote, 312e4276007Sjfb cvs_diff_local, 313e4276007Sjfb NULL, 314e4276007Sjfb cvs_diff_cleanup, 315e4276007Sjfb CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR 316e4276007Sjfb }; 317e4276007Sjfb 318e4276007Sjfb 319e4276007Sjfb struct cvs_cmd cvs_cmd_rdiff = { 320e4276007Sjfb CVS_OP_RDIFF, CVS_REQ_DIFF, "rdiff", 321c9150269Sxsa { "pa", "patch" }, 322e4276007Sjfb "Create 'patch' format diffs between releases", 323c9150269Sxsa "[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev " 324c9150269Sxsa "[-D date2 | -rev2] module ...", 325c9150269Sxsa "cD:flRr:stuV:", 326e4276007Sjfb NULL, 327e4276007Sjfb CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN, 328e4276007Sjfb cvs_diff_init, 329e4276007Sjfb cvs_diff_pre_exec, 330e4276007Sjfb cvs_diff_remote, 331e4276007Sjfb cvs_diff_local, 332e4276007Sjfb NULL, 333e4276007Sjfb cvs_diff_cleanup, 33444381dcbSjoris CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR 33516cfc147Sjoris }; 336af5bb824Sniallo #endif 33708f90673Sjfb 338af5bb824Sniallo #if !defined(RCSPROG) 33916cfc147Sjoris static struct diff_arg *dap = NULL; 34016cfc147Sjoris static int recurse; 34116cfc147Sjoris 342e4276007Sjfb static int 343e4276007Sjfb cvs_diff_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) 34408f90673Sjfb { 34516cfc147Sjoris int ch; 34608f90673Sjfb 3470450b43bSjoris dap = (struct diff_arg *)xmalloc(sizeof(*dap)); 34816cfc147Sjoris dap->date1 = dap->date2 = dap->rev1 = dap->rev2 = NULL; 349dc6a6879Sjfb strlcpy(diffargs, argv[0], sizeof(diffargs)); 350dc6a6879Sjfb 351e4276007Sjfb while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { 35208f90673Sjfb switch (ch) { 35308f90673Sjfb case 'c': 354f5638424Sjfb strlcat(diffargs, " -c", sizeof(diffargs)); 355f9b67873Sniallo diff_format = D_CONTEXT; 35608f90673Sjfb break; 35708f90673Sjfb case 'D': 35816cfc147Sjoris if (dap->date1 == NULL && dap->rev1 == NULL) { 35916cfc147Sjoris dap->date1 = optarg; 36016cfc147Sjoris } else if (dap->date2 == NULL && dap->rev2 == NULL) { 36116cfc147Sjoris dap->date2 = optarg; 36216cfc147Sjoris } else { 36308f90673Sjfb cvs_log(LP_ERR, 36408f90673Sjfb "no more than two revisions/dates can " 36508f90673Sjfb "be specified"); 36608f90673Sjfb } 36708f90673Sjfb break; 36808f90673Sjfb case 'l': 369f5638424Sjfb strlcat(diffargs, " -l", sizeof(diffargs)); 37008f90673Sjfb recurse = 0; 371e4276007Sjfb cvs_cmd_diff.file_flags &= ~CF_RECURSE; 37208f90673Sjfb break; 37308f90673Sjfb case 'i': 374f5638424Sjfb strlcat(diffargs, " -i", sizeof(diffargs)); 37508f90673Sjfb iflag = 1; 37608f90673Sjfb break; 377c710bc5aSjfb case 'N': 378c710bc5aSjfb strlcat(diffargs, " -N", sizeof(diffargs)); 379c710bc5aSjfb Nflag = 1; 380c710bc5aSjfb break; 381394180a4Sjfb case 'n': 382394180a4Sjfb strlcat(diffargs, " -n", sizeof(diffargs)); 383f9b67873Sniallo diff_format = D_RCSDIFF; 384394180a4Sjfb break; 3855e78344dSjfb case 'p': 3865e78344dSjfb strlcat(diffargs, " -p", sizeof(diffargs)); 3875e78344dSjfb pflag = 1; 3885e78344dSjfb break; 38908f90673Sjfb case 'r': 39016cfc147Sjoris if ((dap->rev1 == NULL) && (dap->date1 == NULL)) { 39116cfc147Sjoris dap->rev1 = optarg; 39216cfc147Sjoris } else if ((dap->rev2 == NULL) && 39316cfc147Sjoris (dap->date2 == NULL)) { 39416cfc147Sjoris dap->rev2 = optarg; 39516cfc147Sjoris } else { 39608f90673Sjfb cvs_log(LP_ERR, 39708f90673Sjfb "no more than two revisions/dates can " 39808f90673Sjfb "be specified"); 39931274bbfSjoris return (CVS_EX_USAGE); 40008f90673Sjfb } 40108f90673Sjfb break; 402f203c484Sjoris case 'R': 403e4276007Sjfb cvs_cmd_diff.file_flags |= CF_RECURSE; 404f203c484Sjoris break; 40508f90673Sjfb case 'u': 406f5638424Sjfb strlcat(diffargs, " -u", sizeof(diffargs)); 407f9b67873Sniallo diff_format = D_UNIFIED; 40808f90673Sjfb break; 40908f90673Sjfb default: 41031274bbfSjoris return (CVS_EX_USAGE); 41108f90673Sjfb } 41208f90673Sjfb } 41308f90673Sjfb 41416cfc147Sjoris *arg = optind; 415dc6a6879Sjfb return (0); 416dc6a6879Sjfb } 417dc6a6879Sjfb 41816cfc147Sjoris int 41916cfc147Sjoris cvs_diff_cleanup(void) 42016cfc147Sjoris { 421e4276007Sjfb if (dap != NULL) { 4220450b43bSjoris xfree(dap); 423e4276007Sjfb dap = NULL; 424e4276007Sjfb } 42516cfc147Sjoris return (0); 42616cfc147Sjoris } 427dc6a6879Sjfb 428dc6a6879Sjfb /* 429e4276007Sjfb * cvs_diff_pre_exec() 430dc6a6879Sjfb * 431dc6a6879Sjfb */ 432dc6a6879Sjfb int 433e4276007Sjfb cvs_diff_pre_exec(struct cvsroot *root) 434dc6a6879Sjfb { 435e4276007Sjfb if (root->cr_method != CVS_METHOD_LOCAL) { 43608f90673Sjfb /* send the flags */ 4377e393898Sjoris if (Nflag == 1) 4387e393898Sjoris cvs_sendarg(root, "-N", 0); 4397e393898Sjoris if (pflag == 1) 4407e393898Sjoris cvs_sendarg(root, "-p", 0); 4415e78344dSjfb 4427e393898Sjoris if (diff_format == D_CONTEXT) 4437e393898Sjoris cvs_sendarg(root, "-c", 0); 4447e393898Sjoris else if (diff_format == D_UNIFIED) 4457e393898Sjoris cvs_sendarg(root, "-u", 0); 44608f90673Sjfb 447dc6a6879Sjfb if (dap->rev1 != NULL) { 4487e393898Sjoris cvs_sendarg(root, "-r", 0); 4497e393898Sjoris cvs_sendarg(root, dap->rev1, 0); 4503917c9bfSderaadt } else if (dap->date1 != NULL) { 4517e393898Sjoris cvs_sendarg(root, "-D", 0); 4527e393898Sjoris cvs_sendarg(root, dap->date1, 0); 45308f90673Sjfb } 454dc6a6879Sjfb if (dap->rev2 != NULL) { 4557e393898Sjoris cvs_sendarg(root, "-r", 0); 4567e393898Sjoris cvs_sendarg(root, dap->rev2, 0); 4573917c9bfSderaadt } else if (dap->date2 != NULL) { 4587e393898Sjoris cvs_sendarg(root, "-D", 0); 4597e393898Sjoris cvs_sendarg(root, dap->date2, 0); 46008f90673Sjfb } 461e4276007Sjfb } 46208f90673Sjfb 46308f90673Sjfb return (0); 46408f90673Sjfb } 46508f90673Sjfb 46608f90673Sjfb 46708f90673Sjfb /* 46808f90673Sjfb * cvs_diff_file() 46908f90673Sjfb * 47008f90673Sjfb * Diff a single file. 47108f90673Sjfb */ 472e4276007Sjfb static int 473e4276007Sjfb cvs_diff_remote(struct cvs_file *cfp, void *arg) 47408f90673Sjfb { 475e4276007Sjfb char *dir, *repo; 476e4276007Sjfb char fpath[MAXPATHLEN], dfpath[MAXPATHLEN]; 477dc6a6879Sjfb struct cvsroot *root; 478dc6a6879Sjfb 479dc6a6879Sjfb if (cfp->cf_type == DT_DIR) { 480895e6cf6Sjfb if (cfp->cf_cvstat == CVS_FST_UNKNOWN) { 481bb029937Sjfb root = cfp->cf_parent->cf_root; 482b904ba2eSjoris cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name); 4833917c9bfSderaadt } else { 484bb029937Sjfb root = cfp->cf_root; 48516cfc147Sjoris #if 0 486dc6a6879Sjfb if ((cfp->cf_parent == NULL) || 487bb029937Sjfb (root != cfp->cf_parent->cf_root)) { 488dc6a6879Sjfb cvs_connect(root); 489e4276007Sjfb cvs_diff_pre_exec(root); 490dc6a6879Sjfb } 49116cfc147Sjoris #endif 492dc6a6879Sjfb 493dc6a6879Sjfb cvs_senddir(root, cfp); 494895e6cf6Sjfb } 495895e6cf6Sjfb 496dc6a6879Sjfb return (0); 497dc6a6879Sjfb } 49808f90673Sjfb 4992d5b8b1dSjfb if (cfp->cf_cvstat == CVS_FST_LOST) { 500b904ba2eSjoris cvs_log(LP_WARN, "cannot find file %s", cfp->cf_name); 5012d5b8b1dSjfb return (0); 5022d5b8b1dSjfb } 5032d5b8b1dSjfb 504c710bc5aSjfb diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath)); 505895e6cf6Sjfb 506dc6a6879Sjfb if (cfp->cf_parent != NULL) { 507c710bc5aSjfb dir = cvs_file_getpath(cfp->cf_parent, dfpath, sizeof(dfpath)); 508bb029937Sjfb root = cfp->cf_parent->cf_root; 509bb029937Sjfb repo = cfp->cf_parent->cf_repo; 5103917c9bfSderaadt } else { 511dc6a6879Sjfb dir = "."; 512895e6cf6Sjfb root = NULL; 513dc6a6879Sjfb repo = NULL; 51408f90673Sjfb } 51508f90673Sjfb 516dc6a6879Sjfb if (cfp->cf_cvstat == CVS_FST_UNKNOWN) { 517e4276007Sjfb cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name); 518dc6a6879Sjfb return (0); 51908f90673Sjfb } 52008f90673Sjfb 5217e393898Sjoris cvs_sendentry(root, cfp); 52208f90673Sjfb 523dc6a6879Sjfb if (cfp->cf_cvstat == CVS_FST_UPTODATE) { 524e4276007Sjfb cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name); 52508f90673Sjfb return (0); 52608f90673Sjfb } 52708f90673Sjfb 52808f90673Sjfb /* at this point, the file is modified */ 5297e393898Sjoris cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name); 5307e393898Sjoris cvs_sendfile(root, diff_file); 531e4276007Sjfb 532e4276007Sjfb return (0); 533e4276007Sjfb } 534e4276007Sjfb 535e4276007Sjfb static int 536e4276007Sjfb cvs_diff_local(CVSFILE *cf, void *arg) 537e4276007Sjfb { 538e4276007Sjfb char *repo, buf[64]; 539e4276007Sjfb char fpath[MAXPATHLEN], rcspath[MAXPATHLEN]; 540e4276007Sjfb char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN]; 541e4276007Sjfb BUF *b1, *b2; 542e4276007Sjfb RCSNUM *r1, *r2; 543e4276007Sjfb RCSFILE *rf; 544e4276007Sjfb struct cvsroot *root; 545bf951d2dSniallo struct timeval tv[2], tv2[2]; 546bf951d2dSniallo 547bf951d2dSniallo memset(&tv, 0, sizeof(tv)); 548bf951d2dSniallo memset(&tv2, 0, sizeof(tv2)); 549e4276007Sjfb 550e4276007Sjfb rf = NULL; 551e4276007Sjfb root = CVS_DIR_ROOT(cf); 552e4276007Sjfb repo = CVS_DIR_REPO(cf); 553e4276007Sjfb diff_file = cvs_file_getpath(cf, fpath, sizeof(fpath)); 554e4276007Sjfb 555e4276007Sjfb if (cf->cf_type == DT_DIR) { 556b87c59bdSxsa if (verbosity > 1) 557246675edSxsa cvs_log(LP_NOTICE, "Diffing %s", fpath); 558e4276007Sjfb return (0); 559e4276007Sjfb } 560e4276007Sjfb 561e4276007Sjfb if (cf->cf_cvstat == CVS_FST_LOST) { 562e4276007Sjfb cvs_log(LP_WARN, "cannot find file %s", cf->cf_name); 563e4276007Sjfb return (0); 564e4276007Sjfb } 565e4276007Sjfb 566e4276007Sjfb if (cf->cf_cvstat == CVS_FST_UNKNOWN) { 567e4276007Sjfb cvs_log(LP_WARN, "I know nothing about %s", diff_file); 568e4276007Sjfb return (0); 5695c0cd766Sniallo } else if (cf->cf_cvstat == CVS_FST_UPTODATE) 570e4276007Sjfb return (0); 571e4276007Sjfb 572e4276007Sjfb /* at this point, the file is modified */ 573*cb9f80caSxsa cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); 57408f90673Sjfb 5751b6534b8Sjfb rf = rcs_open(rcspath, RCS_READ); 576dc6a6879Sjfb if (rf == NULL) { 57731274bbfSjoris return (CVS_EX_DATA); 578dc6a6879Sjfb } 57908f90673Sjfb 580dc6a6879Sjfb cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file, 58108f90673Sjfb RCS_DIFF_DIV, rcspath); 58208f90673Sjfb 583dc6a6879Sjfb if (dap->rev1 == NULL) 584e4276007Sjfb r1 = cf->cf_lrev; 58508f90673Sjfb else { 58625b74b48Sjfb if ((r1 = rcsnum_parse(dap->rev1)) == NULL) { 58764672f74Sjoris rcs_close(rf); 58831274bbfSjoris return (CVS_EX_DATA); 5897f535ec4Sjfb } 59008f90673Sjfb } 59108f90673Sjfb 592dc6a6879Sjfb cvs_printf("retrieving revision %s\n", 59308f90673Sjfb rcsnum_tostr(r1, buf, sizeof(buf))); 59408f90673Sjfb b1 = rcs_getrev(rf, r1); 59508f90673Sjfb 59695ae1173Sjoris if (b1 == NULL) { 597289b97f4Sxsa cvs_log(LP_ERR, "failed to retrieve revision %s\n", 59895ae1173Sjoris rcsnum_tostr(r1, buf, sizeof(buf))); 59995ae1173Sjoris if (r1 != cf->cf_lrev) 60095ae1173Sjoris rcsnum_free(r1); 60164672f74Sjoris rcs_close(rf); 60295ae1173Sjoris return (CVS_EX_DATA); 60395ae1173Sjoris } 604bf951d2dSniallo tv[0].tv_sec = (long)rcs_rev_getdate(rf, r1); 605bf951d2dSniallo tv[1].tv_sec = tv[0].tv_sec; 60695ae1173Sjoris 607e4276007Sjfb if (r1 != cf->cf_lrev) 6087f535ec4Sjfb rcsnum_free(r1); 6097f535ec4Sjfb 610dc6a6879Sjfb if (dap->rev2 != NULL) { 611dc6a6879Sjfb cvs_printf("retrieving revision %s\n", dap->rev2); 61225b74b48Sjfb if ((r2 = rcsnum_parse(dap->rev2)) == NULL) { 61364672f74Sjoris rcs_close(rf); 61431274bbfSjoris return (CVS_EX_DATA); 6157f535ec4Sjfb } 61608f90673Sjfb b2 = rcs_getrev(rf, r2); 617f3e6c8ebSniallo tv2[0].tv_sec = (long)rcs_rev_getdate(rf, r2); 618f3e6c8ebSniallo tv2[1].tv_sec = tv2[0].tv_sec; 6197f535ec4Sjfb rcsnum_free(r2); 6203917c9bfSderaadt } else { 621f3e6c8ebSniallo struct stat st; 622f3e6c8ebSniallo if (stat(diff_file, &st) < 0) { 623f3e6c8ebSniallo cvs_log(LP_ERR, "failed to retrieve revision %s\n", 624f3e6c8ebSniallo dap->rev2); 625f3e6c8ebSniallo cvs_buf_free(b1); 626f3e6c8ebSniallo return (CVS_EX_DATA); 627f3e6c8ebSniallo } 628dc6a6879Sjfb b2 = cvs_buf_load(diff_file, BUF_AUTOEXT); 629f3e6c8ebSniallo tv2[0].tv_sec = st.st_mtime; 630f3e6c8ebSniallo tv2[1].tv_sec = st.st_mtime; 63108f90673Sjfb } 63208f90673Sjfb 633dc6a6879Sjfb rcs_close(rf); 634dc6a6879Sjfb 63595ae1173Sjoris if (b2 == NULL) { 636289b97f4Sxsa cvs_log(LP_ERR, "failed to retrieve revision %s\n", 63795ae1173Sjoris dap->rev2); 63895ae1173Sjoris cvs_buf_free(b1); 63995ae1173Sjoris return (CVS_EX_DATA); 64095ae1173Sjoris } 64195ae1173Sjoris 6425ac8b1e7Sjoris cvs_printf("%s", diffargs); 6435ac8b1e7Sjoris cvs_printf(" -r%s", buf); 644dc6a6879Sjfb if (dap->rev2 != NULL) 6455ac8b1e7Sjoris cvs_printf(" -r%s", dap->rev2); 6465ac8b1e7Sjoris cvs_printf(" %s\n", diff_file); 6471dee9299Sxsa strlcpy(path_tmp1, cvs_tmpdir, sizeof(path_tmp1)); 6481dee9299Sxsa strlcat(path_tmp1, "/diff1.XXXXXXXXXX", sizeof(path_tmp1)); 649fbe562adSxsa cvs_buf_write_stmp(b1, path_tmp1, 0600); 6507f535ec4Sjfb cvs_buf_free(b1); 651bf951d2dSniallo if (utimes(path_tmp1, (const struct timeval *)&tv) < 0) 652bf951d2dSniallo cvs_log(LP_ERRNO, "error setting utimes"); 6537f535ec4Sjfb 6541dee9299Sxsa strlcpy(path_tmp2, cvs_tmpdir, sizeof(path_tmp2)); 6551dee9299Sxsa strlcat(path_tmp2, "/diff2.XXXXXXXXXX", sizeof(path_tmp2)); 6568dbc5e14Sxsa cvs_buf_write_stmp(b2, path_tmp2, 0600); 6577f535ec4Sjfb cvs_buf_free(b2); 658bf951d2dSniallo if (utimes(path_tmp2, (const struct timeval *)&tv2) < 0) 659bf951d2dSniallo cvs_log(LP_ERRNO, "error setting utimes"); 6607f535ec4Sjfb 661f9b67873Sniallo cvs_diffreg(path_tmp1, path_tmp2, NULL); 662946f6157Sdjm (void)unlink(path_tmp1); 663946f6157Sdjm (void)unlink(path_tmp2); 66408f90673Sjfb 66508f90673Sjfb return (0); 66608f90673Sjfb } 667af5bb824Sniallo #endif 66808f90673Sjfb 66908f90673Sjfb 67008f90673Sjfb int 671f9b67873Sniallo cvs_diffreg(const char *file1, const char *file2, BUF *out) 67208f90673Sjfb { 67308f90673Sjfb FILE *f1, *f2; 67408f90673Sjfb int i, rval; 6757f535ec4Sjfb void *tmp; 67608f90673Sjfb 67708f90673Sjfb f1 = f2 = NULL; 67808f90673Sjfb rval = D_SAME; 67908f90673Sjfb anychange = 0; 68008f90673Sjfb lastline = 0; 68108f90673Sjfb lastmatchline = 0; 68208f90673Sjfb context_vec_ptr = context_vec_start - 1; 68308f90673Sjfb chrtran = (iflag ? cup2low : clow2low); 684f9b67873Sniallo if (out != NULL) 685f9b67873Sniallo diffbuf = out; 68608f90673Sjfb 68708f90673Sjfb f1 = fopen(file1, "r"); 68808f90673Sjfb if (f1 == NULL) { 68908f90673Sjfb cvs_log(LP_ERRNO, "%s", file1); 69008f90673Sjfb goto closem; 69108f90673Sjfb } 69208f90673Sjfb 69308f90673Sjfb f2 = fopen(file2, "r"); 69408f90673Sjfb if (f2 == NULL) { 69508f90673Sjfb cvs_log(LP_ERRNO, "%s", file2); 69608f90673Sjfb goto closem; 69708f90673Sjfb } 69808f90673Sjfb 699bf951d2dSniallo if (stat(file1, &stb1) < 0) { 700bf951d2dSniallo cvs_log(LP_ERRNO, "%s", file1); 701bf951d2dSniallo goto closem; 702bf951d2dSniallo } 703bf951d2dSniallo if (stat(file2, &stb2) < 0) { 704bf951d2dSniallo cvs_log(LP_ERRNO, "%s", file2); 705bf951d2dSniallo goto closem; 706bf951d2dSniallo } 70708f90673Sjfb switch (files_differ(f1, f2)) { 70808f90673Sjfb case 0: 70908f90673Sjfb goto closem; 71008f90673Sjfb case 1: 71108f90673Sjfb break; 71208f90673Sjfb default: 71308f90673Sjfb /* error */ 71408f90673Sjfb goto closem; 71508f90673Sjfb } 71608f90673Sjfb 71708f90673Sjfb if (!asciifile(f1) || !asciifile(f2)) { 71808f90673Sjfb rval = D_BINARY; 71908f90673Sjfb goto closem; 72008f90673Sjfb } 7217f535ec4Sjfb if ((prepare(0, f1, stb1.st_size) < 0) || 7227f535ec4Sjfb (prepare(1, f2, stb2.st_size) < 0)) { 7237f535ec4Sjfb goto closem; 7247f535ec4Sjfb } 72508f90673Sjfb prune(); 72608f90673Sjfb sort(sfile[0], slen[0]); 72708f90673Sjfb sort(sfile[1], slen[1]); 72808f90673Sjfb 72908f90673Sjfb member = (int *)file[1]; 73008f90673Sjfb equiv(sfile[0], slen[0], sfile[1], slen[1], member); 7310450b43bSjoris tmp = xrealloc(member, (slen[1] + 2) * sizeof(int)); 7327f535ec4Sjfb member = (int *)tmp; 73308f90673Sjfb 73408f90673Sjfb class = (int *)file[0]; 73508f90673Sjfb unsort(sfile[0], slen[0], class); 7360450b43bSjoris tmp = xrealloc(class, (slen[0] + 2) * sizeof(int)); 7377f535ec4Sjfb class = (int *)tmp; 73808f90673Sjfb 7390450b43bSjoris klist = xmalloc((slen[0] + 2) * sizeof(int)); 74008f90673Sjfb clen = 0; 74108f90673Sjfb clistlen = 100; 7420450b43bSjoris clist = xmalloc(clistlen * sizeof(cand)); 743ece76a70Sjoris 744ece76a70Sjoris if ((i = stone(class, slen[0], member, klist)) < 0) 745ece76a70Sjoris goto closem; 746ece76a70Sjoris 7470450b43bSjoris xfree(member); 7480450b43bSjoris xfree(class); 74908f90673Sjfb 7500450b43bSjoris tmp = xrealloc(J, (diff_len[0] + 2) * sizeof(int)); 75118501190Sniallo J = (int *)tmp; 75208f90673Sjfb unravel(klist[i]); 7530450b43bSjoris xfree(clist); 7540450b43bSjoris xfree(klist); 75508f90673Sjfb 7560450b43bSjoris tmp = xrealloc(ixold, (diff_len[0] + 2) * sizeof(long)); 75718501190Sniallo ixold = (long *)tmp; 7580450b43bSjoris 7590450b43bSjoris tmp = xrealloc(ixnew, (diff_len[1] + 2) * sizeof(long)); 76018501190Sniallo ixnew = (long *)tmp; 76108f90673Sjfb check(f1, f2); 76208f90673Sjfb output(file1, f1, file2, f2); 76308f90673Sjfb 76408f90673Sjfb closem: 76537af5b8bSxsa if (anychange == 1) { 76608f90673Sjfb if (rval == D_SAME) 76708f90673Sjfb rval = D_DIFFER; 76808f90673Sjfb } 76908f90673Sjfb if (f1 != NULL) 77008f90673Sjfb fclose(f1); 77108f90673Sjfb if (f2 != NULL) 77208f90673Sjfb fclose(f2); 7737f535ec4Sjfb 77408f90673Sjfb return (rval); 77508f90673Sjfb } 77608f90673Sjfb 77708f90673Sjfb /* 77808f90673Sjfb * Check to see if the given files differ. 77908f90673Sjfb * Returns 0 if they are the same, 1 if different, and -1 on error. 78008f90673Sjfb * XXX - could use code from cmp(1) [faster] 78108f90673Sjfb */ 78208f90673Sjfb static int 78308f90673Sjfb files_differ(FILE *f1, FILE *f2) 78408f90673Sjfb { 78508f90673Sjfb char buf1[BUFSIZ], buf2[BUFSIZ]; 78608f90673Sjfb size_t i, j; 78708f90673Sjfb 78808f90673Sjfb if (stb1.st_size != stb2.st_size) 78908f90673Sjfb return (1); 79008f90673Sjfb for (;;) { 791f1901a5aSxsa i = fread(buf1, (size_t)1, sizeof(buf1), f1); 792f1901a5aSxsa j = fread(buf2, (size_t)1, sizeof(buf2), f2); 79308f90673Sjfb if (i != j) 79408f90673Sjfb return (1); 79537af5b8bSxsa if ((i == 0) && (j == 0)) { 79608f90673Sjfb if (ferror(f1) || ferror(f2)) 79708f90673Sjfb return (1); 79808f90673Sjfb return (0); 79908f90673Sjfb } 80008f90673Sjfb if (memcmp(buf1, buf2, i) != 0) 80108f90673Sjfb return (1); 80208f90673Sjfb } 80308f90673Sjfb } 80408f90673Sjfb 8057f535ec4Sjfb static int 80608f90673Sjfb prepare(int i, FILE *fd, off_t filesize) 80708f90673Sjfb { 8087f535ec4Sjfb void *tmp; 80908f90673Sjfb struct line *p; 81008f90673Sjfb int j, h; 81108f90673Sjfb size_t sz; 81208f90673Sjfb 81308f90673Sjfb rewind(fd); 81408f90673Sjfb 815c48da046Sxsa sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25; 81608f90673Sjfb if (sz < 100) 81708f90673Sjfb sz = 100; 81808f90673Sjfb 8190450b43bSjoris p = (struct line *)xmalloc((sz + 3) * sizeof(struct line)); 82008f90673Sjfb for (j = 0; (h = readhash(fd));) { 82108f90673Sjfb if (j == (int)sz) { 82208f90673Sjfb sz = sz * 3 / 2; 8230450b43bSjoris tmp = xrealloc(p, (sz + 3) * sizeof(struct line)); 8247f535ec4Sjfb p = (struct line *)tmp; 82508f90673Sjfb } 82608f90673Sjfb p[++j].value = h; 82708f90673Sjfb } 828e4276007Sjfb diff_len[i] = j; 82908f90673Sjfb file[i] = p; 8307f535ec4Sjfb 8317f535ec4Sjfb return (0); 83208f90673Sjfb } 83308f90673Sjfb 83408f90673Sjfb static void 83508f90673Sjfb prune(void) 83608f90673Sjfb { 83708f90673Sjfb int i, j; 83808f90673Sjfb 839e4276007Sjfb for (pref = 0; pref < diff_len[0] && pref < diff_len[1] && 84008f90673Sjfb file[0][pref + 1].value == file[1][pref + 1].value; 84108f90673Sjfb pref++) 84208f90673Sjfb ; 843e4276007Sjfb for (suff = 0; 844e4276007Sjfb (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) && 845e4276007Sjfb (file[0][diff_len[0] - suff].value == 846e4276007Sjfb file[1][diff_len[1] - suff].value); 84708f90673Sjfb suff++) 84808f90673Sjfb ; 84908f90673Sjfb for (j = 0; j < 2; j++) { 85008f90673Sjfb sfile[j] = file[j] + pref; 851e4276007Sjfb slen[j] = diff_len[j] - pref - suff; 85208f90673Sjfb for (i = 0; i <= slen[j]; i++) 85308f90673Sjfb sfile[j][i].serial = i; 85408f90673Sjfb } 85508f90673Sjfb } 85608f90673Sjfb 85708f90673Sjfb static void 85808f90673Sjfb equiv(struct line *a, int n, struct line *b, int m, int *c) 85908f90673Sjfb { 86008f90673Sjfb int i, j; 86108f90673Sjfb 86208f90673Sjfb i = j = 1; 86308f90673Sjfb while (i <= n && j <= m) { 86408f90673Sjfb if (a[i].value < b[j].value) 86508f90673Sjfb a[i++].value = 0; 86608f90673Sjfb else if (a[i].value == b[j].value) 86708f90673Sjfb a[i++].value = j; 86808f90673Sjfb else 86908f90673Sjfb j++; 87008f90673Sjfb } 87108f90673Sjfb while (i <= n) 87208f90673Sjfb a[i++].value = 0; 87308f90673Sjfb b[m + 1].value = 0; 87408f90673Sjfb j = 0; 87508f90673Sjfb while (++j <= m) { 87608f90673Sjfb c[j] = -b[j].serial; 87708f90673Sjfb while (b[j + 1].value == b[j].value) { 87808f90673Sjfb j++; 87908f90673Sjfb c[j] = b[j].serial; 88008f90673Sjfb } 88108f90673Sjfb } 88208f90673Sjfb c[j] = -1; 88308f90673Sjfb } 88408f90673Sjfb 88508f90673Sjfb /* Code taken from ping.c */ 88608f90673Sjfb static int 88708f90673Sjfb isqrt(int n) 88808f90673Sjfb { 88908f90673Sjfb int y, x = 1; 89008f90673Sjfb 89108f90673Sjfb if (n == 0) 89208f90673Sjfb return (0); 89308f90673Sjfb 89408f90673Sjfb do { /* newton was a stinker */ 89508f90673Sjfb y = x; 89608f90673Sjfb x = n / x; 89708f90673Sjfb x += y; 89808f90673Sjfb x /= 2; 89908f90673Sjfb } while ((x - y) > 1 || (x - y) < -1); 90008f90673Sjfb 90108f90673Sjfb return (x); 90208f90673Sjfb } 90308f90673Sjfb 90408f90673Sjfb static int 90508f90673Sjfb stone(int *a, int n, int *b, int *c) 90608f90673Sjfb { 907ece76a70Sjoris int ret; 90808f90673Sjfb int i, k, y, j, l; 90908f90673Sjfb int oldc, tc, oldl; 91008f90673Sjfb u_int numtries; 91108f90673Sjfb 912cc649edbSjfb /* XXX move the isqrt() out of the macro to avoid multiple calls */ 913cc649edbSjfb const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n)); 91408f90673Sjfb 91508f90673Sjfb k = 0; 916ece76a70Sjoris if ((ret = newcand(0, 0, 0)) < 0) 917ece76a70Sjoris return (-1); 918ece76a70Sjoris c[0] = ret; 91908f90673Sjfb for (i = 1; i <= n; i++) { 92008f90673Sjfb j = a[i]; 92108f90673Sjfb if (j == 0) 92208f90673Sjfb continue; 92308f90673Sjfb y = -b[j]; 92408f90673Sjfb oldl = 0; 92508f90673Sjfb oldc = c[0]; 92608f90673Sjfb numtries = 0; 92708f90673Sjfb do { 92808f90673Sjfb if (y <= clist[oldc].y) 92908f90673Sjfb continue; 93008f90673Sjfb l = search(c, k, y); 93108f90673Sjfb if (l != oldl + 1) 93208f90673Sjfb oldc = c[l - 1]; 93308f90673Sjfb if (l <= k) { 93408f90673Sjfb if (clist[c[l]].y <= y) 93508f90673Sjfb continue; 93608f90673Sjfb tc = c[l]; 937ece76a70Sjoris if ((ret = newcand(i, y, oldc)) < 0) 938ece76a70Sjoris return (-1); 939ece76a70Sjoris c[l] = ret; 94008f90673Sjfb oldc = tc; 94108f90673Sjfb oldl = l; 94208f90673Sjfb numtries++; 94308f90673Sjfb } else { 944ece76a70Sjoris if ((ret = newcand(i, y, oldc)) < 0) 945ece76a70Sjoris return (-1); 946ece76a70Sjoris c[l] = ret; 94708f90673Sjfb k++; 94808f90673Sjfb break; 94908f90673Sjfb } 95008f90673Sjfb } while ((y = b[++j]) > 0 && numtries < bound); 95108f90673Sjfb } 95208f90673Sjfb return (k); 95308f90673Sjfb } 95408f90673Sjfb 95508f90673Sjfb static int 95608f90673Sjfb newcand(int x, int y, int pred) 95708f90673Sjfb { 95818501190Sniallo struct cand *q, *tmp; 95918501190Sniallo int newclistlen; 96008f90673Sjfb 96108f90673Sjfb if (clen == clistlen) { 96218501190Sniallo newclistlen = clistlen * 11 / 10; 9630450b43bSjoris tmp = xrealloc(clist, newclistlen * sizeof(cand)); 96418501190Sniallo clist = tmp; 96518501190Sniallo clistlen = newclistlen; 96608f90673Sjfb } 96708f90673Sjfb q = clist + clen; 96808f90673Sjfb q->x = x; 96908f90673Sjfb q->y = y; 97008f90673Sjfb q->pred = pred; 97108f90673Sjfb return (clen++); 97208f90673Sjfb } 97308f90673Sjfb 97408f90673Sjfb static int 97508f90673Sjfb search(int *c, int k, int y) 97608f90673Sjfb { 97708f90673Sjfb int i, j, l, t; 97808f90673Sjfb 97908f90673Sjfb if (clist[c[k]].y < y) /* quick look for typical case */ 98008f90673Sjfb return (k + 1); 98108f90673Sjfb i = 0; 98208f90673Sjfb j = k + 1; 98308f90673Sjfb while (1) { 98408f90673Sjfb l = i + j; 98508f90673Sjfb if ((l >>= 1) <= i) 98608f90673Sjfb break; 98708f90673Sjfb t = clist[c[l]].y; 98808f90673Sjfb if (t > y) 98908f90673Sjfb j = l; 99008f90673Sjfb else if (t < y) 99108f90673Sjfb i = l; 99208f90673Sjfb else 99308f90673Sjfb return (l); 99408f90673Sjfb } 99508f90673Sjfb return (l + 1); 99608f90673Sjfb } 99708f90673Sjfb 99808f90673Sjfb static void 99908f90673Sjfb unravel(int p) 100008f90673Sjfb { 100108f90673Sjfb struct cand *q; 100208f90673Sjfb int i; 100308f90673Sjfb 1004e4276007Sjfb for (i = 0; i <= diff_len[0]; i++) 100508f90673Sjfb J[i] = i <= pref ? i : 1006e4276007Sjfb i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0; 100708f90673Sjfb for (q = clist + p; q->y != 0; q = clist + q->pred) 100808f90673Sjfb J[q->x + pref] = q->y + pref; 100908f90673Sjfb } 101008f90673Sjfb 101108f90673Sjfb /* 101208f90673Sjfb * Check does double duty: 101308f90673Sjfb * 1. ferret out any fortuitous correspondences due 101408f90673Sjfb * to confounding by hashing (which result in "jackpot") 101508f90673Sjfb * 2. collect random access indexes to the two files 101608f90673Sjfb */ 101708f90673Sjfb static void 101808f90673Sjfb check(FILE *f1, FILE *f2) 101908f90673Sjfb { 102008f90673Sjfb int i, j, jackpot, c, d; 102108f90673Sjfb long ctold, ctnew; 102208f90673Sjfb 102308f90673Sjfb rewind(f1); 102408f90673Sjfb rewind(f2); 102508f90673Sjfb j = 1; 102608f90673Sjfb ixold[0] = ixnew[0] = 0; 102708f90673Sjfb jackpot = 0; 102808f90673Sjfb ctold = ctnew = 0; 1029e4276007Sjfb for (i = 1; i <= diff_len[0]; i++) { 103008f90673Sjfb if (J[i] == 0) { 103108f90673Sjfb ixold[i] = ctold += skipline(f1); 103208f90673Sjfb continue; 103308f90673Sjfb } 103408f90673Sjfb while (j < J[i]) { 103508f90673Sjfb ixnew[j] = ctnew += skipline(f2); 103608f90673Sjfb j++; 103708f90673Sjfb } 103848dc77e6Sxsa if ((bflag == 1)|| (wflag == 1) || (iflag == 1)) { 103908f90673Sjfb for (;;) { 104008f90673Sjfb c = getc(f1); 104108f90673Sjfb d = getc(f2); 104208f90673Sjfb /* 104308f90673Sjfb * GNU diff ignores a missing newline 104408f90673Sjfb * in one file if bflag || wflag. 104508f90673Sjfb */ 104648dc77e6Sxsa if (((bflag == 1) || (wflag == 1)) && 104708f90673Sjfb ((c == EOF && d == '\n') || 104808f90673Sjfb (c == '\n' && d == EOF))) { 104908f90673Sjfb break; 105008f90673Sjfb } 105108f90673Sjfb ctold++; 105208f90673Sjfb ctnew++; 105348dc77e6Sxsa if ((bflag == 1) && isspace(c) && isspace(d)) { 105408f90673Sjfb do { 105508f90673Sjfb if (c == '\n') 105608f90673Sjfb break; 105708f90673Sjfb ctold++; 105808f90673Sjfb } while (isspace(c = getc(f1))); 105908f90673Sjfb do { 106008f90673Sjfb if (d == '\n') 106108f90673Sjfb break; 106208f90673Sjfb ctnew++; 106308f90673Sjfb } while (isspace(d = getc(f2))); 106448dc77e6Sxsa } else if (wflag == 1) { 106508f90673Sjfb while (isspace(c) && c != '\n') { 106608f90673Sjfb c = getc(f1); 106708f90673Sjfb ctold++; 106808f90673Sjfb } 106908f90673Sjfb while (isspace(d) && d != '\n') { 107008f90673Sjfb d = getc(f2); 107108f90673Sjfb ctnew++; 107208f90673Sjfb } 107308f90673Sjfb } 107408f90673Sjfb if (chrtran[c] != chrtran[d]) { 107508f90673Sjfb jackpot++; 107608f90673Sjfb J[i] = 0; 107737af5b8bSxsa if ((c != '\n') && (c != EOF)) 107808f90673Sjfb ctold += skipline(f1); 107937af5b8bSxsa if ((d != '\n') && (c != EOF)) 108008f90673Sjfb ctnew += skipline(f2); 108108f90673Sjfb break; 108208f90673Sjfb } 108337af5b8bSxsa if ((c == '\n') || (c == EOF)) 108408f90673Sjfb break; 108508f90673Sjfb } 108608f90673Sjfb } else { 108708f90673Sjfb for (;;) { 108808f90673Sjfb ctold++; 108908f90673Sjfb ctnew++; 109008f90673Sjfb if ((c = getc(f1)) != (d = getc(f2))) { 109108f90673Sjfb /* jackpot++; */ 109208f90673Sjfb J[i] = 0; 109337af5b8bSxsa if ((c != '\n') && (c != EOF)) 109408f90673Sjfb ctold += skipline(f1); 109537af5b8bSxsa if ((d != '\n') && (c != EOF)) 109608f90673Sjfb ctnew += skipline(f2); 109708f90673Sjfb break; 109808f90673Sjfb } 109937af5b8bSxsa if ((c == '\n') || (c == EOF)) 110008f90673Sjfb break; 110108f90673Sjfb } 110208f90673Sjfb } 110308f90673Sjfb ixold[i] = ctold; 110408f90673Sjfb ixnew[j] = ctnew; 110508f90673Sjfb j++; 110608f90673Sjfb } 1107e4276007Sjfb for (; j <= diff_len[1]; j++) 110808f90673Sjfb ixnew[j] = ctnew += skipline(f2); 110908f90673Sjfb /* 111037af5b8bSxsa * if (jackpot != 0) 11115ac8b1e7Sjoris * cvs_printf("jackpot\n"); 111208f90673Sjfb */ 111308f90673Sjfb } 111408f90673Sjfb 111508f90673Sjfb /* shellsort CACM #201 */ 111608f90673Sjfb static void 111708f90673Sjfb sort(struct line *a, int n) 111808f90673Sjfb { 111908f90673Sjfb struct line *ai, *aim, w; 112008f90673Sjfb int j, m = 0, k; 112108f90673Sjfb 112208f90673Sjfb if (n == 0) 112308f90673Sjfb return; 112408f90673Sjfb for (j = 1; j <= n; j *= 2) 112508f90673Sjfb m = 2 * j - 1; 112608f90673Sjfb for (m /= 2; m != 0; m /= 2) { 112708f90673Sjfb k = n - m; 112808f90673Sjfb for (j = 1; j <= k; j++) { 112908f90673Sjfb for (ai = &a[j]; ai > a; ai -= m) { 113008f90673Sjfb aim = &ai[m]; 113108f90673Sjfb if (aim < ai) 113208f90673Sjfb break; /* wraparound */ 113308f90673Sjfb if (aim->value > ai[0].value || 113408f90673Sjfb (aim->value == ai[0].value && 113508f90673Sjfb aim->serial > ai[0].serial)) 113608f90673Sjfb break; 113708f90673Sjfb w.value = ai[0].value; 113808f90673Sjfb ai[0].value = aim->value; 113908f90673Sjfb aim->value = w.value; 114008f90673Sjfb w.serial = ai[0].serial; 114108f90673Sjfb ai[0].serial = aim->serial; 114208f90673Sjfb aim->serial = w.serial; 114308f90673Sjfb } 114408f90673Sjfb } 114508f90673Sjfb } 114608f90673Sjfb } 114708f90673Sjfb 114808f90673Sjfb static void 114908f90673Sjfb unsort(struct line *f, int l, int *b) 115008f90673Sjfb { 115108f90673Sjfb int *a, i; 115208f90673Sjfb 11530450b43bSjoris a = (int *)xmalloc((l + 1) * sizeof(int)); 115408f90673Sjfb for (i = 1; i <= l; i++) 115508f90673Sjfb a[f[i].serial] = f[i].value; 115608f90673Sjfb for (i = 1; i <= l; i++) 115708f90673Sjfb b[i] = a[i]; 11580450b43bSjoris xfree(a); 115908f90673Sjfb } 116008f90673Sjfb 116108f90673Sjfb static int 116208f90673Sjfb skipline(FILE *f) 116308f90673Sjfb { 116408f90673Sjfb int i, c; 116508f90673Sjfb 116608f90673Sjfb for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++) 116708f90673Sjfb continue; 116808f90673Sjfb return (i); 116908f90673Sjfb } 117008f90673Sjfb 117108f90673Sjfb static void 117208f90673Sjfb output(const char *file1, FILE *f1, const char *file2, FILE *f2) 117308f90673Sjfb { 117408f90673Sjfb int m, i0, i1, j0, j1; 117508f90673Sjfb 117608f90673Sjfb rewind(f1); 117708f90673Sjfb rewind(f2); 1178e4276007Sjfb m = diff_len[0]; 117908f90673Sjfb J[0] = 0; 1180e4276007Sjfb J[m + 1] = diff_len[1] + 1; 118108f90673Sjfb for (i0 = 1; i0 <= m; i0 = i1 + 1) { 118208f90673Sjfb while (i0 <= m && J[i0] == J[i0 - 1] + 1) 118308f90673Sjfb i0++; 118408f90673Sjfb j0 = J[i0 - 1] + 1; 118508f90673Sjfb i1 = i0 - 1; 118608f90673Sjfb while (i1 < m && J[i1 + 1] == 0) 118708f90673Sjfb i1++; 118808f90673Sjfb j1 = J[i1 + 1] - 1; 118908f90673Sjfb J[i1] = j1; 119008f90673Sjfb change(file1, f1, file2, f2, i0, i1, j0, j1); 119108f90673Sjfb } 119208f90673Sjfb if (m == 0) 1193e4276007Sjfb change(file1, f1, file2, f2, 1, 0, 1, diff_len[1]); 1194f9b67873Sniallo if (diff_format == D_IFDEF) { 119508f90673Sjfb for (;;) { 119608f90673Sjfb #define c i0 119708f90673Sjfb if ((c = getc(f1)) == EOF) 119808f90673Sjfb return; 1199f9b67873Sniallo diff_output("%c", c); 120008f90673Sjfb } 120108f90673Sjfb #undef c 120208f90673Sjfb } 120308f90673Sjfb if (anychange != 0) { 1204f9b67873Sniallo if (diff_format == D_CONTEXT) 120508f90673Sjfb dump_context_vec(f1, f2); 1206f9b67873Sniallo else if (diff_format == D_UNIFIED) 120708f90673Sjfb dump_unified_vec(f1, f2); 120808f90673Sjfb } 120908f90673Sjfb } 121008f90673Sjfb 121108f90673Sjfb static __inline void 121208f90673Sjfb range(int a, int b, char *separator) 121308f90673Sjfb { 1214f9b67873Sniallo diff_output("%d", a > b ? b : a); 121508f90673Sjfb if (a < b) 1216f9b67873Sniallo diff_output("%s%d", separator, b); 121708f90673Sjfb } 121808f90673Sjfb 121908f90673Sjfb static __inline void 122008f90673Sjfb uni_range(int a, int b) 122108f90673Sjfb { 122208f90673Sjfb if (a < b) 1223f9b67873Sniallo diff_output("%d,%d", a, b - a + 1); 122408f90673Sjfb else if (a == b) 1225f9b67873Sniallo diff_output("%d", b); 122608f90673Sjfb else 1227f9b67873Sniallo diff_output("%d,0", b); 122808f90673Sjfb } 122908f90673Sjfb 123008f90673Sjfb static char * 12312a0de57dSjfb preadline(int fd, size_t rlen, off_t off) 123208f90673Sjfb { 123308f90673Sjfb char *line; 123408f90673Sjfb ssize_t nr; 123508f90673Sjfb 12360450b43bSjoris line = xmalloc(rlen + 1); 12372a0de57dSjfb if ((nr = pread(fd, line, rlen, off)) < 0) { 123808f90673Sjfb cvs_log(LP_ERRNO, "preadline failed"); 123908f90673Sjfb return (NULL); 124008f90673Sjfb } 124108f90673Sjfb line[nr] = '\0'; 124208f90673Sjfb return (line); 124308f90673Sjfb } 124408f90673Sjfb 124508f90673Sjfb static int 124608f90673Sjfb ignoreline(char *line) 124708f90673Sjfb { 124808f90673Sjfb int ret; 124908f90673Sjfb 1250f1901a5aSxsa ret = regexec(&ignore_re, line, (size_t)0, NULL, 0); 12510450b43bSjoris xfree(line); 125208f90673Sjfb return (ret == 0); /* if it matched, it should be ignored. */ 125308f90673Sjfb } 125408f90673Sjfb 125508f90673Sjfb /* 125608f90673Sjfb * Indicate that there is a difference between lines a and b of the from file 125708f90673Sjfb * to get to lines c to d of the to file. If a is greater then b then there 125808f90673Sjfb * are no lines in the from file involved and this means that there were 125908f90673Sjfb * lines appended (beginning at b). If c is greater than d then there are 126008f90673Sjfb * lines missing from the to file. 126108f90673Sjfb */ 126208f90673Sjfb static void 126308f90673Sjfb change(const char *file1, FILE *f1, const char *file2, FILE *f2, 126408f90673Sjfb int a, int b, int c, int d) 126508f90673Sjfb { 126608f90673Sjfb static size_t max_context = 64; 126708f90673Sjfb int i; 126808f90673Sjfb 1269f9b67873Sniallo if (diff_format != D_IFDEF && a > b && c > d) 127008f90673Sjfb return; 127108f90673Sjfb if (ignore_pats != NULL) { 127208f90673Sjfb char *line; 127308f90673Sjfb /* 127408f90673Sjfb * All lines in the change, insert, or delete must 127508f90673Sjfb * match an ignore pattern for the change to be 127608f90673Sjfb * ignored. 127708f90673Sjfb */ 127808f90673Sjfb if (a <= b) { /* Changes and deletes. */ 127908f90673Sjfb for (i = a; i <= b; i++) { 128008f90673Sjfb line = preadline(fileno(f1), 128108f90673Sjfb ixold[i] - ixold[i - 1], ixold[i - 1]); 128208f90673Sjfb if (!ignoreline(line)) 128308f90673Sjfb goto proceed; 128408f90673Sjfb } 128508f90673Sjfb } 128637af5b8bSxsa if ((a > b) || (c <= d)) { /* Changes and inserts. */ 128708f90673Sjfb for (i = c; i <= d; i++) { 128808f90673Sjfb line = preadline(fileno(f2), 128908f90673Sjfb ixnew[i] - ixnew[i - 1], ixnew[i - 1]); 129008f90673Sjfb if (!ignoreline(line)) 129108f90673Sjfb goto proceed; 129208f90673Sjfb } 129308f90673Sjfb } 129408f90673Sjfb return; 129508f90673Sjfb } 129608f90673Sjfb proceed: 1297f9b67873Sniallo if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) { 129808f90673Sjfb /* 129908f90673Sjfb * Allocate change records as needed. 130008f90673Sjfb */ 130108f90673Sjfb if (context_vec_ptr == context_vec_end - 1) { 130218501190Sniallo struct context_vec *tmp; 130308f90673Sjfb ptrdiff_t offset = context_vec_ptr - context_vec_start; 130408f90673Sjfb max_context <<= 1; 13050450b43bSjoris tmp = xrealloc(context_vec_start, max_context * 13060450b43bSjoris sizeof(struct context_vec)); 130718501190Sniallo context_vec_start = tmp; 130808f90673Sjfb context_vec_end = context_vec_start + max_context; 130908f90673Sjfb context_vec_ptr = context_vec_start + offset; 131008f90673Sjfb } 131108f90673Sjfb if (anychange == 0) { 131208f90673Sjfb /* 131308f90673Sjfb * Print the context/unidiff header first time through. 131408f90673Sjfb */ 1315f9b67873Sniallo diff_output("%s %s %s", 1316f9b67873Sniallo diff_format == D_CONTEXT ? "***" : "---", diff_file, 131708f90673Sjfb ctime(&stb1.st_mtime)); 1318f9b67873Sniallo diff_output("%s %s %s", 1319f9b67873Sniallo diff_format == D_CONTEXT ? "---" : "+++", diff_file, 132008f90673Sjfb ctime(&stb2.st_mtime)); 132108f90673Sjfb anychange = 1; 132208f90673Sjfb } else if (a > context_vec_ptr->b + (2 * context) + 1 && 132308f90673Sjfb c > context_vec_ptr->d + (2 * context) + 1) { 132408f90673Sjfb /* 132508f90673Sjfb * If this change is more than 'context' lines from the 132608f90673Sjfb * previous change, dump the record and reset it. 132708f90673Sjfb */ 1328f9b67873Sniallo if (diff_format == D_CONTEXT) 132908f90673Sjfb dump_context_vec(f1, f2); 133008f90673Sjfb else 133108f90673Sjfb dump_unified_vec(f1, f2); 133208f90673Sjfb } 133308f90673Sjfb context_vec_ptr++; 133408f90673Sjfb context_vec_ptr->a = a; 133508f90673Sjfb context_vec_ptr->b = b; 133608f90673Sjfb context_vec_ptr->c = c; 133708f90673Sjfb context_vec_ptr->d = d; 133808f90673Sjfb return; 133908f90673Sjfb } 134008f90673Sjfb if (anychange == 0) 134108f90673Sjfb anychange = 1; 1342f9b67873Sniallo switch (diff_format) { 134308f90673Sjfb case D_BRIEF: 134408f90673Sjfb return; 134508f90673Sjfb case D_NORMAL: 134608f90673Sjfb range(a, b, ","); 1347f9b67873Sniallo diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c'); 1348f9b67873Sniallo if (diff_format == D_NORMAL) 134908f90673Sjfb range(c, d, ","); 1350f9b67873Sniallo diff_output("\n"); 135108f90673Sjfb break; 1352394180a4Sjfb case D_RCSDIFF: 1353394180a4Sjfb if (a > b) 1354f9b67873Sniallo diff_output("a%d %d\n", b, d - c + 1); 1355394180a4Sjfb else { 1356f9b67873Sniallo diff_output("d%d %d\n", a, b - a + 1); 1357394180a4Sjfb 1358394180a4Sjfb if (!(c > d)) /* add changed lines */ 1359f9b67873Sniallo diff_output("a%d %d\n", b, d - c + 1); 1360394180a4Sjfb } 1361394180a4Sjfb break; 136208f90673Sjfb } 1363f9b67873Sniallo if (diff_format == D_NORMAL || diff_format == D_IFDEF) { 136408f90673Sjfb fetch(ixold, a, b, f1, '<', 1); 1365f9b67873Sniallo if (a <= b && c <= d && diff_format == D_NORMAL) 1366206543eeSjoris diff_output("---\n"); 136708f90673Sjfb } 1368f9b67873Sniallo i = fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0); 136908f90673Sjfb if (inifdef) { 1370f9b67873Sniallo diff_output("#endif /* %s */\n", ifdefname); 137108f90673Sjfb inifdef = 0; 137208f90673Sjfb } 137308f90673Sjfb } 137408f90673Sjfb 137508f90673Sjfb static int 137608f90673Sjfb fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile) 137708f90673Sjfb { 137808f90673Sjfb int i, j, c, lastc, col, nc; 137908f90673Sjfb 138008f90673Sjfb /* 138108f90673Sjfb * When doing #ifdef's, copy down to current line 138208f90673Sjfb * if this is the first file, so that stuff makes it to output. 138308f90673Sjfb */ 1384f9b67873Sniallo if (diff_format == D_IFDEF && oldfile) { 138508f90673Sjfb long curpos = ftell(lb); 138608f90673Sjfb /* print through if append (a>b), else to (nb: 0 vs 1 orig) */ 138708f90673Sjfb nc = f[a > b ? b : a - 1] - curpos; 138808f90673Sjfb for (i = 0; i < nc; i++) 1389f9b67873Sniallo diff_output("%c", getc(lb)); 139008f90673Sjfb } 139108f90673Sjfb if (a > b) 139208f90673Sjfb return (0); 1393f9b67873Sniallo if (diff_format == D_IFDEF) { 139408f90673Sjfb if (inifdef) { 1395f9b67873Sniallo diff_output("#else /* %s%s */\n", 139608f90673Sjfb oldfile == 1 ? "!" : "", ifdefname); 139708f90673Sjfb } else { 139808f90673Sjfb if (oldfile) 1399f9b67873Sniallo diff_output("#ifndef %s\n", ifdefname); 140008f90673Sjfb else 1401f9b67873Sniallo diff_output("#ifdef %s\n", ifdefname); 140208f90673Sjfb } 140308f90673Sjfb inifdef = 1 + oldfile; 140408f90673Sjfb } 140508f90673Sjfb for (i = a; i <= b; i++) { 140608f90673Sjfb fseek(lb, f[i - 1], SEEK_SET); 140708f90673Sjfb nc = f[i] - f[i - 1]; 1408f9b67873Sniallo if (diff_format != D_IFDEF && ch != '\0') { 1409f9b67873Sniallo diff_output("%c", ch); 141048dc77e6Sxsa if ((Tflag == 1 ) && (diff_format == D_NORMAL || 14119c5161e4Sjoris diff_format == D_CONTEXT || 14129c5161e4Sjoris diff_format == D_UNIFIED)) 1413f9b67873Sniallo diff_output("\t"); 1414f9b67873Sniallo else if (diff_format != D_UNIFIED) 1415f9b67873Sniallo diff_output(" "); 141608f90673Sjfb } 141708f90673Sjfb col = 0; 141808f90673Sjfb for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) { 141908f90673Sjfb if ((c = getc(lb)) == EOF) { 1420f9b67873Sniallo if (diff_format == D_RCSDIFF) 1421b9a6f00eSxsa cvs_log(LP_WARN, 1422b9a6f00eSxsa "No newline at end of file"); 1423394180a4Sjfb else 14249c5161e4Sjoris diff_output("\n\\ No newline at end of " 14259c5161e4Sjoris "file"); 142608f90673Sjfb return (0); 142708f90673Sjfb } 142848dc77e6Sxsa if ((c == '\t') && (tflag == 1)) { 142908f90673Sjfb do { 1430f9b67873Sniallo diff_output(" "); 143108f90673Sjfb } while (++col & 7); 143208f90673Sjfb } else { 1433f9b67873Sniallo diff_output("%c", c); 143408f90673Sjfb col++; 143508f90673Sjfb } 143608f90673Sjfb } 143708f90673Sjfb } 143808f90673Sjfb return (0); 143908f90673Sjfb } 144008f90673Sjfb 144108f90673Sjfb /* 144208f90673Sjfb * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578. 144308f90673Sjfb */ 144408f90673Sjfb static int 144508f90673Sjfb readhash(FILE *f) 144608f90673Sjfb { 144708f90673Sjfb int i, t, space; 144808f90673Sjfb int sum; 144908f90673Sjfb 145008f90673Sjfb sum = 1; 145108f90673Sjfb space = 0; 145248dc77e6Sxsa if ((bflag != 1) && (wflag != 1)) { 145348dc77e6Sxsa if (iflag == 1) 145408f90673Sjfb for (i = 0; (t = getc(f)) != '\n'; i++) { 145508f90673Sjfb if (t == EOF) { 145608f90673Sjfb if (i == 0) 145708f90673Sjfb return (0); 145808f90673Sjfb break; 145908f90673Sjfb } 146008f90673Sjfb sum = sum * 127 + chrtran[t]; 146108f90673Sjfb } 146208f90673Sjfb else 146308f90673Sjfb for (i = 0; (t = getc(f)) != '\n'; i++) { 146408f90673Sjfb if (t == EOF) { 146508f90673Sjfb if (i == 0) 146608f90673Sjfb return (0); 146708f90673Sjfb break; 146808f90673Sjfb } 146908f90673Sjfb sum = sum * 127 + t; 147008f90673Sjfb } 147108f90673Sjfb } else { 147208f90673Sjfb for (i = 0;;) { 147308f90673Sjfb switch (t = getc(f)) { 147408f90673Sjfb case '\t': 147508f90673Sjfb case ' ': 147608f90673Sjfb space++; 147708f90673Sjfb continue; 147808f90673Sjfb default: 147948dc77e6Sxsa if ((space != 0) && (wflag != 1)) { 148008f90673Sjfb i++; 148108f90673Sjfb space = 0; 148208f90673Sjfb } 148308f90673Sjfb sum = sum * 127 + chrtran[t]; 148408f90673Sjfb i++; 148508f90673Sjfb continue; 148608f90673Sjfb case EOF: 148708f90673Sjfb if (i == 0) 148808f90673Sjfb return (0); 148908f90673Sjfb /* FALLTHROUGH */ 149008f90673Sjfb case '\n': 149108f90673Sjfb break; 149208f90673Sjfb } 149308f90673Sjfb break; 149408f90673Sjfb } 149508f90673Sjfb } 149608f90673Sjfb /* 149708f90673Sjfb * There is a remote possibility that we end up with a zero sum. 149808f90673Sjfb * Zero is used as an EOF marker, so return 1 instead. 149908f90673Sjfb */ 150008f90673Sjfb return (sum == 0 ? 1 : sum); 150108f90673Sjfb } 150208f90673Sjfb 150308f90673Sjfb static int 150408f90673Sjfb asciifile(FILE *f) 150508f90673Sjfb { 150608f90673Sjfb char buf[BUFSIZ]; 150708f90673Sjfb int i, cnt; 150808f90673Sjfb 150948dc77e6Sxsa if ((aflag == 1) || (f == NULL)) 151008f90673Sjfb return (1); 151108f90673Sjfb 151208f90673Sjfb rewind(f); 1513f1901a5aSxsa cnt = fread(buf, (size_t)1, sizeof(buf), f); 151408f90673Sjfb for (i = 0; i < cnt; i++) 151508f90673Sjfb if (!isprint(buf[i]) && !isspace(buf[i])) 151608f90673Sjfb return (0); 151708f90673Sjfb return (1); 151808f90673Sjfb } 151908f90673Sjfb 15205e78344dSjfb static char* 15215e78344dSjfb match_function(const long *f, int pos, FILE *fp) 15225e78344dSjfb { 15235e78344dSjfb unsigned char buf[FUNCTION_CONTEXT_SIZE]; 15245e78344dSjfb size_t nc; 15255e78344dSjfb int last = lastline; 15265e78344dSjfb char *p; 15275e78344dSjfb 15285e78344dSjfb lastline = pos; 15295e78344dSjfb while (pos > last) { 15305e78344dSjfb fseek(fp, f[pos - 1], SEEK_SET); 15315e78344dSjfb nc = f[pos] - f[pos - 1]; 15325e78344dSjfb if (nc >= sizeof(buf)) 15335e78344dSjfb nc = sizeof(buf) - 1; 1534f1901a5aSxsa nc = fread(buf, (size_t)1, nc, fp); 15355e78344dSjfb if (nc > 0) { 15365e78344dSjfb buf[nc] = '\0'; 1537634926d6Sniallo p = strchr((const char *)buf, '\n'); 15385e78344dSjfb if (p != NULL) 15395e78344dSjfb *p = '\0'; 15405e78344dSjfb if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') { 15414c06e5f6Sreyk strlcpy(lastbuf, (const char *)buf, 15424c06e5f6Sreyk sizeof lastbuf); 15435e78344dSjfb lastmatchline = pos; 15445e78344dSjfb return lastbuf; 15455e78344dSjfb } 15465e78344dSjfb } 15475e78344dSjfb pos--; 15485e78344dSjfb } 15495e78344dSjfb return (lastmatchline > 0) ? lastbuf : NULL; 15505e78344dSjfb } 15515e78344dSjfb 155208f90673Sjfb 155308f90673Sjfb /* dump accumulated "context" diff changes */ 155408f90673Sjfb static void 155508f90673Sjfb dump_context_vec(FILE *f1, FILE *f2) 155608f90673Sjfb { 155708f90673Sjfb struct context_vec *cvp = context_vec_start; 155808f90673Sjfb int lowa, upb, lowc, upd, do_output; 155908f90673Sjfb int a, b, c, d; 15605e78344dSjfb char ch, *f; 156108f90673Sjfb 156208f90673Sjfb if (context_vec_start > context_vec_ptr) 156308f90673Sjfb return; 156408f90673Sjfb 156508f90673Sjfb b = d = 0; /* gcc */ 1566dc6a6879Sjfb lowa = MAX(1, cvp->a - context); 1567e4276007Sjfb upb = MIN(diff_len[0], context_vec_ptr->b + context); 1568dc6a6879Sjfb lowc = MAX(1, cvp->c - context); 1569e4276007Sjfb upd = MIN(diff_len[1], context_vec_ptr->d + context); 157008f90673Sjfb 1571f9b67873Sniallo diff_output("***************"); 157248dc77e6Sxsa if (pflag == 1) { 15735e78344dSjfb f = match_function(ixold, lowa - 1, f1); 15745e78344dSjfb if (f != NULL) { 1575f9b67873Sniallo diff_output(" "); 1576f9b67873Sniallo diff_output("%s", f); 15775e78344dSjfb } 15785e78344dSjfb } 1579f9b67873Sniallo diff_output("\n*** "); 158008f90673Sjfb range(lowa, upb, ","); 1581f9b67873Sniallo diff_output(" ****\n"); 158208f90673Sjfb 158308f90673Sjfb /* 158408f90673Sjfb * Output changes to the "old" file. The first loop suppresses 158508f90673Sjfb * output if there were no changes to the "old" file (we'll see 158608f90673Sjfb * the "old" lines as context in the "new" list). 158708f90673Sjfb */ 158808f90673Sjfb do_output = 0; 158908f90673Sjfb for (; cvp <= context_vec_ptr; cvp++) 159008f90673Sjfb if (cvp->a <= cvp->b) { 159108f90673Sjfb cvp = context_vec_start; 159208f90673Sjfb do_output++; 159308f90673Sjfb break; 159408f90673Sjfb } 159537af5b8bSxsa if (do_output != 0) { 159608f90673Sjfb while (cvp <= context_vec_ptr) { 159708f90673Sjfb a = cvp->a; 159808f90673Sjfb b = cvp->b; 159908f90673Sjfb c = cvp->c; 160008f90673Sjfb d = cvp->d; 160108f90673Sjfb 160208f90673Sjfb if (a <= b && c <= d) 160308f90673Sjfb ch = 'c'; 160408f90673Sjfb else 160508f90673Sjfb ch = (a <= b) ? 'd' : 'a'; 160608f90673Sjfb 160708f90673Sjfb if (ch == 'a') 160808f90673Sjfb fetch(ixold, lowa, b, f1, ' ', 0); 160908f90673Sjfb else { 161008f90673Sjfb fetch(ixold, lowa, a - 1, f1, ' ', 0); 161108f90673Sjfb fetch(ixold, a, b, f1, 161208f90673Sjfb ch == 'c' ? '!' : '-', 0); 161308f90673Sjfb } 161408f90673Sjfb lowa = b + 1; 161508f90673Sjfb cvp++; 161608f90673Sjfb } 161708f90673Sjfb fetch(ixold, b + 1, upb, f1, ' ', 0); 161808f90673Sjfb } 161908f90673Sjfb /* output changes to the "new" file */ 1620f9b67873Sniallo diff_output("--- "); 162108f90673Sjfb range(lowc, upd, ","); 1622f9b67873Sniallo diff_output(" ----\n"); 162308f90673Sjfb 162408f90673Sjfb do_output = 0; 162508f90673Sjfb for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++) 162608f90673Sjfb if (cvp->c <= cvp->d) { 162708f90673Sjfb cvp = context_vec_start; 162808f90673Sjfb do_output++; 162908f90673Sjfb break; 163008f90673Sjfb } 163137af5b8bSxsa if (do_output != 0) { 163208f90673Sjfb while (cvp <= context_vec_ptr) { 163308f90673Sjfb a = cvp->a; 163408f90673Sjfb b = cvp->b; 163508f90673Sjfb c = cvp->c; 163608f90673Sjfb d = cvp->d; 163708f90673Sjfb 163808f90673Sjfb if (a <= b && c <= d) 163908f90673Sjfb ch = 'c'; 164008f90673Sjfb else 164108f90673Sjfb ch = (a <= b) ? 'd' : 'a'; 164208f90673Sjfb 164308f90673Sjfb if (ch == 'd') 164408f90673Sjfb fetch(ixnew, lowc, d, f2, ' ', 0); 164508f90673Sjfb else { 164608f90673Sjfb fetch(ixnew, lowc, c - 1, f2, ' ', 0); 164708f90673Sjfb fetch(ixnew, c, d, f2, 164808f90673Sjfb ch == 'c' ? '!' : '+', 0); 164908f90673Sjfb } 165008f90673Sjfb lowc = d + 1; 165108f90673Sjfb cvp++; 165208f90673Sjfb } 165308f90673Sjfb fetch(ixnew, d + 1, upd, f2, ' ', 0); 165408f90673Sjfb } 165508f90673Sjfb context_vec_ptr = context_vec_start - 1; 165608f90673Sjfb } 165708f90673Sjfb 165808f90673Sjfb /* dump accumulated "unified" diff changes */ 165908f90673Sjfb static void 166008f90673Sjfb dump_unified_vec(FILE *f1, FILE *f2) 166108f90673Sjfb { 166208f90673Sjfb struct context_vec *cvp = context_vec_start; 166308f90673Sjfb int lowa, upb, lowc, upd; 166408f90673Sjfb int a, b, c, d; 16655e78344dSjfb char ch, *f; 166608f90673Sjfb 166708f90673Sjfb if (context_vec_start > context_vec_ptr) 166808f90673Sjfb return; 166908f90673Sjfb 167008f90673Sjfb b = d = 0; /* gcc */ 1671dc6a6879Sjfb lowa = MAX(1, cvp->a - context); 1672e4276007Sjfb upb = MIN(diff_len[0], context_vec_ptr->b + context); 1673dc6a6879Sjfb lowc = MAX(1, cvp->c - context); 1674e4276007Sjfb upd = MIN(diff_len[1], context_vec_ptr->d + context); 167508f90673Sjfb 1676f9b67873Sniallo diff_output("@@ -"); 167708f90673Sjfb uni_range(lowa, upb); 1678f9b67873Sniallo diff_output(" +"); 167908f90673Sjfb uni_range(lowc, upd); 1680f9b67873Sniallo diff_output(" @@"); 168148dc77e6Sxsa if (pflag == 1) { 16825e78344dSjfb f = match_function(ixold, lowa - 1, f1); 16835e78344dSjfb if (f != NULL) { 1684f9b67873Sniallo diff_output(" "); 1685f9b67873Sniallo diff_output("%s", f); 16865e78344dSjfb } 16875e78344dSjfb } 1688f9b67873Sniallo diff_output("\n"); 168908f90673Sjfb 169008f90673Sjfb /* 169108f90673Sjfb * Output changes in "unified" diff format--the old and new lines 169208f90673Sjfb * are printed together. 169308f90673Sjfb */ 169408f90673Sjfb for (; cvp <= context_vec_ptr; cvp++) { 169508f90673Sjfb a = cvp->a; 169608f90673Sjfb b = cvp->b; 169708f90673Sjfb c = cvp->c; 169808f90673Sjfb d = cvp->d; 169908f90673Sjfb 170008f90673Sjfb /* 170108f90673Sjfb * c: both new and old changes 170208f90673Sjfb * d: only changes in the old file 170308f90673Sjfb * a: only changes in the new file 170408f90673Sjfb */ 170508f90673Sjfb if (a <= b && c <= d) 170608f90673Sjfb ch = 'c'; 170708f90673Sjfb else 170808f90673Sjfb ch = (a <= b) ? 'd' : 'a'; 170908f90673Sjfb 171008f90673Sjfb switch (ch) { 171108f90673Sjfb case 'c': 171208f90673Sjfb fetch(ixold, lowa, a - 1, f1, ' ', 0); 171308f90673Sjfb fetch(ixold, a, b, f1, '-', 0); 171408f90673Sjfb fetch(ixnew, c, d, f2, '+', 0); 171508f90673Sjfb break; 171608f90673Sjfb case 'd': 171708f90673Sjfb fetch(ixold, lowa, a - 1, f1, ' ', 0); 171808f90673Sjfb fetch(ixold, a, b, f1, '-', 0); 171908f90673Sjfb break; 172008f90673Sjfb case 'a': 172108f90673Sjfb fetch(ixnew, lowc, c - 1, f2, ' ', 0); 172208f90673Sjfb fetch(ixnew, c, d, f2, '+', 0); 172308f90673Sjfb break; 172408f90673Sjfb } 172508f90673Sjfb lowa = b + 1; 172608f90673Sjfb lowc = d + 1; 172708f90673Sjfb } 172808f90673Sjfb fetch(ixnew, d + 1, upd, f2, ' ', 0); 172908f90673Sjfb 173008f90673Sjfb context_vec_ptr = context_vec_start - 1; 173108f90673Sjfb } 1732f9b67873Sniallo 173301af718aSjoris void 1734f9b67873Sniallo diff_output(const char *fmt, ...) 1735f9b67873Sniallo { 1736f9b67873Sniallo va_list vap; 1737f9b67873Sniallo char *str; 1738f9b67873Sniallo 1739f9b67873Sniallo va_start(vap, fmt); 1740f9b67873Sniallo vasprintf(&str, fmt, vap); 1741f9b67873Sniallo if (diffbuf != NULL) 1742f9b67873Sniallo cvs_buf_append(diffbuf, str, strlen(str)); 1743f9b67873Sniallo else 1744f9b67873Sniallo cvs_printf("%s", str); 17450450b43bSjoris xfree(str); 1746f9b67873Sniallo va_end(vap); 1747f9b67873Sniallo } 1748