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