.br
Loops over a named variable, running the block for each copy of the
named variable. The return value of the block is the return value of
-the last statement executed. There is currently no way to exit the
-loop early.
+the last statement executed. The loop can be exited early by using
+the "break" keyword. Unlike other languages, "break" here means "exit
+the loop at the next iteration", not "exit the loop now". The result
+is that any statements after the "break" keyword will still be
+executed. We recommend using "break" only when it is the last
+statement in a "foreach" block.
The attribute name is just the name, e.g. reply:Reply-Message, with
none of the usual variable referenced %{...}. This is because it is a
enum { MOD_SINGLE = 1, MOD_GROUP, MOD_LOAD_BALANCE, MOD_REDUNDANT_LOAD_BALANCE,
#ifdef WITH_UNLANG
MOD_IF, MOD_ELSE, MOD_ELSIF, MOD_UPDATE, MOD_SWITCH, MOD_CASE,
- MOD_FOREACH,
+ MOD_FOREACH, MOD_BREAK,
#endif
MOD_POLICY, MOD_REFERENCE, MOD_XLAT } type;
int method;
"switch",
"case",
"foreach",
+ "break",
#endif
"policy",
"reference",
goto handle_result;
}
+ if (child->type == MOD_BREAK) {
+ int i;
+ VALUE_PAIR **copy_p;
+
+ for (i = 8; i >= 0; i--) {
+ copy_p = request_data_get(request,
+ radius_get_vp, i);
+ if (copy_p) {
+ RDEBUG2("%.*s # BREAK Foreach-Variable-%d", stack.pointer + 1, modcall_spaces, i);
+ pairfree(copy_p);
+ break;
+ }
+ }
+
+ myresult = RLM_MODULE_NOOP;
+ goto handle_result;
+ }
+
if (child->type == MOD_FOREACH) {
int i, depth = -1;
VALUE_PAIR *vp;
if (0) {
handle_result:
- RDEBUG2("%.*s} # %s %s = %s",
- stack.pointer + 1, modcall_spaces,
- group_name[child->type], child->name ? child->name : "",
- fr_int2str(rcode_table, myresult, "??"));
+ if (child->type != MOD_BREAK) {
+ RDEBUG2("%.*s} # %s %s = %s",
+ stack.pointer + 1, modcall_spaces,
+ group_name[child->type], child->name ? child->name : "",
+ fr_int2str(rcode_table, myresult, "??"));
+ }
}
/*
csingle->type = MOD_FOREACH;
return csingle;
}
+
+static modcallable *do_compile_modbreak(modcallable *parent, int component)
+{
+ modcallable *csingle;
+
+ component = component; /* -Wunused */
+
+
+ csingle= do_compile_modgroup(parent, component, NULL,
+ GROUPTYPE_SIMPLE, GROUPTYPE_SIMPLE);
+ if (!csingle) return NULL;
+ csingle->name = "";
+ csingle->type = MOD_BREAK;
+ return csingle;
+}
#endif
static modcallable *do_compile_modserver(modcallable *parent,
}
}
+ if (strcmp(modrefname, "break") == 0) {
+ return do_compile_modbreak(parent, component);
+ }
+
/*
* Not a virtual module. It must be a real module.
*/
g = rad_malloc(sizeof(*g));
memset(g, 0, sizeof(*g));
g->grouptype = grouptype;
+ g->children = NULL;
c = mod_grouptocallable(g);
c->parent = parent;
c->next = NULL;
memset(c->actions, 0, sizeof(c->actions));
+ if (!cs) { /* only for "break" */
+ c->name = "";
+ goto set_codes;
+ }
+
/*
* Remember the name for printing, etc.
*
c->type = MOD_POLICY;
}
}
- g->children = NULL;
/*
* Loop over the children of this group.
}
}
+set_codes:
/*
* Set the default actions, if they haven't already been
* set.