xref: /openbsd-src/gnu/usr.bin/cvs/src/main.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*
2  *    Copyright (c) 1992, Brian Berliner and Jeff Polk
3  *    Copyright (c) 1989-1992, Brian Berliner
4  *
5  *    You may distribute under the terms of the GNU General Public License
6  *    as specified in the README file that comes with the CVS source distribution.
7  *
8  * This is the main C driver for the CVS system.
9  *
10  * Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing
11  * the shell-script CVS system that this is based on.
12  *
13  */
14 
15 #include <assert.h>
16 #include "cvs.h"
17 
18 #ifdef HAVE_WINSOCK_H
19 #include <winsock.h>
20 #else
21 extern int gethostname ();
22 #endif
23 
24 char *program_name;
25 char *program_path;
26 char *command_name;
27 
28 char *global_session_id; /* Random session ID */
29 
30 /* I'd dynamically allocate this, but it seems like gethostname
31    requires a fixed size array.  If I'm remembering the RFCs right,
32    256 should be enough.  */
33 #ifndef MAXHOSTNAMELEN
34 #define MAXHOSTNAMELEN  256
35 #endif
36 
37 char hostname[MAXHOSTNAMELEN];
38 
39 int use_editor = 1;
40 int use_cvsrc = 1;
41 int cvswrite = !CVSREAD_DFLT;
42 int really_quiet = 0;
43 int quiet = 0;
44 int trace = 0;
45 int noexec = 0;
46 int readonlyfs = 0;
47 int logoff = 0;
48 
49 /* Set if we should be writing CVSADM directories at top level.  At
50    least for now we'll make the default be off (the CVS 1.9, not CVS
51    1.9.2, behavior). */
52 int top_level_admin = 0;
53 
54 mode_t cvsumask = UMASK_DFLT;
55 char *RCS_citag = NULL;
56 int disable_mdocdate = 0;
57 
58 char *CurDir;
59 
60 /*
61  * Defaults, for the environment variables that are not set
62  */
63 char *Tmpdir = TMPDIR_DFLT;
64 char *Editor = EDITOR_DFLT;
65 
66 
67 /* When our working directory contains subdirectories with different
68    values in CVS/Root files, we maintain a list of them.  */
69 List *root_directories = NULL;
70 
71 /* We step through the above values.  This variable is set to reflect
72  * the currently active value.
73  *
74  * Now static.  FIXME - this variable should be removable (well, localizable)
75  * with a little more work.
76  */
77 static char *current_root = NULL;
78 
79 
80 static const struct cmd
81 {
82     char *fullname;		/* Full name of the function (e.g. "commit") */
83 
84     /* Synonyms for the command, nick1 and nick2.  We supply them
85        mostly for two reasons: (1) CVS has always supported them, and
86        we need to maintain compatibility, (2) if there is a need for a
87        version which is shorter than the fullname, for ease in typing.
88        Synonyms have the disadvantage that people will see "new" and
89        then have to think about it, or look it up, to realize that is
90        the operation they know as "add".  Also, this means that one
91        cannot create a command "cvs new" with a different meaning.  So
92        new synonyms are probably best used sparingly, and where used
93        should be abbreviations of the fullname (preferably consisting
94        of the first 2 or 3 or so letters).
95 
96        One thing that some systems do is to recognize any unique
97        abbreviation, for example "annotat" "annota", etc., for
98        "annotate".  The problem with this is that scripts and user
99        habits will expect a certain abbreviation to be unique, and in
100        a future release of CVS it may not be.  So it is better to
101        accept only an explicit list of abbreviations and plan on
102        supporting them in the future as well as now.  */
103 
104     char *nick1;
105     char *nick2;
106 
107     int (*func) ();		/* Function takes (argc, argv) arguments. */
108     unsigned long attr;		/* Attributes. */
109 } cmds[] =
110 
111 {
112     { "add",      "ad",       "new",       add,       CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
113     { "admin",    "adm",      "rcs",       admin,     CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
114     { "annotate", "ann",      "blame",     annotate,  CVS_CMD_USES_WORK_DIR },
115     { "checkout", "co",       "get",       checkout,  0 },
116     { "commit",   "ci",       "com",       commit,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
117     { "diff",     "di",       "dif",       diff,      CVS_CMD_USES_WORK_DIR },
118     { "edit",     NULL,       NULL,        edit,      CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
119     { "editors",  NULL,       NULL,        editors,   CVS_CMD_USES_WORK_DIR },
120     { "export",   "exp",      "ex",        checkout,  CVS_CMD_USES_WORK_DIR },
121     { "history",  "hi",       "his",       history,   CVS_CMD_USES_WORK_DIR },
122     { "import",   "im",       "imp",       import,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR | CVS_CMD_IGNORE_ADMROOT},
123     { "init",     NULL,       NULL,        init,      CVS_CMD_MODIFIES_REPOSITORY },
124 #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
125     { "kserver",  NULL,       NULL,        server,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */
126 #endif
127     { "log",      "lo",       NULL,        cvslog,    CVS_CMD_USES_WORK_DIR },
128 #ifdef AUTH_CLIENT_SUPPORT
129     { "login",    "logon",    "lgn",       login,     0 },
130     { "logout",   NULL,       NULL,        logout,    0 },
131 #endif /* AUTH_CLIENT_SUPPORT */
132 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
133     { "pserver",  NULL,       NULL,        server,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */
134 #endif
135     { "rannotate","rann",     "ra",        annotate,  0 },
136     { "rdiff",    "patch",    "pa",        patch,     0 },
137     { "release",  "re",       "rel",       release,   0 },
138     { "remove",   "rm",       "delete",    cvsremove, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
139     { "rlog",     "rl",       NULL,        cvslog,    0 },
140     { "rtag",     "rt",       "rfreeze",   cvstag,    CVS_CMD_MODIFIES_REPOSITORY },
141 #ifdef SERVER_SUPPORT
142     { "server",   NULL,       NULL,        server,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
143 #endif
144     { "status",   "st",       "stat",      cvsstatus, CVS_CMD_USES_WORK_DIR },
145     { "tag",      "ta",       "freeze",    cvstag,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
146     { "unedit",   NULL,       NULL,        unedit,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
147     { "update",   "up",       "upd",       update,    CVS_CMD_USES_WORK_DIR },
148     { "version",  "ve",       "ver",       version,   0 },
149     { "watch",    NULL,       NULL,        watch,     CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
150     { "watchers", NULL,       NULL,        watchers,  CVS_CMD_USES_WORK_DIR },
151     { NULL, NULL, NULL, NULL, 0 },
152 };
153 
154 static const char *const usg[] =
155 {
156     /* CVS usage messages never have followed the GNU convention of
157        putting metavariables in uppercase.  I don't know whether that
158        is a good convention or not, but if it changes it would have to
159        change in all the usage messages.  For now, they consistently
160        use lowercase, as far as I know.  Puncutation is pretty funky,
161        though.  Sometimes they use none, as here.  Sometimes they use
162        single quotes (not the TeX-ish `' stuff), as in --help-options.
163        Sometimes they use double quotes, as in cvs -H add.
164 
165        Most (not all) of the usage messages seem to have periods at
166        the end of each line.  I haven't tried to duplicate this style
167        in --help as it is a rather different format from the rest.  */
168 
169     "Usage: %s [cvs-options] command [command-options-and-arguments]\n",
170     "  where cvs-options are -q, -n, etc.\n",
171     "    (specify --help-options for a list of options)\n",
172     "  where command is add, admin, etc.\n",
173     "    (specify --help-commands for a list of commands\n",
174     "     or --help-synonyms for a list of command synonyms)\n",
175     "  where command-options-and-arguments depend on the specific command\n",
176     "    (specify -H followed by a command name for command-specific help)\n",
177     "  Specify --help to receive this message\n",
178     "\n",
179 
180     /* Some people think that a bug-reporting address should go here.  IMHO,
181        the web sites are better because anything else is very likely to go
182        obsolete in the years between a release and when someone might be
183        reading this help.  Besides, we could never adequately discuss
184        bug reporting in a concise enough way to put in a help message.  */
185 
186     /* I was going to put this at the top, but usage() wants the %s to
187        be in the first line.  */
188     "The Concurrent Versions System (CVS) is a tool for version control.\n",
189     /* I really don't think I want to try to define "version control"
190        in one line.  I'm not sure one can get more concise than the
191        paragraph in ../cvs.spec without assuming the reader knows what
192        version control means.  */
193 
194     "For CVS updates and additional information, see\n",
195     "    the CVS home page at http://www.cvshome.org/ or\n",
196     "    Pascal Molli's CVS site at http://www.loria.fr/~molli/cvs-index.html\n",
197     NULL,
198 };
199 
200 static const char *const cmd_usage[] =
201 {
202     "CVS commands are:\n",
203     "        add          Add a new file/directory to the repository\n",
204     "        admin        Administration front end for rcs\n",
205     "        annotate     Show last revision where each line was modified\n",
206     "        checkout     Checkout sources for editing\n",
207     "        commit       Check files into the repository\n",
208     "        diff         Show differences between revisions\n",
209     "        edit         Get ready to edit a watched file\n",
210     "        editors      See who is editing a watched file\n",
211     "        export       Export sources from CVS, similar to checkout\n",
212     "        history      Show repository access history\n",
213     "        import       Import sources into CVS, using vendor branches\n",
214     "        init         Create a CVS repository if it doesn't exist\n",
215 #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
216     "        kserver      Kerberos server mode\n",
217 #endif
218     "        log          Print out history information for files\n",
219 #ifdef AUTH_CLIENT_SUPPORT
220     "        login        Prompt for password for authenticating server\n",
221     "        logout       Removes entry in .cvspass for remote repository\n",
222 #endif /* AUTH_CLIENT_SUPPORT */
223 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
224     "        pserver      Password server mode\n",
225 #endif
226     "        rannotate    Show last revision where each line of module was modified\n",
227     "        rdiff        Create 'patch' format diffs between releases\n",
228     "        release      Indicate that a Module is no longer in use\n",
229     "        remove       Remove an entry from the repository\n",
230     "        rlog         Print out history information for a module\n",
231     "        rtag         Add a symbolic tag to a module\n",
232 #ifdef SERVER_SUPPORT
233     "        server       Server mode\n",
234 #endif
235     "        status       Display status information on checked out files\n",
236     "        tag          Add a symbolic tag to checked out version of files\n",
237     "        unedit       Undo an edit command\n",
238     "        update       Bring work tree in sync with repository\n",
239     "        version      Show current CVS version(s)\n",
240     "        watch        Set watches\n",
241     "        watchers     See who is watching a file\n",
242     "(Specify the --help option for a list of other help options)\n",
243     NULL,
244 };
245 
246 static const char *const opt_usage[] =
247 {
248     /* Omit -b because it is just for compatibility.  */
249     "CVS global options (specified before the command name) are:\n",
250     "    -H           Displays usage information for command.\n",
251     "    -Q           Cause CVS to be really quiet.\n",
252     "    -q           Cause CVS to be somewhat quiet.\n",
253     "    -r           Make checked-out files read-only.\n",
254     "    -w           Make checked-out files read-write (default).\n",
255     "    -l           Turn history logging off.\n",
256     "    -n           Do not execute anything that will change the disk.\n",
257     "    -t           Show trace of program execution -- try with -n.\n",
258     "    -v           CVS version and copyright.\n",
259     "    -R           Read-only repository.\n",
260     "    -T tmpdir    Use 'tmpdir' for temporary files.\n",
261     "    -e editor    Use 'editor' for editing log information.\n",
262     "    -d CVS_root  Overrides $CVSROOT as the root of the CVS tree.\n",
263     "    -f           Do not use the ~/.cvsrc file.\n",
264 #ifdef CLIENT_SUPPORT
265     "    -z #         Use compression level '#' for net traffic.\n",
266 #ifdef ENCRYPTION
267     "    -x           Encrypt all net traffic.\n",
268 #endif
269     "    -a           Authenticate all net traffic.\n",
270 #endif
271     "    -s VAR=VAL   Set CVS user variable.\n",
272     "(Specify the --help option for a list of other help options)\n",
273     NULL
274 };
275 
276 
277 static int
278 set_root_directory (p, ignored)
279     Node *p;
280     void *ignored;
281 {
282     if (current_root == NULL && p->data == NULL)
283     {
284 	current_root = p->key;
285 	return 1;
286     }
287     return 0;
288 }
289 
290 
291 static const char * const*
292 cmd_synonyms ()
293 {
294     char ** synonyms;
295     char ** line;
296     const struct cmd *c = &cmds[0];
297     /* Three more for title, "specify --help" line, and NULL.  */
298     int numcmds = 3;
299 
300     while (c->fullname != NULL)
301     {
302 	numcmds++;
303 	c++;
304     }
305 
306     synonyms = (char **) xmalloc(numcmds * sizeof(char *));
307     line = synonyms;
308     *line++ = "CVS command synonyms are:\n";
309     for (c = &cmds[0]; c->fullname != NULL; c++)
310     {
311 	if (c->nick1 || c->nick2)
312 	{
313 	    *line = xmalloc (strlen (c->fullname)
314 			     + (c->nick1 != NULL ? strlen (c->nick1) : 0)
315 			     + (c->nick2 != NULL ? strlen (c->nick2) : 0)
316 			     + 40);
317 	    sprintf(*line, "        %-12s %s %s\n", c->fullname,
318 		    c->nick1 ? c->nick1 : "",
319 		    c->nick2 ? c->nick2 : "");
320 	    line++;
321 	}
322     }
323     *line++ = "(Specify the --help option for a list of other help options)\n";
324     *line = NULL;
325 
326     return (const char * const*) synonyms; /* will never be freed */
327 }
328 
329 
330 unsigned long int
331 lookup_command_attribute (cmd_name)
332      char *cmd_name;
333 {
334     const struct cmd *cm;
335 
336     for (cm = cmds; cm->fullname; cm++)
337     {
338 	if (strcmp (cmd_name, cm->fullname) == 0)
339 	    break;
340     }
341     return cm->attr;
342 }
343 
344 
345 static RETSIGTYPE
346 main_cleanup (sig)
347     int sig;
348 {
349 #ifndef DONT_USE_SIGNALS
350     const char *name;
351     char temp[10];
352 
353     switch (sig)
354     {
355 #ifdef SIGABRT
356     case SIGABRT:
357 	name = "abort";
358 	break;
359 #endif
360 #ifdef SIGHUP
361     case SIGHUP:
362 	name = "hangup";
363 	break;
364 #endif
365 #ifdef SIGINT
366     case SIGINT:
367 	name = "interrupt";
368 	break;
369 #endif
370 #ifdef SIGQUIT
371     case SIGQUIT:
372 	name = "quit";
373 	break;
374 #endif
375 #ifdef SIGPIPE
376     case SIGPIPE:
377 	name = "broken pipe";
378 	break;
379 #endif
380 #ifdef SIGTERM
381     case SIGTERM:
382 	name = "termination";
383 	break;
384 #endif
385     default:
386 	/* This case should never be reached, because we list above all
387 	   the signals for which we actually establish a signal handler.  */
388 	sprintf (temp, "%d", sig);
389 	name = temp;
390 	break;
391     }
392 
393     error (1, 0, "received %s signal", name);
394 #endif /* !DONT_USE_SIGNALS */
395 }
396 
397 int
398 main (argc, argv)
399     int argc;
400     char **argv;
401 {
402     char *CVSroot = CVSROOT_DFLT;
403     char *cp, *end;
404     const struct cmd *cm;
405     int c, err = 0;
406     int tmpdir_update_env, cvs_update_env;
407     int free_CVSroot = 0;
408     int free_Editor = 0;
409     int free_Tmpdir = 0;
410 
411     int help = 0;		/* Has the user asked for help?  This
412 				   lets us support the `cvs -H cmd'
413 				   convention to give help for cmd. */
414     static const char short_options[] = "+Qqrwtnlvb:T:e:d:Hfz:s:xaR";
415     static struct option long_options[] =
416     {
417         {"help", 0, NULL, 'H'},
418         {"version", 0, NULL, 'v'},
419 	{"help-commands", 0, NULL, 1},
420 	{"help-synonyms", 0, NULL, 2},
421 	{"help-options", 0, NULL, 4},
422 	{"allow-root", required_argument, NULL, 3},
423         {0, 0, 0, 0}
424     };
425     /* `getopt_long' stores the option index here, but right now we
426         don't use it. */
427     int option_index = 0;
428 
429 #ifdef SYSTEM_INITIALIZE
430     /* Hook for OS-specific behavior, for example socket subsystems on
431        NT and OS2 or dealing with windows and arguments on Mac.  */
432     SYSTEM_INITIALIZE (&argc, &argv);
433 #endif
434 
435 #ifdef HAVE_TZSET
436     /* On systems that have tzset (which is almost all the ones I know
437        of), it's a good idea to call it.  */
438     tzset ();
439 #endif
440 
441     /*
442      * Just save the last component of the path for error messages
443      */
444     program_path = xstrdup (argv[0]);
445 #ifdef ARGV0_NOT_PROGRAM_NAME
446     /* On some systems, e.g. VMS, argv[0] is not the name of the command
447        which the user types to invoke the program.  */
448     program_name = "cvs";
449 #else
450     program_name = last_component (argv[0]);
451 #endif
452 
453     /*
454      * Query the environment variables up-front, so that
455      * they can be overridden by command line arguments
456      */
457     cvs_update_env = 0;
458     tmpdir_update_env = *Tmpdir;	/* TMPDIR_DFLT must be set */
459     if ((cp = getenv (TMPDIR_ENV)) != NULL)
460     {
461 	Tmpdir = cp;
462 	tmpdir_update_env = 0;		/* it's already there */
463     }
464     if ((cp = getenv (EDITOR1_ENV)) != NULL)
465  	Editor = cp;
466     else if ((cp = getenv (EDITOR2_ENV)) != NULL)
467 	Editor = cp;
468     else if ((cp = getenv (EDITOR3_ENV)) != NULL)
469 	Editor = cp;
470     if ((cp = getenv (CVSROOT_ENV)) != NULL)
471     {
472 	CVSroot = cp;
473 	cvs_update_env = 0;		/* it's already there */
474     }
475     if (getenv (CVSREAD_ENV) != NULL)
476 	cvswrite = 0;
477     if (getenv (CVSREADONLYFS_ENV)) {
478 	readonlyfs = 1;
479 	logoff = 1;
480     }
481 
482     /* Set this to 0 to force getopt initialization.  getopt() sets
483        this to 1 internally.  */
484     optind = 0;
485 
486     /* We have to parse the options twice because else there is no
487        chance to avoid reading the global options from ".cvsrc".  Set
488        opterr to 0 for avoiding error messages about invalid options.
489        */
490     opterr = 0;
491 
492     while ((c = getopt_long
493             (argc, argv, short_options, long_options, &option_index))
494            != EOF)
495     {
496 	if (c == 'f')
497 	    use_cvsrc = 0;
498     }
499 
500     /*
501      * Scan cvsrc file for global options.
502      */
503     if (use_cvsrc)
504 	read_cvsrc (&argc, &argv, "cvs");
505 
506     optind = 0;
507     opterr = 1;
508 
509     while ((c = getopt_long
510             (argc, argv, short_options, long_options, &option_index))
511            != EOF)
512     {
513 	switch (c)
514 	{
515             case 1:
516 	        /* --help-commands */
517                 usage (cmd_usage);
518                 break;
519             case 2:
520 	        /* --help-synonyms */
521                 usage (cmd_synonyms());
522                 break;
523 	    case 4:
524 		/* --help-options */
525 		usage (opt_usage);
526 		break;
527 	    case 3:
528 		/* --allow-root */
529 		root_allow_add (optarg);
530 		break;
531 	    case 'Q':
532 		really_quiet = 1;
533 		/* FALL THROUGH */
534 	    case 'q':
535 		quiet = 1;
536 		break;
537 	    case 'r':
538 		cvswrite = 0;
539 		break;
540 	    case 'w':
541 		cvswrite = 1;
542 		break;
543 	    case 't':
544 		trace = 1;
545 		break;
546 	    case 'n':
547 		noexec = 1;
548 	    case 'l':			/* Fall through */
549 		logoff = 1;
550 		break;
551 	    case 'R':
552 		logoff = 1;
553 		readonlyfs = 1;
554 		break;
555 	    case 'v':
556 		(void) fputs ("\n", stdout);
557 		version (0, (char **) NULL);
558 		(void) fputs ("\n", stdout);
559 		(void) fputs ("\
560 Copyright (c) 1989-2001 Brian Berliner, david d `zoo' zuhn, \n\
561                         Jeff Polk, and other authors\n", stdout);
562 		(void) fputs ("\n", stdout);
563 		(void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
564 		(void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
565 		(void) fputs ("\n", stdout);
566 
567 		(void) fputs ("Specify the --help option for further information about CVS\n", stdout);
568 
569 		exit (0);
570 		break;
571 	    case 'b':
572 		/* This option used to specify the directory for RCS
573 		   executables.  But since we don't run them any more,
574 		   this is a noop.  Silently ignore it so that .cvsrc
575 		   and scripts and inetd.conf and such can work with
576 		   either new or old CVS.  */
577 		break;
578 	    case 'T':
579 		Tmpdir = xstrdup (optarg);
580 		free_Tmpdir = 1;
581 		tmpdir_update_env = 1;	/* need to update environment */
582 		break;
583 	    case 'e':
584 		Editor = xstrdup (optarg);
585 		free_Editor = 1;
586 		break;
587 	    case 'd':
588 		if (CVSroot_cmdline != NULL)
589 		    free (CVSroot_cmdline);
590 		CVSroot_cmdline = xstrdup (optarg);
591 		if (free_CVSroot)
592 		    free (CVSroot);
593 		CVSroot = xstrdup (optarg);
594 		free_CVSroot = 1;
595 		cvs_update_env = 1;	/* need to update environment */
596 		break;
597 	    case 'H':
598 	        help = 1;
599 		break;
600             case 'f':
601 		use_cvsrc = 0; /* unnecessary, since we've done it above */
602 		break;
603 	    case 'z':
604 #ifdef CLIENT_SUPPORT
605 		gzip_level = atoi (optarg);
606 		if (gzip_level < 0 || gzip_level > 9)
607 		  error (1, 0,
608 			 "gzip compression level must be between 0 and 9");
609 #endif
610 		/* If no CLIENT_SUPPORT, we just silently ignore the gzip
611 		   level, so that users can have it in their .cvsrc and not
612 		   cause any trouble.  */
613 		break;
614 	    case 's':
615 		variable_set (optarg);
616 		break;
617 	    case 'x':
618 #ifdef CLIENT_SUPPORT
619 	        cvsencrypt = 1;
620 #endif /* CLIENT_SUPPORT */
621 		/* If no CLIENT_SUPPORT, ignore -x, so that users can
622                    have it in their .cvsrc and not cause any trouble.
623                    If no ENCRYPTION, we still accept -x, but issue an
624                    error if we are being run as a client.  */
625 		break;
626 	    case 'a':
627 #ifdef CLIENT_SUPPORT
628 		cvsauthenticate = 1;
629 #endif
630 		/* If no CLIENT_SUPPORT, ignore -a, so that users can
631                    have it in their .cvsrc and not cause any trouble.
632                    We will issue an error later if stream
633                    authentication is not supported.  */
634 		break;
635 	    case '?':
636 	    default:
637                 usage (usg);
638 	}
639     }
640 
641     argc -= optind;
642     argv += optind;
643     if (argc < 1)
644 	usage (usg);
645 
646     /* Generate the cvs global session ID */
647 
648     {
649 	int i = 0;
650 	u_int32_t c;
651 	global_session_id = xmalloc(17);
652 
653 	while (i <= 16) {
654 	    c = arc4random_uniform(75) + 48;
655 	    if ((c >= 48 && c <= 57) || (c >= 65 && c <= 90) ||
656 	        (c >= 97 && c <= 122)) {
657 		global_session_id[i] = c;
658 		i++;
659 	    }
660 	}
661 	global_session_id[16] = '\0';
662     }
663 
664     if (trace)
665 	fprintf (stderr, "main: Session ID is %s", global_session_id);
666 
667 
668     /* Look up the command name. */
669 
670     command_name = argv[0];
671     for (cm = cmds; cm->fullname; cm++)
672     {
673 	if (cm->nick1 && !strcmp (command_name, cm->nick1))
674 	    break;
675 	if (cm->nick2 && !strcmp (command_name, cm->nick2))
676 	    break;
677 	if (!strcmp (command_name, cm->fullname))
678 	    break;
679     }
680 
681     if (!cm->fullname)
682     {
683 	fprintf (stderr, "Unknown command: `%s'\n\n", command_name);
684 	usage (cmd_usage);
685     }
686     else
687 	command_name = cm->fullname;	/* Global pointer for later use */
688 
689     if (help)
690     {
691 	argc = -1;		/* some functions only check for this */
692 	err = (*(cm->func)) (argc, argv);
693     }
694     else
695     {
696 	/* The user didn't ask for help, so go ahead and authenticate,
697            set up CVSROOT, and the rest of it. */
698 
699 	/* The UMASK environment variable isn't handled with the
700 	   others above, since we don't want to signal errors if the
701 	   user has asked for help.  This won't work if somebody adds
702 	   a command-line flag to set the umask, since we'll have to
703 	   parse it before we get here. */
704 
705 	if ((cp = getenv (CVSUMASK_ENV)) != NULL)
706 	{
707 	    /* FIXME: Should be accepting symbolic as well as numeric mask.  */
708 	    cvsumask = strtol (cp, &end, 8) & 0777;
709 	    if (*end != '\0')
710 		error (1, errno, "invalid umask value in %s (%s)",
711 		       CVSUMASK_ENV, cp);
712 	}
713 
714 #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
715 	/* If we are invoked with a single argument "kserver", then we are
716 	   running as Kerberos server as root.  Do the authentication as
717 	   the very first thing, to minimize the amount of time we are
718 	   running as root.  */
719 	if (strcmp (command_name, "kserver") == 0)
720 	{
721 	    kserver_authenticate_connection ();
722 
723 	    /* Pretend we were invoked as a plain server.  */
724 	    command_name = "server";
725 	}
726 #endif /* HAVE_KERBEROS */
727 
728 
729 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
730 	if (strcmp (command_name, "pserver") == 0)
731 	{
732 	    /* The reason that --allow-root is not a command option
733 	       is mainly the comment in server() about how argc,argv
734 	       might be from .cvsrc.  I'm not sure about that, and
735 	       I'm not sure it is only true of command options, but
736 	       it seems easier to make it a global option.  */
737 
738 	    /* Gets username and password from client, authenticates, then
739 	       switches to run as that user and sends an ACK back to the
740 	       client. */
741 	    pserver_authenticate_connection ();
742 
743 	    /* Pretend we were invoked as a plain server.  */
744 	    command_name = "server";
745 	}
746 #endif /* (AUTH_SERVER_SUPPORT || HAVE_GSSAPI) && SERVER_SUPPORT */
747 
748 #ifdef SERVER_SUPPORT
749 	server_active = strcmp (command_name, "server") == 0;
750 #endif
751 
752 	/* This is only used for writing into the history file.  For
753 	   remote connections, it might be nice to have hostname
754 	   and/or remote path, on the other hand I'm not sure whether
755 	   it is worth the trouble.  */
756 
757 #ifdef SERVER_SUPPORT
758 	if (server_active)
759 	    CurDir = xstrdup ("<remote>");
760 	else
761 #endif
762 	{
763 	    CurDir = xgetwd ();
764             if (CurDir == NULL)
765 		error (1, errno, "cannot get working directory");
766 	}
767 
768 	if (Tmpdir == NULL || Tmpdir[0] == '\0')
769 	    Tmpdir = "/tmp";
770 
771 #ifdef HAVE_PUTENV
772 	if (tmpdir_update_env)
773 	{
774 	    char *env;
775 	    env = xmalloc (strlen (TMPDIR_ENV) + strlen (Tmpdir) + 1 + 1);
776 	    (void) sprintf (env, "%s=%s", TMPDIR_ENV, Tmpdir);
777 	    (void) putenv (env);
778 	    /* do not free env, as putenv has control of it */
779 	}
780 #endif
781 
782 #ifndef DONT_USE_SIGNALS
783 	/* make sure we clean up on error */
784 #ifdef SIGABRT
785 	(void) SIG_register (SIGABRT, main_cleanup);
786 #endif
787 #ifdef SIGHUP
788 	(void) SIG_register (SIGHUP, main_cleanup);
789 #endif
790 #ifdef SIGINT
791 	(void) SIG_register (SIGINT, main_cleanup);
792 #endif
793 #ifdef SIGQUIT
794 	(void) SIG_register (SIGQUIT, main_cleanup);
795 #endif
796 #ifdef SIGPIPE
797 	(void) SIG_register (SIGPIPE, main_cleanup);
798 #endif
799 #ifdef SIGTERM
800 	(void) SIG_register (SIGTERM, main_cleanup);
801 #endif
802 #endif /* !DONT_USE_SIGNALS */
803 
804 	gethostname(hostname, sizeof (hostname));
805 
806 #ifdef KLUDGE_FOR_WNT_TESTSUITE
807 	/* Probably the need for this will go away at some point once
808 	   we call fflush enough places (e.g. fflush (stdout) in
809 	   cvs_outerr).  */
810 	(void) setvbuf (stdout, (char *) NULL, _IONBF, 0);
811 	(void) setvbuf (stderr, (char *) NULL, _IONBF, 0);
812 #endif /* KLUDGE_FOR_WNT_TESTSUITE */
813 
814 	if (use_cvsrc)
815 	    read_cvsrc (&argc, &argv, command_name);
816 
817 #ifdef SERVER_SUPPORT
818 	/* Fiddling with CVSROOT doesn't make sense if we're running
819 	       in server mode, since the client will send the repository
820 	       directory after the connection is made. */
821 
822 	if (!server_active)
823 #endif
824 	{
825 	    char *CVSADM_Root;
826 
827 	    /* See if we are able to find a 'better' value for CVSroot
828 	       in the CVSADM_ROOT directory. */
829 
830 	    CVSADM_Root = NULL;
831 
832 	    /* "cvs import" shouldn't check CVS/Root; in general it
833 	       ignores CVS directories and CVS/Root is likely to
834 	       specify a different repository than the one we are
835 	       importing to.  */
836 
837 	    if (!(cm->attr & CVS_CMD_IGNORE_ADMROOT)
838 
839 		/* -d overrides CVS/Root, so don't give an error if the
840 		   latter points to a nonexistent repository.  */
841 		&& CVSroot_cmdline == NULL)
842 	    {
843 		CVSADM_Root = Name_Root((char *) NULL, (char *) NULL);
844 	    }
845 
846 	    if (CVSADM_Root != NULL)
847 	    {
848 		if (CVSroot == NULL || !cvs_update_env)
849 		{
850 		    CVSroot = CVSADM_Root;
851 		    cvs_update_env = 1;	/* need to update environment */
852 		}
853 	    }
854 
855 	    /* Now we've reconciled CVSROOT from the command line, the
856 	       CVS/Root file, and the environment variable.  Do the
857 	       last sanity checks on the variable. */
858 
859 	    if (! CVSroot)
860 	    {
861 		error (0, 0,
862 		       "No CVSROOT specified!  Please use the `-d' option");
863 		error (1, 0,
864 		       "or set the %s environment variable.", CVSROOT_ENV);
865 	    }
866 
867 	    if (! *CVSroot)
868 	    {
869 		error (0, 0,
870 		       "CVSROOT is set but empty!  Make sure that the");
871 		error (0, 0,
872 		       "specification of CVSROOT is legal, either via the");
873 		error (0, 0,
874 		       "`-d' option, the %s environment variable, or the",
875 		       CVSROOT_ENV);
876 		error (1, 0,
877 		       "CVS/Root file (if any).");
878 	    }
879 	}
880 
881 	/* Here begins the big loop over unique cvsroot values.  We
882            need to call do_recursion once for each unique value found
883            in CVS/Root.  Prime the list with the current value. */
884 
885 	/* Create the list. */
886 	assert (root_directories == NULL);
887 	root_directories = getlist ();
888 
889 	/* Prime it. */
890 	if (CVSroot != NULL)
891 	{
892 	    Node *n;
893 	    n = getnode ();
894 	    n->type = NT_UNKNOWN;
895 	    n->key = xstrdup (CVSroot);
896 	    n->data = NULL;
897 
898 	    if (addnode (root_directories, n))
899 		error (1, 0, "cannot add initial CVSROOT %s", n->key);
900 	}
901 
902 	assert (current_root == NULL);
903 
904 	/* If we're running the server, we want to execute this main
905 	   loop once and only once (we won't be serving multiple roots
906 	   from this connection, so there's no need to do it more than
907 	   once).  To get out of the loop, we perform a "break" at the
908 	   end of things.  */
909 
910 	while (
911 #ifdef SERVER_SUPPORT
912 	       server_active ||
913 #endif
914 	       walklist (root_directories, set_root_directory, NULL)
915 	       )
916 	{
917 #ifdef SERVER_SUPPORT
918 	    /* Fiddling with CVSROOT doesn't make sense if we're running
919 	       in server mode, since the client will send the repository
920 	       directory after the connection is made. */
921 
922 	    if (!server_active)
923 #endif
924 	    {
925 		/* Now we're 100% sure that we have a valid CVSROOT
926 		   variable.  Parse it to see if we're supposed to do
927 		   remote accesses or use a special access method. */
928 
929 		if (current_parsed_root != NULL)
930 		    free_cvsroot_t (current_parsed_root);
931 		if ((current_parsed_root = parse_cvsroot (current_root)) == NULL)
932 		    error (1, 0, "Bad CVSROOT.");
933 
934 		if (trace)
935 		    fprintf (stderr, "%s-> main loop with CVSROOT=%s\n",
936 			   CLIENT_SERVER_STR, current_root);
937 
938 		/*
939 		 * Check to see if the repository exists.
940 		 */
941 #ifdef CLIENT_SUPPORT
942 		if (!current_parsed_root->isremote)
943 #endif	/* CLIENT_SUPPORT */
944 		{
945 		    char *path;
946 		    int save_errno;
947 
948 		    path = xmalloc (strlen (current_parsed_root->directory)
949 				    + sizeof (CVSROOTADM)
950 				    + 20);
951 		    (void) sprintf (path, "%s/%s", current_parsed_root->directory, CVSROOTADM);
952 		    if (readonlyfs == 0 && !isaccessible (path, R_OK | X_OK))
953 		    {
954 			save_errno = errno;
955 			/* If this is "cvs init", the root need not exist yet.  */
956 			if (strcmp (command_name, "init") != 0)
957 			{
958 			    error (1, save_errno, "%s", path);
959 			}
960 		    }
961 		    free (path);
962 		}
963 
964 #ifdef HAVE_PUTENV
965 		/* Update the CVSROOT environment variable if necessary. */
966 		/* FIXME (njc): should we always set this with the CVSROOT from the command line? */
967 		if (cvs_update_env)
968 		{
969 		    static char *prev;
970 		    char *env;
971 		    env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot)
972 				   + 1 + 1);
973 		    (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot);
974 		    (void) putenv (env);
975 		    /* do not free env yet, as putenv has control of it */
976 		    /* but do free the previous value, if any */
977 		    if (prev != NULL)
978 			free (prev);
979 		    prev = env;
980 		}
981 #endif
982 	    }
983 
984 	    /* Parse the CVSROOT/config file, but only for local.  For the
985 	       server, we parse it after we know $CVSROOT.  For the
986 	       client, it doesn't get parsed at all, obviously.  The
987 	       presence of the parse_config call here is not mean to
988 	       predetermine whether CVSROOT/config overrides things from
989 	       read_cvsrc and other such places or vice versa.  That sort
990 	       of thing probably needs more thought.  */
991 	    if (1
992 #ifdef SERVER_SUPPORT
993 		&& !server_active
994 #endif
995 #ifdef CLIENT_SUPPORT
996 		&& !current_parsed_root->isremote
997 #endif
998 		)
999 	    {
1000 		/* If there was an error parsing the config file, parse_config
1001 		   already printed an error.  We keep going.  Why?  Because
1002 		   if we didn't, then there would be no way to check in a new
1003 		   CVSROOT/config file to fix the broken one!  */
1004 		parse_config (current_parsed_root->directory);
1005 	    }
1006 
1007 #ifdef CLIENT_SUPPORT
1008 	    /* Need to check for current_parsed_root != NULL here since
1009 	     * we could still be in server mode before the server function
1010 	     * gets called below and sets the root
1011 	     */
1012 	    if (current_parsed_root != NULL && current_parsed_root->isremote)
1013 	    {
1014 		/* Create a new list for directory names that we've
1015 		   sent to the server. */
1016 		if (dirs_sent_to_server != NULL)
1017 		    dellist (&dirs_sent_to_server);
1018 		dirs_sent_to_server = getlist ();
1019 	    }
1020 #endif
1021 
1022 	    err = (*(cm->func)) (argc, argv);
1023 
1024 	    /* Mark this root directory as done.  When the server is
1025                active, current_root will be NULL -- don't try and
1026                remove it from the list. */
1027 
1028 	    if (current_root != NULL)
1029 	    {
1030 		Node *n = findnode (root_directories, current_root);
1031 		assert (n != NULL);
1032 		n->data = (void *) 1;
1033 		current_root = NULL;
1034 	    }
1035 
1036 #if 0
1037 	    /* This will not work yet, since it tries to free (void *) 1. */
1038 	    dellist (&root_directories);
1039 #endif
1040 
1041 #ifdef SERVER_SUPPORT
1042 	    if (server_active)
1043 	      break;
1044 #endif
1045 	} /* end of loop for cvsroot values */
1046 
1047     } /* end of stuff that gets done if the user DOESN'T ask for help */
1048 
1049     Lock_Cleanup ();
1050 
1051     free (program_path);
1052     if (CVSroot_cmdline != NULL)
1053 	free (CVSroot_cmdline);
1054     if (free_CVSroot)
1055 	free (CVSroot);
1056     if (free_Editor)
1057 	free (Editor);
1058     if (free_Tmpdir)
1059 	free (Tmpdir);
1060     root_allow_free ();
1061 
1062 #ifdef SYSTEM_CLEANUP
1063     /* Hook for OS-specific behavior, for example socket subsystems on
1064        NT and OS2 or dealing with windows and arguments on Mac.  */
1065     SYSTEM_CLEANUP ();
1066 #endif
1067 
1068     /* This is exit rather than return because apparently that keeps
1069        some tools which check for memory leaks happier.  */
1070     exit (err ? EXIT_FAILURE : 0);
1071 	/* Keep picky/stupid compilers (e.g. Visual C++ 5.0) happy.  */
1072 	return 0;
1073 }
1074 
1075 char *
1076 Make_Date (rawdate)
1077     char *rawdate;
1078 {
1079     time_t unixtime;
1080 
1081     unixtime = get_date (rawdate);
1082     if (unixtime == (time_t) - 1)
1083 	error (1, 0, "Can't parse date/time: %s", rawdate);
1084     return date_from_time_t (unixtime);
1085 }
1086 
1087 /* Convert a time_t to an RCS format date.  This is mainly for the
1088    use of "cvs history", because the CVSROOT/history file contains
1089    time_t format dates; most parts of CVS will want to avoid using
1090    time_t's directly, and instead use RCS_datecmp, Make_Date, &c.
1091    Assuming that the time_t is in GMT (as it generally should be),
1092    then the result will be in GMT too.
1093 
1094    Returns a newly malloc'd string.  */
1095 
1096 char *
1097 date_from_time_t (unixtime)
1098     time_t unixtime;
1099 {
1100     struct tm *ftm;
1101     char date[MAXDATELEN];
1102     char *ret;
1103 
1104     ftm = gmtime (&unixtime);
1105     if (ftm == NULL)
1106 	/* This is a system, like VMS, where the system clock is in local
1107 	   time.  Hopefully using localtime here matches the "zero timezone"
1108 	   hack I added to get_date (get_date of course being the relevant
1109 	   issue for Make_Date, and for history.c too I think).  */
1110 	ftm = localtime (&unixtime);
1111 
1112     (void) sprintf (date, DATEFORM,
1113 		    ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
1114 		    ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
1115 		    ftm->tm_min, ftm->tm_sec);
1116     ret = xstrdup (date);
1117     return (ret);
1118 }
1119 
1120 /* Convert a date to RFC822/1123 format.  This is used in contexts like
1121    dates to send in the protocol; it should not vary based on locale or
1122    other such conventions for users.  We should have another routine which
1123    does that kind of thing.
1124 
1125    The SOURCE date is in our internal RCS format.  DEST should point to
1126    storage managed by the caller, at least MAXDATELEN characters.  */
1127 void
1128 date_to_internet (dest, source)
1129     char *dest;
1130     const char *source;
1131 {
1132     struct tm date;
1133 
1134     date_to_tm (&date, source);
1135     tm_to_internet (dest, &date);
1136 }
1137 
1138 void
1139 date_to_tm (dest, source)
1140     struct tm *dest;
1141     const char *source;
1142 {
1143     if (sscanf (source, SDATEFORM,
1144 		&dest->tm_year, &dest->tm_mon, &dest->tm_mday,
1145 		&dest->tm_hour, &dest->tm_min, &dest->tm_sec)
1146 	    != 6)
1147 	/* Is there a better way to handle errors here?  I made this
1148 	   non-fatal in case we are called from the code which can't
1149 	   deal with fatal errors.  */
1150 	error (0, 0, "internal error: bad date %s", source);
1151 
1152     if (dest->tm_year > 100)
1153 	dest->tm_year -= 1900;
1154 
1155     dest->tm_mon -= 1;
1156 }
1157 
1158 /* Convert a date to RFC822/1123 format.  This is used in contexts like
1159    dates to send in the protocol; it should not vary based on locale or
1160    other such conventions for users.  We should have another routine which
1161    does that kind of thing.
1162 
1163    The SOURCE date is a pointer to a struct tm.  DEST should point to
1164    storage managed by the caller, at least MAXDATELEN characters.  */
1165 void
1166 tm_to_internet (dest, source)
1167     char *dest;
1168     const struct tm *source;
1169 {
1170     /* Just to reiterate, these strings are from RFC822 and do not vary
1171        according to locale.  */
1172     static const char *const month_names[] =
1173       {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1174 	 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
1175 
1176     sprintf (dest, "%d %s %d %02d:%02d:%02d -0000", source->tm_mday,
1177 	     source->tm_mon < 0 || source->tm_mon > 11 ? "???" : month_names[source->tm_mon],
1178 	     source->tm_year + 1900, source->tm_hour, source->tm_min, source->tm_sec);
1179 }
1180 
1181 void
1182 usage (cpp)
1183     register const char *const *cpp;
1184 {
1185     (void) fprintf (stderr, *cpp++, program_name, command_name);
1186     for (; *cpp; cpp++)
1187 	(void) fprintf (stderr, *cpp);
1188     error_exit();
1189 }
1190