summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/main.c')
-rw-r--r--contrib/cvs/src/main.c472
1 files changed, 296 insertions, 176 deletions
diff --git a/contrib/cvs/src/main.c b/contrib/cvs/src/main.c
index 68513df..e211db6 100644
--- a/contrib/cvs/src/main.c
+++ b/contrib/cvs/src/main.c
@@ -12,6 +12,7 @@
*
*/
+#include <assert.h>
#include "cvs.h"
#ifdef HAVE_WINSOCK_H
@@ -57,6 +58,16 @@ char *CurDir;
char *Tmpdir = TMPDIR_DFLT;
char *Editor = EDITOR_DFLT;
+
+/* When our working directory contains subdirectories with different
+ values in CVS/Root files, we maintain a list of them. */
+List *root_directories = NULL;
+
+/* We step through the above values. This variable is set to reflect
+ the currently active value. */
+char *current_root = NULL;
+
+
static const struct cmd
{
char *fullname; /* Full name of the function (e.g. "commit") */
@@ -209,6 +220,7 @@ static const char *const cmd_usage[] =
static const char *const opt_usage[] =
{
+ /* Omit -b because it is just for compatibility. */
"CVS global options (specified before the command name) are:\n",
" -H Displays usage information for command.\n",
" -Q Cause CVS to be really quiet.\n",
@@ -219,7 +231,6 @@ static const char *const opt_usage[] =
" -n Do not execute anything that will change the disk.\n",
" -t Show trace of program execution -- try with -n.\n",
" -v CVS version and copyright.\n",
- " -b bindir Find RCS programs in 'bindir'.\n",
" -T tmpdir Use 'tmpdir' for temporary files.\n",
" -e editor Use 'editor' for editing log information.\n",
" -d CVS_root Overrides $CVSROOT as the root of the CVS tree.\n",
@@ -236,6 +247,21 @@ static const char *const opt_usage[] =
NULL
};
+
+static int
+set_root_directory (p, ignored)
+ Node *p;
+ void *ignored;
+{
+ if (current_root == NULL && p->data == NULL)
+ {
+ current_root = p->key;
+ return 1;
+ }
+ return 0;
+}
+
+
static const char * const*
cmd_synonyms ()
{
@@ -311,7 +337,6 @@ lookup_command_attribute (cmd_name)
(strcmp (cmd_name, "diff") != 0) &&
(strcmp (cmd_name, "rdiff") != 0) &&
(strcmp (cmd_name, "update") != 0) &&
- (strcmp (cmd_name, "history") != 0) &&
(strcmp (cmd_name, "editors") != 0) &&
(strcmp (cmd_name, "export") != 0) &&
(strcmp (cmd_name, "history") != 0) &&
@@ -406,7 +431,6 @@ main (argc, argv)
/* `getopt_long' stores the option index here, but right now we
don't use it. */
int option_index = 0;
- int need_to_create_root = 0;
#ifdef SYSTEM_INITIALIZE
/* Hook for OS-specific behavior, for example socket subsystems on
@@ -562,6 +586,9 @@ Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\
free_Editor = 1;
break;
case 'd':
+ if (CVSroot_cmdline != NULL)
+ free (CVSroot_cmdline);
+ CVSroot_cmdline = xstrdup (optarg);
CVSroot = xstrdup (optarg);
free_CVSroot = 1;
cvs_update_env = 1; /* need to update environment */
@@ -644,7 +671,10 @@ Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\
}
if (help)
+ {
argc = -1; /* some functions only check for this */
+ err = (*(cm->func)) (argc, argv);
+ }
else
{
/* The user didn't ask for help, so go ahead and authenticate,
@@ -701,10 +731,79 @@ Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\
#ifdef SERVER_SUPPORT
server_active = strcmp (command_name, "server") == 0;
+#endif
+
+ /* This is only used for writing into the history file. For
+ remote connections, it might be nice to have hostname
+ and/or remote path, on the other hand I'm not sure whether
+ it is worth the trouble. */
+#ifdef SERVER_SUPPORT
+ if (server_active)
+ CurDir = xstrdup ("<remote>");
+ else
+#endif
+ {
+ CurDir = xgetwd ();
+ if (CurDir == NULL)
+ error (1, errno, "cannot get working directory");
+ }
+
+ if (Tmpdir == NULL || Tmpdir[0] == '\0')
+ Tmpdir = "/tmp";
+
+#ifdef HAVE_PUTENV
+ if (tmpdir_update_env)
+ {
+ char *env;
+ env = xmalloc (strlen (TMPDIR_ENV) + strlen (Tmpdir) + 1 + 1);
+ (void) sprintf (env, "%s=%s", TMPDIR_ENV, Tmpdir);
+ (void) putenv (env);
+ /* do not free env, as putenv has control of it */
+ }
+#endif
+
+#ifndef DONT_USE_SIGNALS
+ /* make sure we clean up on error */
+#ifdef SIGHUP
+ (void) SIG_register (SIGHUP, main_cleanup);
+ (void) SIG_register (SIGHUP, Lock_Cleanup);
+#endif
+#ifdef SIGINT
+ (void) SIG_register (SIGINT, main_cleanup);
+ (void) SIG_register (SIGINT, Lock_Cleanup);
+#endif
+#ifdef SIGQUIT
+ (void) SIG_register (SIGQUIT, main_cleanup);
+ (void) SIG_register (SIGQUIT, Lock_Cleanup);
+#endif
+#ifdef SIGPIPE
+ (void) SIG_register (SIGPIPE, main_cleanup);
+ (void) SIG_register (SIGPIPE, Lock_Cleanup);
+#endif
+#ifdef SIGTERM
+ (void) SIG_register (SIGTERM, main_cleanup);
+ (void) SIG_register (SIGTERM, Lock_Cleanup);
+#endif
+#endif /* !DONT_USE_SIGNALS */
+
+ gethostname(hostname, sizeof (hostname));
+
+#ifdef KLUDGE_FOR_WNT_TESTSUITE
+ /* Probably the need for this will go away at some point once
+ we call fflush enough places (e.g. fflush (stdout) in
+ cvs_outerr). */
+ (void) setvbuf (stdout, (char *) NULL, _IONBF, 0);
+ (void) setvbuf (stderr, (char *) NULL, _IONBF, 0);
+#endif /* KLUDGE_FOR_WNT_TESTSUITE */
+
+ if (use_cvsrc)
+ read_cvsrc (&argc, &argv, command_name);
+
+#ifdef SERVER_SUPPORT
/* Fiddling with CVSROOT doesn't make sense if we're running
- in server mode, since the client will send the repository
- directory after the connection is made. */
+ in server mode, since the client will send the repository
+ directory after the connection is made. */
if (!server_active)
#endif
@@ -721,11 +820,15 @@ Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\
specify a different repository than the one we are
importing to. */
- if (lookup_command_attribute (command_name)
- & CVS_CMD_IGNORE_ADMROOT)
- {
+ if ((lookup_command_attribute (command_name)
+ & CVS_CMD_IGNORE_ADMROOT)
+
+ /* -d overrides CVS/Root, so don't give an error if the
+ latter points to a nonexistent repository. */
+ && CVSroot_cmdline == NULL)
+ {
CVSADM_Root = Name_Root((char *) NULL, (char *) NULL);
- }
+ }
if (CVSADM_Root != NULL)
{
@@ -734,39 +837,11 @@ Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\
CVSroot = CVSADM_Root;
cvs_update_env = 1; /* need to update environment */
}
- /* Let -d override CVS/Root file. The user might want
- to change the access method, use a different server
- (if there are two server machines which share the
- repository using a networked file system), etc. */
- else if (
-#ifdef CLIENT_SUPPORT
- !getenv ("CVS_IGNORE_REMOTE_ROOT") &&
-#endif
- strcmp (CVSroot, CVSADM_Root) != 0)
- {
- /* Once we have verified that this root is usable,
- we will want to write it into CVS/Root.
-
- Don't do it for the "login" command, however.
- Consider: if the user executes "cvs login" with
- the working directory inside an already checked
- out module, we'd incorrectly change the
- CVS/Root file to reflect the CVSROOT of the
- "cvs login" command. Ahh, the things one
- discovers. */
-
- if (lookup_command_attribute (command_name)
- & CVS_CMD_USES_WORK_DIR)
- {
- need_to_create_root = 1;
- }
-
- }
}
/* Now we've reconciled CVSROOT from the command line, the
- CVS/Root file, and the environment variable. Do the
- last sanity checks on the variable. */
+ CVS/Root file, and the environment variable. Do the
+ last sanity checks on the variable. */
if (! CVSroot)
{
@@ -788,172 +863,180 @@ Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\
error (1, 0,
"CVS/Root file (if any).");
}
+ }
+
+ /* Here begins the big loop over unique cvsroot values. We
+ need to call do_recursion once for each unique value found
+ in CVS/Root. Prime the list with the current value. */
+
+ /* Create the list. */
+ assert (root_directories == NULL);
+ root_directories = getlist ();
+
+ /* Prime it. */
+ if (CVSroot != NULL)
+ {
+ Node *n;
+ n = getnode ();
+ n->type = UNKNOWN;
+ n->key = xstrdup (CVSroot);
+ n->data = NULL;
+
+ if (addnode (root_directories, n))
+ error (1, 0, "cannot add initial CVSROOT %s", n->key);
+ }
- /* Now we're 100% sure that we have a valid CVSROOT
- variable. Parse it to see if we're supposed to do
- remote accesses or use a special access method. */
+ assert (current_root == NULL);
- if (parse_cvsroot (CVSroot))
- error (1, 0, "Bad CVSROOT.");
+ /* If we're running the server, we want to execute this main
+ loop once and only once (we won't be serving multiple roots
+ from this connection, so there's no need to do it more than
+ once). To get out of the loop, we perform a "break" at the
+ end of things. */
- /*
- * Check to see if we can write into the history file. If not,
- * we assume that we can't work in the repository.
- * BUT, only if the history file exists.
- */
+ while (
+#ifdef SERVER_SUPPORT
+ server_active ||
+#endif
+ walklist (root_directories, set_root_directory, NULL)
+ )
+ {
+#ifdef SERVER_SUPPORT
+ /* Fiddling with CVSROOT doesn't make sense if we're running
+ in server mode, since the client will send the repository
+ directory after the connection is made. */
- if (!client_active)
+ if (!server_active)
+#endif
{
- char *path;
- int save_errno;
-
- path = xmalloc (strlen (CVSroot_directory)
- + sizeof (CVSROOTADM)
- + 20
- + sizeof (CVSROOTADM_HISTORY));
- (void) sprintf (path, "%s/%s", CVSroot_directory, CVSROOTADM);
- if (!isaccessible (path, R_OK | X_OK))
+ /* Now we're 100% sure that we have a valid CVSROOT
+ variable. Parse it to see if we're supposed to do
+ remote accesses or use a special access method. */
+
+ if (parse_cvsroot (current_root))
+ error (1, 0, "Bad CVSROOT.");
+
+ if (trace)
+ error (0, 0, "notice: main loop with CVSROOT=%s",
+ current_root);
+
+ /*
+ * Check to see if we can write into the history file. If not,
+ * we assume that we can't work in the repository.
+ * BUT, only if the history file exists.
+ */
+
+ if (!client_active)
{
- save_errno = errno;
- /* If this is "cvs init", the root need not exist yet. */
- if (strcmp (command_name, "init") != 0)
+ char *path;
+ int save_errno;
+
+ path = xmalloc (strlen (CVSroot_directory)
+ + sizeof (CVSROOTADM)
+ + 20
+ + sizeof (CVSROOTADM_HISTORY));
+ (void) sprintf (path, "%s/%s", CVSroot_directory, CVSROOTADM);
+ if (!isaccessible (path, R_OK | X_OK))
+ {
+ save_errno = errno;
+ /* If this is "cvs init", the root need not exist yet. */
+ if (strcmp (command_name, "init") != 0)
+ {
+ error (1, save_errno, "%s", path);
+ }
+ }
+ (void) strcat (path, "/");
+ (void) strcat (path, CVSROOTADM_HISTORY);
+ if (isfile (path) && !isaccessible (path, R_OK | W_OK))
{
+ save_errno = errno;
+ error (0, 0, "Sorry, you don't have read/write access to the history file");
error (1, save_errno, "%s", path);
}
+ free (path);
}
- (void) strcat (path, "/");
- (void) strcat (path, CVSROOTADM_HISTORY);
- if (isfile (path) && !isaccessible (path, R_OK | W_OK))
- {
- save_errno = errno;
- error (0, 0, "Sorry, you don't have read/write access to the history file");
- error (1, save_errno, "%s", path);
- }
- free (path);
- }
#ifdef HAVE_PUTENV
- /* Update the CVSROOT environment variable if necessary. */
-
- if (cvs_update_env)
- {
- char *env;
- env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot)
- + 1 + 1);
- (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot);
- (void) putenv (env);
- /* do not free env, as putenv has control of it */
- }
+ /* Update the CVSROOT environment variable if necessary. */
+ /* FIXME (njc): should we always set this with the CVSROOT from the command line? */
+ if (cvs_update_env)
+ {
+ char *env;
+ env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot)
+ + 1 + 1);
+ (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot);
+ (void) putenv (env);
+ /* do not free env, as putenv has control of it */
+ }
#endif
- }
+ }
- /* This is only used for writing into the history file. For
- remote connections, it might be nice to have hostname
- and/or remote path, on the other hand I'm not sure whether
- it is worth the trouble. */
-
+ /* Parse the CVSROOT/config file, but only for local. For the
+ server, we parse it after we know $CVSROOT. For the
+ client, it doesn't get parsed at all, obviously. The
+ presence of the parse_config call here is not mean to
+ predetermine whether CVSROOT/config overrides things from
+ read_cvsrc and other such places or vice versa. That sort
+ of thing probably needs more thought. */
+ if (1
#ifdef SERVER_SUPPORT
- if (server_active)
- CurDir = xstrdup ("<remote>");
- else
+ && !server_active
#endif
- {
- CurDir = xgetwd ();
- if (CurDir == NULL)
- error (1, errno, "cannot get working directory");
- }
-
- if (Tmpdir == NULL || Tmpdir[0] == '\0')
- Tmpdir = "/tmp";
-
-#ifdef HAVE_PUTENV
- if (tmpdir_update_env)
- {
- char *env;
- env = xmalloc (strlen (TMPDIR_ENV) + strlen (Tmpdir) + 1 + 1);
- (void) sprintf (env, "%s=%s", TMPDIR_ENV, Tmpdir);
- (void) putenv (env);
- /* do not free env, as putenv has control of it */
- }
+#ifdef CLIENT_SUPPORT
+ && !client_active
#endif
+ )
+ {
+ /* If there was an error parsing the config file, parse_config
+ already printed an error. We keep going. Why? Because
+ if we didn't, then there would be no way to check in a new
+ CVSROOT/config file to fix the broken one! */
+ parse_config (CVSroot_directory);
+ }
-#ifndef DONT_USE_SIGNALS
- /* make sure we clean up on error */
-#ifdef SIGHUP
- (void) SIG_register (SIGHUP, main_cleanup);
- (void) SIG_register (SIGHUP, Lock_Cleanup);
-#endif
-#ifdef SIGINT
- (void) SIG_register (SIGINT, main_cleanup);
- (void) SIG_register (SIGINT, Lock_Cleanup);
-#endif
-#ifdef SIGQUIT
- (void) SIG_register (SIGQUIT, main_cleanup);
- (void) SIG_register (SIGQUIT, Lock_Cleanup);
-#endif
-#ifdef SIGPIPE
- (void) SIG_register (SIGPIPE, main_cleanup);
- (void) SIG_register (SIGPIPE, Lock_Cleanup);
-#endif
-#ifdef SIGTERM
- (void) SIG_register (SIGTERM, main_cleanup);
- (void) SIG_register (SIGTERM, Lock_Cleanup);
+#ifdef CLIENT_SUPPORT
+ if (client_active)
+ {
+ /* Create a new list for directory names that we've
+ sent to the server. */
+ if (dirs_sent_to_server != NULL)
+ dellist (&dirs_sent_to_server);
+ dirs_sent_to_server = getlist ();
+ }
#endif
-#endif /* !DONT_USE_SIGNALS */
-
- gethostname(hostname, sizeof (hostname));
-#ifdef KLUDGE_FOR_WNT_TESTSUITE
- /* Probably the need for this will go away at some point once
- we call fflush enough places (e.g. fflush (stdout) in
- cvs_outerr). */
- (void) setvbuf (stdout, (char *) NULL, _IONBF, 0);
- (void) setvbuf (stderr, (char *) NULL, _IONBF, 0);
-#endif /* KLUDGE_FOR_WNT_TESTSUITE */
+ err = (*(cm->func)) (argc, argv);
+
+ /* Mark this root directory as done. When the server is
+ active, current_root will be NULL -- don't try and
+ remove it from the list. */
- if (use_cvsrc)
- read_cvsrc (&argc, &argv, command_name);
+ if (current_root != NULL)
+ {
+ Node *n = findnode (root_directories, current_root);
+ assert (n != NULL);
+ n->data = (void *) 1;
+ current_root = NULL;
+ }
+
+#if 0
+ /* This will not work yet, since it tries to free (void *) 1. */
+ dellist (&root_directories);
+#endif
- /* Parse the CVSROOT/config file, but only for local. For the
- server, we parse it after we know $CVSROOT. For the
- client, it doesn't get parsed at all, obviously. The
- presence of the parse_config call here is not mean to
- predetermine whether CVSROOT/config overrides things from
- read_cvsrc and other such places or vice versa. That sort
- of thing probably needs more thought. */
- if (1
#ifdef SERVER_SUPPORT
- && !server_active
-#endif
-#ifdef CLIENT_SUPPORT
- && !client_active
+ if (server_active)
+ break;
#endif
- )
- {
- /* If there was an error parsing the config file, parse_config
- already printed an error. We keep going. Why? Because
- if we didn't, then there would be no way to check in a new
- CVSROOT/config file to fix the broken one! */
- parse_config (CVSroot_directory);
- }
- } /* end of stuff that gets done if the user DOESN'T ask for help */
+ } /* end of loop for cvsroot values */
- err = (*(cm->func)) (argc, argv);
-
- if (need_to_create_root)
- {
- /* Update the CVS/Root file. We might want to do this in
- all directories that we recurse into, but currently we
- don't. Note that if there is an error writing the file,
- we give an error/warning. This is so if users try to rewrite
- CVS/Root with the -d option (a documented feature), they will
- either succeed, or be told why it didn't work. */
- Create_Root (NULL, CVSroot);
- }
+ } /* end of stuff that gets done if the user DOESN'T ask for help */
Lock_Cleanup ();
free (program_path);
+ if (CVSroot_cmdline != NULL)
+ free (CVSroot_cmdline);
if (free_CVSroot)
free (CVSroot);
if (free_Editor)
@@ -1020,6 +1103,43 @@ date_from_time_t (unixtime)
return (ret);
}
+/* Convert a date to RFC822/1123 format. This is used in contexts like
+ dates to send in the protocol; it should not vary based on locale or
+ other such conventions for users. We should have another routine which
+ does that kind of thing.
+
+ The SOURCE date is in our internal RCS format. DEST should point to
+ storage managed by the caller, at least MAXDATELEN characters. */
+void
+date_to_internet (dest, source)
+ char *dest;
+ char *source;
+{
+ int year, month, day, hour, minute, second;
+
+ /* Just to reiterate, these strings are from 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 (sscanf (source, SDATEFORM,
+ &year, &month, &day, &hour, &minute, &second)
+ != 6)
+ /* Is there a better way to handle errors here? I made this
+ non-fatal in case we are called from the code which can't
+ deal with fatal errors. */
+ error (0, 0, "internal error: bad date %s", source);
+
+ /* Always send a four digit year. */
+ if (year < 100)
+ year += 1900;
+
+ sprintf (dest, "%d %s %d %02d:%02d:%02d -0000", day,
+ month < 1 || month > 12 ? "???" : month_names[month - 1],
+ year, hour, minute, second);
+}
+
void
usage (cpp)
register const char *const *cpp;
OpenPOWER on IntegriCloud