xref: /openbsd-src/gnu/usr.bin/cvs/src/main.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
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     if (pledge("stdio rpath wpath cpath fattr getpw proc exec inet dns tty", NULL) == -1)
454 	    error (1, errno, "pledge init");
455 
456     /*
457      * Query the environment variables up-front, so that
458      * they can be overridden by command line arguments
459      */
460     cvs_update_env = 0;
461     tmpdir_update_env = *Tmpdir;	/* TMPDIR_DFLT must be set */
462     if ((cp = getenv (TMPDIR_ENV)) != NULL)
463     {
464 	Tmpdir = cp;
465 	tmpdir_update_env = 0;		/* it's already there */
466     }
467     if ((cp = getenv (EDITOR1_ENV)) != NULL)
468  	Editor = cp;
469     else if ((cp = getenv (EDITOR2_ENV)) != NULL)
470 	Editor = cp;
471     else if ((cp = getenv (EDITOR3_ENV)) != NULL)
472 	Editor = cp;
473     if ((cp = getenv (CVSROOT_ENV)) != NULL)
474     {
475 	CVSroot = cp;
476 	cvs_update_env = 0;		/* it's already there */
477     }
478     if (getenv (CVSREAD_ENV) != NULL)
479 	cvswrite = 0;
480     if (getenv (CVSREADONLYFS_ENV)) {
481 	readonlyfs = 1;
482 	logoff = 1;
483     }
484 
485     /* Set this to 0 to force getopt initialization.  getopt() sets
486        this to 1 internally.  */
487     optind = 0;
488 
489     /* We have to parse the options twice because else there is no
490        chance to avoid reading the global options from ".cvsrc".  Set
491        opterr to 0 for avoiding error messages about invalid options.
492        */
493     opterr = 0;
494 
495     while ((c = getopt_long
496             (argc, argv, short_options, long_options, &option_index))
497            != EOF)
498     {
499 	if (c == 'f')
500 	    use_cvsrc = 0;
501     }
502 
503     /*
504      * Scan cvsrc file for global options.
505      */
506     if (use_cvsrc)
507 	read_cvsrc (&argc, &argv, "cvs");
508 
509     optind = 0;
510     opterr = 1;
511 
512     while ((c = getopt_long
513             (argc, argv, short_options, long_options, &option_index))
514            != EOF)
515     {
516 	switch (c)
517 	{
518             case 1:
519 	        /* --help-commands */
520                 usage (cmd_usage);
521                 break;
522             case 2:
523 	        /* --help-synonyms */
524                 usage (cmd_synonyms());
525                 break;
526 	    case 4:
527 		/* --help-options */
528 		usage (opt_usage);
529 		break;
530 	    case 3:
531 		/* --allow-root */
532 		root_allow_add (optarg);
533 		break;
534 	    case 'Q':
535 		really_quiet = 1;
536 		/* FALL THROUGH */
537 	    case 'q':
538 		quiet = 1;
539 		break;
540 	    case 'r':
541 		cvswrite = 0;
542 		break;
543 	    case 'w':
544 		cvswrite = 1;
545 		break;
546 	    case 't':
547 		trace = 1;
548 		break;
549 	    case 'n':
550 		noexec = 1;
551 	    case 'l':			/* Fall through */
552 		logoff = 1;
553 		break;
554 	    case 'R':
555 		logoff = 1;
556 		readonlyfs = 1;
557 		break;
558 	    case 'v':
559 		(void) fputs ("\n", stdout);
560 		version (0, (char **) NULL);
561 		(void) fputs ("\n", stdout);
562 		(void) fputs ("\
563 Copyright (c) 1989-2001 Brian Berliner, david d `zoo' zuhn, \n\
564                         Jeff Polk, and other authors\n", stdout);
565 		(void) fputs ("\n", stdout);
566 		(void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
567 		(void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
568 		(void) fputs ("\n", stdout);
569 
570 		(void) fputs ("Specify the --help option for further information about CVS\n", stdout);
571 
572 		exit (0);
573 		break;
574 	    case 'b':
575 		/* This option used to specify the directory for RCS
576 		   executables.  But since we don't run them any more,
577 		   this is a noop.  Silently ignore it so that .cvsrc
578 		   and scripts and inetd.conf and such can work with
579 		   either new or old CVS.  */
580 		break;
581 	    case 'T':
582 		Tmpdir = xstrdup (optarg);
583 		free_Tmpdir = 1;
584 		tmpdir_update_env = 1;	/* need to update environment */
585 		break;
586 	    case 'e':
587 		Editor = xstrdup (optarg);
588 		free_Editor = 1;
589 		break;
590 	    case 'd':
591 		if (CVSroot_cmdline != NULL)
592 		    free (CVSroot_cmdline);
593 		CVSroot_cmdline = xstrdup (optarg);
594 		if (free_CVSroot)
595 		    free (CVSroot);
596 		CVSroot = xstrdup (optarg);
597 		free_CVSroot = 1;
598 		cvs_update_env = 1;	/* need to update environment */
599 		break;
600 	    case 'H':
601 	        help = 1;
602 		break;
603             case 'f':
604 		use_cvsrc = 0; /* unnecessary, since we've done it above */
605 		break;
606 	    case 'z':
607 #ifdef CLIENT_SUPPORT
608 		gzip_level = atoi (optarg);
609 		if (gzip_level < 0 || gzip_level > 9)
610 		  error (1, 0,
611 			 "gzip compression level must be between 0 and 9");
612 #endif
613 		/* If no CLIENT_SUPPORT, we just silently ignore the gzip
614 		   level, so that users can have it in their .cvsrc and not
615 		   cause any trouble.  */
616 		break;
617 	    case 's':
618 		variable_set (optarg);
619 		break;
620 	    case 'x':
621 #ifdef CLIENT_SUPPORT
622 	        cvsencrypt = 1;
623 #endif /* CLIENT_SUPPORT */
624 		/* If no CLIENT_SUPPORT, ignore -x, so that users can
625                    have it in their .cvsrc and not cause any trouble.
626                    If no ENCRYPTION, we still accept -x, but issue an
627                    error if we are being run as a client.  */
628 		break;
629 	    case 'a':
630 #ifdef CLIENT_SUPPORT
631 		cvsauthenticate = 1;
632 #endif
633 		/* If no CLIENT_SUPPORT, ignore -a, so that users can
634                    have it in their .cvsrc and not cause any trouble.
635                    We will issue an error later if stream
636                    authentication is not supported.  */
637 		break;
638 	    case '?':
639 	    default:
640                 usage (usg);
641 	}
642     }
643 
644     argc -= optind;
645     argv += optind;
646     if (argc < 1)
647 	usage (usg);
648 
649     /* Generate the cvs global session ID */
650 
651     {
652 	int i = 0;
653 	u_int32_t c;
654 	global_session_id = xmalloc(17);
655 
656 	while (i <= 16) {
657 	    c = arc4random_uniform(75) + 48;
658 	    if ((c >= 48 && c <= 57) || (c >= 65 && c <= 90) ||
659 	        (c >= 97 && c <= 122)) {
660 		global_session_id[i] = c;
661 		i++;
662 	    }
663 	}
664 	global_session_id[16] = '\0';
665     }
666 
667     if (trace)
668 	fprintf (stderr, "main: Session ID is %s", global_session_id);
669 
670 
671     /* Look up the command name. */
672 
673     command_name = argv[0];
674     for (cm = cmds; cm->fullname; cm++)
675     {
676 	if (cm->nick1 && !strcmp (command_name, cm->nick1))
677 	    break;
678 	if (cm->nick2 && !strcmp (command_name, cm->nick2))
679 	    break;
680 	if (!strcmp (command_name, cm->fullname))
681 	    break;
682     }
683 
684     if (!cm->fullname)
685     {
686 	fprintf (stderr, "Unknown command: `%s'\n\n", command_name);
687 	usage (cmd_usage);
688     }
689     else
690 	command_name = cm->fullname;	/* Global pointer for later use */
691 
692     if (help)
693     {
694 	argc = -1;		/* some functions only check for this */
695 	err = (*(cm->func)) (argc, argv);
696     }
697     else
698     {
699 	/* The user didn't ask for help, so go ahead and authenticate,
700            set up CVSROOT, and the rest of it. */
701 
702 	/* The UMASK environment variable isn't handled with the
703 	   others above, since we don't want to signal errors if the
704 	   user has asked for help.  This won't work if somebody adds
705 	   a command-line flag to set the umask, since we'll have to
706 	   parse it before we get here. */
707 
708 	if ((cp = getenv (CVSUMASK_ENV)) != NULL)
709 	{
710 	    /* FIXME: Should be accepting symbolic as well as numeric mask.  */
711 	    cvsumask = strtol (cp, &end, 8) & 0777;
712 	    if (*end != '\0')
713 		error (1, errno, "invalid umask value in %s (%s)",
714 		       CVSUMASK_ENV, cp);
715 	}
716 
717 #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
718 	/* If we are invoked with a single argument "kserver", then we are
719 	   running as Kerberos server as root.  Do the authentication as
720 	   the very first thing, to minimize the amount of time we are
721 	   running as root.  */
722 	if (strcmp (command_name, "kserver") == 0)
723 	{
724 	    kserver_authenticate_connection ();
725 
726 	    /* Pretend we were invoked as a plain server.  */
727 	    command_name = "server";
728 	}
729 #endif /* HAVE_KERBEROS */
730 
731 
732 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
733 	if (strcmp (command_name, "pserver") == 0)
734 	{
735 	    /* The reason that --allow-root is not a command option
736 	       is mainly the comment in server() about how argc,argv
737 	       might be from .cvsrc.  I'm not sure about that, and
738 	       I'm not sure it is only true of command options, but
739 	       it seems easier to make it a global option.  */
740 
741 	    /* Gets username and password from client, authenticates, then
742 	       switches to run as that user and sends an ACK back to the
743 	       client. */
744 	    pserver_authenticate_connection ();
745 
746 	    /* Pretend we were invoked as a plain server.  */
747 	    command_name = "server";
748 	}
749 #endif /* (AUTH_SERVER_SUPPORT || HAVE_GSSAPI) && SERVER_SUPPORT */
750 
751 #ifdef SERVER_SUPPORT
752 	server_active = strcmp (command_name, "server") == 0;
753 	if (server_active)
754 	{
755 	    if (pledge("stdio rpath wpath cpath fattr getpw proc exec", NULL) == -1)
756 	        error (1, errno, "pledge");
757 
758 	}
759 #endif
760 
761 	/* This is only used for writing into the history file.  For
762 	   remote connections, it might be nice to have hostname
763 	   and/or remote path, on the other hand I'm not sure whether
764 	   it is worth the trouble.  */
765 
766 #ifdef SERVER_SUPPORT
767 	if (server_active)
768 	    CurDir = xstrdup ("<remote>");
769 	else
770 #endif
771 	{
772 	    CurDir = xgetwd ();
773             if (CurDir == NULL)
774 		error (1, errno, "cannot get working directory");
775 	}
776 
777 	if (Tmpdir == NULL || Tmpdir[0] == '\0')
778 	    Tmpdir = "/tmp";
779 
780 #ifdef HAVE_PUTENV
781 	if (tmpdir_update_env)
782 	{
783 	    char *env;
784 	    env = xmalloc (strlen (TMPDIR_ENV) + strlen (Tmpdir) + 1 + 1);
785 	    (void) sprintf (env, "%s=%s", TMPDIR_ENV, Tmpdir);
786 	    (void) putenv (env);
787 	    /* do not free env, as putenv has control of it */
788 	}
789 #endif
790 
791 #ifndef DONT_USE_SIGNALS
792 	/* make sure we clean up on error */
793 #ifdef SIGABRT
794 	(void) SIG_register (SIGABRT, main_cleanup);
795 #endif
796 #ifdef SIGHUP
797 	(void) SIG_register (SIGHUP, main_cleanup);
798 #endif
799 #ifdef SIGINT
800 	(void) SIG_register (SIGINT, main_cleanup);
801 #endif
802 #ifdef SIGQUIT
803 	(void) SIG_register (SIGQUIT, main_cleanup);
804 #endif
805 #ifdef SIGPIPE
806 	(void) SIG_register (SIGPIPE, main_cleanup);
807 #endif
808 #ifdef SIGTERM
809 	(void) SIG_register (SIGTERM, main_cleanup);
810 #endif
811 #endif /* !DONT_USE_SIGNALS */
812 
813 	gethostname(hostname, sizeof (hostname));
814 
815 #ifdef KLUDGE_FOR_WNT_TESTSUITE
816 	/* Probably the need for this will go away at some point once
817 	   we call fflush enough places (e.g. fflush (stdout) in
818 	   cvs_outerr).  */
819 	(void) setvbuf (stdout, (char *) NULL, _IONBF, 0);
820 	(void) setvbuf (stderr, (char *) NULL, _IONBF, 0);
821 #endif /* KLUDGE_FOR_WNT_TESTSUITE */
822 
823 	if (use_cvsrc)
824 	    read_cvsrc (&argc, &argv, command_name);
825 
826 #ifdef SERVER_SUPPORT
827 	/* Fiddling with CVSROOT doesn't make sense if we're running
828 	       in server mode, since the client will send the repository
829 	       directory after the connection is made. */
830 
831 	if (!server_active)
832 #endif
833 	{
834 	    char *CVSADM_Root;
835 
836 	    /* See if we are able to find a 'better' value for CVSroot
837 	       in the CVSADM_ROOT directory. */
838 
839 	    CVSADM_Root = NULL;
840 
841 	    /* "cvs import" shouldn't check CVS/Root; in general it
842 	       ignores CVS directories and CVS/Root is likely to
843 	       specify a different repository than the one we are
844 	       importing to.  */
845 
846 	    if (!(cm->attr & CVS_CMD_IGNORE_ADMROOT)
847 
848 		/* -d overrides CVS/Root, so don't give an error if the
849 		   latter points to a nonexistent repository.  */
850 		&& CVSroot_cmdline == NULL)
851 	    {
852 		CVSADM_Root = Name_Root((char *) NULL, (char *) NULL);
853 	    }
854 
855 	    if (CVSADM_Root != NULL)
856 	    {
857 		if (CVSroot == NULL || !cvs_update_env)
858 		{
859 		    CVSroot = CVSADM_Root;
860 		    cvs_update_env = 1;	/* need to update environment */
861 		}
862 	    }
863 
864 	    /* Now we've reconciled CVSROOT from the command line, the
865 	       CVS/Root file, and the environment variable.  Do the
866 	       last sanity checks on the variable. */
867 
868 	    if (! CVSroot)
869 	    {
870 		error (0, 0,
871 		       "No CVSROOT specified!  Please use the `-d' option");
872 		error (1, 0,
873 		       "or set the %s environment variable.", CVSROOT_ENV);
874 	    }
875 
876 	    if (! *CVSroot)
877 	    {
878 		error (0, 0,
879 		       "CVSROOT is set but empty!  Make sure that the");
880 		error (0, 0,
881 		       "specification of CVSROOT is legal, either via the");
882 		error (0, 0,
883 		       "`-d' option, the %s environment variable, or the",
884 		       CVSROOT_ENV);
885 		error (1, 0,
886 		       "CVS/Root file (if any).");
887 	    }
888 	}
889 
890 	/* Here begins the big loop over unique cvsroot values.  We
891            need to call do_recursion once for each unique value found
892            in CVS/Root.  Prime the list with the current value. */
893 
894 	/* Create the list. */
895 	assert (root_directories == NULL);
896 	root_directories = getlist ();
897 
898 	/* Prime it. */
899 	if (CVSroot != NULL)
900 	{
901 	    Node *n;
902 	    n = getnode ();
903 	    n->type = NT_UNKNOWN;
904 	    n->key = xstrdup (CVSroot);
905 	    n->data = NULL;
906 
907 	    if (addnode (root_directories, n))
908 		error (1, 0, "cannot add initial CVSROOT %s", n->key);
909 	}
910 
911 	assert (current_root == NULL);
912 
913 	/* If we're running the server, we want to execute this main
914 	   loop once and only once (we won't be serving multiple roots
915 	   from this connection, so there's no need to do it more than
916 	   once).  To get out of the loop, we perform a "break" at the
917 	   end of things.  */
918 
919 	while (
920 #ifdef SERVER_SUPPORT
921 	       server_active ||
922 #endif
923 	       walklist (root_directories, set_root_directory, NULL)
924 	       )
925 	{
926 #ifdef SERVER_SUPPORT
927 	    /* Fiddling with CVSROOT doesn't make sense if we're running
928 	       in server mode, since the client will send the repository
929 	       directory after the connection is made. */
930 
931 	    if (!server_active)
932 #endif
933 	    {
934 		/* Now we're 100% sure that we have a valid CVSROOT
935 		   variable.  Parse it to see if we're supposed to do
936 		   remote accesses or use a special access method. */
937 
938 		if (current_parsed_root != NULL)
939 		    free_cvsroot_t (current_parsed_root);
940 		if ((current_parsed_root = parse_cvsroot (current_root)) == NULL)
941 		    error (1, 0, "Bad CVSROOT.");
942 
943 		if (current_parsed_root->method == pserver_method) {
944 			if (strcmp(command_name, "login") == 0) {
945 				if (pledge("stdio rpath wpath cpath fattr getpw inet dns tty", NULL) == -1)
946 					error (1, errno, "pledge");
947 			} else {
948 				if (pledge("stdio rpath wpath cpath fattr getpw proc exec inet dns", NULL) == -1)
949 					error (1, errno, "pledge");
950 			}
951 		} else {
952 			if (pledge("stdio rpath wpath cpath fattr getpw proc exec", NULL) == -1)
953 				error (1, errno, "pledge");
954 		}
955 
956 		if (trace)
957 		    fprintf (stderr, "%s-> main loop with CVSROOT=%s\n",
958 			   CLIENT_SERVER_STR, current_root);
959 
960 		/*
961 		 * Check to see if the repository exists.
962 		 */
963 #ifdef CLIENT_SUPPORT
964 		if (!current_parsed_root->isremote)
965 #endif	/* CLIENT_SUPPORT */
966 		{
967 		    char *path;
968 		    int save_errno;
969 
970 		    path = xmalloc (strlen (current_parsed_root->directory)
971 				    + sizeof (CVSROOTADM)
972 				    + 20);
973 		    (void) sprintf (path, "%s/%s", current_parsed_root->directory, CVSROOTADM);
974 		    if (readonlyfs == 0 && !isaccessible (path, R_OK | X_OK))
975 		    {
976 			save_errno = errno;
977 			/* If this is "cvs init", the root need not exist yet.  */
978 			if (strcmp (command_name, "init") != 0)
979 			{
980 			    error (1, save_errno, "%s", path);
981 			}
982 		    }
983 		    free (path);
984 		}
985 
986 #ifdef HAVE_PUTENV
987 		/* Update the CVSROOT environment variable if necessary. */
988 		/* FIXME (njc): should we always set this with the CVSROOT from the command line? */
989 		if (cvs_update_env)
990 		{
991 		    static char *prev;
992 		    char *env;
993 		    env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot)
994 				   + 1 + 1);
995 		    (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot);
996 		    (void) putenv (env);
997 		    /* do not free env yet, as putenv has control of it */
998 		    /* but do free the previous value, if any */
999 		    if (prev != NULL)
1000 			free (prev);
1001 		    prev = env;
1002 		}
1003 #endif
1004 	    }
1005 
1006 	    /* Parse the CVSROOT/config file, but only for local.  For the
1007 	       server, we parse it after we know $CVSROOT.  For the
1008 	       client, it doesn't get parsed at all, obviously.  The
1009 	       presence of the parse_config call here is not mean to
1010 	       predetermine whether CVSROOT/config overrides things from
1011 	       read_cvsrc and other such places or vice versa.  That sort
1012 	       of thing probably needs more thought.  */
1013 	    if (1
1014 #ifdef SERVER_SUPPORT
1015 		&& !server_active
1016 #endif
1017 #ifdef CLIENT_SUPPORT
1018 		&& !current_parsed_root->isremote
1019 #endif
1020 		)
1021 	    {
1022 		/* If there was an error parsing the config file, parse_config
1023 		   already printed an error.  We keep going.  Why?  Because
1024 		   if we didn't, then there would be no way to check in a new
1025 		   CVSROOT/config file to fix the broken one!  */
1026 		parse_config (current_parsed_root->directory);
1027 	    }
1028 
1029 #ifdef CLIENT_SUPPORT
1030 	    /* Need to check for current_parsed_root != NULL here since
1031 	     * we could still be in server mode before the server function
1032 	     * gets called below and sets the root
1033 	     */
1034 	    if (current_parsed_root != NULL && current_parsed_root->isremote)
1035 	    {
1036 		/* Create a new list for directory names that we've
1037 		   sent to the server. */
1038 		if (dirs_sent_to_server != NULL)
1039 		    dellist (&dirs_sent_to_server);
1040 		dirs_sent_to_server = getlist ();
1041 	    }
1042 #endif
1043 
1044 	    err = (*(cm->func)) (argc, argv);
1045 
1046 	    /* Mark this root directory as done.  When the server is
1047                active, current_root will be NULL -- don't try and
1048                remove it from the list. */
1049 
1050 	    if (current_root != NULL)
1051 	    {
1052 		Node *n = findnode (root_directories, current_root);
1053 		assert (n != NULL);
1054 		n->data = (void *) 1;
1055 		current_root = NULL;
1056 	    }
1057 
1058 #if 0
1059 	    /* This will not work yet, since it tries to free (void *) 1. */
1060 	    dellist (&root_directories);
1061 #endif
1062 
1063 #ifdef SERVER_SUPPORT
1064 	    if (server_active)
1065 	      break;
1066 #endif
1067 	} /* end of loop for cvsroot values */
1068 
1069     } /* end of stuff that gets done if the user DOESN'T ask for help */
1070 
1071     Lock_Cleanup ();
1072 
1073     free (program_path);
1074     if (CVSroot_cmdline != NULL)
1075 	free (CVSroot_cmdline);
1076     if (free_CVSroot)
1077 	free (CVSroot);
1078     if (free_Editor)
1079 	free (Editor);
1080     if (free_Tmpdir)
1081 	free (Tmpdir);
1082     root_allow_free ();
1083 
1084 #ifdef SYSTEM_CLEANUP
1085     /* Hook for OS-specific behavior, for example socket subsystems on
1086        NT and OS2 or dealing with windows and arguments on Mac.  */
1087     SYSTEM_CLEANUP ();
1088 #endif
1089 
1090     /* This is exit rather than return because apparently that keeps
1091        some tools which check for memory leaks happier.  */
1092     exit (err ? EXIT_FAILURE : 0);
1093 	/* Keep picky/stupid compilers (e.g. Visual C++ 5.0) happy.  */
1094 	return 0;
1095 }
1096 
1097 char *
1098 Make_Date (rawdate)
1099     char *rawdate;
1100 {
1101     time_t unixtime;
1102 
1103     unixtime = get_date (rawdate);
1104     if (unixtime == (time_t) - 1)
1105 	error (1, 0, "Can't parse date/time: %s", rawdate);
1106     return date_from_time_t (unixtime);
1107 }
1108 
1109 /* Convert a time_t to an RCS format date.  This is mainly for the
1110    use of "cvs history", because the CVSROOT/history file contains
1111    time_t format dates; most parts of CVS will want to avoid using
1112    time_t's directly, and instead use RCS_datecmp, Make_Date, &c.
1113    Assuming that the time_t is in GMT (as it generally should be),
1114    then the result will be in GMT too.
1115 
1116    Returns a newly malloc'd string.  */
1117 
1118 char *
1119 date_from_time_t (unixtime)
1120     time_t unixtime;
1121 {
1122     struct tm *ftm;
1123     char date[MAXDATELEN];
1124     char *ret;
1125 
1126     ftm = gmtime (&unixtime);
1127     if (ftm == NULL)
1128 	/* This is a system, like VMS, where the system clock is in local
1129 	   time.  Hopefully using localtime here matches the "zero timezone"
1130 	   hack I added to get_date (get_date of course being the relevant
1131 	   issue for Make_Date, and for history.c too I think).  */
1132 	ftm = localtime (&unixtime);
1133 
1134     (void) sprintf (date, DATEFORM,
1135 		    ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
1136 		    ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
1137 		    ftm->tm_min, ftm->tm_sec);
1138     ret = xstrdup (date);
1139     return (ret);
1140 }
1141 
1142 /* Convert a date to RFC822/1123 format.  This is used in contexts like
1143    dates to send in the protocol; it should not vary based on locale or
1144    other such conventions for users.  We should have another routine which
1145    does that kind of thing.
1146 
1147    The SOURCE date is in our internal RCS format.  DEST should point to
1148    storage managed by the caller, at least MAXDATELEN characters.  */
1149 void
1150 date_to_internet (dest, source)
1151     char *dest;
1152     const char *source;
1153 {
1154     struct tm date;
1155 
1156     date_to_tm (&date, source);
1157     tm_to_internet (dest, &date);
1158 }
1159 
1160 void
1161 date_to_tm (dest, source)
1162     struct tm *dest;
1163     const char *source;
1164 {
1165     if (sscanf (source, SDATEFORM,
1166 		&dest->tm_year, &dest->tm_mon, &dest->tm_mday,
1167 		&dest->tm_hour, &dest->tm_min, &dest->tm_sec)
1168 	    != 6)
1169 	/* Is there a better way to handle errors here?  I made this
1170 	   non-fatal in case we are called from the code which can't
1171 	   deal with fatal errors.  */
1172 	error (0, 0, "internal error: bad date %s", source);
1173 
1174     if (dest->tm_year > 100)
1175 	dest->tm_year -= 1900;
1176 
1177     dest->tm_mon -= 1;
1178 }
1179 
1180 /* Convert a date to RFC822/1123 format.  This is used in contexts like
1181    dates to send in the protocol; it should not vary based on locale or
1182    other such conventions for users.  We should have another routine which
1183    does that kind of thing.
1184 
1185    The SOURCE date is a pointer to a struct tm.  DEST should point to
1186    storage managed by the caller, at least MAXDATELEN characters.  */
1187 void
1188 tm_to_internet (dest, source)
1189     char *dest;
1190     const struct tm *source;
1191 {
1192     /* Just to reiterate, these strings are from RFC822 and do not vary
1193        according to locale.  */
1194     static const char *const month_names[] =
1195       {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1196 	 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
1197 
1198     sprintf (dest, "%d %s %d %02d:%02d:%02d -0000", source->tm_mday,
1199 	     source->tm_mon < 0 || source->tm_mon > 11 ? "???" : month_names[source->tm_mon],
1200 	     source->tm_year + 1900, source->tm_hour, source->tm_min, source->tm_sec);
1201 }
1202 
1203 void
1204 usage (cpp)
1205     register const char *const *cpp;
1206 {
1207     (void) fprintf (stderr, *cpp++, program_name, command_name);
1208     for (; *cpp; cpp++)
1209 	(void) fprintf (stderr, *cpp);
1210     error_exit();
1211 }
1212