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.c999
1 files changed, 547 insertions, 452 deletions
diff --git a/contrib/cvs/src/main.c b/contrib/cvs/src/main.c
index daa0230..21400ea 100644
--- a/contrib/cvs/src/main.c
+++ b/contrib/cvs/src/main.c
@@ -10,27 +10,6 @@
* Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing
* the shell-script CVS system that this is based on.
*
- * Usage:
- * cvs [options] command [options] [files/modules...]
- *
- * Where "command" is composed of:
- * admin RCS command
- * checkout Check out a module/dir/file
- * export Like checkout, but used for exporting sources
- * update Brings work tree in sync with repository
- * commit Checks files into the repository
- * diff Runs diffs between revisions
- * log Prints "rlog" information for files
- * login Record user, host, repos, password
- * add Adds an entry to the repository
- * remove Removes an entry from the repository
- * status Status info on the revisions
- * rdiff "patch" format diff listing between releases
- * tag Add/delete a symbolic tag to the RCS file
- * rtag Add/delete a symbolic tag to the RCS file
- * import Import sources into CVS, using vendor branches
- * release Indicate that Module is no longer in use.
- * history Display history of Users and Modules.
*/
#include "cvs.h"
@@ -41,35 +20,19 @@
extern int gethostname ();
#endif
-#if HAVE_KERBEROS
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <krb.h>
-#ifndef HAVE_KRB_GET_ERR_TEXT
-#define krb_get_err_text(status) krb_err_txt[status]
-#endif
-#endif
-
char *program_name;
char *program_path;
-/*
- * Initialize comamnd_name to "cvs" so that the first call to
- * read_cvsrc tries to find global cvs options.
- */
-char *command_name = "";
+char *command_name;
-/*
- * Since some systems don't define this...
- */
+/* I'd dynamically allocate this, but it seems like gethostname
+ requires a fixed size array. If I'm remembering the RFCs right,
+ 256 should be enough. */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 256
#endif
char hostname[MAXHOSTNAMELEN];
-#ifdef AUTH_CLIENT_SUPPORT
-int use_authenticating_server = FALSE;
-#endif /* AUTH_CLIENT_SUPPORT */
int use_editor = TRUE;
int use_cvsrc = TRUE;
int cvswrite = !CVSREAD_DFLT;
@@ -86,87 +49,77 @@ char *CurDir;
* Defaults, for the environment variables that are not set
*/
char *Rcsbin = RCSBIN_DFLT;
+char *Tmpdir = TMPDIR_DFLT;
char *Editor = EDITOR_DFLT;
-char *CVSroot = CVSROOT_DFLT;
-/*
- * The path found in CVS/Root must match $CVSROOT and/or 'cvs -d root'
- */
-char *CVSADM_Root = CVSROOT_DFLT;
-
-int add PROTO((int argc, char **argv));
-int admin PROTO((int argc, char **argv));
-int checkout PROTO((int argc, char **argv));
-int commit PROTO((int argc, char **argv));
-int diff PROTO((int argc, char **argv));
-int history PROTO((int argc, char **argv));
-int import PROTO((int argc, char **argv));
-int cvslog PROTO((int argc, char **argv));
-#ifdef AUTH_CLIENT_SUPPORT
-int login PROTO((int argc, char **argv));
-#endif /* AUTH_CLIENT_SUPPORT */
-int patch PROTO((int argc, char **argv));
-int release PROTO((int argc, char **argv));
-int cvsremove PROTO((int argc, char **argv));
-int rtag PROTO((int argc, char **argv));
-int status PROTO((int argc, char **argv));
-int tag PROTO((int argc, char **argv));
-int update PROTO((int argc, char **argv));
-
-const struct cmd
+
+static const struct cmd
{
char *fullname; /* Full name of the function (e.g. "commit") */
- char *nick1; /* alternate name (e.g. "ci") */
- char *nick2; /* another alternate names (e.g. "ci") */
+
+ /* Synonyms for the command, nick1 and nick2. We supply them
+ mostly for two reasons: (1) CVS has always supported them, and
+ we need to maintain compatibility, (2) if there is a need for a
+ version which is shorter than the fullname, for ease in typing.
+ Synonyms have the disadvantage that people will see "new" and
+ then have to think about it, or look it up, to realize that is
+ the operation they know as "add". Also, this means that one
+ cannot create a command "cvs new" with a different meaning. So
+ new synonyms are probably best used sparingly, and where used
+ should be abbreviations of the fullname (preferably consisting
+ of the first 2 or 3 or so letters).
+
+ One thing that some systems do is to recognize any unique
+ abbreviation, for example "annotat" "annota", etc., for
+ "annotate". The problem with this is that scripts and user
+ habits will expect a certain abbreviation to be unique, and in
+ a future release of CVS it may not be. So it is better to
+ accept only an explicit list of abbreviations and plan on
+ supporting them in the future as well as now. */
+
+ char *nick1;
+ char *nick2;
+
int (*func) (); /* Function takes (argc, argv) arguments. */
-#ifdef CLIENT_SUPPORT
- int (*client_func) (); /* Function to do it via the protocol. */
-#endif
} cmds[] =
{
-#ifdef CLIENT_SUPPORT
-#define CMD_ENTRY(n1, n2, n3, f1, f2) { n1, n2, n3, f1, f2 }
-#else
-#define CMD_ENTRY(n1, n2, n3, f1, f2) { n1, n2, n3, f1 }
+ { "add", "ad", "new", add },
+ { "admin", "adm", "rcs", admin },
+ { "annotate", "ann", NULL, annotate },
+ { "checkout", "co", "get", checkout },
+ { "commit", "ci", "com", commit },
+ { "diff", "di", "dif", diff },
+ { "edit", NULL, NULL, edit },
+ { "editors", NULL, NULL, editors },
+ { "export", "exp", "ex", checkout },
+ { "history", "hi", "his", history },
+ { "import", "im", "imp", import },
+ { "init", NULL, NULL, init },
+#ifdef SERVER_SUPPORT
+ { "kserver", NULL, NULL, server }, /* placeholder */
#endif
-
- CMD_ENTRY("add", "ad", "new", add, client_add),
- CMD_ENTRY("admin", "adm", "rcs", admin, client_admin),
- CMD_ENTRY("annotate", NULL, NULL, annotate, client_annotate),
- CMD_ENTRY("checkout", "co", "get", checkout, client_checkout),
- CMD_ENTRY("commit", "ci", "com", commit, client_commit),
- CMD_ENTRY("diff", "di", "dif", diff, client_diff),
- CMD_ENTRY("edit", "edit", "edit", edit, client_edit),
- CMD_ENTRY("editors", "editors","editors",editors, client_editors),
- CMD_ENTRY("export", "exp", "ex", checkout, client_export),
- CMD_ENTRY("history", "hi", "his", history, client_history),
- CMD_ENTRY("import", "im", "imp", import, client_import),
- CMD_ENTRY("init", NULL, NULL, init, client_init),
- CMD_ENTRY("log", "lo", "rlog", cvslog, client_log),
+ { "log", "lo", "rlog", cvslog },
#ifdef AUTH_CLIENT_SUPPORT
- CMD_ENTRY("login", "logon", "lgn", login, login),
+ { "login", "logon", "lgn", login },
+ { "logout", NULL, NULL, logout },
+#ifdef SERVER_SUPPORT
+ { "pserver", NULL, NULL, server }, /* placeholder */
+#endif
#endif /* AUTH_CLIENT_SUPPORT */
- CMD_ENTRY("rdiff", "patch", "pa", patch, client_rdiff),
- CMD_ENTRY("release", "re", "rel", release, client_release),
- CMD_ENTRY("remove", "rm", "delete", cvsremove, client_remove),
- CMD_ENTRY("status", "st", "stat", status, client_status),
- CMD_ENTRY("rtag", "rt", "rfreeze", rtag, client_rtag),
- CMD_ENTRY("tag", "ta", "freeze", tag, client_tag),
- CMD_ENTRY("unedit", "unedit","unedit", unedit, client_unedit),
- CMD_ENTRY("update", "up", "upd", update, client_update),
- CMD_ENTRY("watch", "watch", "watch", watch, client_watch),
- CMD_ENTRY("watchers", "watchers","watchers",watchers,client_watchers),
+ { "rdiff", "patch", "pa", patch },
+ { "release", "re", "rel", release },
+ { "remove", "rm", "delete", cvsremove },
+ { "status", "st", "stat", status },
+ { "rtag", "rt", "rfreeze", rtag },
+ { "tag", "ta", "freeze", cvstag },
+ { "unedit", NULL, NULL, unedit },
+ { "update", "up", "upd", update },
+ { "watch", NULL, NULL, watch },
+ { "watchers", NULL, NULL, watchers },
#ifdef SERVER_SUPPORT
- /*
- * The client_func is also server because we might have picked up a
- * CVSROOT environment variable containing a colon. The client will send
- * the real root later.
- */
- CMD_ENTRY("server", "server", "server", server, server),
+ { "server", NULL, NULL, server },
#endif
- CMD_ENTRY(NULL, NULL, NULL, NULL, NULL),
-
-#undef CMD_ENTRY
+ { NULL, NULL, NULL, NULL },
};
static const char *const usg[] =
@@ -183,11 +136,15 @@ static const char *const usg[] =
" -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",
" -f Do not use the ~/.cvsrc file\n",
#ifdef CLIENT_SUPPORT
- " -z # Use 'gzip -#' for net traffic if possible.\n",
+ " -z # Use compression level '#' for net traffic.\n",
+#ifdef ENCRYPTION
+ " -x Encrypt all net traffic.\n",
+#endif
#endif
" -s VAR=VAL Set CVS user variable.\n",
"\n",
@@ -199,48 +156,165 @@ static const char *const usg[] =
static const char *const cmd_usage[] =
{
"CVS commands are:\n",
- " add Adds a new file/directory to the repository\n",
+ " add Add a new file/directory to the repository\n",
" admin Administration front end for rcs\n",
- " annotate Show revision where each line was modified\n",
+ " annotate Show last revision where each line was modified\n",
" checkout Checkout sources for editing\n",
- " commit Checks files into the repository\n",
- " diff Runs diffs between revisions\n",
+ " commit Check files into the repository\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",
- " history Shows status of files and users\n",
- " import Import sources into CVS, using vendor branches\n",
" export Export sources from CVS, similar to checkout\n",
- " log Prints out 'rlog' information for files\n",
+ " history Show repository access history\n",
+ " import Import sources into CVS, using vendor branches\n",
+ " init Create a CVS repository if it doesn't exist\n",
+ " log Print out history information for files\n",
#ifdef AUTH_CLIENT_SUPPORT
" login Prompt for password for authenticating server.\n",
+ " logout Removes entry in .cvspass for remote repository.\n",
#endif /* AUTH_CLIENT_SUPPORT */
- " rdiff 'patch' format diffs between releases\n",
+ " rdiff Create 'patch' format diffs between releases\n",
" release Indicate that a Module is no longer in use\n",
- " remove Removes an entry from the repository\n",
- " status Status info on the revisions\n",
- " tag Add a symbolic tag to checked out version of RCS file\n",
+ " remove Remove an entry from the repository\n",
+ " rtag Add a symbolic tag to a module\n",
+ " status Display status information on checked out files\n",
+ " tag Add a symbolic tag to checked out version of files\n",
" unedit Undo an edit command\n",
- " rtag Add a symbolic tag to the RCS file\n",
- " update Brings work tree in sync with repository\n",
+ " update Bring work tree in sync with repository\n",
" watch Set watches\n",
" watchers See who is watching a file\n",
+ "(Use the --help-synonyms option for a list of alternate command names)\n",
NULL,
};
-static RETSIGTYPE
-main_cleanup ()
+static const char * const*
+cmd_synonyms ()
{
- exit (EXIT_FAILURE);
+ char ** synonyms;
+ char ** line;
+ const struct cmd *c = &cmds[0];
+ int numcmds = 2; /* two more for title and end */
+
+ while (c->fullname != NULL)
+ {
+ numcmds++;
+ c++;
+ }
+
+ synonyms = (char **) xmalloc(numcmds * sizeof(char *));
+ line = synonyms;
+ *line++ = "CVS command synonyms are:\n";
+ for (c = &cmds[0]; c->fullname != NULL; c++)
+ {
+ if (c->nick1 || c->nick2)
+ {
+ *line = xmalloc (strlen (c->fullname)
+ + (c->nick1 != NULL ? strlen (c->nick1) : 0)
+ + (c->nick2 != NULL ? strlen (c->nick2) : 0)
+ + 40);
+ sprintf(*line, " %-12s %s %s\n", c->fullname,
+ c->nick1 ? c->nick1 : "",
+ c->nick2 ? c->nick2 : "");
+ line++;
+ }
+ }
+ *line = NULL;
+
+ return (const char * const*) synonyms; /* will never be freed */
}
-static void
-error_cleanup PROTO((void))
+
+unsigned long int
+lookup_command_attribute (cmd_name)
+ char *cmd_name;
{
- Lock_Cleanup();
-#ifdef SERVER_SUPPORT
- if (server_active)
- server_cleanup (0);
+ unsigned long int ret = 0;
+
+ if (strcmp (cmd_name, "import") != 0)
+ {
+ ret |= CVS_CMD_IGNORE_ADMROOT;
+ }
+
+
+ if ((strcmp (cmd_name, "checkout") != 0) &&
+ (strcmp (cmd_name, "init") != 0) &&
+ (strcmp (cmd_name, "login") != 0) &&
+ (strcmp (cmd_name, "logout") != 0) &&
+ (strcmp (cmd_name, "rdiff") != 0) &&
+ (strcmp (cmd_name, "release") != 0) &&
+ (strcmp (cmd_name, "rtag") != 0))
+ {
+ ret |= CVS_CMD_USES_WORK_DIR;
+ }
+
+
+ /* The following commands do not modify the repository; we
+ conservatively assume that everything else does. Feel free to
+ add to this list if you are _certain_ something is safe. */
+ if ((strcmp (cmd_name, "checkout") != 0) &&
+ (strcmp (cmd_name, "diff") != 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) &&
+ (strcmp (cmd_name, "log") != 0) &&
+ (strcmp (cmd_name, "noop") != 0) &&
+ (strcmp (cmd_name, "watchers") != 0) &&
+ (strcmp (cmd_name, "status") != 0))
+ {
+ ret |= CVS_CMD_MODIFIES_REPOSITORY;
+ }
+
+ return ret;
+}
+
+
+static RETSIGTYPE
+main_cleanup (sig)
+ int sig;
+{
+#ifndef DONT_USE_SIGNALS
+ const char *name;
+ char temp[10];
+
+ switch (sig)
+ {
+#ifdef SIGHUP
+ case SIGHUP:
+ name = "hangup";
+ break;
+#endif
+#ifdef SIGINT
+ case SIGINT:
+ name = "interrupt";
+ break;
#endif
+#ifdef SIGQUIT
+ case SIGQUIT:
+ name = "quit";
+ break;
+#endif
+#ifdef SIGPIPE
+ case SIGPIPE:
+ name = "broken pipe";
+ break;
+#endif
+#ifdef SIGTERM
+ case SIGTERM:
+ name = "termination";
+ break;
+#endif
+ default:
+ /* This case should never be reached, because we list above all
+ the signals for which we actually establish a signal handler. */
+ sprintf (temp, "%d", sig);
+ name = temp;
+ break;
+ }
+
+ error (1, 0, "received %s signal", name);
+#endif /* !DONT_USE_SIGNALS */
}
int
@@ -248,56 +322,70 @@ main (argc, argv)
int argc;
char **argv;
{
+ char *CVSroot = CVSROOT_DFLT;
extern char *version_string;
extern char *config_string;
char *cp, *end;
const struct cmd *cm;
int c, err = 0;
- static int help = FALSE;
- static int version_flag = FALSE;
- static int help_commands = FALSE;
- int rcsbin_update_env, cvs_update_env = 0;
+ int rcsbin_update_env, tmpdir_update_env, cvs_update_env;
+ 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, &help, TRUE},
- {"version", 0, &version_flag, TRUE},
- {"help-commands", 0, &help_commands, TRUE},
+ {"help", 0, NULL, 'H'},
+ {"version", 0, NULL, 'v'},
+ {"help-commands", 0, NULL, 1},
+ {"help-synonyms", 0, NULL, 2},
{0, 0, 0, 0}
};
/* `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;
- error_set_cleanup (error_cleanup);
+#ifdef SYSTEM_INITIALIZE
+ /* Hook for OS-specific behavior, for example socket subsystems on
+ NT and OS2 or dealing with windows and arguments on Mac. */
+ SYSTEM_INITIALIZE (&argc, &argv);
+#endif
-/* The socket subsystems on NT and OS2 must be initialized before use */
-#ifdef INITIALIZE_SOCKET_SUBSYSTEM
- INITIALIZE_SOCKET_SUBSYSTEM();
-#endif /* INITIALIZE_SOCKET_SUBSYSTEM */
+#ifdef HAVE_TZSET
+ /* On systems that have tzset (which is almost all the ones I know
+ of), it's a good idea to call it. */
+ tzset ();
+#endif
/*
* Just save the last component of the path for error messages
*/
program_path = xstrdup (argv[0]);
+#ifdef ARGV0_NOT_PROGRAM_NAME
+ /* On some systems, e.g. VMS, argv[0] is not the name of the command
+ which the user types to invoke the program. */
+ program_name = "cvs";
+#else
program_name = last_component (argv[0]);
-
- CurDir = xmalloc (PATH_MAX);
-#ifndef SERVER_SUPPORT
- if (!getwd (CurDir))
- error (1, 0, "cannot get working directory: %s", CurDir);
#endif
/*
* Query the environment variables up-front, so that
* they can be overridden by command line arguments
*/
- rcsbin_update_env = *Rcsbin; /* RCSBIN_DFLT must be set */
cvs_update_env = 0;
+ rcsbin_update_env = *Rcsbin; /* RCSBIN_DFLT must be set */
if ((cp = getenv (RCSBIN_ENV)) != NULL)
{
Rcsbin = cp;
rcsbin_update_env = 0; /* it's already there */
}
+ tmpdir_update_env = *Tmpdir; /* TMPDIR_DFLT must be set */
+ if ((cp = getenv (TMPDIR_ENV)) != NULL)
+ {
+ Tmpdir = cp;
+ tmpdir_update_env = 0; /* it's already there */
+ }
if ((cp = getenv (EDITOR1_ENV)) != NULL)
Editor = cp;
else if ((cp = getenv (EDITOR2_ENV)) != NULL)
@@ -311,20 +399,10 @@ main (argc, argv)
}
if (getenv (CVSREAD_ENV) != NULL)
cvswrite = FALSE;
- if ((cp = getenv (CVSUMASK_ENV)) != NULL)
- {
- /* FIXME: Should be accepting symbolic as well as numeric mask. */
- cvsumask = strtol (cp, &end, 8) & 0777;
- if (*end != '\0')
- error (1, errno, "invalid umask value in %s (%s)",
- CVSUMASK_ENV, cp);
- }
- /* This has the effect of setting getopt's ordering to REQUIRE_ORDER,
- which is what we need to distinguish between global options and
- command options. FIXME: It would appear to be possible to do this
- much less kludgily by passing "+" as the first character to the
- option string we pass to getopt_long. */
+ /* 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;
@@ -335,13 +413,13 @@ main (argc, argv)
opterr = 0;
while ((c = getopt_long
- (argc, argv, "f", NULL, NULL))
+ (argc, argv, "+f", NULL, NULL))
!= EOF)
- {
+ {
if (c == 'f')
use_cvsrc = FALSE;
- }
-
+ }
+
/*
* Scan cvsrc file for global options.
*/
@@ -352,13 +430,18 @@ main (argc, argv)
opterr = 1;
while ((c = getopt_long
- (argc, argv, "Qqrwtnlvb:e:d:Hfz:s:", long_options, &option_index))
+ (argc, argv, "+Qqrwtnlvb:T:e:d:Hfz:s:x", long_options, &option_index))
!= EOF)
{
switch (c)
{
- case 0:
- /* getopt_long took care of setting the flag. */
+ case 1:
+ /* --help-commands */
+ usage (cmd_usage);
+ break;
+ case 2:
+ /* --help-synonyms */
+ usage (cmd_synonyms());
break;
case 'Q':
really_quiet = TRUE;
@@ -381,12 +464,26 @@ main (argc, argv)
logoff = TRUE;
break;
case 'v':
- version_flag = TRUE;
+ (void) fputs (version_string, stdout);
+ (void) fputs (config_string, stdout);
+ (void) fputs ("\n", stdout);
+ (void) fputs ("Copyright (c) 1993-1994 Brian Berliner\n", stdout);
+ (void) fputs ("Copyright (c) 1993-1994 david d `zoo' zuhn\n", stdout);
+ (void) fputs ("Copyright (c) 1992, Brian Berliner and Jeff Polk\n", stdout);
+ (void) fputs ("Copyright (c) 1989-1992, Brian Berliner\n", stdout);
+ (void) fputs ("\n", stdout);
+ (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
+ (void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
+ exit (0);
break;
case 'b':
Rcsbin = optarg;
rcsbin_update_env = 1; /* need to update environment */
break;
+ case 'T':
+ Tmpdir = optarg;
+ tmpdir_update_env = 1; /* need to update environment */
+ break;
case 'e':
Editor = optarg;
break;
@@ -395,11 +492,10 @@ main (argc, argv)
cvs_update_env = 1; /* need to update environment */
break;
case 'H':
- use_cvsrc = FALSE; /* this ensure that cvs -H works */
- help = TRUE;
+ help = 1;
break;
case 'f':
- use_cvsrc = FALSE;
+ use_cvsrc = FALSE; /* unnecessary, since we've done it above */
break;
case 'z':
#ifdef CLIENT_SUPPORT
@@ -415,313 +511,307 @@ main (argc, argv)
case 's':
variable_set (optarg);
break;
+ case 'x':
+#ifdef CLIENT_SUPPORT
+ cvsencrypt = 1;
+#endif /* CLIENT_SUPPORT */
+ /* If no CLIENT_SUPPORT, ignore -x, so that users can
+ have it in their .cvsrc and not cause any trouble.
+ If no ENCRYPTION, we still accept -x, but issue an
+ error if we are being run as a client. */
+ break;
case '?':
default:
usage (usg);
}
}
- if (version_flag == TRUE)
- {
- (void) fputs (version_string, stdout);
- (void) fputs (config_string, stdout);
- (void) fputs ("\n", stdout);
- (void) fputs ("Copyright (c) 1993-1994 Brian Berliner\n", stdout);
- (void) fputs ("Copyright (c) 1993-1994 david d `zoo' zuhn\n", stdout);
- (void) fputs ("Copyright (c) 1992, Brian Berliner and Jeff Polk\n", stdout);
- (void) fputs ("Copyright (c) 1989-1992, Brian Berliner\n", stdout);
- (void) fputs ("\n", stdout);
- (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
- (void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
- exit (0);
- }
- else if (help_commands)
- usage (cmd_usage);
-
argc -= optind;
argv += optind;
if (argc < 1)
usage (usg);
-#ifdef HAVE_KERBEROS
- /* If we are invoked with a single argument "kserver", then we are
- running as Kerberos server as root. Do the authentication as
- the very first thing, to minimize the amount of time we are
- running as root. */
- if (strcmp (argv[0], "kserver") == 0)
+
+ /* Look up the command name. */
+
+ command_name = argv[0];
+ for (cm = cmds; cm->fullname; cm++)
{
- int status;
- char instance[INST_SZ];
- struct sockaddr_in peer;
- struct sockaddr_in laddr;
- int len;
- KTEXT_ST ticket;
- AUTH_DAT auth;
- char version[KRB_SENDAUTH_VLEN];
- Key_schedule sched;
- char user[ANAME_SZ];
- struct passwd *pw;
-
- strcpy (instance, "*");
- len = sizeof peer;
- if (getpeername (STDIN_FILENO, (struct sockaddr *) &peer, &len) < 0
- || getsockname (STDIN_FILENO, (struct sockaddr *) &laddr,
- &len) < 0)
- {
- printf ("E Fatal error, aborting.\n\
-error %s getpeername or getsockname failed\n", strerror (errno));
- exit (EXIT_FAILURE);
- }
+ if (cm->nick1 && !strcmp (command_name, cm->nick1))
+ break;
+ if (cm->nick2 && !strcmp (command_name, cm->nick2))
+ break;
+ if (!strcmp (command_name, cm->fullname))
+ break;
+ }
- status = krb_recvauth (KOPT_DO_MUTUAL, STDIN_FILENO, &ticket, "rcmd",
- instance, &peer, &laddr, &auth, "", sched,
- version);
- if (status != KSUCCESS)
- {
- printf ("E Fatal error, aborting.\n\
-error 0 kerberos: %s\n", krb_get_err_text(status));
- exit (EXIT_FAILURE);
- }
+ if (!cm->fullname)
+ usage (cmd_usage); /* no match */
+ else
+ command_name = cm->fullname; /* Global pointer for later use */
- /* Get the local name. */
- status = krb_kntoln (&auth, user);
- if (status != KSUCCESS)
- {
- printf ("E Fatal error, aborting.\n\
-error 0 kerberos: can't get local name: %s\n", krb_get_err_text(status));
- exit (EXIT_FAILURE);
- }
+ if (strcmp (argv[0], "rlog") == 0)
+ {
+ error (0, 0, "warning: the rlog command is deprecated");
+ error (0, 0, "use the synonymous log command instead");
+ }
- pw = getpwnam (user);
- if (pw == NULL)
- {
- printf ("E Fatal error, aborting.\n\
-error 0 %s: no such user\n", user);
- exit (EXIT_FAILURE);
- }
+ if (help)
+ argc = -1; /* some functions only check for this */
+ else
+ {
+ /* The user didn't ask for help, so go ahead and authenticate,
+ set up CVSROOT, and the rest of it. */
- initgroups (pw->pw_name, pw->pw_gid);
- setgid (pw->pw_gid);
- setuid (pw->pw_uid);
- /* Inhibit access by randoms. Don't want people randomly
- changing our temporary tree before we check things in. */
- umask (077);
+ /* The UMASK environment variable isn't handled with the
+ others above, since we don't want to signal errors if the
+ user has asked for help. This won't work if somebody adds
+ a command-line flag to set the umask, since we'll have to
+ parse it before we get here. */
-#if HAVE_PUTENV
- /* Set LOGNAME and USER in the environment, in case they are
- already set to something else. */
+ if ((cp = getenv (CVSUMASK_ENV)) != NULL)
{
- char *env;
+ /* FIXME: Should be accepting symbolic as well as numeric mask. */
+ cvsumask = strtol (cp, &end, 8) & 0777;
+ if (*end != '\0')
+ error (1, errno, "invalid umask value in %s (%s)",
+ CVSUMASK_ENV, cp);
+ }
- env = xmalloc (sizeof "LOGNAME=" + strlen (user));
- (void) sprintf (env, "LOGNAME=%s", user);
- (void) putenv (env);
+#if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
+ /* If we are invoked with a single argument "kserver", then we are
+ running as Kerberos server as root. Do the authentication as
+ the very first thing, to minimize the amount of time we are
+ running as root. */
+ if (strcmp (command_name, "kserver") == 0)
+ {
+ kserver_authenticate_connection ();
- env = xmalloc (sizeof "USER=" + strlen (user));
- (void) sprintf (env, "USER=%s", user);
- (void) putenv (env);
+ /* Pretend we were invoked as a plain server. */
+ command_name = "server";
}
-#endif
-
- /* Pretend we were invoked as a plain server. */
- argv[0] = "server";
- }
#endif /* HAVE_KERBEROS */
#if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT)
- if (strcmp (argv[0], "pserver") == 0)
- {
- /* Gets username and password from client, authenticates, then
- switches to run as that user and sends an ACK back to the
- client. */
- authenticate_connection ();
+ if (strcmp (command_name, "pserver") == 0)
+ {
+ /* Gets username and password from client, authenticates, then
+ switches to run as that user and sends an ACK back to the
+ client. */
+ pserver_authenticate_connection ();
- /* Pretend we were invoked as a plain server. */
- argv[0] = "server";
- }
+ /* Pretend we were invoked as a plain server. */
+ command_name = "server";
+ }
#endif /* AUTH_SERVER_SUPPORT && SERVER_SUPPORT */
- /*
- * See if we are able to find a 'better' value for CVSroot in the
- * CVSADM_ROOT directory.
- */
-#ifdef SERVER_SUPPORT
- if (strcmp (argv[0], "server") == 0 && CVSroot == NULL)
- CVSADM_Root = NULL;
- else
- CVSADM_Root = Name_Root((char *) NULL, (char *) NULL);
-#else /* No SERVER_SUPPORT */
- CVSADM_Root = Name_Root((char *) NULL, (char *) NULL);
-#endif /* No SERVER_SUPPORT */
- if (CVSADM_Root != NULL)
- {
- if (CVSroot == NULL || !cvs_update_env)
- {
- CVSroot = CVSADM_Root;
- cvs_update_env = 1; /* need to update environment */
- }
-#ifdef CLIENT_SUPPORT
- else if (!getenv ("CVS_IGNORE_REMOTE_ROOT"))
-#else /* ! CLIENT_SUPPORT */
- else
-#endif /* CLIENT_SUPPORT */
- {
- /*
- * Now for the hard part, compare the two directories. If they
- * are not identical, then abort this command.
- */
- if ((fncmp (CVSroot, CVSADM_Root) != 0) &&
- !same_directories(CVSroot, CVSADM_Root))
- {
- error (0, 0, "%s value for CVS Root found in %s",
- CVSADM_Root, CVSADM_ROOT);
- error (0, 0, "does not match command line -d %s setting",
- CVSroot);
- error (1, 0,
- "you may wish to try the cvs command again without the -d option ");
- }
- }
- }
-
- /* CVSroot may need fixing up, if an access-method was specified,
- * but not a user. Later code assumes that if CVSroot contains an
- * access-method, then it also has a user. We print a warning and
- * die if we can't guarantee that.
- */
- if (CVSroot
- && *CVSroot
- && (CVSroot[0] == ':')
- && (strchr (CVSroot, '@') == NULL))
- {
- error (1, 0,
- "must also give a username if specifying access method");
- }
+ /* 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. */
- /*
- * Specifying just the '-H' flag to the sub-command causes a Usage
- * message to be displayed.
- */
- command_name = cp = argv[0];
- if (help == TRUE || (argc > 1 && strcmp (argv[1], "-H") == 0))
- argc = -1;
- else
- {
- /*
- * 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.
- */
#ifdef SERVER_SUPPORT
- if (strcmp (command_name, "server") != 0 || CVSroot != NULL)
+ if (strcmp (command_name, "server") != 0)
#endif
{
- char path[PATH_MAX];
- int save_errno;
+ char *CVSADM_Root;
+
+ /* See if we are able to find a 'better' value for CVSroot
+ in the CVSADM_ROOT directory. */
- if (!CVSroot || !*CVSroot)
- error (1, 0, "You don't have a %s environment variable",
- CVSROOT_ENV);
- (void) sprintf (path, "%s/%s", CVSroot, CVSROOTADM);
- if (!isaccessible (path, R_OK | X_OK))
+ CVSADM_Root = NULL;
+
+ /* "cvs import" shouldn't check CVS/Root; in general it
+ ignores CVS directories and CVS/Root is likely to
+ specify a different repository than the one we are
+ importing to. */
+
+ if (lookup_command_attribute (command_name)
+ & CVS_CMD_IGNORE_ADMROOT)
+ {
+ CVSADM_Root = Name_Root((char *) NULL, (char *) NULL);
+ }
+
+ if (CVSADM_Root != NULL)
{
- save_errno = errno;
- /* If this is "cvs init", the root need not exist yet. */
- if (strcmp (command_name, "init") != 0
+ if (CVSroot == NULL || !cvs_update_env)
+ {
+ 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
- /* If we are a remote client, the root need not exist
- on the client machine (FIXME: we should also skip
- the check for CVSROOTADM_HISTORY being writable;
- it shouldn't matter if there is a read-only file
- which happens to have the same name on the client
- machine). */
- && strchr (CVSroot, ':') == NULL)
+ !getenv ("CVS_IGNORE_REMOTE_ROOT") &&
#endif
+ strcmp (CVSroot, CVSADM_Root) != 0)
{
- error (0, 0,
- "Sorry, you don't have sufficient access to %s", CVSroot);
- error (1, save_errno, "%s", path);
+ /* 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;
+ }
+
}
}
- (void) strcat (path, "/");
- (void) strcat (path, CVSROOTADM_HISTORY);
- if (isfile (path) && !isaccessible (path, R_OK | W_OK))
+
+ /* 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. */
+
+ if (! CVSroot)
{
- save_errno = errno;
error (0, 0,
- "Sorry, you don't have read/write access to the history file");
- error (1, save_errno, "%s", path);
+ "No CVSROOT specified! Please use the `-d' option");
+ error (1, 0,
+ "or set the %s environment variable.", CVSROOT_ENV);
+ }
+
+ if (! *CVSroot)
+ {
+ error (0, 0,
+ "CVSROOT is set but empty! Make sure that the");
+ error (0, 0,
+ "specification of CVSROOT is legal, either via the");
+ error (0, 0,
+ "`-d' option, the %s environment variable, or the",
+ CVSROOT_ENV);
+ error (1, 0,
+ "CVS/Root file (if any).");
}
- }
- }
-#ifdef SERVER_SUPPORT
- if (strcmp (command_name, "server") == 0)
- /* This is only used for writing into the history file. Might
- be nice to have hostname and/or remote path, on the other hand
- I'm not sure whether it is worth the trouble. */
- strcpy (CurDir, "<remote>");
- else if (!getwd (CurDir))
- error (1, 0, "cannot get working directory: %s", CurDir);
-#endif
+ /* 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 (CVSroot))
+ error (1, 0, "Bad CVSROOT.");
+
+ /*
+ * 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)
+ {
+ 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);
+ }
#ifdef HAVE_PUTENV
- /* Now, see if we should update the environment with the Rcsbin value */
- if (cvs_update_env)
- {
- char *env;
+ /* Update the CVSROOT environment variable if necessary. */
- 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 */
- }
- if (rcsbin_update_env)
- {
- char *env;
+ 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. */
- env = xmalloc (strlen (RCSBIN_ENV) + strlen (Rcsbin) + 1 + 1);
- (void) sprintf (env, "%s=%s", RCSBIN_ENV, Rcsbin);
- (void) putenv (env);
- /* do not free env, as putenv has control of it */
- }
+#ifdef SERVER_SUPPORT
+ if (strcmp (command_name, "server") == 0)
+ CurDir = xstrdup ("<remote>");
+ else
#endif
+ {
+ CurDir = xgetwd ();
+ if (CurDir == NULL)
+ error (1, errno, "cannot get working directory");
+ }
- /*
- * If Rcsbin is set to something, make sure it is terminated with
- * a slash character. If not, add one.
- */
- if (*Rcsbin)
- {
- int len = strlen (Rcsbin);
- char *rcsbin;
+ if (Tmpdir == NULL || Tmpdir[0] == '\0')
+ Tmpdir = "/tmp";
- if (Rcsbin[len - 1] != '/')
+#ifdef HAVE_PUTENV
+ /* Now, see if we should update the environment with the
+ Rcsbin value */
+ if (rcsbin_update_env)
{
- rcsbin = Rcsbin;
- Rcsbin = xmalloc (len + 2); /* one for '/', one for NULL */
- (void) strcpy (Rcsbin, rcsbin);
- (void) strcat (Rcsbin, "/");
+ char *env;
+ env = xmalloc (strlen (RCSBIN_ENV) + strlen (Rcsbin) + 1 + 1);
+ (void) sprintf (env, "%s=%s", RCSBIN_ENV, Rcsbin);
+ (void) putenv (env);
+ /* do not free env, as putenv has control of it */
}
- }
+ 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
- for (cm = cmds; cm->fullname; cm++)
- {
- if (cm->nick1 && !strcmp (cp, cm->nick1))
- break;
- if (cm->nick2 && !strcmp (cp, cm->nick2))
- break;
- if (!strcmp (cp, cm->fullname))
- break;
- }
+ /*
+ * If Rcsbin is set to something, make sure it is terminated with
+ * a slash character. If not, add one.
+ */
+ if (*Rcsbin)
+ {
+ int len = strlen (Rcsbin);
+ char *rcsbin;
- if (!cm->fullname)
- usage (usg); /* no match */
- else
- {
- command_name = cm->fullname; /* Global pointer for later use */
+ if (Rcsbin[len - 1] != '/')
+ {
+ rcsbin = Rcsbin;
+ Rcsbin = xmalloc (len + 2); /* one for '/', one for NULL */
+ (void) strcpy (Rcsbin, rcsbin);
+ (void) strcat (Rcsbin, "/");
+ }
+ }
+#ifndef DONT_USE_SIGNALS
/* make sure we clean up on error */
#ifdef SIGHUP
(void) SIG_register (SIGHUP, main_cleanup);
@@ -743,39 +833,44 @@ error 0 %s: no such user\n", user);
(void) SIG_register (SIGTERM, main_cleanup);
(void) SIG_register (SIGTERM, Lock_Cleanup);
#endif
+#endif /* !DONT_USE_SIGNALS */
gethostname(hostname, sizeof (hostname));
-#ifdef HAVE_SETVBUF
- /*
- * Make stdout line buffered, so 'tail -f' can monitor progress.
- * Patch creates too much output to monitor and it runs slowly.
- */
- if (strcmp (cm->fullname, "patch"))
- (void) setvbuf (stdout, (char *) NULL, _IOLBF, 0);
-#endif
+#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);
+ read_cvsrc (&argc, &argv, command_name);
-#ifdef CLIENT_SUPPORT
- /* If cvsroot contains a colon, try to do it via the protocol. */
- {
- char *p = CVSroot == NULL ? NULL : strchr (CVSroot, ':');
- if (p)
- err = (*(cm->client_func)) (argc, argv);
- else
- err = (*(cm->func)) (argc, argv);
- }
-#else /* No CLIENT_SUPPORT */
- err = (*(cm->func)) (argc, argv);
+ } /* end of stuff that gets done if the user DOESN'T ask for help */
+
+ err = (*(cm->func)) (argc, argv);
-#endif /* No CLIENT_SUPPORT */
+ 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. */
+ Create_Root (NULL, CVSroot);
}
+
Lock_Cleanup ();
- if (err)
- return (EXIT_FAILURE);
- return 0;
+
+#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
+
+ /* This is exit rather than return because apparently that keeps
+ some tools which check for memory leaks happier. */
+ exit (err ? EXIT_FAILURE : 0);
}
char *
@@ -784,7 +879,7 @@ Make_Date (rawdate)
{
struct tm *ftm;
time_t unixtime;
- char date[256]; /* XXX bigger than we'll ever need? */
+ char date[MAXDATELEN];
char *ret;
unixtime = get_date (rawdate, (struct timeb *) NULL);
@@ -810,5 +905,5 @@ usage (cpp)
(void) fprintf (stderr, *cpp++, program_name, command_name);
for (; *cpp; cpp++)
(void) fprintf (stderr, *cpp);
- exit (EXIT_FAILURE);
+ error_exit ();
}
OpenPOWER on IntegriCloud