]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
there's no need for INPUT_ARGS
authorAlan T. DeKok <aland@freeradius.org>
Sat, 12 Apr 2025 17:30:31 +0000 (13:30 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Sun, 13 Apr 2025 10:24:40 +0000 (06:24 -0400)
there's now only one type of function, as opposed to the
transitional MONO and INPUT_ARGS.  So we remove the redundant
field and update the associated checks

src/lib/unlang/xlat.h
src/lib/unlang/xlat_alloc.c
src/lib/unlang/xlat_eval.c
src/lib/unlang/xlat_expr.c
src/lib/unlang/xlat_func.c
src/lib/unlang/xlat_priv.h
src/lib/unlang/xlat_redundant.c
src/lib/unlang/xlat_tokenize.c

index 216f67c8af5936793041ce07c2b405a757e77cb6..ab1fb0f7a26626f97812efabd619308541edcba5 100644 (file)
@@ -44,11 +44,6 @@ typedef enum {
        XLAT_ACTION_FAIL                        //!< An xlat function failed.
 } xlat_action_t;
 
-typedef enum {
-       XLAT_INPUT_UNPROCESSED,                 //!< No input argument processing
-       XLAT_INPUT_ARGS                         //!< Ingests a number of arguments
-} xlat_input_type_t;
-
 typedef struct xlat_inst_s xlat_inst_t;
 typedef struct xlat_thread_inst_s xlat_thread_inst_t;
 
@@ -421,7 +416,7 @@ static inline fr_slen_t xlat_aprint(TALLOC_CTX *ctx, char **out, xlat_exp_head_t
 
 bool           xlat_is_truthy(xlat_exp_head_t const *head, bool *out);
 
-fr_slen_t      xlat_validate_function_args(xlat_exp_t *node);
+int            xlat_validate_function_args(xlat_exp_t *node);
 
 void           xlat_debug(xlat_exp_t const *node);
 
index 2f8c17567e073cb30cf28f8fb390611c943c8501..635e55de5a1982fad9be7fb7dc92db80b56ead63 100644 (file)
@@ -124,13 +124,14 @@ void _xlat_exp_set_type(NDEBUG_LOCATION_ARGS xlat_exp_t *node, xlat_type_t type)
                break;
 
        case XLAT_FUNC:
-       case XLAT_FUNC_UNRESOLVED:
-               node->call.args = _xlat_exp_head_alloc(NDEBUG_LOCATION_VALS node);
-               node->call.args->is_argv = true;
-               node->flags = node->call.args->flags;
+               node->flags = XLAT_FLAGS_INIT;
                node->flags.needs_resolving = (type == XLAT_FUNC_UNRESOLVED);
                break;
 
+       case XLAT_FUNC_UNRESOLVED:
+               node->flags = (xlat_flags_t) { .needs_resolving = true };
+               break;
+
        case XLAT_BOX:
                node->flags = XLAT_FLAGS_INIT;
                fr_value_box_init_null(&node->data);
index a8b357072493c4233f35b03e6c43b2734f382dec..f991fad364f637c49f5cc6f47f4c1d2912d97bf9 100644 (file)
@@ -188,15 +188,17 @@ static fr_slen_t xlat_fmt_print(fr_sbuff_t *out, xlat_exp_t const *node)
                our_out = FR_SBUFF(out);
                FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%%%s(", node->call.func->name);
 
-               xlat_exp_foreach(node->call.args, arg) {
-                       if ((first_done) && (node->call.func->input_type == XLAT_INPUT_ARGS)) {
-                               FR_SBUFF_IN_CHAR_RETURN(&our_out, ',');
-                       }
+               if (node->call.args) {
+                       xlat_exp_foreach(node->call.args, arg) {
+                               if (first_done && (node->call.func->args)) {
+                                       FR_SBUFF_IN_CHAR_RETURN(&our_out, ',');
+                               }
 
-                       slen = xlat_fmt_print(&our_out, arg);
-                       if (slen < 0) return slen - fr_sbuff_used(&our_out);
+                               slen = xlat_fmt_print(&our_out, arg);
+                               if (slen < 0) return slen - fr_sbuff_used(&our_out);
 
-                       first_done = true;
+                               first_done = true;
+                       }
                }
 
                FR_SBUFF_IN_CHAR_RETURN(&our_out, ')');
@@ -507,144 +509,134 @@ xlat_action_t xlat_process_args(TALLOC_CTX *ctx, fr_value_box_list_t *list,
        if (!func->args) return XLAT_ACTION_DONE;
 
        /*
-        *      xlat needs no input processing just return.
-        */
-       switch (func->input_type) {
-       case XLAT_INPUT_UNPROCESSED:
-               return XLAT_ACTION_DONE;
-
-       /*
-        *      xlat takes all input as a single vb.
+        *      Manage the arguments.
         */
-       case XLAT_INPUT_ARGS:
-               vb = fr_value_box_list_head(list);
-               arg = xlat_exp_head(node->call.args);
-
-               while (arg_p->type != FR_TYPE_NULL) {
-                       /*
-                        *      Separate check to see if the group
-                        *      box is there.  Check in
-                        *      xlat_process_arg_list verifies it
-                        *      has a value.
-                        */
-                       if (!vb) {
-                               if (arg_p->required) {
-                               missing:
-                                       REDEBUG("Function \"%s\" is missing required argument %u",
-                                                func->name, (unsigned int)((arg_p - func->args) + 1));
-                                       return XLAT_ACTION_FAIL;
-                               }
+       vb = fr_value_box_list_head(list);
+       arg = xlat_exp_head(node->call.args);
 
-                               /*
-                                *      The argument isn't required.  Just omit it.  xlat_func_args_set() enforces
-                                *      that optional arguments are at the end of the argument list.
-                                */
-                               return XLAT_ACTION_DONE;
+       while (arg_p->type != FR_TYPE_NULL) {
+               /*
+                *      Separate check to see if the group
+                *      box is there.  Check in
+                *      xlat_process_arg_list verifies it
+                *      has a value.
+                */
+               if (!vb) {
+                       if (arg_p->required) {
+                       missing:
+                               REDEBUG("Function \"%s\" is missing required argument %u",
+                                       func->name, (unsigned int)((arg_p - func->args) + 1));
+                               return XLAT_ACTION_FAIL;
                        }
 
                        /*
-                        *      Everything in the top level list should be
-                        *      groups
+                        *      The argument isn't required.  Just omit it.  xlat_func_args_set() enforces
+                        *      that optional arguments are at the end of the argument list.
                         */
-                       if (!fr_cond_assert(vb->type == FR_TYPE_GROUP)) return XLAT_ACTION_FAIL;
+                       return XLAT_ACTION_DONE;
+               }
 
-                       /*
-                        *      pre-advance, in case the vb is replaced
-                        *      during processing.
-                        */
-                       vb_next = fr_value_box_list_next(list, vb);
-                       arg_next = xlat_exp_next(node->call.args, arg);
+               /*
+                *      Everything in the top level list should be
+                *      groups
+                */
+               if (!fr_cond_assert(vb->type == FR_TYPE_GROUP)) return XLAT_ACTION_FAIL;
+
+               /*
+                *      pre-advance, in case the vb is replaced
+                *      during processing.
+                */
+               vb_next = fr_value_box_list_next(list, vb);
+               arg_next = xlat_exp_next(node->call.args, arg);
 
-                       xa = xlat_process_arg_list(ctx, &vb->vb_group, request, func->name, arg_p, arg,
-                                                  (unsigned int)((arg_p - func->args) + 1));
-                       if (xa != XLAT_ACTION_DONE) return xa;
+               xa = xlat_process_arg_list(ctx, &vb->vb_group, request, func->name, arg_p, arg,
+                                          (unsigned int)((arg_p - func->args) + 1));
+               if (xa != XLAT_ACTION_DONE) return xa;
 
+               /*
+                *      This argument doesn't exist.  That might be OK, or it may be a fatal error.
+                */
+               if (fr_value_box_list_empty(&vb->vb_group)) {
                        /*
-                        *      This argument doesn't exist.  That might be OK, or it may be a fatal error.
+                        *      Variadic rules deal with empty boxes differently...
                         */
-                       if (fr_value_box_list_empty(&vb->vb_group)) {
-                               /*
-                                *      Variadic rules deal with empty boxes differently...
-                                */
-                               switch (arg_p->variadic) {
-                               case XLAT_ARG_VARIADIC_EMPTY_SQUASH:
-                                       fr_value_box_list_talloc_free_head(list);
-                                       goto do_next;
+                       switch (arg_p->variadic) {
+                       case XLAT_ARG_VARIADIC_EMPTY_SQUASH:
+                               fr_value_box_list_talloc_free_head(list);
+                               goto do_next;
 
-                               case XLAT_ARG_VARIADIC_EMPTY_KEEP:
-                                       goto empty_ok;
+                       case XLAT_ARG_VARIADIC_EMPTY_KEEP:
+                               goto empty_ok;
 
-                               case XLAT_ARG_VARIADIC_DISABLED:
-                                       break;
-                               }
+                       case XLAT_ARG_VARIADIC_DISABLED:
+                               break;
+                       }
 
+                       /*
+                        *      Empty groups for optional arguments are OK, we can just stop processing the list.
+                        */
+                       if (!arg_p->required) {
                                /*
-                                *      Empty groups for optional arguments are OK, we can just stop processing the list.
+                                *      If the caller doesn't care about the type, then we leave the
+                                *      empty group there.
                                 */
-                               if (!arg_p->required) {
-                                       /*
-                                        *      If the caller doesn't care about the type, then we leave the
-                                        *      empty group there.
-                                        */
-                                       if (arg_p->type == FR_TYPE_VOID) goto do_next;
-
-                                       /*
-                                        *      The caller does care about the type, and we don't have any
-                                        *      matching data.  Omit this argument, and all arguments after it.
-                                        *
-                                        *      i.e. if the caller has 3 optional arguments, all
-                                        *      FR_TYPE_UINT8, and the first one is missing, then we MUST
-                                        *      either supply boxes all of FR_TYPE_UINT8, OR we supply nothing.
-                                        *
-                                        *      We can't supply a box of any other type, because the caller
-                                        *      has declared that it wants FR_TYPE_UINT8, and is naively
-                                        *      accessing the box as vb_uint8, hoping that it's being passed
-                                        *      the right thing.
-                                        */
-                                       fr_value_box_list_talloc_free_head(list);
-                                       break;
-                               }
+                               if (arg_p->type == FR_TYPE_VOID) goto do_next;
 
                                /*
-                                *      If the caller is expecting a particular type, then getting nothing is
-                                *      an error.
+                                *      The caller does care about the type, and we don't have any
+                                *      matching data.  Omit this argument, and all arguments after it.
+                                *
+                                *      i.e. if the caller has 3 optional arguments, all
+                                *      FR_TYPE_UINT8, and the first one is missing, then we MUST
+                                *      either supply boxes all of FR_TYPE_UINT8, OR we supply nothing.
                                 *
-                                *      If the caller manually checks the input type, then we can leave it as
-                                *      an empty group.
+                                *      We can't supply a box of any other type, because the caller
+                                *      has declared that it wants FR_TYPE_UINT8, and is naively
+                                *      accessing the box as vb_uint8, hoping that it's being passed
+                                *      the right thing.
                                 */
-                               if (arg_p->type != FR_TYPE_VOID) goto missing;
+                               fr_value_box_list_talloc_free_head(list);
+                               break;
                        }
 
-               empty_ok:
                        /*
-                        *      In some cases we replace the current argument with the head of the group.
+                        *      If the caller is expecting a particular type, then getting nothing is
+                        *      an error.
                         *
-                        *      xlat_process_arg_list() has already done concatenations for us.
+                        *      If the caller manually checks the input type, then we can leave it as
+                        *      an empty group.
                         */
-                       if (arg_p->single || arg_p->concat) {
-                               fr_value_box_t *head = fr_value_box_list_pop_head(&vb->vb_group);
+                       if (arg_p->type != FR_TYPE_VOID) goto missing;
+               }
 
-                               /*
-                                *      If we're meant to be smashing the argument
-                                *      to a single box, but the group was empty,
-                                *      add a null box instead so ordering is maintained
-                                *      for subsequent boxes.
-                                */
-                               if (!head) head = fr_value_box_alloc_null(ctx);
-                               fr_value_box_list_replace(list, vb, head);
-                               talloc_free(vb);
-                       }
+       empty_ok:
+               /*
+                *      In some cases we replace the current argument with the head of the group.
+                *
+                *      xlat_process_arg_list() has already done concatenations for us.
+                */
+               if (arg_p->single || arg_p->concat) {
+                       fr_value_box_t *head = fr_value_box_list_pop_head(&vb->vb_group);
 
-               do_next:
-                       if (arg_p->variadic) {
-                               if (!vb_next) break;
-                       } else {
-                               arg_p++;
-                               arg = arg_next;
-                       }
-                       vb = vb_next;
+                       /*
+                        *      If we're meant to be smashing the argument
+                        *      to a single box, but the group was empty,
+                        *      add a null box instead so ordering is maintained
+                        *      for subsequent boxes.
+                        */
+                       if (!head) head = fr_value_box_alloc_null(ctx);
+                       fr_value_box_list_replace(list, vb, head);
+                       talloc_free(vb);
                }
-               break;
+
+       do_next:
+               if (arg_p->variadic) {
+                       if (!vb_next) break;
+               } else {
+                       arg_p++;
+                       arg = arg_next;
+               }
+               vb = vb_next;
        }
 
        return XLAT_ACTION_DONE;
index b9ca7220427f6c32c15ec1335507a4c5f8b1a7cd..3ca212e5616a4c65221d0f549938222b6cc8f7cd 100644 (file)
@@ -81,6 +81,11 @@ static void xlat_func_append_arg(xlat_exp_t *head, xlat_exp_t *node, bool exists
                node = xlat_exists_alloc(head, node);
        }
 
+       if (!head->call.args) {
+               MEM(head->call.args = xlat_exp_head_alloc(head));
+               head->call.args->is_argv = true;
+       }
+
        /*
         *      Wrap it in a group.
         */
index 4ce620fd72d54fafe0e94993d07a86eb0e9683ab..92217b09cbc92ec66214888d240afe6afc7677f2 100644 (file)
@@ -267,7 +267,6 @@ xlat_t *xlat_func_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func,
                .name = talloc_typed_strdup(c, name),
                .func = func,
                .return_type = return_type,
-               .input_type = XLAT_INPUT_UNPROCESSED    /* set default - will be overridden if args are registered */
        };
 
        /*
@@ -379,7 +378,6 @@ int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
                }
        }
        x->args = args;
-       x->input_type = XLAT_INPUT_ARGS;
 
        return 0;
 }
index 5ecfb96b582bac9f447236eef2e66b688594c36e..1db97fb52539ec5d5173bd6ac3acfbc00bf3fdc0 100644 (file)
@@ -89,7 +89,6 @@ typedef struct xlat_s {
 
        xlat_flags_t            flags;                  //!< various flags
 
-       xlat_input_type_t       input_type;             //!< Type of input used.
        xlat_arg_parser_t const *args;                  //!< Definition of args consumed.
 
        call_env_method_t const *call_env_method;       //!< Optional tmpl expansions performed before calling the
@@ -138,8 +137,6 @@ typedef struct {
 
        bool                    ephemeral;              //!< Instance data is ephemeral (not inserted)
                                                        ///< into the instance tree.
-       xlat_input_type_t       input_type;             //!< The input type used inferred from the
-                                                       ///< bracketing style.
 } xlat_call_t;
 
 /** An xlat expansion node
index 16425de27b49c2d8ccaf3c90bb7b5852fcac0893..4c3352546970f8b63915f5c9969ac99bbb2b84ed 100644 (file)
@@ -208,22 +208,40 @@ static xlat_exp_t *xlat_exp_func_alloc(TALLOC_CTX *ctx, xlat_t const *func, xlat
 
        MEM(node = xlat_exp_alloc(ctx, XLAT_FUNC, func->name, strlen(func->name)));
        node->call.func = func;
-       if (unlikely(xlat_copy(node, node->call.args, args) < 0)) {
-               talloc_free(node);
-               return NULL;
-       }
+
        node->flags = func->flags;
        node->flags.impure_func = !func->flags.pure;
-       xlat_flags_merge(&node->flags, &args->flags);
 
-       if (func->input_type == XLAT_INPUT_ARGS) {
-               xlat_arg_parser_t const *arg_p;
-               xlat_exp_t              *arg = xlat_exp_head(node->call.args);
+       if (args) {
+               xlat_flags_merge(&node->flags, &args->flags);
 
                /*
-                *      The original tokenizing is done using the redundant xlat argument parser
-                *      so the boxes haven't been marked up with the appropriate "safe for".
+                *      If the function is pure, AND it's arguments are pure,
+                *      then remember that we need to call a pure function.
                 */
+               node->flags.can_purify = (func->flags.pure && args->flags.pure) | args->flags.can_purify;
+
+               MEM(node->call.args = xlat_exp_head_alloc(node));
+               node->call.args->is_argv = true;
+
+               if (unlikely(xlat_copy(node, node->call.args, args) < 0)) {
+                       talloc_free(node);
+                       return NULL;
+               }
+       }
+
+       /*
+        *      The original tokenizing is done using the redundant xlat argument parser so the boxes need to
+        *      have their "safe_for" value changed to the new one.
+        */
+       if (func->args) {
+               xlat_arg_parser_t const *arg_p;
+               xlat_exp_t              *arg;
+
+               fr_assert(args);
+
+               arg = xlat_exp_head(node->call.args);
+
                for (arg_p = node->call.func->args; arg_p->type != FR_TYPE_NULL; arg_p++) {
                        if (!arg) break;
 
@@ -235,12 +253,6 @@ static xlat_exp_t *xlat_exp_func_alloc(TALLOC_CTX *ctx, xlat_t const *func, xlat
                }
        }
 
-       /*
-        *      If the function is pure, AND it's arguments are pure,
-        *      then remember that we need to call a pure function.
-        */
-       node->flags.can_purify = (func->flags.pure && args->flags.pure) | args->flags.can_purify;
-
        return node;
 }
 
@@ -273,7 +285,8 @@ static int xlat_redundant_instantiate(xlat_inst_ctx_t const *xctx)
                 *      becomes an error when the user tries
                 *      to use the redundant xlat.
                 */
-               if (first->func->input_type != xrf->func->input_type) {
+               if ((!first->func->args && xrf->func->args) ||
+                   (first->func->args && !xrf->func->args)) {
                        cf_log_err(xr->cs, "Expansion functions \"%s\" and \"%s\" use different argument styles "
                                   "cannot be used in the same redundant section", first->func->name, xrf->func->name);
                error:
@@ -291,17 +304,10 @@ static int xlat_redundant_instantiate(xlat_inst_ctx_t const *xctx)
                MEM(node = xlat_exp_func_alloc(head, xrf->func, xctx->ex->call.args));
                xlat_exp_insert_tail(head, node);
 
-               switch (xrf->func->input_type) {
-               case XLAT_INPUT_UNPROCESSED:
-                       break;
-
-               case XLAT_INPUT_ARGS:
-                       if (xlat_validate_function_args(node) < 0) {
-                               PERROR("Invalid arguments for redundant expansion function \"%s\"",
-                                      xrf->func->name);
-                               goto error;
-                       }
-                       break;
+               if (xlat_validate_function_args(node) < 0) {
+                       PERROR("Invalid arguments for redundant expansion function \"%s\"",
+                              xrf->func->name);
+                       goto error;
                }
 
                /*
index 0ee41067758e9d00cb15bff3790b68805fc910bf..6b694d3ba6d7513c4c8220f316ea36b4ffc59e39 100644 (file)
@@ -282,7 +282,7 @@ static int xlat_validate_function_arg(xlat_arg_parser_t const *arg_p, xlat_exp_t
        return 0;
 }
 
-fr_slen_t xlat_validate_function_args(xlat_exp_t *node)
+int xlat_validate_function_args(xlat_exp_t *node)
 {
        xlat_arg_parser_t const *arg_p;
        xlat_exp_t              *arg = xlat_exp_head(node->call.args);
@@ -290,6 +290,29 @@ fr_slen_t xlat_validate_function_args(xlat_exp_t *node)
 
        fr_assert(node->type == XLAT_FUNC);
 
+       /*
+        *      Check the function definition against what the user passed in.
+        */
+       if (!node->call.func->args) {
+               if (node->call.args) {
+                       fr_strerror_const("Too many arguments to function call, expected 0");
+                       return -1;
+               }
+
+               /*
+                *      Function takes no arguments, and none were passed in.  There's nothing to verify.
+                */
+               return 0;
+       }
+
+       if (!node->call.args) {
+               fr_strerror_const("Too few arguments to function call");
+               return -1;
+       }
+
+       /*
+        *      The function both has arguments defined, and the user has supplied them.
+        */
        for (arg_p = node->call.func->args, i = 0; arg_p->type != FR_TYPE_NULL; arg_p++) {
                if (!arg_p->required) break;
 
@@ -434,7 +457,6 @@ static CC_HINT(nonnull) int xlat_tokenize_function_args(xlat_exp_head_t *head, f
                node->call.dict = t_rules->attr.dict_def;
                node->flags = func->flags;
                node->flags.impure_func = !func->flags.pure;
-               node->call.input_type = func->input_type;
        }
 
        fr_sbuff_marker_release(&m_s);
@@ -461,8 +483,10 @@ static CC_HINT(nonnull) int xlat_tokenize_function_args(xlat_exp_head_t *head, f
                talloc_free(node);
                return -1;
        }
+       fr_assert(node->call.args != NULL);
 
        xlat_flags_merge(&node->flags, &node->call.args->flags);
+       node->call.args->is_argv = true;
 
        if (!fr_sbuff_next_if_char(in, ')')) {
                fr_strerror_const("Missing closing brace ')'");
@@ -470,20 +494,13 @@ static CC_HINT(nonnull) int xlat_tokenize_function_args(xlat_exp_head_t *head, f
        }
 
        /*
-        *      Validate the arguments.
+        *      Set the flags.
         */
-       if (node->type == XLAT_FUNC) {
-               switch (node->call.input_type) {
-               case XLAT_INPUT_UNPROCESSED:
-                       break;
-
-               case XLAT_INPUT_ARGS:
-                       /*
-                        *      We might not be able to purify the function call, but perhaps we can purify the arguments to it.
-                        */
-                       node->flags.can_purify = (node->call.func->flags.pure && node->call.args->flags.pure) | node->call.args->flags.can_purify;
-                       break;
-               }
+       if ((node->type == XLAT_FUNC) && node->call.args) {
+               /*
+                *      We might not be able to purify the function call, but perhaps we can purify the arguments to it.
+                */
+               node->flags.can_purify = (node->call.func->flags.pure && node->call.args->flags.pure) | node->call.args->flags.can_purify;
        }
 
        xlat_exp_insert_tail(head, node);
@@ -1612,7 +1629,6 @@ fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t
        }
 
        MEM(head = xlat_exp_head_alloc(ctx));
-       head->is_argv = true;
 
        /*
         *      skip spaces at the beginning as we don't want them to become a whitespace literal.
@@ -1973,22 +1989,9 @@ int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules)
                        node->call.dict = xr_rules->tr_rules->dict_def;
 
                        /*
-                        *      Check input arguments of our freshly
-                        *      resolved function
+                        *      Check input arguments of our freshly resolved function
                         */
-                       switch (node->call.func->input_type) {
-                       case XLAT_INPUT_UNPROCESSED:
-                               break;
-
-                       case XLAT_INPUT_ARGS:
-                               if (node->call.input_type != XLAT_INPUT_ARGS) {
-                                       fr_strerror_const("Function takes defined arguments and should "
-                                                         "be called using %func(args) syntax");
-                                       return -1;
-                               }
-                               if (xlat_validate_function_args(node) < 0) return -1;
-                               break;
-                       }
+                       if (xlat_validate_function_args(node) < 0) return -1;
 
                        /*
                         *      Add the freshly resolved function