Thu Mar 8 20:44:06 2012 UTC ()
add acl function prototype


(christos)
diff -r1.2 -r1.3 src/external/gpl2/xcvs/dist/src/parseinfo.c

cvs diff -r1.2 -r1.3 src/external/gpl2/xcvs/dist/src/parseinfo.c (switch to unified diff)

--- src/external/gpl2/xcvs/dist/src/parseinfo.c 2009/04/08 16:27:51 1.2
+++ src/external/gpl2/xcvs/dist/src/parseinfo.c 2012/03/08 20:44:06 1.3
@@ -1,733 +1,921 @@ @@ -1,733 +1,921 @@
1/* 1/*
2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3 * 3 *
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5 * and others. 5 * and others.
6 * 6 *
7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (C) 1989-1992, Brian Berliner 8 * Portions Copyright (C) 1989-1992, Brian Berliner
9 *  9 *
10 * You may distribute under the terms of the GNU General Public License as 10 * You may distribute under the terms of the GNU General Public License as
11 * specified in the README file that comes with the CVS source distribution. 11 * specified in the README file that comes with the CVS source distribution.
12 */ 12 */
13 13
14#include "cvs.h" 14#include "cvs.h"
15#include "getline.h" 15#include "getline.h"
16#include "history.h" 16#include "history.h"
17 17
18/* 18/*
19 * Parse the INFOFILE file for the specified REPOSITORY. Invoke CALLPROC for 19 * Parse the INFOFILE file for the specified REPOSITORY. Invoke CALLPROC for
20 * the first line in the file that matches the REPOSITORY, or if ALL != 0, any 20 * the first line in the file that matches the REPOSITORY, or if ALL != 0, any
21 * lines matching "ALL", or if no lines match, the last line matching 21 * lines matching "ALL", or if no lines match, the last line matching
22 * "DEFAULT". 22 * "DEFAULT".
23 * 23 *
24 * Return 0 for success, -1 if there was not an INFOFILE, and >0 for failure. 24 * Return 0 for success, -1 if there was not an INFOFILE, and >0 for failure.
25 */ 25 */
26int 26int
27Parse_Info (const char *infofile, const char *repository, CALLPROC callproc, 27Parse_Info (const char *infofile, const char *repository, CALLPROC callproc,
28 int opt, void *closure) 28 int opt, void *closure)
29{ 29{
30 int err = 0; 30 int err = 0;
31 FILE *fp_info; 31 FILE *fp_info;
32 char *infopath; 32 char *infopath;
33 char *line = NULL; 33 char *line = NULL;
34 size_t line_allocated = 0; 34 size_t line_allocated = 0;
35 char *default_value = NULL; 35 char *default_value = NULL;
36 int default_line = 0; 36 int default_line = 0;
37 char *expanded_value; 37 char *expanded_value;
38 bool callback_done; 38 bool callback_done;
39 int line_number; 39 int line_number;
40 char *cp, *exp, *value; 40 char *cp, *exp, *value;
41 const char *srepos; 41 const char *srepos;
42 const char *regex_err; 42 const char *regex_err;
43 43
44 assert (repository); 44 assert (repository);
45 45
46 if (!current_parsed_root) 46 if (!current_parsed_root)
47 { 47 {
48 /* XXX - should be error maybe? */ 48 /* XXX - should be error maybe? */
49 error (0, 0, "CVSROOT variable not set"); 49 error (0, 0, "CVSROOT variable not set");
50 return 1; 50 return 1;
51 } 51 }
52 52
53 /* find the info file and open it */ 53 /* find the info file and open it */
54 infopath = Xasprintf ("%s/%s/%s", current_parsed_root->directory, 54 infopath = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
55 CVSROOTADM, infofile); 55 CVSROOTADM, infofile);
56 fp_info = CVS_FOPEN (infopath, "r"); 56 fp_info = CVS_FOPEN (infopath, "r");
57 if (!fp_info) 57 if (!fp_info)
58 { 58 {
59 /* If no file, don't do anything special. */ 59 /* If no file, don't do anything special. */
60 if (!existence_error (errno)) 60 if (!existence_error (errno))
61 error (0, errno, "cannot open %s", infopath); 61 error (0, errno, "cannot open %s", infopath);
62 free (infopath); 62 free (infopath);
63 return 0; 63 return 0;
64 } 64 }
65 65
66 /* strip off the CVSROOT if repository was absolute */ 66 /* strip off the CVSROOT if repository was absolute */
67 srepos = Short_Repository (repository); 67 srepos = Short_Repository (repository);
68 68
69 TRACE (TRACE_FUNCTION, "Parse_Info (%s, %s, %s)", 69 TRACE (TRACE_FUNCTION, "Parse_Info (%s, %s, %s)",
70 infopath, srepos, (opt & PIOPT_ALL) ? "ALL" : "not ALL"); 70 infopath, srepos, (opt & PIOPT_ALL) ? "ALL" : "not ALL");
71 71
72 /* search the info file for lines that match */ 72 /* search the info file for lines that match */
73 callback_done = false; 73 callback_done = false;
74 line_number = 0; 74 line_number = 0;
75 while (getline (&line, &line_allocated, fp_info) >= 0) 75 while (getline (&line, &line_allocated, fp_info) >= 0)
76 { 76 {
77 line_number++; 77 line_number++;
78 78
79 /* skip lines starting with # */ 79 /* skip lines starting with # */
80 if (line[0] == '#') 80 if (line[0] == '#')
81 continue; 81 continue;
82 82
83 /* skip whitespace at beginning of line */ 83 /* skip whitespace at beginning of line */
84 for (cp = line; *cp && isspace ((unsigned char) *cp); cp++) 84 for (cp = line; *cp && isspace ((unsigned char) *cp); cp++)
85 ; 85 ;
86 86
87 /* if *cp is null, the whole line was blank */ 87 /* if *cp is null, the whole line was blank */
88 if (*cp == '\0') 88 if (*cp == '\0')
89 continue; 89 continue;
90 90
91 /* the regular expression is everything up to the first space */ 91 /* the regular expression is everything up to the first space */
92 for (exp = cp; *cp && !isspace ((unsigned char) *cp); cp++) 92 for (exp = cp; *cp && !isspace ((unsigned char) *cp); cp++)
93 ; 93 ;
94 if (*cp != '\0') 94 if (*cp != '\0')
95 *cp++ = '\0'; 95 *cp++ = '\0';
96 96
97 /* skip whitespace up to the start of the matching value */ 97 /* skip whitespace up to the start of the matching value */
98 while (*cp && isspace ((unsigned char) *cp)) 98 while (*cp && isspace ((unsigned char) *cp))
99 cp++; 99 cp++;
100 100
101 /* no value to match with the regular expression is an error */ 101 /* no value to match with the regular expression is an error */
102 if (*cp == '\0') 102 if (*cp == '\0')
103 { 103 {
104 error (0, 0, "syntax error at line %d file %s; ignored", 104 error (0, 0, "syntax error at line %d file %s; ignored",
105 line_number, infopath); 105 line_number, infopath);
106 continue; 106 continue;
107 } 107 }
108 value = cp; 108 value = cp;
109 109
110 /* strip the newline off the end of the value */ 110 /* strip the newline off the end of the value */
111 cp = strrchr (value, '\n'); 111 cp = strrchr (value, '\n');
112 if (cp) *cp = '\0'; 112 if (cp) *cp = '\0';
113 113
114 /* 114 /*
115 * At this point, exp points to the regular expression, and value 115 * At this point, exp points to the regular expression, and value
116 * points to the value to call the callback routine with. Evaluate 116 * points to the value to call the callback routine with. Evaluate
117 * the regular expression against srepos and callback with the value 117 * the regular expression against srepos and callback with the value
118 * if it matches. 118 * if it matches.
119 */ 119 */
120 120
121 /* save the default value so we have it later if we need it */ 121 /* save the default value so we have it later if we need it */
122 if (strcmp (exp, "DEFAULT") == 0) 122 if (strcmp (exp, "DEFAULT") == 0)
123 { 123 {
124 if (default_value) 124 if (default_value)
125 { 125 {
126 error (0, 0, "Multiple `DEFAULT' lines (%d and %d) in %s file", 126 error (0, 0, "Multiple `DEFAULT' lines (%d and %d) in %s file",
127 default_line, line_number, infofile); 127 default_line, line_number, infofile);
128 free (default_value); 128 free (default_value);
129 } 129 }
130 default_value = xstrdup (value); 130 default_value = xstrdup (value);
131 default_line = line_number; 131 default_line = line_number;
132 continue; 132 continue;
133 } 133 }
134 134
135 /* 135 /*
136 * For a regular expression of "ALL", do the callback always We may 136 * For a regular expression of "ALL", do the callback always We may
137 * execute lots of ALL callbacks in addition to *one* regular matching 137 * execute lots of ALL callbacks in addition to *one* regular matching
138 * callback or default 138 * callback or default
139 */ 139 */
140 if (strcmp (exp, "ALL") == 0) 140 if (strcmp (exp, "ALL") == 0)
141 { 141 {
142 if (!(opt & PIOPT_ALL)) 142 if (!(opt & PIOPT_ALL))
143 error (0, 0, "Keyword `ALL' is ignored at line %d in %s file", 143 error (0, 0, "Keyword `ALL' is ignored at line %d in %s file",
144 line_number, infofile); 144 line_number, infofile);
145 else if ((expanded_value = 145 else if ((expanded_value =
146 expand_path (value, current_parsed_root->directory, 146 expand_path (value, current_parsed_root->directory,
147 true, infofile, line_number))) 147 true, infofile, line_number)))
148 { 148 {
149 err += callproc (repository, expanded_value, closure); 149 err += callproc (repository, expanded_value, closure);
150 free (expanded_value); 150 free (expanded_value);
151 } 151 }
152 else 152 else
153 err++; 153 err++;
154 continue; 154 continue;
155 } 155 }
156 156
157 if (callback_done) 157 if (callback_done)
158 /* only first matching, plus "ALL"'s */ 158 /* only first matching, plus "ALL"'s */
159 continue; 159 continue;
160 160
161 /* see if the repository matched this regular expression */ 161 /* see if the repository matched this regular expression */
162 regex_err = re_comp (exp); 162 regex_err = re_comp (exp);
163 if (regex_err) 163 if (regex_err)
164 { 164 {
165 error (0, 0, "bad regular expression at line %d file %s: %s", 165 error (0, 0, "bad regular expression at line %d file %s: %s",
166 line_number, infofile, regex_err); 166 line_number, infofile, regex_err);
167 continue; 167 continue;
168 } 168 }
169 if (re_exec (srepos) == 0) 169 if (re_exec (srepos) == 0)
170 continue; /* no match */ 170 continue; /* no match */
171 171
172 /* it did, so do the callback and note that we did one */ 172 /* it did, so do the callback and note that we did one */
173 expanded_value = expand_path (value, current_parsed_root->directory, 173 expanded_value = expand_path (value, current_parsed_root->directory,
174 true, infofile, line_number); 174 true, infofile, line_number);
175 if (expanded_value) 175 if (expanded_value)
176 { 176 {
177 err += callproc (repository, expanded_value, closure); 177 err += callproc (repository, expanded_value, closure);
178 free (expanded_value); 178 free (expanded_value);
179 } 179 }
180 else 180 else
181 err++; 181 err++;
182 callback_done = true; 182 callback_done = true;
183 } 183 }
184 if (ferror (fp_info)) 184 if (ferror (fp_info))
185 error (0, errno, "cannot read %s", infopath); 185 error (0, errno, "cannot read %s", infopath);
186 if (fclose (fp_info) < 0) 186 if (fclose (fp_info) < 0)
187 error (0, errno, "cannot close %s", infopath); 187 error (0, errno, "cannot close %s", infopath);
188 188
189 /* if we fell through and didn't callback at all, do the default */ 189 /* if we fell through and didn't callback at all, do the default */
190 if (!callback_done && default_value) 190 if (!callback_done && default_value)
191 { 191 {
192 expanded_value = expand_path (default_value, 192 expanded_value = expand_path (default_value,
193 current_parsed_root->directory, 193 current_parsed_root->directory,
194 true, infofile, line_number); 194 true, infofile, line_number);
195 if (expanded_value) 195 if (expanded_value)
196 { 196 {
197 err += callproc (repository, expanded_value, closure); 197 err += callproc (repository, expanded_value, closure);
198 free (expanded_value); 198 free (expanded_value);
199 } 199 }
200 else 200 else
201 err++; 201 err++;
202 } 202 }
203 203
204 /* free up space if necessary */ 204 /* free up space if necessary */
205 if (default_value) free (default_value); 205 if (default_value) free (default_value);
206 free (infopath); 206 free (infopath);
207 if (line) free (line); 207 if (line) free (line);
208 208
209 return err; 209 return err;
210} 210}
211 211
212 212
213 213
214/* Print a warning and return false if P doesn't look like a string specifying 214/* Print a warning and return false if P doesn't look like a string specifying
215 * something that can be converted into a size_t. 215 * something that can be converted into a size_t.
216 * 216 *
217 * Sets *VAL to the parsed value when it is found to be valid. *VAL will not 217 * Sets *VAL to the parsed value when it is found to be valid. *VAL will not
218 * be altered when false is returned. 218 * be altered when false is returned.
219 */ 219 */
220static bool 220static bool
221readSizeT (const char *infopath, const char *option, const char *p, 221readSizeT (const char *infopath, const char *option, const char *p,
222 size_t *val) 222 size_t *val)
223{ 223{
224 const char *q; 224 const char *q;
225 size_t num, factor = 1; 225 size_t num, factor = 1;
226 226
227 if (!strcasecmp ("unlimited", p)) 227 if (!strcasecmp ("unlimited", p))
228 { 228 {
229 *val = SIZE_MAX; 229 *val = SIZE_MAX;
230 return true; 230 return true;
231 } 231 }
232 232
233 /* Record the factor character (kilo, mega, giga, tera). */ 233 /* Record the factor character (kilo, mega, giga, tera). */
234 if (!isdigit (p[strlen(p) - 1])) 234 if (!isdigit (p[strlen(p) - 1]))
235 { 235 {
236 switch (p[strlen(p) - 1]) 236 switch (p[strlen(p) - 1])
237 { 237 {
238 case 'T': 238 case 'T':
239 factor = xtimes (factor, 1024); 239 factor = xtimes (factor, 1024);
240 case 'G': 240 case 'G':
241 factor = xtimes (factor, 1024); 241 factor = xtimes (factor, 1024);
242 case 'M': 242 case 'M':
243 factor = xtimes (factor, 1024); 243 factor = xtimes (factor, 1024);
244 case 'k': 244 case 'k':
245 factor = xtimes (factor, 1024); 245 factor = xtimes (factor, 1024);
246 break; 246 break;
247 default: 247 default:
248 error (0, 0, 248 error (0, 0,
249 "%s: Unknown %s factor: `%c'", 249 "%s: Unknown %s factor: `%c'",
250 infopath, option, p[strlen(p)]); 250 infopath, option, p[strlen(p)]);
251 return false; 251 return false;
252 } 252 }
253 TRACE (TRACE_DATA, "readSizeT(): Found factor %u for %s", 253 TRACE (TRACE_DATA, "readSizeT(): Found factor %zu for %s",
254 factor, option); 254 factor, option);
255 } 255 }
256 256
257 /* Verify that *q is a number. */ 257 /* Verify that *q is a number. */
258 q = p; 258 q = p;
259 while (q < p + strlen(p) - 1 /* Checked last character above. */) 259 while (q < p + strlen(p) - 1 /* Checked last character above. */)
260 { 260 {
261 if (!isdigit(*q)) 261 if (!isdigit(*q))
262 { 262 {
263 error (0, 0, 263 error (0, 0,
264"%s: %s must be a postitive integer, not '%s'", 264"%s: %s must be a postitive integer, not '%s'",
265 infopath, option, p); 265 infopath, option, p);
266 return false; 266 return false;
267 } 267 }
268 q++; 268 q++;
269 } 269 }
270 270
271 /* Compute final value. */ 271 /* Compute final value. */
272 num = strtoul (p, NULL, 10); 272 num = strtoul (p, NULL, 10);
273 if (num == ULONG_MAX || num > SIZE_MAX) 273 if (num == ULONG_MAX || num > SIZE_MAX)
274 /* Don't return an error, just max out. */ 274 /* Don't return an error, just max out. */
275 num = SIZE_MAX; 275 num = SIZE_MAX;
276 276
277 TRACE (TRACE_DATA, "readSizeT(): read number %u for %s", num, option); 277 TRACE (TRACE_DATA, "readSizeT(): read number %zu for %s", num, option);
278 *val = xtimes (strtoul (p, NULL, 10), factor); 278 *val = xtimes (strtoul (p, NULL, 10), factor);
279 TRACE (TRACE_DATA, "readSizeT(): returnning %u for %s", *val, option); 279 TRACE (TRACE_DATA, "readSizeT(): returnning %zu for %s", *val, option);
280 return true; 280 return true;
281} 281}
282 282
283 283
284 284
285/* Allocate and initialize a new config struct. */ 285/* Allocate and initialize a new config struct. */
286static inline struct config * 286static inline struct config *
287new_config (void) 287new_config (void)
288{ 288{
289 struct config *new = xcalloc (1, sizeof (struct config)); 289 struct config *new = xcalloc (1, sizeof (struct config));
290 290
291 TRACE (TRACE_FLOW, "new_config ()"); 291 TRACE (TRACE_FLOW, "new_config ()");
292 292
293 new->logHistory = xstrdup (ALL_HISTORY_REC_TYPES); 293 new->logHistory = xstrdup (ALL_HISTORY_REC_TYPES);
294 new->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS; 294 new->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
295 new->UserAdminOptions = xstrdup ("k"); 295 new->UserAdminOptions = xstrdup ("k");
296#ifdef CVS_ADMIN_GROUP 296#ifdef CVS_ADMIN_GROUP
297 new->UserAdminGroup = xstrdup (CVS_ADMIN_GROUP); 297 new->UserAdminGroup = xstrdup (CVS_ADMIN_GROUP);
298#else 298#else
299 new->UserAdminGroup = NULL; 299 new->UserAdminGroup = NULL;
300#endif 300#endif
301 new->MaxCommentLeaderLength = 20; 301 new->MaxCommentLeaderLength = 20;
302#ifdef SERVER_SUPPORT 302#ifdef SERVER_SUPPORT
303 new->MaxCompressionLevel = 9; 303 new->MaxCompressionLevel = 9;
304#endif /* SERVER_SUPPORT */ 304#endif /* SERVER_SUPPORT */
305#ifdef PROXY_SUPPORT 305#ifdef PROXY_SUPPORT
306 new->MaxProxyBufferSize = (size_t)(8 * 1024 * 1024); /* 8 megabytes, 306 new->MaxProxyBufferSize = (size_t)(8 * 1024 * 1024); /* 8 megabytes,
307 * by default. 307 * by default.
308 */ 308 */
309#endif /* PROXY_SUPPORT */ 309#endif /* PROXY_SUPPORT */
310#ifdef AUTH_SERVER_SUPPORT 310#ifdef AUTH_SERVER_SUPPORT
311 new->system_auth = true; 311 new->system_auth = true;
312#endif /* AUTH_SERVER_SUPPORT */ 312#endif /* AUTH_SERVER_SUPPORT */
313 313
314 return new; 314 return new;
315} 315}
316 316
317 317
318 318
319void 319void
320free_config (struct config *data) 320free_config (struct config *data)
321{ 321{
322 if (data->keywords) free_keywords (data->keywords); 322 if (data->keywords) free_keywords (data->keywords);
323 free (data); 323 free (data);
324} 324}
325 325
326 326
327 327
328/* Return true if this function has already been called for line LN of file 328/* Return true if this function has already been called for line LN of file
329 * INFOPATH. 329 * INFOPATH.
330 */ 330 */
331bool 331bool
332parse_error (const char *infopath, unsigned int ln) 332parse_error (const char *infopath, unsigned int ln)
333{ 333{
334 static List *errors = NULL; 334 static List *errors = NULL;
335 char *nodename = NULL; 335 char *nodename = NULL;
336 336
337 if (!errors) 337 if (!errors)
338 errors = getlist(); 338 errors = getlist();
339 339
340 nodename = Xasprintf ("%s/%u", infopath, ln); 340 nodename = Xasprintf ("%s/%u", infopath, ln);
341 if (findnode (errors, nodename)) 341 if (findnode (errors, nodename))
342 { 342 {
343 free (nodename); 343 free (nodename);
344 return true; 344 return true;
345 } 345 }
346 346
347 push_string (errors, nodename); 347 push_string (errors, nodename);
348 return false; 348 return false;
349} 349}
350 350
351 351
352 352
353#ifdef ALLOW_CONFIG_OVERRIDE 353#ifdef ALLOW_CONFIG_OVERRIDE
354const char * const allowed_config_prefixes[] = { ALLOW_CONFIG_OVERRIDE }; 354const char * const allowed_config_prefixes[] = { ALLOW_CONFIG_OVERRIDE };
355#endif /* ALLOW_CONFIG_OVERRIDE */ 355#endif /* ALLOW_CONFIG_OVERRIDE */
356 356
357 357
358 358
359/* Parse the CVS config file. The syntax right now is a bit ad hoc 359/* Parse the CVS config file. The syntax right now is a bit ad hoc
360 * but tries to draw on the best or more common features of the other 360 * but tries to draw on the best or more common features of the other
361 * *info files and various unix (or non-unix) config file syntaxes. 361 * *info files and various unix (or non-unix) config file syntaxes.
362 * Lines starting with # are comments. Settings are lines of the form 362 * Lines starting with # are comments. Settings are lines of the form
363 * KEYWORD=VALUE. There is currently no way to have a multi-line 363 * KEYWORD=VALUE. There is currently no way to have a multi-line
364 * VALUE (would be nice if there was, probably). 364 * VALUE (would be nice if there was, probably).
365 * 365 *
366 * CVSROOT is the $CVSROOT directory 366 * CVSROOT is the $CVSROOT directory
367 * (current_parsed_root->directory might not be set yet, so this 367 * (current_parsed_root->directory might not be set yet, so this
368 * function takes the cvsroot as a function argument). 368 * function takes the cvsroot as a function argument).
369 * 369 *
370 * RETURNS 370 * RETURNS
371 * Always returns a fully initialized config struct, which on error may 371 * Always returns a fully initialized config struct, which on error may
372 * contain only the defaults. 372 * contain only the defaults.
373 * 373 *
374 * ERRORS 374 * ERRORS
375 * Calls error(0, ...) on errors in addition to the return value. 375 * Calls error(0, ...) on errors in addition to the return value.
376 * 376 *
377 * xmalloc() failures are fatal, per usual. 377 * xmalloc() failures are fatal, per usual.
378 */ 378 */
379struct config * 379struct config *
380parse_config (const char *cvsroot, const char *path) 380parse_config (const char *cvsroot, const char *path)
381{ 381{
382 const char *infopath; 382 const char *infopath;
383 char *freeinfopath = NULL; 383 char *freeinfopath = NULL;
384 FILE *fp_info; 384 FILE *fp_info;
385 char *line = NULL; 385 char *line = NULL;
386 unsigned int ln; /* Input file line counter. */ 386 unsigned int ln; /* Input file line counter. */
387 char *buf = NULL; 387 char *buf = NULL;
388 size_t buf_allocated = 0; 388 size_t buf_allocated = 0;
389 size_t len; 389 size_t len;
390 char *p; 390 char *p;
391 struct config *retval; 391 struct config *retval;
392 /* PROCESSING Whether config keys are currently being processed for 392 /* PROCESSING Whether config keys are currently being processed for
393 * this root. 393 * this root.
394 * PROCESSED Whether any keys have been processed for this root. 394 * PROCESSED Whether any keys have been processed for this root.
395 * This is initialized to true so that any initial keys 395 * This is initialized to true so that any initial keys
396 * may be processed as global defaults. 396 * may be processed as global defaults.
397 */ 397 */
398 bool processing = true; 398 bool processing = true;
399 bool processed = true; 399 bool processed = true;
400 400
401 TRACE (TRACE_FUNCTION, "parse_config (%s)", cvsroot); 401 TRACE (TRACE_FUNCTION, "parse_config (%s)", cvsroot);
402 402
403#ifdef ALLOW_CONFIG_OVERRIDE 403#ifdef ALLOW_CONFIG_OVERRIDE
404 if (path) 404 if (path)
405 { 405 {
406 const char * const *prefix; 406 const char * const *prefix;
407 char *npath = xcanonicalize_file_name (path); 407 char *npath = xcanonicalize_file_name (path);
408 bool approved = false; 408 bool approved = false;
409 for (prefix = allowed_config_prefixes; *prefix != NULL; prefix++) 409 for (prefix = allowed_config_prefixes; *prefix != NULL; prefix++)
410 { 410 {
411 char *nprefix; 411 char *nprefix;
412 412
413 if (!isreadable (*prefix)) continue; 413 if (!isreadable (*prefix)) continue;
414 nprefix = xcanonicalize_file_name (*prefix); 414 nprefix = xcanonicalize_file_name (*prefix);
415 if (!strncmp (nprefix, npath, strlen (nprefix)) 415 if (!strncmp (nprefix, npath, strlen (nprefix))
416 && (((*prefix)[strlen (*prefix)] != '/' 416 && (((*prefix)[strlen (*prefix)] != '/'
417 && strlen (npath) == strlen (nprefix)) 417 && strlen (npath) == strlen (nprefix))
418 || ((*prefix)[strlen (*prefix)] == '/' 418 || ((*prefix)[strlen (*prefix)] == '/'
419 && npath[strlen (nprefix)] == '/'))) 419 && npath[strlen (nprefix)] == '/')))
420 approved = true; 420 approved = true;
421 free (nprefix); 421 free (nprefix);
422 if (approved) break; 422 if (approved) break;
423 } 423 }
424 if (!approved) 424 if (!approved)
425 error (1, 0, "Invalid path to config file specified: `%s'", 425 error (1, 0, "Invalid path to config file specified: `%s'",
426 path); 426 path);
427 infopath = path; 427 infopath = path;
428 free (npath); 428 free (npath);
429 } 429 }
430 else 430 else
431#endif 431#endif
432 infopath = freeinfopath = 432 infopath = freeinfopath =
433 Xasprintf ("%s/%s/%s", cvsroot, CVSROOTADM, CVSROOTADM_CONFIG); 433 Xasprintf ("%s/%s/%s", cvsroot, CVSROOTADM, CVSROOTADM_CONFIG);
434 434
435 retval = new_config (); 435 retval = new_config ();
436 436
437 fp_info = CVS_FOPEN (infopath, "r"); 437 fp_info = CVS_FOPEN (infopath, "r");
438 if (!fp_info) 438 if (!fp_info)
439 { 439 {
440 /* If no file, don't do anything special. */ 440 /* If no file, don't do anything special. */
441 if (!existence_error (errno)) 441 if (!existence_error (errno))
442 { 442 {
443 /* Just a warning message; doesn't affect return 443 /* Just a warning message; doesn't affect return
444 value, currently at least. */ 444 value, currently at least. */
445 error (0, errno, "cannot open %s", infopath); 445 error (0, errno, "cannot open %s", infopath);
446 } 446 }
447 if (freeinfopath) free (freeinfopath); 447 if (freeinfopath) free (freeinfopath);
448 return retval; 448 return retval;
449 } 449 }
450 450
451 ln = 0; /* Have not read any lines yet. */ 451 ln = 0; /* Have not read any lines yet. */
452 while (getline (&buf, &buf_allocated, fp_info) >= 0) 452 while (getline (&buf, &buf_allocated, fp_info) >= 0)
453 { 453 {
454 ln++; /* Keep track of input file line number for error messages. */ 454 ln++; /* Keep track of input file line number for error messages. */
455 455
456 line = buf; 456 line = buf;
457 457
458 /* Skip leading white space. */ 458 /* Skip leading white space. */
459 while (isspace (*line)) line++; 459 while (isspace (*line)) line++;
460 460
461 /* Skip comments. */ 461 /* Skip comments. */
462 if (line[0] == '#') 462 if (line[0] == '#')
463 continue; 463 continue;
464 464
465 /* Is there any kind of written standard for the syntax of this 465 /* Is there any kind of written standard for the syntax of this
466 sort of config file? Anywhere in POSIX for example (I guess 466 sort of config file? Anywhere in POSIX for example (I guess
467 makefiles are sort of close)? Red Hat Linux has a bunch of 467 makefiles are sort of close)? Red Hat Linux has a bunch of
468 these too (with some GUI tools which edit them)... 468 these too (with some GUI tools which edit them)...
469 469
470 Along the same lines, we might want a table of keywords, 470 Along the same lines, we might want a table of keywords,
471 with various types (boolean, string, &c), as a mechanism 471 with various types (boolean, string, &c), as a mechanism
472 for making sure the syntax is consistent. Any good examples 472 for making sure the syntax is consistent. Any good examples
473 to follow there (Apache?)? */ 473 to follow there (Apache?)? */
474 474
475 /* Strip the trailing newline. There will be one unless we 475 /* Strip the trailing newline. There will be one unless we
476 read a partial line without a newline, and then got end of 476 read a partial line without a newline, and then got end of
477 file (or error?). */ 477 file (or error?). */
478 478
479 len = strlen (line) - 1; 479 len = strlen (line) - 1;
480 if (line[len] == '\n') 480 if (line[len] == '\n')
481 line[len--] = '\0'; 481 line[len--] = '\0';
482 482
483 /* Skip blank lines. */ 483 /* Skip blank lines. */
484 if (line[0] == '\0') 484 if (line[0] == '\0')
485 continue; 485 continue;
486 486
487 TRACE (TRACE_DATA, "parse_info() examining line: `%s'", line); 487 TRACE (TRACE_DATA, "parse_info() examining line: `%s'", line);
488 488
489 /* Check for a root specification. */ 489 /* Check for a root specification. */
490 if (line[0] == '[' && line[len] == ']') 490 if (line[0] == '[' && line[len] == ']')
491 { 491 {
492 cvsroot_t *tmproot; 492 cvsroot_t *tmproot;
493 493
494 line++[len] = '\0'; 494 line++[len] = '\0';
495 tmproot = parse_cvsroot (line); 495 tmproot = parse_cvsroot (line);
496 496
497 /* Ignoring method. */ 497 /* Ignoring method. */
498 if (!tmproot 498 if (!tmproot
499#if defined CLIENT_SUPPORT || defined SERVER_SUPPORT 499#if defined CLIENT_SUPPORT || defined SERVER_SUPPORT
500 || (tmproot->method != local_method 500 || (tmproot->method != local_method
501 && (!tmproot->hostname || !isThisHost (tmproot->hostname))) 501 && (!tmproot->hostname || !isThisHost (tmproot->hostname)))
502#endif /* CLIENT_SUPPORT || SERVER_SUPPORT */ 502#endif /* CLIENT_SUPPORT || SERVER_SUPPORT */
503 || !isSamePath (tmproot->directory, cvsroot)) 503 || !isSamePath (tmproot->directory, cvsroot))
504 { 504 {
505 if (processed) processing = false; 505 if (processed) processing = false;
506 } 506 }
507 else 507 else
508 { 508 {
509 TRACE (TRACE_FLOW, "Matched root section`%s'", line); 509 TRACE (TRACE_FLOW, "Matched root section`%s'", line);
510 processing = true; 510 processing = true;
511 processed = false; 511 processed = false;
512 } 512 }
513 513
514 continue; 514 continue;
515 } 515 }
516 516
517 /* There is data on this line. */ 517 /* There is data on this line. */
518 518
519 /* Even if the data is bad or ignored, consider data processed for 519 /* Even if the data is bad or ignored, consider data processed for
520 * this root. 520 * this root.
521 */ 521 */
522 processed = true; 522 processed = true;
523 523
524 if (!processing) 524 if (!processing)
525 /* ...but it is for a different root. */ 525 /* ...but it is for a different root. */
526 continue; 526 continue;
527 527
528 /* The first '=' separates keyword from value. */ 528 /* The first '=' separates keyword from value. */
529 p = strchr (line, '='); 529 p = strchr (line, '=');
530 if (!p) 530 if (!p)
531 { 531 {
532 if (!parse_error (infopath, ln)) 532 if (!parse_error (infopath, ln))
533 error (0, 0, 533 error (0, 0,
534"%s [%d]: syntax error: missing `=' between keyword and value", 534"%s [%d]: syntax error: missing `=' between keyword and value",
535 infopath, ln); 535 infopath, ln);
536 continue; 536 continue;
537 } 537 }
538 538
539 *p++ = '\0'; 539 *p++ = '\0';
540 540
541 if (strcmp (line, "RCSBIN") == 0) 541 if (strcmp (line, "RCSBIN") == 0)
542 { 542 {
543 /* This option used to specify the directory for RCS 543 /* This option used to specify the directory for RCS
544 executables. But since we don't run them any more, 544 executables. But since we don't run them any more,
545 this is a noop. Silently ignore it so that a 545 this is a noop. Silently ignore it so that a
546 repository can work with either new or old CVS. */ 546 repository can work with either new or old CVS. */
547 ; 547 ;
548 } 548 }
549 else if (strcmp (line, "SystemAuth") == 0) 549 else if (strcmp (line, "SystemAuth") == 0)
550#ifdef AUTH_SERVER_SUPPORT 550#ifdef AUTH_SERVER_SUPPORT
551 readBool (infopath, "SystemAuth", p, &retval->system_auth); 551 readBool (infopath, "SystemAuth", p, &retval->system_auth);
552#else 552#else
553 { 553 {
554 /* Still parse the syntax but ignore the option. That way the same 554 /* Still parse the syntax but ignore the option. That way the same
555 * config file can be used for local and server. 555 * config file can be used for local and server.
556 */ 556 */
557 bool dummy; 557 bool dummy;
558 readBool (infopath, "SystemAuth", p, &dummy); 558 readBool (infopath, "SystemAuth", p, &dummy);
559 } 559 }
560#endif 560#endif
561 else if (strcmp (line, "LocalKeyword") == 0 || 561 else if (strcmp (line, "LocalKeyword") == 0 ||
562 strcmp (line, "tag") == 0) 562 strcmp (line, "tag") == 0)
563 RCS_setlocalid (infopath, ln, &retval->keywords, p); 563 RCS_setlocalid (infopath, ln, &retval->keywords, p);
564 else if (strcmp (line, "KeywordExpand") == 0) 564 else if (strcmp (line, "KeywordExpand") == 0)
565 RCS_setincexc (&retval->keywords, p); 565 RCS_setincexc (&retval->keywords, p);
566 else if (strcmp (line, "PreservePermissions") == 0) 566 else if (strcmp (line, "PreservePermissions") == 0)
567 { 567 {
568#ifdef PRESERVE_PERMISSIONS_SUPPORT 568#ifdef PRESERVE_PERMISSIONS_SUPPORT
569 readBool (infopath, "PreservePermissions", p, 569 readBool (infopath, "PreservePermissions", p,
570 &retval->preserve_perms); 570 &retval->preserve_perms);
571#else 571#else
572 if (!parse_error (infopath, ln)) 572 if (!parse_error (infopath, ln))
573 error (0, 0, "\ 573 error (0, 0, "\
574%s [%u]: warning: this CVS does not support PreservePermissions", 574%s [%u]: warning: this CVS does not support PreservePermissions",
575 infopath, ln); 575 infopath, ln);
576#endif 576#endif
577 } 577 }
578 else if (strcmp (line, "TopLevelAdmin") == 0) 578 else if (strcmp (line, "TopLevelAdmin") == 0)
579 readBool (infopath, "TopLevelAdmin", p, &retval->top_level_admin); 579 readBool (infopath, "TopLevelAdmin", p, &retval->top_level_admin);
580 else if (strcmp (line, "LockDir") == 0) 580 else if (strcmp (line, "LockDir") == 0)
581 { 581 {
582 if (retval->lock_dir) 582 if (retval->lock_dir)
583 free (retval->lock_dir); 583 free (retval->lock_dir);
584 retval->lock_dir = expand_path (p, cvsroot, false, infopath, ln); 584 retval->lock_dir = expand_path (p, cvsroot, false, infopath, ln);
585 /* Could try some validity checking, like whether we can 585 /* Could try some validity checking, like whether we can
586 opendir it or something, but I don't see any particular 586 opendir it or something, but I don't see any particular
587 reason to do that now rather than waiting until lock.c. */ 587 reason to do that now rather than waiting until lock.c. */
588 } 588 }
589 else if (strcmp (line, "HistoryLogPath") == 0) 589 else if (strcmp (line, "HistoryLogPath") == 0)
590 { 590 {
591 if (retval->HistoryLogPath) free (retval->HistoryLogPath); 591 if (retval->HistoryLogPath) free (retval->HistoryLogPath);
592 592
593 /* Expand ~ & $VARs. */ 593 /* Expand ~ & $VARs. */
594 retval->HistoryLogPath = expand_path (p, cvsroot, false, 594 retval->HistoryLogPath = expand_path (p, cvsroot, false,
595 infopath, ln); 595 infopath, ln);
596 596
597 if (retval->HistoryLogPath && !ISABSOLUTE (retval->HistoryLogPath)) 597 if (retval->HistoryLogPath && !ISABSOLUTE (retval->HistoryLogPath))
598 { 598 {
599 error (0, 0, "%s [%u]: HistoryLogPath must be absolute.", 599 error (0, 0, "%s [%u]: HistoryLogPath must be absolute.",
600 infopath, ln); 600 infopath, ln);
601 free (retval->HistoryLogPath); 601 free (retval->HistoryLogPath);
602 retval->HistoryLogPath = NULL; 602 retval->HistoryLogPath = NULL;
603 } 603 }
604 } 604 }
605 else if (strcmp (line, "HistorySearchPath") == 0) 605 else if (strcmp (line, "HistorySearchPath") == 0)
606 { 606 {
607 if (retval->HistorySearchPath) free (retval->HistorySearchPath); 607 if (retval->HistorySearchPath) free (retval->HistorySearchPath);
608 retval->HistorySearchPath = expand_path (p, cvsroot, false, 608 retval->HistorySearchPath = expand_path (p, cvsroot, false,
609 infopath, ln); 609 infopath, ln);
610 610
611 if (retval->HistorySearchPath 611 if (retval->HistorySearchPath
612 && !ISABSOLUTE (retval->HistorySearchPath)) 612 && !ISABSOLUTE (retval->HistorySearchPath))
613 { 613 {
614 error (0, 0, "%s [%u]: HistorySearchPath must be absolute.", 614 error (0, 0, "%s [%u]: HistorySearchPath must be absolute.",
615 infopath, ln); 615 infopath, ln);
616 free (retval->HistorySearchPath); 616 free (retval->HistorySearchPath);
617 retval->HistorySearchPath = NULL; 617 retval->HistorySearchPath = NULL;
618 } 618 }
619 } 619 }
620 else if (strcmp (line, "LogHistory") == 0) 620 else if (strcmp (line, "LogHistory") == 0)
621 { 621 {
622 if (strcmp (p, "all") != 0) 622 if (strcmp (p, "all") != 0)
623 { 623 {
624 static bool gotone = false; 624 static bool gotone = false;
625 if (gotone) 625 if (gotone)
626 error (0, 0, "\ 626 error (0, 0, "\
627%s [%u]: warning: duplicate LogHistory entry found.", 627%s [%u]: warning: duplicate LogHistory entry found.",
628 infopath, ln); 628 infopath, ln);
629 else 629 else
630 gotone = true; 630 gotone = true;
631 free (retval->logHistory); 631 free (retval->logHistory);
632 retval->logHistory = xstrdup (p); 632 retval->logHistory = xstrdup (p);
633 } 633 }
634 } 634 }
635 else if (strcmp (line, "RereadLogAfterVerify") == 0) 635 else if (strcmp (line, "RereadLogAfterVerify") == 0)
636 { 636 {
637 if (!strcasecmp (p, "never")) 637 if (!strcasecmp (p, "never"))
638 retval->RereadLogAfterVerify = LOGMSG_REREAD_NEVER; 638 retval->RereadLogAfterVerify = LOGMSG_REREAD_NEVER;
639 else if (!strcasecmp (p, "always")) 639 else if (!strcasecmp (p, "always"))
640 retval->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS; 640 retval->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
641 else if (!strcasecmp (p, "stat")) 641 else if (!strcasecmp (p, "stat"))
642 retval->RereadLogAfterVerify = LOGMSG_REREAD_STAT; 642 retval->RereadLogAfterVerify = LOGMSG_REREAD_STAT;
643 else 643 else
644 { 644 {
645 bool tmp; 645 bool tmp;
646 if (readBool (infopath, "RereadLogAfterVerify", p, &tmp)) 646 if (readBool (infopath, "RereadLogAfterVerify", p, &tmp))
647 { 647 {
648 if (tmp) 648 if (tmp)
649 retval->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS; 649 retval->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
650 else 650 else
651 retval->RereadLogAfterVerify = LOGMSG_REREAD_NEVER; 651 retval->RereadLogAfterVerify = LOGMSG_REREAD_NEVER;
652 } 652 }
653 } 653 }
654 } 654 }
655 else if (strcmp (line, "TmpDir") == 0) 655 else if (strcmp (line, "TmpDir") == 0)
656 { 656 {
657 if (retval->TmpDir) free (retval->TmpDir); 657 if (retval->TmpDir) free (retval->TmpDir);
658 retval->TmpDir = expand_path (p, cvsroot, false, infopath, ln); 658 retval->TmpDir = expand_path (p, cvsroot, false, infopath, ln);
659 /* Could try some validity checking, like whether we can 659 /* Could try some validity checking, like whether we can
660 * opendir it or something, but I don't see any particular 660 * opendir it or something, but I don't see any particular
661 * reason to do that now rather than when the first function 661 * reason to do that now rather than when the first function
662 * tries to create a temp file. 662 * tries to create a temp file.
663 */ 663 */
664 } 664 }
665 else if (strcmp (line, "UserAdminGroup") == 0 665 else if (strcmp (line, "UserAdminGroup") == 0
666 || strcmp (line, "AdminGroup") == 0) 666 || strcmp (line, "AdminGroup") == 0)
667 retval->UserAdminGroup = xstrdup (p); 667 retval->UserAdminGroup = xstrdup (p);
668 else if (strcmp (line, "UserAdminOptions") == 0 668 else if (strcmp (line, "UserAdminOptions") == 0
669 || strcmp (line, "AdminOptions") == 0) 669 || strcmp (line, "AdminOptions") == 0)
670 retval->UserAdminOptions = xstrdup (p); 670 retval->UserAdminOptions = xstrdup (p);
671 else if (strcmp (line, "UseNewInfoFmtStrings") == 0) 671 else if (strcmp (line, "UseNewInfoFmtStrings") == 0)
672#ifdef SUPPORT_OLD_INFO_FMT_STRINGS 672#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
673 readBool (infopath, "UseNewInfoFmtStrings", p, 673 readBool (infopath, "UseNewInfoFmtStrings", p,
674 &retval->UseNewInfoFmtStrings); 674 &retval->UseNewInfoFmtStrings);
675#else /* !SUPPORT_OLD_INFO_FMT_STRINGS */ 675#else /* !SUPPORT_OLD_INFO_FMT_STRINGS */
676 { 676 {
677 bool dummy; 677 bool dummy;
678 if (readBool (infopath, "UseNewInfoFmtStrings", p, &dummy) 678 if (readBool (infopath, "UseNewInfoFmtStrings", p, &dummy)
679 && !dummy) 679 && !dummy)
680 error (1, 0, 680 error (1, 0,
681"%s [%u]: Old style info format strings not supported by this executable.", 681"%s [%u]: Old style info format strings not supported by this executable.",
682 infopath, ln); 682 infopath, ln);
683 } 683 }
684#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */ 684#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
685 else if (strcmp (line, "ImportNewFilesToVendorBranchOnly") == 0) 685 else if (strcmp (line, "ImportNewFilesToVendorBranchOnly") == 0)
686 readBool (infopath, "ImportNewFilesToVendorBranchOnly", p, 686 readBool (infopath, "ImportNewFilesToVendorBranchOnly", p,
687 &retval->ImportNewFilesToVendorBranchOnly); 687 &retval->ImportNewFilesToVendorBranchOnly);
688 else if (strcmp (line, "PrimaryServer") == 0) 688 else if (strcmp (line, "PrimaryServer") == 0)
689 retval->PrimaryServer = parse_cvsroot (p); 689 retval->PrimaryServer = parse_cvsroot (p);
690#ifdef PROXY_SUPPORT 690#ifdef PROXY_SUPPORT
691 else if (!strcmp (line, "MaxProxyBufferSize")) 691 else if (!strcmp (line, "MaxProxyBufferSize"))
692 readSizeT (infopath, "MaxProxyBufferSize", p, 692 readSizeT (infopath, "MaxProxyBufferSize", p,
693 &retval->MaxProxyBufferSize); 693 &retval->MaxProxyBufferSize);
694#endif /* PROXY_SUPPORT */ 694#endif /* PROXY_SUPPORT */
695 else if (!strcmp (line, "MaxCommentLeaderLength")) 695 else if (!strcmp (line, "MaxCommentLeaderLength"))
696 readSizeT (infopath, "MaxCommentLeaderLength", p, 696 readSizeT (infopath, "MaxCommentLeaderLength", p,
697 &retval->MaxCommentLeaderLength); 697 &retval->MaxCommentLeaderLength);
698 else if (!strcmp (line, "UseArchiveCommentLeader")) 698 else if (!strcmp (line, "UseArchiveCommentLeader"))
699 readBool (infopath, "UseArchiveCommentLeader", p, 699 readBool (infopath, "UseArchiveCommentLeader", p,
700 &retval->UseArchiveCommentLeader); 700 &retval->UseArchiveCommentLeader);
701#ifdef SERVER_SUPPORT 701#ifdef SERVER_SUPPORT
702 else if (!strcmp (line, "MinCompressionLevel")) 702 else if (!strcmp (line, "MinCompressionLevel"))
703 readSizeT (infopath, "MinCompressionLevel", p, 703 readSizeT (infopath, "MinCompressionLevel", p,
704 &retval->MinCompressionLevel); 704 &retval->MinCompressionLevel);
705 else if (!strcmp (line, "MaxCompressionLevel")) 705 else if (!strcmp (line, "MaxCompressionLevel"))
706 readSizeT (infopath, "MaxCompressionLevel", p, 706 readSizeT (infopath, "MaxCompressionLevel", p,
707 &retval->MaxCompressionLevel); 707 &retval->MaxCompressionLevel);
708#endif /* SERVER_SUPPORT */ 708#endif /* SERVER_SUPPORT */
709 else 709 else
710 /* We may be dealing with a keyword which was added in a 710 /* We may be dealing with a keyword which was added in a
711 subsequent version of CVS. In that case it is a good idea 711 subsequent version of CVS. In that case it is a good idea
712 to complain, as (1) the keyword might enable a behavior like 712 to complain, as (1) the keyword might enable a behavior like
713 alternate locking behavior, in which it is dangerous and hard 713 alternate locking behavior, in which it is dangerous and hard
714 to detect if some CVS's have it one way and others have it 714 to detect if some CVS's have it one way and others have it
715 the other way, (2) in general, having us not do what the user 715 the other way, (2) in general, having us not do what the user
716 had in mind when they put in the keyword violates the 716 had in mind when they put in the keyword violates the
717 principle of least surprise. Note that one corollary is 717 principle of least surprise. Note that one corollary is
718 adding new keywords to your CVSROOT/config file is not 718 adding new keywords to your CVSROOT/config file is not
719 particularly recommended unless you are planning on using 719 particularly recommended unless you are planning on using
720 the new features. */ 720 the new features. */
721 if (!parse_error (infopath, ln)) 721 if (!parse_error (infopath, ln))
722 error (0, 0, "%s [%u]: unrecognized keyword `%s'", 722 error (0, 0, "%s [%u]: unrecognized keyword `%s'",
723 infopath, ln, line); 723 infopath, ln, line);
724 } 724 }
725 if (ferror (fp_info)) 725 if (ferror (fp_info))
726 error (0, errno, "cannot read %s", infopath); 726 error (0, errno, "cannot read %s", infopath);
727 if (fclose (fp_info) < 0) 727 if (fclose (fp_info) < 0)
728 error (0, errno, "cannot close %s", infopath); 728 error (0, errno, "cannot close %s", infopath);
729 if (freeinfopath) free (freeinfopath); 729 if (freeinfopath) free (freeinfopath);
730 if (buf) free (buf); 730 if (buf) free (buf);
731 731
732 return retval; 732 return retval;
733} 733}
 734
 735/* cvsacl patch */
 736int
 737parse_aclconfig (const char *cvsroot)
 738{
 739 char *infopath;
 740 FILE *fp_info;
 741 char *line = NULL;
 742 size_t line_allocated = 0;
 743 size_t len;
 744 char *p;
 745 /* FIXME-reentrancy: If we do a multi-threaded server, this would need
 746 to go to the per-connection data structures. */
 747 static int parsed = 0;
 748
 749 /* Authentication code and serve_root might both want to call us.
 750 Let this happen smoothly. */
 751 if (parsed)
 752 return 0;
 753 parsed = 1;
 754
 755 infopath = xmalloc (strlen (cvsroot)
 756 + sizeof (CVSROOTADM_ACLCONFIG)
 757 + sizeof (CVSROOTADM)
 758 + 10);
 759 if (infopath == NULL)
 760 {
 761 error (0, 0, "out of memory; cannot allocate infopath");
 762 goto error_return;
 763 }
 764
 765 strcpy (infopath, cvsroot);
 766 strcat (infopath, "/");
 767 strcat (infopath, CVSROOTADM);
 768 strcat (infopath, "/");
 769 strcat (infopath, CVSROOTADM_ACLCONFIG);
 770
 771 fp_info = CVS_FOPEN (infopath, "r");
 772 if (fp_info == NULL)
 773 {
 774 /* If no file, don't do anything special. */
 775 if (!existence_error (errno))
 776 {
 777 /* Just a warning message; doesn't affect return
 778 value, currently at least. */
 779 error (0, errno, "cannot open %s", infopath);
 780 }
 781 free (infopath);
 782 return 0;
 783 }
 784
 785 while (getline (&line, &line_allocated, fp_info) >= 0)
 786 {
 787 /* Skip comments. */
 788 if (line[0] == '#')
 789 continue;
 790
 791 len = strlen (line) - 1;
 792 if (line[len] == '\n')
 793 line[len] = '\0';
 794
 795 /* Skip blank lines. */
 796 if (line[0] == '\0')
 797 continue;
 798
 799 /* The first '=' separates keyword from value. */
 800 p = strchr (line, '=');
 801 if (p == NULL)
 802 {
 803 /* Probably should be printing line number. */
 804 error (0, 0, "syntax error in %s: line '%s' is missing '='",
 805 infopath, line);
 806 goto error_return;
 807 }
 808
 809 *p++ = '\0';
 810
 811 if (strcmp (line, "UseCVSACL") == 0)
 812 {
 813 if (strcmp (p, "no") == 0)
 814 use_cvs_acl = 0;
 815 else if (strcmp (p, "yes") == 0)
 816 use_cvs_acl = 1;
 817 else
 818 {
 819 error (0, 0, "unrecognized value '%s' for UseCVSACL", p);
 820 goto error_return;
 821 }
 822 }
 823 else if (strcmp (line, "UseSeperateACLFileForEachDir") == 0)
 824 {
 825 if (strcmp (p, "no") == 0)
 826 use_separate_acl_file_for_each_dir = 0;
 827 else if (strcmp (p, "yes") == 0)
 828 use_separate_acl_file_for_each_dir = 1;
 829 else
 830 {
 831 error (0, 0, "unrecognized value '%s' for UseSeperateACLFileForEachDir", p);
 832 goto error_return;
 833 }
 834 }
 835 else if (strcmp (line, "StopAtFirstPermissionDenied") == 0)
 836 {
 837 if (strcmp (p, "no") == 0)
 838 stop_at_first_permission_denied = 0;
 839 else if (strcmp (p, "yes") == 0)
 840 stop_at_first_permission_denied = 1;
 841 else
 842 {
 843 error (0, 0, "unrecognized value '%s' for StopAtFirstPermissionDenied", p);
 844 goto error_return;
 845 }
 846 }
 847 else if (strcmp (line, "CVSACLDefaultPermissions") == 0)
 848 {
 849 if (cvs_acl_default_permissions != NULL)
 850 free (cvs_acl_default_permissions);
 851 if (!given_perms_valid (p))
 852 error (1,0,"Invalid CVS ACL Default Permissions: '%s' in CVSROOT/aclconfig", p);
 853 cvs_acl_default_permissions = xstrdup (p);
 854 }
 855 else if (strcmp (line, "UseCVSGroups") == 0)
 856 {
 857 if (strcmp (p, "no") == 0)
 858 use_cvs_groups = 0;
 859 else if (strcmp (p, "yes") == 0)
 860 use_cvs_groups = 1;
 861 else
 862 {
 863 error (0, 0, "unrecognized value '%s' for UseCVSGroups", p);
 864 goto error_return;
 865 }
 866 }
 867 else if (strcmp (line, "UseSystemGroups") == 0)
 868 {
 869 if (strcmp (p, "no") == 0)
 870 use_system_groups = 0;
 871 else if (strcmp (p, "yes") == 0)
 872 use_system_groups = 1;
 873 else
 874 {
 875 error (0, 0, "unrecognized value '%s' for UseSystemGroups", p);
 876 goto error_return;
 877 }
 878 }
 879 else if (strcmp (line, "CVSACLFileLocation") == 0)
 880 {
 881 if (cvs_acl_file_location != NULL)
 882 free (cvs_acl_file_location);
 883 cvs_acl_file_location = xstrdup (p);
 884 }
 885 else if (strcmp (line, "CVSGroupsFileLocation") == 0)
 886 {
 887 if (cvs_groups_file_location != NULL)
 888 free (cvs_groups_file_location);
 889 cvs_groups_file_location = xstrdup (p);
 890 }
 891 else if (strcmp (line, "CVSServerRunAsUser") == 0)
 892 {
 893 if (cvs_server_run_as != NULL)
 894 free (cvs_server_run_as);
 895 cvs_server_run_as = xstrdup (p);
 896 }
 897
 898 }
 899
 900 if (ferror (fp_info))
 901 {
 902 error (0, errno, "cannot read %s", infopath);
 903 goto error_return;
 904 }
 905 if (fclose (fp_info) < 0)
 906 {
 907 error (0, errno, "cannot close %s", infopath);
 908 goto error_return;
 909 }
 910 free (infopath);
 911 if (line != NULL)
 912 free (line);
 913 return 0;
 914
 915 error_return:
 916 if (infopath != NULL)
 917 free (infopath);
 918 if (line != NULL)
 919 free (line);
 920 return -1;
 921}