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