*/
fr_value_box_t literal; //!< Value data.
- _CONST struct {
- xlat_exp_t *ex; //!< pre-parsed xlat_exp_t
- xlat_flags_t flags; //!< Flags controlling evaluation
- ///< and expansion.
- } xlat;
+ struct {
+ union {
+ _CONST struct {
+ xlat_exp_t *ex; //!< pre-parsed xlat_exp_t
+ xlat_flags_t flags; //!< Flags controlling evaluation
+ ///< and expansion.
+ } xlat;
#ifdef HAVE_REGEX
- _CONST struct {
- regex_t *ex; //!< pre-parsed regex_t
- fr_regex_flags_t flags; //!< Flags for regular expressions.
- } reg;
+ _CONST struct {
+ char *src; //!< Original unescaped source string.
+ regex_t *ex; //!< pre-parsed regex_t
+ bool subcaptures; //!< Whether the regex was compiled with
+ ///< subcaptures.
+ } reg;
#endif
+ };
+ fr_regex_flags_t reg_flags; //!< Flags for regular expressions.
+ ///< Used by:
+ ///< - TMPL_TYPE_REGEX_XLAT
+ ///< - TMPL_TYPE_REGEX_UNCOMPILED
+ ///< - TMPL_TYPE_REGEX
+ ///< - TMPL_TYPE_REGEX_XLAT_UNRESOLVED
+ };
} data;
fr_type_t _CONST cast; //!< Type we should interpret unresolved data with.
*/
#ifdef HAVE_REGEX
# define tmpl_regex(_tmpl) (_tmpl)->data.reg.ex //!< #TMPL_TYPE_REGEX only.
-# define tmpl_regex_flags(_tmpl) (&(_tmpl)->data.reg.flags)
+# define tmpl_regex_flags(_tmpl) (&(_tmpl)->data.reg_flags)
#endif
/** @} */
fr_sbuff_parse_rules_t const *p_rules,
tmpl_rules_t const *t_rules);
+tmpl_t *tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in);
+
ssize_t tmpl_cast_from_substr(fr_type_t *out, fr_sbuff_t *in); /* Parses cast string */
int tmpl_cast_set(tmpl_t *vpt, fr_type_t type) CC_HINT(nonnull); /* Sets cast type */
return fr_sbuff_set(in, &our_in);
}
+/** Copy a tmpl
+ *
+ * Fully duplicates the contents of a tmpl including any nested attribute
+ * references.
+ *
+ * @param[in] ctx to perform allocations under.
+ * @param[in] in tmpl to duplicate.
+ * @return
+ * - NULL on error.
+ * - A new tmpl on success.
+ */
+tmpl_t *tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in)
+{
+ tmpl_t *vpt;
+
+ MEM(vpt = tmpl_alloc(ctx, in->type, in->quote, in->name, in->len));
+ vpt->cast = in->cast;
+ vpt->enumv = in->enumv;
+ vpt->rules = in->rules;
+
+ /*
+ * Copy over the unescaped data
+ */
+ if (tmpl_is_unresolved(vpt) || tmpl_is_regex_uncompiled(vpt)) {
+ if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.unescaped)))) {
+ error:
+ talloc_free(vpt);
+ return NULL;
+ }
+ }
+
+ /*
+ * Copy attribute references
+ */
+ if (tmpl_contains_attr(vpt) && unlikely(tmpl_attr_copy(vpt, in) < 0)) goto error;
+
+ /*
+ * Copy flags for all regex flavours (and possibly recompile the regex)
+ */
+ if (tmpl_contains_regex(vpt)) {
+ vpt->data.reg_flags = in->data.reg_flags;
+
+ /*
+ * If the tmpl contains a _compiled_ regex
+ * then convert it back to an uncompiled
+ * regex and recompile.
+ *
+ * Most of the regex libraries don't allow
+ * copying compiled expressions.
+ */
+ if (tmpl_is_regex(vpt)) {
+ vpt->type = TMPL_TYPE_REGEX_UNCOMPILED;
+ if (unlikely(!(vpt->data.unescaped = talloc_bstrdup(vpt, in->data.reg.src)))) goto error;
+ if (unlikely(tmpl_regex_compile(vpt, vpt->data.reg.subcaptures) < 0)) goto error;
+ return vpt;
+ }
+ }
+
+ /*
+ * Copy the xlat component
+ */
+ if (tmpl_contains_xlat(vpt) && unlikely(xlat_copy(vpt, &vpt->data.xlat.ex, in->data.xlat.ex) < 0)) goto error;
+
+ return vpt;
+}
+
/** Parse a cast specifier
*
* @param[out] out Where to write the cast type.
fr_assert(tmpl_is_regex_uncompiled(vpt) || tmpl_is_regex_xlat(vpt) || tmpl_is_regex_xlat_unresolved(vpt));
- slen = regex_flags_parse(&err, &vpt->data.reg.flags, in, terminals, true);
+ slen = regex_flags_parse(&err, &vpt->data.reg_flags, in, terminals, true);
switch (err) {
case 0:
break;
slen = regex_compile(vpt, &vpt->data.reg.ex,
unescaped, talloc_array_length(unescaped) - 1,
- &vpt->data.reg.flags, subcaptures, vpt->rules.at_runtime);
+ &vpt->data.reg_flags, subcaptures, vpt->rules.at_runtime);
if (slen <= 0) return vpt->quote != T_BARE_WORD ? slen - 1 : slen; /* Account for the quoting */
- talloc_free(unescaped);
vpt->type = TMPL_TYPE_REGEX;
+ vpt->data.reg.src = unescaped; /* Keep this around for debugging and copying */
+ vpt->data.reg.subcaptures = subcaptures;
TMPL_VERIFY(vpt);
int xlat_from_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, tmpl_t **vpt_p);
+int xlat_copy(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *in);
+
/*
* xlat_inst.c
*/
return 0;
}
+
+/** Copy all nodes in the input list to the output list
+ *
+ * @param[in] ctx to allocate new nodes in.
+ * @param[out] out Where to write new nodes.
+ * @param[in] in Input nodes.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int xlat_copy(TALLOC_CTX *ctx, xlat_exp_t **out, xlat_exp_t *in)
+{
+ xlat_exp_t *p;
+ fr_cursor_t out_cursor, in_cursor;
+
+ if (!in) {
+ *out = NULL;
+ return 0;
+ }
+
+ fr_cursor_talloc_init(&out_cursor, out, xlat_exp_t);
+ fr_cursor_talloc_init(&in_cursor, &in, xlat_exp_t);
+
+ /*
+ * Copy everything in the list of nodes
+ */
+ while ((p = fr_cursor_next(&in_cursor))) {
+ xlat_exp_t *node;
+
+ MEM(node = xlat_exp_alloc(ctx, p->type, p->fmt, talloc_array_length(p->fmt) - 1));
+ node->quote = p->quote;
+ node->flags = p->flags;
+
+ switch (p->type) {
+ case XLAT_INVALID:
+ fr_strerror_printf("Cannot copy xlat node of type \"invalid\"");
+ error:
+ fr_cursor_head(&out_cursor);
+ fr_cursor_free_list(&out_cursor);
+ return -1;
+
+ case XLAT_BOX:
+ if (unlikely(fr_value_box_copy(node, &node->data, &p->data) < 0)) goto error;
+ continue;
+
+ case XLAT_ONE_LETTER: /* Done with format */
+ case XLAT_FUNC_UNRESOLVED:
+ case XLAT_VIRTUAL_UNRESOLVED:
+ continue;
+
+ case XLAT_FUNC:
+ case XLAT_VIRTUAL:
+ /*
+ * Only copy the function pointer, and whether this
+ * is ephemeral.
+ *
+ * All instance data is specific to the xlat node and
+ * cannot be duplicated.
+ *
+ * The node xlat nodes will need to be registered in
+ * the xlat instantiation table later.
+ */
+ node->call.func = p->call.func;
+ node->call.ephemeral = p->call.ephemeral;
+ if (unlikely(xlat_copy(node, &node->child, p->child) < 0)) goto error;
+ continue;
+
+ case XLAT_ATTRIBUTE:
+ node->attr = tmpl_copy(node, p->attr);
+ continue;
+
+#ifdef HAVE_REGEX
+ case XLAT_REGEX:
+ node->regex_index = p->regex_index;
+ continue;
+#endif
+
+ case XLAT_ALTERNATE:
+ if (unlikely(xlat_copy(node, &node->alternate, p->alternate) < 0)) goto error;
+ continue;
+
+ case XLAT_GROUP:
+ if (unlikely(xlat_copy(node, &node->child, p->child) < 0)) goto error;
+ continue;
+ }
+ }
+
+ return 0;
+}