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