root.c revision 25839
117721Speter/*
217721Speter * Copyright (c) 1992, Mark D. Baushke
317721Speter *
417721Speter * You may distribute under the terms of the GNU General Public License as
517721Speter * specified in the README file that comes with the CVS 1.4 kit.
617721Speter *
717721Speter * Name of Root
817721Speter *
917721Speter * Determine the path to the CVSROOT and set "Root" accordingly.
1017721Speter * If this looks like of modified clone of Name_Repository() in
1117721Speter * repos.c, it is...
1217721Speter */
1317721Speter
1417721Speter#include "cvs.h"
1525839Speter#include "getline.h"
1617721Speter
1725839Speter/* Printable names for things in the CVSroot_method enum variable.
1825839Speter   Watch out if the enum is changed in cvs.h! */
1925839Speter
2025839Speterchar *method_names[] = {
2125839Speter  "local", "server (rsh)", "pserver", "kserver", "ext"
2225839Speter};
2325839Speter
2425839Speter#ifndef DEBUG
2525839Speter
2617721Speterchar *
2717721SpeterName_Root(dir, update_dir)
2817721Speter     char *dir;
2917721Speter     char *update_dir;
3017721Speter{
3117721Speter    FILE *fpin;
3217721Speter    char *ret, *xupdate_dir;
3325839Speter    char *root = NULL;
3425839Speter    size_t root_allocated = 0;
3525839Speter    char *tmp;
3625839Speter    char *cvsadm;
3717721Speter    char *cp;
3817721Speter
3917721Speter    if (update_dir && *update_dir)
4017721Speter	xupdate_dir = update_dir;
4117721Speter    else
4217721Speter	xupdate_dir = ".";
4317721Speter
4417721Speter    if (dir != NULL)
4517721Speter    {
4625839Speter	cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10);
4717721Speter	(void) sprintf (cvsadm, "%s/%s", dir, CVSADM);
4825839Speter	tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10);
4917721Speter	(void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT);
5017721Speter    }
5117721Speter    else
5217721Speter    {
5325839Speter	cvsadm = xstrdup (CVSADM);
5425839Speter	tmp = xstrdup (CVSADM_ROOT);
5517721Speter    }
5617721Speter
5717721Speter    /*
5817721Speter     * Do not bother looking for a readable file if there is no cvsadm
5917721Speter     * directory present.
6017721Speter     *
6117721Speter     * It is possible that not all repositories will have a CVS/Root
6217721Speter     * file. This is ok, but the user will need to specify -d
6317721Speter     * /path/name or have the environment variable CVSROOT set in
6425839Speter     * order to continue.  */
6517721Speter    if ((!isdir (cvsadm)) || (!isreadable (tmp)))
6617721Speter    {
6725839Speter	ret = NULL;
6825839Speter	goto out;
6917721Speter    }
7017721Speter
7117721Speter    /*
7217721Speter     * The assumption here is that the CVS Root is always contained in the
7317721Speter     * first line of the "Root" file.
7417721Speter     */
7517721Speter    fpin = open_file (tmp, "r");
7617721Speter
7725839Speter    if (getline (&root, &root_allocated, fpin) < 0)
7817721Speter    {
7925839Speter	/* FIXME: should be checking for end of file separately; errno
8025839Speter	   is not set in that case.  */
8117721Speter	error (0, 0, "in directory %s:", xupdate_dir);
8217721Speter	error (0, errno, "cannot read %s", CVSADM_ROOT);
8317721Speter	error (0, 0, "please correct this problem");
8425839Speter	ret = NULL;
8525839Speter	goto out;
8617721Speter    }
8717721Speter    (void) fclose (fpin);
8817721Speter    if ((cp = strrchr (root, '\n')) != NULL)
8917721Speter	*cp = '\0';			/* strip the newline */
9017721Speter
9117721Speter    /*
9217721Speter     * root now contains a candidate for CVSroot. It must be an
9325839Speter     * absolute pathname or specify a remote server.
9417721Speter     */
9517721Speter
9625839Speter    if (
9717721Speter#ifdef CLIENT_SUPPORT
9825839Speter	(strchr (root, ':') == NULL) &&
9925839Speter#endif
10025839Speter    	! isabsolute (root))
10117721Speter    {
10217721Speter	error (0, 0, "in directory %s:", xupdate_dir);
10317721Speter	error (0, 0,
10417721Speter	       "ignoring %s because it does not contain an absolute pathname.",
10517721Speter	       CVSADM_ROOT);
10625839Speter	ret = NULL;
10725839Speter	goto out;
10817721Speter    }
10917721Speter
11017721Speter#ifdef CLIENT_SUPPORT
11117721Speter    if ((strchr (root, ':') == NULL) && !isdir (root))
11217721Speter#else /* ! CLIENT_SUPPORT */
11317721Speter    if (!isdir (root))
11417721Speter#endif /* CLIENT_SUPPORT */
11517721Speter    {
11617721Speter	error (0, 0, "in directory %s:", xupdate_dir);
11717721Speter	error (0, 0,
11817721Speter	       "ignoring %s because it specifies a non-existent repository %s",
11917721Speter	       CVSADM_ROOT, root);
12025839Speter	ret = NULL;
12125839Speter	goto out;
12217721Speter    }
12317721Speter
12417721Speter    /* allocate space to return and fill it in */
12525839Speter    strip_trailing_slashes (root);
12617721Speter    ret = xstrdup (root);
12725839Speter out:
12825839Speter    free (cvsadm);
12925839Speter    free (tmp);
13025839Speter    if (root != NULL)
13125839Speter	free (root);
13217721Speter    return (ret);
13317721Speter}
13417721Speter
13517721Speter/*
13617721Speter * Returns non-zero if the two directories have the same stat values
13717721Speter * which indicates that they are really the same directories.
13817721Speter */
13917721Speterint
14017721Spetersame_directories (dir1, dir2)
14117721Speter     char *dir1;
14217721Speter     char *dir2;
14317721Speter{
14417721Speter    struct stat sb1;
14517721Speter    struct stat sb2;
14617721Speter    int ret;
14717721Speter
14825839Speter    if ( CVS_STAT (dir1, &sb1) < 0)
14917721Speter        return (0);
15025839Speter    if ( CVS_STAT (dir2, &sb2) < 0)
15117721Speter        return (0);
15217721Speter
15317721Speter    ret = 0;
15417721Speter    if ( (memcmp( &sb1.st_dev, &sb2.st_dev, sizeof(dev_t) ) == 0) &&
15517721Speter	 (memcmp( &sb1.st_ino, &sb2.st_ino, sizeof(ino_t) ) == 0))
15617721Speter        ret = 1;
15717721Speter
15817721Speter    return (ret);
15917721Speter}
16017721Speter
16117721Speter
16217721Speter/*
16317721Speter * Write the CVS/Root file so that the environment variable CVSROOT
16417721Speter * and/or the -d option to cvs will be validated or not necessary for
16517721Speter * future work.
16617721Speter */
16717721Spetervoid
16817721SpeterCreate_Root (dir, rootdir)
16917721Speter     char *dir;
17017721Speter     char *rootdir;
17117721Speter{
17217721Speter    FILE *fout;
17325839Speter    char *tmp;
17417721Speter
17517721Speter    if (noexec)
17617721Speter	return;
17717721Speter
17817721Speter    /* record the current cvs root */
17917721Speter
18017721Speter    if (rootdir != NULL)
18117721Speter    {
18217721Speter        if (dir != NULL)
18325839Speter	{
18425839Speter	    tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10);
18517721Speter	    (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT);
18625839Speter	}
18717721Speter        else
18825839Speter	    tmp = xstrdup (CVSADM_ROOT);
18925839Speter
19017721Speter        fout = open_file (tmp, "w+");
19117721Speter        if (fprintf (fout, "%s\n", rootdir) < 0)
19217721Speter	    error (1, errno, "write to %s failed", tmp);
19317721Speter        if (fclose (fout) == EOF)
19417721Speter	    error (1, errno, "cannot close %s", tmp);
19525839Speter	free (tmp);
19617721Speter    }
19717721Speter}
19825839Speter
19925839Speter#endif /* ! DEBUG */
20025839Speter
20125839Speter
20225839Speter/* Parse a CVSROOT variable into its constituent parts -- method,
20325839Speter * username, hostname, directory.  The prototypical CVSROOT variable
20425839Speter * looks like:
20525839Speter *
20625839Speter * :method:user@host:path
20725839Speter *
20825839Speter * Some methods may omit fields; local, for example, doesn't need user
20925839Speter * and host.
21025839Speter *
21125839Speter * Returns zero on success, non-zero on failure. */
21225839Speter
21325839Speterchar *CVSroot_original = NULL;	/* the CVSroot that was passed in */
21425839Speterint client_active;		/* nonzero if we are doing remote access */
21525839SpeterCVSmethod CVSroot_method;	/* one of the enum values defined in cvs.h */
21625839Speterchar *CVSroot_username;		/* the username or NULL if method == local */
21725839Speterchar *CVSroot_hostname;		/* the hostname or NULL if method == local */
21825839Speterchar *CVSroot_directory;	/* the directory name */
21925839Speter
22025839Speter#ifdef AUTH_SERVER_SUPPORT
22125839Speter/* Die if CVSroot_directory and Pserver_Repos don't match. */
22225839Speterstatic void
22325839Spetercheck_root_consistent ()
22425839Speter{
22525839Speter    /* FIXME: Should be using a deferred error, as the rest of
22625839Speter       serve_root does.  As it is now the call to error could conceivably
22725839Speter       cause deadlock, as noted in server_cleanup.  Best solution would
22825839Speter       presumably be to write some code so that error() automatically
22925839Speter       defers the error in those cases where that is needed.  */
23025839Speter    /* FIXME?  Possible that the wording should be more clear (e.g.
23125839Speter          Root says "%s" but pserver protocol says "%s"
23225839Speter       or something which would aid people who are writing implementations
23325839Speter       of the client side of the CVS protocol.  I don't see any security
23425839Speter       problem with revealing that information.  */
23525839Speter    if ((Pserver_Repos != NULL) && (CVSroot_directory != NULL))
23625839Speter	if (strcmp (Pserver_Repos, CVSroot_directory) != 0)
23725839Speter	    error (1, 0, "repository mismatch: \"%s\" vs \"%s\"",
23825839Speter		   Pserver_Repos, CVSroot_directory);
23925839Speter}
24025839Speter
24125839Speter#endif /* AUTH_SERVER_SUPPORT */
24225839Speter
24325839Speter
24425839Speterint
24525839Speterparse_cvsroot (CVSroot)
24625839Speter    char *CVSroot;
24725839Speter{
24825839Speter    static int cvsroot_parsed = 0;
24925839Speter    char *cvsroot_copy, *p;
25025839Speter
25125839Speter    /* Don't go through the trouble twice. */
25225839Speter    if (cvsroot_parsed)
25325839Speter    {
25425839Speter	error (0, 0, "WARNING (parse_cvsroot): someone called me twice!\n");
25525839Speter	return 0;
25625839Speter    }
25725839Speter
25825839Speter    CVSroot_original = xstrdup (CVSroot);
25925839Speter    cvsroot_copy = xstrdup (CVSroot);
26025839Speter
26125839Speter    if ((*cvsroot_copy == ':'))
26225839Speter    {
26325839Speter	char *method = ++cvsroot_copy;
26425839Speter
26525839Speter	/* Access method specified, as in
26625839Speter	 * "cvs -d :pserver:user@host:/path",
26725839Speter	 * "cvs -d :local:e:\path", or
26825839Speter	 * "cvs -d :kserver:user@host:/path".
26925839Speter	 * We need to get past that part of CVSroot before parsing the
27025839Speter	 * rest of it.
27125839Speter	 */
27225839Speter
27325839Speter	if (! (p = strchr (method, ':')))
27425839Speter	{
27525839Speter	    error (0, 0, "bad CVSroot: %s", CVSroot);
27625839Speter	    return 1;
27725839Speter	}
27825839Speter	*p = '\0';
27925839Speter	cvsroot_copy = ++p;
28025839Speter
28125839Speter	/* Now we have an access method -- see if it's valid. */
28225839Speter
28325839Speter	if (strcmp (method, "local") == 0)
28425839Speter	    CVSroot_method = local_method;
28525839Speter	else if (strcmp (method, "pserver") == 0)
28625839Speter	    CVSroot_method = pserver_method;
28725839Speter	else if (strcmp (method, "kserver") == 0)
28825839Speter	    CVSroot_method = kserver_method;
28925839Speter	else if (strcmp (method, "server") == 0)
29025839Speter	    CVSroot_method = server_method;
29125839Speter	else if (strcmp (method, "ext") == 0)
29225839Speter	    CVSroot_method = ext_method;
29325839Speter	else
29425839Speter	{
29525839Speter	    error (0, 0, "unknown method in CVSroot: %s", CVSroot);
29625839Speter	    return 1;
29725839Speter	}
29825839Speter    }
29925839Speter    else
30025839Speter    {
30125839Speter	/* If the method isn't specified, assume
30225839Speter	   SERVER_METHOD/EXT_METHOD if the string contains a colon or
30325839Speter	   LOCAL_METHOD otherwise.  */
30425839Speter
30525839Speter	CVSroot_method = ((strchr (cvsroot_copy, ':'))
30625839Speter#ifdef RSH_NOT_TRANSPARENT
30725839Speter			  ? server_method
30825839Speter#else
30925839Speter			  ? ext_method
31025839Speter#endif
31125839Speter			  : local_method);
31225839Speter    }
31325839Speter
31425839Speter    client_active = (CVSroot_method != local_method);
31525839Speter
31625839Speter    /* Check for username/hostname if we're not LOCAL_METHOD. */
31725839Speter
31825839Speter    CVSroot_username = NULL;
31925839Speter    CVSroot_hostname = NULL;
32025839Speter
32125839Speter    if (CVSroot_method != local_method)
32225839Speter    {
32325839Speter	/* Check to see if there is a username in the string. */
32425839Speter
32525839Speter	if ((p = strchr (cvsroot_copy, '@')))
32625839Speter	{
32725839Speter	    CVSroot_username = cvsroot_copy;
32825839Speter	    *p = '\0';
32925839Speter	    cvsroot_copy = ++p;
33025839Speter	    if (*CVSroot_username == '\0')
33125839Speter		CVSroot_username = NULL;
33225839Speter	}
33325839Speter
33425839Speter	if ((p = strchr (cvsroot_copy, ':')))
33525839Speter	{
33625839Speter	    CVSroot_hostname = cvsroot_copy;
33725839Speter	    *p = '\0';
33825839Speter	    cvsroot_copy = ++p;
33925839Speter
34025839Speter	    if (*CVSroot_hostname == '\0')
34125839Speter		CVSroot_hostname = NULL;
34225839Speter	}
34325839Speter    }
34425839Speter
34525839Speter    CVSroot_directory = cvsroot_copy;
34625839Speter#ifdef AUTH_SERVER_SUPPORT
34725839Speter    check_root_consistent ();
34825839Speter#endif /* AUTH_SERVER_SUPPORT */
34925839Speter
35025839Speter#if ! defined (CLIENT_SUPPORT) && ! defined (DEBUG)
35125839Speter    if (CVSroot_method != local_method)
35225839Speter    {
35325839Speter	error (0, 0, "Your CVSROOT is set for a remote access method");
35425839Speter	error (0, 0, "but your CVS executable doesn't support it");
35525839Speter	error (0, 0, "(%s)", CVSroot);
35625839Speter	return 1;
35725839Speter    }
35825839Speter#endif
35925839Speter
36025839Speter    /* Do various sanity checks. */
36125839Speter
36225839Speter    if (CVSroot_username && ! CVSroot_hostname)
36325839Speter    {
36425839Speter	error (0, 0, "missing hostname in CVSROOT: %s", CVSroot);
36525839Speter	return 1;
36625839Speter    }
36725839Speter
36825839Speter    switch (CVSroot_method)
36925839Speter    {
37025839Speter    case local_method:
37125839Speter	if (CVSroot_username || CVSroot_hostname)
37225839Speter	{
37325839Speter	    error (0, 0, "can't specify hostname and username in CVSROOT");
37425839Speter	    error (0, 0, "when using local access method");
37525839Speter	    error (0, 0, "(%s)", CVSroot);
37625839Speter	    return 1;
37725839Speter	}
37825839Speter	/* cvs.texinfo has always told people that CVSROOT must be an
37925839Speter	   absolute pathname.  Furthermore, attempts to use a relative
38025839Speter	   pathname produced various errors (I couldn't get it to work),
38125839Speter	   so there would seem to be little risk in making this a fatal
38225839Speter	   error.  */
38325839Speter	if (!isabsolute (CVSroot_directory))
38425839Speter	    error (1, 0, "CVSROOT %s must be an absolute pathname",
38525839Speter		   CVSroot_directory);
38625839Speter	break;
38725839Speter    case kserver_method:
38825839Speter#ifndef HAVE_KERBEROS
38925839Speter	error (0, 0, "Your CVSROOT is set for a kerberos access method");
39025839Speter	error (0, 0, "but your CVS executable doesn't support it");
39125839Speter	error (0, 0, "(%s)", CVSroot);
39225839Speter	return 1;
39325839Speter#endif
39425839Speter    case server_method:
39525839Speter    case ext_method:
39625839Speter    case pserver_method:
39725839Speter	if (! CVSroot_hostname)
39825839Speter	{
39925839Speter	    error (0, 0, "didn't specify hostname in CVSROOT: %s", CVSroot);
40025839Speter	    return 1;
40125839Speter	}
40225839Speter	break;
40325839Speter    }
40425839Speter
40525839Speter    if (*CVSroot_directory == '\0')
40625839Speter    {
40725839Speter	error (0, 0, "missing directory in CVSROOT: %s", CVSroot);
40825839Speter	return 1;
40925839Speter    }
41025839Speter
41125839Speter    /* Hooray!  We finally parsed it! */
41225839Speter    return 0;
41325839Speter}
41425839Speter
41525839Speter
41625839Speter/* Set up the global CVSroot* variables as if we're using the local
41725839Speter   repository DIR. */
41825839Speter
41925839Spetervoid
42025839Speterset_local_cvsroot (dir)
42125839Speter    char *dir;
42225839Speter{
42325839Speter    CVSroot_original = xstrdup (dir);
42425839Speter    CVSroot_method = local_method;
42525839Speter    CVSroot_directory = CVSroot_original;
42625839Speter#ifdef AUTH_SERVER_SUPPORT
42725839Speter    check_root_consistent ();
42825839Speter#endif /* AUTH_SERVER_SUPPORT */
42925839Speter    CVSroot_username = NULL;
43025839Speter    CVSroot_hostname = NULL;
43125839Speter    client_active = 0;
43225839Speter}
43325839Speter
43425839Speter
43525839Speter#ifdef DEBUG
43625839Speter/* This is for testing the parsing function. */
43725839Speter
43825839Speter#include <stdio.h>
43925839Speter
44025839Speterchar *CVSroot;
44125839Speterchar *program_name = "testing";
44225839Speterchar *command_name = "parse_cvsroot";		/* XXX is this used??? */
44325839Speter
44425839Spetervoid
44525839Spetermain (argc, argv)
44625839Speter    int argc;
44725839Speter    char *argv[];
44825839Speter{
44925839Speter    program_name = argv[0];
45025839Speter
45125839Speter    if (argc != 2)
45225839Speter    {
45325839Speter	fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name);
45425839Speter	exit (2);
45525839Speter    }
45625839Speter
45725839Speter    if (parse_cvsroot (argv[1]))
45825839Speter    {
45925839Speter	fprintf (stderr, "%s: Parsing failed.", program_name);
46025839Speter	exit (1);
46125839Speter    }
46225839Speter    printf ("CVSroot: %s\n", argv[1]);
46325839Speter    printf ("CVSroot_method: %s\n", method_names[CVSroot_method]);
46425839Speter    printf ("CVSroot_username: %s\n",
46525839Speter	    CVSroot_username ? CVSroot_username : "NULL");
46625839Speter    printf ("CVSroot_hostname: %s\n",
46725839Speter	    CVSroot_hostname ? CVSroot_hostname : "NULL");
46825839Speter    printf ("CVSroot_directory: %s\n", CVSroot_directory);
46925839Speter
47025839Speter   exit (0);
47125839Speter   /* NOTREACHED */
47225839Speter}
47325839Speter#endif
474