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