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