summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin/cvs/src
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/cvs/src')
-rw-r--r--gnu/usr.bin/cvs/src/checkout.c71
-rw-r--r--gnu/usr.bin/cvs/src/commit.c12
-rw-r--r--gnu/usr.bin/cvs/src/cvs.h7
-rw-r--r--gnu/usr.bin/cvs/src/ignore.c8
-rw-r--r--gnu/usr.bin/cvs/src/lock.c2
-rw-r--r--gnu/usr.bin/cvs/src/main.c65
-rw-r--r--gnu/usr.bin/cvs/src/patch.c13
-rw-r--r--gnu/usr.bin/cvs/src/rcs.c384
-rw-r--r--gnu/usr.bin/cvs/src/rcscmds.c2
-rw-r--r--gnu/usr.bin/cvs/src/server.c141
-rw-r--r--gnu/usr.bin/cvs/src/update.c75
11 files changed, 545 insertions, 235 deletions
diff --git a/gnu/usr.bin/cvs/src/checkout.c b/gnu/usr.bin/cvs/src/checkout.c
index 737cbdc8ea1..e36e729b819 100644
--- a/gnu/usr.bin/cvs/src/checkout.c
+++ b/gnu/usr.bin/cvs/src/checkout.c
@@ -44,10 +44,12 @@ static int safe_location PROTO((void));
static const char *const checkout_usage[] =
{
- "Usage:\n %s %s [-ANPcflnps] [-r rev | -D date] [-d dir] [-k kopt] modules...\n",
+ "Usage:\n %s %s [-ANPRcflnps] [-r rev | -D date] [-d dir]\n",
+ " [-j rev1] [-j rev2] [-k kopt] modules...\n",
"\t-A\tReset any sticky tags/date/kopts.\n",
"\t-N\tDon't shorten module paths if -d specified.\n",
"\t-P\tPrune empty directories.\n",
+ "\t-R\tProcess directories recursively.\n",
"\t-c\t\"cat\" the module database.\n",
"\t-f\tForce a head revision match if tag/date not found.\n",
"\t-l\tLocal directory only, not recursive\n",
@@ -64,14 +66,15 @@ static const char *const checkout_usage[] =
static const char *const export_usage[] =
{
- "Usage: %s %s [-NPfln] [-r rev | -D date] [-d dir] [-k kopt] module...\n",
+ "Usage: %s %s [-NRfln] [-r rev | -D date] [-d dir] [-k kopt] module...\n",
"\t-N\tDon't shorten module paths if -d specified.\n",
"\t-f\tForce a head revision match if tag/date not found.\n",
"\t-l\tLocal directory only, not recursive\n",
+ "\t-R\tProcess directories recursively (default).\n",
"\t-n\tDo not run module program (if any).\n",
- "\t-r rev\tCheck out revision or tag.\n",
- "\t-D date\tCheck out revisions as of date.\n",
- "\t-d dir\tCheck out into dir instead of module name.\n",
+ "\t-r rev\tExport revision or tag.\n",
+ "\t-D date\tExport revisions as of date.\n",
+ "\t-d dir\tExport into dir instead of module name.\n",
"\t-k kopt\tUse RCS kopt -k option on checkout.\n",
NULL
};
@@ -132,7 +135,7 @@ checkout (argc, argv)
ign_setup ();
wrap_setup ();
- optind = 1;
+ optind = 0;
while ((c = getopt (argc, argv, valid_options)) != -1)
{
switch (c)
@@ -218,8 +221,11 @@ checkout (argc, argv)
if (shorten == -1)
shorten = 0;
- if ((!(cat + status) && argc == 0) || ((cat + status) && argc != 0))
- usage (valid_usage);
+ if ((cat || status) && argc != 0)
+ error (1, 0, "-c and -s must not get any arguments");
+
+ if (!(cat || status) && argc == 0)
+ error (1, 0, "must specify at least one module or directory");
if (where && pipeout)
error (1, 0, "-d and -p are mutually exclusive");
@@ -227,20 +233,10 @@ checkout (argc, argv)
if (strcmp (command_name, "export") == 0)
{
if (!tag && !date)
- {
- error (0, 0, "must specify a tag or date");
- usage (valid_usage);
- }
+ error (1, 0, "must specify a tag or date");
+
if (tag && isdigit (tag[0]))
error (1, 0, "tag `%s' must be a symbolic tag", tag);
-/*
- * mhy 950615: -kv doesn't work for binaries with RCS keywords.
- * Instead use the default provided in the RCS file (-ko for binaries).
- */
-#if 0
- if (!options)
- options = RCS_check_kflag ("v");/* -kv is default */
-#endif
}
if (!safe_location()) {
@@ -276,10 +272,14 @@ checkout (argc, argv)
client_expand_modules (argc, argv, local);
}
- if (!run_module_prog) send_arg ("-n");
- if (local) send_arg ("-l");
- if (pipeout) send_arg ("-p");
- if (!force_tag_match) send_arg ("-f");
+ if (!run_module_prog)
+ send_arg ("-n");
+ if (local)
+ send_arg ("-l");
+ if (pipeout)
+ send_arg ("-p");
+ if (!force_tag_match)
+ send_arg ("-f");
if (aflag)
send_arg("-A");
if (!shorten)
@@ -290,16 +290,10 @@ checkout (argc, argv)
if (cat)
send_arg("-c");
if (where != NULL)
- {
option_with_arg ("-d", where);
- }
if (status)
send_arg("-s");
- /* Why not send -k for export? This would appear to make
- remote export differ from local export. FIXME. */
- if (strcmp (command_name, "export") != 0
- && options != NULL
- && options[0] != '\0')
+ if (options != NULL && options[0] != '\0')
send_arg (options);
option_with_arg ("-r", tag);
if (date)
@@ -332,6 +326,8 @@ checkout (argc, argv)
if (cat || status)
{
cat_module (status);
+ if (options)
+ free (options);
return (0);
}
db = open_module ();
@@ -343,8 +339,6 @@ checkout (argc, argv)
*/
if (argc > 1 && where != NULL)
{
- char *repository;
-
(void) CVS_MKDIR (where, 0777);
if ( CVS_CHDIR (where) < 0)
error (1, errno, "cannot chdir to %s", where);
@@ -352,6 +346,8 @@ checkout (argc, argv)
where = (char *) NULL;
if (!isfile (CVSADM))
{
+ char *repository;
+
repository = xmalloc (strlen (CVSroot_directory) + 80);
(void) sprintf (repository, "%s/%s/%s", CVSroot_directory,
CVSROOTADM, CVSNULLREPOS);
@@ -381,8 +377,8 @@ checkout (argc, argv)
server_set_entstat (preload_update_dir, repository);
#endif
}
+ free (repository);
}
- free (repository);
}
/* If we will be calling history_write, work out the name to pass
@@ -429,6 +425,8 @@ checkout (argc, argv)
where, shorten, local, run_module_prog,
(char *) NULL);
close_module (db);
+ if (options)
+ free (options);
return (err);
}
@@ -674,6 +672,10 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten,
*/
for (i = 1; i < *pargc; i++)/* free the old ones */
free (argv[i]);
+ /* FIXME: Normally one has to realloc argv to make sure
+ argv[1] exists. But this argv does not points to the
+ beginning of an allocated block. For now we allocate
+ at least 4 entries for argv (in line2argv). */
argv[1] = xstrdup (mfile); /* set up the new one */
*pargc = 2;
@@ -983,6 +985,7 @@ internal error: %s doesn't start with %s in checkout_proc",
free (line);
}
freevers_ts (&vers);
+ freercsnode (&finfo.rcs);
}
Entries_Close (entries);
diff --git a/gnu/usr.bin/cvs/src/commit.c b/gnu/usr.bin/cvs/src/commit.c
index 9d13391950b..c43e35f3538 100644
--- a/gnu/usr.bin/cvs/src/commit.c
+++ b/gnu/usr.bin/cvs/src/commit.c
@@ -332,7 +332,7 @@ commit (argc, argv)
}
#endif /* CVS_BADROOT */
- optind = 1;
+ optind = 0;
while ((c = getopt (argc, argv, "+nlRm:fF:r:")) != -1)
{
switch (c)
@@ -546,6 +546,16 @@ commit (argc, argv)
previous versions of client/server CVS, but it probably is a Good
Thing, or at least Not Such A Bad Thing. */
send_file_names (find_args.argc, find_args.argv, 0);
+
+ /* FIXME: This whole find_args.force/SEND_FORCE business is a
+ kludge. It would seem to be a server bug that we have to
+ say that files are modified when they are not. This makes
+ "cvs commit -r 2" across a whole bunch of files a very slow
+ operation (and it isn't documented in cvsclient.texi). I
+ haven't looked at the server code carefully enough to be
+ _sure_ why this is needed, but if it is because RCS_CI
+ wants the file to exist, then it would be relatively simple
+ (but not trivial) to fix in the server. */
send_files (find_args.argc, find_args.argv, local, 0,
find_args.force ? SEND_FORCE : 0);
diff --git a/gnu/usr.bin/cvs/src/cvs.h b/gnu/usr.bin/cvs/src/cvs.h
index d8a3ca3f9cb..a141ccf7580 100644
--- a/gnu/usr.bin/cvs/src/cvs.h
+++ b/gnu/usr.bin/cvs/src/cvs.h
@@ -423,10 +423,16 @@ void Subdir_Register PROTO((List *, const char *, const char *));
void Subdir_Deregister PROTO((List *, const char *, const char *));
char *Make_Date PROTO((char *rawdate));
char *Name_Repository PROTO((char *dir, char *update_dir));
+
+
char *Name_Root PROTO((char *dir, char *update_dir));
int parse_cvsroot PROTO((char *CVSroot));
void set_local_cvsroot PROTO((char *dir));
void Create_Root PROTO((char *dir, char *rootdir));
+void root_allow_add PROTO ((char *));
+void root_allow_free PROTO ((void));
+int root_allow_ok PROTO ((char *));
+
int same_directories PROTO((char *dir1, char *dir2));
char *Short_Repository PROTO((char *repository));
char *gca PROTO((char *rev1, char *rev2));
@@ -438,6 +444,7 @@ void *xrealloc PROTO((void *ptr, size_t bytes));
void expand_string PROTO ((char **, size_t *, size_t));
char *xstrdup PROTO((const char *str));
void strip_trailing_newlines PROTO((char *str));
+int pathname_levels PROTO ((char *path));
typedef int (*CALLPROC) PROTO((char *repository, char *value));
int Parse_Info PROTO((char *infofile, char *repository, CALLPROC callproc, int all));
diff --git a/gnu/usr.bin/cvs/src/ignore.c b/gnu/usr.bin/cvs/src/ignore.c
index 91b015d94d9..82fd84a6730 100644
--- a/gnu/usr.bin/cvs/src/ignore.c
+++ b/gnu/usr.bin/cvs/src/ignore.c
@@ -269,7 +269,9 @@ ign_name (name)
{
/* We do a case-insensitive match by calling fnmatch on copies of
the pattern and the name which have been converted to
- lowercase. */
+ lowercase. FIXME: would be much cleaner to just unify this
+ with the other case-insensitive fnmatch stuff (FOLD_FN_CHAR
+ in lib/fnmatch.c; os2_fnmatch in emx/system.c). */
char *name_lower;
char *pat_lower;
char *p;
@@ -282,7 +284,7 @@ ign_name (name)
pat_lower = xstrdup (*cpp++);
for (p = pat_lower; *p != '\0'; ++p)
*p = tolower (*p);
- if (fnmatch (pat_lower, name_lower, 0) == 0)
+ if (CVS_FNMATCH (pat_lower, name_lower, 0) == 0)
goto matched;
free (pat_lower);
}
@@ -296,7 +298,7 @@ ign_name (name)
else
{
while (*cpp)
- if (fnmatch (*cpp++, name, 0) == 0)
+ if (CVS_FNMATCH (*cpp++, name, 0) == 0)
return 1;
return 0;
}
diff --git a/gnu/usr.bin/cvs/src/lock.c b/gnu/usr.bin/cvs/src/lock.c
index 1ff6864e4d7..c47943c1e88 100644
--- a/gnu/usr.bin/cvs/src/lock.c
+++ b/gnu/usr.bin/cvs/src/lock.c
@@ -485,7 +485,7 @@ again:
errno = 0;
while ((dp = readdir (dirp)) != NULL)
{
- if (fnmatch (CVSRFLPAT, dp->d_name, 0) == 0)
+ if (CVS_FNMATCH (CVSRFLPAT, dp->d_name, 0) == 0)
{
#ifdef CVS_FUDGELOCKS
time_t now;
diff --git a/gnu/usr.bin/cvs/src/main.c b/gnu/usr.bin/cvs/src/main.c
index 8b5a7bb81b7..046bf9c3f8b 100644
--- a/gnu/usr.bin/cvs/src/main.c
+++ b/gnu/usr.bin/cvs/src/main.c
@@ -163,7 +163,7 @@ static const char *const cmd_usage[] =
" annotate Show last revision where each line was modified\n",
" checkout Checkout sources for editing\n",
" commit Check files into the repository\n",
- " diff Run diffs between revisions\n",
+ " diff Show differences between revisions\n",
" edit Get ready to edit a watched file\n",
" editors See who is editing a watched file\n",
" export Export sources from CVS, similar to checkout\n",
@@ -331,17 +331,23 @@ main (argc, argv)
const struct cmd *cm;
int c, err = 0;
int rcsbin_update_env, tmpdir_update_env, cvs_update_env;
+ int free_CVSroot = 0;
+ int free_Editor = 0;
+ int free_Tmpdir = 0;
+ int free_Rcsbin = 0;
+
int help = 0; /* Has the user asked for help? This
lets us support the `cvs -H cmd'
convention to give help for cmd. */
static struct option long_options[] =
- {
+ {
{"help", 0, NULL, 'H'},
{"version", 0, NULL, 'v'},
{"help-commands", 0, NULL, 1},
{"help-synonyms", 0, NULL, 2},
+ {"allow-root", required_argument, NULL, 3},
{0, 0, 0, 0}
- };
+ };
/* `getopt_long' stores the option index here, but right now we
don't use it. */
int option_index = 0;
@@ -414,11 +420,9 @@ main (argc, argv)
CVSUMASK_ENV, cp);
}
- /* I'm not sure whether this needs to be 1 instead of 0 anymore. Using
- 1 used to accomplish what passing "+" as the first character to
- the option string does, but that reason doesn't exist anymore. */
- optind = 1;
-
+ /* Set this to 0 to force getopt initialization. getopt() sets
+ this to 1 internally. */
+ optind = 0;
/* We have to parse the options twice because else there is no
chance to avoid reading the global options from ".cvsrc". Set
@@ -440,15 +444,15 @@ main (argc, argv)
if (use_cvsrc)
read_cvsrc (&argc, &argv, "cvs");
- optind = 1;
+ optind = 0;
opterr = 1;
while ((c = getopt_long
(argc, argv, "+Qqrwtnlvb:T:e:d:Hfz:s:x", long_options, &option_index))
!= EOF)
- {
+ {
switch (c)
- {
+ {
case 1:
/* --help-commands */
usage (cmd_usage);
@@ -457,6 +461,10 @@ main (argc, argv)
/* --help-synonyms */
usage (cmd_synonyms());
break;
+ case 3:
+ /* --allow-root */
+ root_allow_add (optarg);
+ break;
case 'Q':
really_quiet = TRUE;
/* FALL THROUGH */
@@ -491,18 +499,22 @@ main (argc, argv)
exit (0);
break;
case 'b':
- Rcsbin = optarg;
+ Rcsbin = xstrdup (optarg);
+ free_Rcsbin = 1;
rcsbin_update_env = 1; /* need to update environment */
break;
case 'T':
- Tmpdir = optarg;
+ Tmpdir = xstrdup (optarg);
+ free_Tmpdir = 1;
tmpdir_update_env = 1; /* need to update environment */
break;
case 'e':
- Editor = optarg;
+ Editor = xstrdup (optarg);
+ free_Editor = 1;
break;
case 'd':
- CVSroot = optarg;
+ CVSroot = xstrdup (optarg);
+ free_CVSroot = 1;
cvs_update_env = 1; /* need to update environment */
break;
case 'H':
@@ -610,6 +622,12 @@ main (argc, argv)
#if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT)
if (strcmp (command_name, "pserver") == 0)
{
+ /* The reason that --allow-root is not a command option
+ is mainly the comment in server() about how argc,argv
+ might be from .cvsrc. I'm not sure about that, and
+ I'm not sure it is only true of command options, but
+ it seems easier to make it a global option. */
+
/* Gets username and password from client, authenticates, then
switches to run as that user and sends an ACK back to the
client. */
@@ -877,15 +895,26 @@ main (argc, argv)
Lock_Cleanup ();
+ free (program_path);
+ if (free_CVSroot)
+ free (CVSroot);
+ if (free_Editor)
+ free (Editor);
+ if (free_Tmpdir)
+ free (Tmpdir);
+ if (free_Rcsbin)
+ free (Rcsbin);
+ root_allow_free ();
+
#ifdef SYSTEM_CLEANUP
/* Hook for OS-specific behavior, for example socket subsystems on
NT and OS2 or dealing with windows and arguments on Mac. */
SYSTEM_CLEANUP ();
#endif
- if (err)
- return (EXIT_FAILURE);
- return 0;
+ /* This is exit rather than return because apparently that keeps
+ some tools which check for memory leaks happier. */
+ exit (err ? EXIT_FAILURE : 0);
}
char *
diff --git a/gnu/usr.bin/cvs/src/patch.c b/gnu/usr.bin/cvs/src/patch.c
index ac82e4b65b9..194999be651 100644
--- a/gnu/usr.bin/cvs/src/patch.c
+++ b/gnu/usr.bin/cvs/src/patch.c
@@ -42,10 +42,11 @@ static int unidiff = 0;
static const char *const patch_usage[] =
{
- "Usage: %s %s [-fl] [-c|-u] [-s|-t] [-V %%d]\n",
+ "Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d]\n",
" -r rev|-D date [-r rev2 | -D date2] modules...\n",
"\t-f\tForce a head revision match if tag/date not found.\n",
"\t-l\tLocal directory only, not recursive\n",
+ "\t-R\tProcess directories recursively.\n",
"\t-c\tContext diffs (default)\n",
"\t-u\tUnidiff format.\n",
"\t-s\tShort patch - one liner per file.\n",
@@ -69,7 +70,7 @@ patch (argc, argv)
if (argc == -1)
usage (patch_usage);
- optind = 1;
+ optind = 0;
while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1)
{
switch (c)
@@ -513,7 +514,9 @@ patch_fileproc (callerdat, finfo)
memset ((char *) &t, 0, sizeof (t));
if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
(char *) 0, 0)) != -1)
- (void) utime (tmpfile1, &t);
+ /* I believe this timestamp only affects the dates in our diffs,
+ and therefore should be on the server, not the client. */
+ (void) utime (tmpfile1, &t);
}
else if (toptwo_diffs)
{
@@ -535,7 +538,9 @@ patch_fileproc (callerdat, finfo)
}
if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
(char *) 0, 0)) != -1)
- (void) utime (tmpfile2, &t);
+ /* I believe this timestamp only affects the dates in our diffs,
+ and therefore should be on the server, not the client. */
+ (void) utime (tmpfile2, &t);
}
run_setup ("%s -%c", DIFF, unidiff ? 'u' : 'c');
run_arg (tmpfile1);
diff --git a/gnu/usr.bin/cvs/src/rcs.c b/gnu/usr.bin/cvs/src/rcs.c
index a56f312c498..bc8ed0818ad 100644
--- a/gnu/usr.bin/cvs/src/rcs.c
+++ b/gnu/usr.bin/cvs/src/rcs.c
@@ -801,6 +801,8 @@ rcsvers_delproc (p)
free (rnode->next);
if (rnode->author != (char *) NULL)
free (rnode->author);
+ if (rnode->state != (char *) NULL)
+ free (rnode->state);
if (rnode->other != (List *) NULL)
dellist (&rnode->other);
free ((char *) rnode);
@@ -1046,8 +1048,13 @@ getrcsrev (fp, revp)
do {
c = getc (fp);
if (c == EOF)
+ {
/* FIXME: should be including filename in error message. */
- error (1, errno, "cannot read rcs file");
+ if (ferror (fp))
+ error (1, errno, "cannot read rcs file");
+ else
+ error (1, 0, "unexpected end of file reading rcs file");
+ }
} while (whitespace (c));
if (!(isdigit (c) || c == '.'))
@@ -1071,7 +1078,10 @@ getrcsrev (fp, revp)
if (c == EOF)
{
/* FIXME: should be including filename in error message. */
- error (1, errno, "cannot read rcs file");
+ if (ferror (fp))
+ error (1, errno, "cannot read rcs file");
+ else
+ error (1, 0, "unexpected end of file reading rcs file");
}
}
@@ -2668,7 +2678,7 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen)
/* Now SUB contains a string which is to replace the string
from SRCH to S. SUBLEN is the length of SUB. */
- if (sublen == s - srch)
+ if (srch + sublen == s)
{
memcpy (srch, sub, sublen);
free (sub);
@@ -2697,7 +2707,7 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen)
else
{
assert (srch >= ebuf_last->data);
- assert (srch - ebuf_last->data <= ebuf_last->len);
+ assert (srch <= ebuf_last->data + ebuf_last->len);
ebuf_len -= ebuf_last->len - (srch - ebuf_last->data);
ebuf_last->len = srch - ebuf_last->data;
}
@@ -3432,16 +3442,18 @@ linevector_init (vec)
vec->vector = NULL;
}
-static void linevector_add PROTO ((struct linevector *vec, char *text,
- size_t len, RCSVers *vers,
- unsigned int pos));
+static int linevector_add PROTO ((struct linevector *vec, char *text,
+ size_t len, RCSVers *vers,
+ unsigned int pos));
/* Given some text TEXT, add each of its lines to VEC before line POS
(where line 0 is the first line). The last line in TEXT may or may
not be \n terminated. All \n in TEXT are changed to \0 (FIXME: I
don't think this is needed, or used, now that we have the ->len
- field). Set the version for each of the new lines to VERS. */
-static void
+ field). Set the version for each of the new lines to VERS. This
+ function returns non-zero for success. It returns zero if the line
+ number is out of range. */
+static int
linevector_add (vec, text, len, vers, pos)
struct linevector *vec;
char *text;
@@ -3456,7 +3468,7 @@ linevector_add (vec, text, len, vers, pos)
struct line *lines;
if (len == 0)
- return;
+ return 1;
textend = text + len;
@@ -3484,7 +3496,7 @@ linevector_add (vec, text, len, vers, pos)
vec->vector[i] = vec->vector[i - nnew];
if (pos > vec->nlines)
- error (1, 0, "invalid rcs file: line to add out of range");
+ return 0;
/* Actually add the lines, to LINES and VEC->VECTOR. */
i = pos;
@@ -3510,6 +3522,8 @@ linevector_add (vec, text, len, vers, pos)
}
lines[i - pos - 1].len = p - lines[i - pos - 1].text;
vec->nlines += nnew;
+
+ return 1;
}
static void linevector_delete PROTO ((struct linevector *, unsigned int,
@@ -3589,6 +3603,214 @@ month_printname (month)
return (char *)months[mnum - 1];
}
+static int
+apply_rcs_changes PROTO ((struct linevector *, const char *, size_t,
+ const char *, RCSVers *, RCSVers *));
+
+/* Apply changes to the line vector LINES. DIFFBUF is a buffer of
+ length DIFFLEN holding the change text from an RCS file (the output
+ of diff -n). NAME is used in error messages. The VERS field of
+ any line added is set to ADDVERS. The VERS field of any line
+ deleted is set to DELVERS, unless DELVERS is NULL, in which case
+ the VERS field of deleted lines is unchanged. The function returns
+ non-zero if the change text is applied successfully. It returns
+ zero if the change text does not appear to apply to LINES (e.g., a
+ line number is invalid). If the change text is improperly
+ formatted (e.g., it is not the output of diff -n), the function
+ calls error with a status of 1, causing the program to exit. */
+
+static int
+apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers)
+ struct linevector *lines;
+ const char *diffbuf;
+ size_t difflen;
+ const char *name;
+ RCSVers *addvers;
+ RCSVers *delvers;
+{
+ const char *p;
+ const char *q;
+ int op;
+ /* The RCS format throws us for a loop in that the deltafrags (if
+ we define a deltafrag as an add or a delete) need to be applied
+ in reverse order. So we stick them into a linked list. */
+ struct deltafrag {
+ enum {ADD, DELETE} type;
+ unsigned long pos;
+ unsigned long nlines;
+ char *new_lines;
+ size_t len;
+ struct deltafrag *next;
+ };
+ struct deltafrag *dfhead;
+ struct deltafrag *df;
+
+ dfhead = NULL;
+ for (p = diffbuf; p != NULL && p < diffbuf + difflen; )
+ {
+ op = *p++;
+ if (op != 'a' && op != 'd')
+ /* Can't just skip over the deltafrag, because the value
+ of op determines the syntax. */
+ error (1, 0, "unrecognized operation '%c' in %s", op, name);
+ df = (struct deltafrag *) xmalloc (sizeof (struct deltafrag));
+ df->next = dfhead;
+ dfhead = df;
+ df->pos = strtoul (p, (char **) &q, 10);
+
+ if (p == q)
+ error (1, 0, "number expected in %s", name);
+ p = q;
+ if (*p++ != ' ')
+ error (1, 0, "space expected in %s", name);
+ df->nlines = strtoul (p, (char **) &q, 10);
+ if (p == q)
+ error (1, 0, "number expected in %s", name);
+ p = q;
+ if (*p++ != '\012')
+ error (1, 0, "linefeed expected in %s", name);
+
+ if (op == 'a')
+ {
+ unsigned int i;
+
+ df->type = ADD;
+ i = df->nlines;
+ /* The text we want is the number of lines specified, or
+ until the end of the value, whichever comes first (it
+ will be the former except in the case where we are
+ adding a line which does not end in newline). */
+ for (q = p; i != 0; ++q)
+ if (*q == '\n')
+ --i;
+ else if (q == diffbuf + difflen)
+ {
+ if (i != 1)
+ error (1, 0, "premature end of change in %s", name);
+ else
+ break;
+ }
+
+ /* Copy the text we are adding into allocated space. */
+ df->new_lines = block_alloc (q - p);
+ memcpy (df->new_lines, p, q - p);
+ df->len = q - p;
+
+ p = q;
+ }
+ else
+ {
+ /* Correct for the fact that line numbers in RCS files
+ start with 1. */
+ --df->pos;
+
+ assert (op == 'd');
+ df->type = DELETE;
+ }
+ }
+
+ for (df = dfhead; df != NULL;)
+ {
+ unsigned int ln;
+
+ switch (df->type)
+ {
+ case ADD:
+ if (! linevector_add (lines, df->new_lines, df->len, addvers,
+ df->pos))
+ return 0;
+ break;
+ case DELETE:
+ if (df->pos > lines->nlines
+ || df->pos + df->nlines > lines->nlines)
+ return 0;
+ if (delvers != NULL)
+ for (ln = df->pos; ln < df->pos + df->nlines; ++ln)
+ lines->vector[ln]->vers = delvers;
+ linevector_delete (lines, df->pos, df->nlines);
+ break;
+ }
+ df = df->next;
+ free (dfhead);
+ dfhead = df;
+ }
+
+ return 1;
+}
+
+/* Apply an RCS change text to a buffer. The function name starts
+ with rcs rather than RCS because this does not take an RCSNode
+ argument. NAME is used in error messages. TEXTBUF is the text
+ buffer to change, and TEXTLEN is the size. DIFFBUF and DIFFLEN are
+ the change buffer and size. The new buffer is returned in *RETBUF
+ and *RETLEN. The new buffer is allocated by xmalloc. The function
+ changes the contents of TEXTBUF. This function returns 1 for
+ success. On failure, it calls error and returns 0. */
+
+int
+rcs_change_text (name, textbuf, textlen, diffbuf, difflen, retbuf, retlen)
+ const char *name;
+ char *textbuf;
+ size_t textlen;
+ const char *diffbuf;
+ size_t difflen;
+ char **retbuf;
+ size_t *retlen;
+{
+ struct linevector lines;
+ int ret;
+
+ *retbuf = NULL;
+ *retlen = 0;
+
+ linevector_init (&lines);
+
+ if (! linevector_add (&lines, textbuf, textlen, NULL, 0))
+ error (1, 0, "cannot initialize line vector");
+
+ if (! apply_rcs_changes (&lines, diffbuf, difflen, name, NULL, NULL))
+ {
+ error (0, 0, "invalid change text in %s", name);
+ ret = 0;
+ }
+ else
+ {
+ char *p;
+ size_t n;
+ unsigned int ln;
+
+ n = 0;
+ for (ln = 0; ln < lines.nlines; ++ln)
+ /* 1 for \n */
+ n += lines.vector[ln]->len + 1;
+
+ p = xmalloc (n);
+ *retbuf = p;
+
+ for (ln = 0; ln < lines.nlines; ++ln)
+ {
+ memcpy (p, lines.vector[ln]->text, lines.vector[ln]->len);
+ p += lines.vector[ln]->len;
+ if (lines.vector[ln]->has_newline)
+ *p++ = '\n';
+ }
+
+ *retlen = p - *retbuf;
+ assert (*retlen <= n);
+
+ ret = 1;
+ }
+
+ linevector_free (&lines);
+
+ /* Note that this assumes that we have not called from anything
+ else which uses the block vectors. FIXME: We could fix this by
+ saving and restoring the state of the block allocation code. */
+ block_free ();
+
+ return ret;
+}
+
/* Walk the deltas in RCS to get to revision VERSION.
If OP is RCS_ANNOTATE, then write annotations using cvs_output.
@@ -3731,132 +3953,18 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen)
p = block_alloc (vallen);
memcpy (p, value, vallen);
- linevector_add (&curlines, p, vallen, NULL, 0);
+ if (! linevector_add (&curlines, p, vallen, NULL, 0))
+ error (1, 0, "invalid rcs file %s", rcs->path);
+
ishead = 0;
}
else if (isnext)
{
- char *p;
- char *q;
- int op;
- /* The RCS format throws us for a loop in that the
- deltafrags (if we define a deltafrag as an
- add or a delete) need to be applied in reverse
- order. So we stick them into a linked list. */
- struct deltafrag {
- enum {ADD, DELETE} type;
- unsigned long pos;
- unsigned long nlines;
- char *new_lines;
- size_t len;
- struct deltafrag *next;
- };
- struct deltafrag *dfhead;
- struct deltafrag *df;
-
- dfhead = NULL;
- for (p = value; p != NULL && p < value + vallen; )
- {
- op = *p++;
- if (op != 'a' && op != 'd')
- /* Can't just skip over the deltafrag, because
- the value of op determines the syntax. */
- error (1, 0, "unrecognized operation '%c' in %s",
- op, rcs->path);
- df = (struct deltafrag *)
- xmalloc (sizeof (struct deltafrag));
- df->next = dfhead;
- dfhead = df;
- df->pos = strtoul (p, &q, 10);
-
- if (p == q)
- error (1, 0, "number expected in %s",
- rcs->path);
- p = q;
- if (*p++ != ' ')
- error (1, 0, "space expected in %s",
- rcs->path);
- df->nlines = strtoul (p, &q, 10);
- if (p == q)
- error (1, 0, "number expected in %s",
- rcs->path);
- p = q;
- if (*p++ != '\012')
- error (1, 0, "linefeed expected in %s",
- rcs->path);
-
- if (op == 'a')
- {
- unsigned int i;
-
- df->type = ADD;
- i = df->nlines;
- /* The text we want is the number of lines
- specified, or until the end of the value,
- whichever comes first (it will be the former
- except in the case where we are adding a line
- which does not end in newline). */
- for (q = p; i != 0; ++q)
- if (*q == '\n')
- --i;
- else if (q == value + vallen)
- {
- if (i != 1)
- error (1, 0, "\
-invalid rcs file %s: premature end of value",
- rcs->path);
- else
- break;
- }
-
- /* Copy the text we are adding into allocated
- space. */
- df->new_lines = block_alloc (q - p);
- memcpy (df->new_lines, p, q - p);
- df->len = q - p;
-
- p = q;
- }
- else
- {
- /* Correct for the fact that line numbers in RCS
- files start with 1. */
- --df->pos;
-
- assert (op == 'd');
- df->type = DELETE;
- }
- }
- for (df = dfhead; df != NULL;)
- {
- unsigned int ln;
-
- switch (df->type)
- {
- case ADD:
- linevector_add (&curlines, df->new_lines,
- df->len,
- onbranch ? vers : NULL,
- df->pos);
- break;
- case DELETE:
- if (df->pos > curlines.nlines
- || df->pos + df->nlines > curlines.nlines)
- error (1, 0, "\
-invalid rcs file %s (`d' operand out of range)",
- rcs->path);
- if (! onbranch)
- for (ln = df->pos;
- ln < df->pos + df->nlines;
- ++ln)
- curlines.vector[ln]->vers = prev_vers;
- linevector_delete (&curlines, df->pos, df->nlines);
- break;
- }
- df = df->next;
- free (dfhead);
- dfhead = df;
- }
+ if (! apply_rcs_changes (&curlines, value, vallen,
+ rcs->path,
+ onbranch ? vers : NULL,
+ onbranch ? NULL : prev_vers))
+ error (1, 0, "invalid change text in %s", rcs->path);
}
break;
}
@@ -3947,6 +4055,8 @@ invalid rcs file %s (`d' operand out of range)",
break;
} while (next != NULL);
+ free (branchversion);
+
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", rcs->path);
@@ -4095,8 +4205,9 @@ annotate_fileproc (callerdat, finfo)
static const char *const annotate_usage[] =
{
- "Usage: %s %s [-lf] [-r rev|-D date] [files...]\n",
+ "Usage: %s %s [-lRf] [-r rev|-D date] [files...]\n",
"\t-l\tLocal directory only, no recursion.\n",
+ "\t-R\tProcess directories recursively.\n",
"\t-f\tUse head revision if tag/date not found.\n",
"\t-r rev\tAnnotate file as of specified revision/tag.\n",
"\t-D date\tAnnotate file as of specified date.\n",
@@ -4118,13 +4229,16 @@ annotate (argc, argv)
usage (annotate_usage);
optind = 0;
- while ((c = getopt (argc, argv, "+lr:D:f")) != -1)
+ while ((c = getopt (argc, argv, "+lr:D:fR")) != -1)
{
switch (c)
{
case 'l':
local = 1;
break;
+ case 'R':
+ local = 0;
+ break;
case 'r':
tag = optarg;
break;
diff --git a/gnu/usr.bin/cvs/src/rcscmds.c b/gnu/usr.bin/cvs/src/rcscmds.c
index 1f93fcfb94e..1f356cfd8f8 100644
--- a/gnu/usr.bin/cvs/src/rcscmds.c
+++ b/gnu/usr.bin/cvs/src/rcscmds.c
@@ -199,7 +199,7 @@ RCS_checkin (rcsfile, workfile, message, rev, flags)
if (!existence_error (errno))
error (0, errno, "warning: cannot stat %s", rcsfile);
}
- run_setup ("%s%s -x,v/ -f %s%s", Rcsbin, RCS_CI,
+ run_setup ("%s%s -x,v/ -w%s -f %s%s", Rcsbin, RCS_CI, getcaller (),
rev ? "-r" : "", rev ? rev : "");
if (flags & RCS_FLAGS_DEAD)
run_arg ("-sdead");
diff --git a/gnu/usr.bin/cvs/src/server.c b/gnu/usr.bin/cvs/src/server.c
index 34b6906440b..ea40a4c8901 100644
--- a/gnu/usr.bin/cvs/src/server.c
+++ b/gnu/usr.bin/cvs/src/server.c
@@ -89,7 +89,7 @@ static Key_schedule sched;
the same as the system username the server eventually switches to
run as. CVS_Username gets set iff password authentication is
successful. */
-static char *CVS_Username = NULL;
+char *CVS_Username = NULL;
/* Used to check that same repos is transmitted in pserver auth and in
later CVS protocol. Exported because root.c also uses. */
@@ -567,6 +567,28 @@ Sorry, you don't have read/write access to the history file %s", path);
#endif
parseopts(CVSroot_directory);
}
+
+static int max_dotdot_limit = 0;
+
+/* Is this pathname OK to recurse into when we are running as the server?
+ If not, call error() with a fatal error. */
+void
+server_pathname_check (path)
+ char *path;
+{
+ /* An absolute pathname is almost surely a path on the *client* machine,
+ and is unlikely to do us any good here. It also is probably capable
+ of being a security hole in the anonymous readonly case. */
+ if (isabsolute (path))
+ error (1, 0, "absolute pathname `%s' illegal for server", path);
+ if (pathname_levels (path) > max_dotdot_limit)
+ {
+ /* Similar to the isabsolute case in security implications. */
+ error (0, 0, "protocol error: `%s' contains more leading ..", path);
+ error (1, 0, "than the %d which Max-dotdot specified",
+ max_dotdot_limit);
+ }
+}
/*
* Add as many directories to the temp directory as the client tells us it
@@ -595,6 +617,7 @@ serve_max_dotdot (arg)
if (server_temp_dir != orig_server_temp_dir)
free (server_temp_dir);
server_temp_dir = p;
+ max_dotdot_limit = lim;
}
static char *dir_name;
@@ -615,6 +638,19 @@ dirswitch (dir, repos)
if (dir_name != NULL)
free (dir_name);
+ /* Check for a trailing '/'. This is not ISDIRSEP because \ in the
+ protocol is an ordinary character, not a directory separator (of
+ course, it is perhaps unwise to use it in directory names, but that
+ is another issue). */
+ if (strlen (dir) > 0
+ && dir[strlen (dir) - 1] == '/')
+ {
+ if (alloc_pending (80 + strlen (dir)))
+ sprintf (pending_error_text,
+ "E protocol error: illegal directory syntax in %s", dir);
+ return;
+ }
+
dir_name = malloc (strlen (server_temp_dir) + strlen (dir) + 40);
if (dir_name == NULL)
{
@@ -1798,16 +1834,7 @@ check_command_legal_p (cmd_name)
linebuf[num_red - 1] = '\0';
if (strcmp (linebuf, CVS_Username) == 0)
- {
- free (linebuf);
- linebuf = NULL;
- linebuf_len = 0;
goto handle_illegal;
- }
- /* else */
- free (linebuf);
- linebuf = NULL;
- linebuf_len = 0;
}
/* If not listed specifically as a reader, then this user
@@ -1835,6 +1862,8 @@ check_command_legal_p (cmd_name)
{
/* writers file does not exist, so everyone is a writer,
by default */
+ if (linebuf)
+ free (linebuf);
return 1;
}
@@ -1849,27 +1878,24 @@ check_command_legal_p (cmd_name)
if (strcmp (linebuf, CVS_Username) == 0)
{
- free (linebuf);
- linebuf = NULL;
- linebuf_len = 0;
found_it = 1;
break;
}
- /* else */
- free (linebuf);
- linebuf = NULL;
- linebuf_len = 0;
}
if (found_it)
{
fclose (fp);
+ if (linebuf)
+ free (linebuf);
return 1;
}
else /* writers file exists, but this user not listed in it */
{
handle_illegal:
fclose (fp);
+ if (linebuf)
+ free (linebuf);
return 0;
}
}
@@ -2452,7 +2478,6 @@ server_pause_check()
{
int paused = 0;
char buf[1];
- int n;
while (read (flowcontrol_pipe[0], buf, 1) == 1)
{
@@ -3049,6 +3074,42 @@ server_copy_file (file, update_dir, repository, newfile)
/* See server.h for description. */
void
+server_modtime (finfo, vers_ts)
+ struct file_info *finfo;
+ Vers_TS *vers_ts;
+{
+ char date[MAXDATELEN];
+ int year, month, day, hour, minute, second;
+ /* Note that these strings are specified in RFC822 and do not vary
+ according to locale. */
+ static const char *const month_names[] =
+ {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+ if (!supported_response ("Mod-time"))
+ return;
+
+ /* The only hard part about this routine is converting the date
+ formats. In terms of functionality it all boils down to the
+ call to RCS_getrevtime. */
+ if (RCS_getrevtime (finfo->rcs, vers_ts->vn_rcs, date, 0) == (time_t) -1)
+ /* FIXME? should we be printing some kind of warning? For one
+ thing I'm not 100% sure whether this happens in non-error
+ circumstances. */
+ return;
+
+ sscanf (date, SDATEFORM, &year, &month, &day, &hour, &minute, &second);
+ sprintf (date, "%d %s %d %d:%d:%d -0000", day,
+ month < 1 || month > 12 ? "???" : month_names[month - 1],
+ year, hour, minute, second);
+ buf_output0 (protocol, "Mod-time ");
+ buf_output0 (protocol, date);
+ buf_output0 (protocol, "\n");
+}
+
+/* See server.h for description. */
+
+void
server_updated (finfo, vers, updated, file_info, checksum)
struct file_info *finfo;
Vers_TS *vers;
@@ -3136,6 +3197,8 @@ server_updated (finfo, vers, updated, file_info, checksum)
buf_output0 (protocol, "Merged ");
else if (updated == SERVER_PATCHED)
buf_output0 (protocol, "Patched ");
+ else if (updated == SERVER_RCS_DIFF)
+ buf_output0 (protocol, "Rcs-diff ");
else
abort ();
output_dir (finfo->update_dir, finfo->repository);
@@ -3233,7 +3296,9 @@ server_updated (finfo, vers, updated, file_info, checksum)
* not up-to-date, and we indicate that by leaving the file there.
* I'm thinking of cases like "cvs update foo/foo.c foo".
*/
- if ((updated == SERVER_UPDATED || updated == SERVER_PATCHED)
+ if ((updated == SERVER_UPDATED
+ || updated == SERVER_PATCHED
+ || updated == SERVER_RCS_DIFF)
/* But if we are joining, we'll need the file when we call
join_file. */
&& !joining ())
@@ -3271,6 +3336,14 @@ server_updated (finfo, vers, updated, file_info, checksum)
done:;
}
+/* Return whether we should send patches in RCS format. */
+
+int
+server_use_rcs_diff ()
+{
+ return supported_response ("Rcs-diff");
+}
+
void
server_set_entstat (update_dir, repository)
char *update_dir;
@@ -3621,6 +3694,19 @@ serve_update_prog (arg)
char *arg;
{
FILE *f;
+
+ /* Before we do anything we need to make sure we are not in readonly
+ mode. */
+ if (!check_command_legal_p ("commit"))
+ {
+ /* I might be willing to make this a warning, except we lack the
+ machinery to do so. */
+ if (alloc_pending (80))
+ sprintf (pending_error_text, "\
+E Flag -u in modules not allowed in readonly mode");
+ return;
+ }
+
f = CVS_FOPEN (CVSADM_UPROG, "w+");
if (f == NULL)
{
@@ -4318,8 +4404,6 @@ check_repository_password (username, password, repository, host_user_ptr)
found_it = 1;
break;
}
- free (linebuf);
- linebuf = NULL;
}
if (ferror (fp))
error (0, errno, "cannot read %s", filename);
@@ -4340,9 +4424,7 @@ check_repository_password (username, password, repository, host_user_ptr)
if (strcmp (found_password, crypt (password, found_password)) == 0)
{
/* Give host_user_ptr permanent storage. */
- *host_user_ptr = xmalloc (strlen (host_user_tmp) + 1);
- strcpy (*host_user_ptr, host_user_tmp);
-
+ *host_user_ptr = xstrdup (host_user_tmp);
retval = 1;
}
else
@@ -4358,6 +4440,8 @@ check_repository_password (username, password, repository, host_user_ptr)
}
free (filename);
+ if (linebuf)
+ free (linebuf);
return retval;
}
@@ -4572,6 +4656,14 @@ pserver_authenticate_connection ()
{
error (1, 0, "bad auth protocol end: %s", tmp);
}
+ if (!root_allow_ok (repository))
+ /* At least for the moment I'm going to do the paranoid
+ security thing and not tell them how it failed. I'm not
+ sure that is a good idea; it is a real pain when one needs
+ to track down what is going on for legitimate reasons.
+ The other issue is that the protocol doesn't really have
+ a good way for anything other than I HATE YOU. */
+ goto i_hate_you;
/* We need the real cleartext before we hash it. */
descrambled_password = descramble (password);
@@ -4585,6 +4677,7 @@ pserver_authenticate_connection ()
}
else
{
+ i_hate_you:
printf ("I HATE YOU\n");
fflush (stdout);
/* I'm doing this manually rather than via error_exit ()
diff --git a/gnu/usr.bin/cvs/src/update.c b/gnu/usr.bin/cvs/src/update.c
index 2dd6e0ff22f..82886b7a3e9 100644
--- a/gnu/usr.bin/cvs/src/update.c
+++ b/gnu/usr.bin/cvs/src/update.c
@@ -92,6 +92,7 @@ static int update_prune_dirs = 0;
static int pipeout = 0;
#ifdef SERVER_SUPPORT
static int patches = 0;
+static int rcs_diff_patches = 0;
#endif
static List *ignlist = (List *) NULL;
static time_t last_register_time;
@@ -134,7 +135,7 @@ update (argc, argv)
wrap_setup ();
/* parse the args */
- optind = 1;
+ optind = 0;
while ((c = getopt (argc, argv, "+ApPflRQqduk:r:D:j:I:W:")) != -1)
{
switch (c)
@@ -200,7 +201,10 @@ update (argc, argv)
case 'u':
#ifdef SERVER_SUPPORT
if (server_active)
+ {
patches = 1;
+ rcs_diff_patches = server_use_rcs_diff ();
+ }
else
#endif
usage (update_usage);
@@ -555,10 +559,31 @@ update_fileproc (callerdat, finfo)
else
{
if (wrap_merge_is_copy (finfo->file))
+#if 0
+ /* Look, we can't clobber the user's file. We
+ know it is modified and we're going to
+ overwrite their mod? Puh-leeze. The
+ correct behavior is probably something like
+ what merge_file does for -kb, which is to
+ give the users both files and tell them
+ what the two filenames are. Of course, -m
+ in wrappers needs to be documented *much*
+ better. Anyway, until then, make this a
+ fatal error. */
+
/* Should we be warning the user that we are
* overwriting the user's copy of the file? */
retval =
checkout_file (finfo, vers, 0);
+#else
+ {
+ error (0, 0, "A -m 'COPY' wrapper is specified");
+ error (0, 0, "but file %s needs merge",
+ finfo->fullname);
+ error (1, 0, "\
+You probably want to avoid -m 'COPY' wrappers");
+#endif
+ }
else
retval = merge_file (finfo, vers);
}
@@ -626,8 +651,10 @@ update_fileproc (callerdat, finfo)
{
if (server_active && retval == 0)
server_updated (finfo, vers,
- SERVER_PATCHED, &file_info,
- checksum);
+ (rcs_diff_patches
+ ? SERVER_RCS_DIFF
+ : SERVER_PATCHED),
+ &file_info, checksum);
break;
}
}
@@ -1242,7 +1269,7 @@ struct patch_file_data
int final_nl;
};
-/* Patch a file. Runs rcsdiff. This is only done when running as the
+/* Patch a file. Runs diff. This is only done when running as the
* server. The hope is that the diff will be smaller than the file
* itself.
*/
@@ -1356,16 +1383,36 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
retcode = 0;
if (! fail)
{
- /* FIXME: This whole thing with diff/patch is rather more
- convoluted than necessary (lots of forks and execs, need to
- worry about versions of diff and patch, etc.). Also, we
- send context lines which aren't needed (in the rare case in
- which the diff doesn't apply, the checksum would catches it).
- Solution perhaps is to librarify the RCS routines which apply
- deltas or something equivalent. */
- /* This is -c, not -u, because we have no way of knowing which
- DIFF is in use. */
- run_setup ("%s -c %s %s", DIFF, file1, file2);
+ const char *diff_options;
+
+ /* FIXME: It might be better to come up with a diff library
+ which can be shared with the diffutils. */
+ /* If the client does not support the Rcs-diff command, we
+ send a context diff, and the client must invoke patch.
+ That approach was problematical for various reasons. The
+ new approach only requires running diff in the server; the
+ client can handle everything without invoking an external
+ program. */
+ if (! rcs_diff_patches)
+ {
+ /* We use -c, not -u, because we have no way of knowing
+ which DIFF is in use. */
+ diff_options = "-c";
+ }
+ else
+ {
+ /* FIXME: We should use -a if diff supports it. We should
+ probably just copy over most or all of the diff
+ handling in the RCS configure script. */
+ /* IMHO, we shouldn't copy over anything which even
+ vaguely resembles the RCS configure script. That kind of
+ thing tends to be ugly, slow, and fragile. It also is a
+ a support headache for CVS to behave differently in subtle
+ ways based on whether it was installed correctly. Instead we
+ should come up with a diff library. -kingdon, Apr 1997. */
+ diff_options = "-n";
+ }
+ run_setup ("%s %s %s %s", DIFF, diff_options, file1, file2);
/* A retcode of 0 means no differences. 1 means some differences. */
if ((retcode = run_exec (RUN_TTY, finfo->file, RUN_TTY, RUN_NORMAL)) != 0