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