Annotation of src/usr.bin/cvs/root.c, Revision 1.33
1.33 ! joris 1: /* $OpenBSD: root.c,v 1.32 2006/05/27 03:30:31 joris Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.13 tedu 4: * All rights reserved.
1.1 jfb 5: *
1.13 tedu 6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
1.1 jfb 9: *
1.13 tedu 10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.13 tedu 13: * derived from this software without specific prior written permission.
1.1 jfb 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
1.13 tedu 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 25: */
26:
1.28 xsa 27: #include "includes.h"
1.1 jfb 28:
29: #include "cvs.h"
30: #include "log.h"
31:
32: extern char *cvs_rootstr;
33:
34: /* keep these ordered with the defines */
35: const char *cvs_methods[] = {
36: "",
37: "local",
38: "ssh",
39: "pserver",
40: "kserver",
41: "gserver",
42: "ext",
43: "fork",
44: };
45:
1.20 xsa 46: #define CVS_NBMETHODS (sizeof(cvs_methods)/sizeof(cvs_methods[0]))
1.6 jfb 47:
48: /*
49: * CVSROOT cache
50: *
51: * Whenever cvsroot_parse() gets called for a specific string, it first
52: * checks in the cache to see if there is already a parsed version of the
53: * same string and returns a pointer to it in case one is found (it also
54: * increases the reference count). Otherwise, it does the parsing and adds
55: * the result to the cache for future hits.
56: */
1.22 joris 57: static TAILQ_HEAD(, cvsroot) cvs_rcache = TAILQ_HEAD_INITIALIZER(cvs_rcache);
1.25 joris 58: static void cvsroot_free(struct cvsroot *);
1.1 jfb 59:
60: /*
61: * cvsroot_parse()
62: *
63: * Parse a CVS root string (as found in CVS/Root files or the CVSROOT
64: * environment variable) and store the fields in a dynamically
65: * allocated cvs_root structure. The format of the string is as follows:
1.6 jfb 66: * :method:[[user[:pass]@host]:path
1.1 jfb 67: * Returns a pointer to the allocated information on success, or NULL
68: * on failure.
69: */
1.21 xsa 70: struct cvsroot *
1.1 jfb 71: cvsroot_parse(const char *str)
72: {
73: u_int i;
74: char *cp, *sp, *pp;
75: struct cvsroot *root;
76:
1.22 joris 77: /*
78: * Look if we have it in cache, if we found it add it to the cache
79: * at the first position again.
80: */
81: TAILQ_FOREACH(root, &cvs_rcache, root_cache) {
82: if (strcmp(str, root->cr_str) == 0) {
83: TAILQ_REMOVE(&cvs_rcache, root, root_cache);
84: TAILQ_INSERT_HEAD(&cvs_rcache, root, root_cache);
85: root->cr_ref++;
86: return (root);
1.6 jfb 87: }
88: }
89:
1.31 ray 90: root = xcalloc(1, sizeof(*root));
1.15 jfb 91: root->cr_ref = 1;
1.1 jfb 92: root->cr_method = CVS_METHOD_NONE;
1.10 jfb 93: CVS_RSTVR(root);
1.1 jfb 94:
1.26 joris 95: root->cr_str = xstrdup(str);
96: root->cr_buf = xstrdup(str);
1.1 jfb 97:
98: sp = root->cr_buf;
99: cp = root->cr_buf;
100: if (*sp == ':') {
101: sp++;
1.27 xsa 102: if ((cp = strchr(sp, ':')) == NULL)
103: fatal("failed to parse CVSROOT: unterminated method");
104:
1.1 jfb 105: *(cp++) = '\0';
106:
107: for (i = 0; i < CVS_NBMETHODS; i++) {
108: if (strcmp(sp, cvs_methods[i]) == 0) {
109: root->cr_method = i;
110: break;
111: }
1.14 jfb 112: }
1.27 xsa 113: if (i == CVS_NBMETHODS)
114: fatal("cvsroot_parse: unknown method `%s'", sp);
1.1 jfb 115: }
116:
117: /* find the start of the actual path */
1.27 xsa 118: if ((sp = strchr(cp, '/')) == NULL)
119: fatal("no path specification in CVSROOT");
1.1 jfb 120:
121: root->cr_dir = sp;
1.32 joris 122: STRIP_SLASH(root->cr_dir);
1.1 jfb 123: if (sp == cp) {
124: if (root->cr_method == CVS_METHOD_NONE)
125: root->cr_method = CVS_METHOD_LOCAL;
126: /* stop here, it's just a path */
1.24 joris 127: TAILQ_INSERT_HEAD(&cvs_rcache, root, root_cache);
1.1 jfb 128: return (root);
129: }
130:
1.27 xsa 131: if (*(sp - 1) != ':')
132: fatal("missing host/path delimiter in CVSROOT");
133:
1.1 jfb 134: *(sp - 1) = '\0';
135:
136: /*
137: * looks like we have more than just a directory path, so
138: * attempt to split it into user and host parts
139: */
140: sp = strchr(cp, '@');
141: if (sp != NULL) {
142: *(sp++) = '\0';
143:
144: /* password ? */
145: pp = strchr(cp, ':');
146: if (pp != NULL) {
147: *(pp++) = '\0';
148: root->cr_pass = pp;
149: }
150:
151: root->cr_user = cp;
1.12 deraadt 152: } else
1.11 jfb 153: sp = cp;
1.1 jfb 154:
155: pp = strchr(sp, ':');
156: if (pp != NULL) {
157: *(pp++) = '\0';
158: root->cr_port = (u_int)strtol(pp, &cp, 10);
1.27 xsa 159: if ((*cp != '\0') || (root->cr_port > 65535))
160: fatal("invalid port specification in CVSROOT");
1.1 jfb 161:
162: }
163:
164: root->cr_host = sp;
165:
166: if (root->cr_method == CVS_METHOD_NONE) {
167: /* no method found from start of CVSROOT, guess */
168: if (root->cr_host != NULL)
169: root->cr_method = CVS_METHOD_SERVER;
170: else
171: root->cr_method = CVS_METHOD_LOCAL;
172: }
173:
1.6 jfb 174: /* add to the cache */
1.22 joris 175: TAILQ_INSERT_HEAD(&cvs_rcache, root, root_cache);
1.1 jfb 176: return (root);
177: }
178:
179: /*
1.25 joris 180: * cvsroot_remove()
1.1 jfb 181: *
1.25 joris 182: * Remove a CVSROOT structure from the cache, and free it.
1.1 jfb 183: */
184: void
1.25 joris 185: cvsroot_remove(struct cvsroot *root)
1.1 jfb 186: {
1.6 jfb 187: root->cr_ref--;
188: if (root->cr_ref == 0) {
1.22 joris 189: TAILQ_REMOVE(&cvs_rcache, root, root_cache);
1.25 joris 190: cvsroot_free(root);
1.6 jfb 191: }
1.1 jfb 192: }
193:
1.25 joris 194: /*
195: * cvsroot_free()
196: *
197: * Free a CVSROOT structure previously allocated and returned by
198: * cvsroot_parse().
199: */
200: static void
201: cvsroot_free(struct cvsroot *root)
202: {
203: if (root->cr_str != NULL)
1.26 joris 204: xfree(root->cr_str);
1.25 joris 205: if (root->cr_buf != NULL)
1.26 joris 206: xfree(root->cr_buf);
1.25 joris 207: if (root->cr_version != NULL)
1.26 joris 208: xfree(root->cr_version);
209: xfree(root);
1.25 joris 210: }
1.1 jfb 211:
212: /*
213: * cvsroot_get()
214: *
215: * Get the CVSROOT information for a specific directory <dir>. The
216: * value is taken from one of 3 possible sources (in order of precedence):
217: *
218: * 1) the `-d' command-line option
219: * 2) the CVS/Root file found in checked-out trees
220: * 3) the CVSROOT environment variable
221: */
1.21 xsa 222: struct cvsroot *
1.1 jfb 223: cvsroot_get(const char *dir)
224: {
225: size_t len;
1.5 jfb 226: char rootpath[MAXPATHLEN], *rootstr, line[128];
1.1 jfb 227: FILE *fp;
228:
229: if (cvs_rootstr != NULL)
230: return cvsroot_parse(cvs_rootstr);
231:
1.29 xsa 232: if (strlcpy(rootpath, dir, sizeof(rootpath)) >= sizeof(rootpath) ||
233: strlcat(rootpath, "/", sizeof(rootpath)) >= sizeof(rootpath) ||
234: strlcat(rootpath, CVS_PATH_ROOTSPEC,
235: sizeof(rootpath)) >= sizeof(rootpath)) {
1.16 xsa 236: errno = ENAMETOOLONG;
1.27 xsa 237: fatal("cvsroot_get: %s: %s", rootpath, strerror(errno));
1.16 xsa 238: }
239:
1.27 xsa 240: if ((fp = fopen(rootpath, "r")) == NULL) {
1.1 jfb 241: if (errno == ENOENT) {
242: /* try env as a last resort */
243: if ((rootstr = getenv("CVSROOT")) != NULL)
244: return cvsroot_parse(rootstr);
245: else
1.32 joris 246: return (NULL);
1.12 deraadt 247: } else {
1.27 xsa 248: fatal("cvsroot_get: fopen: `%s': %s",
249: CVS_PATH_ROOTSPEC, strerror(errno));
1.1 jfb 250: }
251: }
252:
1.27 xsa 253: if (fgets(line, (int)sizeof(line), fp) == NULL)
254: fatal("cvsroot_get: fgets: `%s'", CVS_PATH_ROOTSPEC);
255:
1.5 jfb 256: (void)fclose(fp);
1.1 jfb 257:
1.5 jfb 258: len = strlen(line);
1.13 tedu 259: if (len == 0)
1.32 joris 260: cvs_log(LP_ERR, "empty %s file", CVS_PATH_ROOTSPEC);
1.13 tedu 261: else if (line[len - 1] == '\n')
1.5 jfb 262: line[--len] = '\0';
1.1 jfb 263:
1.5 jfb 264: return cvsroot_parse(line);
1.1 jfb 265: }