return cs;
}
+/*
+ * Replace pair in a given section with a new pair,
+ * of the given value.
+ */
+int cf_pair_replace(CONF_SECTION *cs, CONF_PAIR *cp, const char *value)
+{
+ CONF_PAIR *newp;
+ CONF_ITEM *ci, *cn, **last;
+
+ newp = cf_pair_alloc(cp->attr, value, cp->operator, cp->value_type,
+ cs);
+ if (!newp) return -1;
+
+ ci = cf_pairtoitem(cp);
+ cn = cf_pairtoitem(newp);
+
+ /*
+ * Find the old one from the linked list, and replace it
+ * with the new one.
+ */
+ for (last = &cs->children; (*last) != NULL; last = &(*last)->next) {
+ if (*last == ci) {
+ cn->next = (*last)->next;
+ *last = cn;
+ ci->next = NULL;
+ break;
+ }
+ }
+
+ rbtree_deletebydata(cs->pair_tree, ci);
+
+ rbtree_insert(cs->pair_tree, cn);
+
+ return 0;
+}
+
/*
* Add an item to a configuration section.
if (!cs->section_tree) {
cs->section_tree = rbtree_create(section_cmp, NULL, 0);
- /* ignore any errors */
+ if (!cs->section_tree) {
+ radlog(L_ERR, "Out of memory");
+ _exit(1);
+ }
}
- if (cs->section_tree) {
- rbtree_insert(cs->section_tree, cs_new); }
+ rbtree_insert(cs->section_tree, cs_new);
/*
* Two names: find the named instance.
* "foo.bar.baz" means "from the root"
*/
} else if (strchr(p, '.') != NULL) {
+ if (!parentcs) goto no_such_item;
+
cs = parentcs;
}
/*
* "foo" is "in the current section, OR in main".
*/
- if ((p == name) && (cs != parentcs)) {
+ if ((p == name) && (parentcs != NULL) && (cs != parentcs)) {
cs = parentcs;
goto retry;
}
* Substitute the value of the variable.
*/
cp = cf_itemtopair(ci);
+ if (!cp->value) {
+ radlog(L_ERR, "%s[%d]: Reference \"%s\" has no value",
+ cf, *lineno, input);
+ return NULL;
+ }
strcpy(p, cp->value);
p += strlen(p);
ptr = end + 1;
char **q;
const char *value;
fr_ipaddr_t ipaddr;
- const CONF_PAIR *cp;
+ const CONF_PAIR *cp = NULL;
char ipbuf[128];
- cp = cf_pair_find(cs, name);
+ if (cs) cp = cf_pair_find(cs, name);
if (cp) {
value = cp->value;
if (value == dflt) {
char buffer[8192];
- int lineno = cs->item.lineno;
+ int lineno = 0;
+
+ if (cs) lineno = cs->item.lineno;
/*
* FIXME: sizeof(buffer)?
if (value == dflt) {
char buffer[8192];
- int lineno = cs->item.lineno;
+ int lineno = 0;
+
+ if (cs) lineno = cs->item.lineno;
/*
* FIXME: sizeof(buffer)?
* Parse error.
*/
if (*q != '{') {
+ DEBUG2("Expected open brace '{' after condition at %s", p);
return 0;
}
}
}
+ DEBUG3("Unexpected error");
return 0;
}
* Read, checking for line continuations ('\\' at EOL)
*/
for (;;) {
- int eof;
+ int at_eof;
/*
* Get data, and remember if we are at EOF.
*/
- eof = (fgets(cbuf, sizeof(buf) - (cbuf - buf), fp) == NULL);
+ at_eof = (fgets(cbuf, sizeof(buf) - (cbuf - buf), fp) == NULL);
(*lineno)++;
/*
* conditions.
*/
if (cbuf == buf) {
- if (eof) break;
+ if (at_eof) break;
ptr = buf;
while (*ptr && isspace((int) *ptr)) ptr++;
if (!*ptr || (*ptr == '#')) continue;
- } else if (eof || (len == 0)) {
+ } else if (at_eof || (len == 0)) {
radlog(L_ERR, "%s[%d]: Continuation at EOF is illegal",
filename, *lineno);
return -1;
*/
while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
- if ((ptr[0] == '%') && (ptr[1] == '{')) {
+ if (((ptr[0] == '%') && (ptr[1] == '{')) ||
+ (ptr[0] == '`')) {
int hack;
- hack = rad_copy_variable(buf1, ptr);
+ if (ptr[0] == '%') {
+ hack = rad_copy_variable(buf1, ptr);
+ } else {
+ hack = rad_copy_string(buf1, ptr);
+ }
if (hack < 0) {
radlog(L_ERR, "%s[%d]: Invalid expansion: %s",
filename, *lineno, ptr);
t1 = T_BARE_WORD;
ptr += hack;
-
t2 = gettoken(&ptr, buf2, sizeof(buf2));
switch (t2) {
case T_EOL:
if (strcasecmp(buf1, "$template") == 0) {
CONF_ITEM *ci;
- CONF_SECTION *parentcs;
+ CONF_SECTION *parentcs, *templatecs;
t2 = getword(&ptr, buf2, sizeof(buf2));
parentcs = cf_top_section(current);
- ci = cf_reference_item(parentcs, this, buf2);
+ templatecs = cf_section_sub_find(parentcs, "templates");
+ if (!templatecs) {
+ radlog(L_ERR, "%s[%d]: No \"templates\" section for reference \"%s\"",
+ filename, *lineno, buf2);
+ return -1;
+ }
+
+ ci = cf_reference_item(parentcs, templatecs, buf2);
if (!ci || (ci->type != CONF_ITEM_SECTION)) {
radlog(L_ERR, "%s[%d]: Reference \"%s\" not found",
filename, *lineno, buf2);
case T_EOL:
case T_HASH:
do_bare_word:
+ t3 = t2;
t2 = T_OP_EQ;
value = NULL;
goto do_set;
case T_OP_SUB:
case T_OP_LE:
case T_OP_GE:
+ case T_OP_CMP_FALSE:
if (!this || (strcmp(this->name1, "update") != 0)) {
radlog(L_ERR, "%s[%d]: Invalid operator in assignment",
filename, *lineno);
case T_OP_EQ:
case T_OP_SET:
- do_set:
t3 = getstring(&ptr, buf3, sizeof(buf3));
+ if (t3 == T_OP_INVALID) {
+ radlog(L_ERR, "%s[%d]: Parse error: %s",
+ filename, *lineno,
+ fr_strerror());
+ return -1;
+ }
/*
* Handle variable substitution via ${foo}
/*
* Add this CONF_PAIR to our CONF_SECTION
*/
+ do_set:
cpn = cf_pair_alloc(buf1, value, t2, t3, this);
cpn->item.filename = filename;
cpn->item.lineno = *lineno;
return -1;
}
+ if (cf_data_find_internal(cs, filename, PW_TYPE_FILENAME)) {
+ radlog(L_ERR, "Cannot include the same file twice: \"%s\"",
+ filename);
+ return -1;
+ }
+
/*
* Add the filename to the section
*/
if (cf_data_add_internal(cs, filename, mtime, free,
PW_TYPE_FILENAME) < 0) {
fclose(fp);
- radlog(L_ERR|L_CONS, "Internal error open file \"%s\"",
+ radlog(L_ERR|L_CONS, "Internal error opening file \"%s\"",
filename);
return -1;
}
cd = cf_data_find_internal(cs, filename, PW_TYPE_FILENAME);
if (!cd) {
fclose(fp);
- radlog(L_ERR|L_CONS, "Internal error open file \"%s\"",
+ radlog(L_ERR|L_CONS, "Internal error opening file \"%s\"",
filename);
return -1;
}
/*
* Copied here for error reporting.
*/
-extern void librad_log(const char *, ...);
+extern void fr_strerror_printf(const char *, ...);
/*
* Turn a CONF_PAIR into a VALUE_PAIR
VALUE_PAIR *vp;
if (!pair) {
- librad_log("Internal error");
+ fr_strerror_printf("Internal error");
return NULL;
}
if (!pair->value) {
- librad_log("No value given for attribute %s", pair->attr);
+ fr_strerror_printf("No value given for attribute %s", pair->attr);
return NULL;
}
{
CONF_ITEM *ci;
+ if (!name) return NULL; /* can't find an un-named section */
+
/*
* Do the fast lookup if possible.
*/
- if (name && cs->section_tree) {
+ if (cs->section_tree) {
CONF_SECTION mycs;
mycs.name1 = name;
}
-void cf_log_info(UNUSED CONF_SECTION *cs, const char *fmt, ...)
+void cf_log_info(CONF_SECTION *cs, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
- if (debug_flag > 1 && cf_log_config) vradlog(L_DBG, fmt, ap);
+ if (debug_flag > 1 && cf_log_config && cs) vradlog(L_DBG, fmt, ap);
va_end(ap);
}
/*
* Wrapper to simplify the code.
*/
-void cf_log_module(UNUSED CONF_SECTION *cs, const char *fmt, ...)
+void cf_log_module(CONF_SECTION *cs, const char *fmt, ...)
{
va_list ap;
char buffer[256];
va_start(ap, fmt);
- if (debug_flag > 1 && cf_log_modules) {
+ if (debug_flag > 1 && cf_log_modules && cs) {
vsnprintf(buffer, sizeof(buffer), fmt, ap);
radlog(L_DBG, " Module: %s", buffer);
va_end(ap);
}
+const CONF_PARSER *cf_section_parse_table(CONF_SECTION *cs)
+{
+ if (!cs) return NULL;
+
+ return cs->variables;
+}
#if 0
/*
return dump_config_section(cs, 0);
}
#endif
+
+static const char *cf_pair_print_value(const CONF_PAIR *cp,
+ char *buffer, size_t buflen)
+{
+ char *p;
+
+ if (!cp->value) return "";
+
+ switch (cp->value_type) {
+ default:
+ case T_BARE_WORD:
+ snprintf(buffer, buflen, "%s", cp->value);
+ break;
+
+ case T_SINGLE_QUOTED_STRING:
+ snprintf(buffer, buflen, "'%s'", cp->value);
+ break;
+
+ case T_DOUBLE_QUOTED_STRING:
+ buffer[0] = '"';
+ fr_print_string(cp->value, strlen(cp->value),
+ buffer + 1, buflen - 3);
+ p = buffer + strlen(buffer); /* yuck... */
+ p[0] = '"';
+ p[1] = '\0';
+ break;
+ }
+
+ return buffer;
+}
+
+
+int cf_pair2xml(FILE *fp, const CONF_PAIR *cp)
+{
+ fprintf(fp, "<%s>", cp->attr);
+ if (cp->value) {
+ char buffer[2048];
+
+ char *p = buffer;
+ const char *q = cp->value;
+
+ while (*q && (p < (buffer + sizeof(buffer) - 1))) {
+ if (q[0] == '&') {
+ memcpy(p, "&", 4);
+ p += 5;
+
+ } else if (q[0] == '<') {
+ memcpy(p, "<", 4);
+ p += 4;
+
+ } else if (q[0] == '>') {
+ memcpy(p, ">", 4);
+ p += 4;
+
+ } else {
+ *(p++) = *q;
+ }
+ q++;
+ }
+
+ *p = '\0';
+ fprintf(fp, "%s", buffer);
+ }
+
+ fprintf(fp, "</%s>\n", cp->attr);
+
+ return 1;
+}
+
+int cf_section2xml(FILE *fp, const CONF_SECTION *cs)
+{
+ CONF_ITEM *ci, *next;
+
+ /*
+ * Section header
+ */
+ fprintf(fp, "<%s>\n", cs->name1);
+ if (cs->name2) {
+ fprintf(fp, "<_name2>%s</_name2>\n", cs->name2);
+ }
+
+ /*
+ * Loop over contents.
+ */
+ for (ci = cs->children; ci; ci = next) {
+ next = ci->next;
+
+ switch (ci->type) {
+ case CONF_ITEM_PAIR:
+ if (!cf_pair2xml(fp, (CONF_PAIR *) ci)) return 0;
+ break;
+
+ case CONF_ITEM_SECTION:
+ if (!cf_section2xml(fp, (CONF_SECTION *) ci)) return 0;
+ break;
+
+ default: /* should really be an error. */
+ break;
+
+ }
+ }
+
+ fprintf(fp, "</%s>\n", cs->name1);
+
+ return 1; /* success */
+}
+
+int cf_pair2file(FILE *fp, const CONF_PAIR *cp)
+{
+ char buffer[2048];
+
+ fprintf(fp, "\t%s = %s\n", cp->attr,
+ cf_pair_print_value(cp, buffer, sizeof(buffer)));
+
+ return 1;
+}
+
+int cf_section2file(FILE *fp, const CONF_SECTION *cs)
+{
+ const CONF_ITEM *ci, *next;
+
+ /*
+ * Section header
+ */
+ if (!cs->name2) {
+ fprintf(fp, "%s {\n", cs->name1);
+ } else {
+ fprintf(fp, "%s %s {\n",
+ cs->name1, cs->name2);
+ }
+
+ /*
+ * Loop over contents.
+ */
+ for (ci = cs->children; ci; ci = next) {
+ next = ci->next;
+
+ switch (ci->type) {
+ case CONF_ITEM_PAIR:
+ if (!cf_pair2file(fp, (const CONF_PAIR *) ci)) return 0;
+ break;
+
+ case CONF_ITEM_SECTION:
+ if (!cf_section2file(fp, (const CONF_SECTION *) ci)) return 0;
+ break;
+
+ default: /* should really be an error. */
+ break;
+
+ }
+ }
+
+ fprintf(fp, "}\n");
+
+ return 1; /* success */
+}