#include "conffile.h"
#include "xlog.h"
-static void conf_load_defaults (int);
-#if 0
-static int conf_find_trans_xf (int, char *);
-#endif
+#pragma GCC visibility push(hidden)
-size_t strlcpy(char *, const char *, size_t);
+static void conf_load_defaults(void);
+static int conf_set(int , char *, char *, char *,
+ char *, int , int );
struct conf_trans {
TAILQ_ENTRY (conf_trans) link;
int trans;
enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op;
char *section;
+ char *arg;
char *tag;
char *value;
int override;
struct conf_binding {
LIST_ENTRY (conf_binding) link;
char *section;
+ char *arg;
char *tag;
char *value;
int is_default;
LIST_REMOVE(cb, link);
xlog(LOG_INFO,"[%s]:%s->%s removed", section, tag, cb->value);
free(cb->section);
+ free(cb->arg);
free(cb->tag);
free(cb->value);
free(cb);
LIST_REMOVE(cb, link);
xlog(LOG_INFO, "[%s]:%s->%s removed", section, cb->tag, cb->value);
free(cb->section);
+ free(cb->arg);
free(cb->tag);
free(cb->value);
free(cb);
* into SECTION of our configuration database.
*/
static int
-conf_set_now(char *section, char *tag, char *value, int override,
- int is_default)
+conf_set_now(char *section, char *arg, char *tag,
+ char *value, int override, int is_default)
{
struct conf_binding *node = 0;
if (override)
conf_remove_now(section, tag);
- else if (conf_get_str(section, tag)) {
+ else if (conf_get_section(section, arg, tag)) {
if (!is_default) {
xlog(LOG_INFO, "conf_set: duplicate tag [%s]:%s, ignoring...\n",
section, tag);
}
return 1;
}
-
node = calloc(1, sizeof *node);
if (!node) {
xlog_warn("conf_set: calloc (1, %lu) failed", (unsigned long)sizeof *node);
return 1;
}
node->section = strdup(section);
+ if (arg)
+ node->arg = strdup(arg);
node->tag = strdup(tag);
node->value = strdup(value);
node->is_default = is_default;
static void
conf_parse_line(int trans, char *line, size_t sz)
{
- char *val;
- size_t i;
- int j;
+ char *val, *ptr;
+ size_t i, valsize;
+ size_t j;
static char *section = 0;
+ static char *arg = 0;
static int ln = 0;
/* Lines starting with '#' or ';' are comments. */
ln++;
+ /* Ignore blank lines */
+ if (*line == '\0')
+ return;
+
+ /* Strip off any leading blanks */
+ while (isblank(*line))
+ line++;
+
if (*line == '#' || *line == ';')
return;
/* '[section]' parsing... */
if (*line == '[') {
- for (i = 1; i < sz; i++)
- if (line[i] == ']')
+ line++;
+ /* Strip off any blanks after '[' */
+ while (isblank(*line))
+ line++;
+ for (i = 0; i < sz; i++) {
+ if (line[i] == ']') {
break;
+ }
+ }
if (section)
- free (section);
+ free(section);
if (i == sz) {
- xlog_warn("conf_parse_line: %d:"
+ xlog_warn("config file error: line %d: "
"non-matched ']', ignoring until next section", ln);
section = 0;
return;
}
- section = malloc(i);
+ /* Strip off any blanks before ']' */
+ val = line;
+ j=0;
+ while (*val && !isblank(*val))
+ val++, j++;
+ if (*val)
+ i = j;
+ section = malloc(i+1);
if (!section) {
xlog_warn("conf_parse_line: %d: malloc (%lu) failed", ln,
(unsigned long)i);
return;
}
- strlcpy(section, line + 1, i);
+ strncpy(section, line, i);
+ section[i] = '\0';
+
+ if (arg)
+ free(arg);
+ arg = 0;
+
+ ptr = strchr(val, '"');
+ if (ptr == NULL)
+ return;
+ line = ++ptr;
+ while (*ptr && *ptr != '"' && *ptr != ']')
+ ptr++;
+ if (*ptr == '\0' || *ptr == ']') {
+ xlog_warn("config file error: line %d: "
+ "non-matched '\"', ignoring until next section", ln);
+ } else {
+ *ptr = '\0';
+ arg = strdup(line);
+ if (!arg)
+ xlog_warn("conf_parse_line: %d: malloc arg failed", ln);
+ }
return;
}
if (line[i] == '=') {
/* If no section, we are ignoring the lines. */
if (!section) {
- xlog_warn("conf_parse_line: %d: ignoring line due to no section",
- ln);
+ xlog_warn("config file error: line %d: "
+ "ignoring line due to no section", ln);
return;
}
line[strcspn (line, " \t=")] = '\0';
val = line + i + 1 + strspn (line + i + 1, " \t");
- /* Skip trailing whitespace, if any */
- for (j = sz - (val - line) - 1; j > 0 && isspace(val[j]); j--)
- val[j] = '\0';
+ valsize = 0;
+ while (val[valsize++]);
+
+ /* Skip trailing spaces and comments */
+ for (j = 0; j < valsize; j++) {
+ if (val[j] == '#' || val[j] == ';' || isspace(val[j])) {
+ val[j] = '\0';
+ break;
+ }
+ }
/* XXX Perhaps should we not ignore errors? */
- conf_set(trans, section, line, val, 0, 0);
+ conf_set(trans, section, arg, line, val, 0, 0);
return;
}
}
/* Other non-empty lines are weird. */
i = strspn(line, " \t");
if (line[i])
- xlog_warn("conf_parse_line: %d: syntax error", ln);
+ xlog_warn("config file error: line %d:", ln);
return;
}
}
static void
-conf_load_defaults(int tr)
+conf_load_defaults(void)
{
/* No defaults */
return;
trans = conf_begin();
/* Load default configuration values. */
- conf_load_defaults(trans);
+ conf_load_defaults();
/* Free potential existing configuration. */
if (conf_addr) {
}
return 0;
}
+/*
+ * Find a section that may or may not have an argument
+ */
+char *
+conf_get_section(char *section, char *arg, char *tag)
+{
+ struct conf_binding *cb;
+
+ cb = LIST_FIRST (&conf_bindings[conf_hash (section)]);
+ for (; cb; cb = LIST_NEXT (cb, link)) {
+ if (strcasecmp(section, cb->section) != 0)
+ continue;
+ if (arg && strcasecmp(arg, cb->arg) != 0)
+ continue;
+ if (strcasecmp(tag, cb->tag) != 0)
+ continue;
+ return cb->value;
+ }
+ return 0;
+}
/*
* Build a list of string values out of the comma separated value denoted by
}
/* Queue a set operation. */
-int
-conf_set(int transaction, char *section, char *tag,
- char *value, int override, int is_default)
+static int
+conf_set(int transaction, char *section, char *arg,
+ char *tag, char *value, int override, int is_default)
{
struct conf_trans *node;
xlog_warn("conf_set: strdup(\"%s\") failed", section);
goto fail;
}
+ /* Make Section names case-insensitive */
+ upper2lower(node->section);
+
+ if (arg) {
+ node->arg = strdup(arg);
+ if (!node->arg) {
+ xlog_warn("conf_set: strdup(\"%s\") failed", arg);
+ goto fail;
+ }
+ } else
+ node->arg = NULL;
+
node->tag = strdup(tag);
if (!node->tag) {
xlog_warn("conf_set: strdup(\"%s\") failed", tag);
if (commit) {
switch (node->op) {
case CONF_SET:
- conf_set_now(node->section, node->tag, node->value,
- node->override, node->is_default);
+ conf_set_now(node->section, node->arg,
+ node->tag, node->value, node->override,
+ node->is_default);
break;
case CONF_REMOVE:
conf_remove_now(node->section, node->tag);
conf_report (void)
{
struct conf_binding *cb, *last = 0;
- unsigned int i, len;
+ unsigned int i, len, diff_arg = 0;
char *current_section = (char *)0;
+ char *current_arg = (char *)0;
struct dumper *dumper, *dnode;
dumper = dnode = (struct dumper *)calloc(1, sizeof *dumper);
for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
for (cb = LIST_FIRST(&conf_bindings[i]); cb; cb = LIST_NEXT(cb, link)) {
if (!cb->is_default) {
+ /* Make sure the Section arugment is the same */
+ if (current_arg && current_section && cb->arg) {
+ if (strcmp(cb->section, current_section) == 0 &&
+ strcmp(cb->arg, current_arg) != 0)
+ diff_arg = 1;
+ }
/* Dump this entry. */
- if (!current_section || strcmp(cb->section, current_section)) {
- if (current_section) {
+ if (!current_section || strcmp(cb->section, current_section)
+ || diff_arg) {
+ if (current_section || diff_arg) {
len = strlen (current_section) + 3;
+ if (current_arg)
+ len += strlen(current_arg) + 3;
dnode->s = malloc(len);
if (!dnode->s)
goto mem_fail;
- snprintf(dnode->s, len, "[%s]", current_section);
+ if (current_arg)
+ snprintf(dnode->s, len, "[%s \"%s\"]",
+ current_section, current_arg);
+ else
+ snprintf(dnode->s, len, "[%s]", current_section);
+
dnode->next =
(struct dumper *)calloc(1, sizeof (struct dumper));
dnode = dnode->next;
goto mem_fail;
}
current_section = cb->section;
+ current_arg = cb->arg;
+ diff_arg = 0;
}
dnode->s = cb->tag;
dnode->v = cb->value;
if (last) {
len = strlen(last->section) + 3;
+ if (last->arg)
+ len += strlen(last->arg) + 3;
dnode->s = malloc(len);
if (!dnode->s)
goto mem_fail;
- snprintf(dnode->s, len, "[%s]", last->section);
+ if (last->arg)
+ snprintf(dnode->s, len, "[%s \"%s\"]", last->section, last->arg);
+ else
+ snprintf(dnode->s, len, "[%s]", last->section);
}
conf_report_dump(dumper);
return;