xref: /dflybsd-src/contrib/cvs-1.12/src/repos.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /*
2*86d7f5d3SJohn Marino  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3*86d7f5d3SJohn Marino  *
4*86d7f5d3SJohn Marino  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5*86d7f5d3SJohn Marino  *                                  and others.
6*86d7f5d3SJohn Marino  *
7*86d7f5d3SJohn Marino  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8*86d7f5d3SJohn Marino  * Portions Copyright (C) 1989-1992, Brian Berliner
9*86d7f5d3SJohn Marino  *
10*86d7f5d3SJohn Marino  * You may distribute under the terms of the GNU General Public License as
11*86d7f5d3SJohn Marino  * specified in the README file that comes with the CVS source distribution.
12*86d7f5d3SJohn Marino  */
13*86d7f5d3SJohn Marino 
14*86d7f5d3SJohn Marino #include "cvs.h"
15*86d7f5d3SJohn Marino #include "getline.h"
16*86d7f5d3SJohn Marino 
17*86d7f5d3SJohn Marino 
18*86d7f5d3SJohn Marino 
19*86d7f5d3SJohn Marino /* Determine the name of the RCS repository for directory DIR in the
20*86d7f5d3SJohn Marino    current working directory, or for the current working directory
21*86d7f5d3SJohn Marino    itself if DIR is NULL.  Returns the name in a newly-malloc'd
22*86d7f5d3SJohn Marino    string.  On error, gives a fatal error and does not return.
23*86d7f5d3SJohn Marino    UPDATE_DIR is the path from where cvs was invoked (for use in error
24*86d7f5d3SJohn Marino    messages), and should contain DIR as its last component.
25*86d7f5d3SJohn Marino    UPDATE_DIR can be NULL to signify the directory in which cvs was
26*86d7f5d3SJohn Marino    invoked.  */
27*86d7f5d3SJohn Marino 
28*86d7f5d3SJohn Marino char *
Name_Repository(const char * dir,const char * update_dir)29*86d7f5d3SJohn Marino Name_Repository (const char *dir, const char *update_dir)
30*86d7f5d3SJohn Marino {
31*86d7f5d3SJohn Marino     FILE *fpin;
32*86d7f5d3SJohn Marino     const char *xupdate_dir;
33*86d7f5d3SJohn Marino     char *repos = NULL;
34*86d7f5d3SJohn Marino     size_t repos_allocated = 0;
35*86d7f5d3SJohn Marino     char *tmp;
36*86d7f5d3SJohn Marino     char *cp;
37*86d7f5d3SJohn Marino 
38*86d7f5d3SJohn Marino     if (update_dir && *update_dir)
39*86d7f5d3SJohn Marino 	xupdate_dir = update_dir;
40*86d7f5d3SJohn Marino     else
41*86d7f5d3SJohn Marino 	xupdate_dir = ".";
42*86d7f5d3SJohn Marino 
43*86d7f5d3SJohn Marino     if (dir != NULL)
44*86d7f5d3SJohn Marino 	tmp = Xasprintf ("%s/%s", dir, CVSADM_REP);
45*86d7f5d3SJohn Marino     else
46*86d7f5d3SJohn Marino 	tmp = xstrdup (CVSADM_REP);
47*86d7f5d3SJohn Marino 
48*86d7f5d3SJohn Marino     /*
49*86d7f5d3SJohn Marino      * The assumption here is that the repository is always contained in the
50*86d7f5d3SJohn Marino      * first line of the "Repository" file.
51*86d7f5d3SJohn Marino      */
52*86d7f5d3SJohn Marino     fpin = CVS_FOPEN (tmp, "r");
53*86d7f5d3SJohn Marino 
54*86d7f5d3SJohn Marino     if (fpin == NULL)
55*86d7f5d3SJohn Marino     {
56*86d7f5d3SJohn Marino 	int save_errno = errno;
57*86d7f5d3SJohn Marino 	char *cvsadm;
58*86d7f5d3SJohn Marino 
59*86d7f5d3SJohn Marino 	if (dir != NULL)
60*86d7f5d3SJohn Marino 	    cvsadm = Xasprintf ("%s/%s", dir, CVSADM);
61*86d7f5d3SJohn Marino 	else
62*86d7f5d3SJohn Marino 	    cvsadm = xstrdup (CVSADM);
63*86d7f5d3SJohn Marino 
64*86d7f5d3SJohn Marino 	if (!isdir (cvsadm))
65*86d7f5d3SJohn Marino 	{
66*86d7f5d3SJohn Marino 	    error (0, 0, "in directory `%s':", xupdate_dir);
67*86d7f5d3SJohn Marino 	    error (1, 0, "there is no version here; do `%s checkout' first",
68*86d7f5d3SJohn Marino 		   program_name);
69*86d7f5d3SJohn Marino 	}
70*86d7f5d3SJohn Marino 	free (cvsadm);
71*86d7f5d3SJohn Marino 
72*86d7f5d3SJohn Marino 	if (existence_error (save_errno))
73*86d7f5d3SJohn Marino 	{
74*86d7f5d3SJohn Marino 	    /* This occurs at least in the case where the user manually
75*86d7f5d3SJohn Marino 	     * creates a directory named CVS.
76*86d7f5d3SJohn Marino 	     */
77*86d7f5d3SJohn Marino 	    error (0, 0, "in directory `%s':", xupdate_dir);
78*86d7f5d3SJohn Marino 	    error (0, 0, "CVS directory found without administrative files.");
79*86d7f5d3SJohn Marino 	    error (0, 0, "Use CVS to create the CVS directory, or rename the");
80*86d7f5d3SJohn Marino 	    error (0, 0, "directory if it is intended to store something");
81*86d7f5d3SJohn Marino 	    error (0, 0, "besides CVS administrative files.");
82*86d7f5d3SJohn Marino 	    error (1, 0, "*PANIC* administration files missing!");
83*86d7f5d3SJohn Marino 	}
84*86d7f5d3SJohn Marino 
85*86d7f5d3SJohn Marino 	error (1, save_errno, "cannot open `%s'", tmp);
86*86d7f5d3SJohn Marino     }
87*86d7f5d3SJohn Marino 
88*86d7f5d3SJohn Marino     if (getline (&repos, &repos_allocated, fpin) < 0)
89*86d7f5d3SJohn Marino     {
90*86d7f5d3SJohn Marino 	/* FIXME: should be checking for end of file separately.  */
91*86d7f5d3SJohn Marino 	error (0, 0, "in directory `%s':", xupdate_dir);
92*86d7f5d3SJohn Marino 	error (1, errno, "cannot read `%s'", CVSADM_REP);
93*86d7f5d3SJohn Marino     }
94*86d7f5d3SJohn Marino     if (fclose (fpin) < 0)
95*86d7f5d3SJohn Marino 	error (0, errno, "cannot close `%s'", tmp);
96*86d7f5d3SJohn Marino     free (tmp);
97*86d7f5d3SJohn Marino 
98*86d7f5d3SJohn Marino     if ((cp = strrchr (repos, '\n')) != NULL)
99*86d7f5d3SJohn Marino 	*cp = '\0';			/* strip the newline */
100*86d7f5d3SJohn Marino 
101*86d7f5d3SJohn Marino     /* If this is a relative repository pathname, turn it into an absolute
102*86d7f5d3SJohn Marino      * one by tacking on the current root.  There is no need to grab it from
103*86d7f5d3SJohn Marino      * the CVS/Root file via the Name_Root() function because by the time
104*86d7f5d3SJohn Marino      * this function is called, we the contents of CVS/Root have already been
105*86d7f5d3SJohn Marino      * compared to original_root and found to match.
106*86d7f5d3SJohn Marino      */
107*86d7f5d3SJohn Marino     if (!ISABSOLUTE (repos))
108*86d7f5d3SJohn Marino     {
109*86d7f5d3SJohn Marino 	char *newrepos;
110*86d7f5d3SJohn Marino 
111*86d7f5d3SJohn Marino 	if (current_parsed_root == NULL)
112*86d7f5d3SJohn Marino 	{
113*86d7f5d3SJohn Marino 	    error (0, 0, "in directory `%s:", xupdate_dir);
114*86d7f5d3SJohn Marino 	    error (0, 0, "must set the CVSROOT environment variable\n");
115*86d7f5d3SJohn Marino 	    error (0, 0, "or specify the '-d' option to `%s'.", program_name);
116*86d7f5d3SJohn Marino 	    error (1, 0, "invalid repository setting");
117*86d7f5d3SJohn Marino 	}
118*86d7f5d3SJohn Marino 	if (pathname_levels (repos) > 0)
119*86d7f5d3SJohn Marino 	{
120*86d7f5d3SJohn Marino 	    error (0, 0, "in directory `%s':", xupdate_dir);
121*86d7f5d3SJohn Marino 	    error (0, 0, "`..'-relative repositories are not supported.");
122*86d7f5d3SJohn Marino 	    error (1, 0, "invalid source repository");
123*86d7f5d3SJohn Marino 	}
124*86d7f5d3SJohn Marino 	newrepos = Xasprintf ("%s/%s", original_parsed_root->directory, repos);
125*86d7f5d3SJohn Marino 	free (repos);
126*86d7f5d3SJohn Marino 	repos = newrepos;
127*86d7f5d3SJohn Marino     }
128*86d7f5d3SJohn Marino 
129*86d7f5d3SJohn Marino     Sanitize_Repository_Name (repos);
130*86d7f5d3SJohn Marino 
131*86d7f5d3SJohn Marino     return repos;
132*86d7f5d3SJohn Marino }
133*86d7f5d3SJohn Marino 
134*86d7f5d3SJohn Marino 
135*86d7f5d3SJohn Marino 
136*86d7f5d3SJohn Marino /*
137*86d7f5d3SJohn Marino  * Return a pointer to the repository name relative to CVSROOT from a
138*86d7f5d3SJohn Marino  * possibly fully qualified repository
139*86d7f5d3SJohn Marino  */
140*86d7f5d3SJohn Marino const char *
Short_Repository(const char * repository)141*86d7f5d3SJohn Marino Short_Repository (const char *repository)
142*86d7f5d3SJohn Marino {
143*86d7f5d3SJohn Marino     if (repository == NULL)
144*86d7f5d3SJohn Marino 	return NULL;
145*86d7f5d3SJohn Marino 
146*86d7f5d3SJohn Marino     /* If repository matches CVSroot at the beginning, strip off CVSroot */
147*86d7f5d3SJohn Marino     /* And skip leading '/' in rep, in case CVSroot ended with '/'. */
148*86d7f5d3SJohn Marino     if (strncmp (original_parsed_root->directory, repository,
149*86d7f5d3SJohn Marino 		 strlen (original_parsed_root->directory)) == 0)
150*86d7f5d3SJohn Marino     {
151*86d7f5d3SJohn Marino 	const char *rep = repository + strlen (original_parsed_root->directory);
152*86d7f5d3SJohn Marino 	return (*rep == '/') ? rep+1 : rep;
153*86d7f5d3SJohn Marino     }
154*86d7f5d3SJohn Marino     else
155*86d7f5d3SJohn Marino 	return repository;
156*86d7f5d3SJohn Marino }
157*86d7f5d3SJohn Marino 
158*86d7f5d3SJohn Marino 
159*86d7f5d3SJohn Marino 
160*86d7f5d3SJohn Marino /* Sanitize the repository name (in place) by removing trailing
161*86d7f5d3SJohn Marino  * slashes and a trailing "." if present.  It should be safe for
162*86d7f5d3SJohn Marino  * callers to use strcat and friends to create repository names.
163*86d7f5d3SJohn Marino  * Without this check, names like "/path/to/repos/./foo" and
164*86d7f5d3SJohn Marino  * "/path/to/repos//foo" would be created.  For example, one
165*86d7f5d3SJohn Marino  * significant case is the CVSROOT-detection code in commit.c.  It
166*86d7f5d3SJohn Marino  * decides whether or not it needs to rebuild the administrative file
167*86d7f5d3SJohn Marino  * database by doing a string compare.  If we've done a `cvs co .' to
168*86d7f5d3SJohn Marino  * get the CVSROOT files, "/path/to/repos/./CVSROOT" and
169*86d7f5d3SJohn Marino  * "/path/to/repos/CVSROOT" are the arguments that are compared!
170*86d7f5d3SJohn Marino  *
171*86d7f5d3SJohn Marino  * This function ends up being called from the same places as
172*86d7f5d3SJohn Marino  * strip_path, though what it does is much more conservative.  Many
173*86d7f5d3SJohn Marino  * comments about this operation (which was scattered around in
174*86d7f5d3SJohn Marino  * several places in the source code) ran thus:
175*86d7f5d3SJohn Marino  *
176*86d7f5d3SJohn Marino  *    ``repository ends with "/."; omit it.  This sort of thing used
177*86d7f5d3SJohn Marino  *    to be taken care of by strip_path.  Now we try to be more
178*86d7f5d3SJohn Marino  *    selective.  I suspect that it would be even better to push it
179*86d7f5d3SJohn Marino  *    back further someday, so that the trailing "/." doesn't get into
180*86d7f5d3SJohn Marino  *    repository in the first place, but we haven't taken things that
181*86d7f5d3SJohn Marino  *    far yet.''        --Jim Kingdon (recurse.c, 07-Sep-97)
182*86d7f5d3SJohn Marino  */
183*86d7f5d3SJohn Marino 
184*86d7f5d3SJohn Marino void
Sanitize_Repository_Name(char * repository)185*86d7f5d3SJohn Marino Sanitize_Repository_Name (char *repository)
186*86d7f5d3SJohn Marino {
187*86d7f5d3SJohn Marino     size_t len;
188*86d7f5d3SJohn Marino 
189*86d7f5d3SJohn Marino     assert (repository != NULL);
190*86d7f5d3SJohn Marino 
191*86d7f5d3SJohn Marino     strip_trailing_slashes (repository);
192*86d7f5d3SJohn Marino 
193*86d7f5d3SJohn Marino     len = strlen (repository);
194*86d7f5d3SJohn Marino     if (len >= 2
195*86d7f5d3SJohn Marino 	&& repository[len - 1] == '.'
196*86d7f5d3SJohn Marino 	&& ISSLASH (repository[len - 2]))
197*86d7f5d3SJohn Marino     {
198*86d7f5d3SJohn Marino 	repository[len - 2] = '\0';
199*86d7f5d3SJohn Marino     }
200*86d7f5d3SJohn Marino }
201