xref: /openbsd/gnu/usr.bin/cvs/src/repos.c (revision f9bbbf45)
11e72d8d2Sderaadt /*
21e72d8d2Sderaadt  * Copyright (c) 1992, Brian Berliner and Jeff Polk
31e72d8d2Sderaadt  * Copyright (c) 1989-1992, Brian Berliner
41e72d8d2Sderaadt  *
51e72d8d2Sderaadt  * You may distribute under the terms of the GNU General Public License as
62286d8edStholo  * specified in the README file that comes with the CVS source distribution.
71e72d8d2Sderaadt  */
81e72d8d2Sderaadt 
92286d8edStholo #include <assert.h>
101e72d8d2Sderaadt #include "cvs.h"
11780d15dfStholo #include "getline.h"
121e72d8d2Sderaadt 
13c2c61682Stholo /* Determine the name of the RCS repository for directory DIR in the
14c2c61682Stholo    current working directory, or for the current working directory
15c2c61682Stholo    itself if DIR is NULL.  Returns the name in a newly-malloc'd
16c2c61682Stholo    string.  On error, gives a fatal error and does not return.
17c2c61682Stholo    UPDATE_DIR is the path from where cvs was invoked (for use in error
18c2c61682Stholo    messages), and should contain DIR as its last component.
19c2c61682Stholo    UPDATE_DIR can be NULL to signify the directory in which cvs was
20c2c61682Stholo    invoked.  */
21c2c61682Stholo 
221e72d8d2Sderaadt char *
Name_Repository(dir,update_dir)231e72d8d2Sderaadt Name_Repository (dir, update_dir)
241e72d8d2Sderaadt     char *dir;
251e72d8d2Sderaadt     char *update_dir;
261e72d8d2Sderaadt {
271e72d8d2Sderaadt     FILE *fpin;
28780d15dfStholo     char *xupdate_dir;
29780d15dfStholo     char *repos = NULL;
30780d15dfStholo     size_t repos_allocated = 0;
31780d15dfStholo     char *tmp;
321e72d8d2Sderaadt     char *cp;
331e72d8d2Sderaadt 
341e72d8d2Sderaadt     if (update_dir && *update_dir)
351e72d8d2Sderaadt 	xupdate_dir = update_dir;
361e72d8d2Sderaadt     else
371e72d8d2Sderaadt 	xupdate_dir = ".";
381e72d8d2Sderaadt 
391e72d8d2Sderaadt     if (dir != NULL)
40780d15dfStholo     {
41780d15dfStholo 	tmp = xmalloc (strlen (dir) + sizeof (CVSADM_REP) + 10);
4250bf276cStholo 	(void) sprintf (tmp, "%s/%s", dir, CVSADM_REP);
43780d15dfStholo     }
4450bf276cStholo     else
45780d15dfStholo 	tmp = xstrdup (CVSADM_REP);
4650bf276cStholo 
4750bf276cStholo     /*
4850bf276cStholo      * The assumption here is that the repository is always contained in the
4950bf276cStholo      * first line of the "Repository" file.
5050bf276cStholo      */
5150bf276cStholo     fpin = CVS_FOPEN (tmp, "r");
5250bf276cStholo 
5350bf276cStholo     if (fpin == NULL)
5450bf276cStholo     {
5550bf276cStholo 	int save_errno = errno;
56780d15dfStholo 	char *cvsadm;
5750bf276cStholo 
5850bf276cStholo 	if (dir != NULL)
59780d15dfStholo 	{
60780d15dfStholo 	    cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10);
611e72d8d2Sderaadt 	    (void) sprintf (cvsadm, "%s/%s", dir, CVSADM);
62780d15dfStholo 	}
631e72d8d2Sderaadt 	else
64780d15dfStholo 	    cvsadm = xstrdup (CVSADM);
651e72d8d2Sderaadt 
661e72d8d2Sderaadt 	if (!isdir (cvsadm))
671e72d8d2Sderaadt 	{
681e72d8d2Sderaadt 	    error (0, 0, "in directory %s:", xupdate_dir);
691e72d8d2Sderaadt 	    error (1, 0, "there is no version here; do '%s checkout' first",
701e72d8d2Sderaadt 		   program_name);
711e72d8d2Sderaadt 	}
72780d15dfStholo 	free (cvsadm);
731e72d8d2Sderaadt 
7450bf276cStholo 	if (existence_error (save_errno))
751e72d8d2Sderaadt 	{
76461cc63eStholo 	    /* FIXME: This is a very poorly worded error message.  It
77461cc63eStholo 	       occurs at least in the case where the user manually
78461cc63eStholo 	       creates a directory named CVS, so the error message
79461cc63eStholo 	       should be more along the lines of "CVS directory found
80461cc63eStholo 	       without administrative files; use CVS to create the CVS
81461cc63eStholo 	       directory, or rename it to something else if the
82461cc63eStholo 	       intention is to store something besides CVS
83461cc63eStholo 	       administrative files".  */
841e72d8d2Sderaadt 	    error (0, 0, "in directory %s:", xupdate_dir);
851e72d8d2Sderaadt 	    error (1, 0, "*PANIC* administration files missing");
861e72d8d2Sderaadt 	}
871e72d8d2Sderaadt 
8850bf276cStholo 	error (1, save_errno, "cannot open %s", tmp);
891e72d8d2Sderaadt     }
901e72d8d2Sderaadt 
91*f9bbbf45Sfgsch     if (get_line (&repos, &repos_allocated, fpin) < 0)
921e72d8d2Sderaadt     {
93780d15dfStholo 	/* FIXME: should be checking for end of file separately.  */
941e72d8d2Sderaadt 	error (0, 0, "in directory %s:", xupdate_dir);
951e72d8d2Sderaadt 	error (1, errno, "cannot read %s", CVSADM_REP);
961e72d8d2Sderaadt     }
972286d8edStholo     if (fclose (fpin) < 0)
982286d8edStholo 	error (0, errno, "cannot close %s", tmp);
992286d8edStholo     free (tmp);
1002286d8edStholo 
1011e72d8d2Sderaadt     if ((cp = strrchr (repos, '\n')) != NULL)
1021e72d8d2Sderaadt 	*cp = '\0';			/* strip the newline */
1031e72d8d2Sderaadt 
1041e72d8d2Sderaadt     /*
1051e72d8d2Sderaadt      * If this is a relative repository pathname, turn it into an absolute
1061e72d8d2Sderaadt      * one by tacking on the CVSROOT environment variable. If the CVSROOT
1071e72d8d2Sderaadt      * environment variable is not set, die now.
1081e72d8d2Sderaadt      */
1091e72d8d2Sderaadt     if (! isabsolute(repos))
1101e72d8d2Sderaadt     {
111780d15dfStholo 	char *newrepos;
112780d15dfStholo 
11343c1707eStholo 	if (current_parsed_root == NULL)
1141e72d8d2Sderaadt 	{
1151e72d8d2Sderaadt 	    error (0, 0, "in directory %s:", xupdate_dir);
1161e72d8d2Sderaadt 	    error (0, 0, "must set the CVSROOT environment variable\n");
1171e72d8d2Sderaadt 	    error (0, 0, "or specify the '-d' option to %s.", program_name);
1181e72d8d2Sderaadt 	    error (1, 0, "illegal repository setting");
1191e72d8d2Sderaadt 	}
120e77048c1Stholo 	if (pathname_levels (repos) > 0)
121e77048c1Stholo 	{
122e77048c1Stholo 	    error (0, 0, "in directory %s:", xupdate_dir);
123e77048c1Stholo 	    error (0, 0, "`..'-relative repositories are not supported.");
124e77048c1Stholo 	    error (1, 0, "illegal source repository");
125e77048c1Stholo 	}
12643c1707eStholo 	newrepos = xmalloc (strlen (current_parsed_root->directory) + strlen (repos) + 2);
12743c1707eStholo 	(void) sprintf (newrepos, "%s/%s", current_parsed_root->directory, repos);
128780d15dfStholo 	free (repos);
129780d15dfStholo 	repos = newrepos;
1301e72d8d2Sderaadt     }
1311e72d8d2Sderaadt 
1322286d8edStholo     Sanitize_Repository_Name (repos);
1332286d8edStholo 
134780d15dfStholo     return repos;
1351e72d8d2Sderaadt }
1361e72d8d2Sderaadt 
1371e72d8d2Sderaadt /*
1381e72d8d2Sderaadt  * Return a pointer to the repository name relative to CVSROOT from a
1391e72d8d2Sderaadt  * possibly fully qualified repository
1401e72d8d2Sderaadt  */
1411e72d8d2Sderaadt char *
Short_Repository(repository)1421e72d8d2Sderaadt Short_Repository (repository)
1431e72d8d2Sderaadt     char *repository;
1441e72d8d2Sderaadt {
1451e72d8d2Sderaadt     if (repository == NULL)
1461e72d8d2Sderaadt 	return (NULL);
1471e72d8d2Sderaadt 
1481e72d8d2Sderaadt     /* If repository matches CVSroot at the beginning, strip off CVSroot */
1491e72d8d2Sderaadt     /* And skip leading '/' in rep, in case CVSroot ended with '/'. */
15043c1707eStholo     if (strncmp (current_parsed_root->directory, repository,
15143c1707eStholo 		 strlen (current_parsed_root->directory)) == 0)
1521e72d8d2Sderaadt     {
15343c1707eStholo 	char *rep = repository + strlen (current_parsed_root->directory);
1541e72d8d2Sderaadt 	return (*rep == '/') ? rep+1 : rep;
1551e72d8d2Sderaadt     }
1561e72d8d2Sderaadt     else
1571e72d8d2Sderaadt 	return (repository);
1581e72d8d2Sderaadt }
1592286d8edStholo 
1602286d8edStholo /* Sanitize the repository name (in place) by removing trailing
1612286d8edStholo  * slashes and a trailing "." if present.  It should be safe for
1622286d8edStholo  * callers to use strcat and friends to create repository names.
1632286d8edStholo  * Without this check, names like "/path/to/repos/./foo" and
1642286d8edStholo  * "/path/to/repos//foo" would be created.  For example, one
1652286d8edStholo  * significant case is the CVSROOT-detection code in commit.c.  It
1662286d8edStholo  * decides whether or not it needs to rebuild the administrative file
1672286d8edStholo  * database by doing a string compare.  If we've done a `cvs co .' to
1682286d8edStholo  * get the CVSROOT files, "/path/to/repos/./CVSROOT" and
1692286d8edStholo  * "/path/to/repos/CVSROOT" are the arguments that are compared!
1702286d8edStholo  *
1712286d8edStholo  * This function ends up being called from the same places as
1722286d8edStholo  * strip_path, though what it does is much more conservative.  Many
1732286d8edStholo  * comments about this operation (which was scattered around in
1742286d8edStholo  * several places in the source code) ran thus:
1752286d8edStholo  *
1762286d8edStholo  *    ``repository ends with "/."; omit it.  This sort of thing used
1772286d8edStholo  *    to be taken care of by strip_path.  Now we try to be more
1782286d8edStholo  *    selective.  I suspect that it would be even better to push it
1792286d8edStholo  *    back further someday, so that the trailing "/." doesn't get into
1802286d8edStholo  *    repository in the first place, but we haven't taken things that
1812286d8edStholo  *    far yet.''        --Jim Kingdon (recurse.c, 07-Sep-97)
1822286d8edStholo  *
1832286d8edStholo  * Ahh, all too true.  The major consideration is RELATIVE_REPOS.  If
1842286d8edStholo  * the "/." doesn't end up in the repository while RELATIVE_REPOS is
1852286d8edStholo  * defined, there will be nothing in the CVS/Repository file.  I
1862286d8edStholo  * haven't verified that the remote protocol will handle that
1872286d8edStholo  * correctly yet, so I've not made that change. */
1882286d8edStholo 
1892286d8edStholo void
Sanitize_Repository_Name(repository)1902286d8edStholo Sanitize_Repository_Name (repository)
1912286d8edStholo     char *repository;
1922286d8edStholo {
1932286d8edStholo     size_t len;
1942286d8edStholo 
1952286d8edStholo     assert (repository != NULL);
1962286d8edStholo 
1972286d8edStholo     strip_trailing_slashes (repository);
1982286d8edStholo 
1992286d8edStholo     len = strlen (repository);
2002286d8edStholo     if (len >= 2
2012286d8edStholo 	&& repository[len - 1] == '.'
2022286d8edStholo 	&& ISDIRSEP (repository[len - 2]))
2032286d8edStholo     {
2042286d8edStholo 	repository[len - 2] = '\0';
2052286d8edStholo     }
2062286d8edStholo }
207