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