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