Annotation of src/usr.bin/cvs/root.c, Revision 1.4
1.1 jfb 1: /* $OpenBSD$ */
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 <sys/types.h>
28:
29: #include <stdlib.h>
30: #include <stdio.h>
31: #include <unistd.h>
32: #include <err.h>
33: #include <errno.h>
34: #include <string.h>
35: #include <paths.h>
36:
37: #include "cvs.h"
38: #include "log.h"
39:
40:
41: extern char *cvs_rootstr;
42:
43:
44: /* keep these ordered with the defines */
45: const char *cvs_methods[] = {
46: "",
47: "local",
48: "ssh",
49: "pserver",
50: "kserver",
51: "gserver",
52: "ext",
53: "fork",
54: };
55:
56: #define CVS_NBMETHODS (sizeof(cvs_methods)/sizeof(cvs_methods[0]))
57:
58:
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:
66: * :method:path
67: * Returns a pointer to the allocated information on success, or NULL
68: * on failure.
69: */
70:
71: struct cvsroot*
72: cvsroot_parse(const char *str)
73: {
74: u_int i;
75: char *cp, *sp, *pp;
76: struct cvsroot *root;
77:
78: root = (struct cvsroot *)malloc(sizeof(*root));
79: if (root == NULL) {
80: cvs_log(LP_ERRNO, "failed to allocate CVS root data");
81: return (NULL);
82: }
1.4 ! jfb 83: memset(root, 0, sizeof(*root));
1.1 jfb 84:
85: root->cr_method = CVS_METHOD_NONE;
86:
87: root->cr_buf = strdup(str);
88: if (root->cr_buf == NULL) {
89: cvs_log(LP_ERRNO, "failed to copy CVS root");
90: free(root);
91: return (NULL);
92: }
93:
94: sp = root->cr_buf;
95: cp = root->cr_buf;
96:
97: if (*sp == ':') {
98: sp++;
99: cp = strchr(sp, ':');
100: if (cp == NULL) {
101: cvs_log(LP_ERR, "failed to parse CVSROOT: "
102: "unterminated method");
103: free(root->cr_buf);
104: free(root);
105: return (NULL);
106: }
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: }
114: }
115: }
116:
117: /* find the start of the actual path */
118: sp = strchr(cp, '/');
119: if (sp == NULL) {
120: cvs_log(LP_ERR, "no path specification in CVSROOT");
121: free(root->cr_buf);
122: free(root);
123: return (NULL);
124: }
125:
126: root->cr_dir = sp;
127: if (sp == cp) {
128: if (root->cr_method == CVS_METHOD_NONE)
129: root->cr_method = CVS_METHOD_LOCAL;
130: /* stop here, it's just a path */
131: return (root);
132: }
133:
134: if (*(sp - 1) != ':') {
135: cvs_log(LP_ERR, "missing host/path delimiter in CVS root");
136: free(root);
137: return (NULL);
138: }
139: *(sp - 1) = '\0';
140:
141: /*
142: * looks like we have more than just a directory path, so
143: * attempt to split it into user and host parts
144: */
145: sp = strchr(cp, '@');
146: if (sp != NULL) {
147: *(sp++) = '\0';
148:
149: /* password ? */
150: pp = strchr(cp, ':');
151: if (pp != NULL) {
152: *(pp++) = '\0';
153: root->cr_pass = pp;
154: }
155:
156: root->cr_user = cp;
157: }
158:
159: pp = strchr(sp, ':');
160: if (pp != NULL) {
161: *(pp++) = '\0';
162: root->cr_port = (u_int)strtol(pp, &cp, 10);
163: if (*cp != '\0' || root->cr_port > 65535) {
164: cvs_log(LP_ERR,
165: "invalid port specification in CVSROOT");
166: free(root);
167: return (NULL);
168: }
169:
170: }
171:
172: root->cr_host = sp;
173:
174: if (root->cr_method == CVS_METHOD_NONE) {
175: /* no method found from start of CVSROOT, guess */
176: if (root->cr_host != NULL)
177: root->cr_method = CVS_METHOD_SERVER;
178: else
179: root->cr_method = CVS_METHOD_LOCAL;
180: }
181:
182: return (root);
183: }
184:
185:
186: /*
187: * cvsroot_free()
188: *
189: * Free a CVSROOT structure previously allocated and returned by
190: * cvsroot_parse().
191: */
192:
193: void
194: cvsroot_free(struct cvsroot *root)
195: {
196: free(root->cr_buf);
197: free(root);
198: }
199:
200:
201: /*
202: * cvsroot_get()
203: *
204: * Get the CVSROOT information for a specific directory <dir>. The
205: * value is taken from one of 3 possible sources (in order of precedence):
206: *
207: * 1) the `-d' command-line option
208: * 2) the CVS/Root file found in checked-out trees
209: * 3) the CVSROOT environment variable
210: */
211:
212: struct cvsroot*
213: cvsroot_get(const char *dir)
214: {
215: size_t len;
216: char rootpath[MAXPATHLEN], *rootstr, *line;
217: FILE *fp;
218: struct cvsroot *rp;
219:
220: if (cvs_rootstr != NULL)
221: return cvsroot_parse(cvs_rootstr);
222:
223: snprintf(rootpath, sizeof(rootpath), "%s/" CVS_PATH_ROOTSPEC, dir);
224: fp = fopen(rootpath, "r");
225: if (fp == NULL) {
226: if (errno == ENOENT) {
227: /* try env as a last resort */
228: if ((rootstr = getenv("CVSROOT")) != NULL)
229: return cvsroot_parse(rootstr);
230: else
231: return (NULL);
232: }
233: else {
234: cvs_log(LP_ERRNO, "failed to open CVS/Root");
235: return (NULL);
236: }
237: }
238:
239: line = fgetln(fp, &len);
240: if (line == NULL) {
241: cvs_log(LP_ERR, "failed to read CVSROOT line from CVS/Root");
242: (void)fclose(fp);
243: }
244:
245: /* line is not NUL-terminated, but we don't need to allocate an
246: * extra byte because we don't want the trailing newline. It will
247: * get replaced by a \0.
248: */
249: rootstr = (char *)malloc(len);
250: if (rootstr == NULL) {
251: cvs_log(LP_ERRNO, "failed to allocate CVSROOT string");
252: (void)fclose(fp);
253: return (NULL);
254: }
1.3 jfb 255: memcpy(rootstr, line, len - 1);
256: rootstr[len - 1] = '\0';
1.1 jfb 257: rp = cvsroot_parse(rootstr);
258:
259: (void)fclose(fp);
260: free(rootstr);
261:
262: return (rp);
263: }