summaryrefslogtreecommitdiff
path: root/contrib/cvs/src/diff.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/diff.c')
-rw-r--r--contrib/cvs/src/diff.c436
1 files changed, 251 insertions, 185 deletions
diff --git a/contrib/cvs/src/diff.c b/contrib/cvs/src/diff.c
index 88af6511fdc1a..2e7aea2a4a143 100644
--- a/contrib/cvs/src/diff.c
+++ b/contrib/cvs/src/diff.c
@@ -14,6 +14,7 @@
* files.
*/
+#include <assert.h>
#include "cvs.h"
enum diff_file
@@ -25,18 +26,21 @@ enum diff_file
DIFF_SAME
};
-static Dtype diff_dirproc PROTO ((void *callerdat, char *dir,
- char *pos_repos, char *update_dir,
- List *entries));
+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,
- char *repos, char *update_dir,
- List *entries));
-static int diff_dirleaveproc PROTO ((void *callerdat, char *dir,
- int err, char *update_dir,
- List *entries));
-static enum diff_file diff_file_nodiff PROTO ((struct file_info *finfo,
+ 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));
+ 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));
@@ -270,6 +274,13 @@ diff (argc, argv)
diff_date2 = NULL;
optind = 0;
+ /* FIXME: This should really be allocating an argv to be passed to diff
+ * later rather than strcatting onto the opts variable. We have some
+ * handling routines that can already handle most of the argc/argv
+ * maintenance for us and currently, if anyone were to attempt to pass a
+ * quoted string in here, it would be split on spaces and tabs on its way
+ * to diff.
+ */
while ((c = getopt_long (argc, argv,
"+abcdefhilnpstuwy0123456789BHNRTC:D:F:I:L:U:W:k:r:",
longopts, &option_index)) != -1)
@@ -422,7 +433,8 @@ diff (argc, argv)
/* start the recursion processor */
err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc,
diff_dirleaveproc, NULL, argc, argv, local,
- which, 0, CVS_LOCK_READ, (char *) NULL, 1);
+ which, 0, CVS_LOCK_READ, (char *) NULL, 1,
+ (char *) NULL);
/* clean up */
free (options);
@@ -448,16 +460,12 @@ diff_fileproc (callerdat, finfo)
int status, err = 2; /* 2 == trouble, like rcsdiff */
Vers_TS *vers;
enum diff_file empty_file = DIFF_DIFFERENT;
- char *tmp;
- char *tocvsPath;
- char *fname;
+ char *tmp = NULL;
+ char *tocvsPath = NULL;
+ char *fname = NULL;
char *label1;
char *label2;
-
- /* Initialize these solely to avoid warnings from gcc -Wall about
- variables that might be used uninitialized. */
- tmp = NULL;
- fname = NULL;
+ char *rev1_cache = NULL;
user_file_rev = 0;
vers = Version_TS (finfo, NULL, NULL, NULL, 1, 0);
@@ -505,30 +513,69 @@ diff_fileproc (callerdat, finfo)
error (0, 0,
"%s no longer exists, no comparison available",
finfo->fullname);
- freevers_ts (&vers);
- diff_mark_errors (err);
- return (err);
+ goto out;
}
}
else
{
error (0, 0, "I know nothing about %s", finfo->fullname);
- freevers_ts (&vers);
- diff_mark_errors (err);
- return (err);
+ goto out;
}
}
else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
{
- if (empty_files)
- empty_file = DIFF_ADDED;
- else
+ /* The file was added locally. */
+ int exists = 0;
+
+ if (vers->srcfile != NULL)
{
- error (0, 0, "%s is a new entry, no comparison available",
- finfo->fullname);
- freevers_ts (&vers);
- diff_mark_errors (err);
- return (err);
+ /* The file does exist in the repository. */
+
+ if ((diff_rev1 != NULL || diff_date1 != NULL))
+ {
+ /* special handling for TAG_HEAD */
+ 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
+ {
+ /* The file was added locally, but an RCS archive exists. Our
+ * base revision must be dead.
+ */
+ /* No need to set, exists = 0, here. That's the default. */
+ }
+ }
+ if (!exists)
+ {
+ /* If we got here, then either the RCS archive does not exist or
+ * the relevant revision is dead.
+ */
+ 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] == '-')
@@ -539,9 +586,7 @@ diff_fileproc (callerdat, finfo)
{
error (0, 0, "%s was removed, no comparison available",
finfo->fullname);
- freevers_ts (&vers);
- diff_mark_errors (err);
- return (err);
+ goto out;
}
}
else
@@ -550,18 +595,14 @@ diff_fileproc (callerdat, finfo)
{
error (0, 0, "cannot find revision control file for %s",
finfo->fullname);
- freevers_ts (&vers);
- diff_mark_errors (err);
- return (err);
+ goto out;
}
else
{
if (vers->ts_user == NULL)
{
error (0, 0, "cannot find %s", finfo->fullname);
- freevers_ts (&vers);
- diff_mark_errors (err);
- return (err);
+ goto out;
}
else if (!strcmp (vers->ts_user, vers->ts_rcs))
{
@@ -573,73 +614,21 @@ diff_fileproc (callerdat, finfo)
}
}
- empty_file = diff_file_nodiff (finfo, vers, empty_file);
- if (empty_file == DIFF_SAME || empty_file == DIFF_ERROR)
+ empty_file = diff_file_nodiff( finfo, vers, empty_file, &rev1_cache );
+ if( empty_file == DIFF_SAME )
{
- freevers_ts (&vers);
- if (empty_file == DIFF_SAME)
- {
- /* In the server case, would be nice to send a "Checked-in"
- response, so that the client can rewrite its timestamp.
- server_checked_in by itself isn't the right thing (it
- needs a server_register), but I'm not sure what is.
- It isn't clear to me how "cvs status" handles this (that
- is, for a client which sends Modified not Is-modified to
- "cvs status"), but it does. */
- return (0);
- }
- else
- {
- diff_mark_errors (err);
- return (err);
- }
- }
-
- if (empty_file == DIFF_DIFFERENT)
- {
- int dead1, dead2;
-
- if (use_rev1 == NULL)
- dead1 = 0;
- else
- dead1 = RCS_isdead (vers->srcfile, use_rev1);
- if (use_rev2 == NULL)
- dead2 = 0;
- else
- dead2 = RCS_isdead (vers->srcfile, use_rev2);
-
- if (dead1 && dead2)
- {
- freevers_ts (&vers);
- return (0);
- }
- else if (dead1)
- {
- if (empty_files)
- empty_file = DIFF_ADDED;
- else
- {
- error (0, 0, "%s is a new entry, no comparison available",
- finfo->fullname);
- freevers_ts (&vers);
- diff_mark_errors (err);
- return (err);
- }
- }
- else if (dead2)
- {
- if (empty_files)
- empty_file = DIFF_REMOVED;
- else
- {
- error (0, 0, "%s was removed, no comparison available",
- finfo->fullname);
- freevers_ts (&vers);
- diff_mark_errors (err);
- return (err);
- }
- }
+ /* In the server case, would be nice to send a "Checked-in"
+ response, so that the client can rewrite its timestamp.
+ server_checked_in by itself isn't the right thing (it
+ needs a server_register), but I'm not sure what is.
+ It isn't clear to me how "cvs status" handles this (that
+ is, for a client which sends Modified not Is-modified to
+ "cvs status"), but it does. */
+ err = 0;
+ goto out;
}
+ else if( empty_file == DIFF_ERROR )
+ goto out;
/* Output an "Index:" line for patch to use */
cvs_output ("Index: ", 0);
@@ -647,7 +636,7 @@ diff_fileproc (callerdat, finfo)
cvs_output ("\n", 1);
tocvsPath = wrap_tocvs_process_file(finfo->file);
- if (tocvsPath)
+ if( tocvsPath != NULL )
{
/* Backup the current version of the file to CVS/,,filename */
fname = xmalloc (strlen (finfo->file)
@@ -676,7 +665,8 @@ diff_fileproc (callerdat, finfo)
make_file_label (DEVNULL, NULL, NULL);
else
label1 =
- make_file_label (finfo->fullname, use_rev1, vers ? vers->srcfile : NULL);
+ make_file_label (finfo->fullname, use_rev1,
+ vers ? vers->srcfile : NULL);
}
if (!have_rev2_label)
@@ -686,7 +676,8 @@ diff_fileproc (callerdat, finfo)
make_file_label (DEVNULL, NULL, NULL);
else
label2 =
- make_file_label (finfo->fullname, use_rev2, vers ? vers->srcfile : NULL);
+ make_file_label (finfo->fullname, use_rev2,
+ vers ? vers->srcfile : NULL);
}
if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED)
@@ -709,7 +700,8 @@ RCS file: ", 0);
if (empty_file == DIFF_ADDED)
{
if (use_rev2 == NULL)
- status = diff_exec (DEVNULL, finfo->file, label1, label2, opts, RUN_TTY);
+ status = diff_exec (DEVNULL, finfo->file, label1, label2, opts,
+ RUN_TTY);
else
{
int retcode;
@@ -722,11 +714,8 @@ RCS file: ", 0);
: vers->options),
tmp, (RCSCHECKOUTPROC) NULL,
(void *) NULL);
- if (retcode != 0)
- {
- diff_mark_errors (err);
- return err;
- }
+ if( retcode != 0 )
+ goto out;
status = diff_exec (DEVNULL, tmp, label1, label2, opts, RUN_TTY);
}
@@ -742,26 +731,24 @@ RCS file: ", 0);
tmp, (RCSCHECKOUTPROC) NULL,
(void *) NULL);
if (retcode != 0)
- {
- diff_mark_errors (err);
- return err;
- }
+ 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, use_rev2,
- label1, label2,
- finfo->file);
-
- if (label1) free (label1);
- if (label2) free (label2);
+ 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: /* fork failed */
@@ -775,7 +762,8 @@ RCS file: ", 0);
break;
}
- if (tocvsPath)
+out:
+ if( tocvsPath != NULL )
{
if (unlink_file_dir (finfo->file) < 0)
if (! existence_error (errno))
@@ -787,17 +775,25 @@ RCS file: ", 0);
free (fname);
}
- if (empty_file == DIFF_REMOVED
- || (empty_file == DIFF_ADDED && use_rev2 != NULL))
+ /* Call CVS_UNLINK() rather than unlink_file() below to avoid the check
+ * for noexec.
+ */
+ if( tmp != NULL )
{
- if (CVS_UNLINK (tmp) < 0)
+ 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);
+ return err;
}
/*
@@ -820,9 +816,9 @@ diff_mark_errors (err)
static Dtype
diff_dirproc (callerdat, dir, pos_repos, update_dir, entries)
void *callerdat;
- char *dir;
- char *pos_repos;
- char *update_dir;
+ const char *dir;
+ const char *pos_repos;
+ const char *update_dir;
List *entries;
{
/* XXX - check for dirs we don't want to process??? */
@@ -844,8 +840,8 @@ static int
diff_filesdoneproc (callerdat, err, repos, update_dir, entries)
void *callerdat;
int err;
- char *repos;
- char *update_dir;
+ const char *repos;
+ const char *update_dir;
List *entries;
{
return (diff_errors);
@@ -858,9 +854,9 @@ diff_filesdoneproc (callerdat, err, repos, update_dir, entries)
static int
diff_dirleaveproc (callerdat, dir, err, update_dir, entries)
void *callerdat;
- char *dir;
+ const char *dir;
int err;
- char *update_dir;
+ const char *update_dir;
List *entries;
{
return (diff_errors);
@@ -870,10 +866,13 @@ diff_dirleaveproc (callerdat, dir, err, update_dir, entries)
* verify that a file is different
*/
static enum diff_file
-diff_file_nodiff (finfo, vers, empty_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; /* Cache the content of rev1 if we have to look
+ * it up.
+ */
{
Vers_TS *xvers;
int retcode;
@@ -889,9 +888,10 @@ diff_file_nodiff (finfo, vers, empty_file)
{
/* special handling for TAG_HEAD */
if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
- use_rev1 = ((vers->vn_rcs == NULL || vers->srcfile == NULL)
- ? NULL
- : RCS_branch_head (vers->srcfile, vers->vn_rcs));
+ {
+ 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);
@@ -904,9 +904,10 @@ diff_file_nodiff (finfo, vers, empty_file)
{
/* special handling for TAG_HEAD */
if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD) == 0)
- use_rev2 = ((vers->vn_rcs == NULL || vers->srcfile == NULL)
- ? NULL
- : RCS_branch_head (vers->srcfile, vers->vn_rcs));
+ {
+ 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);
@@ -915,19 +916,39 @@ diff_file_nodiff (finfo, vers, empty_file)
freevers_ts (&xvers);
}
- if (use_rev1 == NULL)
+ if( use_rev1 == NULL || RCS_isdead( vers->srcfile, use_rev1 ) )
{
/* The first revision does not exist. If EMPTY_FILES is
true, treat this as an added file. Otherwise, warn
about the missing tag. */
- if (use_rev2 == NULL)
+ if( use_rev2 == NULL || RCS_isdead( vers->srcfile, use_rev2 ) )
/* At least in the case where DIFF_REV1 and DIFF_REV2
- are both numeric, we should be returning some kind
- of error (see basicb-8a0 in testsuite). The symbolic
- case may be more complicated. */
+ * are both numeric (and non-existant (NULL), as opposed to
+ * dead?), we should be returning some kind of error (see
+ * basicb-8a0 in testsuite). The symbolic case may be more
+ * complicated.
+ */
return DIFF_SAME;
- else if (empty_files)
+ 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);
@@ -937,13 +958,32 @@ diff_file_nodiff (finfo, vers, empty_file)
return DIFF_ERROR;
}
- if (use_rev2 == NULL)
+ assert( use_rev1 != NULL );
+ if( use_rev2 == NULL || RCS_isdead( vers->srcfile, use_rev2 ) )
{
/* The second revision does not exist. If EMPTY_FILES is
true, treat this as a removed file. Otherwise warn
about the missing tag. */
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);
@@ -952,15 +992,23 @@ diff_file_nodiff (finfo, vers, empty_file)
diff_date2, finfo->fullname);
return DIFF_ERROR;
}
-
- /* now, see if we really need to do the diff */
- if (strcmp (use_rev1, use_rev2) == 0)
+ /* Now, see if we really need to do the diff. We can't assume that the
+ * files are different when the revs are.
+ */
+ assert( use_rev2 != NULL );
+ if( strcmp (use_rev1, use_rev2) == 0 )
return DIFF_SAME;
- else
- return DIFF_DIFFERENT;
+ /* else fall through and do the diff */
}
- if ((diff_rev1 || diff_date1) && use_rev1 == NULL)
+ /* If we had a r1/d1 & r2/d2, then at this point we must have a C3P0...
+ * err... ok, then both rev1 & rev2 must have resolved to an existing,
+ * live version due to if statement we just closed.
+ */
+ assert (!(diff_rev2 || diff_date2) || (use_rev1 && use_rev2));
+
+ if ((diff_rev1 || diff_date1) &&
+ (use_rev1 == NULL || RCS_isdead (vers->srcfile, use_rev1)))
{
/* The first revision does not exist, and no second revision
was given. */
@@ -968,25 +1016,39 @@ diff_file_nodiff (finfo, vers, empty_file)
{
if (empty_file == DIFF_REMOVED)
return DIFF_SAME;
- else
- {
- if (user_file_rev && use_rev2 == NULL)
- use_rev2 = xstrdup (user_file_rev);
- return DIFF_ADDED;
- }
+ if( user_file_rev && use_rev2 == NULL )
+ use_rev2 = xstrdup( user_file_rev );
+ return DIFF_ADDED;
}
- else
+ if( use_rev1 != NULL )
{
if (diff_rev1)
- error (0, 0, "tag %s is not in file %s", diff_rev1,
- finfo->fullname);
+ {
+ error( 0, 0,
+ "Tag %s refers to a dead (removed) revision 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;
+ {
+ 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)
{
/* drop user_file_rev into first unused use_rev */
@@ -995,20 +1057,25 @@ diff_file_nodiff (finfo, vers, empty_file)
else if (!use_rev2)
use_rev2 = xstrdup (user_file_rev);
/* and if not, it wasn't needed anyhow */
- user_file_rev = 0;
+ user_file_rev = NULL;
}
- /* now, see if we really need to do the diff */
- if (use_rev1 && use_rev2)
+ /* Now, see if we really need to do the diff. We can't assume that the
+ * files are different when the revs are.
+ */
+ if( use_rev1 && use_rev2)
{
if (strcmp (use_rev1, use_rev2) == 0)
return DIFF_SAME;
- else
- return DIFF_DIFFERENT;
+ /* Fall through and do the diff. */
}
-
- if (use_rev1 == NULL
- || (vers->vn_user != NULL && strcmp (use_rev1, vers->vn_user) == 0))
+ /* Don't want to do the timestamp check with both use_rev1 & use_rev2 set.
+ * The timestamp check is just for the default case of diffing the
+ * workspace file against its base revision.
+ */
+ 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
@@ -1033,13 +1100,12 @@ diff_file_nodiff (finfo, vers, empty_file)
return empty_file;
/*
- * with 0 or 1 -r option specified, run a quick diff to see if we
- * should bother with it at all.
+ * Run a quick cmp to see if we should bother with a full diff.
*/
- retcode = RCS_cmp_file (vers->srcfile, use_rev1,
- *options ? options : vers->options,
- finfo->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;
}