]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
parse new syntax for "foreach"
authorAlan T. DeKok <aland@freeradius.org>
Thu, 29 Aug 2024 19:02:01 +0000 (15:02 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 30 Aug 2024 14:44:46 +0000 (10:44 -0400)
nothing is compiled or intepreted as yet, but we're a step ahead

src/lib/server/cf_file.c
src/tests/keywords/foreach-variable [new file with mode: 0644]
src/tests/keywords/foreach-variable.attrs [new symlink]

index 473bfc09f4a4729bd1d86f0dc07a085ce849fa96..2178d663b0ea96b52a40fbe850a168b465d539f4 100644 (file)
@@ -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 (file)
index 0000000..f21d2d2
--- /dev/null
@@ -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 (symlink)
index 0000000..37573fb
--- /dev/null
@@ -0,0 +1 @@
+foreach.attrs
\ No newline at end of file