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