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: stable/9/contrib/cvs/src/main.c 177398 2008-03-19 15:00:43Z obrien $
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
28 extern int gethostname ();
29 #endif
30 
31 const char *program_name;
32 const char *program_path;
33 const 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 
42 char hostname[MAXHOSTNAMELEN];
43 
44 int use_editor = 1;
45 int use_cvsrc = 1;
46 int cvswrite = !CVSREAD_DFLT;
47 int really_quiet = 0;
48 int quiet = 0;
49 int trace = 0;
50 int noexec = 0;
51 int readonlyfs = 0;
52 int require_real_user = 0;
53 int 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  */
59 int 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). */
64 int top_level_admin = 0;
65 
66 mode_t cvsumask = UMASK_DFLT;
67 
68 char *CurDir;
69 
70 /*
71  * Defaults, for the environment variables that are not set
72  */
73 char *Tmpdir = TMPDIR_DFLT;
74 char *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.  */
79 List *root_directories = NULL;
80 
81 static 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 
155 static 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 
200 static 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 
246 static 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 
277 static int
set_root_directory(p,ignored)278 set_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 
291 static const char * const*
cmd_synonyms()292 cmd_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 
330 unsigned long int
lookup_command_attribute(cmd_name)331 lookup_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 
347 static RETSIGTYPE
main_cleanup(sig)348 main_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 
399 int
main(argc,argv)400 main (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 ("\
564 Copyright (C) 2006 Free Software Foundation, Inc.\n\
565 \n\
566 Senior active maintainers include Larry Jones, Derek R. Price,\n\
567 and Mark D. Baushke.  Please see the AUTHORS and README files from the CVS\n\
568 distribution 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 
1065 char *
Make_Date(rawdate)1066 Make_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 
1086 char *
date_from_time_t(unixtime)1087 date_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.  */
1117 void
date_to_internet(dest,source)1118 date_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 
1128 void
date_to_tm(dest,source)1129 date_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.  */
1155 void
tm_to_internet(dest,source)1156 tm_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 
1171 void
usage(cpp)1172 usage (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 
1181 void
parseopts(root)1182 parseopts(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