main.c revision 25866
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 1.4 kit.
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 "cvs.h"
16
17#ifdef HAVE_WINSOCK_H
18#include <winsock.h>
19#else
20extern int gethostname ();
21#endif
22
23char *program_name;
24char *program_path;
25char *command_name;
26
27/* I'd dynamically allocate this, but it seems like gethostname
28   requires a fixed size array.  If I'm remembering the RFCs right,
29   256 should be enough.  */
30#ifndef MAXHOSTNAMELEN
31#define MAXHOSTNAMELEN  256
32#endif
33
34char hostname[MAXHOSTNAMELEN];
35
36int use_editor = TRUE;
37int use_cvsrc = TRUE;
38int cvswrite = !CVSREAD_DFLT;
39int really_quiet = FALSE;
40int quiet = FALSE;
41int trace = FALSE;
42int noexec = FALSE;
43int readonlyfs = FALSE;
44int logoff = FALSE;
45mode_t cvsumask = UMASK_DFLT;
46
47char *CurDir;
48
49/*
50 * Defaults, for the environment variables that are not set
51 */
52char *Rcsbin = RCSBIN_DFLT;
53char *Tmpdir = TMPDIR_DFLT;
54char *Editor = EDITOR_DFLT;
55
56static const struct cmd
57{
58    char *fullname;		/* Full name of the function (e.g. "commit") */
59
60    /* Synonyms for the command, nick1 and nick2.  We supply them
61       mostly for two reasons: (1) CVS has always supported them, and
62       we need to maintain compatibility, (2) if there is a need for a
63       version which is shorter than the fullname, for ease in typing.
64       Synonyms have the disadvantage that people will see "new" and
65       then have to think about it, or look it up, to realize that is
66       the operation they know as "add".  Also, this means that one
67       cannot create a command "cvs new" with a different meaning.  So
68       new synonyms are probably best used sparingly, and where used
69       should be abbreviations of the fullname (preferably consisting
70       of the first 2 or 3 or so letters).
71
72       One thing that some systems do is to recognize any unique
73       abbreviation, for example "annotat" "annota", etc., for
74       "annotate".  The problem with this is that scripts and user
75       habits will expect a certain abbreviation to be unique, and in
76       a future release of CVS it may not be.  So it is better to
77       accept only an explicit list of abbreviations and plan on
78       supporting them in the future as well as now.  */
79
80    char *nick1;
81    char *nick2;
82
83    int (*func) ();		/* Function takes (argc, argv) arguments. */
84} cmds[] =
85
86{
87    { "add",      "ad",       "new",       add },
88    { "admin",    "adm",      "rcs",       admin },
89    { "annotate", "ann",      NULL,        annotate },
90    { "checkout", "co",       "get",       checkout },
91    { "commit",   "ci",       "com",       commit },
92    { "diff",     "di",       "dif",       diff },
93    { "edit",     NULL,	      NULL,	   edit },
94    { "editors",  NULL,       NULL,	   editors },
95    { "export",   "exp",      "ex",        checkout },
96    { "history",  "hi",       "his",       history },
97    { "import",   "im",       "imp",       import },
98    { "init",     NULL,       NULL,        init },
99#ifdef SERVER_SUPPORT
100    { "kserver",  NULL,       NULL,        server }, /* placeholder */
101#endif
102    { "log",      "lo",       "rlog",      cvslog },
103#ifdef AUTH_CLIENT_SUPPORT
104    { "login",    "logon",    "lgn",       login },
105    { "logout",   NULL,       NULL,        logout },
106#ifdef SERVER_SUPPORT
107    { "pserver",  NULL,       NULL,        server }, /* placeholder */
108#endif
109#endif /* AUTH_CLIENT_SUPPORT */
110    { "rdiff",    "patch",    "pa",        patch },
111    { "release",  "re",       "rel",       release },
112    { "remove",   "rm",       "delete",    cvsremove },
113    { "status",   "st",       "stat",      status },
114    { "rtag",     "rt",       "rfreeze",   rtag },
115    { "tag",      "ta",       "freeze",    cvstag },
116    { "unedit",   NULL,	      NULL,	   unedit },
117    { "update",   "up",       "upd",       update },
118    { "watch",    NULL,	      NULL,	   watch },
119    { "watchers", NULL,	      NULL,	   watchers },
120#ifdef SERVER_SUPPORT
121    { "server",   NULL,       NULL,        server },
122#endif
123    { NULL, NULL, NULL, NULL },
124};
125
126static const char *const usg[] =
127{
128    "Usage: %s [cvs-options] command [command-options] [files...]\n",
129    "    Where 'cvs-options' are:\n",
130    "        -H           Displays Usage information for command\n",
131    "        -Q           Cause CVS to be really quiet.\n",
132    "        -q           Cause CVS to be somewhat quiet.\n",
133    "        -r           Make checked-out files read-only\n",
134    "        -w           Make checked-out files read-write (default)\n",
135    "        -l           Turn History logging off\n",
136    "        -n           Do not execute anything that will change the disk\n",
137    "        -t           Show trace of program execution -- Try with -n\n",
138    "        -v           CVS version and copyright\n",
139    "        -b bindir    Find RCS programs in 'bindir'\n",
140    "        -T tmpdir    Use 'tmpdir' for temporary files\n",
141    "        -e editor    Use 'editor' for editing log information\n",
142    "        -d CVS_root  Overrides $CVSROOT as the root of the CVS tree\n",
143    "        -f           Do not use the ~/.cvsrc file\n",
144#ifdef CLIENT_SUPPORT
145    "        -z #         Use compression level '#' for net traffic.\n",
146#ifdef ENCRYPTION
147    "        -x           Encrypt all net traffic.\n",
148#endif
149#endif
150    "        -s VAR=VAL   Set CVS user variable.\n",
151    "\n",
152    "    and where 'command' is: add, admin, etc. (use the --help-commands\n",
153    "    option for a list of commands)\n",
154    NULL,
155};
156
157static const char *const cmd_usage[] =
158{
159    "CVS commands are:\n",
160    "        add          Add a new file/directory to the repository\n",
161    "        admin        Administration front end for rcs\n",
162    "        annotate     Show last revision where each line was modified\n",
163    "        checkout     Checkout sources for editing\n",
164    "        commit       Check files into the repository\n",
165    "        diff         Show differences between revisions\n",
166    "        edit         Get ready to edit a watched file\n",
167    "        editors      See who is editing a watched file\n",
168    "        export       Export sources from CVS, similar to checkout\n",
169    "        history      Show repository access history\n",
170    "        import       Import sources into CVS, using vendor branches\n",
171    "        init         Create a CVS repository if it doesn't exist\n",
172    "        log          Print out history information for files\n",
173#ifdef AUTH_CLIENT_SUPPORT
174    "        login        Prompt for password for authenticating server.\n",
175    "        logout       Removes entry in .cvspass for remote repository.\n",
176#endif /* AUTH_CLIENT_SUPPORT */
177    "        rdiff        Create 'patch' format diffs between releases\n",
178    "        release      Indicate that a Module is no longer in use\n",
179    "        remove       Remove an entry from the repository\n",
180    "        rtag         Add a symbolic tag to a module\n",
181    "        status       Display status information on checked out files\n",
182    "        tag          Add a symbolic tag to checked out version of files\n",
183    "        unedit       Undo an edit command\n",
184    "        update       Bring work tree in sync with repository\n",
185    "        watch        Set watches\n",
186    "        watchers     See who is watching a file\n",
187    "(Use the --help-synonyms option for a list of alternate command names)\n",
188    NULL,
189};
190
191static const char * const*
192cmd_synonyms ()
193{
194    char ** synonyms;
195    char ** line;
196    const struct cmd *c = &cmds[0];
197    int numcmds = 2;		/* two more for title and end */
198
199    while (c->fullname != NULL)
200    {
201	numcmds++;
202	c++;
203    }
204
205    synonyms = (char **) xmalloc(numcmds * sizeof(char *));
206    line = synonyms;
207    *line++ = "CVS command synonyms are:\n";
208    for (c = &cmds[0]; c->fullname != NULL; c++)
209    {
210	if (c->nick1 || c->nick2)
211	{
212	    *line = xmalloc (strlen (c->fullname)
213			     + (c->nick1 != NULL ? strlen (c->nick1) : 0)
214			     + (c->nick2 != NULL ? strlen (c->nick2) : 0)
215			     + 40);
216	    sprintf(*line, "        %-12s %s %s\n", c->fullname,
217		    c->nick1 ? c->nick1 : "",
218		    c->nick2 ? c->nick2 : "");
219	    line++;
220	}
221    }
222    *line = NULL;
223
224    return (const char * const*) synonyms; /* will never be freed */
225}
226
227
228unsigned long int
229lookup_command_attribute (cmd_name)
230     char *cmd_name;
231{
232    unsigned long int ret = 0;
233
234    if (strcmp (cmd_name, "import") != 0)
235    {
236        ret |= CVS_CMD_IGNORE_ADMROOT;
237    }
238
239
240    if ((strcmp (cmd_name, "checkout") != 0) &&
241        (strcmp (cmd_name, "init") != 0) &&
242        (strcmp (cmd_name, "login") != 0) &&
243	(strcmp (cmd_name, "logout") != 0) &&
244        (strcmp (cmd_name, "rdiff") != 0) &&
245        (strcmp (cmd_name, "release") != 0) &&
246        (strcmp (cmd_name, "rtag") != 0))
247    {
248        ret |= CVS_CMD_USES_WORK_DIR;
249    }
250
251
252    /* The following commands do not modify the repository; we
253       conservatively assume that everything else does.  Feel free to
254       add to this list if you are _certain_ something is safe. */
255    if ((strcmp (cmd_name, "checkout") != 0) &&
256        (strcmp (cmd_name, "diff") != 0) &&
257        (strcmp (cmd_name, "update") != 0) &&
258        (strcmp (cmd_name, "history") != 0) &&
259        (strcmp (cmd_name, "editors") != 0) &&
260        (strcmp (cmd_name, "export") != 0) &&
261        (strcmp (cmd_name, "history") != 0) &&
262        (strcmp (cmd_name, "log") != 0) &&
263        (strcmp (cmd_name, "noop") != 0) &&
264        (strcmp (cmd_name, "watchers") != 0) &&
265        (strcmp (cmd_name, "status") != 0))
266    {
267        ret |= CVS_CMD_MODIFIES_REPOSITORY;
268    }
269
270    return ret;
271}
272
273
274static RETSIGTYPE
275main_cleanup (sig)
276    int sig;
277{
278#ifndef DONT_USE_SIGNALS
279    const char *name;
280    char temp[10];
281
282    switch (sig)
283    {
284#ifdef SIGHUP
285    case SIGHUP:
286	name = "hangup";
287	break;
288#endif
289#ifdef SIGINT
290    case SIGINT:
291	name = "interrupt";
292	break;
293#endif
294#ifdef SIGQUIT
295    case SIGQUIT:
296	name = "quit";
297	break;
298#endif
299#ifdef SIGPIPE
300    case SIGPIPE:
301	name = "broken pipe";
302	break;
303#endif
304#ifdef SIGTERM
305    case SIGTERM:
306	name = "termination";
307	break;
308#endif
309    default:
310	/* This case should never be reached, because we list above all
311	   the signals for which we actually establish a signal handler.  */
312	sprintf (temp, "%d", sig);
313	name = temp;
314	break;
315    }
316
317    error (1, 0, "received %s signal", name);
318#endif /* !DONT_USE_SIGNALS */
319}
320
321int
322main (argc, argv)
323    int argc;
324    char **argv;
325{
326    char *CVSroot = CVSROOT_DFLT;
327    extern char *version_string;
328    extern char *config_string;
329    char *cp, *end;
330    const struct cmd *cm;
331    int c, err = 0;
332    int rcsbin_update_env, tmpdir_update_env, cvs_update_env;
333    int help = 0;		/* Has the user asked for help?  This
334				   lets us support the `cvs -H cmd'
335				   convention to give help for cmd. */
336    static struct option long_options[] =
337      {
338        {"help", 0, NULL, 'H'},
339        {"version", 0, NULL, 'v'},
340	{"help-commands", 0, NULL, 1},
341	{"help-synonyms", 0, NULL, 2},
342        {0, 0, 0, 0}
343      };
344    /* `getopt_long' stores the option index here, but right now we
345        don't use it. */
346    int option_index = 0;
347    int need_to_create_root = 0;
348
349#ifdef SYSTEM_INITIALIZE
350    /* Hook for OS-specific behavior, for example socket subsystems on
351       NT and OS2 or dealing with windows and arguments on Mac.  */
352    SYSTEM_INITIALIZE (&argc, &argv);
353#endif
354
355#ifdef HAVE_TZSET
356    /* On systems that have tzset (which is almost all the ones I know
357       of), it's a good idea to call it.  */
358    tzset ();
359#endif
360
361    /*
362     * Just save the last component of the path for error messages
363     */
364    program_path = xstrdup (argv[0]);
365#ifdef ARGV0_NOT_PROGRAM_NAME
366    /* On some systems, e.g. VMS, argv[0] is not the name of the command
367       which the user types to invoke the program.  */
368    program_name = "cvs";
369#else
370    program_name = last_component (argv[0]);
371#endif
372
373    /*
374     * Query the environment variables up-front, so that
375     * they can be overridden by command line arguments
376     */
377    cvs_update_env = 0;
378    rcsbin_update_env = *Rcsbin;	/* RCSBIN_DFLT must be set */
379    if ((cp = getenv (RCSBIN_ENV)) != NULL)
380    {
381	Rcsbin = cp;
382	rcsbin_update_env = 0;		/* it's already there */
383    }
384    tmpdir_update_env = *Tmpdir;	/* TMPDIR_DFLT must be set */
385    if ((cp = getenv (TMPDIR_ENV)) != NULL)
386    {
387	Tmpdir = cp;
388	tmpdir_update_env = 0;		/* it's already there */
389    }
390    if ((cp = getenv (EDITOR1_ENV)) != NULL)
391 	Editor = cp;
392    else if ((cp = getenv (EDITOR2_ENV)) != NULL)
393	Editor = cp;
394    else if ((cp = getenv (EDITOR3_ENV)) != NULL)
395	Editor = cp;
396    if ((cp = getenv (CVSROOT_ENV)) != NULL)
397    {
398	CVSroot = cp;
399	cvs_update_env = 0;		/* it's already there */
400    }
401    if (getenv (CVSREAD_ENV) != NULL)
402	cvswrite = FALSE;
403    if (getenv (CVSREADONLYFS_ENV) != NULL) {
404	readonlyfs = TRUE;
405	logoff = TRUE;
406    }
407
408    /* I'm not sure whether this needs to be 1 instead of 0 anymore.  Using
409       1 used to accomplish what passing "+" as the first character to
410       the option string does, but that reason doesn't exist anymore.  */
411    optind = 1;
412
413
414    /* We have to parse the options twice because else there is no
415       chance to avoid reading the global options from ".cvsrc".  Set
416       opterr to 0 for avoiding error messages about invalid options.
417       */
418    opterr = 0;
419
420    while ((c = getopt_long
421            (argc, argv, "+f", NULL, NULL))
422           != EOF)
423    {
424	if (c == 'f')
425	    use_cvsrc = FALSE;
426    }
427
428    /*
429     * Scan cvsrc file for global options.
430     */
431    if (use_cvsrc)
432	read_cvsrc (&argc, &argv, "cvs");
433
434    optind = 1;
435    opterr = 1;
436
437    while ((c = getopt_long
438            (argc, argv, "+Qqrwtnlvb:T:e:d:Hfz:s:x", long_options, &option_index))
439           != EOF)
440      {
441	switch (c)
442          {
443            case 1:
444	        /* --help-commands */
445                usage (cmd_usage);
446                break;
447            case 2:
448	        /* --help-synonyms */
449                usage (cmd_synonyms());
450                break;
451	    case 'Q':
452		really_quiet = TRUE;
453		/* FALL THROUGH */
454	    case 'q':
455		quiet = TRUE;
456		break;
457	    case 'r':
458		cvswrite = FALSE;
459		break;
460	    case 'w':
461		cvswrite = TRUE;
462		break;
463	    case 't':
464		trace = TRUE;
465		break;
466	    case 'n':
467		noexec = TRUE;
468	    case 'l':			/* Fall through */
469		logoff = TRUE;
470		break;
471	    case 'v':
472		(void) fputs (version_string, stdout);
473		(void) fputs (config_string, stdout);
474		(void) fputs ("\n", stdout);
475		(void) fputs ("Copyright (c) 1993-1994 Brian Berliner\n", stdout);
476		(void) fputs ("Copyright (c) 1993-1994 david d `zoo' zuhn\n", stdout);
477		(void) fputs ("Copyright (c) 1992, Brian Berliner and Jeff Polk\n", stdout);
478		(void) fputs ("Copyright (c) 1989-1992, Brian Berliner\n", stdout);
479		(void) fputs ("\n", stdout);
480		(void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
481		(void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
482		exit (0);
483		break;
484	    case 'b':
485		Rcsbin = optarg;
486		rcsbin_update_env = 1;	/* need to update environment */
487		break;
488	    case 'T':
489		Tmpdir = optarg;
490		tmpdir_update_env = 1;	/* need to update environment */
491		break;
492	    case 'e':
493		Editor = optarg;
494		break;
495	    case 'd':
496		CVSroot = optarg;
497		cvs_update_env = 1;	/* need to update environment */
498		break;
499	    case 'H':
500	        help = 1;
501		break;
502            case 'f':
503		use_cvsrc = FALSE; /* unnecessary, since we've done it above */
504		break;
505	    case 'z':
506#ifdef CLIENT_SUPPORT
507		gzip_level = atoi (optarg);
508		if (gzip_level <= 0 || gzip_level > 9)
509		  error (1, 0,
510			 "gzip compression level must be between 1 and 9");
511#endif
512		/* If no CLIENT_SUPPORT, we just silently ignore the gzip
513		   level, so that users can have it in their .cvsrc and not
514		   cause any trouble.  */
515		break;
516	    case 's':
517		variable_set (optarg);
518		break;
519	    case 'x':
520#ifdef CLIENT_SUPPORT
521	        cvsencrypt = 1;
522#endif /* CLIENT_SUPPORT */
523		/* If no CLIENT_SUPPORT, ignore -x, so that users can
524                   have it in their .cvsrc and not cause any trouble.
525                   If no ENCRYPTION, we still accept -x, but issue an
526                   error if we are being run as a client.  */
527		break;
528	    case '?':
529	    default:
530                usage (usg);
531	}
532    }
533
534    argc -= optind;
535    argv += optind;
536    if (argc < 1)
537	usage (usg);
538
539
540    /* Look up the command name. */
541
542    command_name = argv[0];
543    for (cm = cmds; cm->fullname; cm++)
544    {
545	if (cm->nick1 && !strcmp (command_name, cm->nick1))
546	    break;
547	if (cm->nick2 && !strcmp (command_name, cm->nick2))
548	    break;
549	if (!strcmp (command_name, cm->fullname))
550	    break;
551    }
552
553    if (!cm->fullname)
554	usage (cmd_usage);	        /* no match */
555    else
556	command_name = cm->fullname;	/* Global pointer for later use */
557
558    if (strcmp (argv[0], "rlog") == 0)
559    {
560	error (0, 0, "warning: the rlog command is deprecated");
561	error (0, 0, "use the synonymous log command instead");
562    }
563
564    if (help)
565	argc = -1;		/* some functions only check for this */
566    else
567    {
568	/* The user didn't ask for help, so go ahead and authenticate,
569           set up CVSROOT, and the rest of it. */
570
571	/* The UMASK environment variable isn't handled with the
572	   others above, since we don't want to signal errors if the
573	   user has asked for help.  This won't work if somebody adds
574	   a command-line flag to set the umask, since we'll have to
575	   parse it before we get here. */
576
577	if ((cp = getenv (CVSUMASK_ENV)) != NULL)
578	{
579	    /* FIXME: Should be accepting symbolic as well as numeric mask.  */
580	    cvsumask = strtol (cp, &end, 8) & 0777;
581	    if (*end != '\0')
582		error (1, errno, "invalid umask value in %s (%s)",
583		       CVSUMASK_ENV, cp);
584	}
585
586#if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
587	/* If we are invoked with a single argument "kserver", then we are
588	   running as Kerberos server as root.  Do the authentication as
589	   the very first thing, to minimize the amount of time we are
590	   running as root.  */
591	if (strcmp (command_name, "kserver") == 0)
592	{
593	    kserver_authenticate_connection ();
594
595	    /* Pretend we were invoked as a plain server.  */
596	    command_name = "server";
597	}
598#endif /* HAVE_KERBEROS */
599
600
601#if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT)
602	if (strcmp (command_name, "pserver") == 0)
603	{
604	    /* Gets username and password from client, authenticates, then
605	       switches to run as that user and sends an ACK back to the
606	       client. */
607	    pserver_authenticate_connection ();
608
609	    /* Pretend we were invoked as a plain server.  */
610	    command_name = "server";
611	}
612#endif /* AUTH_SERVER_SUPPORT && SERVER_SUPPORT */
613
614
615	/* Fiddling with CVSROOT doesn't make sense if we're running
616           in server mode, since the client will send the repository
617           directory after the connection is made. */
618
619#ifdef SERVER_SUPPORT
620	if (strcmp (command_name, "server") != 0)
621#endif
622	{
623	    char *CVSADM_Root;
624
625	    /* See if we are able to find a 'better' value for CVSroot
626	       in the CVSADM_ROOT directory. */
627
628	    CVSADM_Root = NULL;
629
630	    /* "cvs import" shouldn't check CVS/Root; in general it
631	       ignores CVS directories and CVS/Root is likely to
632	       specify a different repository than the one we are
633	       importing to.  */
634
635	    if (lookup_command_attribute (command_name)
636                & CVS_CMD_IGNORE_ADMROOT)
637            {
638		CVSADM_Root = Name_Root((char *) NULL, (char *) NULL);
639            }
640
641	    if (CVSADM_Root != NULL)
642	    {
643		if (CVSroot == NULL || !cvs_update_env)
644		{
645		    CVSroot = CVSADM_Root;
646		    cvs_update_env = 1;	/* need to update environment */
647		}
648		/* Let -d override CVS/Root file.  The user might want
649		   to change the access method, use a different server
650		   (if there are two server machines which share the
651		   repository using a networked file system), etc.  */
652		else if (
653#ifdef CLIENT_SUPPORT
654		         !getenv ("CVS_IGNORE_REMOTE_ROOT") &&
655#endif
656			 strcmp (CVSroot, CVSADM_Root) != 0)
657		{
658		    /* Once we have verified that this root is usable,
659		       we will want to write it into CVS/Root.
660
661		       Don't do it for the "login" command, however.
662		       Consider: if the user executes "cvs login" with
663		       the working directory inside an already checked
664		       out module, we'd incorrectly change the
665		       CVS/Root file to reflect the CVSROOT of the
666		       "cvs login" command.  Ahh, the things one
667		       discovers. */
668
669		    if (lookup_command_attribute (command_name)
670                        & CVS_CMD_USES_WORK_DIR)
671                    {
672			need_to_create_root = 1;
673                    }
674
675		}
676	    }
677
678	    /* Now we've reconciled CVSROOT from the command line, the
679               CVS/Root file, and the environment variable.  Do the
680               last sanity checks on the variable. */
681
682	    if (! CVSroot)
683	    {
684		error (0, 0,
685		       "No CVSROOT specified!  Please use the `-d' option");
686		error (1, 0,
687		       "or set the %s environment variable.", CVSROOT_ENV);
688	    }
689
690	    if (! *CVSroot)
691	    {
692		error (0, 0,
693		       "CVSROOT is set but empty!  Make sure that the");
694		error (0, 0,
695		       "specification of CVSROOT is legal, either via the");
696		error (0, 0,
697		       "`-d' option, the %s environment variable, or the",
698		       CVSROOT_ENV);
699		error (1, 0,
700		       "CVS/Root file (if any).");
701	    }
702
703	    /* Now we're 100% sure that we have a valid CVSROOT
704	       variable.  Parse it to see if we're supposed to do
705	       remote accesses or use a special access method. */
706
707	    if (parse_cvsroot (CVSroot))
708		error (1, 0, "Bad CVSROOT.");
709
710	    /*
711	     * Check to see if we can write into the history file.  If not,
712	     * we assume that we can't work in the repository.
713	     * BUT, only if the history file exists.
714	     */
715
716	    if (!client_active)
717	    {
718		char *path;
719		int save_errno;
720
721		path = xmalloc (strlen (CVSroot_directory)
722				+ sizeof (CVSROOTADM)
723				+ 20
724				+ sizeof (CVSROOTADM_HISTORY));
725		(void) sprintf (path, "%s/%s", CVSroot_directory, CVSROOTADM);
726		if (!isaccessible (path, R_OK | X_OK))
727		{
728		    save_errno = errno;
729		    /* If this is "cvs init", the root need not exist yet.  */
730		    if (strcmp (command_name, "init") != 0)
731		    {
732			error (1, save_errno, "%s", path);
733		    }
734		}
735		(void) strcat (path, "/");
736		(void) strcat (path, CVSROOTADM_HISTORY);
737		if (readonlyfs == 0 && isfile (path) && !isaccessible (path, R_OK | W_OK))
738		{
739		    save_errno = errno;
740		    error (0, 0, "Sorry, you don't have read/write access to the history file");
741		    error (1, save_errno, "%s", path);
742		}
743		free (path);
744		parseopts(CVSroot_directory);
745	    }
746
747#ifdef HAVE_PUTENV
748	    /* Update the CVSROOT environment variable if necessary. */
749
750	    if (cvs_update_env)
751	    {
752		char *env;
753		env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot)
754			       + 1 + 1);
755		(void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot);
756		(void) putenv (env);
757		/* do not free env, as putenv has control of it */
758	    }
759#endif
760	}
761
762	/* This is only used for writing into the history file.  For
763	   remote connections, it might be nice to have hostname
764	   and/or remote path, on the other hand I'm not sure whether
765	   it is worth the trouble.  */
766
767#ifdef SERVER_SUPPORT
768	if (strcmp (command_name, "server") == 0)
769	    CurDir = xstrdup ("<remote>");
770	else
771#endif
772	{
773	    CurDir = xgetwd ();
774            if (CurDir == NULL)
775		error (1, errno, "cannot get working directory");
776	}
777
778	if (Tmpdir == NULL || Tmpdir[0] == '\0')
779	    Tmpdir = "/tmp";
780
781#ifdef HAVE_PUTENV
782	/* Now, see if we should update the environment with the
783           Rcsbin value */
784	if (rcsbin_update_env)
785	{
786	    char *env;
787	    env = xmalloc (strlen (RCSBIN_ENV) + strlen (Rcsbin) + 1 + 1);
788	    (void) sprintf (env, "%s=%s", RCSBIN_ENV, Rcsbin);
789	    (void) putenv (env);
790	    /* do not free env, as putenv has control of it */
791	}
792	if (tmpdir_update_env)
793	{
794	    char *env;
795	    env = xmalloc (strlen (TMPDIR_ENV) + strlen (Tmpdir) + 1 + 1);
796	    (void) sprintf (env, "%s=%s", TMPDIR_ENV, Tmpdir);
797	    (void) putenv (env);
798	    /* do not free env, as putenv has control of it */
799	}
800	{
801	    char *env;
802	    env = xmalloc (sizeof "CVS_PID=" + 32); /* XXX pid < 10^32 */
803	    (void) sprintf (env, "CVS_PID=%ld", (long) getpid ());
804	    (void) putenv (env);
805	}
806#endif
807
808	/*
809	 * If Rcsbin is set to something, make sure it is terminated with
810	 * a slash character.  If not, add one.
811	 */
812	if (*Rcsbin)
813	{
814	    int len = strlen (Rcsbin);
815	    char *rcsbin;
816
817	    if (Rcsbin[len - 1] != '/')
818	    {
819		rcsbin = Rcsbin;
820		Rcsbin = xmalloc (len + 2);	/* one for '/', one for NULL */
821		(void) strcpy (Rcsbin, rcsbin);
822		(void) strcat (Rcsbin, "/");
823	    }
824	}
825
826#ifndef DONT_USE_SIGNALS
827	/* make sure we clean up on error */
828#ifdef SIGHUP
829	(void) SIG_register (SIGHUP, main_cleanup);
830	(void) SIG_register (SIGHUP, Lock_Cleanup);
831#endif
832#ifdef SIGINT
833	(void) SIG_register (SIGINT, main_cleanup);
834	(void) SIG_register (SIGINT, Lock_Cleanup);
835#endif
836#ifdef SIGQUIT
837	(void) SIG_register (SIGQUIT, main_cleanup);
838	(void) SIG_register (SIGQUIT, Lock_Cleanup);
839#endif
840#ifdef SIGPIPE
841	(void) SIG_register (SIGPIPE, main_cleanup);
842	(void) SIG_register (SIGPIPE, Lock_Cleanup);
843#endif
844#ifdef SIGTERM
845	(void) SIG_register (SIGTERM, main_cleanup);
846	(void) SIG_register (SIGTERM, Lock_Cleanup);
847#endif
848#endif /* !DONT_USE_SIGNALS */
849
850	gethostname(hostname, sizeof (hostname));
851
852#ifdef KLUDGE_FOR_WNT_TESTSUITE
853	/* Probably the need for this will go away at some point once
854	   we call fflush enough places (e.g. fflush (stdout) in
855	   cvs_outerr).  */
856	(void) setvbuf (stdout, (char *) NULL, _IONBF, 0);
857	(void) setvbuf (stderr, (char *) NULL, _IONBF, 0);
858#endif /* KLUDGE_FOR_WNT_TESTSUITE */
859
860	if (use_cvsrc)
861	    read_cvsrc (&argc, &argv, command_name);
862
863    } /* end of stuff that gets done if the user DOESN'T ask for help */
864
865    err = (*(cm->func)) (argc, argv);
866
867    if (need_to_create_root)
868    {
869	/* Update the CVS/Root file.  We might want to do this in
870	   all directories that we recurse into, but currently we
871	   don't.  */
872	Create_Root (NULL, CVSroot);
873    }
874
875    Lock_Cleanup ();
876
877#ifdef SYSTEM_CLEANUP
878    /* Hook for OS-specific behavior, for example socket subsystems on
879       NT and OS2 or dealing with windows and arguments on Mac.  */
880    SYSTEM_CLEANUP ();
881#endif
882
883    /* This is exit rather than return because apparently that keeps
884       some tools which check for memory leaks happier.  */
885    exit (err ? EXIT_FAILURE : 0);
886}
887
888char *
889Make_Date (rawdate)
890    char *rawdate;
891{
892    struct tm *ftm;
893    time_t unixtime;
894    char date[MAXDATELEN];
895    char *ret;
896
897    unixtime = get_date (rawdate, (struct timeb *) NULL);
898    if (unixtime == (time_t) - 1)
899	error (1, 0, "Can't parse date/time: %s", rawdate);
900#ifdef HAVE_RCS5
901    ftm = gmtime (&unixtime);
902#else
903    ftm = localtime (&unixtime);
904#endif
905    (void) sprintf (date, DATEFORM,
906		    ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
907		    ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
908		    ftm->tm_min, ftm->tm_sec);
909    ret = xstrdup (date);
910    return (ret);
911}
912
913void
914usage (cpp)
915    register const char *const *cpp;
916{
917    (void) fprintf (stderr, *cpp++, program_name, command_name);
918    for (; *cpp; cpp++)
919	(void) fprintf (stderr, *cpp);
920    error_exit ();
921}
922
923void
924parseopts(root)
925    const char *root;
926{
927    char path[PATH_MAX];
928    int save_errno;
929    char buf[1024];
930    const char *p;
931    char *q;
932    FILE *fp;
933
934    if (root == NULL) {
935	printf("no CVSROOT in parseopts\n");
936	return;
937    }
938    p = strchr (root, ':');
939    if (p)
940	p++;
941    else
942	p = root;
943    if (p == NULL) {
944	printf("mangled CVSROOT in parseopts\n");
945	return;
946    }
947    (void) sprintf (path, "%s/%s/%s", p, CVSROOTADM, CVSROOTADM_OPTIONS);
948    if ((fp = fopen(path, "r")) != NULL) {
949	while (fgets(buf, sizeof buf, fp) != NULL) {
950	    if (buf[0] == '#')
951		continue;
952	    q = strrchr(buf, '\n');
953	    if (q)
954		*q = '\0';
955
956	    if (!strncmp(buf, "tag=", 4)) {
957		char *what;
958		char *rcs_localid;
959
960		rcs_localid = buf + 4;
961		RCS_setlocalid(rcs_localid);
962		what = malloc(sizeof("RCSLOCALID") + 2 + strlen(rcs_localid));
963		if (what == NULL) {
964			printf("no memory for local tag\n");
965			return;
966		}
967		sprintf(what, "RCSLOCALID=%s", rcs_localid);
968		putenv(what);
969	    }
970	    if (!strncmp(buf, "tagexpand=", 10)) {
971		char *what;
972		char *rcs_incexc;
973
974		rcs_incexc = buf + 10;
975		RCS_setincexc(rcs_incexc);
976		what = malloc(sizeof("RCSINCEXC") + 2 + strlen(rcs_incexc));
977		if (what == NULL) {
978			printf("no memory for tag expand mode\n");
979			return;
980		}
981		sprintf(what, "RCSINCEXC=%s", rcs_incexc);
982		putenv(what);
983	    }
984	    /*
985	     * OpenBSD has a "umask=" and "dlimit=" command, we silently
986	     * ignore them here since they are not much use to us.  cvsumask
987	     * defaults to 002 already, and the dlimit (data size limit)
988	     * should really be handled elsewhere.
989	     */
990	}
991	fclose(fp);
992    }
993}
994