2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
5 * You may distribute under the terms of the GNU General Public License as
6 * specified in the README file that comes with the CVS source distribution.
16 * Parse the INFOFILE file for the specified REPOSITORY. Invoke CALLPROC for
17 * the first line in the file that matches the REPOSITORY, or if ALL != 0, any
18 * lines matching "ALL", or if no lines match, the last line matching
21 * Return 0 for success, -1 if there was not an INFOFILE, and >0 for failure.
24 Parse_Info (const char *infofile, const char *repository, CALLPROC callproc,
25 int opt, void *closure)
31 size_t line_allocated = 0;
32 char *default_value = NULL;
35 int callback_done, line_number;
36 char *cp, *exp, *value;
38 const char *regex_err;
40 if (current_parsed_root == NULL)
42 /* XXX - should be error maybe? */
43 error (0, 0, "CVSROOT variable not set");
47 /* find the info file and open it */
48 infopath = xmalloc (strlen (current_parsed_root->directory)
52 (void) sprintf (infopath, "%s/%s/%s", current_parsed_root->directory,
53 CVSROOTADM, infofile);
54 fp_info = CVS_FOPEN (infopath, "r");
57 /* If no file, don't do anything special. */
58 if (!existence_error (errno))
59 error (0, errno, "cannot open %s", infopath);
64 /* strip off the CVSROOT if repository was absolute */
65 srepos = Short_Repository (repository);
67 TRACE (1, "Parse_Info (%s, %s, %s)",
68 infopath, srepos, (opt & PIOPT_ALL) ? "ALL" : "not ALL");
70 /* search the info file for lines that match */
71 callback_done = line_number = 0;
72 while (getline (&line, &line_allocated, fp_info) >= 0)
76 /* skip lines starting with # */
80 /* skip whitespace at beginning of line */
81 for (cp = line; *cp && isspace ((unsigned char) *cp); cp++)
84 /* if *cp is null, the whole line was blank */
88 /* the regular expression is everything up to the first space */
89 for (exp = cp; *cp && !isspace ((unsigned char) *cp); cp++)
94 /* skip whitespace up to the start of the matching value */
95 while (*cp && isspace ((unsigned char) *cp))
98 /* no value to match with the regular expression is an error */
101 error (0, 0, "syntax error at line %d file %s; ignored",
102 line_number, infopath);
107 /* strip the newline off the end of the value */
108 if ((cp = strrchr (value, '\n')) != NULL)
112 * At this point, exp points to the regular expression, and value
113 * points to the value to call the callback routine with. Evaluate
114 * the regular expression against srepos and callback with the value
118 /* save the default value so we have it later if we need it */
119 if (strcmp (exp, "DEFAULT") == 0)
121 if (default_value != NULL)
123 error (0, 0, "Multiple `DEFAULT' lines (%d and %d) in %s file",
124 default_line, line_number, infofile);
125 free (default_value);
127 default_value = xstrdup (value);
128 default_line = line_number;
133 * For a regular expression of "ALL", do the callback always We may
134 * execute lots of ALL callbacks in addition to *one* regular matching
135 * callback or default
137 if (strcmp (exp, "ALL") == 0)
139 if (!(opt & PIOPT_ALL))
140 error (0, 0, "Keyword `ALL' is ignored at line %d in %s file",
141 line_number, infofile);
142 else if ((expanded_value = expand_path (value, infofile,
146 err += callproc (repository, expanded_value, closure);
147 free (expanded_value);
155 /* only first matching, plus "ALL"'s */
158 /* see if the repository matched this regular expression */
159 if ((regex_err = re_comp (exp)) != NULL)
161 error (0, 0, "bad regular expression at line %d file %s: %s",
162 line_number, infofile, regex_err);
165 if (re_exec (srepos) == 0)
166 continue; /* no match */
168 /* it did, so do the callback and note that we did one */
169 if ((expanded_value = expand_path( value, infofile, line_number, 1)
172 err += callproc (repository, expanded_value, closure);
173 free (expanded_value);
179 if (ferror (fp_info))
180 error (0, errno, "cannot read %s", infopath);
181 if (fclose (fp_info) < 0)
182 error (0, errno, "cannot close %s", infopath);
184 /* if we fell through and didn't callback at all, do the default */
185 if (callback_done == 0 && default_value != NULL)
187 if ((expanded_value = expand_path (default_value, infofile,
191 err += callproc (repository, expanded_value, closure);
192 free (expanded_value);
198 /* free up space if necessary */
199 if (default_value != NULL)
200 free (default_value);
210 /* Print a warning and return false if P doesn't look like a string specifying
211 * something that can be converted into a size_t.
213 * Sets *VAL to the parsed value when it is found to be valid. *VAL will not
214 * be altered when false is returned.
217 readSizeT (const char *infopath, const char *option, const char *p,
221 size_t num, factor = 1;
223 if (!strcasecmp ("unlimited", p))
229 /* Record the factor character (kilo, mega, giga, tera). */
230 if (!isdigit (p[strlen(p) - 1]))
232 switch (p[strlen(p) - 1])
235 factor = xtimes (factor, 1024);
237 factor = xtimes (factor, 1024);
239 factor = xtimes (factor, 1024);
241 factor = xtimes (factor, 1024);
245 "%s: Unknown %s factor: `%c'",
246 infopath, option, p[strlen(p)]);
249 TRACE (TRACE_DATA, "readSizeT(): Found factor %u for %s",
253 /* Verify that *q is a number. */
255 while (q < p + strlen(p) - 1 /* Checked last character above. */)
260 "%s: %s must be a postitive integer, not '%s'",
261 infopath, option, p);
267 /* Compute final value. */
268 num = strtoul (p, NULL, 10);
269 if (num == ULONG_MAX || num > SIZE_MAX)
270 /* Don't return an error, just max out. */
273 TRACE (TRACE_DATA, "readSizeT(): read number %u for %s", num, option);
274 *val = xtimes (strtoul (p, NULL, 10), factor);
275 TRACE (TRACE_DATA, "readSizeT(): returnning %u for %s", *val, option);
281 /* Allocate and initialize a new config struct. */
282 static inline struct config *
285 struct config *new = xcalloc (1, sizeof (struct config));
287 TRACE (TRACE_FLOW, "new_config ()");
289 new->logHistory = ALL_HISTORY_REC_TYPES;
290 new->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
291 new->UserAdminOptions = xstrdup ("k");
292 new->MaxCommentLeaderLength = 20;
294 new->MaxProxyBufferSize = (size_t)(8 * 1024 * 1024); /* 8 megabytes,
297 #endif /* PROXY_SUPPORT */
298 #ifdef AUTH_SERVER_SUPPORT
299 new->system_auth = true;
300 #endif /* AUTH_SERVER_SUPPORT */
308 free_config (struct config *data)
310 if (data->keywords) free_keywords (data->keywords);
316 /* Parse the CVS config file. The syntax right now is a bit ad hoc
317 * but tries to draw on the best or more common features of the other
318 * *info files and various unix (or non-unix) config file syntaxes.
319 * Lines starting with # are comments. Settings are lines of the form
320 * KEYWORD=VALUE. There is currently no way to have a multi-line
321 * VALUE (would be nice if there was, probably).
323 * CVSROOT is the $CVSROOT directory
324 * (current_parsed_root->directory might not be set yet, so this
325 * function takes the cvsroot as a function argument).
328 * Always returns a fully initialized config struct, which on error may
329 * contain only the defaults.
332 * Calls error(0, ...) on errors in addition to the return value.
334 * xmalloc() failures are fatal, per usual.
337 parse_config (const char *cvsroot)
342 size_t line_allocated = 0;
345 struct config *retval;
347 TRACE (TRACE_FUNCTION, "parse_config (%s)", cvsroot);
349 retval = new_config ();
351 infopath = Xasprintf ("%s/%s/%s", cvsroot, CVSROOTADM, CVSROOTADM_CONFIG);
353 fp_info = CVS_FOPEN (infopath, "r");
356 /* If no file, don't do anything special. */
357 if (!existence_error (errno))
359 /* Just a warning message; doesn't affect return
360 value, currently at least. */
361 error (0, errno, "cannot open %s", infopath);
367 while (getline (&line, &line_allocated, fp_info) >= 0)
373 /* At least for the moment we don't skip whitespace at the start
374 of the line. Too picky? Maybe. But being insufficiently
375 picky leads to all sorts of confusion, and it is a lot easier
376 to start out picky and relax it than the other way around.
378 Is there any kind of written standard for the syntax of this
379 sort of config file? Anywhere in POSIX for example (I guess
380 makefiles are sort of close)? Red Hat Linux has a bunch of
381 these too (with some GUI tools which edit them)...
383 Along the same lines, we might want a table of keywords,
384 with various types (boolean, string, &c), as a mechanism
385 for making sure the syntax is consistent. Any good examples
386 to follow there (Apache?)? */
388 /* Strip the trailing newline. There will be one unless we
389 read a partial line without a newline, and then got end of
392 len = strlen (line) - 1;
393 if (line[len] == '\n')
396 /* Skip blank lines. */
400 TRACE (TRACE_DATA, "parse_info() examining line: `%s'", line);
402 /* The first '=' separates keyword from value. */
403 p = strchr (line, '=');
406 /* Probably should be printing line number. */
407 error (0, 0, "syntax error in %s: line '%s' is missing '='",
414 if (strcmp (line, "RCSBIN") == 0)
416 /* This option used to specify the directory for RCS
417 executables. But since we don't run them any more,
418 this is a noop. Silently ignore it so that a
419 repository can work with either new or old CVS. */
422 else if (strcmp (line, "SystemAuth") == 0)
423 #ifdef AUTH_SERVER_SUPPORT
424 readBool (infopath, "SystemAuth", p, &retval->system_auth);
427 /* Still parse the syntax but ignore the option. That way the same
428 * config file can be used for local and server.
431 readBool (infopath, "SystemAuth", p, &dummy);
434 else if (strcmp (line, "LocalKeyword") == 0)
435 RCS_setlocalid (&retval->keywords, p);
436 else if (strcmp (line, "KeywordExpand") == 0)
437 RCS_setincexc (&retval->keywords, p);
438 else if (strcmp (line, "PreservePermissions") == 0)
439 #ifdef PRESERVE_PERMISSIONS_SUPPORT
440 readBool (infopath, "PreservePermissions", p,
441 &retval->preserve_perms);
444 warning: this CVS does not support PreservePermissions");
446 else if (strcmp (line, "TopLevelAdmin") == 0)
447 readBool (infopath, "TopLevelAdmin", p, &retval->top_level_admin);
448 else if (strcmp (line, "LockDir") == 0)
450 if (retval->lock_dir != NULL)
451 free (retval->lock_dir);
452 retval->lock_dir = xstrdup (p);
453 /* Could try some validity checking, like whether we can
454 opendir it or something, but I don't see any particular
455 reason to do that now rather than waiting until lock.c. */
457 else if (strcmp (line, "LogHistory") == 0)
459 if (strcmp (p, "all") != 0)
460 retval->logHistory = xstrdup (p);
462 else if (strcmp (line, "RereadLogAfterVerify") == 0)
464 if (!strcasecmp (p, "never"))
465 retval->RereadLogAfterVerify = LOGMSG_REREAD_NEVER;
466 else if (!strcasecmp (p, "always"))
467 retval->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
468 else if (!strcasecmp (p, "stat"))
469 retval->RereadLogAfterVerify = LOGMSG_REREAD_STAT;
473 if (readBool (infopath, "RereadLogAfterVerify", p, &tmp))
476 retval->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
478 retval->RereadLogAfterVerify = LOGMSG_REREAD_NEVER;
482 else if (strcmp (line, "UserAdminOptions") == 0)
483 retval->UserAdminOptions = xstrdup (p);
484 else if (strcmp (line, "UseNewInfoFmtStrings") == 0)
485 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
486 readBool (infopath, "UseNewInfoFmtStrings", p,
487 &retval->UseNewInfoFmtStrings);
488 #else /* !SUPPORT_OLD_INFO_FMT_STRINGS */
491 if (readBool (infopath, "UseNewInfoFmtStrings", p, &dummy)
494 "%s: Old style info format strings not supported by this executable.",
497 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
498 else if (strcmp (line, "ImportNewFilesToVendorBranchOnly") == 0)
499 readBool (infopath, "ImportNewFilesToVendorBranchOnly", p,
500 &retval->ImportNewFilesToVendorBranchOnly);
502 else if (strcmp (line, "PrimaryServer") == 0)
504 retval->PrimaryServer = parse_cvsroot (p);
505 if (retval->PrimaryServer->method != fork_method
506 && retval->PrimaryServer->method != ext_method)
508 /* I intentionally neglect to mention :fork: here. It is
509 * really only useful for testing.
512 "%s: Only PrimaryServers with :ext: methods are valid, not `%s'.",
516 else if (!strcmp (line, "MaxProxyBufferSize"))
517 readSizeT (infopath, "MaxProxyBufferSize", p,
518 &retval->MaxProxyBufferSize);
519 #endif /* PROXY_SUPPORT */
520 else if (!strcmp (line, "MaxCommentLeaderLength"))
521 readSizeT (infopath, "MaxCommentLeaderLength", p,
522 &retval->MaxCommentLeaderLength);
523 else if (!strcmp (line, "UseArchiveCommentLeader"))
524 readBool (infopath, "UseArchiveCommentLeader", p,
525 &retval->UseArchiveCommentLeader);
527 /* We may be dealing with a keyword which was added in a
528 subsequent version of CVS. In that case it is a good idea
529 to complain, as (1) the keyword might enable a behavior like
530 alternate locking behavior, in which it is dangerous and hard
531 to detect if some CVS's have it one way and others have it
532 the other way, (2) in general, having us not do what the user
533 had in mind when they put in the keyword violates the
534 principle of least surprise. Note that one corollary is
535 adding new keywords to your CVSROOT/config file is not
536 particularly recommended unless you are planning on using
538 error (0, 0, "%s: unrecognized keyword '%s'",
541 if (ferror (fp_info))
542 error (0, errno, "cannot read %s", infopath);
543 if (fclose (fp_info) < 0)
544 error (0, errno, "cannot close %s", infopath);