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