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