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