From: Alan T. DeKok Date: Sat, 12 Apr 2025 17:30:31 +0000 (-0400) Subject: there's no need for INPUT_ARGS X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aa7ddd990c3283f0a5285a2627d7b099bf9c395a;p=thirdparty%2Ffreeradius-server.git there's no need for INPUT_ARGS 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 --- diff --git a/src/lib/unlang/xlat.h b/src/lib/unlang/xlat.h index 216f67c8af5..ab1fb0f7a26 100644 --- a/src/lib/unlang/xlat.h +++ b/src/lib/unlang/xlat.h @@ -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); diff --git a/src/lib/unlang/xlat_alloc.c b/src/lib/unlang/xlat_alloc.c index 2f8c17567e0..635e55de5a1 100644 --- a/src/lib/unlang/xlat_alloc.c +++ b/src/lib/unlang/xlat_alloc.c @@ -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); diff --git a/src/lib/unlang/xlat_eval.c b/src/lib/unlang/xlat_eval.c index a8b35707249..f991fad364f 100644 --- a/src/lib/unlang/xlat_eval.c +++ b/src/lib/unlang/xlat_eval.c @@ -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; diff --git a/src/lib/unlang/xlat_expr.c b/src/lib/unlang/xlat_expr.c index b9ca7220427..3ca212e5616 100644 --- a/src/lib/unlang/xlat_expr.c +++ b/src/lib/unlang/xlat_expr.c @@ -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. */ diff --git a/src/lib/unlang/xlat_func.c b/src/lib/unlang/xlat_func.c index 4ce620fd72d..92217b09cbc 100644 --- a/src/lib/unlang/xlat_func.c +++ b/src/lib/unlang/xlat_func.c @@ -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; } diff --git a/src/lib/unlang/xlat_priv.h b/src/lib/unlang/xlat_priv.h index 5ecfb96b582..1db97fb5253 100644 --- a/src/lib/unlang/xlat_priv.h +++ b/src/lib/unlang/xlat_priv.h @@ -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 diff --git a/src/lib/unlang/xlat_redundant.c b/src/lib/unlang/xlat_redundant.c index 16425de27b4..4c335254697 100644 --- a/src/lib/unlang/xlat_redundant.c +++ b/src/lib/unlang/xlat_redundant.c @@ -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; } /* diff --git a/src/lib/unlang/xlat_tokenize.c b/src/lib/unlang/xlat_tokenize.c index 0ee41067758..6b694d3ba6d 100644 --- a/src/lib/unlang/xlat_tokenize.c +++ b/src/lib/unlang/xlat_tokenize.c @@ -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