#include <assert.h>
#include "cvs.h"
enum diff_file
{
DIFF_ERROR,
DIFF_ADDED,
DIFF_REMOVED,
DIFF_DIFFERENT,
DIFF_SAME
};
static Dtype diff_dirproc PROTO ((void *callerdat, const char *dir,
const char *pos_repos,
const char *update_dir,
List *entries));
static int diff_filesdoneproc PROTO ((void *callerdat, int err,
const char *repos,
const char *update_dir,
List *entries));
static int diff_dirleaveproc PROTO ((void *callerdat, const char *dir,
int err, const char *update_dir,
List *entries));
static enum diff_file diff_file_nodiff PROTO(( struct file_info *finfo,
Vers_TS *vers,
enum diff_file,
char **rev1_cache ));
static int diff_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static void diff_mark_errors PROTO((int err));
static char *diff_rev1, *diff_rev2;
static char *diff_date1, *diff_date2;
static char *use_rev1, *use_rev2;
static int have_rev1_label, have_rev2_label;
static char *user_file_rev;
static char *options;
static char *opts;
static size_t opts_allocated = 1;
static int diff_errors;
static int empty_files = 0;
static const char *const diff_usage[] =
{
"Usage: %s %s [-lR] [-k kopt] [format_options]\n",
" [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n",
"\t-l\tLocal directory only, not recursive\n",
"\t-R\tProcess directories recursively.\n",
"\t-k kopt\tSpecify keyword expansion mode.\n",
"\t-D d1\tDiff revision for date against working file.\n",
"\t-D d2\tDiff rev1/date1 against date2.\n",
"\t-r rev1\tDiff revision for rev1 against working file.\n",
"\t-r rev2\tDiff rev1/date1 against rev2.\n",
"\nformat_options:\n",
" -i --ignore-case Consider upper- and lower-case to be the same.\n",
" -w --ignore-all-space Ignore all white space.\n",
" -b --ignore-space-change Ignore changes in the amount of white space.\n",
" -B --ignore-blank-lines Ignore changes whose lines are all blank.\n",
" -I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE.\n",
" --binary Read and write data in binary mode.\n",
" -a --text Treat all files as text.\n\n",
" -c -C NUM --context[=NUM] Output NUM (default 2) lines of copied context.\n",
" -u -U NUM --unified[=NUM] Output NUM (default 2) lines of unified context.\n",
" -NUM Use NUM context lines.\n",
" -L LABEL --label LABEL Use LABEL instead of file name.\n",
" -p --show-c-function Show which C function each change is in.\n",
" -F RE --show-function-line=RE Show the most recent line matching RE.\n",
" --brief Output only whether files differ.\n",
" -e --ed Output an ed script.\n",
" -f --forward-ed Output something like an ed script in forward order.\n",
" -n --rcs Output an RCS format diff.\n",
" -y --side-by-side Output in two columns.\n",
" -W NUM --width=NUM Output at most NUM (default 130) characters per line.\n",
" --left-column Output only the left column of common lines.\n",
" --suppress-common-lines Do not output common lines.\n",
" --ifdef=NAME Output merged file to show `#ifdef NAME' diffs.\n",
" --GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT.\n",
" --line-format=LFMT Similar, but format all input lines with LFMT.\n",
" --LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT.\n",
" LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'.\n",
" GFMT may contain:\n",
" %%< lines from FILE1\n",
" %%> lines from FILE2\n",
" %%= lines common to FILE1 and FILE2\n",
" %%[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER\n",
" LETTERs are as follows for new group, lower case for old group:\n",
" F first line number\n",
" L last line number\n",
" N number of lines = L-F+1\n",
" E F-1\n",
" M L+1\n",
" LFMT may contain:\n",
" %%L contents of line\n",
" %%l contents of line, excluding any trailing newline\n",
" %%[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number\n",
" Either GFMT or LFMT may contain:\n",
" %%%% %%\n",
" %%c'C' the single character C\n",
" %%c'\\OOO' the character with octal code OOO\n\n",
" -t --expand-tabs Expand tabs to spaces in output.\n",
" -T --initial-tab Make tabs line up by prepending a tab.\n\n",
" -N --new-file Treat absent files as empty.\n",
" -s --report-identical-files Report when two files are the same.\n",
" --horizon-lines=NUM Keep NUM lines of the common prefix and suffix.\n",
" -d --minimal Try hard to find a smaller set of changes.\n",
" -H --speed-large-files Assume large files and many scattered small changes.\n",
"\n(Specify the --help global option for a list of other help options)\n",
NULL
};
static struct option const longopts[] =
{
{"ignore-blank-lines", 0, 0, 'B'},
{"context", 2, 0, 143},
{"ifdef", 1, 0, 131},
{"show-function-line", 1, 0, 'F'},
{"speed-large-files", 0, 0, 'H'},
{"ignore-matching-lines", 1, 0, 'I'},
{"label", 1, 0, 'L'},
{"new-file", 0, 0, 'N'},
{"initial-tab", 0, 0, 'T'},
{"width", 1, 0, 'W'},
{"text", 0, 0, 'a'},
{"ignore-space-change", 0, 0, 'b'},
{"minimal", 0, 0, 'd'},
{"ed", 0, 0, 'e'},
{"forward-ed", 0, 0, 'f'},
{"ignore-case", 0, 0, 'i'},
{"rcs", 0, 0, 'n'},
{"show-c-function", 0, 0, 'p'},
{"brief", 0, 0, 145},
{"report-identical-files", 0, 0, 's'},
{"expand-tabs", 0, 0, 't'},
{"ignore-all-space", 0, 0, 'w'},
{"side-by-side", 0, 0, 'y'},
{"unified", 2, 0, 146},
{"left-column", 0, 0, 129},
{"suppress-common-lines", 0, 0, 130},
{"old-line-format", 1, 0, 132},
{"new-line-format", 1, 0, 133},
{"unchanged-line-format", 1, 0, 134},
{"line-format", 1, 0, 135},
{"old-group-format", 1, 0, 136},
{"new-group-format", 1, 0, 137},
{"unchanged-group-format", 1, 0, 138},
{"changed-group-format", 1, 0, 139},
{"horizon-lines", 1, 0, 140},
{"binary", 0, 0, 142},
{0, 0, 0, 0}
};
int
diff (argc, argv)
int argc;
char **argv;
{
char tmp[50];
int c, err = 0;
int local = 0;
int which;
int option_index;
if (argc == -1)
usage (diff_usage);
have_rev1_label = have_rev2_label = 0;
if (opts == NULL)
{
opts_allocated = 1;
opts = xmalloc (opts_allocated);
}
opts[0] = '\0';
diff_rev1 = NULL;
diff_rev2 = NULL;
diff_date1 = NULL;
diff_date2 = NULL;
optind = 0;
while ((c = getopt_long (argc, argv,
"+abcdefhilnpstuwy0123456789BHNRTC:D:F:I:L:U:W:k:r:",
longopts, &option_index)) != -1)
{
switch (c)
{
case 'y':
xrealloc_and_strcat (&opts, &opts_allocated, " --side-by-side");
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'h': case 'i': case 'n': case 'p': case 's': case 't':
case 'u': case 'w':
case '0': case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
case 'B': case 'H': case 'T':
(void) sprintf (tmp, " -%c", (char) c);
xrealloc_and_strcat (&opts, &opts_allocated, tmp);
break;
case 'L':
if (have_rev1_label++)
if (have_rev2_label++)
{
error (0, 0, "extra -L arguments ignored");
break;
}
xrealloc_and_strcat (&opts, &opts_allocated, " -L");
xrealloc_and_strcat (&opts, &opts_allocated, optarg);
break;
case 'C': case 'F': case 'I': case 'U': case 'W':
(void) sprintf (tmp, " -%c", (char) c);
xrealloc_and_strcat (&opts, &opts_allocated, tmp);
xrealloc_and_strcat (&opts, &opts_allocated, optarg);
break;
case 131:
xrealloc_and_strcat (&opts, &opts_allocated, " --ifdef=");
xrealloc_and_strcat (&opts, &opts_allocated, optarg);
break;
case 129: case 130: case 132: case 133: case 134:
case 135: case 136: case 137: case 138: case 139: case 140:
case 141: case 142: case 143: case 145: case 146:
xrealloc_and_strcat (&opts, &opts_allocated, " --");
xrealloc_and_strcat (&opts, &opts_allocated,
longopts[option_index].name);
if (longopts[option_index].has_arg == 1
|| (longopts[option_index].has_arg == 2
&& optarg != NULL))
{
xrealloc_and_strcat (&opts, &opts_allocated, "=");
xrealloc_and_strcat (&opts, &opts_allocated, optarg);
}
break;
case 'R':
local = 0;
break;
case 'l':
local = 1;
break;
case 'k':
if (options)
free (options);
options = RCS_check_kflag (optarg);
break;
case 'r':
if (diff_rev2 != NULL || diff_date2 != NULL)
error (1, 0,
"no more than two revisions/dates can be specified");
if (diff_rev1 != NULL || diff_date1 != NULL)
diff_rev2 = optarg;
else
diff_rev1 = optarg;
break;
case 'D':
if (diff_rev2 != NULL || diff_date2 != NULL)
error (1, 0,
"no more than two revisions/dates can be specified");
if (diff_rev1 != NULL || diff_date1 != NULL)
diff_date2 = Make_Date (optarg);
else
diff_date1 = Make_Date (optarg);
break;
case 'N':
empty_files = 1;
break;
case '?':
default:
usage (diff_usage);
break;
}
}
argc -= optind;
argv += optind;
if (!options)
options = xstrdup ("");
#ifdef CLIENT_SUPPORT
if (current_parsed_root->isremote) {
start_server ();
ign_setup ();
if (local)
send_arg("-l");
if (empty_files)
send_arg("-N");
send_option_string (opts);
if (options[0] != '\0')
send_arg (options);
if (diff_rev1)
option_with_arg ("-r", diff_rev1);
if (diff_date1)
client_senddate (diff_date1);
if (diff_rev2)
option_with_arg ("-r", diff_rev2);
if (diff_date2)
client_senddate (diff_date2);
send_arg ("--");
if (diff_rev2 == NULL && diff_date2 == NULL)
send_files (argc, argv, local, 0, 0);
else
send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
send_file_names (argc, argv, SEND_EXPAND_WILD);
send_to_server ("diff\012", 0);
err = get_responses_and_close ();
free (options);
options = NULL;
return (err);
}
#endif
if (diff_rev1 != NULL)
tag_check_valid (diff_rev1, argc, argv, local, 0, "");
if (diff_rev2 != NULL)
tag_check_valid (diff_rev2, argc, argv, local, 0, "");
which = W_LOCAL;
if (diff_rev1 != NULL || diff_date1 != NULL)
which |= W_REPOS | W_ATTIC;
wrap_setup ();
err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc,
diff_dirleaveproc, NULL, argc, argv, local,
which, 0, CVS_LOCK_READ, (char *) NULL, 1,
(char *) NULL);
free (options);
options = NULL;
if (diff_date1 != NULL)
free (diff_date1);
if (diff_date2 != NULL)
free (diff_date2);
return (err);
}
static int
diff_fileproc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
int status, err = 2;
Vers_TS *vers;
enum diff_file empty_file = DIFF_DIFFERENT;
char *tmp = NULL;
char *tocvsPath = NULL;
char *fname = NULL;
char *label1;
char *label2;
char *rev1_cache = NULL;
user_file_rev = 0;
vers = Version_TS (finfo, NULL, NULL, NULL, 1, 0);
if (diff_rev2 != NULL || diff_date2 != NULL)
{
}
else if (vers->vn_user == NULL)
{
if ((diff_rev1 != NULL || diff_date1 != NULL)
&& vers->srcfile != NULL)
{
if (empty_files)
empty_file = DIFF_REMOVED;
else
{
int exists;
exists = 0;
if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
{
char *head =
(vers->vn_rcs == NULL
? NULL
: RCS_branch_head (vers->srcfile, vers->vn_rcs));
exists = head != NULL && !RCS_isdead(vers->srcfile, head);
if (head != NULL)
free (head);
}
else
{
Vers_TS *xvers;
xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1,
1, 0);
exists = xvers->vn_rcs != NULL && !RCS_isdead(xvers->srcfile, xvers->vn_rcs);
freevers_ts (&xvers);
}
if (exists)
error (0, 0,
"%s no longer exists, no comparison available",
finfo->fullname);
goto out;
}
}
else
{
error (0, 0, "I know nothing about %s", finfo->fullname);
goto out;
}
}
else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
{
int exists = 0;
if (vers->srcfile != NULL)
{
if ((diff_rev1 != NULL || diff_date1 != NULL))
{
if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
{
char *head =
(vers->vn_rcs == NULL
? NULL
: RCS_branch_head (vers->srcfile, vers->vn_rcs));
exists = head != NULL && !RCS_isdead(vers->srcfile, head);
if (head != NULL)
free (head);
}
else
{
Vers_TS *xvers;
xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1,
1, 0);
exists = xvers->vn_rcs != NULL
&& !RCS_isdead (xvers->srcfile, xvers->vn_rcs);
freevers_ts (&xvers);
}
}
else
{
}
}
if (!exists)
{
if (empty_files)
empty_file = DIFF_ADDED;
else
{
error (0, 0, "%s is a new entry, no comparison available",
finfo->fullname);
goto out;
}
}
}
else if (vers->vn_user[0] == '-')
{
if (empty_files)
empty_file = DIFF_REMOVED;
else
{
error (0, 0, "%s was removed, no comparison available",
finfo->fullname);
goto out;
}
}
else
{
if (vers->vn_rcs == NULL && vers->srcfile == NULL)
{
error (0, 0, "cannot find revision control file for %s",
finfo->fullname);
goto out;
}
else
{
if (vers->ts_user == NULL)
{
error (0, 0, "cannot find %s", finfo->fullname);
goto out;
}
else if (!strcmp (vers->ts_user, vers->ts_rcs))
{
user_file_rev = vers->vn_user;
}
}
}
empty_file = diff_file_nodiff( finfo, vers, empty_file, &rev1_cache );
if( empty_file == DIFF_SAME )
{
err = 0;
goto out;
}
else if( empty_file == DIFF_ERROR )
goto out;
cvs_output ("Index: ", 0);
cvs_output (finfo->fullname, 0);
cvs_output ("\n", 1);
tocvsPath = wrap_tocvs_process_file(finfo->file);
if( tocvsPath != NULL )
{
fname = xmalloc (strlen (finfo->file)
+ sizeof CVSADM
+ sizeof CVSPREFIX
+ 10);
sprintf(fname,"%s/%s%s",CVSADM, CVSPREFIX, finfo->file);
if (unlink_file_dir (fname) < 0)
if (! existence_error (errno))
error (1, errno, "cannot remove %s", fname);
rename_file (finfo->file, fname);
copy_file (tocvsPath, finfo->file);
}
label1 = NULL;
label2 = NULL;
if (!have_rev1_label)
{
if (empty_file == DIFF_ADDED)
label1 =
make_file_label (DEVNULL, NULL, NULL);
else
label1 =
make_file_label (finfo->fullname, use_rev1,
vers ? vers->srcfile : NULL);
}
if (!have_rev2_label)
{
if (empty_file == DIFF_REMOVED)
label2 =
make_file_label (DEVNULL, NULL, NULL);
else
label2 =
make_file_label (finfo->fullname, use_rev2,
vers ? vers->srcfile : NULL);
}
if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED)
{
cvs_output ("\
===================================================================\n\
RCS file: ", 0);
cvs_output (finfo->fullname, 0);
cvs_output ("\n", 1);
cvs_output ("diff -N ", 0);
cvs_output (finfo->fullname, 0);
cvs_output ("\n", 1);
if (empty_file == DIFF_ADDED)
{
if (use_rev2 == NULL)
status = diff_exec (DEVNULL, finfo->file, label1, label2, opts,
RUN_TTY);
else
{
int retcode;
tmp = cvs_temp_name ();
retcode = RCS_checkout (vers->srcfile, (char *) NULL,
use_rev2, (char *) NULL,
(*options
? options
: vers->options),
tmp, (RCSCHECKOUTPROC) NULL,
(void *) NULL);
if( retcode != 0 )
goto out;
status = diff_exec (DEVNULL, tmp, label1, label2, opts, RUN_TTY);
}
}
else
{
int retcode;
tmp = cvs_temp_name ();
retcode = RCS_checkout (vers->srcfile, (char *) NULL,
use_rev1, (char *) NULL,
*options ? options : vers->options,
tmp, (RCSCHECKOUTPROC) NULL,
(void *) NULL);
if (retcode != 0)
goto out;
status = diff_exec (tmp, DEVNULL, label1, label2, opts, RUN_TTY);
}
}
else
{
status = RCS_exec_rcsdiff(vers->srcfile, opts,
*options ? options : vers->options,
use_rev1, rev1_cache, use_rev2,
label1, label2,
finfo->file);
}
if (label1) free (label1);
if (label2) free (label2);
switch (status)
{
case -1:
error (1, errno, "fork failed while diffing %s",
vers->srcfile->path);
case 0:
err = 0;
break;
default:
err = status;
break;
}
out:
if( tocvsPath != NULL )
{
if (unlink_file_dir (finfo->file) < 0)
if (! existence_error (errno))
error (1, errno, "cannot remove %s", finfo->file);
rename_file (fname, finfo->file);
if (unlink_file (tocvsPath) < 0)
error (1, errno, "cannot remove %s", tocvsPath);
free (fname);
}
if( tmp != NULL )
{
if (CVS_UNLINK(tmp) < 0)
error (0, errno, "cannot remove %s", tmp);
free (tmp);
}
if( rev1_cache != NULL )
{
if( CVS_UNLINK( rev1_cache ) < 0 )
error( 0, errno, "cannot remove %s", rev1_cache );
free( rev1_cache );
}
freevers_ts (&vers);
diff_mark_errors (err);
return err;
}
static void
diff_mark_errors (err)
int err;
{
if (err > diff_errors)
diff_errors = err;
}
static Dtype
diff_dirproc (callerdat, dir, pos_repos, update_dir, entries)
void *callerdat;
const char *dir;
const char *pos_repos;
const char *update_dir;
List *entries;
{
if (!isdir (dir))
return (R_SKIP_ALL);
if (!quiet)
error (0, 0, "Diffing %s", update_dir);
return (R_PROCESS);
}
static int
diff_filesdoneproc (callerdat, err, repos, update_dir, entries)
void *callerdat;
int err;
const char *repos;
const char *update_dir;
List *entries;
{
return (diff_errors);
}
static int
diff_dirleaveproc (callerdat, dir, err, update_dir, entries)
void *callerdat;
const char *dir;
int err;
const char *update_dir;
List *entries;
{
return (diff_errors);
}
static enum diff_file
diff_file_nodiff( finfo, vers, empty_file, rev1_cache )
struct file_info *finfo;
Vers_TS *vers;
enum diff_file empty_file;
char **rev1_cache;
{
Vers_TS *xvers;
int retcode;
if (use_rev1)
free (use_rev1);
if (use_rev2)
free (use_rev2);
use_rev1 = use_rev2 = (char *) NULL;
if (diff_rev1 || diff_date1)
{
if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
{
if (vers->vn_rcs != NULL && vers->srcfile != NULL)
use_rev1 = RCS_branch_head (vers->srcfile, vers->vn_rcs);
}
else
{
xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, 1, 0);
if (xvers->vn_rcs != NULL)
use_rev1 = xstrdup (xvers->vn_rcs);
freevers_ts (&xvers);
}
}
if (diff_rev2 || diff_date2)
{
if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD) == 0)
{
if (vers->vn_rcs != NULL && vers->srcfile != NULL)
use_rev2 = RCS_branch_head (vers->srcfile, vers->vn_rcs);
}
else
{
xvers = Version_TS (finfo, NULL, diff_rev2, diff_date2, 1, 0);
if (xvers->vn_rcs != NULL)
use_rev2 = xstrdup (xvers->vn_rcs);
freevers_ts (&xvers);
}
if( use_rev1 == NULL || RCS_isdead( vers->srcfile, use_rev1 ) )
{
if( use_rev2 == NULL || RCS_isdead( vers->srcfile, use_rev2 ) )
return DIFF_SAME;
if( empty_files )
return DIFF_ADDED;
if( use_rev1 != NULL )
{
if (diff_rev1)
{
error( 0, 0,
"Tag %s refers to a dead (removed) revision in file `%s'.",
diff_rev1, finfo->fullname );
}
else
{
error( 0, 0,
"Date %s refers to a dead (removed) revision in file `%s'.",
diff_date1, finfo->fullname );
}
error( 0, 0,
"No comparison available. Pass `-N' to `%s diff'?",
program_name );
}
else if (diff_rev1)
error (0, 0, "tag %s is not in file %s", diff_rev1,
finfo->fullname);
else
error (0, 0, "no revision for date %s in file %s",
diff_date1, finfo->fullname);
return DIFF_ERROR;
}
assert( use_rev1 != NULL );
if( use_rev2 == NULL || RCS_isdead( vers->srcfile, use_rev2 ) )
{
if (empty_files)
return DIFF_REMOVED;
if( use_rev2 != NULL )
{
if (diff_rev2)
{
error( 0, 0,
"Tag %s refers to a dead (removed) revision in file `%s'.",
diff_rev2, finfo->fullname );
}
else
{
error( 0, 0,
"Date %s refers to a dead (removed) revision in file `%s'.",
diff_date2, finfo->fullname );
}
error( 0, 0,
"No comparison available. Pass `-N' to `%s diff'?",
program_name );
}
else if (diff_rev2)
error (0, 0, "tag %s is not in file %s", diff_rev2,
finfo->fullname);
else
error (0, 0, "no revision for date %s in file %s",
diff_date2, finfo->fullname);
return DIFF_ERROR;
}
assert( use_rev2 != NULL );
if( strcmp (use_rev1, use_rev2) == 0 )
return DIFF_SAME;
}
assert (!(diff_rev2 || diff_date2) || (use_rev1 && use_rev2));
if ((diff_rev1 || diff_date1) &&
(use_rev1 == NULL || RCS_isdead (vers->srcfile, use_rev1)))
{
if (empty_files)
{
if (empty_file == DIFF_REMOVED)
return DIFF_SAME;
if( user_file_rev && use_rev2 == NULL )
use_rev2 = xstrdup( user_file_rev );
return DIFF_ADDED;
}
if( use_rev1 != NULL )
{
if (diff_rev1)
{
error( 0, 0,
"Tag %s refers to a dead (removed) revision in file `%s'.",
diff_rev1, finfo->fullname );
}
else
{
error( 0, 0,
"Date %s refers to a dead (removed) revision in file `%s'.",
diff_date1, finfo->fullname );
}
error( 0, 0,
"No comparison available. Pass `-N' to `%s diff'?",
program_name );
}
else if ( diff_rev1 )
error( 0, 0, "tag %s is not in file %s", diff_rev1,
finfo->fullname );
else
error( 0, 0, "no revision for date %s in file %s",
diff_date1, finfo->fullname );
return DIFF_ERROR;
}
assert( !diff_rev1 || use_rev1 );
if (user_file_rev)
{
if (!use_rev1)
use_rev1 = xstrdup (user_file_rev);
else if (!use_rev2)
use_rev2 = xstrdup (user_file_rev);
user_file_rev = NULL;
}
if( use_rev1 && use_rev2)
{
if (strcmp (use_rev1, use_rev2) == 0)
return DIFF_SAME;
}
else if( use_rev1 == NULL
|| ( vers->vn_user != NULL
&& strcmp( use_rev1, vers->vn_user ) == 0 ) )
{
if (empty_file == DIFF_DIFFERENT
&& vers->ts_user != NULL
&& strcmp (vers->ts_rcs, vers->ts_user) == 0
&& (!(*options) || strcmp (options, vers->options) == 0))
{
return DIFF_SAME;
}
if (use_rev1 == NULL
&& (vers->vn_user[0] != '0' || vers->vn_user[1] != '\0'))
{
if (vers->vn_user[0] == '-')
use_rev1 = xstrdup (vers->vn_user + 1);
else
use_rev1 = xstrdup (vers->vn_user);
}
}
if (empty_file != DIFF_DIFFERENT)
return empty_file;
retcode = RCS_cmp_file( vers->srcfile, use_rev1, rev1_cache,
use_rev2, *options ? options : vers->options,
finfo->file );
return retcode == 0 ? DIFF_SAME : DIFF_DIFFERENT;
}