summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/main.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>1996-08-20 23:46:10 +0000
committerpeter <peter@FreeBSD.org>1996-08-20 23:46:10 +0000
commit8982e501c77217c860f79bba431f46a62b607a21 (patch)
tree70187fdf5be4cbefd0baf46bddac7e5e32c13c24 /contrib/cvs/src/main.c
parent01ee40fd6a76f6ff7ef247fc1b2cf6e337f216c5 (diff)
downloadFreeBSD-src-8982e501c77217c860f79bba431f46a62b607a21.zip
FreeBSD-src-8982e501c77217c860f79bba431f46a62b607a21.tar.gz
Import of slightly trimmed cvs-1.8 distribution. Generated files
and non-unix code has been left out.
Diffstat (limited to 'contrib/cvs/src/main.c')
-rw-r--r--contrib/cvs/src/main.c814
1 files changed, 814 insertions, 0 deletions
diff --git a/contrib/cvs/src/main.c b/contrib/cvs/src/main.c
new file mode 100644
index 0000000..daa0230
--- /dev/null
+++ b/contrib/cvs/src/main.c
@@ -0,0 +1,814 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License
+ * as specified in the README file that comes with the CVS 1.4 kit.
+ *
+ * This is the main C driver for the CVS system.
+ *
+ * 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"
+
+#ifdef HAVE_WINSOCK_H
+#include <winsock.h>
+#else
+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 = "";
+
+/*
+ * Since some systems don't define this...
+ */
+#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;
+int really_quiet = FALSE;
+int quiet = FALSE;
+int trace = FALSE;
+int noexec = FALSE;
+int logoff = FALSE;
+mode_t cvsumask = UMASK_DFLT;
+
+char *CurDir;
+
+/*
+ * Defaults, for the environment variables that are not set
+ */
+char *Rcsbin = RCSBIN_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
+{
+ 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") */
+ 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 }
+#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),
+#ifdef AUTH_CLIENT_SUPPORT
+ CMD_ENTRY("login", "logon", "lgn", login, login),
+#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),
+#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),
+#endif
+ CMD_ENTRY(NULL, NULL, NULL, NULL, NULL),
+
+#undef CMD_ENTRY
+};
+
+static const char *const usg[] =
+{
+ "Usage: %s [cvs-options] command [command-options] [files...]\n",
+ " Where 'cvs-options' are:\n",
+ " -H Displays Usage information for command\n",
+ " -Q Cause CVS to be really quiet.\n",
+ " -q Cause CVS to be somewhat quiet.\n",
+ " -r Make checked-out files read-only\n",
+ " -w Make checked-out files read-write (default)\n",
+ " -l Turn History logging off\n",
+ " -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",
+ " -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",
+#endif
+ " -s VAR=VAL Set CVS user variable.\n",
+ "\n",
+ " and where 'command' is: add, admin, etc. (use the --help-commands\n",
+ " option for a list of commands)\n",
+ NULL,
+};
+
+static const char *const cmd_usage[] =
+{
+ "CVS commands are:\n",
+ " add Adds a new file/directory to the repository\n",
+ " admin Administration front end for rcs\n",
+ " annotate Show 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",
+ " 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",
+#ifdef AUTH_CLIENT_SUPPORT
+ " login Prompt for password for authenticating server.\n",
+#endif /* AUTH_CLIENT_SUPPORT */
+ " rdiff '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",
+ " 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",
+ " watch Set watches\n",
+ " watchers See who is watching a file\n",
+ NULL,
+};
+
+static RETSIGTYPE
+main_cleanup ()
+{
+ exit (EXIT_FAILURE);
+}
+
+static void
+error_cleanup PROTO((void))
+{
+ Lock_Cleanup();
+#ifdef SERVER_SUPPORT
+ if (server_active)
+ server_cleanup (0);
+#endif
+}
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ 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;
+ static struct option long_options[] =
+ {
+ {"help", 0, &help, TRUE},
+ {"version", 0, &version_flag, TRUE},
+ {"help-commands", 0, &help_commands, TRUE},
+ {0, 0, 0, 0}
+ };
+ /* `getopt_long' stores the option index here, but right now we
+ don't use it. */
+ int option_index = 0;
+
+ error_set_cleanup (error_cleanup);
+
+/* The socket subsystems on NT and OS2 must be initialized before use */
+#ifdef INITIALIZE_SOCKET_SUBSYSTEM
+ INITIALIZE_SOCKET_SUBSYSTEM();
+#endif /* INITIALIZE_SOCKET_SUBSYSTEM */
+
+ /*
+ * Just save the last component of the path for error messages
+ */
+ program_path = xstrdup (argv[0]);
+ 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;
+ if ((cp = getenv (RCSBIN_ENV)) != NULL)
+ {
+ Rcsbin = cp;
+ rcsbin_update_env = 0; /* it's already there */
+ }
+ if ((cp = getenv (EDITOR1_ENV)) != NULL)
+ Editor = cp;
+ else if ((cp = getenv (EDITOR2_ENV)) != NULL)
+ Editor = cp;
+ else if ((cp = getenv (EDITOR3_ENV)) != NULL)
+ Editor = cp;
+ if ((cp = getenv (CVSROOT_ENV)) != NULL)
+ {
+ CVSroot = cp;
+ cvs_update_env = 0; /* it's already there */
+ }
+ 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. */
+ optind = 1;
+
+
+ /* We have to parse the options twice because else there is no
+ chance to avoid reading the global options from ".cvsrc". Set
+ opterr to 0 for avoiding error messages about invalid options.
+ */
+ opterr = 0;
+
+ while ((c = getopt_long
+ (argc, argv, "f", NULL, NULL))
+ != EOF)
+ {
+ if (c == 'f')
+ use_cvsrc = FALSE;
+ }
+
+ /*
+ * Scan cvsrc file for global options.
+ */
+ if (use_cvsrc)
+ read_cvsrc (&argc, &argv, "cvs");
+
+ optind = 1;
+ opterr = 1;
+
+ while ((c = getopt_long
+ (argc, argv, "Qqrwtnlvb:e:d:Hfz:s:", long_options, &option_index))
+ != EOF)
+ {
+ switch (c)
+ {
+ case 0:
+ /* getopt_long took care of setting the flag. */
+ break;
+ case 'Q':
+ really_quiet = TRUE;
+ /* FALL THROUGH */
+ case 'q':
+ quiet = TRUE;
+ break;
+ case 'r':
+ cvswrite = FALSE;
+ break;
+ case 'w':
+ cvswrite = TRUE;
+ break;
+ case 't':
+ trace = TRUE;
+ break;
+ case 'n':
+ noexec = TRUE;
+ case 'l': /* Fall through */
+ logoff = TRUE;
+ break;
+ case 'v':
+ version_flag = TRUE;
+ break;
+ case 'b':
+ Rcsbin = optarg;
+ rcsbin_update_env = 1; /* need to update environment */
+ break;
+ case 'e':
+ Editor = optarg;
+ break;
+ case 'd':
+ CVSroot = optarg;
+ cvs_update_env = 1; /* need to update environment */
+ break;
+ case 'H':
+ use_cvsrc = FALSE; /* this ensure that cvs -H works */
+ help = TRUE;
+ break;
+ case 'f':
+ use_cvsrc = FALSE;
+ break;
+ case 'z':
+#ifdef CLIENT_SUPPORT
+ gzip_level = atoi (optarg);
+ if (gzip_level <= 0 || gzip_level > 9)
+ error (1, 0,
+ "gzip compression level must be between 1 and 9");
+#endif
+ /* If no CLIENT_SUPPORT, we just silently ignore the gzip
+ level, so that users can have it in their .cvsrc and not
+ cause any trouble. */
+ break;
+ case 's':
+ variable_set (optarg);
+ 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)
+ {
+ 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);
+ }
+
+ 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);
+ }
+
+ /* 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);
+ }
+
+ pw = getpwnam (user);
+ if (pw == NULL)
+ {
+ printf ("E Fatal error, aborting.\n\
+error 0 %s: no such user\n", user);
+ exit (EXIT_FAILURE);
+ }
+
+ 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);
+
+#if HAVE_PUTENV
+ /* Set LOGNAME and USER in the environment, in case they are
+ already set to something else. */
+ {
+ char *env;
+
+ env = xmalloc (sizeof "LOGNAME=" + strlen (user));
+ (void) sprintf (env, "LOGNAME=%s", user);
+ (void) putenv (env);
+
+ env = xmalloc (sizeof "USER=" + strlen (user));
+ (void) sprintf (env, "USER=%s", user);
+ (void) putenv (env);
+ }
+#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 ();
+
+ /* Pretend we were invoked as a plain server. */
+ argv[0] = "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");
+ }
+
+ /*
+ * 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)
+#endif
+ {
+ char path[PATH_MAX];
+ int save_errno;
+
+ 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))
+ {
+ save_errno = errno;
+ /* If this is "cvs init", the root need not exist yet. */
+ if (strcmp (command_name, "init") != 0
+#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)
+#endif
+ {
+ error (0, 0,
+ "Sorry, you don't have sufficient access to %s", CVSroot);
+ 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);
+ }
+ }
+ }
+
+#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
+
+#ifdef HAVE_PUTENV
+ /* Now, see if we should update the environment with the Rcsbin value */
+ 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 */
+ }
+ if (rcsbin_update_env)
+ {
+ 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 */
+ }
+#endif
+
+ /*
+ * 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 (Rcsbin[len - 1] != '/')
+ {
+ rcsbin = Rcsbin;
+ Rcsbin = xmalloc (len + 2); /* one for '/', one for NULL */
+ (void) strcpy (Rcsbin, rcsbin);
+ (void) strcat (Rcsbin, "/");
+ }
+ }
+
+ 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 (!cm->fullname)
+ usage (usg); /* no match */
+ else
+ {
+ command_name = cm->fullname; /* Global pointer for later use */
+
+ /* 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
+
+ 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
+
+ if (use_cvsrc)
+ 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);
+
+#endif /* No CLIENT_SUPPORT */
+ }
+ Lock_Cleanup ();
+ if (err)
+ return (EXIT_FAILURE);
+ return 0;
+}
+
+char *
+Make_Date (rawdate)
+ char *rawdate;
+{
+ struct tm *ftm;
+ time_t unixtime;
+ char date[256]; /* XXX bigger than we'll ever need? */
+ char *ret;
+
+ unixtime = get_date (rawdate, (struct timeb *) NULL);
+ if (unixtime == (time_t) - 1)
+ error (1, 0, "Can't parse date/time: %s", rawdate);
+#ifdef HAVE_RCS5
+ ftm = gmtime (&unixtime);
+#else
+ ftm = localtime (&unixtime);
+#endif
+ (void) sprintf (date, DATEFORM,
+ ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
+ ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
+ ftm->tm_min, ftm->tm_sec);
+ ret = xstrdup (date);
+ return (ret);
+}
+
+void
+usage (cpp)
+ register const char *const *cpp;
+{
+ (void) fprintf (stderr, *cpp++, program_name, command_name);
+ for (; *cpp; cpp++)
+ (void) fprintf (stderr, *cpp);
+ exit (EXIT_FAILURE);
+}
OpenPOWER on IntegriCloud