From: Alan T. DeKok Date: Thu, 29 Aug 2024 19:02:01 +0000 (-0400) Subject: parse new syntax for "foreach" X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b8299eade29ac1da0831296389303c9b96888608;p=thirdparty%2Ffreeradius-server.git parse new syntax for "foreach" nothing is compiled or intepreted as yet, but we're a step ahead --- diff --git a/src/lib/server/cf_file.c b/src/lib/server/cf_file.c index 473bfc09f4a..2178d663b0e 100644 --- a/src/lib/server/cf_file.c +++ b/src/lib/server/cf_file.c @@ -1979,6 +1979,172 @@ static CONF_ITEM *process_catch(cf_stack_t *stack) return cf_section_to_item(css); } +static int parse_error(cf_stack_t *stack, char const *ptr, char const *message) +{ + char *spaces, *text; + cf_stack_frame_t *frame = &stack->frame[stack->depth]; + + if (!ptr) ptr = stack->ptr; + + /* + * We must pass a _negative_ offset to this function. + */ + fr_canonicalize_error(NULL, &spaces, &text, stack->ptr - ptr, stack->ptr); + + ERROR("%s[%d]: %s", frame->filename, frame->lineno, text); + ERROR("%s[%d]: %s^ - %s", frame->filename, frame->lineno, spaces, message); + + talloc_free(spaces); + talloc_free(text); + return -1; +} + +static CONF_ITEM *process_foreach(cf_stack_t *stack) +{ + fr_token_t token; + fr_type_t type; + CONF_SECTION *css; + char const *ptr = stack->ptr, *ptr2; + cf_stack_frame_t *frame = &stack->frame[stack->depth]; + CONF_SECTION *parent = frame->current; + + /* + * Get the first argument to "foreach". For backwards + * compatibility, it could be an attribute reference. + */ + ptr2 = ptr; + if (cf_get_token(parent, &ptr, &token, stack->buff[1], stack->bufsize, + frame->filename, frame->lineno) < 0) { + return NULL; + } + + if (token != T_BARE_WORD) { + (void) parse_error(stack, ptr2, "Unexpected argument to 'foreach'"); + return NULL; + } + + fr_skip_whitespace(ptr); + + if (*ptr == '{') { + css = cf_section_alloc(parent, parent, "foreach", stack->buff[1]); + if (!css) { + ERROR("%s[%d]: Failed allocating memory for section", + frame->filename, frame->lineno); + return NULL; + } + + cf_filename_set(css, frame->filename); + cf_lineno_set(css, frame->lineno); + css->name2_quote = T_BARE_WORD; + css->unlang = CF_UNLANG_ALLOW; + css->allow_locals = true; + + ptr++; + stack->ptr = ptr; + + return cf_section_to_item(css); + } + + if (strcmp(stack->buff[1], "auto") == 0) { + type = FR_TYPE_VOID; + + } else { + type = fr_table_value_by_str(fr_type_table, stack->buff[1], FR_TYPE_NULL); + switch (type) { + default: + break; + + case FR_TYPE_NULL: + case FR_TYPE_VOID: + case FR_TYPE_VALUE_BOX: + case FR_TYPE_MAX: + (void) parse_error(stack, ptr2, "Unknown or invalid variable type in 'foreach'"); + return NULL; + } + } + + fr_skip_whitespace(ptr); + ptr2 = ptr; + + /* + * Parse the variable name. @todo - allow '-' in names. + */ + token = gettoken(&ptr, stack->buff[2], stack->bufsize, false); + if (token != T_BARE_WORD) { + (void) parse_error(stack, ptr2, "Invalid variable name for key in 'foreach'"); + return NULL; + } + fr_skip_whitespace(ptr); + + /* + * The thing to loop over must now be in an expression block. + */ + if (*ptr != '(') { + (void) parse_error(stack, ptr, "Expected (...) after 'foreach' variable definition"); + return NULL; + } + + /* + * "(" whitespace EXPRESSION whitespace ")" + */ + ptr++; + fr_skip_whitespace(ptr); + + if (cf_get_token(parent, &ptr, &token, stack->buff[1], stack->bufsize, + frame->filename, frame->lineno) < 0) { + return NULL; + } + + if (token != T_BARE_WORD) { + (void) parse_error(stack, ptr2, "Invalid thingy in 'foreach'"); + return NULL; + } + + fr_skip_whitespace(ptr); + if (*ptr != ')') { + (void) parse_error(stack, ptr, "Missing ')' in 'foreach'"); + return NULL; + } + ptr++; + fr_skip_whitespace(ptr); + + if (*ptr != '{') { + (void) parse_error(stack, ptr, "Expected '{' in 'foreach'"); + return NULL; + } + + css = cf_section_alloc(parent, parent, "foreach", stack->buff[1]); + if (!css) { + ERROR("%s[%d]: Failed allocating memory for section", + frame->filename, frame->lineno); + return NULL; + } + + cf_filename_set(css, frame->filename); + cf_lineno_set(css, frame->lineno); + css->name2_quote = T_BARE_WORD; + css->unlang = CF_UNLANG_ALLOW; + css->allow_locals = true; + + /* + * Add in the extra arguments + */ + css->argc = 2; + css->argv = talloc_array(css, char const *, css->argc); + css->argv_quote = talloc_array(css, fr_token_t, css->argc); + + css->argv[0] = fr_type_to_str(type); + css->argv_quote[0] = T_BARE_WORD; + + css->argv[1] = talloc_typed_strdup(css->argv, stack->buff[2]); + css->argv_quote[1] = T_BARE_WORD; + + ptr++; + stack->ptr = ptr; + + return cf_section_to_item(css); +} + static int add_pair(CONF_SECTION *parent, char const *attr, char const *value, fr_token_t name1_token, fr_token_t op_token, fr_token_t value_token, @@ -2038,6 +2204,7 @@ static int add_pair(CONF_SECTION *parent, char const *attr, char const *value, static fr_table_ptr_sorted_t unlang_keywords[] = { { L("catch"), (void *) process_catch }, { L("elsif"), (void *) process_if }, + { L("foreach"), (void *) process_foreach }, { L("if"), (void *) process_if }, { L("map"), (void *) process_map }, { L("subrequest"), (void *) process_subrequest } @@ -2046,26 +2213,6 @@ static int unlang_keywords_len = NUM_ELEMENTS(unlang_keywords); typedef CONF_ITEM *(*cf_process_func_t)(cf_stack_t *); -static int parse_error(cf_stack_t *stack, char const *ptr, char const *message) -{ - char *spaces, *text; - cf_stack_frame_t *frame = &stack->frame[stack->depth]; - - if (!ptr) ptr = stack->ptr; - - /* - * We must pass a _negative_ offset to this function. - */ - fr_canonicalize_error(NULL, &spaces, &text, stack->ptr - ptr, stack->ptr); - - ERROR("%s[%d]: %s", frame->filename, frame->lineno, text); - ERROR("%s[%d]: %s^ - %s", frame->filename, frame->lineno, spaces, message); - - talloc_free(spaces); - talloc_free(text); - return -1; -} - static int parse_input(cf_stack_t *stack) { fr_token_t name1_token, name2_token, value_token, op_token; @@ -2182,8 +2329,9 @@ static int parse_input(cf_stack_t *stack) stack->ptr = ptr; ci = process(stack); - ptr = stack->ptr; if (!ci) return -1; + + ptr = stack->ptr; if (cf_item_is_section(ci)) { parent->allow_locals = false; css = cf_item_to_section(ci); diff --git a/src/tests/keywords/foreach-variable b/src/tests/keywords/foreach-variable new file mode 100644 index 00000000000..f21d2d2345a --- /dev/null +++ b/src/tests/keywords/foreach-variable @@ -0,0 +1,10 @@ +&request -= &Packet-Type + +# +# New syntax +# +foreach string thing ( &Filter-Id ) { + &reply += { + &Called-Station-Id = "%{Foreach-Variable-0}" + } +} diff --git a/src/tests/keywords/foreach-variable.attrs b/src/tests/keywords/foreach-variable.attrs new file mode 120000 index 00000000000..37573fb44f7 --- /dev/null +++ b/src/tests/keywords/foreach-variable.attrs @@ -0,0 +1 @@ +foreach.attrs \ No newline at end of file