Merge import conflicts

This commit is contained in:
Peter Wemm 1997-05-15 22:52:26 +00:00
parent 02e03a8b5f
commit 625799437a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=25843
4 changed files with 3623 additions and 1170 deletions

View File

@ -16,46 +16,159 @@
#include "cvs.h"
static Dtype diff_dirproc PROTO((char *dir, char *pos_repos, char *update_dir));
static int diff_filesdoneproc PROTO((int err, char *repos, char *update_dir));
static int diff_dirleaveproc PROTO((char *dir, int err, char *update_dir));
static int diff_file_nodiff PROTO((char *file, char *repository, List *entries,
RCSNode *rcs, Vers_TS *vers));
static int diff_fileproc PROTO((struct file_info *finfo));
enum diff_file
{
DIFF_ERROR,
DIFF_ADDED,
DIFF_REMOVED,
DIFF_DIFFERENT,
DIFF_SAME
};
static Dtype diff_dirproc PROTO ((void *callerdat, char *dir,
char *pos_repos, 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,
Vers_TS *vers,
enum diff_file));
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;
#ifdef SERVER_SUPPORT
/* Revision of the user file, if it is unchanged from something in the
repository and we want to use that fact. */
static char *user_file_rev;
#endif
static char *options;
static char opts[PATH_MAX];
static char *opts;
static size_t opts_allocated = 1;
static int diff_errors;
static int empty_files = 0;
/* FIXME: should be documenting all the options here. They don't
perfectly match rcsdiff options (for example, we always support
--ifdef and --context, but rcsdiff only does if diff does). */
static const char *const diff_usage[] =
{
"Usage: %s %s [-lN] [rcsdiff-options]\n",
#ifdef CVS_DIFFDATE
"Usage: %s %s [-lNR] [rcsdiff-options]\n",
" [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n",
#else
" [-r rev1 [-r rev2]] [files...] \n",
#endif
"\t-l\tLocal directory only, not recursive\n",
"\t-R\tProcess directories recursively.\n",
"\t-D d1\tDiff revision for date against working file.\n",
"\t-D d2\tDiff rev1/date1 against date2.\n",
"\t-N\tinclude diffs for added and removed files.\n",
"\t-r rev1\tDiff revision for rev1 against working file.\n",
"\t-r rev2\tDiff rev1/date1 against rev2.\n",
"\t--ifdef=arg\tOutput diffs in ifdef format.\n",
"(consult the documentation for your diff program for rcsdiff-options.\n",
"The most popular is -c for context diffs but there are many more).\n",
NULL
};
/* I copied this array directly out of diff.c in diffutils 2.7, after
removing the following entries, none of which seem relevant to use
with CVS:
--help
--version
--recursive
--unidirectional-new-file
--starting-file
--exclude
--exclude-from
--sdiff-merge-assist
I changed the options which take optional arguments (--context and
--unified) to return a number rather than a letter, so that the
optional argument could be handled more easily. I changed the
--paginate and --brief options to return a number, since -l and -q
mean something else to cvs diff.
The numbers 129- that appear in the fourth element of some entries
tell the big switch in `diff' how to process those options. -- Ian
The following options, which diff lists as "An alias, no longer
recommended" have been removed: --file-label --entire-new-file
--ascii --print. */
static struct option const longopts[] =
{
{"ignore-blank-lines", 0, 0, 'B'},
{"context", 2, 0, 143},
{"ifdef", 1, 0, 147},
{"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'},
{"paginate", 0, 0, 144},
{"rcs", 0, 0, 'n'},
{"show-c-function", 0, 0, 'p'},
/* This is a potentially very useful option, except the output is so
silly. It would be much better for it to look like "cvs rdiff -s"
which displays all the same info, minus quite a few lines of
extraneous garbage. */
{"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}
};
static void strcat_and_allocate PROTO ((char **, size_t *, const char *));
/* *STR is a pointer to a malloc'd string. *LENP is its allocated
length. Add SRC to the end of it, reallocating if necessary. */
static void
strcat_and_allocate (str, lenp, src)
char **str;
size_t *lenp;
const char *src;
{
size_t new_size;
new_size = strlen (*str) + strlen (src) + 1;
if (*str == NULL || new_size >= *lenp)
{
while (new_size >= *lenp)
*lenp *= 2;
*str = xrealloc (*str, *lenp);
}
strcat (*str, src);
}
int
diff (argc, argv)
int argc;
@ -65,6 +178,7 @@ diff (argc, argv)
int c, err = 0;
int local = 0;
int which;
int option_index;
if (argc == -1)
usage (diff_usage);
@ -74,37 +188,56 @@ diff (argc, argv)
* intercept the -r arguments for doing revision diffs; and -l/-R for a
* non-recursive/recursive diff.
*/
#ifdef SERVER_SUPPORT
/* Need to be able to do this command more than once (according to
the protocol spec, even if the current client doesn't use it). */
/* For server, need to be able to do this command more than once
(according to the protocol spec, even if the current client
doesn't use it). */
if (opts == NULL)
{
opts_allocated = 1;
opts = xmalloc (opts_allocated);
}
opts[0] = '\0';
#endif
optind = 1;
while ((c = getopt (argc, argv,
"abcdefhilnpqtuw0123456789BHNQRTC:D:F:I:L:V:k:r:")) != -1)
while ((c = getopt_long (argc, argv,
"+abcdefhilnpstuwy0123456789BHNRTC:D:F:I:L:U:V:W:k:r:",
longopts, &option_index)) != -1)
{
switch (c)
{
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'h': case 'i': case 'n': case 'p': 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': case 'Q':
case 'h': case 'i': case 'n': case 'p': case 's': case 't':
case 'u': case 'w': case 'y': 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);
(void) strcat (opts, tmp);
if (c == 'Q')
{
quiet = 1;
really_quiet = 1;
c = 'q';
}
strcat_and_allocate (&opts, &opts_allocated, tmp);
break;
case 'C': case 'F': case 'I': case 'L': case 'V':
#ifndef CVS_DIFFDATE
case 'D':
#endif
(void) sprintf (tmp, " -%c%s", (char) c, optarg);
(void) strcat (opts, tmp);
case 'C': case 'F': case 'I': case 'L': case 'U': case 'V':
case 'W':
(void) sprintf (tmp, " -%c", (char) c);
strcat_and_allocate (&opts, &opts_allocated, tmp);
strcat_and_allocate (&opts, &opts_allocated, optarg);
break;
case 147:
/* --ifdef. */
strcat_and_allocate (&opts, &opts_allocated, " -D");
strcat_and_allocate (&opts, &opts_allocated, optarg);
break;
case 129: case 130: case 131: 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 144: case 145: case 146:
strcat_and_allocate (&opts, &opts_allocated, " --");
strcat_and_allocate (&opts, &opts_allocated,
longopts[option_index].name);
if (longopts[option_index].has_arg == 1
|| (longopts[option_index].has_arg == 2
&& optarg != NULL))
{
strcat_and_allocate (&opts, &opts_allocated, "=");
strcat_and_allocate (&opts, &opts_allocated, optarg);
}
break;
case 'R':
local = 0;
@ -112,9 +245,6 @@ diff (argc, argv)
case 'l':
local = 1;
break;
case 'q':
quiet = 1;
break;
case 'k':
if (options)
free (options);
@ -129,7 +259,6 @@ diff (argc, argv)
else
diff_rev1 = optarg;
break;
#ifdef CVS_DIFFDATE
case 'D':
if (diff_rev2 != NULL || diff_date2 != NULL)
error (1, 0,
@ -139,7 +268,6 @@ diff (argc, argv)
else
diff_date1 = Make_Date (optarg);
break;
#endif
case 'N':
empty_files = 1;
break;
@ -168,6 +296,8 @@ diff (argc, argv)
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)
@ -178,15 +308,12 @@ diff (argc, argv)
client_senddate (diff_date2);
send_file_names (argc, argv, SEND_EXPAND_WILD);
#if 0
/* FIXME: We shouldn't have to send current files to diff two
revs, but it doesn't work yet and I haven't debugged it.
So send the files -- it's slower but it works.
gnu@cygnus.com Apr94 */
/* Send the current files unless diffing two revs from the archive */
if (diff_rev2 == NULL && diff_date2 == NULL)
#endif
send_files (argc, argv, local, 0);
send_files (argc, argv, local, 0, 0);
else
send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
send_to_server ("diff\012", 0);
err = get_responses_and_close ();
@ -201,15 +328,15 @@ diff (argc, argv)
tag_check_valid (diff_rev2, argc, argv, local, 0, "");
which = W_LOCAL;
if (diff_rev2 != NULL || diff_date2 != NULL)
if (diff_rev1 != NULL || diff_date1 != NULL)
which |= W_REPOS | W_ATTIC;
wrap_setup ();
/* start the recursion processor */
err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc,
diff_dirleaveproc, argc, argv, local,
which, 0, 1, (char *) NULL, 1, 0);
diff_dirleaveproc, NULL, argc, argv, local,
which, 0, 1, (char *) NULL, 1);
/* clean up */
free (options);
@ -221,26 +348,19 @@ diff (argc, argv)
*/
/* ARGSUSED */
static int
diff_fileproc (finfo)
diff_fileproc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
int status, err = 2; /* 2 == trouble, like rcsdiff */
Vers_TS *vers;
enum {
DIFF_ERROR,
DIFF_ADDED,
DIFF_REMOVED,
DIFF_NEITHER
} empty_file = DIFF_NEITHER;
char tmp[L_tmpnam+1];
enum diff_file empty_file = DIFF_DIFFERENT;
char *tmp;
char *tocvsPath;
char fname[PATH_MAX];
char *fname;
#ifdef SERVER_SUPPORT
user_file_rev = 0;
#endif
vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL,
finfo->file, 1, 0, finfo->entries, finfo->rcs);
vers = Version_TS (finfo, NULL, NULL, NULL, 1, 0);
if (diff_rev2 != NULL || diff_date2 != NULL)
{
@ -249,10 +369,46 @@ diff_fileproc (finfo)
}
else if (vers->vn_user == NULL)
{
error (0, 0, "I know nothing about %s", finfo->fullname);
freevers_ts (&vers);
diff_mark_errors (err);
return (err);
/* The file does not exist in the working directory. */
if ((diff_rev1 != NULL || diff_date1 != NULL)
&& vers->srcfile != NULL)
{
/* The file does exist in the repository. */
if (empty_files)
empty_file = DIFF_REMOVED;
else
{
int exists;
exists = 0;
/* special handling for TAG_HEAD */
if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
exists = vers->vn_rcs != NULL;
else
{
Vers_TS *xvers;
xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1,
1, 0);
exists = xvers->vn_rcs != NULL;
freevers_ts (&xvers);
}
if (exists)
error (0, 0,
"%s no longer exists, no comparison available",
finfo->fullname);
freevers_ts (&vers);
diff_mark_errors (err);
return (err);
}
}
else
{
error (0, 0, "I know nothing about %s", finfo->fullname);
freevers_ts (&vers);
diff_mark_errors (err);
return (err);
}
}
else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
{
@ -299,7 +455,6 @@ diff_fileproc (finfo)
diff_mark_errors (err);
return (err);
}
#ifdef SERVER_SUPPORT
else if (!strcmp (vers->ts_user, vers->ts_rcs))
{
/* The user file matches some revision in the repository
@ -307,18 +462,76 @@ diff_fileproc (finfo)
have a copy of the user file around). */
user_file_rev = vers->vn_user;
}
#endif
}
}
if (empty_file == DIFF_NEITHER && diff_file_nodiff (finfo->file, finfo->repository, finfo->entries, finfo->rcs, vers))
empty_file = diff_file_nodiff (finfo, vers, empty_file);
if (empty_file == DIFF_SAME || empty_file == DIFF_ERROR)
{
freevers_ts (&vers);
return (0);
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);
}
}
/* FIXME: Check whether use_rev1 and use_rev2 are dead and deal
accordingly. */
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);
}
}
}
/* Output an "Index:" line for patch to use */
(void) fflush (stdout);
@ -329,10 +542,14 @@ diff_fileproc (finfo)
if (tocvsPath)
{
/* Backup the current version of the file to CVS/,,filename */
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", finfo->file);
error (1, errno, "cannot remove %s", fname);
rename_file (finfo->file, fname);
/* Copy the wrapped file to the current directory then go to work */
copy_file (tocvsPath, finfo->file);
@ -348,23 +565,44 @@ diff_fileproc (finfo)
if (empty_file == DIFF_ADDED)
{
run_setup ("%s %s %s %s", DIFF, opts, DEVNULL, finfo->file);
if (use_rev2 == NULL)
run_setup ("%s %s %s %s", DIFF, opts, DEVNULL, finfo->file);
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 == -1)
{
(void) CVS_UNLINK (tmp);
error (1, errno, "fork failed during checkout of %s",
vers->srcfile->path);
}
/* FIXME: what if retcode > 0? */
run_setup ("%s %s %s %s", DIFF, opts, DEVNULL, tmp);
}
}
else
{
int retcode;
/*
* FIXME: Should be setting use_rev1 using the logic in
* diff_file_nodiff, and using that revision. This code
* is broken for "cvs diff -N -r foo".
*/
retcode = RCS_checkout (vers->srcfile->path, NULL, vers->vn_rcs,
*options ? options : vers->options, tmpnam (tmp),
0, 0);
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 == -1)
{
(void) unlink (tmp);
(void) CVS_UNLINK (tmp);
error (1, errno, "fork failed during checkout of %s",
vers->srcfile->path);
}
@ -409,13 +647,18 @@ diff_fileproc (finfo)
if (! existence_error (errno))
error (1, errno, "cannot remove %s", finfo->file);
rename_file (fname,finfo->file);
rename_file (fname, finfo->file);
if (unlink_file (tocvsPath) < 0)
error (1, errno, "cannot remove %s", finfo->file);
error (1, errno, "cannot remove %s", tocvsPath);
free (fname);
}
if (empty_file == DIFF_REMOVED)
(void) unlink (tmp);
if (empty_file == DIFF_REMOVED
|| (empty_file == DIFF_ADDED && use_rev2 != NULL))
{
(void) CVS_UNLINK (tmp);
free (tmp);
}
(void) fflush (stdout);
freevers_ts (&vers);
@ -441,10 +684,12 @@ diff_mark_errors (err)
*/
/* ARGSUSED */
static Dtype
diff_dirproc (dir, pos_repos, update_dir)
diff_dirproc (callerdat, dir, pos_repos, update_dir, entries)
void *callerdat;
char *dir;
char *pos_repos;
char *update_dir;
List *entries;
{
/* XXX - check for dirs we don't want to process??? */
@ -462,10 +707,12 @@ diff_dirproc (dir, pos_repos, update_dir)
*/
/* ARGSUSED */
static int
diff_filesdoneproc (err, repos, update_dir)
diff_filesdoneproc (callerdat, err, repos, update_dir, entries)
void *callerdat;
int err;
char *repos;
char *update_dir;
List *entries;
{
return (diff_errors);
}
@ -475,27 +722,26 @@ diff_filesdoneproc (err, repos, update_dir)
*/
/* ARGSUSED */
static int
diff_dirleaveproc (dir, err, update_dir)
diff_dirleaveproc (callerdat, dir, err, update_dir, entries)
void *callerdat;
char *dir;
int err;
char *update_dir;
List *entries;
{
return (diff_errors);
}
/*
* verify that a file is different 0=same 1=different
* verify that a file is different
*/
static int
diff_file_nodiff (file, repository, entries, rcs, vers)
char *file;
char *repository;
List *entries;
RCSNode *rcs;
static enum diff_file
diff_file_nodiff (finfo, vers, empty_file)
struct file_info *finfo;
Vers_TS *vers;
enum diff_file empty_file;
{
Vers_TS *xvers;
char tmp[L_tmpnam+1];
int retcode;
/* free up any old use_rev* variables and reset 'em */
@ -512,23 +758,9 @@ diff_file_nodiff (file, repository, entries, rcs, vers)
use_rev1 = xstrdup (vers->vn_rcs);
else
{
xvers = Version_TS (repository, (char *) NULL, diff_rev1,
diff_date1, file, 1, 0, entries, rcs);
if (xvers->vn_rcs == NULL)
{
/* Don't gripe if it doesn't exist, just ignore! */
if (! isfile (file))
/* null statement */ ;
else if (diff_rev1)
error (0, 0, "tag %s is not in file %s", diff_rev1, file);
else
error (0, 0, "no revision for date %s in file %s",
diff_date1, file);
freevers_ts (&xvers);
return (1);
}
use_rev1 = xstrdup (xvers->vn_rcs);
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);
}
}
@ -539,36 +771,85 @@ diff_file_nodiff (file, repository, entries, rcs, vers)
use_rev2 = xstrdup (vers->vn_rcs);
else
{
xvers = Version_TS (repository, (char *) NULL, diff_rev2,
diff_date2, file, 1, 0, entries, rcs);
if (xvers->vn_rcs == NULL)
{
/* Don't gripe if it doesn't exist, just ignore! */
if (! isfile (file))
/* null statement */ ;
else if (diff_rev1)
error (0, 0, "tag %s is not in file %s", diff_rev2, file);
else
error (0, 0, "no revision for date %s in file %s",
diff_date2, file);
freevers_ts (&xvers);
return (1);
}
use_rev2 = xstrdup (xvers->vn_rcs);
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)
{
/* 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)
/* 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. */
return DIFF_SAME;
else if (empty_files)
return DIFF_ADDED;
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;
}
if (use_rev2 == NULL)
{
/* 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;
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;
}
/* now, see if we really need to do the diff */
if (use_rev1 && use_rev2) {
return (strcmp (use_rev1, use_rev2) == 0);
} else {
error(0, 0, "No HEAD revision for file %s", file);
return (1);
if (strcmp (use_rev1, use_rev2) == 0)
return DIFF_SAME;
else
return DIFF_DIFFERENT;
}
if ((diff_rev1 || diff_date1) && use_rev1 == NULL)
{
/* The first revision does not exist, and no second revision
was given. */
if (empty_files)
{
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;
}
}
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;
}
}
#ifdef SERVER_SUPPORT
if (user_file_rev)
if (user_file_rev)
{
/* drop user_file_rev into first unused use_rev */
if (!use_rev1)
@ -582,42 +863,45 @@ diff_file_nodiff (file, repository, entries, rcs, vers)
/* now, see if we really need to do the diff */
if (use_rev1 && use_rev2)
{
return (strcmp (use_rev1, use_rev2) == 0);
if (strcmp (use_rev1, use_rev2) == 0)
return DIFF_SAME;
else
return DIFF_DIFFERENT;
}
#endif /* SERVER_SUPPORT */
if (use_rev1 == NULL || strcmp (use_rev1, vers->vn_user) == 0)
if (use_rev1 == NULL
|| (vers->vn_user != NULL && strcmp (use_rev1, vers->vn_user) == 0))
{
if (strcmp (vers->ts_rcs, vers->ts_user) == 0 &&
(!(*options) || strcmp (options, vers->options) == 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 (1);
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 (use_rev1 == NULL)
use_rev1 = xstrdup (vers->vn_user);
}
/* If we already know that the file is being added or removed,
then we don't want to do an actual file comparison here. */
if (empty_file != DIFF_DIFFERENT)
return empty_file;
/*
* with 0 or 1 -r option specified, run a quick diff to see if we
* should bother with it at all.
*/
retcode = RCS_checkout (vers->srcfile->path, NULL, use_rev1,
*options ? options : vers->options, tmpnam (tmp), 0, 0);
switch (retcode)
{
case 0: /* everything ok */
if (xcmp (file, tmp) == 0)
{
(void) unlink (tmp);
return (1);
}
break;
case -1: /* fork failed */
(void) unlink (tmp);
error (1, errno, "fork failed during checkout of %s",
vers->srcfile->path);
default:
break;
}
(void) unlink (tmp);
return (0);
retcode = RCS_cmp_file (vers->srcfile, use_rev1,
*options ? options : vers->options,
finfo->file);
return retcode == 0 ? DIFF_SAME : DIFF_DIFFERENT;
}

File diff suppressed because it is too large Load Diff

View File

@ -7,13 +7,14 @@
#include "cvs.h"
#include "savecwd.h"
#include "getline.h"
#ifndef DBLKSIZ
#define DBLKSIZ 4096 /* since GNU ndbm doesn't define it */
#endif
static int checkout_file PROTO((char *file, char *temp));
static void make_tempfile PROTO((char *temp));
static char *make_tempfile PROTO((void));
static void rename_rcsfile PROTO((char *temp, char *real));
#ifndef MY_NDBM
@ -28,38 +29,50 @@ struct admin_file {
/* This is a one line description of what the file is for. It is not
currently used, although one wonders whether it should be, somehow.
If NULL, then don't process this file in mkmodules (FIXME: a bit of
If NULL, then don't process this file in mkmodules (FIXME?: a bit of
a kludge; probably should replace this with a flags field). */
char *errormsg;
/* Contents which the file should have in a new repository. To avoid
problems with brain-dead compilers which choke on long string constants,
this is a pointer to an array of char * terminated by NULL--each of
the strings is concatenated. */
the strings is concatenated.
If this field is NULL, the file is not created in a new
repository, but it can be added with "cvs add" (just as if one
had created the repository with a version of CVS which didn't
know about the file) and the checked-out copy will be updated
without having to add it to checkoutlist. */
const char * const *contents;
};
static const char *const loginfo_contents[] = {
"# The \"loginfo\" file is used to control where \"cvs commit\" log information\n",
"# is sent. The first entry on a line is a regular expression which is tested\n",
"# against the directory that the change is being made to, relative to the\n",
"# $CVSROOT. For the first match that is found, then the remainder of the\n",
"# line is a filter program that should expect log information on its standard\n",
"# input.\n",
"# The \"loginfo\" file controls where \"cvs commit\" log information\n",
"# is sent. The first entry on a line is a regular expression which must match\n",
"# the directory that the change is being made to, relative to the\n",
"# $CVSROOT. If a match is found, then the remainder of the line is a filter\n",
"# program that should expect log information on its standard input.\n",
"#\n",
"# If the repository name does not match any of the regular expressions in the\n",
"# first field of this file, the \"DEFAULT\" line is used, if it is specified.\n",
"# If the repository name does not match any of the regular expressions in this\n",
"# file, the \"DEFAULT\" line is used, if it is specified.\n",
"#\n",
"# If the name \"ALL\" appears as a regular expression it is always used\n",
"# in addition to the first matching regex or \"DEFAULT\".\n",
"# If the name ALL appears as a regular expression it is always used\n",
"# in addition to the first matching regex or DEFAULT.\n",
"#\n",
"# The filter program may use one and only one \"%s\" modifier (ala printf). If\n",
"# such a \"%s\" is specified in the filter program, a brief title is included\n",
"# (as one argument, enclosed in single quotes) showing the relative directory\n",
"# name and listing the modified file names.\n",
"# You may specify a format string as part of the\n",
"# filter. The string is composed of a `%' followed\n",
"# by a single format character, or followed by a set of format\n",
"# characters surrounded by `{' and `}' as separators. The format\n",
"# characters are:\n",
"#\n",
"# s = file name\n",
"# V = old version number (pre-checkin)\n",
"# v = new version number (post-checkin)\n",
"#\n",
"# For example:\n",
"#DEFAULT (echo \"\"; who am i; date; cat) >> $CVSROOT/CVSROOT/commitlog\n",
"#DEFAULT (echo \"\"; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog\n",
"# or\n",
"#DEFAULT (echo \"\"; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog\n",
NULL
};
@ -99,8 +112,33 @@ static const char *const editinfo_contents[] = {
"# Actions such as mailing a copy of the report to each reviewer are\n",
"# better handled by an entry in the loginfo file.\n",
"#\n",
"# One thing that should be noted is the the ALL keyword is not\n",
"# supported. There can be only one entry that matches a given\n",
"# One thing that should be noted is the the ALL keyword is not\n",
"# supported. There can be only one entry that matches a given\n",
"# repository.\n",
NULL
};
static const char *const verifymsg_contents[] = {
"# The \"verifymsg\" file is used to allow verification of logging\n",
"# information. It works best when a template (as specified in the\n",
"# rcsinfo file) is provided for the logging procedure. Given a\n",
"# template with locations for, a bug-id number, a list of people who\n",
"# reviewed the code before it can be checked in, and an external\n",
"# process to catalog the differences that were code reviewed, the\n",
"# following test can be applied to the code:\n",
"#\n",
"# Making sure that the entered bug-id number is correct.\n",
"# Validating that the code that was reviewed is indeed the code being\n",
"# checked in (using the bug-id number or a seperate review\n",
"# number to identify this particular code set.).\n",
"#\n",
"# If any of the above test failed, then the commit would be aborted.\n",
"#\n",
"# Actions such as mailing a copy of the report to each reviewer are\n",
"# better handled by an entry in the loginfo file.\n",
"#\n",
"# One thing that should be noted is the the ALL keyword is not\n",
"# supported. There can be only one entry that matches a given\n",
"# repository.\n",
NULL
};
@ -222,6 +260,9 @@ static const char *const modules_contents[] = {
"# -d dir Place module in directory \"dir\" instead of module name.\n",
"# -l Top-level directory only -- do not recurse.\n",
"#\n",
"# NOTE: If you change any of the \"Run\" options above, you'll have to\n",
"# release and re-checkout any working directories of these modules.\n",
"#\n",
"# And \"directory\" is a path to a directory relative to $CVSROOT.\n",
"#\n",
"# The \"-a\" option specifies an alias. An alias is interpreted as if\n",
@ -244,6 +285,9 @@ static const struct admin_file filelist[] = {
{CVSROOTADM_EDITINFO,
"a %s file can be used to validate log messages",
editinfo_contents},
{CVSROOTADM_VERIFYMSG,
"a %s file can be used to validate log messages",
verifymsg_contents},
{CVSROOTADM_COMMITINFO,
"a %s file can be used to configure 'cvs commit' checking",
commitinfo_contents},
@ -266,6 +310,20 @@ static const struct admin_file filelist[] = {
/* modules is special-cased in mkmodules. */
NULL,
modules_contents},
{CVSROOTADM_READERS,
"a %s file specifies read-only users",
NULL},
{CVSROOTADM_WRITERS,
"a %s file specifies read/write users",
NULL},
/* Some have suggested listing CVSROOTADM_PASSWD here too. The
security implications of transmitting hashed passwords over the
net are no worse than transmitting cleartext passwords which pserver
does, so this isn't a problem. But I'm worried about the implications
of storing old passwords--if someone used a password in the past
they might be using it elsewhere, using a similar password, etc,
and so it doesn't seem to me like we should be saving old passwords,
even hashed. */
{NULL, NULL}
};
@ -275,27 +333,26 @@ mkmodules (dir)
char *dir;
{
struct saved_cwd cwd;
/* FIXME: arbitrary limit */
char temp[PATH_MAX];
char *temp;
char *cp, *last, *fname;
#ifdef MY_NDBM
DBM *db;
#endif
FILE *fp;
/* FIXME: arbitrary limit */
char line[512];
char *line = NULL;
size_t line_allocated = 0;
const struct admin_file *fileptr;
if (save_cwd (&cwd))
exit (EXIT_FAILURE);
error_exit ();
if (chdir (dir) < 0)
if ( CVS_CHDIR (dir) < 0)
error (1, errno, "cannot chdir to %s", dir);
/*
* First, do the work necessary to update the "modules" database.
*/
make_tempfile (temp);
temp = make_tempfile ();
switch (checkout_file (CVSROOTADM_MODULES, temp))
{
@ -313,7 +370,7 @@ mkmodules (dir)
case -1: /* fork failed */
(void) unlink_file (temp);
exit (EXIT_FAILURE);
error (1, errno, "cannot check out %s", CVSROOTADM_MODULES);
/* NOTREACHED */
default:
@ -324,12 +381,13 @@ mkmodules (dir)
} /* switch on checkout_file() */
(void) unlink_file (temp);
free (temp);
/* Checkout the files that need it in CVSROOT dir */
for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) {
if (fileptr->errormsg == NULL)
continue;
make_tempfile (temp);
temp = make_tempfile ();
if (checkout_file (fileptr->filename, temp) == 0)
rename_rcsfile (temp, fileptr->filename);
#if 0
@ -344,10 +402,10 @@ mkmodules (dir)
error (0, 0, fileptr->errormsg, fileptr->filename);
#endif
(void) unlink_file (temp);
free (temp);
}
/* Use 'fopen' instead of 'open_file' because we want to ignore error */
fp = fopen (CVSROOTADM_CHECKOUTLIST, "r");
fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r");
if (fp)
{
/*
@ -356,7 +414,7 @@ mkmodules (dir)
*
* comment lines begin with '#'
*/
while (fgets (line, sizeof (line), fp) != NULL)
while (getline (&line, &line_allocated, fp) >= 0)
{
/* skip lines starting with # */
if (line[0] == '#')
@ -374,7 +432,7 @@ mkmodules (dir)
;
*cp = '\0';
make_tempfile (temp);
temp = make_tempfile ();
if (checkout_file (fname, temp) == 0)
{
rename_rcsfile (temp, fname);
@ -386,12 +444,24 @@ mkmodules (dir)
if (cp < last && *cp)
error (0, 0, cp, fname);
}
free (temp);
}
(void) fclose (fp);
if (line)
free (line);
if (ferror (fp))
error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST);
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST);
}
else
{
/* Error from CVS_FOPEN. */
if (!existence_error (errno))
error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST);
}
if (restore_cwd (&cwd, NULL))
exit (EXIT_FAILURE);
error_exit ();
free_cwd (&cwd);
return (0);
@ -400,25 +470,27 @@ mkmodules (dir)
/*
* Yeah, I know, there are NFS race conditions here.
*/
static void
make_tempfile (temp)
char *temp;
static char *
make_tempfile ()
{
static int seed = 0;
int fd;
char *temp;
if (seed == 0)
seed = getpid ();
temp = xmalloc (sizeof (BAKPREFIX) + 40);
while (1)
{
(void) sprintf (temp, "%s%d", BAKPREFIX, seed++);
if ((fd = open (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1)
if ((fd = CVS_OPEN (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1)
break;
if (errno != EEXIST)
error (1, errno, "cannot create temporary file %s", temp);
}
if (close(fd) < 0)
error(1, errno, "cannot close temporary file %s", temp);
return temp;
}
static int
@ -426,18 +498,31 @@ checkout_file (file, temp)
char *file;
char *temp;
{
char rcs[PATH_MAX];
char *rcs;
RCSNode *rcsnode;
int retcode = 0;
(void) sprintf (rcs, "%s%s", file, RCSEXT);
if (noexec)
return 0;
rcs = xmalloc (strlen (file) + 5);
strcpy (rcs, file);
strcat (rcs, RCSEXT);
if (!isfile (rcs))
return (1);
run_setup ("%s%s -x,v/ -q -p", Rcsbin, RCS_CO);
run_arg (rcs);
if ((retcode = run_exec (RUN_TTY, temp, RUN_TTY, RUN_NORMAL)) != 0)
{
error (0, retcode == -1 ? errno : 0, "failed to check out %s file", file);
free (rcs);
return (1);
}
rcsnode = RCS_parsercsfile (rcs);
retcode = RCS_checkout (rcsnode, NULL, NULL, NULL, NULL, temp,
(RCSCHECKOUTPROC) NULL, (void *) NULL);
if (retcode != 0)
{
error (0, retcode == -1 ? errno : 0, "failed to check out %s file",
file);
}
freercsnode (&rcsnode);
free (rcs);
return (retcode);
}
@ -568,12 +653,12 @@ rename_dbmfile (temp)
(void) unlink_file (bakdir); /* rm .#modules.dir .#modules.pag */
(void) unlink_file (bakpag);
(void) unlink_file (bakdb);
(void) rename (dotdir, bakdir); /* mv modules.dir .#modules.dir */
(void) rename (dotpag, bakpag); /* mv modules.pag .#modules.pag */
(void) rename (dotdb, bakdb); /* mv modules.db .#modules.db */
(void) rename (newdir, dotdir); /* mv "temp".dir modules.dir */
(void) rename (newpag, dotpag); /* mv "temp".pag modules.pag */
(void) rename (newdb, dotdb); /* mv "temp".db modules.db */
(void) CVS_RENAME (dotdir, bakdir); /* mv modules.dir .#modules.dir */
(void) CVS_RENAME (dotpag, bakpag); /* mv modules.pag .#modules.pag */
(void) CVS_RENAME (dotdb, bakdb); /* mv modules.db .#modules.db */
(void) CVS_RENAME (newdir, dotdir); /* mv "temp".dir modules.dir */
(void) CVS_RENAME (newpag, dotpag); /* mv "temp".pag modules.pag */
(void) CVS_RENAME (newdb, dotdb); /* mv "temp".db modules.db */
/* OK -- make my day */
SIG_endCrSect ();
@ -586,21 +671,25 @@ rename_rcsfile (temp, real)
char *temp;
char *real;
{
char bak[50];
char *bak;
struct stat statbuf;
char rcs[PATH_MAX];
char *rcs;
/* Set "x" bits if set in original. */
rcs = xmalloc (strlen (real) + sizeof (RCSEXT) + 10);
(void) sprintf (rcs, "%s%s", real, RCSEXT);
statbuf.st_mode = 0; /* in case rcs file doesn't exist, but it should... */
(void) stat (rcs, &statbuf);
(void) CVS_STAT (rcs, &statbuf);
free (rcs);
if (chmod (temp, 0444 | (statbuf.st_mode & 0111)) < 0)
error (0, errno, "warning: cannot chmod %s", temp);
bak = xmalloc (strlen (real) + sizeof (BAKPREFIX) + 10);
(void) sprintf (bak, "%s%s", BAKPREFIX, real);
(void) unlink_file (bak); /* rm .#loginfo */
(void) rename (real, bak); /* mv loginfo .#loginfo */
(void) rename (temp, real); /* mv "temp" loginfo */
(void) CVS_RENAME (real, bak); /* mv loginfo .#loginfo */
(void) CVS_RENAME (temp, real); /* mv "temp" loginfo */
free (bak);
}
const char *const init_usage[] = {
@ -608,26 +697,6 @@ const char *const init_usage[] = {
NULL
};
/* Create directory NAME if it does not already exist; fatal error for
other errors. FIXME: This should be in filesubr.c or thereabouts,
probably. Perhaps it should be further abstracted, though (for example
to handle CVSUMASK where appropriate?). */
static void
mkdir_if_needed (name)
char *name;
{
if (CVS_MKDIR (name, 0777) < 0)
{
if (errno != EEXIST
#ifdef EACCESS
/* OS/2; see longer comment in client.c. */
&& errno != EACCESS
#endif
)
error (1, errno, "cannot mkdir %s", name);
}
}
int
init (argc, argv)
int argc;
@ -647,6 +716,7 @@ init (argc, argv)
if (argc == -1 || argc > 1)
usage (init_usage);
#ifdef CLIENT_SUPPORT
if (client_active)
{
start_server ();
@ -655,21 +725,22 @@ init (argc, argv)
send_init_command ();
return get_responses_and_close ();
}
#endif /* CLIENT_SUPPORT */
/* Note: we do *not* create parent directories as needed like the
old cvsinit.sh script did. Few utilities do that, and a
non-existent parent directory is as likely to be a typo as something
which needs to be created. */
mkdir_if_needed (CVSroot);
mkdir_if_needed (CVSroot_directory);
adm = xmalloc (strlen (CVSroot) + sizeof (CVSROOTADM) + 10);
strcpy (adm, CVSroot);
adm = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + 10);
strcpy (adm, CVSroot_directory);
strcat (adm, "/");
strcat (adm, CVSROOTADM);
mkdir_if_needed (adm);
/* This is needed by the call to "ci" below. */
if (chdir (adm) < 0)
if ( CVS_CHDIR (adm) < 0)
error (1, errno, "cannot change to directory %s", adm);
/* 80 is long enough for all the administrative file names, plus

File diff suppressed because it is too large Load Diff