return 0;
}
+ /*
+ * FR_CONF_FUNC rules own their own output routing via the
+ * parse function - there's no guaranteed offset into `base`
+ * for us to NULL out. The func decides where the result
+ * lives.
+ */
+ if (rule->func) return 0;
+
if (rule->data) {
*(char **) rule->data = NULL;
} else if (base) {
size_t subcs_size = rule->subcs_size;
conf_parser_t const *rules = rule->subcs;
+ /*
+ * CF_IDENT_ANY section rules act as a catch-all: skip any
+ * subsection that's already been claimed and marked parsed
+ * by an earlier specific rule, so the wildcard only handles
+ * the leftovers. CONF_FLAG_ALWAYS_PARSE overrides this and
+ * makes the rule observe every matching subsection.
+ */
+ bool const skip_parsed = (rule->name1 == CF_IDENT_ANY) &&
+ !(rule->flags & CONF_FLAG_ALWAYS_PARSE);
+
uint8_t **array = NULL;
fr_assert(rule->flags & CONF_FLAG_SUBSECTION);
- subcs = cf_section_find(cs, rule->name1, rule->name2);
+ if (skip_parsed) {
+ while ((subcs = cf_section_find_next(cs, subcs, rule->name1, rule->name2))) {
+ if (!cf_item_is_parsed(cf_section_to_item(subcs))) break;
+ }
+ } else {
+ subcs = cf_section_find(cs, rule->name1, rule->name2);
+ }
if (!subcs) return 0;
/*
* Handle the multi subsection case (which is harder)
*/
subcs = NULL;
- while ((subcs = cf_section_find_next(cs, subcs, rule->name1, rule->name2))) count++;
+ while ((subcs = cf_section_find_next(cs, subcs, rule->name1, rule->name2))) {
+ if (skip_parsed && cf_item_is_parsed(cf_section_to_item(subcs))) continue;
+ count++;
+ }
/*
* Allocate an array to hold the subsections
while ((subcs = cf_section_find_next(cs, subcs, rule->name1, rule->name2))) {
uint8_t *buff = NULL;
+ if (skip_parsed && cf_item_is_parsed(cf_section_to_item(subcs))) continue;
+
if (DEBUG_ENABLED4) cf_log_debug(cs, "Evaluating rules for %s[%i] section. Output %p",
cf_section_name1(subcs),
i, out);
return cf_subsection_parse(ctx, data, base, cs, rule);
}
+ /*
+ * A pair rule with name1 == CF_IDENT_ANY is a catch-all:
+ * feed every still-unparsed CONF_PAIR in this section to the
+ * rule's parser func. Order matters - specific rules before
+ * this entry have already claimed (and marked parsed) their
+ * pairs, so the catch-all only sees the leftovers, unless
+ * CONF_FLAG_ALWAYS_PARSE is set (in which case it sees every
+ * pair regardless).
+ *
+ * The rule must provide a func; there's no sensible default
+ * output offset when the pair's name varies. The func
+ * receives one CONF_PAIR at a time via ci and can read the
+ * name via cf_pair_attr().
+ */
+ if (!(rule->flags & CONF_FLAG_REF) && rule->name1 == CF_IDENT_ANY) {
+ bool const always_parse = (rule->flags & CONF_FLAG_ALWAYS_PARSE);
+ CONF_PAIR *cp = NULL;
+
+ if (!rule->func) {
+ cf_log_err(cs, "CF_IDENT_ANY pair rule must provide a parse function");
+ return -1;
+ }
+
+ while ((cp = cf_pair_find_next(cs, cp, NULL))) {
+ if (!always_parse && cf_item_is_parsed(cf_pair_to_item(cp))) continue;
+
+ if (rule->func(ctx, data, base, cf_pair_to_item(cp), rule) < 0) return -1;
+ cf_item_mark_parsed(cf_pair_to_item(cp));
+ }
+ return 0;
+ }
+
/*
* Ignore this rule if it's a reference, as the
* rules it points to have been pushed by the