2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (C) 1989-1992, Brian Berliner
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.
21 * Parse the INFOFILE file for the specified REPOSITORY. Invoke CALLPROC for
22 * the first line in the file that matches the REPOSITORY, or if ALL != 0, any
23 * lines matching "ALL", or if no lines match, the last line matching
26 * Return 0 for success, -1 if there was not an INFOFILE, and >0 for failure.
29 Parse_Info (const char *infofile, const char *repository, CALLPROC callproc,
30 int opt, void *closure)
36 size_t line_allocated = 0;
37 char *default_value = NULL;
40 int callback_done, line_number;
41 char *cp, *exp, *value;
43 const char *regex_err;
47 if (current_parsed_root == NULL)
49 /* XXX - should be error maybe? */
50 error (0, 0, "CVSROOT variable not set");
54 /* find the info file and open it */
55 infopath = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
56 CVSROOTADM, infofile);
57 fp_info = CVS_FOPEN (infopath, "r");
60 /* If no file, don't do anything special. */
61 if (!existence_error (errno))
62 error (0, errno, "cannot open %s", infopath);
67 /* strip off the CVSROOT if repository was absolute */
68 srepos = Short_Repository (repository);
70 TRACE (TRACE_FUNCTION, "Parse_Info (%s, %s, %s)",
71 infopath, srepos, (opt & PIOPT_ALL) ? "ALL" : "not ALL");
73 /* search the info file for lines that match */
74 callback_done = line_number = 0;
75 while (getline (&line, &line_allocated, fp_info) >= 0)
79 /* skip lines starting with # */
83 /* skip whitespace at beginning of line */
84 for (cp = line; *cp && isspace ((unsigned char) *cp); cp++)
87 /* if *cp is null, the whole line was blank */
91 /* the regular expression is everything up to the first space */
92 for (exp = cp; *cp && !isspace ((unsigned char) *cp); cp++)
97 /* skip whitespace up to the start of the matching value */
98 while (*cp && isspace ((unsigned char) *cp))
101 /* no value to match with the regular expression is an error */
104 error (0, 0, "syntax error at line %d file %s; ignored",
105 line_number, infopath);
110 /* strip the newline off the end of the value */
111 if ((cp = strrchr (value, '\n')) != NULL)
115 * At this point, exp points to the regular expression, and value
116 * points to the value to call the callback routine with. Evaluate
117 * the regular expression against srepos and callback with the value
121 /* save the default value so we have it later if we need it */
122 if (strcmp (exp, "DEFAULT") == 0)
124 if (default_value != NULL)
126 error (0, 0, "Multiple `DEFAULT' lines (%d and %d) in %s file",
127 default_line, line_number, infofile);
128 free (default_value);
130 default_value = xstrdup (value);
131 default_line = line_number;
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
138 * callback or default
140 if (strcmp (exp, "ALL") == 0)
142 if (!(opt & PIOPT_ALL))
143 error (0, 0, "Keyword `ALL' is ignored at line %d in %s file",
144 line_number, infofile);
145 else if ((expanded_value = expand_path (value, infofile,
149 err += callproc (repository, expanded_value, closure);
150 free (expanded_value);
158 /* only first matching, plus "ALL"'s */
161 /* see if the repository matched this regular expression */
162 if ((regex_err = re_comp (exp)) != NULL)
164 error (0, 0, "bad regular expression at line %d file %s: %s",
165 line_number, infofile, regex_err);
168 if (re_exec (srepos) == 0)
169 continue; /* no match */
171 /* it did, so do the callback and note that we did one */
172 if ((expanded_value = expand_path( value, infofile, line_number, 1)
175 err += callproc (repository, expanded_value, closure);
176 free (expanded_value);
182 if (ferror (fp_info))
183 error (0, errno, "cannot read %s", infopath);
184 if (fclose (fp_info) < 0)
185 error (0, errno, "cannot close %s", infopath);
187 /* if we fell through and didn't callback at all, do the default */
188 if (callback_done == 0 && default_value != NULL)
190 if ((expanded_value = expand_path (default_value, infofile,
194 err += callproc (repository, expanded_value, closure);
195 free (expanded_value);
201 /* free up space if necessary */
202 if (default_value != NULL)
203 free (default_value);
213 /* Print a warning and return false if P doesn't look like a string specifying
214 * something that can be converted into a size_t.
216 * Sets *VAL to the parsed value when it is found to be valid. *VAL will not
217 * be altered when false is returned.
220 readSizeT (const char *infopath, const char *option, const char *p,
224 size_t num, factor = 1;
226 if (!strcasecmp ("unlimited", p))
232 /* Record the factor character (kilo, mega, giga, tera). */
233 if (!isdigit (p[strlen(p) - 1]))
235 switch (p[strlen(p) - 1])
238 factor = xtimes (factor, 1024);
240 factor = xtimes (factor, 1024);
242 factor = xtimes (factor, 1024);
244 factor = xtimes (factor, 1024);
248 "%s: Unknown %s factor: `%c'",
249 infopath, option, p[strlen(p)]);
252 TRACE (TRACE_DATA, "readSizeT(): Found factor %u for %s",
256 /* Verify that *q is a number. */
258 while (q < p + strlen(p) - 1 /* Checked last character above. */)
263 "%s: %s must be a postitive integer, not '%s'",
264 infopath, option, p);
270 /* Compute final value. */
271 num = strtoul (p, NULL, 10);
272 if (num == ULONG_MAX || num > SIZE_MAX)
273 /* Don't return an error, just max out. */
276 TRACE (TRACE_DATA, "readSizeT(): read number %u for %s", num, option);
277 *val = xtimes (strtoul (p, NULL, 10), factor);
278 TRACE (TRACE_DATA, "readSizeT(): returnning %u for %s", *val, option);
284 /* Allocate and initialize a new config struct. */
285 static inline struct config *
288 struct config *new = xcalloc (1, sizeof (struct config));
290 TRACE (TRACE_FLOW, "new_config ()");
292 new->logHistory = xstrdup (ALL_HISTORY_REC_TYPES);
293 new->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
294 new->UserAdminOptions = xstrdup ("k");
295 new->MaxCommentLeaderLength = 20;
297 new->MaxProxyBufferSize = (size_t)(8 * 1024 * 1024); /* 8 megabytes,
300 #endif /* PROXY_SUPPORT */
301 #ifdef AUTH_SERVER_SUPPORT
302 new->system_auth = true;
303 #endif /* AUTH_SERVER_SUPPORT */
311 free_config (struct config *data)
313 if (data->keywords) free_keywords (data->keywords);
319 /* Return true if this function has already been called for line LN of file
323 parse_error (const char *infopath, unsigned int ln)
325 static List *errors = NULL;
326 char *nodename = NULL;
331 nodename = Xasprintf ("%s/%u", infopath, ln);
332 if (findnode (errors, nodename))
338 push_string (errors, nodename);
344 /* Parse the CVS config file. The syntax right now is a bit ad hoc
345 * but tries to draw on the best or more common features of the other
346 * *info files and various unix (or non-unix) config file syntaxes.
347 * Lines starting with # are comments. Settings are lines of the form
348 * KEYWORD=VALUE. There is currently no way to have a multi-line
349 * VALUE (would be nice if there was, probably).
351 * CVSROOT is the $CVSROOT directory
352 * (current_parsed_root->directory might not be set yet, so this
353 * function takes the cvsroot as a function argument).
356 * Always returns a fully initialized config struct, which on error may
357 * contain only the defaults.
360 * Calls error(0, ...) on errors in addition to the return value.
362 * xmalloc() failures are fatal, per usual.
365 parse_config (const char *cvsroot)
370 unsigned int ln; /* Input file line counter. */
371 size_t line_allocated = 0;
374 struct config *retval;
376 TRACE (TRACE_FUNCTION, "parse_config (%s)", cvsroot);
378 retval = new_config ();
380 infopath = Xasprintf ("%s/%s/%s", cvsroot, CVSROOTADM, CVSROOTADM_CONFIG);
382 fp_info = CVS_FOPEN (infopath, "r");
385 /* If no file, don't do anything special. */
386 if (!existence_error (errno))
388 /* Just a warning message; doesn't affect return
389 value, currently at least. */
390 error (0, errno, "cannot open %s", infopath);
396 ln = 0; /* Have not read any lines yet. */
397 while (getline (&line, &line_allocated, fp_info) >= 0)
399 ln++; /* Keep track of input file line number for error messages. */
405 /* At least for the moment we don't skip whitespace at the start
406 of the line. Too picky? Maybe. But being insufficiently
407 picky leads to all sorts of confusion, and it is a lot easier
408 to start out picky and relax it than the other way around.
410 Is there any kind of written standard for the syntax of this
411 sort of config file? Anywhere in POSIX for example (I guess
412 makefiles are sort of close)? Red Hat Linux has a bunch of
413 these too (with some GUI tools which edit them)...
415 Along the same lines, we might want a table of keywords,
416 with various types (boolean, string, &c), as a mechanism
417 for making sure the syntax is consistent. Any good examples
418 to follow there (Apache?)? */
420 /* Strip the trailing newline. There will be one unless we
421 read a partial line without a newline, and then got end of
424 len = strlen (line) - 1;
425 if (line[len] == '\n')
428 /* Skip blank lines. */
432 TRACE (TRACE_DATA, "parse_info() examining line: `%s'", line);
434 /* The first '=' separates keyword from value. */
435 p = strchr (line, '=');
438 if (!parse_error (infopath, ln))
440 "%s [%d]: syntax error: missing `=' between keyword and value",
447 if (strcmp (line, "RCSBIN") == 0)
449 /* This option used to specify the directory for RCS
450 executables. But since we don't run them any more,
451 this is a noop. Silently ignore it so that a
452 repository can work with either new or old CVS. */
455 else if (strcmp (line, "SystemAuth") == 0)
456 #ifdef AUTH_SERVER_SUPPORT
457 readBool (infopath, "SystemAuth", p, &retval->system_auth);
460 /* Still parse the syntax but ignore the option. That way the same
461 * config file can be used for local and server.
464 readBool (infopath, "SystemAuth", p, &dummy);
467 else if (strcmp (line, "LocalKeyword") == 0)
468 RCS_setlocalid (infopath, ln, &retval->keywords, p);
469 else if (strcmp (line, "KeywordExpand") == 0)
470 RCS_setincexc (&retval->keywords, p);
471 else if (strcmp (line, "PreservePermissions") == 0)
473 #ifdef PRESERVE_PERMISSIONS_SUPPORT
474 readBool (infopath, "PreservePermissions", p,
475 &retval->preserve_perms);
477 if (!parse_error (infopath, ln))
479 %s [%u]: warning: this CVS does not support PreservePermissions",
483 else if (strcmp (line, "TopLevelAdmin") == 0)
484 readBool (infopath, "TopLevelAdmin", p, &retval->top_level_admin);
485 else if (strcmp (line, "LockDir") == 0)
487 if (retval->lock_dir != NULL)
488 free (retval->lock_dir);
489 retval->lock_dir = xstrdup (p);
490 /* Could try some validity checking, like whether we can
491 opendir it or something, but I don't see any particular
492 reason to do that now rather than waiting until lock.c. */
494 else if (strcmp (line, "LogHistory") == 0)
496 if (strcmp (p, "all") != 0)
498 static bool gotone = false;
501 %s [%u]: warning: duplicate LogHistory entry found.",
505 free (retval->logHistory);
506 retval->logHistory = xstrdup (p);
509 else if (strcmp (line, "RereadLogAfterVerify") == 0)
511 if (!strcasecmp (p, "never"))
512 retval->RereadLogAfterVerify = LOGMSG_REREAD_NEVER;
513 else if (!strcasecmp (p, "always"))
514 retval->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
515 else if (!strcasecmp (p, "stat"))
516 retval->RereadLogAfterVerify = LOGMSG_REREAD_STAT;
520 if (readBool (infopath, "RereadLogAfterVerify", p, &tmp))
523 retval->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
525 retval->RereadLogAfterVerify = LOGMSG_REREAD_NEVER;
529 else if (strcmp (line, "UserAdminOptions") == 0)
530 retval->UserAdminOptions = xstrdup (p);
531 else if (strcmp (line, "UseNewInfoFmtStrings") == 0)
532 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
533 readBool (infopath, "UseNewInfoFmtStrings", p,
534 &retval->UseNewInfoFmtStrings);
535 #else /* !SUPPORT_OLD_INFO_FMT_STRINGS */
538 if (readBool (infopath, "UseNewInfoFmtStrings", p, &dummy)
541 "%s [%u]: Old style info format strings not supported by this executable.",
544 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
545 else if (strcmp (line, "ImportNewFilesToVendorBranchOnly") == 0)
546 readBool (infopath, "ImportNewFilesToVendorBranchOnly", p,
547 &retval->ImportNewFilesToVendorBranchOnly);
548 else if (strcmp (line, "PrimaryServer") == 0)
549 retval->PrimaryServer = parse_cvsroot (p);
551 else if (!strcmp (line, "MaxProxyBufferSize"))
552 readSizeT (infopath, "MaxProxyBufferSize", p,
553 &retval->MaxProxyBufferSize);
554 #endif /* PROXY_SUPPORT */
555 else if (!strcmp (line, "MaxCommentLeaderLength"))
556 readSizeT (infopath, "MaxCommentLeaderLength", p,
557 &retval->MaxCommentLeaderLength);
558 else if (!strcmp (line, "UseArchiveCommentLeader"))
559 readBool (infopath, "UseArchiveCommentLeader", p,
560 &retval->UseArchiveCommentLeader);
562 /* We may be dealing with a keyword which was added in a
563 subsequent version of CVS. In that case it is a good idea
564 to complain, as (1) the keyword might enable a behavior like
565 alternate locking behavior, in which it is dangerous and hard
566 to detect if some CVS's have it one way and others have it
567 the other way, (2) in general, having us not do what the user
568 had in mind when they put in the keyword violates the
569 principle of least surprise. Note that one corollary is
570 adding new keywords to your CVSROOT/config file is not
571 particularly recommended unless you are planning on using
573 if (!parse_error (infopath, ln))
574 error (0, 0, "%s [%u]: unrecognized keyword `%s'",
577 if (ferror (fp_info))
578 error (0, errno, "cannot read %s", infopath);
579 if (fclose (fp_info) < 0)
580 error (0, errno, "cannot close %s", infopath);