117721Speter/*
2175261Sobrien * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3175261Sobrien *
4175261Sobrien * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5175261Sobrien *                                  and others.
6175261Sobrien *
7175261Sobrien * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8175261Sobrien * Portions Copyright (C) 1989-1992, Brian Berliner
917721Speter *
1017721Speter * You may distribute under the terms of the GNU General Public License as
1132785Speter * specified in the README file that comes with the CVS source distribution.
1217721Speter */
1317721Speter
1432785Speter#include <assert.h>
1517721Speter#include "cvs.h"
1625839Speter#include "getline.h"
1717721Speter
1817721Speter/* Determine the name of the RCS repository for directory DIR in the
1917721Speter   current working directory, or for the current working directory
2017721Speter   itself if DIR is NULL.  Returns the name in a newly-malloc'd
2117721Speter   string.  On error, gives a fatal error and does not return.
2217721Speter   UPDATE_DIR is the path from where cvs was invoked (for use in error
2317721Speter   messages), and should contain DIR as its last component.
2417721Speter   UPDATE_DIR can be NULL to signify the directory in which cvs was
2517721Speter   invoked.  */
2617721Speter
2717721Speterchar *
2817721SpeterName_Repository (dir, update_dir)
29128266Speter    const char *dir;
30128266Speter    const char *update_dir;
3117721Speter{
3217721Speter    FILE *fpin;
33128266Speter    const char *xupdate_dir;
3425839Speter    char *repos = NULL;
3525839Speter    size_t repos_allocated = 0;
3625839Speter    char *tmp;
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	tmp = xmalloc (strlen (dir) + sizeof (CVSADM_REP) + 10);
4725839Speter	(void) sprintf (tmp, "%s/%s", dir, CVSADM_REP);
4817721Speter    }
4917721Speter    else
5025839Speter	tmp = xstrdup (CVSADM_REP);
5117721Speter
5217721Speter    /*
5317721Speter     * The assumption here is that the repository is always contained in the
5417721Speter     * first line of the "Repository" file.
5517721Speter     */
5625839Speter    fpin = CVS_FOPEN (tmp, "r");
5717721Speter
5825839Speter    if (fpin == NULL)
5917721Speter    {
6025839Speter	int save_errno = errno;
6125839Speter	char *cvsadm;
6225839Speter
6325839Speter	if (dir != NULL)
6425839Speter	{
6525839Speter	    cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10);
6625839Speter	    (void) sprintf (cvsadm, "%s/%s", dir, CVSADM);
6725839Speter	}
6825839Speter	else
6925839Speter	    cvsadm = xstrdup (CVSADM);
7025839Speter
7125839Speter	if (!isdir (cvsadm))
7225839Speter	{
7325839Speter	    error (0, 0, "in directory %s:", xupdate_dir);
7425839Speter	    error (1, 0, "there is no version here; do '%s checkout' first",
7525839Speter		   program_name);
7625839Speter	}
7725839Speter	free (cvsadm);
7825839Speter
7925839Speter	if (existence_error (save_errno))
8025839Speter	{
8125839Speter	    /* FIXME: This is a very poorly worded error message.  It
8225839Speter	       occurs at least in the case where the user manually
8325839Speter	       creates a directory named CVS, so the error message
8425839Speter	       should be more along the lines of "CVS directory found
8525839Speter	       without administrative files; use CVS to create the CVS
8625839Speter	       directory, or rename it to something else if the
8725839Speter	       intention is to store something besides CVS
8825839Speter	       administrative files".  */
8925839Speter	    error (0, 0, "in directory %s:", xupdate_dir);
9025839Speter	    error (1, 0, "*PANIC* administration files missing");
9125839Speter	}
9225839Speter
9325839Speter	error (1, save_errno, "cannot open %s", tmp);
9425839Speter    }
9525839Speter
9625839Speter    if (getline (&repos, &repos_allocated, fpin) < 0)
9725839Speter    {
9825839Speter	/* FIXME: should be checking for end of file separately.  */
9917721Speter	error (0, 0, "in directory %s:", xupdate_dir);
10017721Speter	error (1, errno, "cannot read %s", CVSADM_REP);
10117721Speter    }
10232785Speter    if (fclose (fpin) < 0)
10332785Speter	error (0, errno, "cannot close %s", tmp);
10432785Speter    free (tmp);
10532785Speter
10617721Speter    if ((cp = strrchr (repos, '\n')) != NULL)
10717721Speter	*cp = '\0';			/* strip the newline */
10817721Speter
10917721Speter    /*
11017721Speter     * If this is a relative repository pathname, turn it into an absolute
11117721Speter     * one by tacking on the CVSROOT environment variable. If the CVSROOT
11217721Speter     * environment variable is not set, die now.
11317721Speter     */
11417721Speter    if (! isabsolute(repos))
11517721Speter    {
11625839Speter	char *newrepos;
11725839Speter
11881404Speter	if (current_parsed_root == NULL)
11917721Speter	{
12017721Speter	    error (0, 0, "in directory %s:", xupdate_dir);
12117721Speter	    error (0, 0, "must set the CVSROOT environment variable\n");
12217721Speter	    error (0, 0, "or specify the '-d' option to %s.", program_name);
12317721Speter	    error (1, 0, "illegal repository setting");
12417721Speter	}
12566525Speter	if (pathname_levels (repos) > 0)
12666525Speter	{
12766525Speter	    error (0, 0, "in directory %s:", xupdate_dir);
12866525Speter	    error (0, 0, "`..'-relative repositories are not supported.");
12966525Speter	    error (1, 0, "illegal source repository");
13066525Speter	}
131128266Speter	newrepos = xmalloc (strlen (current_parsed_root->directory)
132128266Speter	                    + strlen (repos) + 2);
133128266Speter	sprintf (newrepos, "%s/%s", current_parsed_root->directory, repos);
13425839Speter	free (repos);
13525839Speter	repos = newrepos;
13617721Speter    }
13717721Speter
13832785Speter    Sanitize_Repository_Name (repos);
13932785Speter
14025839Speter    return repos;
14117721Speter}
14217721Speter
143128266Speter
144128266Speter
14517721Speter/*
14617721Speter * Return a pointer to the repository name relative to CVSROOT from a
14717721Speter * possibly fully qualified repository
14817721Speter */
149128266Speterconst char *
15017721SpeterShort_Repository (repository)
151128266Speter    const char *repository;
15217721Speter{
15317721Speter    if (repository == NULL)
154128266Speter	return NULL;
15517721Speter
15617721Speter    /* If repository matches CVSroot at the beginning, strip off CVSroot */
15717721Speter    /* And skip leading '/' in rep, in case CVSroot ended with '/'. */
15881404Speter    if (strncmp (current_parsed_root->directory, repository,
15981404Speter		 strlen (current_parsed_root->directory)) == 0)
16017721Speter    {
161128266Speter	const char *rep = repository + strlen (current_parsed_root->directory);
16217721Speter	return (*rep == '/') ? rep+1 : rep;
16317721Speter    }
16417721Speter    else
165128266Speter	return repository;
16617721Speter}
16732785Speter
168128266Speter
169128266Speter
17032785Speter/* Sanitize the repository name (in place) by removing trailing
17132785Speter * slashes and a trailing "." if present.  It should be safe for
17232785Speter * callers to use strcat and friends to create repository names.
17332785Speter * Without this check, names like "/path/to/repos/./foo" and
17432785Speter * "/path/to/repos//foo" would be created.  For example, one
17532785Speter * significant case is the CVSROOT-detection code in commit.c.  It
17632785Speter * decides whether or not it needs to rebuild the administrative file
17732785Speter * database by doing a string compare.  If we've done a `cvs co .' to
17832785Speter * get the CVSROOT files, "/path/to/repos/./CVSROOT" and
17932785Speter * "/path/to/repos/CVSROOT" are the arguments that are compared!
18032785Speter *
18132785Speter * This function ends up being called from the same places as
18232785Speter * strip_path, though what it does is much more conservative.  Many
18332785Speter * comments about this operation (which was scattered around in
18432785Speter * several places in the source code) ran thus:
18532785Speter *
18632785Speter *    ``repository ends with "/."; omit it.  This sort of thing used
18732785Speter *    to be taken care of by strip_path.  Now we try to be more
18832785Speter *    selective.  I suspect that it would be even better to push it
18932785Speter *    back further someday, so that the trailing "/." doesn't get into
19032785Speter *    repository in the first place, but we haven't taken things that
19132785Speter *    far yet.''        --Jim Kingdon (recurse.c, 07-Sep-97)
192128266Speter */
19332785Speter
19432785Spetervoid
19532785SpeterSanitize_Repository_Name (repository)
19632785Speter    char *repository;
19732785Speter{
19832785Speter    size_t len;
19932785Speter
20032785Speter    assert (repository != NULL);
20132785Speter
20232785Speter    strip_trailing_slashes (repository);
20332785Speter
20432785Speter    len = strlen (repository);
20532785Speter    if (len >= 2
20632785Speter	&& repository[len - 1] == '.'
20732785Speter	&& ISDIRSEP (repository[len - 2]))
20832785Speter    {
20932785Speter	repository[len - 2] = '\0';
21032785Speter    }
21132785Speter}
212