}
#endif
+static ssize_t xlat_tokenize_word(TALLOC_CTX *ctx, xlat_exp_t **out, fr_sbuff_t *in, fr_sbuff_marker_t *m,
+ fr_token_t quote,
+ fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
+{
+ ssize_t slen;
+ fr_sbuff_t our_in = FR_SBUFF(in);
+ xlat_exp_t *node;
+
+ MEM(node = xlat_exp_alloc(ctx, XLAT_GROUP, NULL, 0));
+ node->quote = quote;
+
+ switch (quote) {
+ case T_BARE_WORD:
+ XLAT_DEBUG("ARGV bare word <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
+
+ /*
+ * @todo - do %{3} for regex.
+ *
+ * @todo - peek ahead for attribute references.
+ *
+ * @todo - peek ahead for values, if the input is alphanumeric + ".:" with no spaces,
+ * it's likely a value, and we can just tokenize the tmpl. And then hoist the value-box.
+ */
+
+ slen = xlat_tokenize_expression(node, &node->group, &our_in, p_rules, t_rules);
+ if (slen < 0) {
+ error:
+ talloc_free(node);
+ FR_SBUFF_ERROR_RETURN(&our_in);
+ }
+ break;
+
+ /*
+ * "Double quoted strings may contain %{expansions}"
+ */
+ case T_DOUBLE_QUOTED_STRING:
+ XLAT_DEBUG("ARGV double quotes <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
+
+ if (xlat_tokenize_input(node->group, &our_in,
+ &value_parse_rules_double_quoted, t_rules) < 0) goto error;
+ break;
+
+ /*
+ * 'Single quoted strings get parsed as literal strings'
+ */
+ case T_SINGLE_QUOTED_STRING:
+ {
+ char *str;
+ xlat_exp_t *child;
+
+ XLAT_DEBUG("ARGV single quotes <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
+
+ child = xlat_exp_alloc(node->group, XLAT_BOX, NULL, 0);
+ slen = fr_sbuff_out_aunescape_until(child, &str, &our_in, SIZE_MAX,
+ value_parse_rules_single_quoted.terminals,
+ value_parse_rules_single_quoted.escapes);
+ if (slen < 0) goto error;
+
+ xlat_exp_set_name_shallow(child, str);
+ fr_value_box_strdup(child, &child->data, NULL, str, false);
+ fr_value_box_mark_safe_for(&child->data, t_rules->literals_safe_for); /* Literal values are treated as implicitly safe */
+ child->flags.constant = true;
+ fr_assert(child->flags.pure);
+ xlat_exp_insert_tail(node->group, child);
+ }
+ break;
+
+ /*
+ * `back quoted strings aren't supported`
+ */
+ case T_BACK_QUOTED_STRING:
+ fr_strerror_const("Unexpected `...` string");
+ goto error;
+
+ default:
+ fr_assert(0);
+ goto error;
+ }
+
+ if ((quote != T_BARE_WORD) && !fr_sbuff_next_if_char(&our_in, fr_token_quote[quote])) { /* Quoting */
+ fr_strerror_const("Unterminated string");
+ fr_sbuff_set(&our_in, m);
+ FR_SBUFF_ERROR_RETURN(&our_in);
+ }
+
+ node->flags = node->group->flags;
+ *out = node;
+
+ FR_SBUFF_SET_RETURN(in, &our_in);
+}
/** Tokenize an xlat expansion into a series of XLAT_TYPE_CHILD arguments
*
*/
if (!spaces) fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL);
- /*
- * Alloc a new node to hold the child nodes
- * that make up the argument.
- */
- MEM(node = xlat_exp_alloc(head, XLAT_GROUP, NULL, 0));
-
if (!spaces && !xlat_func_bare_words) {
quote = T_BARE_WORD;
- node->quote = quote;
- goto tokenize_expression;
+ } else {
+ fr_sbuff_out_by_longest_prefix(&slen, "e, xlat_quote_table, &our_in, T_BARE_WORD);
}
- fr_sbuff_out_by_longest_prefix(&slen, "e, xlat_quote_table, &our_in, T_BARE_WORD);
- node->quote = quote;
-
- switch (quote) {
- /*
- * Barewords --may-contain=%{expansions}
- */
- case T_BARE_WORD:
- XLAT_DEBUG("ARGV bare word <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
+ if ((quote == T_BARE_WORD) && (spaces || xlat_func_bare_words)) {
+ MEM(node = xlat_exp_alloc(ctx, XLAT_GROUP, NULL, 0));
+ node->quote = quote;
/*
- * &User-Name is an attribute reference
+ * Spaces - each argument is a bare word all by itself, OR an xlat thing all by itself.
*
- * @todo - move '&' to be a _dcursor_. and not an attribute reference.
- *
- * @todo - Perhaps &"foo" can dynamically create the string, and then pass it to
- * the the tmpl tokenizer, and then pass the tmpl to the function. Which also
- * means that we need to be able to have a fr_value_box_t which holds a ptr to a
- * tmpl. And update the function arguments to say "we want a tmpl, not a
- * string".
+ * No spaces - each arugment is an expression, which can have embedded spaces.
*/
- if (spaces || xlat_func_bare_words) {
- /*
- * Spaces - each argument is a bare word all by itself, OR an xlat thing all by itself.
- *
- * No spaces - each arugment is an expression, which can have embedded spaces.
- */
- slen = xlat_tokenize_input(node->group, &our_in, our_p_rules, &arg_t_rules);
+ slen = xlat_tokenize_input(node->group, &our_in, our_p_rules, &arg_t_rules);
- } else {
- tokenize_expression:
- if (fr_sbuff_is_char(&our_in, ')')) {
- /*
- * %foo()
- */
- slen = 0;
-
- } else {
- slen = xlat_tokenize_expression(node, &node->group, &our_in, our_p_rules, &arg_t_rules);
- }
- }
- if (slen < 0) {
- error:
- if (our_p_rules == &tmp_p_rules) talloc_const_free(our_p_rules->terminals);
- talloc_free(head);
-
- FR_SBUFF_ERROR_RETURN(&our_in); /* error */
- }
+ } else if ((quote == T_BARE_WORD) && fr_sbuff_is_char(&our_in, ')')) {
+ MEM(node = xlat_exp_alloc(ctx, XLAT_GROUP, NULL, 0));
+ node->quote = quote;
/*
- * No data, but the argument was required. Complain.
+ * We've reached the end of the arguments, don't try to tokenize anything else.
*/
- if (!slen && arg->required) {
- fr_strerror_printf("Missing required arg %u", argc);
- goto error;
- }
+ slen = 0;
- /*
- * Validate the argument immediately on parsing it, and not later.
- */
- if (arg->type == FR_TYPE_NULL) {
- fr_strerror_printf("Too many arguments, expected %zu, got %d",
- (size_t) (arg - arg_start), argc);
- goto error;
- }
+ } else {
- /*
- * Ensure that the function args are correct.
- */
- if (xlat_validate_function_arg(arg, node, argc) < 0) {
- fr_sbuff_set(&our_in, &m);
- goto error;
- }
- break;
+ slen = xlat_tokenize_word(head, &node, &our_in, &m, quote, our_p_rules, &arg_t_rules);
+ }
- /*
- * "Double quoted strings may contain %{expansions}"
- */
- case T_DOUBLE_QUOTED_STRING:
- XLAT_DEBUG("ARGV double quotes <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
+ if (slen < 0) {
+ error:
+ if (our_p_rules == &tmp_p_rules) talloc_const_free(our_p_rules->terminals);
+ talloc_free(head);
- if (xlat_tokenize_input(node->group, &our_in,
- &value_parse_rules_double_quoted, &arg_t_rules) < 0) goto error;
- break;
+ FR_SBUFF_ERROR_RETURN(&our_in); /* error */
+ }
/*
- * 'Single quoted strings get parsed as literal strings'
+ * No data, but the argument was required. Complain.
*/
- case T_SINGLE_QUOTED_STRING:
- {
- char *str;
- xlat_exp_t *child;
-
- XLAT_DEBUG("ARGV single quotes <-- %.*s", (int) fr_sbuff_remaining(&our_in), fr_sbuff_current(&our_in));
-
- child = xlat_exp_alloc(node->group, XLAT_BOX, NULL, 0);
- slen = fr_sbuff_out_aunescape_until(child, &str, &our_in, SIZE_MAX,
- value_parse_rules_single_quoted.terminals,
- value_parse_rules_single_quoted.escapes);
- if (slen < 0) goto error;
-
- xlat_exp_set_name_shallow(child, str);
- fr_value_box_strdup(child, &child->data, NULL, str, false);
- fr_value_box_mark_safe_for(&child->data, arg->safe_for); /* Literal values are treated as implicitly safe */
- child->flags.constant = true;
- fr_assert(child->flags.pure);
- xlat_exp_insert_tail(node->group, child);
+ if (!slen && arg->required) {
+ fr_strerror_printf("Missing required arg %u", argc);
+ goto error;
}
- break;
/*
- * `back quoted strings aren't supported`
+ * Validate the argument immediately on parsing it, and not later.
*/
- case T_BACK_QUOTED_STRING:
- fr_strerror_const("Unexpected `...` string");
+ if (arg->type == FR_TYPE_NULL) {
+ fr_strerror_printf("Too many arguments, expected %zu, got %d",
+ (size_t) (arg - arg_start), argc);
goto error;
-
- default:
- fr_assert(0);
- break;
}
- if ((quote != T_BARE_WORD) && !fr_sbuff_next_if_char(&our_in, fr_token_quote[quote])) { /* Quoting */
- fr_strerror_const("Unterminated string");
+ /*
+ * Ensure that the function args are correct.
+ */
+ if (xlat_validate_function_arg(arg, node, argc) < 0) {
fr_sbuff_set(&our_in, &m);
goto error;
}
xlat_exp_set_name(node, fr_sbuff_current(&m), fr_sbuff_behind(&m));
- /*
- * Assert that the parser has created things which are safe for the current argument.
- *
- * @todo - function should be marked up with safe_for, and not each individual argument.
- */
-// xlat_safe_for(node->group, arg->safe_for);
-
- node->flags = node->group->flags;
-
xlat_exp_insert_tail(head, node);
/*