version 1.90, 2006/04/14 23:29:01 |
version 1.91, 2006/05/27 03:30:30 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* |
/* |
* Copyright (C) Caldera International Inc. 2001-2002. |
* Copyright (c) 2006 Joris Vink <joris@openbsd.org> |
* All rights reserved. |
|
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Permission to use, copy, modify, and distribute this software for any |
* modification, are permitted provided that the following conditions |
* purpose with or without fee is hereby granted, provided that the above |
* are met: |
* copyright notice and this permission notice appear in all copies. |
* 1. Redistributions of source code and documentation must retain the above |
|
* copyright notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* 3. All advertising materials mentioning features or use of this software |
|
* must display the following acknowledgement: |
|
* This product includes software developed or owned by Caldera |
|
* International, Inc. |
|
* 4. Neither the name of Caldera International, Inc. nor the names of other |
|
* contributors may be used to endorse or promote products derived from |
|
* this software without specific prior written permission. |
|
* |
* |
* USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
* INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
* IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
* INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
* POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
*/ |
/*- |
|
* Copyright (c) 1991, 1993 |
|
* The Regents of the University of California. All rights reserved. |
|
* Copyright (c) 2004 Jean-Francois Brousseau. All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* 3. Neither the name of the University nor the names of its contributors |
|
* may be used to endorse or promote products derived from this software |
|
* without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
* SUCH DAMAGE. |
|
* |
|
* @(#)diffreg.c 8.1 (Berkeley) 6/6/93 |
|
*/ |
|
/* |
|
* Uses an algorithm due to Harold Stone, which finds |
|
* a pair of longest identical subsequences in the two |
|
* files. |
|
* |
|
* The major goal is to generate the match vector J. |
|
* J[i] is the index of the line in file1 corresponding |
|
* to line i file0. J[i] = 0 if there is no |
|
* such line in file1. |
|
* |
|
* Lines are hashed so as to work in core. All potential |
|
* matches are located by sorting the lines of each file |
|
* on the hash (called ``value''). In particular, this |
|
* collects the equivalence classes in file1 together. |
|
* Subroutine equiv replaces the value of each line in |
|
* file0 by the index of the first element of its |
|
* matching equivalence in (the reordered) file1. |
|
* To save space equiv squeezes file1 into a single |
|
* array member in which the equivalence classes |
|
* are simply concatenated, except that their first |
|
* members are flagged by changing sign. |
|
* |
|
* Next the indices that point into member are unsorted into |
|
* array class according to the original order of file0. |
|
* |
|
* The cleverness lies in routine stone. This marches |
|
* through the lines of file0, developing a vector klist |
|
* of "k-candidates". At step i a k-candidate is a matched |
|
* pair of lines x,y (x in file0 y in file1) such that |
|
* there is a common subsequence of length k |
|
* between the first i lines of file0 and the first y |
|
* lines of file1, but there is no such subsequence for |
|
* any smaller y. x is the earliest possible mate to y |
|
* that occurs in such a subsequence. |
|
* |
|
* Whenever any of the members of the equivalence class of |
|
* lines in file1 matable to a line in file0 has serial number |
|
* less than the y of some k-candidate, that k-candidate |
|
* with the smallest such y is replaced. The new |
|
* k-candidate is chained (via pred) to the current |
|
* k-1 candidate so that the actual subsequence can |
|
* be recovered. When a member has serial number greater |
|
* that the y of all k-candidates, the klist is extended. |
|
* At the end, the longest subsequence is pulled out |
|
* and placed in the array J by unravel |
|
* |
|
* With J in hand, the matches there recorded are |
|
* check'ed against reality to assure that no spurious |
|
* matches have crept in due to hashing. If they have, |
|
* they are broken, and "jackpot" is recorded--a harmless |
|
* matter except that a true match for a spuriously |
|
* mated line may now be unnecessarily reported as a change. |
|
* |
|
* Much of the complexity of the program comes simply |
|
* from trying to minimize core utilization and |
|
* maximize the range of doable problems by dynamically |
|
* allocating what is needed and reusing what is not. |
|
* The core requirements for problems larger than somewhat |
|
* are (in words) 2*length(file0) + length(file1) + |
|
* 3*(number of k-candidates installed), typically about |
|
* 6n words for files of length n. |
|
*/ |
|
|
|
#include "includes.h" |
#include "includes.h" |
|
|
#include "buf.h" |
|
#include "cvs.h" |
#include "cvs.h" |
#include "diff.h" |
#include "diff.h" |
#include "log.h" |
#include "log.h" |
#include "proto.h" |
#include "proto.h" |
|
|
#include "xmalloc.h" |
int cvs_diff(int, char **); |
|
void cvs_diff_local(struct cvs_file *); |
|
|
struct cand { |
|
int x; |
|
int y; |
|
int pred; |
|
} cand; |
|
|
|
struct line { |
|
int serial; |
|
int value; |
|
} *file[2]; |
|
|
|
/* |
|
* The following struct is used to record change in formation when |
|
* doing a "context" or "unified" diff. (see routine "change" to |
|
* understand the highly mnemonic field names) |
|
*/ |
|
struct context_vec { |
|
int a; /* start line in old file */ |
|
int b; /* end line in old file */ |
|
int c; /* start line in new file */ |
|
int d; /* end line in new file */ |
|
}; |
|
|
|
struct diff_arg { |
|
char *rev1; |
|
char *rev2; |
|
char *date1; |
|
char *date2; |
|
}; |
|
|
|
#if !defined(RCSPROG) |
|
static int cvs_diff_init(struct cvs_cmd *, int, char **, int *); |
|
static int cvs_diff_remote(CVSFILE *, void *); |
|
static int cvs_diff_local(CVSFILE *, void *); |
|
static int cvs_diff_pre_exec(struct cvsroot *); |
|
static int cvs_diff_cleanup(void); |
|
#endif |
|
|
|
static void output(FILE *, FILE *); |
|
static void check(FILE *, FILE *); |
|
static void range(int, int, char *); |
|
static void uni_range(int, int); |
|
static void dump_context_vec(FILE *, FILE *); |
|
static void dump_unified_vec(FILE *, FILE *); |
|
static int prepare(int, FILE *, off_t); |
|
static void prune(void); |
|
static void equiv(struct line *, int, struct line *, int, int *); |
|
static void unravel(int); |
|
static void unsort(struct line *, int, int *); |
|
static void change(FILE *, FILE *, int, int, int, int); |
|
static void sort(struct line *, int); |
|
static int ignoreline(char *); |
|
static int asciifile(FILE *); |
|
static void fetch(long *, int, int, FILE *, int, int); |
|
static int newcand(int, int, int); |
|
static int search(int *, int, int); |
|
static int skipline(FILE *); |
|
static int isqrt(int); |
|
static int stone(int *, int, int *, int *); |
|
static int readhash(FILE *); |
|
static int files_differ(FILE *, FILE *); |
|
static char *match_function(const long *, int, FILE *); |
|
static char *preadline(int, size_t, off_t); |
|
|
|
|
|
#if !defined(RCSPROG) |
|
static int Nflag; |
|
#endif |
|
static int aflag, bflag, dflag, iflag, pflag, tflag, Tflag, wflag; |
|
static int context = 3; |
|
int diff_format = D_NORMAL; |
|
char *diff_file = NULL; |
|
RCSNUM *diff_rev1 = NULL; |
|
RCSNUM *diff_rev2 = NULL; |
|
char diffargs[128]; |
|
static struct stat stb1, stb2; |
|
static char *ifdefname, *ignore_pats; |
|
regex_t ignore_re; |
|
|
|
static int *J; /* will be overlaid on class */ |
|
static int *class; /* will be overlaid on file[0] */ |
|
static int *klist; /* will be overlaid on file[0] after class */ |
|
static int *member; /* will be overlaid on file[1] */ |
|
static int clen; |
|
static int inifdef; /* whether or not we are in a #ifdef block */ |
|
static int diff_len[2]; |
|
static int pref, suff; /* length of prefix and suffix */ |
|
static int slen[2]; |
|
static int anychange; |
|
static long *ixnew; /* will be overlaid on file[1] */ |
|
static long *ixold; /* will be overlaid on klist */ |
|
static struct cand *clist; /* merely a free storage pot for candidates */ |
|
static int clistlen; /* the length of clist */ |
|
static struct line *sfile[2]; /* shortened by pruning common prefix/suffix */ |
|
static u_char *chrtran; /* translation table for case-folding */ |
|
static struct context_vec *context_vec_start; |
|
static struct context_vec *context_vec_end; |
|
static struct context_vec *context_vec_ptr; |
|
|
|
#define FUNCTION_CONTEXT_SIZE 41 |
|
static char lastbuf[FUNCTION_CONTEXT_SIZE]; |
|
static int lastline; |
|
static int lastmatchline; |
|
BUF *diffbuf = NULL; |
|
|
|
/* |
|
* chrtran points to one of 2 translation tables: cup2low if folding upper to |
|
* lower case clow2low if not folding case |
|
*/ |
|
u_char clow2low[256] = { |
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, |
|
0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, |
|
0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, |
|
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, |
|
0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, |
|
0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, |
|
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, |
|
0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, |
|
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, |
|
0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, |
|
0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, |
|
0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, |
|
0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, |
|
0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, |
|
0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, |
|
0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, |
|
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, |
|
0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, |
|
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, |
|
0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, |
|
0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, |
|
0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, |
|
0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, |
|
0xfd, 0xfe, 0xff |
|
}; |
|
|
|
u_char cup2low[256] = { |
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, |
|
0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, |
|
0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, |
|
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, |
|
0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, |
|
0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61, |
|
0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, |
|
0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, |
|
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62, |
|
0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, |
|
0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, |
|
0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, |
|
0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, |
|
0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, |
|
0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, |
|
0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, |
|
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, |
|
0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, |
|
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, |
|
0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, |
|
0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, |
|
0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, |
|
0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, |
|
0xfd, 0xfe, 0xff |
|
}; |
|
|
|
#if !defined(RCSPROG) |
|
struct cvs_cmd cvs_cmd_diff = { |
struct cvs_cmd cvs_cmd_diff = { |
CVS_OP_DIFF, CVS_REQ_DIFF, "diff", |
CVS_OP_DIFF, CVS_REQ_DIFF, "diff", |
{ "di", "dif" }, |
{ "di", "dif" }, |
|
|
"[-k mode] [file ...]", |
"[-k mode] [file ...]", |
"cD:iklNnpr:Ru", |
"cD:iklNnpr:Ru", |
NULL, |
NULL, |
CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN, |
cvs_diff |
cvs_diff_init, |
|
cvs_diff_pre_exec, |
|
cvs_diff_remote, |
|
cvs_diff_local, |
|
NULL, |
|
cvs_diff_cleanup, |
|
CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR |
|
}; |
}; |
|
|
|
int |
struct cvs_cmd cvs_cmd_rdiff = { |
cvs_diff(int argc, char **argv) |
CVS_OP_RDIFF, CVS_REQ_DIFF, "rdiff", |
|
{ "pa", "patch" }, |
|
"Create 'patch' format diffs between releases", |
|
"[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev " |
|
"[-D date2 | -rev2] module ...", |
|
"cD:flRr:stuV:", |
|
NULL, |
|
CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN, |
|
cvs_diff_init, |
|
cvs_diff_pre_exec, |
|
cvs_diff_remote, |
|
cvs_diff_local, |
|
NULL, |
|
cvs_diff_cleanup, |
|
CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR |
|
}; |
|
#endif |
|
|
|
#if !defined(RCSPROG) |
|
static struct diff_arg *dap = NULL; |
|
|
|
static int |
|
cvs_diff_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) |
|
{ |
{ |
int ch; |
int ch; |
|
char *arg = "."; |
|
struct cvs_recursion cr; |
|
|
dap = xmalloc(sizeof(*dap)); |
|
dap->date1 = dap->date2 = dap->rev1 = dap->rev2 = NULL; |
|
strlcpy(diffargs, argv[0], sizeof(diffargs)); |
strlcpy(diffargs, argv[0], sizeof(diffargs)); |
|
|
while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { |
while ((ch = getopt(argc, argv, cvs_cmd_diff.cmd_opts)) != -1) { |
switch (ch) { |
switch (ch) { |
case 'c': |
case 'c': |
strlcat(diffargs, " -c", sizeof(diffargs)); |
strlcat(diffargs, " -c", sizeof(diffargs)); |
diff_format = D_CONTEXT; |
diff_format = D_CONTEXT; |
break; |
break; |
case 'D': |
|
if (dap->date1 == NULL && dap->rev1 == NULL) { |
|
dap->date1 = optarg; |
|
} else if (dap->date2 == NULL && dap->rev2 == NULL) { |
|
dap->date2 = optarg; |
|
} else { |
|
cvs_log(LP_ERR, |
|
"no more than two revisions/dates can " |
|
"be specified"); |
|
} |
|
break; |
|
case 'l': |
|
strlcat(diffargs, " -l", sizeof(diffargs)); |
|
cvs_cmd_diff.file_flags &= ~CF_RECURSE; |
|
break; |
|
case 'i': |
|
strlcat(diffargs, " -i", sizeof(diffargs)); |
|
iflag = 1; |
|
break; |
|
case 'N': |
|
strlcat(diffargs, " -N", sizeof(diffargs)); |
|
Nflag = 1; |
|
break; |
|
case 'n': |
case 'n': |
strlcat(diffargs, " -n", sizeof(diffargs)); |
strlcat(diffargs, " -n", sizeof(diffargs)); |
diff_format = D_RCSDIFF; |
diff_format = D_RCSDIFF; |
break; |
break; |
case 'p': |
|
strlcat(diffargs, " -p", sizeof(diffargs)); |
|
pflag = 1; |
|
break; |
|
case 'r': |
case 'r': |
if (dap->rev1 == NULL && dap->date1 == NULL) { |
if (diff_rev1 == NULL) { |
dap->rev1 = optarg; |
diff_rev1 = rcsnum_parse(optarg); |
} else if (dap->rev2 == NULL && |
if (diff_rev1 == NULL) |
dap->date2 == NULL) { |
fatal("rcsnum_parse failed"); |
dap->rev2 = optarg; |
} else if (diff_rev2 == NULL) { |
|
diff_rev2 = rcsnum_parse(optarg); |
|
if (diff_rev2 == NULL) |
|
fatal("rcsnum_parse failed"); |
} else { |
} else { |
cvs_log(LP_ERR, |
fatal("no more than 2 revisions/dates can" |
"no more than two revisions/dates can " |
" be specified"); |
"be specified"); |
|
return (CVS_EX_USAGE); |
|
} |
} |
break; |
break; |
case 'R': |
|
cvs_cmd_diff.file_flags |= CF_RECURSE; |
|
break; |
|
case 'u': |
case 'u': |
strlcat(diffargs, " -u", sizeof(diffargs)); |
strlcat(diffargs, " -u", sizeof(diffargs)); |
diff_format = D_UNIFIED; |
diff_format = D_UNIFIED; |
break; |
break; |
default: |
default: |
return (CVS_EX_USAGE); |
fatal("%s", cvs_cmd_diff.cmd_synopsis); |
} |
} |
} |
} |
|
|
*arg = optind; |
argc -= optind; |
return (0); |
argv += optind; |
} |
|
|
|
int |
cr.enterdir = NULL; |
cvs_diff_cleanup(void) |
cr.leavedir = NULL; |
{ |
cr.local = cvs_diff_local; |
if (dap != NULL) { |
cr.remote = NULL; |
xfree(dap); |
|
dap = NULL; |
|
} |
|
return (0); |
|
} |
|
|
|
/* |
if (argc > 0) |
* cvs_diff_pre_exec() |
cvs_file_run(argc, argv, &cr); |
* |
|
*/ |
|
int |
|
cvs_diff_pre_exec(struct cvsroot *root) |
|
{ |
|
if (root->cr_method != CVS_METHOD_LOCAL) { |
|
/* send the flags */ |
|
if (Nflag == 1) |
|
cvs_sendarg(root, "-N", 0); |
|
if (pflag == 1) |
|
cvs_sendarg(root, "-p", 0); |
|
|
|
if (diff_format == D_CONTEXT) |
|
cvs_sendarg(root, "-c", 0); |
|
else if (diff_format == D_UNIFIED) |
|
cvs_sendarg(root, "-u", 0); |
|
|
|
if (dap->rev1 != NULL) { |
|
cvs_sendarg(root, "-r", 0); |
|
cvs_sendarg(root, dap->rev1, 0); |
|
} else if (dap->date1 != NULL) { |
|
cvs_sendarg(root, "-D", 0); |
|
cvs_sendarg(root, dap->date1, 0); |
|
} |
|
if (dap->rev2 != NULL) { |
|
cvs_sendarg(root, "-r", 0); |
|
cvs_sendarg(root, dap->rev2, 0); |
|
} else if (dap->date2 != NULL) { |
|
cvs_sendarg(root, "-D", 0); |
|
cvs_sendarg(root, dap->date2, 0); |
|
} |
|
} |
|
|
|
return (0); |
|
} |
|
|
|
|
|
/* |
|
* cvs_diff_file() |
|
* |
|
* Diff a single file. |
|
*/ |
|
static int |
|
cvs_diff_remote(struct cvs_file *cfp, void *arg) |
|
{ |
|
char fpath[MAXPATHLEN]; |
|
struct cvsroot *root; |
|
|
|
if (cfp->cf_type == DT_DIR) { |
|
if (cfp->cf_cvstat == CVS_FST_UNKNOWN) { |
|
root = cfp->cf_parent->cf_root; |
|
cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name); |
|
} else { |
|
root = cfp->cf_root; |
|
#if 0 |
|
if (cfp->cf_parent == NULL || |
|
(root != cfp->cf_parent->cf_root)) { |
|
cvs_connect(root); |
|
cvs_diff_pre_exec(root); |
|
} |
|
#endif |
|
|
|
cvs_senddir(root, cfp); |
|
} |
|
|
|
return (0); |
|
} |
|
|
|
if (cfp->cf_cvstat == CVS_FST_LOST) { |
|
cvs_log(LP_WARN, "cannot find file %s", cfp->cf_name); |
|
return (0); |
|
} |
|
|
|
diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath)); |
|
|
|
if (cfp->cf_parent != NULL) |
|
root = cfp->cf_parent->cf_root; |
|
else |
else |
root = NULL; |
cvs_file_run(1, &arg, &cr); |
|
|
if (cfp->cf_cvstat == CVS_FST_UNKNOWN) { |
|
cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name); |
|
return (0); |
|
} |
|
|
|
cvs_sendentry(root, cfp); |
|
|
|
if (cfp->cf_cvstat == CVS_FST_UPTODATE) { |
|
cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name); |
|
return (0); |
|
} |
|
|
|
/* at this point, the file is modified */ |
|
cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name); |
|
cvs_sendfile(root, diff_file); |
|
|
|
return (0); |
return (0); |
} |
} |
|
|
static int |
void |
cvs_diff_local(CVSFILE *cf, void *arg) |
cvs_diff_local(struct cvs_file *cf) |
{ |
{ |
char buf[64]; |
size_t len; |
char fpath[MAXPATHLEN], rcspath[MAXPATHLEN]; |
RCSNUM *r1; |
char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN]; |
|
BUF *b1, *b2; |
BUF *b1, *b2; |
RCSNUM *r1, *r2; |
struct stat st; |
RCSFILE *rf; |
|
struct timeval tv[2], tv2[2]; |
struct timeval tv[2], tv2[2]; |
|
char rbuf[16], p1[MAXPATHLEN], p2[MAXPATHLEN]; |
|
|
memset(&tv, 0, sizeof(tv)); |
cvs_log(LP_TRACE, "cvs_diff_local(%s)", cf->file_path); |
memset(&tv2, 0, sizeof(tv2)); |
|
|
|
rf = NULL; |
if (cf->file_type == CVS_DIR) { |
diff_file = cvs_file_getpath(cf, fpath, sizeof(fpath)); |
|
|
|
if (cf->cf_type == DT_DIR) { |
|
if (verbosity > 1) |
if (verbosity > 1) |
cvs_log(LP_NOTICE, "Diffing %s", fpath); |
cvs_log(LP_NOTICE, "Diffing inside %s", cf->file_path); |
return (0); |
|
} |
|
|
|
if (cf->cf_cvstat == CVS_FST_LOST) { |
|
cvs_log(LP_WARN, "cannot find file %s", cf->cf_name); |
|
return (0); |
|
} |
|
|
|
if (cf->cf_cvstat == CVS_FST_UNKNOWN) { |
|
cvs_log(LP_WARN, "I know nothing about %s", diff_file); |
|
return (0); |
|
} else if (cf->cf_cvstat == CVS_FST_UPTODATE) |
|
return (0); |
|
|
|
/* at this point, the file is modified */ |
|
cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); |
|
|
|
if ((rf = rcs_open(rcspath, RCS_READ)) == NULL) |
|
fatal("cvs_diff_local: rcs_open `%s': %s", rcspath, |
|
rcs_errstr(rcs_errno)); |
|
|
|
cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file, |
|
RCS_DIFF_DIV, rcspath); |
|
|
|
if (dap->rev1 == NULL) |
|
r1 = cf->cf_lrev; |
|
else { |
|
if ((r1 = rcsnum_parse(dap->rev1)) == NULL) |
|
fatal("cvs_diff_local: rcsnum_parse failed"); |
|
} |
|
|
|
cvs_printf("retrieving revision %s\n", |
|
rcsnum_tostr(r1, buf, sizeof(buf))); |
|
b1 = rcs_getrev(rf, r1); |
|
|
|
if (b1 == NULL) { |
|
cvs_log(LP_ERR, "failed to retrieve revision %s", |
|
rcsnum_tostr(r1, buf, sizeof(buf))); |
|
if (r1 != cf->cf_lrev) |
|
rcsnum_free(r1); |
|
rcs_close(rf); |
|
return (CVS_EX_DATA); |
|
} |
|
tv[0].tv_sec = (long)rcs_rev_getdate(rf, r1); |
|
tv[1].tv_sec = tv[0].tv_sec; |
|
|
|
if (r1 != cf->cf_lrev) |
|
rcsnum_free(r1); |
|
|
|
if (dap->rev2 != NULL) { |
|
cvs_printf("retrieving revision %s\n", dap->rev2); |
|
if ((r2 = rcsnum_parse(dap->rev2)) == NULL) { |
|
rcs_close(rf); |
|
return (CVS_EX_DATA); |
|
} |
|
b2 = rcs_getrev(rf, r2); |
|
tv2[0].tv_sec = (long)rcs_rev_getdate(rf, r2); |
|
tv2[1].tv_sec = tv2[0].tv_sec; |
|
rcsnum_free(r2); |
|
} else { |
|
struct stat st; |
|
if (stat(diff_file, &st) < 0) { |
|
cvs_log(LP_ERR, "failed to retrieve revision %s", |
|
dap->rev2); |
|
cvs_buf_free(b1); |
|
return (CVS_EX_DATA); |
|
} |
|
b2 = cvs_buf_load(diff_file, BUF_AUTOEXT); |
|
tv2[0].tv_sec = st.st_mtime; |
|
tv2[1].tv_sec = st.st_mtime; |
|
} |
|
|
|
rcs_close(rf); |
|
|
|
if (b2 == NULL) { |
|
cvs_log(LP_ERR, "failed to retrieve revision %s", |
|
dap->rev2); |
|
cvs_buf_free(b1); |
|
return (CVS_EX_DATA); |
|
} |
|
|
|
cvs_printf("%s", diffargs); |
|
cvs_printf(" -r%s", buf); |
|
if (dap->rev2 != NULL) |
|
cvs_printf(" -r%s", dap->rev2); |
|
cvs_printf(" %s\n", diff_file); |
|
strlcpy(path_tmp1, cvs_tmpdir, sizeof(path_tmp1)); |
|
strlcat(path_tmp1, "/diff1.XXXXXXXXXX", sizeof(path_tmp1)); |
|
cvs_buf_write_stmp(b1, path_tmp1, 0600); |
|
cvs_buf_free(b1); |
|
if (utimes(path_tmp1, (const struct timeval *)&tv) < 0) |
|
cvs_log(LP_ERRNO, "error setting utimes"); |
|
|
|
strlcpy(path_tmp2, cvs_tmpdir, sizeof(path_tmp2)); |
|
strlcat(path_tmp2, "/diff2.XXXXXXXXXX", sizeof(path_tmp2)); |
|
cvs_buf_write_stmp(b2, path_tmp2, 0600); |
|
cvs_buf_free(b2); |
|
if (utimes(path_tmp2, (const struct timeval *)&tv2) < 0) |
|
cvs_log(LP_ERRNO, "error setting utimes"); |
|
|
|
cvs_diffreg(path_tmp1, path_tmp2, NULL); |
|
(void)unlink(path_tmp1); |
|
(void)unlink(path_tmp2); |
|
|
|
return (0); |
|
} |
|
#endif |
|
|
|
|
|
int |
|
cvs_diffreg(const char *file1, const char *file2, BUF *out) |
|
{ |
|
FILE *f1, *f2; |
|
int i, rval; |
|
void *tmp; |
|
|
|
f1 = f2 = NULL; |
|
rval = D_SAME; |
|
anychange = 0; |
|
lastline = 0; |
|
lastmatchline = 0; |
|
context_vec_ptr = context_vec_start - 1; |
|
chrtran = (iflag ? cup2low : clow2low); |
|
if (out != NULL) |
|
diffbuf = out; |
|
|
|
f1 = fopen(file1, "r"); |
|
if (f1 == NULL) { |
|
cvs_log(LP_ERRNO, "%s", file1); |
|
goto closem; |
|
} |
|
|
|
f2 = fopen(file2, "r"); |
|
if (f2 == NULL) { |
|
cvs_log(LP_ERRNO, "%s", file2); |
|
goto closem; |
|
} |
|
|
|
if (stat(file1, &stb1) < 0) { |
|
cvs_log(LP_ERRNO, "%s", file1); |
|
goto closem; |
|
} |
|
if (stat(file2, &stb2) < 0) { |
|
cvs_log(LP_ERRNO, "%s", file2); |
|
goto closem; |
|
} |
|
switch (files_differ(f1, f2)) { |
|
case 0: |
|
goto closem; |
|
case 1: |
|
break; |
|
default: |
|
/* error */ |
|
goto closem; |
|
} |
|
|
|
if (!asciifile(f1) || !asciifile(f2)) { |
|
rval = D_BINARY; |
|
goto closem; |
|
} |
|
if (prepare(0, f1, stb1.st_size) < 0 || |
|
prepare(1, f2, stb2.st_size) < 0) { |
|
goto closem; |
|
} |
|
prune(); |
|
sort(sfile[0], slen[0]); |
|
sort(sfile[1], slen[1]); |
|
|
|
member = (int *)file[1]; |
|
equiv(sfile[0], slen[0], sfile[1], slen[1], member); |
|
tmp = xrealloc(member, slen[1] + 2, sizeof(*member)); |
|
member = tmp; |
|
|
|
class = (int *)file[0]; |
|
unsort(sfile[0], slen[0], class); |
|
tmp = xrealloc(class, slen[0] + 2, sizeof(*class)); |
|
class = tmp; |
|
|
|
klist = xcalloc(slen[0] + 2, sizeof(*klist)); |
|
clen = 0; |
|
clistlen = 100; |
|
clist = xcalloc(clistlen, sizeof(*clist)); |
|
|
|
if ((i = stone(class, slen[0], member, klist)) < 0) |
|
goto closem; |
|
|
|
xfree(member); |
|
xfree(class); |
|
|
|
tmp = xrealloc(J, diff_len[0] + 2, sizeof(*J)); |
|
J = tmp; |
|
unravel(klist[i]); |
|
xfree(clist); |
|
xfree(klist); |
|
|
|
tmp = xrealloc(ixold, diff_len[0] + 2, sizeof(*ixold)); |
|
ixold = tmp; |
|
|
|
tmp = xrealloc(ixnew, diff_len[1] + 2, sizeof(*ixnew)); |
|
ixnew = tmp; |
|
check(f1, f2); |
|
output(f1, f2); |
|
|
|
closem: |
|
if (anychange == 1) { |
|
if (rval == D_SAME) |
|
rval = D_DIFFER; |
|
} |
|
if (f1 != NULL) |
|
fclose(f1); |
|
if (f2 != NULL) |
|
fclose(f2); |
|
|
|
return (rval); |
|
} |
|
|
|
/* |
|
* Check to see if the given files differ. |
|
* Returns 0 if they are the same, 1 if different, and -1 on error. |
|
* XXX - could use code from cmp(1) [faster] |
|
*/ |
|
static int |
|
files_differ(FILE *f1, FILE *f2) |
|
{ |
|
char buf1[BUFSIZ], buf2[BUFSIZ]; |
|
size_t i, j; |
|
|
|
if (stb1.st_size != stb2.st_size) |
|
return (1); |
|
for (;;) { |
|
i = fread(buf1, (size_t)1, sizeof(buf1), f1); |
|
j = fread(buf2, (size_t)1, sizeof(buf2), f2); |
|
if (i != j) |
|
return (1); |
|
if (i == 0 && j == 0) { |
|
if (ferror(f1) || ferror(f2)) |
|
return (1); |
|
return (0); |
|
} |
|
if (memcmp(buf1, buf2, i) != 0) |
|
return (1); |
|
} |
|
} |
|
|
|
static int |
|
prepare(int i, FILE *fd, off_t filesize) |
|
{ |
|
void *tmp; |
|
struct line *p; |
|
int j, h; |
|
size_t sz; |
|
|
|
rewind(fd); |
|
|
|
sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25; |
|
if (sz < 100) |
|
sz = 100; |
|
|
|
p = xcalloc(sz + 3, sizeof(*p)); |
|
for (j = 0; (h = readhash(fd));) { |
|
if (j == (int)sz) { |
|
sz = sz * 3 / 2; |
|
tmp = xrealloc(p, sz + 3, sizeof(*p)); |
|
p = tmp; |
|
} |
|
p[++j].value = h; |
|
} |
|
diff_len[i] = j; |
|
file[i] = p; |
|
|
|
return (0); |
|
} |
|
|
|
static void |
|
prune(void) |
|
{ |
|
int i, j; |
|
|
|
for (pref = 0; pref < diff_len[0] && pref < diff_len[1] && |
|
file[0][pref + 1].value == file[1][pref + 1].value; |
|
pref++) |
|
; |
|
for (suff = 0; |
|
(suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) && |
|
(file[0][diff_len[0] - suff].value == |
|
file[1][diff_len[1] - suff].value); |
|
suff++) |
|
; |
|
for (j = 0; j < 2; j++) { |
|
sfile[j] = file[j] + pref; |
|
slen[j] = diff_len[j] - pref - suff; |
|
for (i = 0; i <= slen[j]; i++) |
|
sfile[j][i].serial = i; |
|
} |
|
} |
|
|
|
static void |
|
equiv(struct line *a, int n, struct line *b, int m, int *c) |
|
{ |
|
int i, j; |
|
|
|
i = j = 1; |
|
while (i <= n && j <= m) { |
|
if (a[i].value < b[j].value) |
|
a[i++].value = 0; |
|
else if (a[i].value == b[j].value) |
|
a[i++].value = j; |
|
else |
|
j++; |
|
} |
|
while (i <= n) |
|
a[i++].value = 0; |
|
b[m + 1].value = 0; |
|
j = 0; |
|
while (++j <= m) { |
|
c[j] = -b[j].serial; |
|
while (b[j + 1].value == b[j].value) { |
|
j++; |
|
c[j] = b[j].serial; |
|
} |
|
} |
|
c[j] = -1; |
|
} |
|
|
|
/* Code taken from ping.c */ |
|
static int |
|
isqrt(int n) |
|
{ |
|
int y, x = 1; |
|
|
|
if (n == 0) |
|
return (0); |
|
|
|
do { /* newton was a stinker */ |
|
y = x; |
|
x = n / x; |
|
x += y; |
|
x /= 2; |
|
} while (x - y > 1 || x - y < -1); |
|
|
|
return (x); |
|
} |
|
|
|
static int |
|
stone(int *a, int n, int *b, int *c) |
|
{ |
|
int ret; |
|
int i, k, y, j, l; |
|
int oldc, tc, oldl; |
|
u_int numtries; |
|
|
|
/* XXX move the isqrt() out of the macro to avoid multiple calls */ |
|
const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n)); |
|
|
|
k = 0; |
|
if ((ret = newcand(0, 0, 0)) < 0) |
|
return (-1); |
|
c[0] = ret; |
|
for (i = 1; i <= n; i++) { |
|
j = a[i]; |
|
if (j == 0) |
|
continue; |
|
y = -b[j]; |
|
oldl = 0; |
|
oldc = c[0]; |
|
numtries = 0; |
|
do { |
|
if (y <= clist[oldc].y) |
|
continue; |
|
l = search(c, k, y); |
|
if (l != oldl + 1) |
|
oldc = c[l - 1]; |
|
if (l <= k) { |
|
if (clist[c[l]].y <= y) |
|
continue; |
|
tc = c[l]; |
|
if ((ret = newcand(i, y, oldc)) < 0) |
|
return (-1); |
|
c[l] = ret; |
|
oldc = tc; |
|
oldl = l; |
|
numtries++; |
|
} else { |
|
if ((ret = newcand(i, y, oldc)) < 0) |
|
return (-1); |
|
c[l] = ret; |
|
k++; |
|
break; |
|
} |
|
} while ((y = b[++j]) > 0 && numtries < bound); |
|
} |
|
return (k); |
|
} |
|
|
|
static int |
|
newcand(int x, int y, int pred) |
|
{ |
|
struct cand *q, *tmp; |
|
int newclistlen; |
|
|
|
if (clen == clistlen) { |
|
newclistlen = clistlen * 11 / 10; |
|
tmp = xrealloc(clist, newclistlen, sizeof(*clist)); |
|
clist = tmp; |
|
clistlen = newclistlen; |
|
} |
|
q = clist + clen; |
|
q->x = x; |
|
q->y = y; |
|
q->pred = pred; |
|
return (clen++); |
|
} |
|
|
|
static int |
|
search(int *c, int k, int y) |
|
{ |
|
int i, j, l, t; |
|
|
|
if (clist[c[k]].y < y) /* quick look for typical case */ |
|
return (k + 1); |
|
i = 0; |
|
j = k + 1; |
|
for (;;) { |
|
l = (i + j) / 2; |
|
if (l <= i) |
|
break; |
|
t = clist[c[l]].y; |
|
if (t > y) |
|
j = l; |
|
else if (t < y) |
|
i = l; |
|
else |
|
return (l); |
|
} |
|
return (l + 1); |
|
} |
|
|
|
static void |
|
unravel(int p) |
|
{ |
|
struct cand *q; |
|
int i; |
|
|
|
for (i = 0; i <= diff_len[0]; i++) |
|
J[i] = i <= pref ? i : |
|
i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0; |
|
for (q = clist + p; q->y != 0; q = clist + q->pred) |
|
J[q->x + pref] = q->y + pref; |
|
} |
|
|
|
/* |
|
* Check does double duty: |
|
* 1. ferret out any fortuitous correspondences due |
|
* to confounding by hashing (which result in "jackpot") |
|
* 2. collect random access indexes to the two files |
|
*/ |
|
static void |
|
check(FILE *f1, FILE *f2) |
|
{ |
|
int i, j, jackpot, c, d; |
|
long ctold, ctnew; |
|
|
|
rewind(f1); |
|
rewind(f2); |
|
j = 1; |
|
ixold[0] = ixnew[0] = 0; |
|
jackpot = 0; |
|
ctold = ctnew = 0; |
|
for (i = 1; i <= diff_len[0]; i++) { |
|
if (J[i] == 0) { |
|
ixold[i] = ctold += skipline(f1); |
|
continue; |
|
} |
|
while (j < J[i]) { |
|
ixnew[j] = ctnew += skipline(f2); |
|
j++; |
|
} |
|
if (bflag == 1 || wflag == 1 || iflag == 1) { |
|
for (;;) { |
|
c = getc(f1); |
|
d = getc(f2); |
|
/* |
|
* GNU diff ignores a missing newline |
|
* in one file if bflag || wflag. |
|
*/ |
|
if ((bflag == 1 || wflag == 1) && |
|
((c == EOF && d == '\n') || |
|
(c == '\n' && d == EOF))) { |
|
break; |
|
} |
|
ctold++; |
|
ctnew++; |
|
if (bflag == 1 && isspace(c) && isspace(d)) { |
|
do { |
|
if (c == '\n') |
|
break; |
|
ctold++; |
|
} while (isspace(c = getc(f1))); |
|
do { |
|
if (d == '\n') |
|
break; |
|
ctnew++; |
|
} while (isspace(d = getc(f2))); |
|
} else if (wflag == 1) { |
|
while (isspace(c) && c != '\n') { |
|
c = getc(f1); |
|
ctold++; |
|
} |
|
while (isspace(d) && d != '\n') { |
|
d = getc(f2); |
|
ctnew++; |
|
} |
|
} |
|
if (chrtran[c] != chrtran[d]) { |
|
jackpot++; |
|
J[i] = 0; |
|
if (c != '\n' && c != EOF) |
|
ctold += skipline(f1); |
|
if (d != '\n' && c != EOF) |
|
ctnew += skipline(f2); |
|
break; |
|
} |
|
if (c == '\n' || c == EOF) |
|
break; |
|
} |
|
} else { |
|
for (;;) { |
|
ctold++; |
|
ctnew++; |
|
if ((c = getc(f1)) != (d = getc(f2))) { |
|
/* jackpot++; */ |
|
J[i] = 0; |
|
if (c != '\n' && c != EOF) |
|
ctold += skipline(f1); |
|
if (d != '\n' && c != EOF) |
|
ctnew += skipline(f2); |
|
break; |
|
} |
|
if (c == '\n' || c == EOF) |
|
break; |
|
} |
|
} |
|
ixold[i] = ctold; |
|
ixnew[j] = ctnew; |
|
j++; |
|
} |
|
for (; j <= diff_len[1]; j++) |
|
ixnew[j] = ctnew += skipline(f2); |
|
/* |
|
* if (jackpot != 0) |
|
* cvs_printf("jackpot\n"); |
|
*/ |
|
} |
|
|
|
/* shellsort CACM #201 */ |
|
static void |
|
sort(struct line *a, int n) |
|
{ |
|
struct line *ai, *aim, w; |
|
int j, m = 0, k; |
|
|
|
if (n == 0) |
|
return; |
return; |
for (j = 1; j <= n; j *= 2) |
|
m = 2 * j - 1; |
|
for (m /= 2; m != 0; m /= 2) { |
|
k = n - m; |
|
for (j = 1; j <= k; j++) { |
|
for (ai = &a[j]; ai > a; ai -= m) { |
|
aim = &ai[m]; |
|
if (aim < ai) |
|
break; /* wraparound */ |
|
if (aim->value > ai[0].value || |
|
(aim->value == ai[0].value && |
|
aim->serial > ai[0].serial)) |
|
break; |
|
w.value = ai[0].value; |
|
ai[0].value = aim->value; |
|
aim->value = w.value; |
|
w.serial = ai[0].serial; |
|
ai[0].serial = aim->serial; |
|
aim->serial = w.serial; |
|
} |
|
} |
|
} |
} |
} |
|
|
|
static void |
cvs_file_classify(cf); |
unsort(struct line *f, int l, int *b) |
|
{ |
|
int *a, i; |
|
|
|
a = xcalloc(l + 1, sizeof(*a)); |
if (cf->file_status == FILE_LOST) { |
for (i = 1; i <= l; i++) |
cvs_log(LP_ERR, "cannot find file %s", cf->file_path); |
a[f[i].serial] = f[i].value; |
|
for (i = 1; i <= l; i++) |
|
b[i] = a[i]; |
|
xfree(a); |
|
} |
|
|
|
static int |
|
skipline(FILE *f) |
|
{ |
|
int i, c; |
|
|
|
for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++) |
|
continue; |
|
return (i); |
|
} |
|
|
|
static void |
|
output(FILE *f1, FILE *f2) |
|
{ |
|
int m, i0, i1, j0, j1; |
|
|
|
rewind(f1); |
|
rewind(f2); |
|
m = diff_len[0]; |
|
J[0] = 0; |
|
J[m + 1] = diff_len[1] + 1; |
|
for (i0 = 1; i0 <= m; i0 = i1 + 1) { |
|
while (i0 <= m && J[i0] == J[i0 - 1] + 1) |
|
i0++; |
|
j0 = J[i0 - 1] + 1; |
|
i1 = i0 - 1; |
|
while (i1 < m && J[i1 + 1] == 0) |
|
i1++; |
|
j1 = J[i1 + 1] - 1; |
|
J[i1] = j1; |
|
change(f1, f2, i0, i1, j0, j1); |
|
} |
|
if (m == 0) |
|
change(f1, f2, 1, 0, 1, diff_len[1]); |
|
if (diff_format == D_IFDEF) { |
|
for (;;) { |
|
#define c i0 |
|
if ((c = getc(f1)) == EOF) |
|
return; |
|
diff_output("%c", c); |
|
} |
|
#undef c |
|
} |
|
if (anychange != 0) { |
|
if (diff_format == D_CONTEXT) |
|
dump_context_vec(f1, f2); |
|
else if (diff_format == D_UNIFIED) |
|
dump_unified_vec(f1, f2); |
|
} |
|
} |
|
|
|
static __inline void |
|
range(int a, int b, char *separator) |
|
{ |
|
diff_output("%d", a > b ? b : a); |
|
if (a < b) |
|
diff_output("%s%d", separator, b); |
|
} |
|
|
|
static __inline void |
|
uni_range(int a, int b) |
|
{ |
|
if (a < b) |
|
diff_output("%d,%d", a, b - a + 1); |
|
else if (a == b) |
|
diff_output("%d", b); |
|
else |
|
diff_output("%d,0", b); |
|
} |
|
|
|
static char * |
|
preadline(int fd, size_t rlen, off_t off) |
|
{ |
|
char *line; |
|
ssize_t nr; |
|
|
|
line = xmalloc(rlen + 1); |
|
if ((nr = pread(fd, line, rlen, off)) < 0) { |
|
cvs_log(LP_ERRNO, "preadline failed"); |
|
return (NULL); |
|
} |
|
line[nr] = '\0'; |
|
return (line); |
|
} |
|
|
|
static int |
|
ignoreline(char *line) |
|
{ |
|
int ret; |
|
|
|
ret = regexec(&ignore_re, line, (size_t)0, NULL, 0); |
|
xfree(line); |
|
return (ret == 0); /* if it matched, it should be ignored. */ |
|
} |
|
|
|
/* |
|
* Indicate that there is a difference between lines a and b of the from file |
|
* to get to lines c to d of the to file. If a is greater then b then there |
|
* are no lines in the from file involved and this means that there were |
|
* lines appended (beginning at b). If c is greater than d then there are |
|
* lines missing from the to file. |
|
*/ |
|
static void |
|
change(FILE *f1, FILE *f2, int a, int b, int c, int d) |
|
{ |
|
int i; |
|
static size_t max_context = 64; |
|
char buf[64]; |
|
struct tm *t; |
|
|
|
if (diff_format != D_IFDEF && a > b && c > d) |
|
return; |
return; |
if (ignore_pats != NULL) { |
} else if (cf->file_status == FILE_UNKNOWN) { |
char *line; |
cvs_log(LP_ERR, "I know nothing about %s", cf->file_path); |
/* |
|
* All lines in the change, insert, or delete must |
|
* match an ignore pattern for the change to be |
|
* ignored. |
|
*/ |
|
if (a <= b) { /* Changes and deletes. */ |
|
for (i = a; i <= b; i++) { |
|
line = preadline(fileno(f1), |
|
ixold[i] - ixold[i - 1], ixold[i - 1]); |
|
if (!ignoreline(line)) |
|
goto proceed; |
|
} |
|
} |
|
if (a > b || c <= d) { /* Changes and inserts. */ |
|
for (i = c; i <= d; i++) { |
|
line = preadline(fileno(f2), |
|
ixnew[i] - ixnew[i - 1], ixnew[i - 1]); |
|
if (!ignoreline(line)) |
|
goto proceed; |
|
} |
|
} |
|
return; |
return; |
} |
} else if (cf->file_status == FILE_UPTODATE && diff_rev2 == NULL) |
proceed: |
|
if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) { |
|
/* |
|
* Allocate change records as needed. |
|
*/ |
|
if (context_vec_ptr == context_vec_end - 1) { |
|
struct context_vec *tmp; |
|
ptrdiff_t offset = context_vec_ptr - context_vec_start; |
|
max_context <<= 1; |
|
tmp = xrealloc(context_vec_start, max_context, |
|
sizeof(*context_vec_start)); |
|
context_vec_start = tmp; |
|
context_vec_end = context_vec_start + max_context; |
|
context_vec_ptr = context_vec_start + offset; |
|
} |
|
if (anychange == 0) { |
|
/* |
|
* Print the context/unidiff header first time through. |
|
*/ |
|
t = localtime(&stb1.st_mtime); |
|
(void)strftime(buf, sizeof(buf), |
|
"%Y/%m/%d %H:%M:%S", t); |
|
|
|
diff_output("%s %s %s", |
|
diff_format == D_CONTEXT ? "***" : "---", diff_file, |
|
buf); |
|
|
|
if (diff_rev1 != NULL) { |
|
rcsnum_tostr(diff_rev1, buf, sizeof(buf)); |
|
diff_output("\t%s", buf); |
|
} |
|
|
|
printf("\n"); |
|
|
|
t = localtime(&stb2.st_mtime); |
|
(void)strftime(buf, sizeof(buf), |
|
"%Y/%m/%d %H:%M:%S", t); |
|
|
|
diff_output("%s %s %s", |
|
diff_format == D_CONTEXT ? "---" : "+++", diff_file, |
|
buf); |
|
|
|
if (diff_rev2 != NULL) { |
|
rcsnum_tostr(diff_rev2, buf, sizeof(buf)); |
|
diff_output("\t%s", buf); |
|
} |
|
|
|
printf("\n"); |
|
anychange = 1; |
|
} else if (a > context_vec_ptr->b + (2 * context) + 1 && |
|
c > context_vec_ptr->d + (2 * context) + 1) { |
|
/* |
|
* If this change is more than 'context' lines from the |
|
* previous change, dump the record and reset it. |
|
*/ |
|
if (diff_format == D_CONTEXT) |
|
dump_context_vec(f1, f2); |
|
else |
|
dump_unified_vec(f1, f2); |
|
} |
|
context_vec_ptr++; |
|
context_vec_ptr->a = a; |
|
context_vec_ptr->b = b; |
|
context_vec_ptr->c = c; |
|
context_vec_ptr->d = d; |
|
return; |
return; |
} |
|
if (anychange == 0) |
|
anychange = 1; |
|
switch (diff_format) { |
|
case D_BRIEF: |
|
return; |
|
case D_NORMAL: |
|
range(a, b, ","); |
|
diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c'); |
|
if (diff_format == D_NORMAL) |
|
range(c, d, ","); |
|
diff_output("\n"); |
|
break; |
|
case D_RCSDIFF: |
|
if (a > b) |
|
diff_output("a%d %d\n", b, d - c + 1); |
|
else { |
|
diff_output("d%d %d\n", a, b - a + 1); |
|
|
|
if (!(c > d)) /* add changed lines */ |
diff_file = cf->file_path; |
diff_output("a%d %d\n", b, d - c + 1); |
cvs_printf("Index: %s\n%s\nRCS file: %s\n", cf->file_path, |
} |
RCS_DIFF_DIV, cf->file_rpath); |
break; |
|
} |
|
if (diff_format == D_NORMAL || diff_format == D_IFDEF) { |
|
fetch(ixold, a, b, f1, '<', 1); |
|
if (a <= b && c <= d && diff_format == D_NORMAL) |
|
diff_output("---\n"); |
|
} |
|
fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0); |
|
if (inifdef) { |
|
diff_output("#endif /* %s */\n", ifdefname); |
|
inifdef = 0; |
|
} |
|
} |
|
|
|
static void |
if (diff_rev1 != NULL) |
fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile) |
r1 = diff_rev1; |
{ |
else |
long j, nc; |
r1 = cf->file_ent->ce_rev; |
int i, c, col; |
|
|
|
/* |
diff_rev1 = r1; |
* When doing #ifdef's, copy down to current line |
rcsnum_tostr(r1, rbuf , sizeof(rbuf)); |
* if this is the first file, so that stuff makes it to output. |
cvs_printf("retrieving revision %s\n", rbuf); |
*/ |
if ((b1 = rcs_getrev(cf->file_rcs, r1)) == NULL) |
if (diff_format == D_IFDEF && oldfile) { |
fatal("failed to retrieve revision %s", rbuf); |
long curpos = ftell(lb); |
|
/* print through if append (a>b), else to (nb: 0 vs 1 orig) */ |
|
nc = f[a > b ? b : a - 1] - curpos; |
|
for (i = 0; i < nc; i++) |
|
diff_output("%c", getc(lb)); |
|
} |
|
if (a > b) |
|
return; |
|
if (diff_format == D_IFDEF) { |
|
if (inifdef) { |
|
diff_output("#else /* %s%s */\n", |
|
oldfile == 1 ? "!" : "", ifdefname); |
|
} else { |
|
if (oldfile) |
|
diff_output("#ifndef %s\n", ifdefname); |
|
else |
|
diff_output("#ifdef %s\n", ifdefname); |
|
} |
|
inifdef = 1 + oldfile; |
|
} |
|
for (i = a; i <= b; i++) { |
|
fseek(lb, f[i - 1], SEEK_SET); |
|
nc = f[i] - f[i - 1]; |
|
if (diff_format != D_IFDEF && ch != '\0') { |
|
diff_output("%c", ch); |
|
if (Tflag == 1 && (diff_format == D_NORMAL || |
|
diff_format == D_CONTEXT || |
|
diff_format == D_UNIFIED)) |
|
diff_output("\t"); |
|
else if (diff_format != D_UNIFIED) |
|
diff_output(" "); |
|
} |
|
col = 0; |
|
for (j = 0; j < nc; j++) { |
|
if ((c = getc(lb)) == EOF) { |
|
if (diff_format == D_RCSDIFF) |
|
cvs_log(LP_WARN, |
|
"No newline at end of file"); |
|
else |
|
diff_output("\n\\ No newline at end of " |
|
"file"); |
|
return; |
|
} |
|
if (c == '\t' && tflag == 1) { |
|
do { |
|
diff_output(" "); |
|
} while (++col & 7); |
|
} else { |
|
diff_output("%c", c); |
|
col++; |
|
} |
|
} |
|
} |
|
} |
|
|
|
/* |
tv[0].tv_sec = rcs_rev_getdate(cf->file_rcs, r1); |
* Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578. |
tv[0].tv_usec = 0; |
*/ |
tv[1] = tv[0]; |
static int |
|
readhash(FILE *f) |
|
{ |
|
int i, t, space; |
|
int sum; |
|
|
|
sum = 1; |
if (diff_rev2 != NULL) { |
space = 0; |
rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf)); |
if (bflag != 1 && wflag != 1) { |
cvs_printf("retrieving revision %s\n", rbuf); |
if (iflag == 1) |
if ((b2 = rcs_getrev(cf->file_rcs, diff_rev2)) == NULL) |
for (i = 0; (t = getc(f)) != '\n'; i++) { |
fatal("failed to retrieve revision %s", rbuf); |
if (t == EOF) { |
|
if (i == 0) |
tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev2); |
return (0); |
tv2[0].tv_usec = 0; |
break; |
tv2[1] = tv2[0]; |
} |
|
sum = sum * 127 + chrtran[t]; |
|
} |
|
else |
|
for (i = 0; (t = getc(f)) != '\n'; i++) { |
|
if (t == EOF) { |
|
if (i == 0) |
|
return (0); |
|
break; |
|
} |
|
sum = sum * 127 + t; |
|
} |
|
} else { |
} else { |
for (i = 0;;) { |
if (fstat(cf->fd, &st) == -1) |
switch (t = getc(f)) { |
fatal("fstat failed %s", strerror(errno)); |
case '\t': |
if ((b2 = cvs_buf_load(cf->file_path, BUF_AUTOEXT)) == NULL) |
case ' ': |
fatal("failed to load %s", cf->file_path); |
space++; |
|
continue; |
|
default: |
|
if (space != 0 && wflag != 1) { |
|
i++; |
|
space = 0; |
|
} |
|
sum = sum * 127 + chrtran[t]; |
|
i++; |
|
continue; |
|
case EOF: |
|
if (i == 0) |
|
return (0); |
|
/* FALLTHROUGH */ |
|
case '\n': |
|
break; |
|
} |
|
break; |
|
} |
|
} |
|
/* |
|
* There is a remote possibility that we end up with a zero sum. |
|
* Zero is used as an EOF marker, so return 1 instead. |
|
*/ |
|
return (sum == 0 ? 1 : sum); |
|
} |
|
|
|
static int |
st.st_mtime = cvs_hack_time(st.st_mtime, 1); |
asciifile(FILE *f) |
if (st.st_mtime == 0) |
{ |
fatal("cvs_diff_local: to gmt failed"); |
char buf[BUFSIZ]; |
|
size_t i, cnt; |
|
|
|
if (aflag == 1 || f == NULL) |
tv2[0].tv_sec = st.st_mtime; |
return (1); |
tv2[0].tv_usec = 0; |
|
tv2[1] = tv2[0]; |
rewind(f); |
|
cnt = fread(buf, (size_t)1, sizeof(buf), f); |
|
for (i = 0; i < cnt; i++) |
|
if (!isprint(buf[i]) && !isspace(buf[i])) |
|
return (0); |
|
return (1); |
|
} |
|
|
|
static char* |
|
match_function(const long *f, int pos, FILE *fp) |
|
{ |
|
unsigned char buf[FUNCTION_CONTEXT_SIZE]; |
|
size_t nc; |
|
int last = lastline; |
|
char *p; |
|
|
|
lastline = pos; |
|
while (pos > last) { |
|
fseek(fp, f[pos - 1], SEEK_SET); |
|
nc = f[pos] - f[pos - 1]; |
|
if (nc >= sizeof(buf)) |
|
nc = sizeof(buf) - 1; |
|
nc = fread(buf, (size_t)1, nc, fp); |
|
if (nc > 0) { |
|
buf[nc] = '\0'; |
|
p = strchr((const char *)buf, '\n'); |
|
if (p != NULL) |
|
*p = '\0'; |
|
if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') { |
|
strlcpy(lastbuf, (const char *)buf, |
|
sizeof lastbuf); |
|
lastmatchline = pos; |
|
return lastbuf; |
|
} |
|
} |
|
pos--; |
|
} |
} |
return (lastmatchline > 0) ? lastbuf : NULL; |
|
} |
|
|
|
|
cvs_printf("%s", diffargs); |
|
|
/* dump accumulated "context" diff changes */ |
rcsnum_tostr(r1, rbuf, sizeof(rbuf)); |
static void |
cvs_printf(" -r%s", rbuf); |
dump_context_vec(FILE *f1, FILE *f2) |
|
{ |
|
struct context_vec *cvp = context_vec_start; |
|
int lowa, upb, lowc, upd, do_output; |
|
int a, b, c, d; |
|
char ch, *f; |
|
|
|
if (context_vec_start > context_vec_ptr) |
if (diff_rev2 != NULL) { |
return; |
rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf)); |
|
cvs_printf(" -r%s", rbuf); |
b = d = 0; /* gcc */ |
|
lowa = MAX(1, cvp->a - context); |
|
upb = MIN(diff_len[0], context_vec_ptr->b + context); |
|
lowc = MAX(1, cvp->c - context); |
|
upd = MIN(diff_len[1], context_vec_ptr->d + context); |
|
|
|
diff_output("***************"); |
|
if (pflag == 1) { |
|
f = match_function(ixold, lowa - 1, f1); |
|
if (f != NULL) { |
|
diff_output(" "); |
|
diff_output("%s", f); |
|
} |
|
} |
} |
diff_output("\n*** "); |
|
range(lowa, upb, ","); |
|
diff_output(" ****\n"); |
|
|
|
/* |
cvs_printf(" %s\n", cf->file_path); |
* Output changes to the "old" file. The first loop suppresses |
|
* output if there were no changes to the "old" file (we'll see |
|
* the "old" lines as context in the "new" list). |
|
*/ |
|
do_output = 0; |
|
for (; cvp <= context_vec_ptr; cvp++) |
|
if (cvp->a <= cvp->b) { |
|
cvp = context_vec_start; |
|
do_output++; |
|
break; |
|
} |
|
if (do_output != 0) { |
|
while (cvp <= context_vec_ptr) { |
|
a = cvp->a; |
|
b = cvp->b; |
|
c = cvp->c; |
|
d = cvp->d; |
|
|
|
if (a <= b && c <= d) |
len = strlcpy(p1, cvs_tmpdir, sizeof(p1)); |
ch = 'c'; |
if (len >= sizeof(p1)) |
else |
fatal("cvs_diff_local: truncation"); |
ch = (a <= b) ? 'd' : 'a'; |
|
|
|
if (ch == 'a') |
len = strlcat(p1, "/diff1.XXXXXXXXXX", sizeof(p1)); |
fetch(ixold, lowa, b, f1, ' ', 0); |
if (len >= sizeof(p1)) |
else { |
fatal("cvs_diff_local: truncation"); |
fetch(ixold, lowa, a - 1, f1, ' ', 0); |
|
fetch(ixold, a, b, f1, |
|
ch == 'c' ? '!' : '-', 0); |
|
} |
|
lowa = b + 1; |
|
cvp++; |
|
} |
|
fetch(ixold, b + 1, upb, f1, ' ', 0); |
|
} |
|
/* output changes to the "new" file */ |
|
diff_output("--- "); |
|
range(lowc, upd, ","); |
|
diff_output(" ----\n"); |
|
|
|
do_output = 0; |
cvs_buf_write_stmp(b1, p1, 0600, tv); |
for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++) |
cvs_buf_free(b1); |
if (cvp->c <= cvp->d) { |
|
cvp = context_vec_start; |
|
do_output++; |
|
break; |
|
} |
|
if (do_output != 0) { |
|
while (cvp <= context_vec_ptr) { |
|
a = cvp->a; |
|
b = cvp->b; |
|
c = cvp->c; |
|
d = cvp->d; |
|
|
|
if (a <= b && c <= d) |
len = strlcpy(p2, cvs_tmpdir, sizeof(p2)); |
ch = 'c'; |
if (len >= sizeof(p2)) |
else |
fatal("cvs_diff_local: truncation"); |
ch = (a <= b) ? 'd' : 'a'; |
|
|
|
if (ch == 'd') |
len = strlcat(p2, "/diff2.XXXXXXXXXX", sizeof(p2)); |
fetch(ixnew, lowc, d, f2, ' ', 0); |
if (len >= sizeof(p2)) |
else { |
fatal("cvs_diff_local: truncation"); |
fetch(ixnew, lowc, c - 1, f2, ' ', 0); |
|
fetch(ixnew, c, d, f2, |
|
ch == 'c' ? '!' : '+', 0); |
|
} |
|
lowc = d + 1; |
|
cvp++; |
|
} |
|
fetch(ixnew, d + 1, upd, f2, ' ', 0); |
|
} |
|
context_vec_ptr = context_vec_start - 1; |
|
} |
|
|
|
/* dump accumulated "unified" diff changes */ |
cvs_buf_write_stmp(b2, p2, 0600, tv2); |
static void |
cvs_buf_free(b2); |
dump_unified_vec(FILE *f1, FILE *f2) |
|
{ |
|
struct context_vec *cvp = context_vec_start; |
|
int lowa, upb, lowc, upd; |
|
int a, b, c, d; |
|
char ch, *f; |
|
|
|
if (context_vec_start > context_vec_ptr) |
cvs_diffreg(p1, p2, NULL); |
return; |
cvs_worklist_run(&temp_files, cvs_worklist_unlink); |
|
|
b = d = 0; /* gcc */ |
|
lowa = MAX(1, cvp->a - context); |
|
upb = MIN(diff_len[0], context_vec_ptr->b + context); |
|
lowc = MAX(1, cvp->c - context); |
|
upd = MIN(diff_len[1], context_vec_ptr->d + context); |
|
|
|
diff_output("@@ -"); |
|
uni_range(lowa, upb); |
|
diff_output(" +"); |
|
uni_range(lowc, upd); |
|
diff_output(" @@"); |
|
if (pflag == 1) { |
|
f = match_function(ixold, lowa - 1, f1); |
|
if (f != NULL) { |
|
diff_output(" "); |
|
diff_output("%s", f); |
|
} |
|
} |
|
diff_output("\n"); |
|
|
|
/* |
|
* Output changes in "unified" diff format--the old and new lines |
|
* are printed together. |
|
*/ |
|
for (; cvp <= context_vec_ptr; cvp++) { |
|
a = cvp->a; |
|
b = cvp->b; |
|
c = cvp->c; |
|
d = cvp->d; |
|
|
|
/* |
|
* c: both new and old changes |
|
* d: only changes in the old file |
|
* a: only changes in the new file |
|
*/ |
|
if (a <= b && c <= d) |
|
ch = 'c'; |
|
else |
|
ch = (a <= b) ? 'd' : 'a'; |
|
|
|
switch (ch) { |
|
case 'c': |
|
fetch(ixold, lowa, a - 1, f1, ' ', 0); |
|
fetch(ixold, a, b, f1, '-', 0); |
|
fetch(ixnew, c, d, f2, '+', 0); |
|
break; |
|
case 'd': |
|
fetch(ixold, lowa, a - 1, f1, ' ', 0); |
|
fetch(ixold, a, b, f1, '-', 0); |
|
break; |
|
case 'a': |
|
fetch(ixnew, lowc, c - 1, f2, ' ', 0); |
|
fetch(ixnew, c, d, f2, '+', 0); |
|
break; |
|
} |
|
lowa = b + 1; |
|
lowc = d + 1; |
|
} |
|
fetch(ixnew, d + 1, upd, f2, ' ', 0); |
|
|
|
context_vec_ptr = context_vec_start - 1; |
|
} |
|
|
|
void |
|
diff_output(const char *fmt, ...) |
|
{ |
|
va_list vap; |
|
int i; |
|
char *str; |
|
|
|
va_start(vap, fmt); |
|
i = vasprintf(&str, fmt, vap); |
|
va_end(vap); |
|
if (i == -1) |
|
fatal("diff_output: %s", strerror(errno)); |
|
if (diffbuf != NULL) |
|
cvs_buf_append(diffbuf, str, strlen(str)); |
|
else |
|
cvs_printf("%s", str); |
|
xfree(str); |
|
} |
} |