xref: /openbsd-src/usr.bin/cvs/root.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /*	$OpenBSD: root.c,v 1.45 2008/06/20 23:00:13 tobias Exp $	*/
2 /*
3  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include "cvs.h"
32 
33 extern char *cvs_rootstr;
34 
35 /* keep these ordered with the defines */
36 const char *cvs_methods[] = {
37 	"",
38 	"local",
39 	"ssh",
40 	"pserver",
41 	"kserver",
42 	"gserver",
43 	"ext",
44 	"fork",
45 };
46 
47 #define CVS_NBMETHODS	(sizeof(cvs_methods)/sizeof(cvs_methods[0]))
48 
49 /*
50  * cvsroot_parse()
51  *
52  * Parse a CVS root string (as found in CVS/Root files or the CVSROOT
53  * environment variable) and store the fields in a dynamically
54  * allocated cvs_root structure.  The format of the string is as follows:
55  *	[:method:][[user[:pass]@]host[:port]:]path
56  * Returns a pointer to the allocated information on success, or NULL
57  * on failure.
58  */
59 static struct cvsroot *
60 cvsroot_parse(const char *str)
61 {
62 	u_int i;
63 	char *cp, *sp, *pp;
64 	const char *errstr;
65 	static struct cvsroot *root = NULL;
66 
67 	if (root != NULL)
68 		return (root);
69 
70 	root = xcalloc(1, sizeof(*root));
71 	root->cr_method = CVS_METHOD_NONE;
72 	CVS_RSTVR(root);
73 
74 	root->cr_str = xstrdup(str);
75 	root->cr_buf = xstrdup(str);
76 
77 	sp = root->cr_buf;
78 	cp = root->cr_buf;
79 	if (*sp == ':') {
80 		sp++;
81 		if ((cp = strchr(sp, ':')) == NULL)
82 			fatal("failed to parse CVSROOT: unterminated method");
83 
84 		*(cp++) = '\0';
85 
86 		for (i = 0; i < CVS_NBMETHODS; i++) {
87 			if (strcmp(sp, cvs_methods[i]) == 0) {
88 				root->cr_method = i;
89 				break;
90 			}
91 		}
92 		if (i == CVS_NBMETHODS)
93 			fatal("cvsroot_parse: unknown method `%s'", sp);
94 	}
95 
96 	/* find the start of the actual path */
97 	if ((sp = strchr(cp, '/')) == NULL)
98 		fatal("no path specification in CVSROOT");
99 
100 	root->cr_dir = sp;
101 	STRIP_SLASH(root->cr_dir);
102 	if (sp == cp) {
103 		if (root->cr_method == CVS_METHOD_NONE)
104 			root->cr_method = CVS_METHOD_LOCAL;
105 		/* stop here, it's just a path */
106 		return (root);
107 	}
108 
109 	if (*(sp - 1) != ':')
110 		fatal("missing host/path delimiter in CVSROOT");
111 
112 	*(sp - 1) = '\0';
113 
114 	/*
115 	 * looks like we have more than just a directory path, so
116 	 * attempt to split it into user and host parts
117 	 */
118 	sp = strchr(cp, '@');
119 	if (sp != NULL) {
120 		*(sp++) = '\0';
121 
122 		/* password ? */
123 		pp = strchr(cp, ':');
124 		if (pp != NULL) {
125 			*(pp++) = '\0';
126 			root->cr_pass = pp;
127 		}
128 
129 		root->cr_user = cp;
130 	} else
131 		sp = cp;
132 
133 	pp = strchr(sp, ':');
134 	if (pp != NULL) {
135 		*(pp++) = '\0';
136 		root->cr_port = strtonum(pp, 1, 65535, &errstr);
137 		if (errstr != NULL)
138 			fatal("port specification in CVSROOT is %s", errstr);
139 
140 	}
141 
142 	root->cr_host = sp;
143 
144 	if (root->cr_method == CVS_METHOD_NONE) {
145 		/* no method found from start of CVSROOT, guess */
146 		if (root->cr_host != NULL)
147 			root->cr_method = CVS_METHOD_SERVER;
148 		else
149 			root->cr_method = CVS_METHOD_LOCAL;
150 	}
151 
152 	return (root);
153 }
154 
155 /*
156  * cvsroot_get()
157  *
158  * Get the CVSROOT information for a specific directory <dir>.  The
159  * value is taken from one of 3 possible sources (in order of precedence):
160  *
161  * 1) the `-d' command-line option
162  * 2) the CVS/Root file found in checked-out trees
163  * 3) the CVSROOT environment variable
164  */
165 struct cvsroot *
166 cvsroot_get(const char *dir)
167 {
168 	char rootpath[MAXPATHLEN], *rootstr, line[128];
169 	FILE *fp;
170 
171 	if (cvs_rootstr != NULL)
172 		return cvsroot_parse(cvs_rootstr);
173 
174 	if (cvs_server_active == 1)
175 		return cvsroot_parse(dir);
176 
177 	if (cvs_cmdop == CVS_OP_IMPORT)
178 		return NULL;
179 
180 	(void)xsnprintf(rootpath, MAXPATHLEN, "%s/%s", dir, CVS_PATH_ROOTSPEC);
181 
182 	if ((fp = fopen(rootpath, "r")) == NULL) {
183 		if (errno == ENOENT) {
184 			/* try env as a last resort */
185 			if ((rootstr = getenv("CVSROOT")) != NULL)
186 				return cvsroot_parse(rootstr);
187 			else
188 				return (NULL);
189 		} else {
190 			fatal("cvsroot_get: fopen: `%s': %s",
191 			    CVS_PATH_ROOTSPEC, strerror(errno));
192 		}
193 	}
194 
195 	if (fgets(line, (int)sizeof(line), fp) == NULL)
196 		fatal("cvsroot_get: fgets: `%s'", CVS_PATH_ROOTSPEC);
197 
198 	(void)fclose(fp);
199 
200 	line[strcspn(line, "\n")] = '\0';
201 	if (line[0] == '\0')
202 		cvs_log(LP_ERR, "empty %s file", CVS_PATH_ROOTSPEC);
203 
204 	return cvsroot_parse(line);
205 }
206