Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
      3  *
      4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
      5  *                                  and others.
      6  *
      7  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
      8  * Portions Copyright (C) 1989-1992, Brian Berliner
      9  *
     10  * You may distribute under the terms of the GNU General Public License
     11  * as specified in the README file that comes with the CVS source distribution.
     12  *
     13  * This is the main C driver for the CVS system.
     14  *
     15  * Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing
     16  * the shell-script CVS system that this is based on.
     17  *
     18  */
     19 #include <sys/cdefs.h>
     20 __RCSID("$NetBSD: main.c,v 1.9 2024/02/04 20:47:25 christos Exp $");
     21 
     22 #include "cvs.h"
     23 
     24 #include "closeout.h"
     25 #include "setenv.h"
     26 #include "strftime.h"
     27 #include "xgethostname.h"
     28 
     29 const char *program_name;
     30 const char *program_path;
     31 const char *cvs_cmd_name;
     32 const char *processing = "init";
     33 
     34 const char *global_session_id; /* Random session ID */
     35 
     36 char *hostname;
     37 /* FIXME: Perhaps this should be renamed original_hostname or the like?  */
     38 char *server_hostname;
     39 
     40 int use_editor = 1;
     41 int use_cvsrc = 1;
     42 int cvswrite = !CVSREAD_DFLT;
     43 int really_quiet = 0;
     44 int quiet = 0;
     45 int trace = 0;
     46 int noexec = 0;
     47 int nolock = 0;
     48 int readonlyfs = 0;
     49 int logoff = 0;
     50 const char *cvsDir = "CVS";
     51 
     52 
     53 
     54 /***
     55  ***
     56  ***   CVSROOT/config options
     57  ***
     58  ***/
     59 struct config *config;
     60 
     61 
     62 
     63 mode_t cvsumask = UMASK_DFLT;
     64 
     65 char *CurDir;
     66 
     67 /*
     68  * Defaults, for the environment variables that are not set
     69  */
     70 char *Editor = EDITOR_DFLT;
     71 
     72 
     73 
     74 /* Temp dir stuff.  */
     75 
     76 /* Temp dir, if set by the user.  */
     77 static char *tmpdir_cmdline;
     78 
     79 
     80 
     81 /* Returns in order of precedence:
     82  *
     83  *	1.  Temp dir as set via the command line.
     84  *	2.  Temp dir as set in CVSROOT/config.
     85  *	3.  Temp dir as set in $TMPDIR env var.
     86  *	4.  Contents of TMPDIR_DFLT preprocessor macro.
     87  *
     88  * ERRORS
     89  *  It is a fatal error if this function would otherwise return NULL or an
     90  *  empty string.
     91  */
     92 const char *
     93 get_cvs_tmp_dir (void)
     94 {
     95     const char *retval;
     96     if (tmpdir_cmdline) retval = tmpdir_cmdline;
     97     else if (config && config->TmpDir) retval = config->TmpDir;
     98     else retval = get_system_temp_dir ();
     99     if (!retval) retval = TMPDIR_DFLT;
    100 
    101     if (!retval || !*retval) error (1, 0, "No temp dir specified.");
    102 
    103     return retval;
    104 }
    105 
    106 
    107 
    108 /* When our working directory contains subdirectories with different
    109    values in CVS/Root files, we maintain a list of them.  */
    110 List *root_directories = NULL;
    111 
    112 static const struct cmd
    113 {
    114     const char *fullname;	/* Full name of the function (e.g. "commit") */
    115 
    116     /* Synonyms for the command, nick1 and nick2.  We supply them
    117        mostly for two reasons: (1) CVS has always supported them, and
    118        we need to maintain compatibility, (2) if there is a need for a
    119        version which is shorter than the fullname, for ease in typing.
    120        Synonyms have the disadvantage that people will see "new" and
    121        then have to think about it, or look it up, to realize that is
    122        the operation they know as "add".  Also, this means that one
    123        cannot create a command "cvs new" with a different meaning.  So
    124        new synonyms are probably best used sparingly, and where used
    125        should be abbreviations of the fullname (preferably consisting
    126        of the first 2 or 3 or so letters).
    127 
    128        One thing that some systems do is to recognize any unique
    129        abbreviation, for example "annotat" "annota", etc., for
    130        "annotate".  The problem with this is that scripts and user
    131        habits will expect a certain abbreviation to be unique, and in
    132        a future release of CVS it may not be.  So it is better to
    133        accept only an explicit list of abbreviations and plan on
    134        supporting them in the future as well as now.  */
    135 
    136     const char *nick1;
    137     const char *nick2;
    138 
    139     int (*func) (int, char **);	/* Function takes (argc, argv) arguments. */
    140     unsigned long attr;		/* Attributes. */
    141 } cmds[] =
    142 
    143 {
    144     /* cvsacl patch */
    145     { "acl",      NULL,       NULL,		   cvsacl,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
    146     { "racl",     NULL,       NULL,		   cvsacl,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
    147     { "add",      "ad",       "new",       add,       CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
    148     { "admin",    "adm",      "rcs",       admin,     CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
    149     { "annotate", "ann",      NULL,        annotate,  CVS_CMD_USES_WORK_DIR },
    150     { "checkout", "co",       "get",       checkout,  0 },
    151     { "commit",   "ci",       "com",       commit,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
    152     { "diff",     "di",       "dif",       diff,      CVS_CMD_USES_WORK_DIR },
    153     { "edit",     NULL,       NULL,        edit,      CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
    154     { "editors",  NULL,       NULL,        editors,   CVS_CMD_USES_WORK_DIR },
    155     { "export",   "exp",      "ex",        checkout,  CVS_CMD_USES_WORK_DIR },
    156     { "history",  "hi",       "his",       history,   CVS_CMD_USES_WORK_DIR },
    157     { "import",   "im",       "imp",       import,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR | CVS_CMD_IGNORE_ADMROOT},
    158     { "init",     NULL,       NULL,        init,      CVS_CMD_MODIFIES_REPOSITORY },
    159 #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
    160     { "kserver",  NULL,       NULL,        server,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */
    161 #endif
    162     { "log",      "lo",       NULL,        cvslog,    CVS_CMD_USES_WORK_DIR },
    163 #ifdef AUTH_CLIENT_SUPPORT
    164     { "login",    "logon",    "lgn",       login,     0 },
    165     { "logout",   NULL,       NULL,        logout,    0 },
    166 #endif /* AUTH_CLIENT_SUPPORT */
    167     { "ls",       "dir",      "list",      ls,        0 },
    168 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
    169     { "pserver",  NULL,       NULL,        server,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */
    170 #endif
    171     { "rannotate","rann",     "ra",        annotate,  0 },
    172     { "rdiff",    "patch",    "pa",        patch,     0 },
    173     { "release",  "re",       "rel",       release,   CVS_CMD_MODIFIES_REPOSITORY },
    174     { "remove",   "rm",       "delete",    cvsremove, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
    175     { "rlog",     "rl",       NULL,        cvslog,    0 },
    176     { "rls",      "rdir",     "rlist",     ls,        0 },
    177     { "rtag",     "rt",       "rfreeze",   cvstag,    CVS_CMD_MODIFIES_REPOSITORY },
    178 #ifdef SERVER_SUPPORT
    179     { "server",   NULL,       NULL,        server,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
    180 #endif
    181     { "status",   "st",       "stat",      cvsstatus, CVS_CMD_USES_WORK_DIR },
    182     { "tag",      "ta",       "freeze",    cvstag,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
    183     { "unedit",   NULL,       NULL,        unedit,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
    184     { "update",   "up",       "upd",       update,    CVS_CMD_USES_WORK_DIR },
    185     { "version",  "ve",       "ver",       version,   0 },
    186     { "watch",    NULL,       NULL,        watch,     CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
    187     { "watchers", NULL,       NULL,        watchers,  CVS_CMD_USES_WORK_DIR },
    188     { NULL, NULL, NULL, NULL, 0 },
    189 };
    190 
    191 static const char *const usg[] =
    192 {
    193     /* CVS usage messages never have followed the GNU convention of
    194        putting metavariables in uppercase.  I don't know whether that
    195        is a good convention or not, but if it changes it would have to
    196        change in all the usage messages.  For now, they consistently
    197        use lowercase, as far as I know.  Punctuation is pretty funky,
    198        though.  Sometimes they use none, as here.  Sometimes they use
    199        single quotes (not the TeX-ish `' stuff), as in --help-options.
    200        Sometimes they use double quotes, as in cvs -H add.
    201 
    202        Most (not all) of the usage messages seem to have periods at
    203        the end of each line.  I haven't tried to duplicate this style
    204        in --help as it is a rather different format from the rest.  */
    205 
    206     "Usage: %s [cvs-options] command [command-options-and-arguments]\n",
    207     "  where cvs-options are -q, -n, etc.\n",
    208     "    (specify --help-options for a list of options)\n",
    209     "  where command is add, admin, etc.\n",
    210     "    (specify --help-commands for a list of commands\n",
    211     "     or --help-synonyms for a list of command synonyms)\n",
    212     "  where command-options-and-arguments depend on the specific command\n",
    213     "    (specify -H followed by a command name for command-specific help)\n",
    214     "  Specify --help to receive this message\n",
    215     "\n",
    216 
    217     /* Some people think that a bug-reporting address should go here.  IMHO,
    218        the web sites are better because anything else is very likely to go
    219        obsolete in the years between a release and when someone might be
    220        reading this help.  Besides, we could never adequately discuss
    221        bug reporting in a concise enough way to put in a help message.  */
    222 
    223     /* I was going to put this at the top, but usage() wants the %s to
    224        be in the first line.  */
    225     "The Concurrent Versions System (CVS) is a tool for version control.\n",
    226     /* I really don't think I want to try to define "version control"
    227        in one line.  I'm not sure one can get more concise than the
    228        paragraph in ../cvs.spec without assuming the reader knows what
    229        version control means.  */
    230 
    231     "For CVS updates and additional information, see\n",
    232     "    the CVS home page at http://www.nongnu.org/cvs/ or\n",
    233     "    the CVSNT home page at http://www.cvsnt.org/\n",
    234     NULL,
    235 };
    236 
    237 static const char *const cmd_usage[] =
    238 {
    239     "CVS commands are:\n",
    240     "        acl          Add/modify/delete ACLs in files and directories\n",
    241     "        add          Add a new file/directory to the repository\n",
    242     "        admin        Administration front end for rcs\n",
    243     "        annotate     Show last revision where each line was modified\n",
    244     "        checkout     Checkout sources for editing\n",
    245     "        commit       Check files into the repository\n",
    246     "        diff         Show differences between revisions\n",
    247     "        edit         Get ready to edit a watched file\n",
    248     "        editors      See who is editing a watched file\n",
    249     "        export       Export sources from CVS, similar to checkout\n",
    250     "        history      Show repository access history\n",
    251     "        import       Import sources into CVS, using vendor branches\n",
    252     "        init         Create a CVS repository if it doesn't exist\n",
    253 #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
    254     "        kserver      Kerberos server mode\n",
    255 #endif
    256     "        log          Print out history information for files\n",
    257 #ifdef AUTH_CLIENT_SUPPORT
    258     "        login        Prompt for password for authenticating server\n",
    259     "        logout       Removes entry in .cvspass for remote repository\n",
    260 #endif /* AUTH_CLIENT_SUPPORT */
    261     "        ls           List files available from CVS\n",
    262 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
    263     "        pserver      Password server mode\n",
    264 #endif
    265     "        racl         Add/modify/delete ACLs in files and directories\n",
    266     "        rannotate    Show last revision where each line of module was modified\n",
    267     "        rdiff        Create 'patch' format diffs between releases\n",
    268     "        release      Indicate that a Module is no longer in use\n",
    269     "        remove       Remove an entry from the repository\n",
    270     "        rlog         Print out history information for a module\n",
    271     "        rls          List files in a module\n",
    272     "        rtag         Add a symbolic tag to a module\n",
    273 #ifdef SERVER_SUPPORT
    274     "        server       Server mode\n",
    275 #endif
    276     "        status       Display status information on checked out files\n",
    277     "        tag          Add a symbolic tag to checked out version of files\n",
    278     "        unedit       Undo an edit command\n",
    279     "        update       Bring work tree in sync with repository\n",
    280     "        version      Show current CVS version(s)\n",
    281     "        watch        Set watches\n",
    282     "        watchers     See who is watching a file\n",
    283     "(Specify the --help option for a list of other help options)\n",
    284     NULL,
    285 };
    286 
    287 static const char *const opt_usage[] =
    288 {
    289     /* Omit -b because it is just for compatibility.  */
    290     "CVS global options (specified before the command name) are:\n",
    291     "    -H           Displays usage information for command.\n",
    292     "    -Q           Cause CVS to be really quiet.\n",
    293     "    -q           Cause CVS to be somewhat quiet.\n",
    294     "    -r           Make checked-out files read-only.\n",
    295     "    -w           Make checked-out files read-write (default).\n",
    296     "    -n           Do not execute anything that will change the disk.\n",
    297     "    -u           Don't create locks (implies -l).\n",
    298     "    -t           Show trace of program execution (repeat for more\n",
    299     "                 verbosity) -- try with -n.\n",
    300     "    -R           Assume repository is read-only, such as CDROM\n",
    301     "    -v           CVS version and copyright.\n",
    302     "    -T tmpdir    Use 'tmpdir' for temporary files.\n",
    303     "    -e editor    Use 'editor' for editing log information.\n",
    304     "    -d CVS_root  Overrides $CVSROOT as the root of the CVS tree.\n",
    305     "    -D dir       Use DIR as the bookkeeping directory instead of CVS.\n"
    306     "    -f           Do not use the ~/.cvsrc file.\n",
    307 #ifdef CLIENT_SUPPORT
    308     "    -z #         Request compression level '#' for net traffic.\n",
    309 #ifdef ENCRYPTION
    310     "    -x           Encrypt all net traffic.\n",
    311 #endif
    312     "    -a           Authenticate all net traffic.\n",
    313 #endif
    314     "    -s VAR=VAL   Set CVS user variable.\n",
    315     "(Specify the --help option for a list of other help options)\n",
    316     NULL
    317 };
    318 
    319 
    320 static int
    321 set_root_directory (Node *p, void *ignored)
    322 {
    323     if (current_parsed_root == NULL && p->data != NULL)
    324     {
    325 	current_parsed_root = p->data;
    326 	original_parsed_root = current_parsed_root;
    327 	return 1;
    328     }
    329     return 0;
    330 }
    331 
    332 
    333 static const char * const*
    334 cmd_synonyms (void)
    335 {
    336     char ** synonyms;
    337     char ** line;
    338     const struct cmd *c = &cmds[0];
    339     /* Three more for title, "specify --help" line, and NULL.  */
    340     int numcmds = 3;
    341 
    342     while (c->fullname != NULL)
    343     {
    344 	numcmds++;
    345 	c++;
    346     }
    347 
    348     synonyms = xnmalloc (numcmds, sizeof(char *));
    349     line = synonyms;
    350     *line++ = "CVS command synonyms are:\n";
    351     for (c = &cmds[0]; c->fullname != NULL; c++)
    352     {
    353 	if (c->nick1 || c->nick2)
    354 	{
    355 	    *line = Xasprintf ("        %-12s %s %s\n", c->fullname,
    356 			       c->nick1 ? c->nick1 : "",
    357 			       c->nick2 ? c->nick2 : "");
    358 	    line++;
    359 	}
    360     }
    361     *line++ = "(Specify the --help option for a list of other help options)\n";
    362     *line = NULL;
    363 
    364     return (const char * const*) synonyms; /* will never be freed */
    365 }
    366 
    367 
    368 
    369 unsigned long int
    370 lookup_command_attribute (const char *cmd_name)
    371 {
    372     const struct cmd *cm;
    373 
    374     for (cm = cmds; cm->fullname; cm++)
    375     {
    376 	if (strcmp (cmd_name, cm->fullname) == 0)
    377 	    break;
    378     }
    379     if (!cm->fullname)
    380 	error (1, 0, "unknown command: %s", cmd_name);
    381     return cm->attr;
    382 }
    383 
    384 
    385 
    386 /*
    387  * Exit with an error code and an informative message about the signal
    388  * received.  This function, by virtue of causing an actual call to exit(),
    389  * causes all the atexit() handlers to be called.
    390  *
    391  * INPUTS
    392  *   sig	The signal recieved.
    393  *
    394  * ERRORS
    395  *   The cleanup routines registered via atexit() and the error function
    396  *   itself can potentially change the exit status.  They shouldn't do this
    397  *   unless they encounter problems doing their own jobs.
    398  *
    399  * RETURNS
    400  *   Nothing.  This function will always exit.  It should exit with an exit
    401  *   status of 1, but might not, as noted in the ERRORS section above.
    402  */
    403 #ifndef DONT_USE_SIGNALS
    404 static RETSIGTYPE main_cleanup (int) __attribute__ ((__noreturn__));
    405 #endif /* DONT_USE_SIGNALS */
    406 static RETSIGTYPE
    407 main_cleanup (int sig)
    408 {
    409 #ifndef DONT_USE_SIGNALS
    410     const char *name;
    411     char temp[10];
    412     static int reenter = 0;
    413 
    414     if (reenter++)
    415 	_exit(1);
    416 
    417     switch (sig)
    418     {
    419 #ifdef SIGABRT
    420     case SIGABRT:
    421 	name = "abort";
    422 	break;
    423 #endif
    424 #ifdef SIGHUP
    425     case SIGHUP:
    426 	name = "hangup";
    427 	break;
    428 #endif
    429 #ifdef SIGINT
    430     case SIGINT:
    431 	name = "interrupt";
    432 	break;
    433 #endif
    434 #ifdef SIGQUIT
    435     case SIGQUIT:
    436 	name = "quit";
    437 	break;
    438 #endif
    439 #ifdef SIGPIPE
    440     case SIGPIPE:
    441 	name = "broken pipe";
    442 	break;
    443 #endif
    444 #ifdef SIGTERM
    445     case SIGTERM:
    446 	name = "termination";
    447 	break;
    448 #endif
    449     default:
    450 	/* This case should never be reached, because we list above all
    451 	   the signals for which we actually establish a signal handler.  */
    452 	sprintf (temp, "%d", sig);
    453 	name = temp;
    454 	break;
    455     }
    456 
    457     /* This always exits, which will cause our exit handlers to be called.  */
    458     error (1, 0, "received %s signal", name);
    459     /* but make the exit explicit to silence warnings when gcc processes the
    460      * noreturn attribute.
    461      */
    462     exit (EXIT_FAILURE);
    463 #endif /* !DONT_USE_SIGNALS */
    464 }
    465 
    466 
    467 
    468 /* From server.c.
    469  *
    470  * When !defined ALLOW_CONFIG_OVERRIDE, this will never have any value but
    471  * NULL.
    472  */
    473 extern char *gConfigPath;
    474 
    475 
    476 
    477 
    478 enum {RANDOM_BYTES = 8};
    479 enum {COMMITID_RAW_SIZE = (sizeof(time_t) + RANDOM_BYTES)};
    480 
    481 static char const alphabet[62] =
    482   "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    483 
    484 /* Divide BUF by D, returning the remainder.  Replace BUF by the
    485    quotient.  BUF[0] is the most significant part of BUF.
    486    D must not exceed UINT_MAX >> CHAR_BIT.  */
    487 static unsigned int
    488 divide_by (unsigned char buf[COMMITID_RAW_SIZE], unsigned int d)
    489 {
    490     unsigned int carry = 0;
    491     int i;
    492     for (i = 0; i < COMMITID_RAW_SIZE; i++)
    493     {
    494 	unsigned int byte = buf[i];
    495 	unsigned int dividend = (carry << CHAR_BIT) + byte;
    496 	buf[i] = dividend / d;
    497 	carry = dividend % d;
    498     }
    499     return carry;
    500 }
    501 
    502 #ifdef SIGINFO
    503 #include <paths.h>
    504 
    505 static void
    506 show_status (int n)
    507 {
    508 	char wd[PATH_MAX];
    509 	char buf[2048];
    510 	static int ttyfd = -2;
    511 
    512 	if (ttyfd == -2)
    513 		ttyfd = open(_PATH_TTY, O_RDWR | O_CLOEXEC);
    514 
    515 	if (ttyfd == -1)
    516 		return;
    517 
    518 	if (getcwd(wd, sizeof(wd)) == NULL)
    519 		return;
    520 	n = snprintf(buf, sizeof(buf), "%s[%d]: %s in %s\n", getprogname(),
    521 	    (int)getpid(), processing, wd);
    522 	if (n <= 0)
    523 		return;
    524 	write(ttyfd, buf, (size_t)n);
    525 }
    526 #endif
    527 
    528 static void
    529 convert (char const input[COMMITID_RAW_SIZE], char *output)
    530 {
    531     static char const zero[COMMITID_RAW_SIZE] = { 0, };
    532     unsigned char buf[COMMITID_RAW_SIZE];
    533     size_t o = 0;
    534     memcpy (buf, input, COMMITID_RAW_SIZE);
    535     while (memcmp (buf, zero, COMMITID_RAW_SIZE) != 0)
    536 	output[o++] = alphabet[divide_by (buf, sizeof alphabet)];
    537     if (! o)
    538 	output[o++] = '0';
    539     output[o] = '\0';
    540 }
    541 
    542 
    543 int
    544 main (int argc, char **argv)
    545 {
    546     cvsroot_t *CVSroot_parsed = NULL;
    547     bool cvsroot_update_env = true;
    548     char *cp, *end;
    549     const struct cmd *cm;
    550     int c, err = 0;
    551     int free_Editor = 0;
    552 
    553     int help = 0;		/* Has the user asked for help?  This
    554 				   lets us support the `cvs -H cmd'
    555 				   convention to give help for cmd. */
    556     static const char short_options[] = "+QqrwtlnRuvb:T:e:d:D:Hfz:s:xa";
    557     static struct option long_options[] =
    558     {
    559         {"help", 0, NULL, 'H'},
    560         {"version", 0, NULL, 'v'},
    561 	{"help-commands", 0, NULL, 1},
    562 	{"help-synonyms", 0, NULL, 2},
    563 	{"help-options", 0, NULL, 4},
    564 #ifdef SERVER_SUPPORT
    565 	{"allow-root", required_argument, NULL, 3},
    566 #endif /* SERVER_SUPPORT */
    567         {0, 0, 0, 0}
    568     };
    569     /* `getopt_long' stores the option index here, but right now we
    570         don't use it. */
    571     int option_index = 0;
    572 
    573 #ifdef SYSTEM_INITIALIZE
    574     /* Hook for OS-specific behavior, for example socket subsystems on
    575        NT and OS2 or dealing with windows and arguments on Mac.  */
    576     SYSTEM_INITIALIZE (&argc, &argv);
    577 #endif
    578 
    579 #ifdef SYSTEM_CLEANUP
    580 	/* Hook for OS-specific behavior, for example socket subsystems on
    581 	   NT and OS2 or dealing with windows and arguments on Mac.  */
    582 	cleanup_register (SYSTEM_CLEANUP);
    583 #endif
    584 
    585 #ifdef HAVE_TZSET
    586     /* On systems that have tzset (which is almost all the ones I know
    587        of), it's a good idea to call it.  */
    588     tzset ();
    589 #endif
    590 
    591     /*
    592      * Just save the last component of the path for error messages
    593      */
    594     program_path = xstrdup (argv[0]);
    595 #ifdef ARGV0_NOT_PROGRAM_NAME
    596     /* On some systems, e.g. VMS, argv[0] is not the name of the command
    597        which the user types to invoke the program.  */
    598     program_name = "cvs";
    599 #else
    600     program_name = last_component (argv[0]);
    601 #endif
    602 
    603     /*
    604      * Query the environment variables up-front, so that
    605      * they can be overridden by command line arguments
    606      */
    607     if ((cp = getenv (EDITOR1_ENV)) != NULL)
    608  	Editor = cp;
    609     else if ((cp = getenv (EDITOR2_ENV)) != NULL)
    610 	Editor = cp;
    611     else if ((cp = getenv (EDITOR3_ENV)) != NULL)
    612 	Editor = cp;
    613     if (getenv (CVSREAD_ENV) != NULL)
    614 	cvswrite = 0;
    615     if (getenv (CVSREADONLYFS_ENV) != NULL) {
    616 	readonlyfs = 1;
    617 	logoff = 1;
    618     }
    619 
    620     /* Set this to 0 to force getopt initialization.  getopt() sets
    621        this to 1 internally.  */
    622     getoptreset ();
    623 
    624     /* We have to parse the options twice because else there is no
    625        chance to avoid reading the global options from ".cvsrc".  Set
    626        opterr to 0 for avoiding error messages about invalid options.
    627        */
    628     opterr = 0;
    629 
    630     while ((c = getopt_long
    631             (argc, argv, short_options, long_options, &option_index))
    632            != EOF)
    633     {
    634 	if (c == 'f')
    635 	    use_cvsrc = 0;
    636     }
    637 
    638 #ifdef SERVER_SUPPORT
    639     /* Don't try and read a .cvsrc file if we are a server.  */
    640     if (optind < argc
    641 	&& (false
    642 # if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)
    643 	    || !strcmp (argv[optind], "pserver")
    644 # endif
    645 # ifdef HAVE_KERBEROS
    646 	    || !strcmp (argv[optind], "kserver")
    647 # endif /* HAVE_KERBEROS */
    648 	    || !strcmp (argv[optind], "server")))
    649 	{
    650 	    /* Avoid any .cvsrc file.  */
    651 	    use_cvsrc = 0;
    652 	    /* Pre-parse the server options to get the config path.  */
    653 	    cvs_cmd_name = argv[optind];
    654 	    parseServerOptions (argc - optind, argv + optind);
    655 	}
    656 #endif /* SERVER_SUPPORT */
    657 
    658     /*
    659      * Scan cvsrc file for global options.
    660      */
    661     if (use_cvsrc)
    662 	read_cvsrc (&argc, &argv, "cvs");
    663 
    664     getoptreset();
    665     while ((c = getopt_long
    666             (argc, argv, short_options, long_options, &option_index))
    667            != EOF)
    668     {
    669 	switch (c)
    670 	{
    671             case 1:
    672 	        /* --help-commands */
    673                 usage (cmd_usage);
    674                 break;
    675             case 2:
    676 	        /* --help-synonyms */
    677                 usage (cmd_synonyms());
    678                 break;
    679 	    case 4:
    680 		/* --help-options */
    681 		usage (opt_usage);
    682 		break;
    683 #ifdef SERVER_SUPPORT
    684 	    case 3:
    685 		/* --allow-root */
    686 		root_allow_add (optarg, gConfigPath);
    687 		break;
    688 #endif /* SERVER_SUPPORT */
    689 	    case 'Q':
    690 		really_quiet = 1;
    691 		/* FALL THROUGH */
    692 	    case 'q':
    693 		quiet = 1;
    694 		break;
    695 	    case 'r':
    696 		cvswrite = 0;
    697 		break;
    698 	    case 'w':
    699 		cvswrite = 1;
    700 		break;
    701 	    case 't':
    702 		trace++;
    703 		break;
    704 	    case 'R':
    705 		readonlyfs = -1;
    706 		logoff = 1;
    707 		break;
    708 	    case 'n':
    709 		noexec = 1;
    710 	    case 'u':			/* Fall through */
    711 		nolock = 1;
    712 	    case 'l':			/* Fall through */
    713 		logoff = 1;
    714 		break;
    715 	    case 'v':
    716 		(void) fputs ("\n", stdout);
    717 		version (0, NULL);
    718 		(void) fputs ("\n", stdout);
    719 		(void) fputs ("\
    720 Copyright (C) 2005 Free Software Foundation, Inc.\n\
    721 \n\
    722 Senior active maintainers include Larry Jones, Derek R. Price,\n\
    723 and Mark D. Baushke.  Please see the AUTHORS and README files from the CVS\n\
    724 distribution kit for a complete list of contributors and copyrights.\n",
    725 		              stdout);
    726 		(void) fputs ("\n", stdout);
    727 		(void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
    728 		(void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
    729 		(void) fputs ("\n", stdout);
    730 
    731 		(void) fputs ("Specify the --help option for further information about CVS\n", stdout);
    732 
    733 		exit (0);
    734 		break;
    735 	    case 'b':
    736 		/* This option used to specify the directory for RCS
    737 		   executables.  But since we don't run them any more,
    738 		   this is a noop.  Silently ignore it so that .cvsrc
    739 		   and scripts and inetd.conf and such can work with
    740 		   either new or old CVS.  */
    741 		break;
    742 	    case 'T':
    743 		if (tmpdir_cmdline) free (tmpdir_cmdline);
    744 		tmpdir_cmdline = xstrdup (optarg);
    745 		break;
    746 	    case 'e':
    747 		if (free_Editor) free (Editor);
    748 		Editor = xstrdup (optarg);
    749 		free_Editor = 1;
    750 		break;
    751 	    case 'd':
    752 		if (CVSroot_cmdline != NULL)
    753 		    free (CVSroot_cmdline);
    754 		CVSroot_cmdline = xstrdup (optarg);
    755 		break;
    756 	    case 'H':
    757 	        help = 1;
    758 		break;
    759             case 'f':
    760 		use_cvsrc = 0; /* unnecessary, since we've done it above */
    761 		break;
    762 	    case 'z':
    763 #ifdef CLIENT_SUPPORT
    764 		gzip_level = strtol (optarg, &end, 10);
    765 		if (*end != '\0' || gzip_level < 0 || gzip_level > 9)
    766 		  error (1, 0,
    767 			 "gzip compression level must be between 0 and 9");
    768 #endif /* CLIENT_SUPPORT */
    769 		/* If no CLIENT_SUPPORT, we just silently ignore the gzip
    770 		 * level, so that users can have it in their .cvsrc and not
    771 		 * cause any trouble.
    772 		 *
    773 		 * We still parse the argument to -z for correctness since
    774 		 * one user complained of being bitten by a run of
    775 		 * `cvs -z -n up' which read -n as the argument to -z without
    776 		 * complaining.  */
    777 		break;
    778 	    case 's':
    779 		variable_set (optarg);
    780 		break;
    781 	    case 'x':
    782 #ifdef CLIENT_SUPPORT
    783 	        cvsencrypt = 1;
    784 #endif /* CLIENT_SUPPORT */
    785 		/* If no CLIENT_SUPPORT, ignore -x, so that users can
    786                    have it in their .cvsrc and not cause any trouble.
    787                    If no ENCRYPTION, we still accept -x, but issue an
    788                    error if we are being run as a client.  */
    789 		break;
    790 	    case 'a':
    791 #ifdef CLIENT_SUPPORT
    792 		cvsauthenticate = 1;
    793 #endif
    794 		/* If no CLIENT_SUPPORT, ignore -a, so that users can
    795                    have it in their .cvsrc and not cause any trouble.
    796                    We will issue an error later if stream
    797                    authentication is not supported.  */
    798 		break;
    799 	    case 'D':
    800 		cvsDir = xstrdup (optarg);
    801 		if (strchr (cvsDir, '/') != NULL)
    802 		    error (1, 0, "cvsDir is not allowed to have slashes");
    803 		break;
    804 	    case '?':
    805 	    default:
    806                 usage (usg);
    807 	}
    808     }
    809 
    810     argc -= optind;
    811     argv += optind;
    812     if (argc < 1)
    813 	usage (usg);
    814 
    815     if (readonlyfs && !really_quiet) {
    816 	error (0, 0,
    817 	       "WARNING: Read-only repository access mode selected via `cvs -R'.\n\
    818 Using this option to access a repository which some users write to may\n\
    819 cause intermittent sandbox corruption.");
    820     }
    821 
    822     /* Calculate the cvs global session ID */
    823 
    824     {
    825 	char buf[COMMITID_RAW_SIZE] = { 0, };
    826 	char out[COMMITID_RAW_SIZE * 2];
    827 	ssize_t len = 0;
    828 	time_t rightnow = time (NULL);
    829 	char *startrand = buf + sizeof (time_t);
    830 	unsigned char *p = (unsigned char *) startrand;
    831 	size_t randbytes = RANDOM_BYTES;
    832 	int flags = O_RDONLY;
    833 	int fd;
    834 #ifdef O_NOCTTY
    835 	flags |= O_NOCTTY;
    836 #endif
    837 	if (rightnow != (time_t)-1)
    838 		while (rightnow > 0) {
    839 		    *--p = rightnow % (UCHAR_MAX + 1);
    840 		    rightnow /= UCHAR_MAX + 1;
    841 		}
    842 	else {
    843 	    /* try to use more random data */
    844 	    randbytes = COMMITID_RAW_SIZE;
    845 	    startrand = buf;
    846 	}
    847 	fd = open ("/dev/urandom", flags);
    848 	if (fd >= 0) {
    849 	    len = read (fd, startrand, randbytes);
    850 	    close (fd);
    851 	}
    852 	if (len <= 0) {
    853 	    /* no random data was available so use pid */
    854 	    long int pid = (long int)getpid ();
    855 	    p = (unsigned char *) (startrand + sizeof (pid));
    856 	    while (pid > 0) {
    857 		*--p = pid % (UCHAR_MAX + 1);
    858 		pid /= UCHAR_MAX + 1;
    859 	    }
    860 	}
    861 	convert(buf, out);
    862 	global_session_id = strdup (out);
    863     }
    864 
    865 
    866     TRACE (TRACE_FUNCTION, "main: Session ID is %s", global_session_id);
    867 
    868     /* Look up the command name. */
    869 
    870     cvs_cmd_name = argv[0];
    871     for (cm = cmds; cm->fullname; cm++)
    872     {
    873 	if (cm->nick1 && !strcmp (cvs_cmd_name, cm->nick1))
    874 	    break;
    875 	if (cm->nick2 && !strcmp (cvs_cmd_name, cm->nick2))
    876 	    break;
    877 	if (!strcmp (cvs_cmd_name, cm->fullname))
    878 	    break;
    879     }
    880 
    881     if (!cm->fullname)
    882     {
    883 	fprintf (stderr, "Unknown command: `%s'\n\n", cvs_cmd_name);
    884 	usage (cmd_usage);
    885     }
    886     else
    887 	cvs_cmd_name = cm->fullname;	/* Global pointer for later use */
    888 
    889     if (help)
    890     {
    891 	argc = -1;		/* some functions only check for this */
    892 	err = (*(cm->func)) (argc, argv);
    893     }
    894     else
    895     {
    896 	/* The user didn't ask for help, so go ahead and authenticate,
    897            set up CVSROOT, and the rest of it. */
    898 
    899 	short int lock_cleanup_setup = 0;
    900 
    901 	/* The UMASK environment variable isn't handled with the
    902 	   others above, since we don't want to signal errors if the
    903 	   user has asked for help.  This won't work if somebody adds
    904 	   a command-line flag to set the umask, since we'll have to
    905 	   parse it before we get here. */
    906 
    907 	if ((cp = getenv (CVSUMASK_ENV)) != NULL)
    908 	{
    909 	    /* FIXME: Should be accepting symbolic as well as numeric mask.  */
    910 	    cvsumask = strtol (cp, &end, 8) & 0777;
    911 	    if (*end != '\0')
    912 		error (1, errno, "invalid umask value in %s (%s)",
    913 		       CVSUMASK_ENV, cp);
    914 	}
    915 
    916 	/* HOSTNAME & SERVER_HOSTNAME need to be set before they are
    917 	 * potentially used in gserver_authenticate_connection() (called from
    918 	 * pserver_authenticate_connection, below).
    919 	 */
    920 	hostname = xgethostname ();
    921 	if (!hostname)
    922 	{
    923             error (0, errno,
    924                    "xgethostname () returned NULL, using \"localhost\"");
    925             hostname = xstrdup ("localhost");
    926 	}
    927 
    928 	/* Keep track of this separately since the client can change
    929 	 * HOSTNAME on the server.
    930 	 */
    931 	server_hostname = xstrdup (hostname);
    932 
    933 #ifdef SERVER_SUPPORT
    934 
    935 # ifdef HAVE_KERBEROS
    936 	/* If we are invoked with a single argument "kserver", then we are
    937 	   running as Kerberos server as root.  Do the authentication as
    938 	   the very first thing, to minimize the amount of time we are
    939 	   running as root.  */
    940 	if (strcmp (cvs_cmd_name, "kserver") == 0)
    941 	{
    942 	    kserver_authenticate_connection ();
    943 
    944 	    /* Pretend we were invoked as a plain server.  */
    945 	    cvs_cmd_name = "server";
    946 	}
    947 # endif /* HAVE_KERBEROS */
    948 
    949 # if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)
    950 	if (strcmp (cvs_cmd_name, "pserver") == 0)
    951 	{
    952 	    /* The reason that --allow-root is not a command option
    953 	       is mainly that it seems easier to make it a global option.  */
    954 
    955 	    /* Gets username and password from client, authenticates, then
    956 	       switches to run as that user and sends an ACK back to the
    957 	       client. */
    958 	    pserver_authenticate_connection ();
    959 
    960 	    /* Pretend we were invoked as a plain server.  */
    961 	    cvs_cmd_name = "server";
    962 	}
    963 # endif /* AUTH_SERVER_SUPPORT || HAVE_GSSAPI */
    964 #endif /* SERVER_SUPPORT */
    965 
    966 	server_active = strcmp (cvs_cmd_name, "server") == 0;
    967 
    968 #ifdef SERVER_SUPPORT
    969 	if (server_active)
    970 	{
    971 	    /* This is only used for writing into the history file.  For
    972 	       remote connections, it might be nice to have hostname
    973 	       and/or remote path, on the other hand I'm not sure whether
    974 	       it is worth the trouble.  */
    975 	    CurDir = xstrdup ("<remote>");
    976 	    cleanup_register (server_cleanup);
    977 	}
    978 	else
    979 #endif
    980 	{
    981 	    cleanup_register (close_stdout);
    982 	    CurDir = xgetcwd ();
    983             if (CurDir == NULL)
    984 		error (1, errno, "cannot get working directory");
    985 	}
    986 
    987 	{
    988 	    char *val;
    989 	    /* XXX pid < 10^32 */
    990 	    val = Xasprintf ("%ld", (long) getpid ());
    991 	    setenv (CVS_PID_ENV, val, 1);
    992 	    free (val);
    993 	}
    994 
    995 	/* make sure we clean up on error */
    996 	signals_register (main_cleanup);
    997 #ifdef SIGINFO
    998 	signal (SIGINFO, show_status);
    999 #endif
   1000 
   1001 #ifdef KLUDGE_FOR_WNT_TESTSUITE
   1002 	/* Probably the need for this will go away at some point once
   1003 	   we call fflush enough places (e.g. fflush (stdout) in
   1004 	   cvs_outerr).  */
   1005 	(void) setvbuf (stdout, NULL, _IONBF, 0);
   1006 	(void) setvbuf (stderr, NULL, _IONBF, 0);
   1007 #endif /* KLUDGE_FOR_WNT_TESTSUITE */
   1008 
   1009 	if (use_cvsrc)
   1010 	    read_cvsrc (&argc, &argv, cvs_cmd_name);
   1011 
   1012 	/* Fiddling with CVSROOT doesn't make sense if we're running
   1013 	 * in server mode, since the client will send the repository
   1014 	 * directory after the connection is made.
   1015 	 */
   1016 	if (!server_active)
   1017 	{
   1018 	    /* First check if a root was set via the command line.  */
   1019 	    if (CVSroot_cmdline)
   1020 	    {
   1021 		 if (!(CVSroot_parsed = parse_cvsroot (CVSroot_cmdline)))
   1022 		     error (1, 0, "Bad CVSROOT: `%s'.", CVSroot_cmdline);
   1023 	    }
   1024 
   1025 	    /* See if we are able to find a 'better' value for CVSroot
   1026 	     * in the CVSADM_ROOT directory.
   1027 	     *
   1028 	     * "cvs import" shouldn't check CVS/Root; in general it
   1029 	     * ignores CVS directories and CVS/Root is likely to
   1030 	     * specify a different repository than the one we are
   1031 	     * importing to, but if this is not import and no root was
   1032 	     * specified on the command line, set the root from the
   1033 	     * CVS/Root file.
   1034 	     */
   1035 	    if (!CVSroot_parsed
   1036 		&& !(cm->attr & CVS_CMD_IGNORE_ADMROOT)
   1037 	       )
   1038 		CVSroot_parsed = Name_Root (NULL, NULL);
   1039 
   1040 	    /* Now, if there is no root on the command line and we didn't find
   1041 	     * one in a file, set it via the $CVSROOT env var.
   1042 	     */
   1043 	    if (!CVSroot_parsed)
   1044 	    {
   1045 		char *tmp = getenv (CVSROOT_ENV);
   1046 		if (tmp)
   1047 		{
   1048 		    if (!(CVSroot_parsed = parse_cvsroot (tmp)))
   1049 			error (1, 0, "Bad CVSROOT: `%s'.", tmp);
   1050 		    cvsroot_update_env = false;
   1051 		}
   1052 	    }
   1053 
   1054 #ifdef CVSROOT_DFLT
   1055 	    if (!CVSroot_parsed)
   1056 	    {
   1057 		if (!(CVSroot_parsed = parse_cvsroot (CVSROOT_DFLT)))
   1058 		    error (1, 0, "Bad CVSROOT: `%s'.", CVSROOT_DFLT);
   1059 	    }
   1060 #endif /* CVSROOT_DFLT */
   1061 
   1062 	    /* Now we've reconciled CVSROOT from the command line, the
   1063 	       CVS/Root file, and the environment variable.  Do the
   1064 	       last sanity checks on the variable. */
   1065 	    if (!CVSroot_parsed)
   1066 	    {
   1067 		error (0, 0,
   1068 		       "No CVSROOT specified!  Please use the `-d' option");
   1069 		error (1, 0,
   1070 		       "or set the %s environment variable.", CVSROOT_ENV);
   1071 	    }
   1072 	}
   1073 
   1074 	/* Here begins the big loop over unique cvsroot values.  We
   1075            need to call do_recursion once for each unique value found
   1076            in CVS/Root.  Prime the list with the current value. */
   1077 
   1078 	/* Create the list. */
   1079 	assert (root_directories == NULL);
   1080 	root_directories = getlist ();
   1081 
   1082 	/* Prime it. */
   1083 	if (CVSroot_parsed)
   1084 	{
   1085 	    Node *n;
   1086 	    n = getnode ();
   1087 	    n->type = NT_UNKNOWN;
   1088 	    n->key = xstrdup (CVSroot_parsed->original);
   1089 	    n->data = CVSroot_parsed;
   1090 
   1091 	    if (addnode (root_directories, n))
   1092 		error (1, 0, "cannot add initial CVSROOT %s", n->key);
   1093 	}
   1094 
   1095 	assert (current_parsed_root == NULL);
   1096 
   1097 	/* If we're running the server, we want to execute this main
   1098 	   loop once and only once (we won't be serving multiple roots
   1099 	   from this connection, so there's no need to do it more than
   1100 	   once).  To get out of the loop, we perform a "break" at the
   1101 	   end of things.  */
   1102 
   1103 	while (server_active ||
   1104 	       walklist (root_directories, set_root_directory, NULL))
   1105 	{
   1106 	    /* Fiddling with CVSROOT doesn't make sense if we're running
   1107 	       in server mode, since the client will send the repository
   1108 	       directory after the connection is made. */
   1109 
   1110 	    if (!server_active)
   1111 	    {
   1112 		/* Now we're 100% sure that we have a valid CVSROOT
   1113 		   variable.  Parse it to see if we're supposed to do
   1114 		   remote accesses or use a special access method. */
   1115 
   1116 		TRACE (TRACE_FUNCTION,
   1117 		       "main loop with CVSROOT=%s",
   1118 		       current_parsed_root ? current_parsed_root->directory
   1119 					   : "(null)");
   1120 
   1121 		/*
   1122 		 * Check to see if the repository exists.
   1123 		 */
   1124 		if (!current_parsed_root->isremote && !nolock)
   1125 		{
   1126 		    char *path;
   1127 		    int save_errno;
   1128 
   1129 		    path = Xasprintf ("%s/%s", current_parsed_root->directory,
   1130 				      CVSROOTADM);
   1131 		    if (!isaccessible (path, R_OK | X_OK))
   1132 		    {
   1133 			save_errno = errno;
   1134 			/* If this is "cvs init", the root need not exist yet.
   1135 			 */
   1136 			if (strcmp (cvs_cmd_name, "init"))
   1137 			    error (1, save_errno, "%s", path);
   1138 		    }
   1139 		    free (path);
   1140 		}
   1141 
   1142 		/* Update the CVSROOT environment variable.  */
   1143 		if (cvsroot_update_env)
   1144 		    setenv (CVSROOT_ENV, current_parsed_root->original, 1);
   1145 	    }
   1146 
   1147 	    /* Parse the CVSROOT/config file, but only for local.  For the
   1148 	       server, we parse it after we know $CVSROOT.  For the
   1149 	       client, it doesn't get parsed at all, obviously.  The
   1150 	       presence of the parse_config call here is not meant to
   1151 	       predetermine whether CVSROOT/config overrides things from
   1152 	       read_cvsrc and other such places or vice versa.  That sort
   1153 	       of thing probably needs more thought.  */
   1154 	    if (!server_active && !current_parsed_root->isremote)
   1155 	    {
   1156 		/* If there was an error parsing the config file, parse_config
   1157 		   already printed an error.  We keep going.  Why?  Because
   1158 		   if we didn't, then there would be no way to check in a new
   1159 		   CVSROOT/config file to fix the broken one!  */
   1160 		if (config) free_config (config);
   1161 		config = parse_config (current_parsed_root->directory, NULL);
   1162 
   1163 		/* Can set TMPDIR in the environment if necessary now, since
   1164 		 * if it was set in config, we now know it.
   1165 		 */
   1166 		push_env_temp_dir ();
   1167 
   1168 		/* cvsacl patch */
   1169 		parse_aclconfig (current_parsed_root->directory);
   1170 	    }
   1171 
   1172 #ifdef CLIENT_SUPPORT
   1173 	    /* Need to check for current_parsed_root != NULL here since
   1174 	     * we could still be in server mode before the server function
   1175 	     * gets called below and sets the root
   1176 	     */
   1177 	    if (current_parsed_root != NULL && current_parsed_root->isremote)
   1178 	    {
   1179 		/* Create a new list for directory names that we've
   1180 		   sent to the server. */
   1181 		if (dirs_sent_to_server != NULL)
   1182 		    dellist (&dirs_sent_to_server);
   1183 		dirs_sent_to_server = getlist ();
   1184 	    }
   1185 #endif
   1186 
   1187 	    if (
   1188 #ifdef SERVER_SUPPORT
   1189 		/* Don't worry about lock_cleanup_setup when the server is
   1190 		 * active since we can only go through this loop once in that
   1191 		 * case anyhow.
   1192 		 */
   1193 		server_active ||
   1194 #endif
   1195 	        (
   1196 #ifdef CLIENT_SUPPORT
   1197 		 !current_parsed_root->isremote &&
   1198 #endif
   1199 		 !lock_cleanup_setup))
   1200 	    {
   1201 		/* Set up to clean up any locks we might create on exit.  */
   1202 		cleanup_register (Lock_Cleanup);
   1203 		lock_cleanup_setup = 1;
   1204 	    }
   1205 
   1206 	    /* Call our worker function.  */
   1207 	    err = (*(cm->func)) (argc, argv);
   1208 
   1209 	    /* Mark this root directory as done.  When the server is
   1210                active, our list will be empty -- don't try and
   1211                remove it from the list. */
   1212 
   1213 	    if (!server_active)
   1214 	    {
   1215 		Node *n = findnode (root_directories,
   1216 				    original_parsed_root->original);
   1217 		assert (n != NULL);
   1218 		assert (n->data != NULL);
   1219 		n->data = NULL;
   1220 		current_parsed_root = NULL;
   1221 	    }
   1222 
   1223 	    if (server_active)
   1224 		break;
   1225 	} /* end of loop for cvsroot values */
   1226 
   1227 	dellist (&root_directories);
   1228     } /* end of stuff that gets done if the user DOESN'T ask for help */
   1229 
   1230     root_allow_free ();
   1231 
   1232     /* This is exit rather than return because apparently that keeps
   1233        some tools which check for memory leaks happier.  */
   1234     exit (err ? EXIT_FAILURE : 0);
   1235 	/* Keep picky/stupid compilers (e.g. Visual C++ 5.0) happy.  */
   1236 	return 0;
   1237 }
   1238 
   1239 
   1240 
   1241 char *
   1242 Make_Date (const char *rawdate)
   1243 {
   1244     struct timespec t;
   1245 
   1246     if (!get_date (&t, rawdate, NULL))
   1247 	error (1, 0, "Can't parse date/time: `%s'", rawdate);
   1248 
   1249     /* Truncate nanoseconds.  */
   1250     return date_from_time_t (t.tv_sec);
   1251 }
   1252 
   1253 
   1254 
   1255 /* Parse a string of the form TAG[:DATE], where TAG could be the empty string.
   1256  *
   1257  * INPUTS
   1258  *   input	The string to be parsed.
   1259  *
   1260  * OUTPUTS
   1261  *   tag	The tag found, if any.  If TAG is the empty string, then leave
   1262  *		this value unchanged.
   1263  *   date	The date found, if any.  If DATE is the empty string or is
   1264  *		missing, leave this value unchanged.
   1265  *
   1266  * NOTES
   1267  *   If either TAG or DATE is replaced for output, the previous value is freed.
   1268  *
   1269  * ERRORS
   1270  *   If either TAG or DATE cannot be parsed, then this function will exit with
   1271  *   a fatal error message.
   1272  *
   1273  * RETURNS
   1274  *   Nothing.
   1275  */
   1276 void
   1277 parse_tagdate (char **tag, char **date, const char *input)
   1278 {
   1279     char *p;
   1280 
   1281     TRACE (TRACE_FUNCTION, "parse_tagdate (%s, %s, %s)",
   1282 	   *tag ? *tag : "(null)", *date ? *date : "(null)",
   1283 	   input);
   1284 
   1285     if ((p = strchr (input, ':')))
   1286     {
   1287 	/* Parse the tag.  */
   1288 	if (p - input)
   1289 	{
   1290 	    /* The tag has > 0 length.  */
   1291 	    if (*tag) free (*tag);
   1292 	    *tag = xmalloc (p - input + 1);
   1293 	    strncpy (*tag, input, p - input);
   1294 	    (*tag)[p - input] = '\0';
   1295 	}
   1296 
   1297 	/* Parse the date.  */
   1298 	if (*++p)
   1299 	{
   1300 	    if (*date) free (*date);
   1301 	    *date = Make_Date (p);
   1302 	}
   1303     }
   1304     else if (strlen (input))
   1305     {
   1306 	/* The tag has > 0 length.  */
   1307 	if (*tag) free (*tag);
   1308 	*tag = xstrdup (input);
   1309     }
   1310 
   1311     TRACE (TRACE_DATA, "parse_tagdate: got tag = `%s', date = `%s'",
   1312 	   *tag ? *tag : "(null)", *date ? *date : "(null)");
   1313 }
   1314 
   1315 
   1316 
   1317 /* Convert a time_t to an RCS format date.  This is mainly for the
   1318    use of "cvs history", because the CVSROOT/history file contains
   1319    time_t format dates; most parts of CVS will want to avoid using
   1320    time_t's directly, and instead use RCS_datecmp, Make_Date, &c.
   1321    Assuming that the time_t is in GMT (as it generally should be),
   1322    then the result will be in GMT too.
   1323 
   1324    Returns a newly malloc'd string.  */
   1325 
   1326 char *
   1327 date_from_time_t (time_t unixtime)
   1328 {
   1329     struct tm *ftm;
   1330     char date[MAXDATELEN];
   1331     char *ret;
   1332 
   1333     ftm = gmtime (&unixtime);
   1334     if (ftm == NULL)
   1335 	/* This is a system, like VMS, where the system clock is in local
   1336 	   time.  Hopefully using localtime here matches the "zero timezone"
   1337 	   hack I added to get_date (get_date of course being the relevant
   1338 	   issue for Make_Date, and for history.c too I think).  */
   1339 	ftm = localtime (&unixtime);
   1340 
   1341     (void) sprintf (date, DATEFORM,
   1342 		    ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
   1343 		    ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
   1344 		    ftm->tm_min, ftm->tm_sec);
   1345     ret = xstrdup (date);
   1346     return ret;
   1347 }
   1348 
   1349 
   1350 const char *
   1351 getCVSDir (const char *suffix)
   1352 {
   1353     static const char *buf[20][2];
   1354     size_t i, len;
   1355 
   1356     for (i = 0; i < 20; i++) {
   1357 	if (buf[i][0] == NULL)
   1358 	    break;
   1359 	if (strcmp (buf[i][0], suffix) == 0)
   1360 	    return buf[i][1];
   1361     }
   1362 
   1363     if (i == 20)
   1364 	error (1, 0, "Out of static buffer space");
   1365 
   1366     buf[i][0] = suffix;
   1367     buf[i][1] = xmalloc (len = strlen(cvsDir) + strlen(suffix) + 1);
   1368     snprintf ((char *)buf[i][1], len, "%s%s", cvsDir, suffix);
   1369     return buf[i][1];
   1370 }
   1371 
   1372 
   1373 
   1374 /* Convert a date to RFC822/1123 format.  This is used in contexts like
   1375    dates to send in the protocol; it should not vary based on locale or
   1376    other such conventions for users.  We should have another routine which
   1377    does that kind of thing.
   1378 
   1379    The SOURCE date is in our internal RCS format.  DEST should point to
   1380    storage managed by the caller, at least MAXDATELEN characters.  */
   1381 void
   1382 date_to_internet (char *dest, const char *source)
   1383 {
   1384     struct tm date;
   1385 
   1386     date_to_tm (&date, source);
   1387     tm_to_internet (dest, &date);
   1388 }
   1389 
   1390 
   1391 
   1392 void
   1393 date_to_tm (struct tm *dest, const char *source)
   1394 {
   1395     if (sscanf (source, SDATEFORM,
   1396 		&dest->tm_year, &dest->tm_mon, &dest->tm_mday,
   1397 		&dest->tm_hour, &dest->tm_min, &dest->tm_sec)
   1398 	    != 6)
   1399 	/* Is there a better way to handle errors here?  I made this
   1400 	   non-fatal in case we are called from the code which can't
   1401 	   deal with fatal errors.  */
   1402 	error (0, 0, "internal error: bad date %s", source);
   1403 
   1404     if (dest->tm_year > 100)
   1405 	dest->tm_year -= 1900;
   1406 
   1407     dest->tm_mon -= 1;
   1408 }
   1409 
   1410 
   1411 
   1412 /* Convert a date to RFC822/1123 format.  This is used in contexts like
   1413    dates to send in the protocol; it should not vary based on locale or
   1414    other such conventions for users.  We should have another routine which
   1415    does that kind of thing.
   1416 
   1417    The SOURCE date is a pointer to a struct tm.  DEST should point to
   1418    storage managed by the caller, at least MAXDATELEN characters.  */
   1419 void
   1420 tm_to_internet (char *dest, const struct tm *source)
   1421 {
   1422     /* Just to reiterate, these strings are from RFC822 and do not vary
   1423        according to locale.  */
   1424     static const char *const month_names[] =
   1425       {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
   1426 	 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
   1427 
   1428     sprintf (dest, "%d %s %d %02d:%02d:%02d -0000", source->tm_mday,
   1429 	     source->tm_mon < 0 || source->tm_mon > 11
   1430                ? "???" : month_names[source->tm_mon],
   1431 	     source->tm_year + 1900, source->tm_hour, source->tm_min,
   1432              source->tm_sec);
   1433 }
   1434 
   1435 
   1436 
   1437 /*
   1438  * Format a date for the current locale.
   1439  *
   1440  * INPUT
   1441  *   UNIXTIME	The UNIX seconds since the epoch.
   1442  *
   1443  * RETURNS
   1444  *   If my_strftime() encounters an error, this function can return NULL.
   1445  *
   1446  *   Otherwise, returns a date string in ISO8601 format, e.g.:
   1447  *
   1448  *	2004-04-29 13:24:22 -0700
   1449  *
   1450  *   It is the responsibility of the caller to return of this string.
   1451  */
   1452 static char *
   1453 format_time_t (time_t unixtime)
   1454 {
   1455     static char buf[sizeof ("yyyy-mm-dd HH:MM:SS -HHMM")];
   1456     /* Convert to a time in the local time zone.  */
   1457     struct tm ltm = *(localtime (&unixtime));
   1458 
   1459     if (!my_strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S %z", &ltm, 0, 0))
   1460 	return NULL;
   1461 
   1462     return xstrdup (buf);
   1463 }
   1464 
   1465 
   1466 
   1467 /* Like format_time_t(), but return time in UTC.
   1468  */
   1469 char *
   1470 gmformat_time_t (time_t unixtime)
   1471 {
   1472     static char buf[sizeof ("yyyy-mm-dd HH:MM:SS -HHMM")];
   1473     /* Convert to a time in the local time zone.  */
   1474     struct tm ltm = *(gmtime (&unixtime));
   1475 
   1476     if (!my_strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S %z", &ltm, 0, 0))
   1477 	return NULL;
   1478 
   1479     return xstrdup (buf);
   1480 }
   1481 
   1482 
   1483 
   1484 /* Format a date in the local timezone using format_time_t() given a date from
   1485  * an arbitrary timezone in a string.
   1486  *
   1487  * INPUT
   1488  *   DATESTR	A string that looks like anything get_date() can parse, e.g.:
   1489  *
   1490  *                      2004-04-29 20:24:22
   1491  *
   1492  * ERRORS
   1493  *   As get_date() & format_time_t().  Prints a warning if either provide
   1494  *   error return values.  See RETURNS.
   1495  *
   1496  * RETURNS
   1497  *   A freshly allocated string that is a copy of the input string if either
   1498  *   get_date() or format_time_t() encounter an error and as format_time_t()
   1499  *   otherwise.
   1500  */
   1501 char *
   1502 format_date_alloc (char *datestr)
   1503 {
   1504     struct timespec t;
   1505     char *buf;
   1506 
   1507     TRACE (TRACE_FUNCTION, "format_date (%s)", datestr);
   1508 
   1509     /* Convert the date string to seconds since the epoch. */
   1510     if (!get_date (&t, datestr, NULL))
   1511     {
   1512 	error (0, 0, "Can't parse date/time: `%s'.", datestr);
   1513 	goto as_is;
   1514     }
   1515 
   1516     /* Get the time into a string, truncating any nanoseconds returned by
   1517      * getdate.
   1518      */
   1519     if ((buf = format_time_t (t.tv_sec)) == NULL)
   1520     {
   1521 	error (0, 0, "Unable to reformat date `%s'.", datestr);
   1522 	goto as_is;
   1523     }
   1524 
   1525     return buf;
   1526 
   1527  as_is:
   1528     return xstrdup (datestr);
   1529 }
   1530 
   1531 void
   1532 getoptreset (void)
   1533 {
   1534 #ifdef HAVE_GETOPT_OPTRESET
   1535 	optreset = 1;
   1536 	optind = 1;
   1537 #else
   1538 	optind = 0;
   1539 #endif
   1540 	opterr = 1;
   1541 }
   1542 
   1543 
   1544 void
   1545 usage (register const char *const *cpp)
   1546 {
   1547     (void) fprintf (stderr, *cpp++, program_name, cvs_cmd_name);
   1548     for (; *cpp; cpp++)
   1549 	(void) fprintf (stderr, "%s", *cpp);
   1550     exit (EXIT_FAILURE);
   1551 }
   1552 
   1553 /* vim:tabstop=8:shiftwidth=4
   1554  */
   1555