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.
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
21 * lines matching "ALL", or if no lines match, the last line matching
24 * Return 0 for success, -1 if there was not an INFOFILE, and >0 for failure.
27 Parse_Info (const char *infofile
, const char *repository
, CALLPROC callproc
,
28 int opt
, void *closure
)
34 size_t line_allocated
= 0;
35 char *default_value
= NULL
;
40 char *cp
, *exp
, *value
;
42 const char *regex_err
;
46 if (!current_parsed_root
)
48 /* XXX - should be error maybe? */
49 error (0, 0, "CVSROOT variable not set");
53 /* find the info file and open it */
54 infopath
= Xasprintf ("%s/%s/%s", current_parsed_root
->directory
,
55 CVSROOTADM
, infofile
);
56 fp_info
= CVS_FOPEN (infopath
, "r");
59 /* If no file, don't do anything special. */
60 if (!existence_error (errno
))
61 error (0, errno
, "cannot open %s", infopath
);
66 /* strip off the CVSROOT if repository was absolute */
67 srepos
= Short_Repository (repository
);
69 TRACE (TRACE_FUNCTION
, "Parse_Info (%s, %s, %s)",
70 infopath
, srepos
, (opt
& PIOPT_ALL
) ? "ALL" : "not ALL");
72 /* search the info file for lines that match */
73 callback_done
= false;
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 cp
= strrchr (value
, '\n');
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)
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
=
146 expand_path (value
, current_parsed_root
->directory
,
147 true, infofile
, line_number
)))
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 regex_err
= re_comp (exp
);
165 error (0, 0, "bad regular expression at line %d file %s: %s",
166 line_number
, infofile
, regex_err
);
169 if (re_exec (srepos
) == 0)
170 continue; /* no match */
172 /* it did, so do the callback and note that we did one */
173 expanded_value
= expand_path (value
, current_parsed_root
->directory
,
174 true, infofile
, line_number
);
177 err
+= callproc (repository
, expanded_value
, closure
);
178 free (expanded_value
);
182 callback_done
= true;
184 if (ferror (fp_info
))
185 error (0, errno
, "cannot read %s", infopath
);
186 if (fclose (fp_info
) < 0)
187 error (0, errno
, "cannot close %s", infopath
);
189 /* if we fell through and didn't callback at all, do the default */
190 if (!callback_done
&& default_value
)
192 expanded_value
= expand_path (default_value
,
193 current_parsed_root
->directory
,
194 true, infofile
, line_number
);
197 err
+= callproc (repository
, expanded_value
, closure
);
198 free (expanded_value
);
204 /* free up space if necessary */
205 if (default_value
) free (default_value
);
207 if (line
) free (line
);
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.
217 * Sets *VAL to the parsed value when it is found to be valid. *VAL will not
218 * be altered when false is returned.
221 readSizeT (const char *infopath
, const char *option
, const char *p
,
225 size_t num
, factor
= 1;
227 if (!strcasecmp ("unlimited", p
))
233 /* Record the factor character (kilo, mega, giga, tera). */
234 if (!isdigit (p
[strlen(p
) - 1]))
236 switch (p
[strlen(p
) - 1])
239 factor
= xtimes (factor
, 1024);
241 factor
= xtimes (factor
, 1024);
243 factor
= xtimes (factor
, 1024);
245 factor
= xtimes (factor
, 1024);
249 "%s: Unknown %s factor: `%c'",
250 infopath
, option
, p
[strlen(p
)]);
253 TRACE (TRACE_DATA
, "readSizeT(): Found factor %u for %s",
257 /* Verify that *q is a number. */
259 while (q
< p
+ strlen(p
) - 1 /* Checked last character above. */)
264 "%s: %s must be a postitive integer, not '%s'",
265 infopath
, option
, p
);
271 /* Compute final value. */
272 num
= strtoul (p
, NULL
, 10);
273 if (num
== ULONG_MAX
|| num
> SIZE_MAX
)
274 /* Don't return an error, just max out. */
277 TRACE (TRACE_DATA
, "readSizeT(): read number %u for %s", num
, option
);
278 *val
= xtimes (strtoul (p
, NULL
, 10), factor
);
279 TRACE (TRACE_DATA
, "readSizeT(): returnning %u for %s", *val
, option
);
285 /* Allocate and initialize a new config struct. */
286 static inline struct config
*
289 struct config
*new = xcalloc (1, sizeof (struct config
));
291 TRACE (TRACE_FLOW
, "new_config ()");
293 new->logHistory
= xstrdup (ALL_HISTORY_REC_TYPES
);
294 new->RereadLogAfterVerify
= LOGMSG_REREAD_ALWAYS
;
295 new->UserAdminOptions
= xstrdup ("k");
296 #ifdef CVS_ADMIN_GROUP
297 new->UserAdminGroup
= xstrdup (CVS_ADMIN_GROUP
);
299 new->UserAdminGroup
= NULL
;
301 new->MaxCommentLeaderLength
= 20;
302 #ifdef SERVER_SUPPORT
303 new->MaxCompressionLevel
= 9;
304 #endif /* SERVER_SUPPORT */
306 new->MaxProxyBufferSize
= (size_t)(8 * 1024 * 1024); /* 8 megabytes,
309 #endif /* PROXY_SUPPORT */
310 #ifdef AUTH_SERVER_SUPPORT
311 new->system_auth
= true;
312 #endif /* AUTH_SERVER_SUPPORT */
320 free_config (struct config
*data
)
322 if (data
->keywords
) free_keywords (data
->keywords
);
328 /* Return true if this function has already been called for line LN of file
332 parse_error (const char *infopath
, unsigned int ln
)
334 static List
*errors
= NULL
;
335 char *nodename
= NULL
;
340 nodename
= Xasprintf ("%s/%u", infopath
, ln
);
341 if (findnode (errors
, nodename
))
347 push_string (errors
, nodename
);
353 #ifdef ALLOW_CONFIG_OVERRIDE
354 const char * const allowed_config_prefixes
[] = { ALLOW_CONFIG_OVERRIDE
};
355 #endif /* ALLOW_CONFIG_OVERRIDE */
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
361 * *info files and various unix (or non-unix) config file syntaxes.
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
364 * VALUE (would be nice if there was, probably).
366 * CVSROOT is the $CVSROOT directory
367 * (current_parsed_root->directory might not be set yet, so this
368 * function takes the cvsroot as a function argument).
371 * Always returns a fully initialized config struct, which on error may
372 * contain only the defaults.
375 * Calls error(0, ...) on errors in addition to the return value.
377 * xmalloc() failures are fatal, per usual.
380 parse_config (const char *cvsroot
, const char *path
)
382 const char *infopath
;
383 char *freeinfopath
= NULL
;
386 unsigned int ln
; /* Input file line counter. */
388 size_t buf_allocated
= 0;
391 struct config
*retval
;
392 /* PROCESSING Whether config keys are currently being processed for
394 * PROCESSED Whether any keys have been processed for this root.
395 * This is initialized to true so that any initial keys
396 * may be processed as global defaults.
398 bool processing
= true;
399 bool processed
= true;
401 TRACE (TRACE_FUNCTION
, "parse_config (%s)", cvsroot
);
403 #ifdef ALLOW_CONFIG_OVERRIDE
406 const char * const *prefix
;
407 char *npath
= xcanonicalize_file_name (path
);
408 bool approved
= false;
409 for (prefix
= allowed_config_prefixes
; *prefix
!= NULL
; prefix
++)
413 if (!isreadable (*prefix
)) continue;
414 nprefix
= xcanonicalize_file_name (*prefix
);
415 if (!strncmp (nprefix
, npath
, strlen (nprefix
))
416 && (((*prefix
)[strlen (*prefix
)] != '/'
417 && strlen (npath
) == strlen (nprefix
))
418 || ((*prefix
)[strlen (*prefix
)] == '/'
419 && npath
[strlen (nprefix
)] == '/')))
425 error (1, 0, "Invalid path to config file specified: `%s'",
432 infopath
= freeinfopath
=
433 Xasprintf ("%s/%s/%s", cvsroot
, CVSROOTADM
, CVSROOTADM_CONFIG
);
435 retval
= new_config ();
437 fp_info
= CVS_FOPEN (infopath
, "r");
440 /* If no file, don't do anything special. */
441 if (!existence_error (errno
))
443 /* Just a warning message; doesn't affect return
444 value, currently at least. */
445 error (0, errno
, "cannot open %s", infopath
);
447 if (freeinfopath
) free (freeinfopath
);
451 ln
= 0; /* Have not read any lines yet. */
452 while (getline (&buf
, &buf_allocated
, fp_info
) >= 0)
454 ln
++; /* Keep track of input file line number for error messages. */
458 /* Skip leading white space. */
459 while (isspace (*line
)) line
++;
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
467 makefiles are sort of close)? Red Hat Linux has a bunch of
468 these too (with some GUI tools which edit them)...
470 Along the same lines, we might want a table of keywords,
471 with various types (boolean, string, &c), as a mechanism
472 for making sure the syntax is consistent. Any good examples
473 to follow there (Apache?)? */
475 /* Strip the trailing newline. There will be one unless we
476 read a partial line without a newline, and then got end of
479 len
= strlen (line
) - 1;
480 if (line
[len
] == '\n')
483 /* Skip blank lines. */
487 TRACE (TRACE_DATA
, "parse_info() examining line: `%s'", line
);
489 /* Check for a root specification. */
490 if (line
[0] == '[' && line
[len
] == ']')
495 tmproot
= parse_cvsroot (line
);
497 /* Ignoring method. */
499 #if defined CLIENT_SUPPORT || defined SERVER_SUPPORT
500 || (tmproot
->method
!= local_method
501 && (!tmproot
->hostname
|| !isThisHost (tmproot
->hostname
)))
502 #endif /* CLIENT_SUPPORT || SERVER_SUPPORT */
503 || !isSamePath (tmproot
->directory
, cvsroot
))
505 if (processed
) processing
= false;
509 TRACE (TRACE_FLOW
, "Matched root section`%s'", line
);
517 /* There is data on this line. */
519 /* Even if the data is bad or ignored, consider data processed for
525 /* ...but it is for a different root. */
528 /* The first '=' separates keyword from value. */
529 p
= strchr (line
, '=');
532 if (!parse_error (infopath
, ln
))
534 "%s [%d]: syntax error: missing `=' between keyword and value",
541 if (strcmp (line
, "RCSBIN") == 0)
543 /* This option used to specify the directory for RCS
544 executables. But since we don't run them any more,
545 this is a noop. Silently ignore it so that a
546 repository can work with either new or old CVS. */
549 else if (strcmp (line
, "SystemAuth") == 0)
550 #ifdef AUTH_SERVER_SUPPORT
551 readBool (infopath
, "SystemAuth", p
, &retval
->system_auth
);
554 /* Still parse the syntax but ignore the option. That way the same
555 * config file can be used for local and server.
558 readBool (infopath
, "SystemAuth", p
, &dummy
);
561 else if (strcmp (line
, "LocalKeyword") == 0 ||
562 strcmp (line
, "tag") == 0)
563 RCS_setlocalid (infopath
, ln
, &retval
->keywords
, p
);
564 else if (strcmp (line
, "KeywordExpand") == 0)
565 RCS_setincexc (&retval
->keywords
, p
);
566 else if (strcmp (line
, "PreservePermissions") == 0)
568 #ifdef PRESERVE_PERMISSIONS_SUPPORT
569 readBool (infopath
, "PreservePermissions", p
,
570 &retval
->preserve_perms
);
572 if (!parse_error (infopath
, ln
))
574 %s [%u]: warning: this CVS does not support PreservePermissions",
578 else if (strcmp (line
, "TopLevelAdmin") == 0)
579 readBool (infopath
, "TopLevelAdmin", p
, &retval
->top_level_admin
);
580 else if (strcmp (line
, "LockDir") == 0)
582 if (retval
->lock_dir
)
583 free (retval
->lock_dir
);
584 retval
->lock_dir
= expand_path (p
, cvsroot
, false, infopath
, ln
);
585 /* Could try some validity checking, like whether we can
586 opendir it or something, but I don't see any particular
587 reason to do that now rather than waiting until lock.c. */
589 else if (strcmp (line
, "HistoryLogPath") == 0)
591 if (retval
->HistoryLogPath
) free (retval
->HistoryLogPath
);
593 /* Expand ~ & $VARs. */
594 retval
->HistoryLogPath
= expand_path (p
, cvsroot
, false,
597 if (retval
->HistoryLogPath
&& !ISABSOLUTE (retval
->HistoryLogPath
))
599 error (0, 0, "%s [%u]: HistoryLogPath must be absolute.",
601 free (retval
->HistoryLogPath
);
602 retval
->HistoryLogPath
= NULL
;
605 else if (strcmp (line
, "HistorySearchPath") == 0)
607 if (retval
->HistorySearchPath
) free (retval
->HistorySearchPath
);
608 retval
->HistorySearchPath
= expand_path (p
, cvsroot
, false,
611 if (retval
->HistorySearchPath
612 && !ISABSOLUTE (retval
->HistorySearchPath
))
614 error (0, 0, "%s [%u]: HistorySearchPath must be absolute.",
616 free (retval
->HistorySearchPath
);
617 retval
->HistorySearchPath
= NULL
;
620 else if (strcmp (line
, "LogHistory") == 0)
622 if (strcmp (p
, "all") != 0)
624 static bool gotone
= false;
627 %s [%u]: warning: duplicate LogHistory entry found.",
631 free (retval
->logHistory
);
632 retval
->logHistory
= xstrdup (p
);
635 else if (strcmp (line
, "RereadLogAfterVerify") == 0)
637 if (!strcasecmp (p
, "never"))
638 retval
->RereadLogAfterVerify
= LOGMSG_REREAD_NEVER
;
639 else if (!strcasecmp (p
, "always"))
640 retval
->RereadLogAfterVerify
= LOGMSG_REREAD_ALWAYS
;
641 else if (!strcasecmp (p
, "stat"))
642 retval
->RereadLogAfterVerify
= LOGMSG_REREAD_STAT
;
646 if (readBool (infopath
, "RereadLogAfterVerify", p
, &tmp
))
649 retval
->RereadLogAfterVerify
= LOGMSG_REREAD_ALWAYS
;
651 retval
->RereadLogAfterVerify
= LOGMSG_REREAD_NEVER
;
655 else if (strcmp (line
, "TmpDir") == 0)
657 if (retval
->TmpDir
) free (retval
->TmpDir
);
658 retval
->TmpDir
= expand_path (p
, cvsroot
, false, infopath
, ln
);
659 /* Could try some validity checking, like whether we can
660 * opendir it or something, but I don't see any particular
661 * reason to do that now rather than when the first function
662 * tries to create a temp file.
665 else if (strcmp (line
, "UserAdminGroup") == 0
666 || strcmp (line
, "AdminGroup") == 0)
667 retval
->UserAdminGroup
= xstrdup (p
);
668 else if (strcmp (line
, "UserAdminOptions") == 0
669 || strcmp (line
, "AdminOptions") == 0)
670 retval
->UserAdminOptions
= xstrdup (p
);
671 else if (strcmp (line
, "UseNewInfoFmtStrings") == 0)
672 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
673 readBool (infopath
, "UseNewInfoFmtStrings", p
,
674 &retval
->UseNewInfoFmtStrings
);
675 #else /* !SUPPORT_OLD_INFO_FMT_STRINGS */
678 if (readBool (infopath
, "UseNewInfoFmtStrings", p
, &dummy
)
681 "%s [%u]: Old style info format strings not supported by this executable.",
684 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
685 else if (strcmp (line
, "ImportNewFilesToVendorBranchOnly") == 0)
686 readBool (infopath
, "ImportNewFilesToVendorBranchOnly", p
,
687 &retval
->ImportNewFilesToVendorBranchOnly
);
688 else if (strcmp (line
, "PrimaryServer") == 0)
689 retval
->PrimaryServer
= parse_cvsroot (p
);
691 else if (!strcmp (line
, "MaxProxyBufferSize"))
692 readSizeT (infopath
, "MaxProxyBufferSize", p
,
693 &retval
->MaxProxyBufferSize
);
694 #endif /* PROXY_SUPPORT */
695 else if (!strcmp (line
, "MaxCommentLeaderLength"))
696 readSizeT (infopath
, "MaxCommentLeaderLength", p
,
697 &retval
->MaxCommentLeaderLength
);
698 else if (!strcmp (line
, "UseArchiveCommentLeader"))
699 readBool (infopath
, "UseArchiveCommentLeader", p
,
700 &retval
->UseArchiveCommentLeader
);
701 #ifdef SERVER_SUPPORT
702 else if (!strcmp (line
, "MinCompressionLevel"))
703 readSizeT (infopath
, "MinCompressionLevel", p
,
704 &retval
->MinCompressionLevel
);
705 else if (!strcmp (line
, "MaxCompressionLevel"))
706 readSizeT (infopath
, "MaxCompressionLevel", p
,
707 &retval
->MaxCompressionLevel
);
708 #endif /* SERVER_SUPPORT */
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
712 to complain, as (1) the keyword might enable a behavior like
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
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
717 principle of least surprise. Note that one corollary is
718 adding new keywords to your CVSROOT/config file is not
719 particularly recommended unless you are planning on using
721 if (!parse_error (infopath
, ln
))
722 error (0, 0, "%s [%u]: unrecognized keyword `%s'",
725 if (ferror (fp_info
))
726 error (0, errno
, "cannot read %s", infopath
);
727 if (fclose (fp_info
) < 0)
728 error (0, errno
, "cannot close %s", infopath
);
729 if (freeinfopath
) free (freeinfopath
);