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