#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) 2005 Free Software Foundation, Inc.\n\
\n\
Senior active maintainers include Larry Jones, Derek R. Price,\n\
and Mark D. Baushke. Please see the AUTHORS and README files from the CVS\n\
distribution kit for a complete list of contributors and copyrights.\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':
if (free_Tmpdir) free (Tmpdir);
Tmpdir = xstrdup (optarg);
free_Tmpdir = 1;
tmpdir_update_env = 1;
break;
case 'e':
if (free_Editor) free (Editor);
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')
{
if (free_Tmpdir) free (Tmpdir);
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 ();
}