#include <assert.h>
#include "cvs.h"
#ifdef HAVE_WINSOCK_H
#include <winsock.h>
#else
extern int gethostname ();
#endif
const char *program_name;
const char *program_path;
const char *cvs_cmd_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;
List *root_directories = NULL;
static const struct cmd
{
char *fullname;
char *nick1;
char *nick2;
int (*func) ();
unsigned long attr;
} cmds[] =
{
{ "add", "ad", "new", add, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "admin", "adm", "rcs", admin, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "annotate", "ann", NULL, annotate, CVS_CMD_USES_WORK_DIR },
{ "checkout", "co", "get", checkout, 0 },
{ "commit", "ci", "com", commit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "diff", "di", "dif", diff, CVS_CMD_USES_WORK_DIR },
{ "edit", NULL, NULL, edit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "editors", NULL, NULL, editors, CVS_CMD_USES_WORK_DIR },
{ "export", "exp", "ex", checkout, CVS_CMD_USES_WORK_DIR },
{ "history", "hi", "his", history, CVS_CMD_USES_WORK_DIR },
{ "import", "im", "imp", import, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR | CVS_CMD_IGNORE_ADMROOT},
{ "init", NULL, NULL, init, CVS_CMD_MODIFIES_REPOSITORY },
#if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
{ "kserver", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
#endif
{ "log", "lo", NULL, cvslog, CVS_CMD_USES_WORK_DIR },
#ifdef AUTH_CLIENT_SUPPORT
{ "login", "logon", "lgn", login, 0 },
{ "logout", NULL, NULL, logout, 0 },
#endif
#if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
{ "pserver", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
#endif
{ "rannotate","rann", "ra", annotate, 0 },
{ "rdiff", "patch", "pa", patch, 0 },
{ "release", "re", "rel", release, 0 },
{ "remove", "rm", "delete", cvsremove, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "rlog", "rl", NULL, cvslog, 0 },
{ "rtag", "rt", "rfreeze", cvstag, CVS_CMD_MODIFIES_REPOSITORY },
#ifdef SERVER_SUPPORT
{ "server", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
#endif
{ "status", "st", "stat", cvsstatus, CVS_CMD_USES_WORK_DIR },
{ "tag", "ta", "freeze", cvstag, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "unedit", NULL, NULL, unedit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "update", "up", "upd", update, CVS_CMD_USES_WORK_DIR },
{ "version", "ve", "ver", version, 0 },
{ "watch", NULL, NULL, watch, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "watchers", NULL, NULL, watchers, CVS_CMD_USES_WORK_DIR },
{ NULL, NULL, NULL, NULL, 0 },
};
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",
" the CVS home page at http://www.cvshome.org/ 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",
#if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
" kserver Kerberos server mode\n",
#endif
" 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
#if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
" pserver Password server mode\n",
#endif
" rannotate Show last revision where each line of module was modified\n",
" 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",
" rlog Print out history information for a module\n",
" rtag Add a symbolic tag to a module\n",
#ifdef SERVER_SUPPORT
" server Server mode\n",
#endif
" 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",
" version Show current CVS version(s)\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",
" -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",
" -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 int
set_root_directory (p, ignored)
Node *p;
void *ignored;
{
if (current_parsed_root == NULL && p->data != NULL)
{
current_parsed_root = p->data;
return 1;
}
return 0;
}
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;
{
const struct cmd *cm;
for (cm = cmds; cm->fullname; cm++)
{
if (strcmp (cmd_name, cm->fullname) == 0)
break;
}
if (!cm->fullname)
error (1, 0, "unknown command: %s", cmd_name);
return cm->attr;
}
static RETSIGTYPE
main_cleanup (sig)
int sig;
{
#ifndef DONT_USE_SIGNALS
const char *name;
char temp[10];
switch (sig)
{
#ifdef SIGABRT
case SIGABRT:
name = "abort";
break;
#endif
#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;
{
cvsroot_t *CVSroot_parsed = NULL;
int cvsroot_update_env = 1;
char *cp, *end;
const struct cmd *cm;
int c, err = 0;
int tmpdir_update_env;
int free_Editor = 0;
int free_Tmpdir = 0;
int help = 0;
static const char short_options[] = "+QqrwtnRvb:T:e:d:Hfz:s:xa";
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;
#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
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 (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, short_options, long_options, &option_index))
!= 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, short_options, 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;
logoff = 1;
break;
case 'v':
(void) fputs ("\n", stdout);
version (0, (char **) NULL);
(void) fputs ("\n", stdout);
(void) fputs ("\
Copyright (c) 1989-2004 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':
if (CVSroot_cmdline != NULL)
free (CVSroot_cmdline);
CVSroot_cmdline = xstrdup (optarg);
break;
case 'H':
help = 1;
break;
case 'f':
use_cvsrc = 0;
break;
case 'z':
#ifdef CLIENT_SUPPORT
gzip_level = strtol (optarg, &end, 10);
if (*end != '\0' || gzip_level < 0 || gzip_level > 9)
error (1, 0,
"gzip compression level must be between 0 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);
cvs_cmd_name = argv[0];
for (cm = cmds; cm->fullname; cm++)
{
if (cm->nick1 && !strcmp (cvs_cmd_name, cm->nick1))
break;
if (cm->nick2 && !strcmp (cvs_cmd_name, cm->nick2))
break;
if (!strcmp (cvs_cmd_name, cm->fullname))
break;
}
if (!cm->fullname)
{
fprintf (stderr, "Unknown command: `%s'\n\n", cvs_cmd_name);
usage (cmd_usage);
}
else
cvs_cmd_name = cm->fullname;
if (help)
{
argc = -1;
err = (*(cm->func)) (argc, argv);
}
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);
}
#ifdef SERVER_SUPPORT
# ifdef HAVE_KERBEROS
if (strcmp (cvs_cmd_name, "kserver") == 0)
{
kserver_authenticate_connection ();
cvs_cmd_name = "server";
}
# endif
# if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)
if (strcmp (cvs_cmd_name, "pserver") == 0)
{
pserver_authenticate_connection ();
cvs_cmd_name = "server";
}
# endif
server_active = strcmp (cvs_cmd_name, "server") == 0;
#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 SIGABRT
(void) SIG_register (SIGABRT, main_cleanup);
#endif
#ifdef SIGHUP
(void) SIG_register (SIGHUP, main_cleanup);
#endif
#ifdef SIGINT
(void) SIG_register (SIGINT, main_cleanup);
#endif
#ifdef SIGQUIT
(void) SIG_register (SIGQUIT, main_cleanup);
#endif
#ifdef SIGPIPE
(void) SIG_register (SIGPIPE, main_cleanup);
#endif
#ifdef SIGTERM
(void) SIG_register (SIGTERM, main_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, cvs_cmd_name);
#ifdef SERVER_SUPPORT
if (!server_active)
#endif
{
if (CVSroot_cmdline)
{
if (!(CVSroot_parsed = parse_cvsroot (CVSroot_cmdline)))
error (1, 0, "Bad CVSROOT: `%s'.", CVSroot_cmdline);
}
if (!CVSroot_parsed
&& !(cm->attr & CVS_CMD_IGNORE_ADMROOT)
)
CVSroot_parsed = Name_Root (NULL, NULL);
if (!CVSroot_parsed)
{
char *tmp = getenv (CVSROOT_ENV);
if (tmp)
{
if (!(CVSroot_parsed = parse_cvsroot (tmp)))
error (1, 0, "Bad CVSROOT: `%s'.", tmp);
cvsroot_update_env = 0;
}
}
#ifdef CVSROOT_DFLT
if (!CVSroot_parsed)
{
if (!(CVSroot_parsed = parse_cvsroot (CVSROOT_DFLT)))
error (1, 0, "Bad CVSROOT: `%s'.", CVSROOT_DFLT);
}
#endif
if (!CVSroot_parsed)
{
error (0, 0,
"No CVSROOT specified! Please use the `-d' option");
error (1, 0,
"or set the %s environment variable.", CVSROOT_ENV);
}
}
assert (root_directories == NULL);
root_directories = getlist ();
if (CVSroot_parsed)
{
Node *n;
n = getnode ();
n->type = NT_UNKNOWN;
n->key = xstrdup (CVSroot_parsed->original);
n->data = CVSroot_parsed;
if (addnode (root_directories, n))
error (1, 0, "cannot add initial CVSROOT %s", n->key);
}
assert (current_parsed_root == NULL);
while (
#ifdef SERVER_SUPPORT
server_active ||
#endif
walklist (root_directories, set_root_directory, NULL)
)
{
#ifdef SERVER_SUPPORT
if (!server_active)
#endif
{
if (trace)
fprintf (stderr, "%s-> main loop with CVSROOT=%s\n",
CLIENT_SERVER_STR, current_parsed_root->original);
#ifdef CLIENT_SUPPORT
if (!current_parsed_root->isremote)
#endif
{
char *path;
int save_errno;
path = xmalloc (strlen (current_parsed_root->directory)
+ strlen (CVSROOTADM) + 2);
sprintf (path, "%s/%s", current_parsed_root->directory,
CVSROOTADM);
if (!isaccessible (path, R_OK | X_OK))
{
save_errno = errno;
if (strcmp (cvs_cmd_name, "init"))
error (1, save_errno, "%s", path);
}
free (path);
}
#ifdef HAVE_PUTENV
if (cvsroot_update_env)
{
static char *prev;
char *env;
size_t dummy;
env = xmalloc (strlen (CVSROOT_ENV)
+ strlen (current_parsed_root->original)
+ 2);
sprintf (env, "%s=%s", CVSROOT_ENV,
current_parsed_root->original);
(void) putenv (env);
if (prev != NULL)
free (prev);
prev = env;
}
#endif
}
if (1
#ifdef SERVER_SUPPORT
&& !server_active
#endif
#ifdef CLIENT_SUPPORT
&& !current_parsed_root->isremote
#endif
)
{
parse_config (current_parsed_root->directory);
}
#ifdef CLIENT_SUPPORT
if (current_parsed_root != NULL && current_parsed_root->isremote)
{
if (dirs_sent_to_server != NULL)
dellist (&dirs_sent_to_server);
dirs_sent_to_server = getlist ();
}
#endif
err = (*(cm->func)) (argc, argv);
#ifdef SERVER_SUPPORT
if (!server_active)
#endif
{
Node *n = findnode (root_directories,
current_parsed_root->original);
assert (n != NULL);
assert (n->data != NULL);
free_cvsroot_t (n->data);
n->data = NULL;
current_parsed_root = NULL;
}
#ifdef SERVER_SUPPORT
if (server_active)
{
server_active = 0;
break;
}
#endif
}
dellist (&root_directories);
}
Lock_Cleanup ();
free ((char *)program_path);
if (CVSroot_cmdline != NULL)
free (CVSroot_cmdline);
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
date_to_internet (dest, source)
char *dest;
const char *source;
{
struct tm date;
date_to_tm (&date, source);
tm_to_internet (dest, &date);
}
void
date_to_tm (dest, source)
struct tm *dest;
const char *source;
{
if (sscanf (source, SDATEFORM,
&dest->tm_year, &dest->tm_mon, &dest->tm_mday,
&dest->tm_hour, &dest->tm_min, &dest->tm_sec)
!= 6)
error (0, 0, "internal error: bad date %s", source);
if (dest->tm_year > 100)
dest->tm_year -= 1900;
dest->tm_mon -= 1;
}
void
tm_to_internet (dest, source)
char *dest;
const struct tm *source;
{
static const char *const month_names[] =
{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
sprintf (dest, "%d %s %d %02d:%02d:%02d -0000", source->tm_mday,
source->tm_mon < 0 || source->tm_mon > 11 ? "???" : month_names[source->tm_mon],
source->tm_year + 1900, source->tm_hour, source->tm_min, source->tm_sec);
}
void
usage (cpp)
register const char *const *cpp;
{
(void) fprintf (stderr, *cpp++, program_name, cvs_cmd_name);
for (; *cpp; cpp++)
(void) fprintf (stderr, *cpp);
error_exit ();
}