#include "cvs.h"
#ifdef HAVE_WINSOCK_H
#include <winsock.h>
#else
extern int gethostname ();
#endif
char *program_name;
char *program_path;
char *command_name;
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 256
#endif
char hostname[MAXHOSTNAMELEN];
int use_editor = 1;
int use_cvsrc = 1;
int cvswrite = !CVSREAD_DFLT;
int really_quiet = 0;
int quiet = 0;
int trace = 0;
int noexec = 0;
int readonlyfs = 0;
int logoff = 0;
int top_level_admin = 0;
mode_t cvsumask = UMASK_DFLT;
char *CurDir;
char *Tmpdir = TMPDIR_DFLT;
char *Editor = EDITOR_DFLT;
static const struct cmd
{
char *fullname;
char *nick1;
char *nick2;
int (*func) ();
} cmds[] =
{
{ "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 },
#endif
{ "log", "lo", "rlog", cvslog },
#ifdef AUTH_CLIENT_SUPPORT
{ "login", "logon", "lgn", login },
{ "logout", NULL, NULL, logout },
#ifdef SERVER_SUPPORT
{ "pserver", NULL, NULL, server },
#endif
#endif
{ "rdiff", "patch", "pa", patch },
{ "release", "re", "rel", release },
{ "remove", "rm", "delete", cvsremove },
{ "status", "st", "stat", cvsstatus },
{ "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
{ "server", NULL, NULL, server },
#endif
{ NULL, NULL, NULL, NULL },
};
static const char *const usg[] =
{
"Usage: %s [cvs-options] command [command-options-and-arguments]\n",
" where cvs-options are -q, -n, etc.\n",
" (specify --help-options for a list of options)\n",
" where command is add, admin, etc.\n",
" (specify --help-commands for a list of commands\n",
" or --help-synonyms for a list of command synonyms)\n",
" where command-options-and-arguments depend on the specific command\n",
" (specify -H followed by a command name for command-specific help)\n",
" Specify --help to receive this message\n",
"\n",
"The Concurrent Versions System (CVS) is a tool for version control.\n",
"For CVS updates and additional information, see\n",
" Cyclic Software at http://www.cyclic.com/ or\n",
" Pascal Molli's CVS site at http://www.loria.fr/~molli/cvs-index.html\n",
NULL,
};
static const char *const cmd_usage[] =
{
"CVS commands are:\n",
" add Add a new file/directory to the repository\n",
" admin Administration front end for rcs\n",
" annotate Show last revision where each line was modified\n",
" checkout Checkout sources for editing\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",
" export Export sources from CVS, similar to checkout\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
" rdiff Create 'patch' format diffs between releases\n",
" release Indicate that a Module is no longer in use\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",
" update Bring work tree in sync with repository\n",
" watch Set watches\n",
" watchers See who is watching a file\n",
"(Specify the --help option for a list of other help options)\n",
NULL,
};
static const char *const opt_usage[] =
{
"CVS global options (specified before the command name) are:\n",
" -H Displays usage information for command.\n",
" -Q Cause CVS to be really quiet.\n",
" -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",
" -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 compression level '#' for net traffic.\n",
#ifdef ENCRYPTION
" -x Encrypt all net traffic.\n",
#endif
" -a Authenticate all net traffic.\n",
#endif
" -s VAR=VAL Set CVS user variable.\n",
"(Specify the --help option for a list of other help options)\n",
NULL
};
static const char * const*
cmd_synonyms ()
{
char ** synonyms;
char ** line;
const struct cmd *c = &cmds[0];
int numcmds = 3;
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++ = "(Specify the --help option for a list of other help options)\n";
*line = NULL;
return (const char * const*) synonyms;
}
unsigned long int
lookup_command_attribute (cmd_name)
char *cmd_name;
{
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;
}
if ((strcmp (cmd_name, "annotate") != 0) &&
(strcmp (cmd_name, "checkout") != 0) &&
(strcmp (cmd_name, "diff") != 0) &&
(strcmp (cmd_name, "rdiff") != 0) &&
(strcmp (cmd_name, "update") != 0) &&
(strcmp (cmd_name, "history") != 0) &&
(strcmp (cmd_name, "editors") != 0) &&
(strcmp (cmd_name, "export") != 0) &&
(strcmp (cmd_name, "history") != 0) &&
(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:
sprintf (temp, "%d", sig);
name = temp;
break;
}
error (1, 0, "received %s signal", name);
#endif
}
int
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;
int tmpdir_update_env, cvs_update_env;
int free_CVSroot = 0;
int free_Editor = 0;
int free_Tmpdir = 0;
int help = 0;
static struct option long_options[] =
{
{"help", 0, NULL, 'H'},
{"version", 0, NULL, 'v'},
{"help-commands", 0, NULL, 1},
{"help-synonyms", 0, NULL, 2},
{"help-options", 0, NULL, 4},
{"allow-root", required_argument, NULL, 3},
{0, 0, 0, 0}
};
int option_index = 0;
int need_to_create_root = 0;
#ifdef SYSTEM_INITIALIZE
SYSTEM_INITIALIZE (&argc, &argv);
#endif
#ifdef HAVE_TZSET
tzset ();
#endif
program_path = xstrdup (argv[0]);
#ifdef ARGV0_NOT_PROGRAM_NAME
program_name = "cvs";
#else
program_name = last_component (argv[0]);
#endif
cvs_update_env = 0;
tmpdir_update_env = *Tmpdir;
if ((cp = getenv (TMPDIR_ENV)) != NULL)
{
Tmpdir = cp;
tmpdir_update_env = 0;
}
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;
}
if (getenv (CVSREAD_ENV) != NULL)
cvswrite = 0;
if (getenv (CVSREADONLYFS_ENV) != NULL) {
readonlyfs = 1;
logoff = 1;
}
optind = 0;
opterr = 0;
while ((c = getopt_long
(argc, argv, "+f", NULL, NULL))
!= EOF)
{
if (c == 'f')
use_cvsrc = 0;
}
if (use_cvsrc)
read_cvsrc (&argc, &argv, "cvs");
optind = 0;
opterr = 1;
while ((c = getopt_long
(argc, argv, "+QqrwtnRlvb:T:e:d:Hfz:s:xa", long_options, &option_index))
!= EOF)
{
switch (c)
{
case 1:
usage (cmd_usage);
break;
case 2:
usage (cmd_synonyms());
break;
case 4:
usage (opt_usage);
break;
case 3:
root_allow_add (optarg);
break;
case 'Q':
really_quiet = 1;
case 'q':
quiet = 1;
break;
case 'r':
cvswrite = 0;
break;
case 'w':
cvswrite = 1;
break;
case 't':
trace = 1;
break;
case 'R':
readonlyfs = 1;
logoff = 1;
break;
case 'n':
noexec = 1;
case 'l':
logoff = 1;
break;
case 'v':
(void) fputs (version_string, stdout);
(void) fputs (config_string, stdout);
(void) fputs ("\n", stdout);
(void) fputs ("\
Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\
Jeff Polk, and other authors\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);
(void) fputs ("\n", stdout);
(void) fputs ("Specify the --help option for further information about CVS\n", stdout);
exit (0);
break;
case 'b':
break;
case 'T':
Tmpdir = xstrdup (optarg);
free_Tmpdir = 1;
tmpdir_update_env = 1;
break;
case 'e':
Editor = xstrdup (optarg);
free_Editor = 1;
break;
case 'd':
CVSroot = xstrdup (optarg);
free_CVSroot = 1;
cvs_update_env = 1;
break;
case 'H':
help = 1;
break;
case 'f':
use_cvsrc = 0;
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
break;
case 's':
variable_set (optarg);
break;
case 'x':
#ifdef CLIENT_SUPPORT
cvsencrypt = 1;
#endif
break;
case 'a':
#ifdef CLIENT_SUPPORT
cvsauthenticate = 1;
#endif
break;
case '?':
default:
usage (usg);
}
}
argc -= optind;
argv += optind;
if (argc < 1)
usage (usg);
command_name = argv[0];
for (cm = cmds; cm->fullname; cm++)
{
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;
}
if (!cm->fullname)
usage (cmd_usage);
else
command_name = cm->fullname;
if (strcmp (argv[0], "rlog") == 0)
{
error (0, 0, "warning: the rlog command is deprecated");
error (0, 0, "use the synonymous log command instead");
}
if (help)
argc = -1;
else
{
if ((cp = getenv (CVSUMASK_ENV)) != NULL)
{
cvsumask = strtol (cp, &end, 8) & 0777;
if (*end != '\0')
error (1, errno, "invalid umask value in %s (%s)",
CVSUMASK_ENV, cp);
}
#if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
if (strcmp (command_name, "kserver") == 0)
{
kserver_authenticate_connection ();
command_name = "server";
}
#endif
#if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
if (strcmp (command_name, "pserver") == 0)
{
pserver_authenticate_connection ();
command_name = "server";
}
#endif
#ifdef SERVER_SUPPORT
server_active = strcmp (command_name, "server") == 0;
if (!server_active)
#endif
{
char *CVSADM_Root;
CVSADM_Root = NULL;
if (lookup_command_attribute (command_name)
& CVS_CMD_IGNORE_ADMROOT)
{
CVSADM_Root = Name_Root((char *) NULL, (char *) NULL);
}
if (CVSADM_Root != NULL)
{
if (CVSroot == NULL || !cvs_update_env)
{
CVSroot = CVSADM_Root;
cvs_update_env = 1;
}
else if (
#ifdef CLIENT_SUPPORT
!getenv ("CVS_IGNORE_REMOTE_ROOT") &&
#endif
strcmp (CVSroot, CVSADM_Root) != 0)
{
if (lookup_command_attribute (command_name)
& CVS_CMD_USES_WORK_DIR)
{
need_to_create_root = 1;
}
}
}
if (! CVSroot)
{
error (0, 0,
"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).");
}
if (parse_cvsroot (CVSroot))
error (1, 0, "Bad CVSROOT.");
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 (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
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);
}
#endif
}
#ifdef SERVER_SUPPORT
if (server_active)
CurDir = xstrdup ("<remote>");
else
#endif
{
CurDir = xgetwd ();
if (CurDir == NULL)
error (1, errno, "cannot get working directory");
}
if (Tmpdir == NULL || Tmpdir[0] == '\0')
Tmpdir = "/tmp";
#ifdef HAVE_PUTENV
if (tmpdir_update_env)
{
char *env;
env = xmalloc (strlen (TMPDIR_ENV) + strlen (Tmpdir) + 1 + 1);
(void) sprintf (env, "%s=%s", TMPDIR_ENV, Tmpdir);
(void) putenv (env);
}
#endif
#ifndef DONT_USE_SIGNALS
#ifdef SIGHUP
(void) SIG_register (SIGHUP, main_cleanup);
(void) SIG_register (SIGHUP, Lock_Cleanup);
#endif
#ifdef SIGINT
(void) SIG_register (SIGINT, main_cleanup);
(void) SIG_register (SIGINT, Lock_Cleanup);
#endif
#ifdef SIGQUIT
(void) SIG_register (SIGQUIT, main_cleanup);
(void) SIG_register (SIGQUIT, Lock_Cleanup);
#endif
#ifdef SIGPIPE
(void) SIG_register (SIGPIPE, main_cleanup);
(void) SIG_register (SIGPIPE, Lock_Cleanup);
#endif
#ifdef SIGTERM
(void) SIG_register (SIGTERM, main_cleanup);
(void) SIG_register (SIGTERM, Lock_Cleanup);
#endif
#endif
gethostname(hostname, sizeof (hostname));
#ifdef KLUDGE_FOR_WNT_TESTSUITE
(void) setvbuf (stdout, (char *) NULL, _IONBF, 0);
(void) setvbuf (stderr, (char *) NULL, _IONBF, 0);
#endif
if (use_cvsrc)
read_cvsrc (&argc, &argv, command_name);
if (1
#ifdef SERVER_SUPPORT
&& !server_active
#endif
#ifdef CLIENT_SUPPORT
&& !client_active
#endif
)
{
parse_config (CVSroot_directory);
}
}
err = (*(cm->func)) (argc, argv);
if (need_to_create_root)
{
Create_Root (NULL, CVSroot);
}
Lock_Cleanup ();
free (program_path);
if (free_CVSroot)
free (CVSroot);
if (free_Editor)
free (Editor);
if (free_Tmpdir)
free (Tmpdir);
root_allow_free ();
#ifdef SYSTEM_CLEANUP
SYSTEM_CLEANUP ();
#endif
exit (err ? EXIT_FAILURE : 0);
return 0;
}
char *
Make_Date (rawdate)
char *rawdate;
{
time_t unixtime;
unixtime = get_date (rawdate, (struct timeb *) NULL);
if (unixtime == (time_t) - 1)
error (1, 0, "Can't parse date/time: %s", rawdate);
return date_from_time_t (unixtime);
}
char *
date_from_time_t (unixtime)
time_t unixtime;
{
struct tm *ftm;
char date[MAXDATELEN];
char *ret;
ftm = gmtime (&unixtime);
if (ftm == NULL)
ftm = localtime (&unixtime);
(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);
error_exit ();
}