#include "cvs.h"
#include "getline.h"
#include "history.h"
int
Parse_Info (const char *infofile, const char *repository, CALLPROC callproc,
int opt, void *closure)
{
int err = 0;
FILE *fp_info;
char *infopath;
char *line = NULL;
size_t line_allocated = 0;
char *default_value = NULL;
int default_line = 0;
char *expanded_value;
bool callback_done;
int line_number;
char *cp, *exp, *value;
const char *srepos;
const char *regex_err;
assert (repository);
if (!current_parsed_root)
{
error (0, 0, "CVSROOT variable not set");
return 1;
}
infopath = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
CVSROOTADM, infofile);
fp_info = CVS_FOPEN (infopath, "r");
if (!fp_info)
{
if (!existence_error (errno))
error (0, errno, "cannot open %s", infopath);
free (infopath);
return 0;
}
srepos = Short_Repository (repository);
TRACE (TRACE_FUNCTION, "Parse_Info (%s, %s, %s)",
infopath, srepos, (opt & PIOPT_ALL) ? "ALL" : "not ALL");
callback_done = false;
line_number = 0;
while (getline (&line, &line_allocated, fp_info) >= 0)
{
line_number++;
if (line[0] == '#')
continue;
for (cp = line; *cp && isspace ((unsigned char) *cp); cp++)
;
if (*cp == '\0')
continue;
for (exp = cp; *cp && !isspace ((unsigned char) *cp); cp++)
;
if (*cp != '\0')
*cp++ = '\0';
while (*cp && isspace ((unsigned char) *cp))
cp++;
if (*cp == '\0')
{
error (0, 0, "syntax error at line %d file %s; ignored",
line_number, infopath);
continue;
}
value = cp;
cp = strrchr (value, '\n');
if (cp) *cp = '\0';
if (strcmp (exp, "DEFAULT") == 0)
{
if (default_value)
{
error (0, 0, "Multiple `DEFAULT' lines (%d and %d) in %s file",
default_line, line_number, infofile);
free (default_value);
}
default_value = xstrdup (value);
default_line = line_number;
continue;
}
if (strcmp (exp, "ALL") == 0)
{
if (!(opt & PIOPT_ALL))
error (0, 0, "Keyword `ALL' is ignored at line %d in %s file",
line_number, infofile);
else if ((expanded_value =
expand_path (value, current_parsed_root->directory,
true, infofile, line_number)))
{
err += callproc (repository, expanded_value, closure);
free (expanded_value);
}
else
err++;
continue;
}
if (callback_done)
continue;
regex_err = re_comp (exp);
if (regex_err)
{
error (0, 0, "bad regular expression at line %d file %s: %s",
line_number, infofile, regex_err);
continue;
}
if (re_exec (srepos) == 0)
continue;
expanded_value = expand_path (value, current_parsed_root->directory,
true, infofile, line_number);
if (expanded_value)
{
err += callproc (repository, expanded_value, closure);
free (expanded_value);
}
else
err++;
callback_done = true;
}
if (ferror (fp_info))
error (0, errno, "cannot read %s", infopath);
if (fclose (fp_info) < 0)
error (0, errno, "cannot close %s", infopath);
if (!callback_done && default_value)
{
expanded_value = expand_path (default_value,
current_parsed_root->directory,
true, infofile, line_number);
if (expanded_value)
{
err += callproc (repository, expanded_value, closure);
free (expanded_value);
}
else
err++;
}
if (default_value) free (default_value);
free (infopath);
if (line) free (line);
return err;
}
static bool
readSizeT (const char *infopath, const char *option, const char *p,
size_t *val)
{
const char *q;
size_t num, factor = 1;
if (!strcasecmp ("unlimited", p))
{
*val = SIZE_MAX;
return true;
}
if (!isdigit (p[strlen(p) - 1]))
{
switch (p[strlen(p) - 1])
{
case 'T':
factor = xtimes (factor, 1024);
case 'G':
factor = xtimes (factor, 1024);
case 'M':
factor = xtimes (factor, 1024);
case 'k':
factor = xtimes (factor, 1024);
break;
default:
error (0, 0,
"%s: Unknown %s factor: `%c'",
infopath, option, p[strlen(p)]);
return false;
}
TRACE (TRACE_DATA, "readSizeT(): Found factor %u for %s",
factor, option);
}
q = p;
while (q < p + strlen(p) - 1 )
{
if (!isdigit(*q))
{
error (0, 0,
"%s: %s must be a postitive integer, not '%s'",
infopath, option, p);
return false;
}
q++;
}
num = strtoul (p, NULL, 10);
if (num == ULONG_MAX || num > SIZE_MAX)
num = SIZE_MAX;
TRACE (TRACE_DATA, "readSizeT(): read number %u for %s", num, option);
*val = xtimes (strtoul (p, NULL, 10), factor);
TRACE (TRACE_DATA, "readSizeT(): returnning %u for %s", *val, option);
return true;
}
static inline struct config *
new_config (void)
{
struct config *new = xcalloc (1, sizeof (struct config));
TRACE (TRACE_FLOW, "new_config ()");
new->logHistory = xstrdup (ALL_HISTORY_REC_TYPES);
new->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
new->UserAdminOptions = xstrdup ("k");
new->MaxCommentLeaderLength = 20;
#ifdef SERVER_SUPPORT
new->MaxCompressionLevel = 9;
#endif
#ifdef PROXY_SUPPORT
new->MaxProxyBufferSize = (size_t)(8 * 1024 * 1024);
#endif
#ifdef AUTH_SERVER_SUPPORT
new->system_auth = true;
#endif
return new;
}
void
free_config (struct config *data)
{
if (data->keywords) free_keywords (data->keywords);
free (data);
}
bool
parse_error (const char *infopath, unsigned int ln)
{
static List *errors = NULL;
char *nodename = NULL;
if (!errors)
errors = getlist();
nodename = Xasprintf ("%s/%u", infopath, ln);
if (findnode (errors, nodename))
{
free (nodename);
return true;
}
push_string (errors, nodename);
return false;
}
#ifdef ALLOW_CONFIG_OVERRIDE
const char * const allowed_config_prefixes[] = { ALLOW_CONFIG_OVERRIDE };
#endif
struct config *
parse_config (const char *cvsroot, const char *path)
{
const char *infopath;
char *freeinfopath = NULL;
FILE *fp_info;
char *line = NULL;
unsigned int ln;
char *buf = NULL;
size_t buf_allocated = 0;
size_t len;
char *p;
struct config *retval;
bool processing = true;
bool processed = true;
TRACE (TRACE_FUNCTION, "parse_config (%s)", cvsroot);
#ifdef ALLOW_CONFIG_OVERRIDE
if (path)
{
const char * const *prefix;
char *npath = xcanonicalize_file_name (path);
bool approved = false;
for (prefix = allowed_config_prefixes; *prefix != NULL; prefix++)
{
char *nprefix;
if (!isreadable (*prefix)) continue;
nprefix = xcanonicalize_file_name (*prefix);
if (!strncmp (nprefix, npath, strlen (nprefix))
&& (((*prefix)[strlen (*prefix)] != '/'
&& strlen (npath) == strlen (nprefix))
|| ((*prefix)[strlen (*prefix)] == '/'
&& npath[strlen (nprefix)] == '/')))
approved = true;
free (nprefix);
if (approved) break;
}
if (!approved)
error (1, 0, "Invalid path to config file specified: `%s'",
path);
infopath = path;
free (npath);
}
else
#endif
infopath = freeinfopath =
Xasprintf ("%s/%s/%s", cvsroot, CVSROOTADM, CVSROOTADM_CONFIG);
retval = new_config ();
fp_info = CVS_FOPEN (infopath, "r");
if (!fp_info)
{
if (!existence_error (errno))
{
error (0, errno, "cannot open %s", infopath);
}
if (freeinfopath) free (freeinfopath);
return retval;
}
ln = 0;
while (getline (&buf, &buf_allocated, fp_info) >= 0)
{
ln++;
line = buf;
while (isspace (*line)) line++;
if (line[0] == '#')
continue;
len = strlen (line) - 1;
if (line[len] == '\n')
line[len--] = '\0';
if (line[0] == '\0')
continue;
TRACE (TRACE_DATA, "parse_info() examining line: `%s'", line);
if (line[0] == '[' && line[len] == ']')
{
cvsroot_t *tmproot;
line++[len] = '\0';
tmproot = parse_cvsroot (line);
if (!tmproot
#if defined CLIENT_SUPPORT || defined SERVER_SUPPORT
|| (tmproot->method != local_method
&& (!tmproot->hostname || !isThisHost (tmproot->hostname)))
#endif
|| !isSamePath (tmproot->directory, cvsroot))
{
if (processed) processing = false;
}
else
{
TRACE (TRACE_FLOW, "Matched root section`%s'", line);
processing = true;
processed = false;
}
continue;
}
processed = true;
if (!processing)
continue;
p = strchr (line, '=');
if (!p)
{
if (!parse_error (infopath, ln))
error (0, 0,
"%s [%d]: syntax error: missing `=' between keyword and value",
infopath, ln);
continue;
}
*p++ = '\0';
if (strcmp (line, "RCSBIN") == 0)
{
;
}
else if (strcmp (line, "SystemAuth") == 0)
#ifdef AUTH_SERVER_SUPPORT
readBool (infopath, "SystemAuth", p, &retval->system_auth);
#else
{
bool dummy;
readBool (infopath, "SystemAuth", p, &dummy);
}
#endif
else if (strcmp (line, "LocalKeyword") == 0)
RCS_setlocalid (infopath, ln, &retval->keywords, p);
else if (strcmp (line, "KeywordExpand") == 0)
RCS_setincexc (&retval->keywords, p);
else if (strcmp (line, "PreservePermissions") == 0)
{
#ifdef PRESERVE_PERMISSIONS_SUPPORT
readBool (infopath, "PreservePermissions", p,
&retval->preserve_perms);
#else
if (!parse_error (infopath, ln))
error (0, 0, "\
%s [%u]: warning: this CVS does not support PreservePermissions",
infopath, ln);
#endif
}
else if (strcmp (line, "TopLevelAdmin") == 0)
readBool (infopath, "TopLevelAdmin", p, &retval->top_level_admin);
else if (strcmp (line, "LockDir") == 0)
{
if (retval->lock_dir)
free (retval->lock_dir);
retval->lock_dir = expand_path (p, cvsroot, false, infopath, ln);
}
else if (strcmp (line, "HistoryLogPath") == 0)
{
if (retval->HistoryLogPath) free (retval->HistoryLogPath);
retval->HistoryLogPath = expand_path (p, cvsroot, false,
infopath, ln);
if (retval->HistoryLogPath && !ISABSOLUTE (retval->HistoryLogPath))
{
error (0, 0, "%s [%u]: HistoryLogPath must be absolute.",
infopath, ln);
free (retval->HistoryLogPath);
retval->HistoryLogPath = NULL;
}
}
else if (strcmp (line, "HistorySearchPath") == 0)
{
if (retval->HistorySearchPath) free (retval->HistorySearchPath);
retval->HistorySearchPath = expand_path (p, cvsroot, false,
infopath, ln);
if (retval->HistorySearchPath
&& !ISABSOLUTE (retval->HistorySearchPath))
{
error (0, 0, "%s [%u]: HistorySearchPath must be absolute.",
infopath, ln);
free (retval->HistorySearchPath);
retval->HistorySearchPath = NULL;
}
}
else if (strcmp (line, "LogHistory") == 0)
{
if (strcmp (p, "all") != 0)
{
static bool gotone = false;
if (gotone)
error (0, 0, "\
%s [%u]: warning: duplicate LogHistory entry found.",
infopath, ln);
else
gotone = true;
free (retval->logHistory);
retval->logHistory = xstrdup (p);
}
}
else if (strcmp (line, "RereadLogAfterVerify") == 0)
{
if (!strcasecmp (p, "never"))
retval->RereadLogAfterVerify = LOGMSG_REREAD_NEVER;
else if (!strcasecmp (p, "always"))
retval->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
else if (!strcasecmp (p, "stat"))
retval->RereadLogAfterVerify = LOGMSG_REREAD_STAT;
else
{
bool tmp;
if (readBool (infopath, "RereadLogAfterVerify", p, &tmp))
{
if (tmp)
retval->RereadLogAfterVerify = LOGMSG_REREAD_ALWAYS;
else
retval->RereadLogAfterVerify = LOGMSG_REREAD_NEVER;
}
}
}
else if (strcmp (line, "TmpDir") == 0)
{
if (retval->TmpDir) free (retval->TmpDir);
retval->TmpDir = expand_path (p, cvsroot, false, infopath, ln);
}
else if (strcmp (line, "UserAdminOptions") == 0)
retval->UserAdminOptions = xstrdup (p);
else if (strcmp (line, "UseNewInfoFmtStrings") == 0)
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
readBool (infopath, "UseNewInfoFmtStrings", p,
&retval->UseNewInfoFmtStrings);
#else
{
bool dummy;
if (readBool (infopath, "UseNewInfoFmtStrings", p, &dummy)
&& !dummy)
error (1, 0,
"%s [%u]: Old style info format strings not supported by this executable.",
infopath, ln);
}
#endif
else if (strcmp (line, "ImportNewFilesToVendorBranchOnly") == 0)
readBool (infopath, "ImportNewFilesToVendorBranchOnly", p,
&retval->ImportNewFilesToVendorBranchOnly);
else if (strcmp (line, "PrimaryServer") == 0)
retval->PrimaryServer = parse_cvsroot (p);
#ifdef PROXY_SUPPORT
else if (!strcmp (line, "MaxProxyBufferSize"))
readSizeT (infopath, "MaxProxyBufferSize", p,
&retval->MaxProxyBufferSize);
#endif
else if (!strcmp (line, "MaxCommentLeaderLength"))
readSizeT (infopath, "MaxCommentLeaderLength", p,
&retval->MaxCommentLeaderLength);
else if (!strcmp (line, "UseArchiveCommentLeader"))
readBool (infopath, "UseArchiveCommentLeader", p,
&retval->UseArchiveCommentLeader);
#ifdef SERVER_SUPPORT
else if (!strcmp (line, "MinCompressionLevel"))
readSizeT (infopath, "MinCompressionLevel", p,
&retval->MinCompressionLevel);
else if (!strcmp (line, "MaxCompressionLevel"))
readSizeT (infopath, "MaxCompressionLevel", p,
&retval->MaxCompressionLevel);
#endif
else
if (!parse_error (infopath, ln))
error (0, 0, "%s [%u]: unrecognized keyword `%s'",
infopath, ln, line);
}
if (ferror (fp_info))
error (0, errno, "cannot read %s", infopath);
if (fclose (fp_info) < 0)
error (0, errno, "cannot close %s", infopath);
if (freeinfopath) free (freeinfopath);
if (buf) free (buf);
return retval;
}