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