diff options
Diffstat (limited to 'contrib/cvs/src/main.c')
-rw-r--r-- | contrib/cvs/src/main.c | 999 |
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 (); } |