]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
xlat: Make %{} and %() functions take the same format argument list
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 21 Feb 2023 20:11:02 +0000 (14:11 -0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 22 Feb 2023 02:56:47 +0000 (20:56 -0600)
Add support for quoted arguments to curly bracketed expansions

17 files changed:
src/lib/unlang/xlat_builtin.c
src/lib/unlang/xlat_eval.c
src/lib/unlang/xlat_expr.c
src/lib/unlang/xlat_tokenize.c
src/lib/util/dlist.h
src/modules/rlm_cipher/rlm_cipher.c
src/modules/rlm_escape/rlm_escape.c
src/modules/rlm_idn/rlm_idn.c
src/modules/rlm_json/rlm_json.c
src/modules/rlm_ldap/rlm_ldap.c
src/modules/rlm_sql/rlm_sql.c
src/modules/rlm_yubikey/rlm_yubikey.c
src/tests/keywords/if-regex-match-named
src/tests/keywords/xlat-subst
src/tests/modules/json/encode_error.unlang
src/tests/modules/linelog/linelog_xlat.unlang
src/tests/unit/xlat/base.txt

index 1f16d555850b22a35a38ffeafdf321d77d17319a..84ddc71e66b166a63a616d6a350cb297a7cecc57 100644 (file)
@@ -398,16 +398,14 @@ int xlat_func_args(xlat_t *x, xlat_arg_parser_t const args[])
  * For xlats that take all their input as a single argument
  *
  * @param[in,out] x            to have it's arguments registered
- * @param[in] arg              to be registered
+ * @param[in] args             to be registered
  * @return
  *     - 0 on success.
  *     - < 0 on failure.
  */
-int xlat_func_mono(xlat_t *x, xlat_arg_parser_t const *arg)
+int xlat_func_mono(xlat_t *x, xlat_arg_parser_t const args[])
 {
-       if (xlat_arg_parser_validate(arg, true) < 0) return -1;
-
-       x->args = arg;
+       if (xlat_func_args(x, args) < 0) return -1;
        x->input_type = XLAT_INPUT_MONO;
 
        return 0;
@@ -969,11 +967,12 @@ static xlat_arg_parser_t const xlat_func_debug_args[] = {
  */
 static xlat_action_t xlat_func_debug(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                     UNUSED xlat_ctx_t const *xctx,
-                                    request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                    request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        int level = 0;
-       fr_value_box_t  *vb;
-       fr_value_box_t  *in_head = fr_value_box_list_head(in);
+       fr_value_box_t  *vb, *lvl_vb;
+
+       XLAT_ARGS(args, &lvl_vb);
 
        /*
         *  Expand to previous (or current) level
@@ -985,9 +984,9 @@ static xlat_action_t xlat_func_debug(TALLOC_CTX *ctx, fr_dcursor_t *out,
        /*
         *  Assume we just want to get the current value and NOT set it to 0
         */
-       if (!in_head) goto done;
+       if (!lvl_vb) goto done;
 
-       level = in_head->vb_int8;
+       level = lvl_vb->vb_int8;
        if (level == 0) {
                request->log.lvl = RAD_REQUEST_LVL_NONE;
        } else {
@@ -1136,15 +1135,17 @@ void xlat_debug_attr_list(request_t *request, fr_pair_list_t const *list)
  */
 static xlat_action_t xlat_func_debug_attr(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out,
                                          UNUSED xlat_ctx_t const *xctx,
-                                         request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                         request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        fr_pair_t               *vp;
        fr_dcursor_t            cursor;
        tmpl_dcursor_ctx_t      cc;
        tmpl_t                  *vpt;
-       fr_value_box_t          *attr = fr_value_box_list_head(in);
+       fr_value_box_t          *attr;
        char const              *fmt;
 
+       XLAT_ARGS(args, &attr);
+
        if (!RDEBUG_ENABLED2) return XLAT_ACTION_DONE;  /* NOOP if debugging isn't enabled */
 
        fmt = attr->vb_strvalue;
@@ -1191,13 +1192,15 @@ static xlat_action_t xlat_func_debug_attr(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcur
  */
 static xlat_action_t xlat_func_flatten(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out,
                                          UNUSED xlat_ctx_t const *xctx,
-                                         request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                         request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        fr_pair_t               *vp;
        tmpl_t                  *vpt;
-       fr_value_box_t          *attr = fr_value_box_list_head(in);
+       fr_value_box_t          *attr;
        char const              *fmt;
 
+       XLAT_ARGS(args, &attr);
+
        fmt = attr->vb_strvalue;
        if (tmpl_afrom_attr_str(request, NULL, &vpt, fmt,
                                &(tmpl_rules_t){
@@ -1238,13 +1241,15 @@ static xlat_action_t xlat_func_flatten(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor
  */
 static xlat_action_t xlat_func_unflatten(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out,
                                          UNUSED xlat_ctx_t const *xctx,
-                                         request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                         request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        fr_pair_t               *vp;
        tmpl_t                  *vpt;
-       fr_value_box_t          *attr = fr_value_box_list_head(in);
+       fr_value_box_t          *attr;
        char const              *fmt;
 
+       XLAT_ARGS(args, &attr);
+
        fmt = attr->vb_strvalue;
        if (tmpl_afrom_attr_str(request, NULL, &vpt, fmt,
                                &(tmpl_rules_t){
@@ -1324,14 +1329,18 @@ update request {
  */
 static xlat_action_t xlat_func_explode(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                       UNUSED xlat_ctx_t const *xctx,
-                                      request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                      request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
-       fr_value_box_t          *strings = fr_value_box_list_head(in);
-       FR_DLIST_HEAD(fr_value_box_list)        *list = &strings->vb_group;
-       fr_value_box_t          *delim_vb = fr_value_box_list_next(in, strings);
-       ssize_t                 delim_len;
-       char const              *delim;
-       fr_value_box_t          *string, *vb;
+       fr_value_box_t                          *strings;
+       FR_DLIST_HEAD(fr_value_box_list)        *list;
+       fr_value_box_t                          *delim_vb;
+       ssize_t                                 delim_len;
+       char const                              *delim;
+       fr_value_box_t                          *string, *vb;
+
+       XLAT_ARGS(args, &strings, &delim_vb);
+
+       list = &strings->vb_group;
 
        /* coverity[dereference] */
        if (delim_vb->vb_length == 0) {
@@ -1405,14 +1414,16 @@ update request {
  */
 static xlat_action_t xlat_func_integer(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                       UNUSED xlat_ctx_t const *xctx,
-                                      request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                      request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
-       fr_value_box_t  *in_vb = fr_value_box_list_head(in);
+       fr_value_box_t  *in_vb;
        char const *p;
 
+       XLAT_ARGS(args, &in_vb);
+
        fr_strerror_clear(); /* Make sure we don't print old errors */
 
-       fr_value_box_list_remove(in, in_vb);
+       fr_value_box_list_remove(args, in_vb);
 
        switch (in_vb->type) {
        default:
@@ -1514,10 +1525,9 @@ static xlat_action_t xlat_func_integer(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-static xlat_arg_parser_t const xlat_func_map_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_STRING
+static xlat_arg_parser_t const xlat_func_map_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Processes fmt as a map string and applies it to the current request
@@ -1534,11 +1544,11 @@ static xlat_arg_parser_t const xlat_func_map_arg = {
  */
 static xlat_action_t xlat_func_map(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                   UNUSED xlat_ctx_t const *xctx,
-                                  request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                  request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        map_t           *map = NULL;
        int             ret;
-       fr_value_box_t  *fmt_vb = fr_value_box_list_head(in);
+       fr_value_box_t  *fmt_vb;
        fr_value_box_t  *vb;
 
        tmpl_rules_t    attr_rules = {
@@ -1549,6 +1559,8 @@ static xlat_action_t xlat_func_map(TALLOC_CTX *ctx, fr_dcursor_t *out,
                }
        };
 
+       XLAT_ARGS(args, &fmt_vb);
+
        if (map_afrom_attr_str(request, &map, fmt_vb->vb_strvalue, &attr_rules, &attr_rules) < 0) {
                RPEDEBUG("Failed parsing \"%s\" as map", fmt_vb->vb_strvalue);
                return XLAT_ACTION_FAIL;
@@ -1613,7 +1625,7 @@ static xlat_arg_parser_t const xlat_func_next_time_args[] = {
  */
 static xlat_action_t xlat_func_next_time(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                         UNUSED xlat_ctx_t const *xctx,
-                                        request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                        request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        long            num;
 
@@ -1621,9 +1633,11 @@ static xlat_action_t xlat_func_next_time(TALLOC_CTX *ctx, fr_dcursor_t *out,
        char            *q;
        time_t          now;
        struct tm       *local, local_buff;
-       fr_value_box_t  *in_head = fr_value_box_list_head(in);
+       fr_value_box_t  *in_head;
        fr_value_box_t  *vb;
 
+       XLAT_ARGS(args, &in_head);
+
        /*
         *      We want to limit based on _now_, not on when they logged in.
         */
@@ -1706,10 +1720,9 @@ static xlat_action_t xlat_eval_resume(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_
        return xa;
 }
 
-static xlat_arg_parser_t const xlat_func_eval_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_STRING
+static xlat_arg_parser_t const xlat_func_eval_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Dynamically evaluate an expansion string
@@ -1718,11 +1731,8 @@ static xlat_arg_parser_t const xlat_func_eval_arg = {
  */
 static xlat_action_t xlat_func_eval(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                    UNUSED xlat_ctx_t const *xctx,
-                                   request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                   request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
-       xlat_eval_rctx_t        *rctx;
-       fr_value_box_t          *arg = fr_value_box_list_head(in);
-
        /*
         *      These are escaping rules applied to the
         *      input string. They're mostly here to
@@ -1742,6 +1752,11 @@ static xlat_action_t xlat_func_eval(TALLOC_CTX *ctx, fr_dcursor_t *out,
                .do_oct = false
        };
 
+       xlat_eval_rctx_t        *rctx;
+       fr_value_box_t          *arg = fr_value_box_list_head(args);
+
+       XLAT_ARGS(args, &arg);
+
        MEM(rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), xlat_eval_rctx_t));
 
        /*
@@ -1795,11 +1810,8 @@ static xlat_action_t xlat_func_eval(TALLOC_CTX *ctx, fr_dcursor_t *out,
  */
 static xlat_action_t xlat_func_expr(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                    UNUSED xlat_ctx_t const *xctx,
-                                   request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                   request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
-       xlat_eval_rctx_t        *rctx;
-       fr_value_box_t          *arg = fr_value_box_list_head(in);
-
        /*
         *      These are escaping rules applied to the
         *      input string. They're mostly here to
@@ -1819,6 +1831,11 @@ static xlat_action_t xlat_func_expr(TALLOC_CTX *ctx, fr_dcursor_t *out,
                .do_oct = false
        };
 
+       xlat_eval_rctx_t        *rctx;
+       fr_value_box_t          *arg;
+
+       XLAT_ARGS(args, &arg);
+
        MEM(rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), xlat_eval_rctx_t));
 
        /*
@@ -1988,17 +2005,22 @@ static xlat_action_t xlat_func_rpad(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out,
                                    UNUSED xlat_ctx_t const *xctx,
                                    request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
-       fr_value_box_t          *values = fr_value_box_list_head(args);
-       FR_DLIST_HEAD(fr_value_box_list)        *list = &values->vb_group;
-       fr_value_box_t          *pad = fr_value_box_list_next(args, values);
+       fr_value_box_t          *values;
+       FR_DLIST_HEAD(fr_value_box_list)        *list;
+       fr_value_box_t          *pad;
        /* coverity[dereference] */
-       size_t                  pad_len = (size_t)pad->vb_uint64;
-       fr_value_box_t          *fill = fr_value_box_list_next(args, pad);
+       size_t                  pad_len;
+       fr_value_box_t          *fill;
        char const              *fill_str = NULL;
        size_t                  fill_len = 0;
 
        fr_value_box_t          *in = NULL;
 
+       XLAT_ARGS(args, &values, &pad, &fill);
+
+       list = &values->vb_group;
+       pad_len = (size_t)pad->vb_uint64;
+
        /*
         *      Fill is optional
         */
@@ -2049,10 +2071,9 @@ static xlat_action_t xlat_func_rpad(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-static xlat_arg_parser_t const xlat_func_base64_encode_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_OCTETS
+static xlat_arg_parser_t const xlat_func_base64_encode_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Encode string or attribute as base64
@@ -2072,7 +2093,9 @@ static xlat_action_t xlat_func_base64_encode(TALLOC_CTX *ctx, fr_dcursor_t *out,
        ssize_t         elen;
        char            *buff;
        fr_value_box_t  *vb;
-       fr_value_box_t  *in = fr_value_box_list_head(args);
+       fr_value_box_t  *in;
+
+       XLAT_ARGS(args, &in);
 
        alen = FR_BASE64_ENC_LENGTH(in->vb_length);
 
@@ -2096,10 +2119,9 @@ static xlat_action_t xlat_func_base64_encode(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-static xlat_arg_parser_t const xlat_func_base64_decode_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_OCTETS
+static xlat_arg_parser_t const xlat_func_base64_decode_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Decode base64 string
@@ -2119,7 +2141,9 @@ static xlat_action_t xlat_func_base64_decode(TALLOC_CTX *ctx, fr_dcursor_t *out,
        ssize_t         declen = 0;
        uint8_t         *decbuf;
        fr_value_box_t  *vb;
-       fr_value_box_t  *in = fr_value_box_list_head(args);
+       fr_value_box_t  *in;
+
+       XLAT_ARGS(args, &in);
 
        alen = FR_BASE64_DEC_LENGTH(in->vb_length);
        MEM(vb = fr_value_box_alloc_null(ctx));
@@ -2142,10 +2166,9 @@ static xlat_action_t xlat_func_base64_decode(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-static xlat_arg_parser_t const xlat_func_bin_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_STRING
+static xlat_arg_parser_t const xlat_func_bin_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Convert hex string to binary
@@ -2161,7 +2184,7 @@ static xlat_arg_parser_t const xlat_func_bin_arg = {
  */
 static xlat_action_t xlat_func_bin(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                   UNUSED xlat_ctx_t const *xctx,
-                                  request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                  request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        fr_value_box_t          *result;
        char const              *p, *end;
@@ -2170,7 +2193,8 @@ static xlat_action_t xlat_func_bin(TALLOC_CTX *ctx, fr_dcursor_t *out,
        fr_sbuff_parse_error_t  err;
        fr_value_box_t          *hex;
 
-       hex = fr_value_box_list_head(in);
+       XLAT_ARGS(args, &hex);
+
        len = hex->vb_length;
        if ((len > 1) && (len & 0x01)) {
                REDEBUG("Input data length must be >1 and even, got %zu", len);
@@ -2196,7 +2220,7 @@ static xlat_action_t xlat_func_bin(TALLOC_CTX *ctx, fr_dcursor_t *out,
        outlen = len / 2;
 
        MEM(result = fr_value_box_alloc_null(ctx));
-       MEM(fr_value_box_mem_alloc(result, &bin, result, NULL, outlen, fr_value_box_list_tainted(in)) == 0);
+       MEM(fr_value_box_mem_alloc(result, &bin, result, NULL, outlen, fr_value_box_list_tainted(args)) == 0);
        fr_base16_decode(&err, &FR_DBUFF_TMP(bin, outlen), &FR_SBUFF_IN(p, end - p), true);
        if (err) {
                REDEBUG2("Invalid hex string");
@@ -2229,12 +2253,14 @@ static xlat_arg_parser_t const xlat_func_cast_args[] = {
  */
 static xlat_action_t xlat_func_cast(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                    UNUSED xlat_ctx_t const *xctx,
-                                   request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                   request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
-       fr_value_box_t  *name = fr_value_box_list_head(in);
+       fr_value_box_t  *name;
        fr_value_box_t  *arg;
        fr_type_t       type;
 
+       XLAT_ARGS(args, &name);
+
        /*
         *      Get the type, which can be in one of a few formats.
         */
@@ -2267,13 +2293,13 @@ static xlat_action_t xlat_func_cast(TALLOC_CTX *ctx, fr_dcursor_t *out,
                fr_sbuff_t *agg;
                fr_value_box_t *dst;
 
-               (void) fr_value_box_list_pop_head(in);
+               (void) fr_value_box_list_pop_head(args);
                talloc_free(name);
 
                FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 256, 8192);
 
                MEM(dst = fr_value_box_alloc_null(ctx));
-               if (fr_value_box_list_concat_as_string(NULL, agg, in, NULL, 0, &fr_value_escape_double,
+               if (fr_value_box_list_concat_as_string(NULL, agg, args, NULL, 0, &fr_value_escape_double,
                                                       FR_VALUE_BOX_LIST_FREE_BOX, true, true) < 0) {
                        RPEDEBUG("Failed concatenating string");
                        return XLAT_ACTION_FAIL;
@@ -2289,7 +2315,7 @@ static xlat_action_t xlat_func_cast(TALLOC_CTX *ctx, fr_dcursor_t *out,
         *      Copy inputs to outputs, casting them along the way.
         */
        arg = name;
-       while ((arg = fr_value_box_list_next(in, arg)) != NULL) {
+       while ((arg = fr_value_box_list_next(args, arg)) != NULL) {
                fr_value_box_t  *vb, *p;
 
                fr_assert(arg->type == FR_TYPE_GROUP);
@@ -2332,14 +2358,16 @@ static xlat_arg_parser_t const xlat_func_concat_args[] = {
  */
 static xlat_action_t xlat_func_concat(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                      UNUSED xlat_ctx_t const *xctx,
-                                     request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                     request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        fr_value_box_t  *result;
-       fr_value_box_t  *list = fr_value_box_list_head(in);
-       fr_value_box_t  *separator = fr_value_box_list_next(in, list);
+       fr_value_box_t  *list;
+       fr_value_box_t  *separator;
        char            *buff;
        char const      *sep;
 
+       XLAT_ARGS(args, &list, &separator);
+
        sep = (separator) ? separator->vb_strvalue : "";
 
        result = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL, false);
@@ -2352,17 +2380,16 @@ static xlat_action_t xlat_func_concat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        buff = fr_value_box_list_aprint(result, &list->vb_group, sep, NULL);
        if (!buff) goto error;
 
-       fr_value_box_bstrdup_buffer_shallow(NULL, result, NULL, buff, fr_value_box_list_tainted(in));
+       fr_value_box_bstrdup_buffer_shallow(NULL, result, NULL, buff, fr_value_box_list_tainted(args));
 
        fr_dcursor_append(out, result);
 
        return XLAT_ACTION_DONE;
 }
 
-static xlat_arg_parser_t const xlat_func_hex_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_OCTETS
+static xlat_arg_parser_t const xlat_func_hex_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Print data as hex, not as VALUE.
@@ -2378,10 +2405,14 @@ static xlat_arg_parser_t const xlat_func_hex_arg = {
  */
 static xlat_action_t xlat_func_hex(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out,
                                   UNUSED xlat_ctx_t const *xctx,
-                                  UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                  UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        char            *new_buff;
-       fr_value_box_t  *bin = fr_value_box_list_pop_head(in);  /* First argument */
+       fr_value_box_t  *bin;
+
+       XLAT_ARGS(args, &bin);
+
+       fr_value_box_list_remove(args, bin);
 
        /*
         *      Use existing box, but with new buffer
@@ -2410,12 +2441,11 @@ typedef enum {
 } hmac_type;
 
 static xlat_action_t xlat_hmac(TALLOC_CTX *ctx, fr_dcursor_t *out,
-                               FR_DLIST_HEAD(fr_value_box_list) *in, uint8_t *digest, int digest_len, hmac_type type)
+                               FR_DLIST_HEAD(fr_value_box_list) *args, uint8_t *digest, int digest_len, hmac_type type)
 {
        fr_value_box_t  *vb, *data, *key;
 
-       data = fr_value_box_list_head(in);
-       key = fr_value_box_list_next(in, data);
+       XLAT_ARGS(args, &data, &key);
 
        if (type == HMAC_MD5) {
                /* coverity[dereference] */
@@ -2566,9 +2596,9 @@ static xlat_action_t xlat_func_length(TALLOC_CTX *ctx, fr_dcursor_t *out,
 }
 
 
-static xlat_arg_parser_t const xlat_func_md4_arg = {
-       .concat = true,
-       .type = FR_TYPE_OCTETS
+static xlat_arg_parser_t const xlat_func_md4_arg[] = {
+       { .concat = true, .type = FR_TYPE_OCTETS },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Calculate the MD4 hash of a string or attribute.
@@ -2582,11 +2612,13 @@ static xlat_arg_parser_t const xlat_func_md4_arg = {
  */
 static xlat_action_t xlat_func_md4(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                   UNUSED xlat_ctx_t const *xctx,
-                                  UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                  UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        uint8_t         digest[MD5_DIGEST_LENGTH];
        fr_value_box_t  *vb;
-       fr_value_box_t  *in_head = fr_value_box_list_head(in);
+       fr_value_box_t  *in_head;
+
+       XLAT_ARGS(args, &in_head);
 
        if (in_head) {
                fr_md4_calc(digest, in_head->vb_octets, in_head->vb_length);
@@ -2603,9 +2635,9 @@ static xlat_action_t xlat_func_md4(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-static xlat_arg_parser_t const xlat_func_md5_arg = {
-       .concat = true,
-       .type = FR_TYPE_OCTETS
+static xlat_arg_parser_t const xlat_func_md5_arg[] = {
+       { .concat = true, .type = FR_TYPE_OCTETS },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Calculate the MD5 hash of a string or attribute.
@@ -2619,11 +2651,13 @@ static xlat_arg_parser_t const xlat_func_md5_arg = {
  */
 static xlat_action_t xlat_func_md5(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                   UNUSED xlat_ctx_t const *xctx,
-                                  UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                  UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        uint8_t         digest[MD5_DIGEST_LENGTH];
        fr_value_box_t  *vb;
-       fr_value_box_t  *in_head = fr_value_box_list_head(in);
+       fr_value_box_t  *in_head;
+
+       XLAT_ARGS(args, &in_head);
 
        if (in_head) {
                fr_md5_calc(digest, in_head->vb_octets, in_head->vb_length);
@@ -2682,10 +2716,9 @@ static xlat_action_t xlat_func_module(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-static xlat_arg_parser_t const xlat_func_pack_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_OCTETS
+static xlat_arg_parser_t const xlat_func_pack_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Pack multiple things together
@@ -2736,16 +2769,18 @@ static xlat_arg_parser_t const xlat_func_pairs_args[] = {
  */
 static xlat_action_t xlat_func_pairs(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                     UNUSED xlat_ctx_t const *xctx,
-                                    request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                    request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        tmpl_t                  *vpt = NULL;
        fr_dcursor_t            cursor;
        tmpl_dcursor_ctx_t      cc;
        fr_value_box_t          *vb;
-       fr_value_box_t          *in_head = fr_value_box_list_head(in);
+       fr_value_box_t          *in_head;
 
        fr_pair_t *vp;
 
+       XLAT_ARGS(args, &in_head);
+
        if (tmpl_afrom_attr_str(ctx, NULL, &vpt, in_head->vb_strvalue,
                                &(tmpl_rules_t){
                                        .attr = {
@@ -2781,11 +2816,9 @@ static xlat_action_t xlat_func_pairs(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-
-static xlat_arg_parser_t const xlat_func_rand_arg = {
-       .required = true,
-       .single = true,
-       .type = FR_TYPE_UINT32
+static xlat_arg_parser_t const xlat_func_rand_arg[] = {
+       { .required = true, .single = true, .type = FR_TYPE_UINT32 },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Generate a random integer value
@@ -2823,11 +2856,9 @@ static xlat_action_t xlat_func_rand(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-
-static xlat_arg_parser_t const xlat_func_randstr_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_STRING
+static xlat_arg_parser_t const xlat_func_randstr_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Generate a string of random chars
@@ -2858,7 +2889,7 @@ static xlat_arg_parser_t const xlat_func_randstr_arg = {
  */
 static xlat_action_t xlat_func_randstr(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                       UNUSED xlat_ctx_t const *xctx,
-                                      request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                      request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        /*
         *      Lookup tables for randstr char classes
@@ -2879,7 +2910,9 @@ static xlat_action_t xlat_func_randstr(TALLOC_CTX *ctx, fr_dcursor_t *out,
        unsigned int    reps;
        size_t          outlen = 0;
        fr_value_box_t* vb;
-       fr_value_box_t  *in_head = fr_value_box_list_head(in);
+       fr_value_box_t  *in_head;
+
+       XLAT_ARGS(args, &in_head);
 
        /** Max repetitions of a single character class
         *
@@ -3040,11 +3073,17 @@ static xlat_action_t xlat_func_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                     UNUSED xlat_ctx_t const *xctx,
                                     request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
 {
-       fr_value_box_t  *in_head  = fr_value_box_list_head(in);
+       fr_value_box_t  *in_head = fr_value_box_list_head(in);
+
+       /*
+        *      Find the first child of the first argument group
+        */
+       fr_value_box_t  *arg = fr_value_box_list_head(&in_head->vb_group);
+
        /*
         *      Return the complete capture if no other capture is specified
         */
-       if (!in_head) {
+       if (!arg) {
                fr_value_box_t  *vb;
                char            *p;
 
@@ -3062,7 +3101,7 @@ static xlat_action_t xlat_func_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
                return XLAT_ACTION_DONE;
        }
 
-       switch (in_head->type) {
+       switch (arg->type) {
        /*
         *      If the input is an integer value then get an
         *      arbitrary subcapture index.
@@ -3078,7 +3117,7 @@ static xlat_action_t xlat_func_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
                        return XLAT_ACTION_FAIL;
                }
 
-               if (fr_value_box_cast(NULL, &idx, FR_TYPE_UINT32, NULL, in_head) < 0) {
+               if (fr_value_box_cast(NULL, &idx, FR_TYPE_UINT32, NULL, arg) < 0) {
                        RPEDEBUG("Bad subcapture index");
                        return XLAT_ACTION_FAIL;
                }
@@ -3105,7 +3144,7 @@ static xlat_action_t xlat_func_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
                 *      Concatenate all input
                 */
                if (fr_value_box_list_concat_in_place(ctx,
-                                                     in_head, in, FR_TYPE_STRING,
+                                                     arg, &in_head->vb_group, FR_TYPE_STRING,
                                                      FR_VALUE_BOX_LIST_FREE, true,
                                                      SIZE_MAX) < 0) {
                        RPEDEBUG("Failed concatenating input");
@@ -3113,7 +3152,7 @@ static xlat_action_t xlat_func_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
                }
 
                MEM(vb = fr_value_box_alloc_null(ctx));
-               if (regex_request_to_sub_named(vb, &p, request, in_head->vb_strvalue) < 0) {
+               if (regex_request_to_sub_named(vb, &p, request, arg->vb_strvalue) < 0) {
                        REDEBUG2("No previous named regex capture group");
                        talloc_free(vb);
                        return XLAT_ACTION_FAIL;
@@ -3129,9 +3168,9 @@ static xlat_action_t xlat_func_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
 }
 #endif
 
-static xlat_arg_parser_t const xlat_func_sha_arg = {
-       .concat = true,
-       .type = FR_TYPE_OCTETS
+static xlat_arg_parser_t const xlat_func_sha_arg[] = {
+       { .concat = true, .type = FR_TYPE_OCTETS },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Calculate the SHA1 hash of a string or attribute.
@@ -3145,12 +3184,14 @@ static xlat_arg_parser_t const xlat_func_sha_arg = {
  */
 static xlat_action_t xlat_func_sha1(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                    UNUSED xlat_ctx_t const *xctx,
-                                   UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                   UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        uint8_t         digest[SHA1_DIGEST_LENGTH];
        fr_sha1_ctx     sha1_ctx;
        fr_value_box_t  *vb;
-       fr_value_box_t  *in_head = fr_value_box_list_head(in);
+       fr_value_box_t  *in_head;
+
+       XLAT_ARGS(args, &in_head);
 
        fr_sha1_init(&sha1_ctx);
        if (in_head) {
@@ -3169,7 +3210,6 @@ static xlat_action_t xlat_func_sha1(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-
 /** Calculate any digest supported by OpenSSL EVP_MD
  *
  * Example:
@@ -3182,13 +3222,15 @@ static xlat_action_t xlat_func_sha1(TALLOC_CTX *ctx, fr_dcursor_t *out,
 #ifdef HAVE_OPENSSL_EVP_H
 static xlat_action_t xlat_evp_md(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                 UNUSED xlat_ctx_t const *xctx,
-                                UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in, EVP_MD const *md)
+                                UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args, EVP_MD const *md)
 {
        uint8_t         digest[EVP_MAX_MD_SIZE];
        unsigned int    digestlen;
        EVP_MD_CTX      *md_ctx;
        fr_value_box_t  *vb;
-       fr_value_box_t  *in_head = fr_value_box_list_head(in);
+       fr_value_box_t  *in_head;
+
+       XLAT_ARGS(args, &in_head);
 
        md_ctx = EVP_MD_CTX_create();
        EVP_DigestInit_ex(md_ctx, md, NULL);
@@ -3234,10 +3276,9 @@ EVP_MD_XLAT(sha3_512, sha3_512)
 #endif
 
 
-static xlat_arg_parser_t const xlat_func_string_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_STRING
+static xlat_arg_parser_t const xlat_func_string_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Print data as string, if possible.
@@ -3262,9 +3303,9 @@ static xlat_action_t xlat_func_string(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out,
 }
 
 
-static xlat_arg_parser_t const xlat_func_strlen_arg = {
-       .concat = true,
-       .type = FR_TYPE_STRING
+static xlat_arg_parser_t const xlat_func_strlen_arg[] = {
+       { .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Print length of given string
@@ -3280,10 +3321,12 @@ static xlat_arg_parser_t const xlat_func_strlen_arg = {
  */
 static xlat_action_t xlat_func_strlen(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                      UNUSED xlat_ctx_t const *xctx,
-                                     UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                     UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        fr_value_box_t  *vb;
-       fr_value_box_t  *in_head = fr_value_box_list_head(in);
+       fr_value_box_t  *in_head;
+
+       XLAT_ARGS(args, &in_head);
 
        MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_SIZE, NULL, false));
 
@@ -3318,8 +3361,8 @@ static xlat_action_t xlat_func_strlen(TALLOC_CTX *ctx, fr_dcursor_t *out,
  * @ingroup xlat_functions
  */
 static xlat_action_t xlat_func_subst_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
-                                        UNUSED xlat_ctx_t const *xctx, request_t *request,
-                                        FR_DLIST_HEAD(fr_value_box_list) *in)
+                                          UNUSED xlat_ctx_t const *xctx, request_t *request,
+                                          FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        char const              *p, *q, *end;
        char const              *regex;
@@ -3329,9 +3372,11 @@ static xlat_action_t xlat_func_subst_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
        regex_t                 *pattern;
        fr_regex_flags_t        flags;
        fr_value_box_t          *vb;
-       fr_value_box_t          *subject_vb = fr_value_box_list_head(in);
-       fr_value_box_t          *regex_vb = fr_value_box_list_next(in, subject_vb);
-       fr_value_box_t          *rep_vb = fr_value_box_list_next(in, regex_vb);
+       fr_value_box_t          *subject_vb;
+       fr_value_box_t          *regex_vb;
+       fr_value_box_t          *rep_vb;
+
+       XLAT_ARGS(args, &subject_vb, &regex_vb, &rep_vb);
 
        /* coverity[dereference] */
        p = regex_vb->vb_strvalue;
@@ -3392,7 +3437,6 @@ static xlat_action_t xlat_func_subst_regex(TALLOC_CTX *ctx, fr_dcursor_t *out,
 }
 #endif
 
-
 static xlat_arg_parser_t const xlat_func_subst_args[] = {
        { .required = true, .concat = true, .type = FR_TYPE_STRING },
        { .required = true, .concat = true, .type = FR_TYPE_STRING },
@@ -3421,7 +3465,7 @@ static xlat_action_t xlat_func_subst(TALLOC_CTX *ctx, fr_dcursor_t *out,
 #else
                                   UNUSED xlat_ctx_t const *xctx,
 #endif
-                                  request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                  request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        char const              *p, *q, *end;
        char                    *vb_str;
@@ -3430,15 +3474,16 @@ static xlat_action_t xlat_func_subst(TALLOC_CTX *ctx, fr_dcursor_t *out,
        size_t                  pattern_len, rep_len;
 
        fr_value_box_t          *rep_vb, *vb;
-       fr_value_box_t          *subject_vb = fr_value_box_list_head(in);
-       fr_value_box_t          *pattern_vb = fr_value_box_list_next(in, subject_vb);
+       fr_value_box_t          *subject_vb;
+       fr_value_box_t          *pattern_vb;
+
+       XLAT_ARGS(args, &subject_vb, &pattern_vb, &rep_vb);
 
        /* coverity[dereference] */
        pattern = pattern_vb->vb_strvalue;
-
        if (*pattern == '/') {
 #ifdef HAVE_REGEX_PCRE2
-               return xlat_func_subst_regex(ctx, out, xctx, request, in);
+               return xlat_func_subst_regex(ctx, out, xctx, request, args);
 #else
                REDEBUG("regex based substitutions require libpcre2.  "
                        "Check ${features.regex-pcre2} to determine support");
@@ -3455,7 +3500,6 @@ static xlat_action_t xlat_func_subst(TALLOC_CTX *ctx, fr_dcursor_t *out,
                return XLAT_ACTION_FAIL;
        }
 
-       rep_vb = fr_value_box_list_next(in, pattern_vb);
        rep = rep_vb->vb_strvalue;
        rep_len = rep_vb->vb_length;
 
@@ -3489,7 +3533,6 @@ static xlat_action_t xlat_func_subst(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-
 static xlat_arg_parser_t const xlat_func_time_args[] = {
        { .required = false, .single = true, .type = FR_TYPE_STRING },
        XLAT_ARG_PARSER_TERMINATOR
@@ -3514,12 +3557,14 @@ update reply {
  */
 static xlat_action_t xlat_func_time(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                    UNUSED xlat_ctx_t const *xctx,
-                                   request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                   request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
-       fr_value_box_t          *arg = fr_value_box_list_head(in);
+       fr_value_box_t          *arg;
        fr_value_box_t          *vb;
        fr_unix_time_t          value;
 
+       XLAT_ARGS(args, &arg);
+
        if (!arg || (strcmp(arg->vb_strvalue, "now") == 0)) {
                value = fr_time_to_unix_time(fr_time());
 
@@ -3593,11 +3638,13 @@ append:
  * If upper is true, change to uppercase, otherwise, change to lowercase
  */
 static xlat_action_t xlat_change_case(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out,
-                                      UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in, bool upper)
+                                      UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args, bool upper)
 {
        char            *p;
        char const      *end;
-       fr_value_box_t  *vb = fr_value_box_list_pop_head(in);
+       fr_value_box_t  *vb;
+
+       XLAT_ARGS(args, &vb);
 
        p = UNCONST(char *, vb->vb_strvalue);
        end = p + vb->vb_length;
@@ -3607,15 +3654,15 @@ static xlat_action_t xlat_change_case(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out,
                p++;
        }
 
+       fr_value_box_list_remove(args, vb);     /* Can't leave it in both lists */
        fr_dcursor_append(out, vb);
 
        return XLAT_ACTION_DONE;
 }
 
-static xlat_arg_parser_t const xlat_change_case_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_STRING
+static xlat_arg_parser_t const xlat_change_case_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 
@@ -3657,10 +3704,9 @@ static xlat_action_t xlat_func_toupper(TALLOC_CTX *ctx, fr_dcursor_t *out,
 }
 
 
-static xlat_arg_parser_t const xlat_func_urlquote_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_STRING
+static xlat_arg_parser_t const xlat_func_urlquote_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** URLencode special characters
@@ -3674,13 +3720,15 @@ static xlat_arg_parser_t const xlat_func_urlquote_arg = {
  */
 static xlat_action_t xlat_func_urlquote(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                        UNUSED xlat_ctx_t const *xctx,
-                                       UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                       UNUSED request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        char const      *p, *end;
        char            *buff_p;
        size_t          outlen = 0;
        fr_value_box_t  *vb;
-       fr_value_box_t  *in_head = fr_value_box_list_head(in);
+       fr_value_box_t  *in_head;
+
+       XLAT_ARGS(args, &in_head);
 
        p = in_head->vb_strvalue;
        end = p + in_head->vb_length;
@@ -3737,10 +3785,9 @@ static xlat_action_t xlat_func_urlquote(TALLOC_CTX *ctx, fr_dcursor_t *out,
 }
 
 
-static xlat_arg_parser_t const xlat_func_urlunquote_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_STRING
+static xlat_arg_parser_t const xlat_func_urlunquote_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** URLdecode special characters
@@ -3756,14 +3803,16 @@ static xlat_arg_parser_t const xlat_func_urlunquote_arg = {
  */
 static xlat_action_t xlat_func_urlunquote(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                          UNUSED xlat_ctx_t const *xctx,
-                                         request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                         request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        char const      *p, *end;
        char            *buff_p;
        char            *c1, *c2;
        size_t          outlen = 0;
        fr_value_box_t  *vb;
-       fr_value_box_t  *in_head = fr_value_box_list_head(in);
+       fr_value_box_t  *in_head;
+
+       XLAT_ARGS(args, &in_head);
 
        p = in_head->vb_strvalue;
        end = p + in_head->vb_length;
@@ -3886,7 +3935,7 @@ static xlat_arg_parser_t const protocol_encode_xlat_args[] = {
  */
 static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                          xlat_ctx_t const *xctx,
-                                         request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                         request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        tmpl_t          *vpt;
        fr_pair_t       *vp;
@@ -3898,11 +3947,12 @@ static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        uint8_t         binbuf[2048];
        uint8_t         *p = binbuf, *end = p + sizeof(binbuf);
        ssize_t         len = 0;
-       fr_value_box_t  *in_head = fr_value_box_list_head(in);
-
+       fr_value_box_t  *in_head;
        void            *encode_ctx = NULL;
        fr_test_point_pair_encode_t const *tp_encode;
 
+       XLAT_ARGS(args, &in_head);
+
        memcpy(&tp_encode, xctx->inst, sizeof(tp_encode)); /* const issues */
 
        if (tmpl_afrom_attr_str(ctx, NULL, &vpt, in_head->vb_strvalue,
@@ -4144,7 +4194,7 @@ do { \
 #define XLAT_REGISTER_MONO(_xlat, _func, _return_type, _arg) \
 do { \
        if (!(xlat = xlat_register(NULL, _xlat, _func, _return_type, XLAT_FLAG_PURE))) return -1; \
-       xlat_func_mono(xlat, &_arg); \
+       xlat_func_mono(xlat, _arg); \
        xlat_internal(xlat); \
 } while (0)
 
@@ -4192,7 +4242,7 @@ do { \
 #define XLAT_REGISTER_MONO(_xlat, _func, _return_type, _arg) \
 do { \
        if (!(xlat = xlat_register(NULL, _xlat, _func, _return_type, NULL))) return -1; \
-       xlat_func_mono(xlat, &_arg); \
+       xlat_func_mono(xlat, _arg); \
        xlat_internal(xlat); \
 } while (0)
 
index a73324a846aeb664b063bfe057ed993c6346351f..446db673fb801d5b10be346963d8bf43000b7080 100644 (file)
@@ -47,9 +47,9 @@ static fr_dict_autoload_t xlat_eval_dict[] = {
        { NULL }
 };
 
-fr_dict_attr_t const       *attr_expr_bool_enum; /* xlat_expr.c */
-fr_dict_attr_t const           *attr_module_return_code; /* xlat_expr.c */
-fr_dict_attr_t const           *attr_cast_base; /* xlat_expr.c */
+fr_dict_attr_t const *attr_expr_bool_enum; /* xlat_expr.c */
+fr_dict_attr_t const *attr_module_return_code; /* xlat_expr.c */
+fr_dict_attr_t const *attr_cast_base; /* xlat_expr.c */
 
 static fr_dict_attr_autoload_t xlat_eval_dict_attr[] = {
        { .out = &attr_module_return_code, .name = "Module-Return-Code", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
@@ -258,14 +258,14 @@ static xlat_action_t xlat_process_arg_list(TALLOC_CTX *ctx, FR_DLIST_HEAD(fr_val
 do { \
        if ((_arg)->func && ((_vb)->tainted || (_arg)->always_escape) && \
            ((_arg)->func(request, _vb, (_arg)->uctx) < 0)) { \
-               RPEDEBUG("Function %s failed escaping argument %u", name, _arg_num); \
+               RPEDEBUG("Function \"%s\" failed escaping argument %u", name, _arg_num); \
                return XLAT_ACTION_FAIL; \
        } \
 } while (0)
 
        if (fr_value_box_list_empty(list)) {
                if (arg->required) {
-                       RWDEBUG("Function %s is missing required argument %u", name, arg_num);
+                       REDEBUG("Function \"%s\" is missing required argument %u", name, arg_num);
                        return XLAT_ACTION_FAIL;
                }
                return XLAT_ACTION_DONE;
@@ -288,7 +288,7 @@ do { \
                                                      vb, list, arg->type,
                                                      FR_VALUE_BOX_LIST_FREE, true,
                                                      SIZE_MAX) < 0) {
-                       RPEDEBUG("Function %s failed concatenating arguments to type %s", name, fr_type_to_str(arg->type));
+                       RPEDEBUG("Function \"%s\" failed concatenating arguments to type %s", name, fr_type_to_str(arg->type));
                        return XLAT_ACTION_FAIL;
                }
                fr_assert(fr_value_box_list_num_elements(list) <= 1);
@@ -302,7 +302,7 @@ do { \
         */
        if (arg->single) {
                if (fr_value_box_list_num_elements(list) > 1) {
-                       RPEDEBUG("Function %s was provided an incorrect number of values at argument %u, "
+                       RPEDEBUG("Function \"%s\" was provided an incorrect number of values at argument %u, "
                                 "expected %s got %u",
                                 name, arg_num,
                                 arg->required ? "0-1" : "1",
@@ -316,7 +316,7 @@ do { \
                cast_error:
                        if (fr_value_box_cast_in_place(ctx, vb,
                                                       arg->type, NULL) < 0) {
-                               RPEDEBUG("Function %s failed cast argument %u to type %s", name, arg_num, fr_type_to_str(arg->type));
+                               RPEDEBUG("Function \"%s\" failed to cast argument %u to type %s", name, arg_num, fr_type_to_str(arg->type));
                                return XLAT_ACTION_FAIL;
                        }
                }
@@ -362,7 +362,8 @@ do { \
  * @param[in] func             to call
  */
 static inline CC_HINT(always_inline)
-xlat_action_t xlat_process_args(TALLOC_CTX *ctx, FR_DLIST_HEAD(fr_value_box_list) *list, request_t *request, xlat_t const *func)
+xlat_action_t xlat_process_args(TALLOC_CTX *ctx, FR_DLIST_HEAD(fr_value_box_list) *list,
+                               request_t *request, xlat_t const *func)
 {
        xlat_arg_parser_t const *arg_p = func->args;
        xlat_action_t           xa;
@@ -373,6 +374,7 @@ xlat_action_t xlat_process_args(TALLOC_CTX *ctx, FR_DLIST_HEAD(fr_value_box_list
         */
        if (!func->args) return XLAT_ACTION_DONE;
 
+
        /*
         *      xlat needs no input processing just return.
         */
@@ -384,11 +386,6 @@ xlat_action_t xlat_process_args(TALLOC_CTX *ctx, FR_DLIST_HEAD(fr_value_box_list
         *      xlat takes all input as a single vb.
         */
        case XLAT_INPUT_MONO:
-               return xlat_process_arg_list(ctx, list, request, func->name, arg_p, 1);
-
-       /*
-        *      xlat consumes a sequence of arguments.
-        */
        case XLAT_INPUT_ARGS:
                vb = fr_value_box_list_head(list);
                while (arg_p->type != FR_TYPE_NULL) {
@@ -401,7 +398,8 @@ xlat_action_t xlat_process_args(TALLOC_CTX *ctx, FR_DLIST_HEAD(fr_value_box_list
                        if (!vb) {
                                if (arg_p->required) {
                                missing:
-                                       REDEBUG("Function %s is missing required argument %u", func->name, (unsigned int)((arg_p - func->args) + 1));
+                                       REDEBUG("Function \"%s\" is missing required argument %u",
+                                                func->name, (unsigned int)((arg_p - func->args) + 1));
                                        return XLAT_ACTION_FAIL;
                                }
 
@@ -553,7 +551,8 @@ bool xlat_process_return(request_t *request, xlat_t const *func, FR_DLIST_HEAD(f
  *
  */
 static inline CC_HINT(always_inline)
-xlat_action_t xlat_eval_one_letter(TALLOC_CTX *ctx, FR_DLIST_HEAD(fr_value_box_list) *out, request_t *request, char letter)
+xlat_action_t xlat_eval_one_letter(TALLOC_CTX *ctx, FR_DLIST_HEAD(fr_value_box_list) *out,
+                                  request_t *request, char letter)
 {
 
        char            buffer[64];
@@ -788,9 +787,9 @@ void xlat_signal(xlat_func_signal_t signal, xlat_exp_t const *exp,
  *                             when it yielded.
  */
 xlat_action_t xlat_frame_eval_resume(TALLOC_CTX *ctx, fr_dcursor_t *out,
-                                      xlat_exp_head_t const **child,
-                                      request_t *request,  xlat_exp_head_t const *head, xlat_exp_t const **in,
-                                      FR_DLIST_HEAD(fr_value_box_list) *result, xlat_func_t resume, void *rctx)
+                                    xlat_exp_head_t const **child,
+                                    request_t *request,  xlat_exp_head_t const *head, xlat_exp_t const **in,
+                                    FR_DLIST_HEAD(fr_value_box_list) *result, xlat_func_t resume, void *rctx)
 {
        xlat_action_t           xa;
        xlat_exp_t const        *node = *in;
index fc4ef326d2b4a19d152cd112d1ac17662c5ff1f0..f4fc2f58c056cabd540832e5382f8f8415241b59 100644 (file)
@@ -1404,8 +1404,9 @@ static int xlat_function_args_to_tmpl(xlat_inst_ctx_t const *xctx)
 }
 
 
-static xlat_arg_parser_t const xlat_func_rcode_arg = {
-       .concat = true, .type = FR_TYPE_STRING,
+static xlat_arg_parser_t const xlat_func_rcode_arg[] = {
+       { .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Return the rcode as a string, or bool match if the argument is an rcode name
@@ -1420,11 +1421,12 @@ static xlat_arg_parser_t const xlat_func_rcode_arg = {
  */
 static xlat_action_t xlat_func_rcode(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                     UNUSED xlat_ctx_t const *xctx,
-                                    request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
+                                    request_t *request, FR_DLIST_HEAD(fr_value_box_list) *args)
 {
        fr_value_box_t  *vb;
-       fr_value_box_t  *src = fr_value_box_list_head(in);
+       fr_value_box_t  *src;
 
+       XLAT_ARGS(args, &src);
        /*
         *      Query the rcode if there's no argument.  Otherwise do a boolean check if the passed string
         *      matches the current rcode.
@@ -1456,8 +1458,9 @@ typedef struct {
        FR_DLIST_HEAD(fr_value_box_list)        list;
 } xlat_exists_rctx_t;
 
-static xlat_arg_parser_t const xlat_func_exists_arg = {
-       .concat = true, .type = FR_TYPE_STRING,
+static xlat_arg_parser_t const xlat_func_exists_arg[] = {
+       { .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /*
@@ -1665,7 +1668,7 @@ do { \
 #define XLAT_REGISTER_MONO(_xlat, _func, _arg) \
 do { \
        if (!(xlat = xlat_register(NULL, _xlat, _func, FR_TYPE_VOID, NULL))) return -1; \
-       xlat_func_mono(xlat, &_arg); \
+       xlat_func_mono(xlat, _arg); \
        xlat_internal(xlat); \
 } while (0)
 
index 70416bacb46e7e82db459b03177fe1677bc7999a..146d9b0eeaff8b58b667daa05d51048e6c7acd1e 100644 (file)
@@ -271,7 +271,7 @@ static inline int xlat_tokenize_function_mono(xlat_exp_head_t *head,
                                              fr_sbuff_t *in,
                                              tmpl_rules_t const *t_rules)
 {
-       xlat_exp_t              *node;
+       xlat_exp_t              *node, *arg_group;
        xlat_t                  *func;
        fr_sbuff_marker_t       m_s;
 
@@ -338,15 +338,75 @@ static inline int xlat_tokenize_function_mono(xlat_exp_head_t *head,
        XLAT_DEBUG("FUNC-ARGS <-- %s ... %pV",
                   node->fmt, fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
 
-       fr_sbuff_marker_release(&m_s);
+       fr_sbuff_set(&m_s, in);
 
        /*
-        *      Now parse the child nodes that form the
-        *      function's arguments.
+        *      Allocate a top level group to hold all
+        *      the argument fragments.  This gives us
+        *      somewhere to store the quoting.
         */
-       if (xlat_tokenize_string(node->call.args, in, true, &xlat_expansion_rules, t_rules) < 0) {
-               goto error;
+       arg_group = xlat_exp_alloc(node->call.args, XLAT_GROUP, NULL, 0);
+
+       /*
+        *      It's the escape char.  We can't start
+        *      a quoted string.
+        */
+       if (fr_sbuff_is_char(in, xlat_expansion_rules.escapes->chr)) {
+       bareword:
+               arg_group->quote = T_BARE_WORD;
+
+               if (xlat_tokenize_string(arg_group->group, in, true, &xlat_expansion_rules, t_rules) < 0) {
+                       goto error;
+               }
+               xlat_flags_merge(&arg_group->flags, &arg_group->group->flags);
+       /*
+        *      Support passing the monolithic argument
+        *      as a string.
+        */
+       } else if (fr_sbuff_next_if_char(in, '\'')) {
+               char            *str;
+               ssize_t         slen;
+               xlat_exp_t      *literal;
+
+               arg_group->quote = T_SINGLE_QUOTED_STRING;
+
+               literal = xlat_exp_alloc(arg_group, XLAT_BOX, NULL, 0);
+
+               /*
+                *      Find the next token
+                */
+               slen = fr_sbuff_out_aunescape_until(literal, &str, in, SIZE_MAX,
+                                                   value_parse_rules_single_quoted.terminals,
+                                                   value_parse_rules_single_quoted.escapes);
+               if (slen < 0) goto error;
+
+               xlat_exp_set_name_buffer_shallow(literal, str);
+               fr_value_box_strdup_shallow(&literal->data, NULL, str, false);
+               literal->flags.constant = true;
+               xlat_exp_insert_tail(arg_group->group, literal);
+
+               if (!fr_sbuff_next_if_char(in, '\'')) {
+                       fr_strerror_const("Missing closing \"'\"");
+                       goto error;
+               }
+       } else if (fr_sbuff_next_if_char(in, '"')) {
+               arg_group->quote = T_DOUBLE_QUOTED_STRING;
+
+               if (xlat_tokenize_string(arg_group->group, in, false,
+                                        &value_parse_rules_double_quoted, t_rules) < 0) {
+                       goto error;
+               }
+               if (!fr_sbuff_next_if_char(in, '"')) {
+                       fr_strerror_const("Missing closing '\"'");
+                       goto error;
+               }
+       } else {
+               goto bareword;
        }
+       xlat_exp_set_name(arg_group, fr_sbuff_current(&m_s), fr_sbuff_behind(&m_s));
+       xlat_exp_insert_tail(node->call.args, arg_group);
+
+       fr_sbuff_marker_release(&m_s);
 
        /*
         *      Check there's input if it's needed
index 4a97e40c1ee0f97670a6ea8a542195ff614187c9..f5e31966802d59b07ad1aa0c525fa016f66409e3 100644 (file)
@@ -88,12 +88,12 @@ static_assert(sizeof(unsigned int) >= 4, "Unsigned integer too small on this pla
 #define fr_dlist_foreach_safe(_list_head, _type, _iter) \
 { \
        _type *_iter; \
-       fr_dlist_t _tmp; \
+       fr_dlist_t _tmp ## _iter; \
        for (_iter = fr_dlist_head(_list_head), \
-            _tmp = fr_dlist_head(_list_head) ? *fr_dlist_item_to_entry((_list_head)->offset, fr_dlist_head(_list_head)) : (fr_dlist_t){ .prev = NULL, .next = NULL }; \
+            _tmp ## _iter = fr_dlist_head(_list_head) ? *fr_dlist_item_to_entry((_list_head)->offset, fr_dlist_head(_list_head)) : (fr_dlist_t){ .prev = NULL, .next = NULL }; \
             _iter; \
-            _iter = _tmp.next && (_tmp.next != &(_list_head)->entry) ? fr_dlist_entry_to_item((_list_head)->offset, _tmp.next) : NULL, \
-            _tmp = _tmp.next && (_tmp.next != &(_list_head)->entry) ? *_tmp.next : (fr_dlist_t){ .prev = NULL, .next = NULL })
+            _iter = _tmp ## _iter.next && (_tmp ## _iter.next != &(_list_head)->entry) ? fr_dlist_entry_to_item((_list_head)->offset, _tmp ## _iter.next) : NULL, \
+            _tmp ## _iter = _tmp ## _iter.next && (_tmp ## _iter.next != &(_list_head)->entry) ? *_tmp ## _iter.next : (fr_dlist_t){ .prev = NULL, .next = NULL })
 
 
 /** Find the dlist pointers within a list item
index b6d755aaf1c04b1bba307e2ddf2e890232707410..e83239984931c81413f1dedceb5fa9613cc60e4b 100644 (file)
@@ -563,10 +563,9 @@ static int cipher_rsa_certificate_file_load(TALLOC_CTX *ctx, void *out, void *pa
        return 0;
 }
 
-static xlat_arg_parser_t const cipher_rsa_encrypt_xlat_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_STRING
+static xlat_arg_parser_t const cipher_rsa_encrypt_xlat_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Encrypt input data
@@ -625,10 +624,9 @@ static xlat_action_t cipher_rsa_encrypt_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
 }
 
 
-static xlat_arg_parser_t const cipher_rsa_sign_xlat_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_STRING,
+static xlat_arg_parser_t const cipher_rsa_sign_xlat_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Sign input data
@@ -704,10 +702,9 @@ static xlat_action_t cipher_rsa_sign_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-static xlat_arg_parser_t const cipher_rsa_decrypt_xlat_arg = {
-       .required = true,
-       .concat = true,
-       .type = FR_TYPE_OCTETS
+static xlat_arg_parser_t const cipher_rsa_decrypt_xlat_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_OCTETS },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Decrypt input data
@@ -1303,7 +1300,7 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
                         */
                        xlat_name = talloc_asprintf(inst, "%s_decrypt", mctx->inst->name);
                        xlat = xlat_register_module(inst, mctx, xlat_name, cipher_rsa_decrypt_xlat, FR_TYPE_STRING, NULL);
-                       xlat_func_mono(xlat, &cipher_rsa_decrypt_xlat_arg);
+                       xlat_func_mono(xlat, cipher_rsa_decrypt_xlat_arg);
                        talloc_free(xlat_name);
 
                        /*
@@ -1340,7 +1337,7 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
                         */
                        xlat_name = talloc_asprintf(inst, "%s_encrypt", mctx->inst->name);
                        xlat = xlat_register_module(inst, mctx, xlat_name, cipher_rsa_encrypt_xlat, FR_TYPE_OCTETS, NULL);
-                       xlat_func_mono(xlat, &cipher_rsa_encrypt_xlat_arg);
+                       xlat_func_mono(xlat, cipher_rsa_encrypt_xlat_arg);
                        talloc_free(xlat_name);
 
                        /*
@@ -1348,7 +1345,7 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
                         */
                        xlat_name = talloc_asprintf(inst, "%s_sign", mctx->inst->name);
                        xlat = xlat_register_module(inst, mctx, xlat_name, cipher_rsa_sign_xlat, FR_TYPE_OCTETS, NULL);
-                       xlat_func_mono(xlat, &cipher_rsa_sign_xlat_arg);
+                       xlat_func_mono(xlat, cipher_rsa_sign_xlat_arg);
                        talloc_free(xlat_name);
 
                        /*
index 078df5f19e480a5d63c9702690ff152aab78504b..861bb21192daa9639e1421559aa95f2d35783910 100644 (file)
@@ -21,6 +21,7 @@
  *
  * @copyright 2018 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
  */
+#include "lib/unlang/xlat.h"
 RCSID("$Id$")
 USES_APPLE_DEPRECATED_API
 
@@ -45,7 +46,10 @@ static const CONF_PARSER module_config[] = {
 
 static char const hextab[] = "0123456789abcdef";
 
-static xlat_arg_parser_t const escape_xlat_arg = { .required = true, .concat = true, .type = FR_TYPE_STRING };
+static xlat_arg_parser_t const escape_xlat_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
+};
 
 /** Equivalent to the old safe_characters functionality in rlm_sql but with utf8 support
  *
@@ -116,7 +120,10 @@ static xlat_action_t escape_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-static xlat_arg_parser_t const unescape_xlat_arg = { .required = true, .concat = true, .type = FR_TYPE_STRING };
+static xlat_arg_parser_t const unescape_xlat_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
+};
 
 /** Equivalent to the old safe_characters functionality in rlm_sql
  *
@@ -190,10 +197,10 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
 
        MEM(unescape = talloc_asprintf(NULL, "un%s", mctx->inst->name));
        xlat = xlat_register_module(NULL, mctx, mctx->inst->name, escape_xlat, FR_TYPE_STRING, XLAT_FLAG_PURE);
-       xlat_func_mono(xlat, &escape_xlat_arg);
+       xlat_func_mono(xlat, escape_xlat_arg);
 
        xlat = xlat_register_module(NULL, mctx, unescape, unescape_xlat, FR_TYPE_STRING, XLAT_FLAG_PURE);
-       xlat_func_mono(xlat, &unescape_xlat_arg);
+       xlat_func_mono(xlat, unescape_xlat_arg);
        talloc_free(unescape);
 
        return 0;
index ef6b2a48f1f1f80d4437ff303cd0c14e2f13042a..db7a5a5a9fe2c5a30fbaf1a9485f0e57e4d50833 100644 (file)
@@ -86,7 +86,10 @@ static const CONF_PARSER mod_config[] = {
        CONF_PARSER_TERMINATOR
 };
 
-static xlat_arg_parser_t const xlat_idna_arg = { .required = true, .concat = true, .type = FR_TYPE_STRING };
+static xlat_arg_parser_t const xlat_idna_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
+};
 
 /** Convert domain name to ASCII punycode
  *
@@ -150,7 +153,7 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
        xlat_t          *xlat;
 
        xlat = xlat_register_module(inst, mctx, mctx->inst->name, xlat_idna, FR_TYPE_STRING, XLAT_FLAG_PURE);
-       xlat_func_mono(xlat, &xlat_idna_arg);
+       xlat_func_mono(xlat, xlat_idna_arg);
 
        return 0;
 }
index b7b38c080341838829e60d0eb7e3e931e6692981..a43538d75fa2bce8a76e5d5ac0608bc645d481df 100644 (file)
@@ -77,8 +77,9 @@ typedef struct {
        json_object             *root;
 } rlm_json_jpath_to_eval_t;
 
-static xlat_arg_parser_t const json_quote_xlat_arg = {
-       .concat = true, .type = FR_TYPE_STRING
+static xlat_arg_parser_t const json_quote_xlat_arg[] = {
+       { .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Ensure contents are quoted correctly for a JSON document
@@ -110,8 +111,9 @@ static xlat_action_t json_quote_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-static xlat_arg_parser_t const jpath_validate_xlat_arg = {
-       .required = true, .concat = true, .type = FR_TYPE_STRING
+static xlat_arg_parser_t const jpath_validate_xlat_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Determine if a jpath expression is valid
@@ -151,8 +153,9 @@ static xlat_action_t jpath_validate_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
-static xlat_arg_parser_t const json_encode_xlat_arg = {
-       .required = true, .concat = true, .type = FR_TYPE_STRING
+static xlat_arg_parser_t const json_encode_xlat_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Convert given attributes to a JSON document
@@ -527,13 +530,13 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
        fr_json_format_t        *format = inst->format;
 
        xlat = xlat_register_module(inst, mctx, "jsonquote", json_quote_xlat, FR_TYPE_STRING, NULL);
-       if (xlat) xlat_func_mono(xlat, &json_quote_xlat_arg);
+       if (xlat) xlat_func_mono(xlat, json_quote_xlat_arg);
        xlat = xlat_register_module(inst, mctx, "jpathvalidate", jpath_validate_xlat, FR_TYPE_STRING, NULL);
-       if (xlat) xlat_func_mono(xlat, &jpath_validate_xlat_arg);
+       if (xlat) xlat_func_mono(xlat, jpath_validate_xlat_arg);
 
        name = talloc_asprintf(inst, "%s_encode", mctx->inst->name);
        xlat = xlat_register_module(inst, mctx, name, json_encode_xlat, FR_TYPE_STRING, NULL);
-       xlat_func_mono(xlat, &json_encode_xlat_arg);
+       xlat_func_mono(xlat, json_encode_xlat_arg);
        talloc_free(name);
 
        /*
index a5fd0991ba3bc29108c882cd018e08b34f8069f8..6697c757c157dc0b67a30be949a24f872ed282a5 100644 (file)
@@ -176,8 +176,10 @@ global_lib_autoinst_t const *rlm_ldap_lib[] = {
        GLOBAL_LIB_TERMINATOR
 };
 
-
-static xlat_arg_parser_t const ldap_escape_xlat_arg = { .required = true, .concat = true, .type = FR_TYPE_STRING };
+static xlat_arg_parser_t const ldap_escape_xlat_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
+};
 
 /** Escape LDAP string
  *
@@ -384,7 +386,10 @@ static fr_uri_part_t const ldap_uri_parts[] = {
        XLAT_URI_PART_TERMINATOR
 };
 
-static xlat_arg_parser_t const ldap_xlat_arg = { .required = true, .type = FR_TYPE_STRING };
+static xlat_arg_parser_t const ldap_xlat_arg[] = {
+       { .required = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
+};
 
 /** Expand an LDAP URL into a query, and return a string result from that query.
  *
@@ -395,7 +400,7 @@ static xlat_action_t ldap_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out,
                               request_t *request, FR_DLIST_HEAD(fr_value_box_list) *in)
 {
        fr_ldap_thread_t        *t = talloc_get_type_abort(xctx->mctx->thread, fr_ldap_thread_t);
-       fr_value_box_t          *in_vb = NULL;
+       fr_value_box_t          *uri_components, *uri;
        char                    *host_url;
        fr_ldap_config_t const  *handle_config = t->config;
        fr_ldap_thread_trunk_t  *ttrunk;
@@ -403,21 +408,27 @@ static xlat_action_t ldap_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out,
 
        LDAPURLDesc             *ldap_url;
 
-       if (fr_uri_escape(in, ldap_uri_parts, NULL) < 0) return XLAT_ACTION_FAIL;
+       XLAT_ARGS(in, &uri_components);
+
+       if (fr_uri_escape(&uri_components->vb_group, ldap_uri_parts, NULL) < 0) return XLAT_ACTION_FAIL;
+
+       /*
+        *      Smush everything into the first URI box
+        */
+       uri = fr_value_box_list_head(&uri_components->vb_group);
 
-       in_vb = fr_value_box_list_head(in);
-       if (fr_value_box_list_concat_in_place(in_vb, in_vb, in, FR_TYPE_STRING, FR_VALUE_BOX_LIST_FREE,
-                                            true, SIZE_MAX) < 0) {
+       if (fr_value_box_list_concat_in_place(uri, uri, &uri_components->vb_group,
+                                             FR_TYPE_STRING, FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
                REDEBUG("Failed concattenating input");
                return XLAT_ACTION_FAIL;
        }
 
-       if (!ldap_is_ldap_url(in_vb->vb_strvalue)) {
+       if (!ldap_is_ldap_url(uri->vb_strvalue)) {
                REDEBUG("String passed does not look like an LDAP URL");
                return XLAT_ACTION_FAIL;
        }
 
-       if (ldap_url_parse(in_vb->vb_strvalue, &ldap_url)){
+       if (ldap_url_parse(uri->vb_strvalue, &ldap_url)){
                REDEBUG("Parsing LDAP URL failed");
        error:
                ldap_free_urldesc(ldap_url);
@@ -1760,12 +1771,12 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
        }
 
        xlat = xlat_register_module(NULL, mctx, mctx->inst->name, ldap_xlat, FR_TYPE_STRING, NULL);
-       xlat_func_mono(xlat, &ldap_xlat_arg);
+       xlat_func_mono(xlat, ldap_xlat_arg);
 
        xlat = xlat_register_module(NULL, mctx, "ldap_escape", ldap_escape_xlat, FR_TYPE_STRING, XLAT_FLAG_PURE);
-       if (xlat) xlat_func_mono(xlat, &ldap_escape_xlat_arg);
+       if (xlat) xlat_func_mono(xlat, ldap_escape_xlat_arg);
        xlat = xlat_register_module(NULL, mctx, "ldap_unescape", ldap_unescape_xlat, FR_TYPE_STRING, XLAT_FLAG_PURE);
-       if (xlat) xlat_func_mono(xlat, &ldap_escape_xlat_arg);
+       if (xlat) xlat_func_mono(xlat, ldap_escape_xlat_arg);
 
        map_proc_register(inst, mctx->inst->name, mod_map_proc, ldap_map_verify, 0);
 
index b6f62d91fa512f37e49ac7c5a536384c57afa058..cd0c7ebd534a5f72bb9b05702c0c60ebdf7fb1b4 100644 (file)
@@ -1064,12 +1064,14 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
         *      The xlat escape function needs access to inst - so
         *      argument parser details need to be defined here
         */
-       sql_xlat_arg = talloc_zero(inst, xlat_arg_parser_t);
-       sql_xlat_arg->type = FR_TYPE_STRING;
-       sql_xlat_arg->required = true;
-       sql_xlat_arg->concat = true;
-       sql_xlat_arg->func = sql_xlat_escape;
-       sql_xlat_arg->uctx = inst;
+       sql_xlat_arg = talloc_zero_array(inst, xlat_arg_parser_t, 2);
+       sql_xlat_arg[0].type = FR_TYPE_STRING;
+       sql_xlat_arg[0].required = true;
+       sql_xlat_arg[0].concat = true;
+       sql_xlat_arg[0].func = sql_xlat_escape;
+       sql_xlat_arg[0].uctx = inst;
+       sql_xlat_arg[1] = (xlat_arg_parser_t)XLAT_ARG_PARSER_TERMINATOR;
+
        xlat_func_mono(xlat, sql_xlat_arg);
 
        /*
index d25d5210400d7719e504fb583419687fceb2b24f..b5a371f9b13d0c9ba12054f64d844fce28a3fc44 100644 (file)
@@ -126,8 +126,9 @@ static ssize_t modhex2hex(char const *modhex, char *hex, size_t len)
        return i;
 }
 
-static xlat_arg_parser_t const modhex_to_hex_xlat_arg = {
-       .required = true, .concat = true, .type = FR_TYPE_STRING
+static xlat_arg_parser_t const modhex_to_hex_xlat_arg[] = {
+       { .required = true, .concat = true, .type = FR_TYPE_STRING },
+       XLAT_ARG_PARSER_TERMINATOR
 };
 
 /** Xlat to convert Yubikey modhex to standard hex
@@ -196,7 +197,7 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
 #endif
 
        xlat = xlat_register_module(inst, mctx, "modhextohex", modhex_to_hex_xlat, FR_TYPE_STRING, XLAT_FLAG_PURE);
-       if (xlat) xlat_func_mono(xlat, &modhex_to_hex_xlat_arg);
+       if (xlat) xlat_func_mono(xlat, modhex_to_hex_xlat_arg);
 
        return 0;
 }
index b80c9dab4fa833e5377cdc53d5ba25bf6fa0455e..719e5c2f87da5ca9744fbf7cc9c01d6997867beb 100644 (file)
@@ -22,7 +22,7 @@ if ("%{regex:foo}") {
        test_fail
 }
 
-if (!(&Module-Failure-Message == "No previous named regex capture group")) {
+if (!(&Module-Failure-Message[*] == "No previous named regex capture group")) {
        test_fail
 }
 
@@ -33,7 +33,7 @@ if ("%{regex:%{Tmp-Integer-1}}") {
        test_fail
 }
 
-if (!(&Module-Failure-Message == "No previous numbered regex capture group")) {
+if (!(&Module-Failure-Message[*] == "No previous numbered regex capture group")) {
        test_fail
 }
 
index 35a195caa93bceaaec6c81a41bc5d942ceb3622e..34cab022c6f3f86291cdb4cbddbb029dfba0b106 100644 (file)
@@ -105,7 +105,7 @@ if ("%(subst:%{Tmp-String-0} /***/g .)") {
        test_fail
 }
 
-if !(&Module-Failure-Message[0] == 'Failed compiling regex: quantifier does not follow a repeatable item') {
+if !(&Module-Failure-Message[*] == 'Failed compiling regex: quantifier does not follow a repeatable item') {
        test_fail
 }
 
@@ -116,7 +116,7 @@ if ("%(subst:%{Tmp-String-0} //g .)") {
        test_fail
 }
 
-if !(&Module-Failure-Message[0] == 'Failed compiling regex: Empty expression') {
+if !(&Module-Failure-Message[*] == 'Failed compiling regex: Empty expression') {
        test_fail
 }
 }
index 6356f0ad5c7ca85f516f66fdf7f670dce85d1eb5..3f4c2ad76f3a8d1e17bcf640f45cb30d8c73dc97 100644 (file)
@@ -2,4 +2,9 @@
 #       json xlat input parsing test - error with no input
 #
 
-&Tmp-String-1 := "%{json_object_ex_encode:}"  # ERROR
+&Tmp-String-1 := %{json_object_ex_encode:}  # ERROR
+if (&Tmp-String-1) {
+       test_fail
+}
+
+test_pass
index 363b71bc28aeec9b6d6f5ae5be6b40473e40792b..6662a88979edf738ec11c7102fd6fb5bd53c2d93 100644 (file)
@@ -45,3 +45,21 @@ else {
 
 #  Remove the file
 &Tmp-String-0 := `/bin/sh -c "rm $ENV{MODULE_TEST_DIR}/test_xlat.log"`
+
+
+# Try with some handcrafted JSON
+
+if (%{linelog_fmt_delim_xlat:"{ \"foo\" : \"bar\", \"baz\" : \"boink\" }"} == 36) {
+       test_pass
+} else {
+       test_fail
+}
+
+&Tmp-String-0 := `/bin/sh -c "tail -n1 $ENV{MODULE_TEST_DIR}/test_xlat.log"`
+
+if (&Tmp-String-0 == '{ "foo" : "bar", "baz" : "boink" }, ') {
+       test_pass
+}
+else {
+       test_fail
+}
index 1b8bcd0980d7d062c008906dc39e824d7b76556a..0d2dab7e052957a082bcd10f3237cc780f93bc2b 100644 (file)
@@ -324,5 +324,29 @@ match %(rpad:&User-Name 5 x)
 xlat %(rpad:&User-Name foo x)
 match ERROR offset 24: Failed parsing argument 1 as type 'uint64'
 
+#
+#  Argument quoting
+#
+#  The code here isn't great.  If it's ever fixes to actually show the quoting
+#  then these should be correct
+#
+xlat %{md5:"arg"}
+match %{md5:"arg"}
+
+xlat %{md5:"arg}"}
+match %{md5:"arg}"}
+
+xlat %{md5:"arg}
+match ERROR offset 12: Missing closing '"'
+
+xlat %{md5:"arg\""}
+match %{md5:"arg\""}
+
+xlat %{md5:'arg'}
+match %{md5:'arg'}
+
+xlat %{md5:'arg"'}
+match %{md5:'arg"'}
+
 count
-match 187
+match 199