.RE
.sp
..
-.TH unlang 5 "19 May 2010" "" "FreeRADIUS Processing un-language"
+.TH unlang 5 "27 May 2011" "" "FreeRADIUS Processing un-language"
.SH NAME
unlang \- FreeRADIUS Processing un\-language
.SH DESCRIPTION
.br
}
.DE
+.IP foreach
+.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 attribute name is just the name, e.g. reply:Reply-Message, with
+none of the usual variable referenced %{...}. This is because it is a
+reference to the attribute, and not an expansion of the attribute.
+
+Inside of the "foreach" block, the attribute which is being looped
+over can be referenced as "Foreach-Variable-#". Where "#" is the
+depth of the loop, starting at "0". e.g. "Foreach-Variable-0". The
+loops can be nested up to eight (8) deep, though this is not
+recommended.
+
+.DS
+ foreach Attribute-Name {
+.br
+ ...
+.br
+ }
+.DE
.IP switch
.br
Evaluate the given string, and choose the first matching "case"
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,
#endif
MOD_POLICY, MOD_REFERENCE, MOD_XLAT } type;
int method;
"update",
"switch",
"case",
+ "foreach",
#endif
"policy"
};
}
goto handle_result;
}
+
+ if (child->type == MOD_FOREACH) {
+ int i, depth = -1;
+ VALUE_PAIR *vp;
+ modgroup *g = mod_callabletogroup(child);
+
+ for (i = 0; i < 8; i++) {
+ if (!request_data_get(request, radius_get_vp, i)) {
+ depth = i;
+ break;
+ }
+ }
+
+ if (depth < 0) {
+ RDEBUG("ERROR: Foreach Nesting too deep!");
+ myresult = RLM_MODULE_FAIL;
+ goto handle_result;
+ }
+
+ if (radius_get_vp(request, child->name, &vp)) {
+ RDEBUG2("Foreach %s", child->name);
+ while (vp) {
+ request_data_add(request, modcall,
+ depth, vp, NULL);
+
+ myresult = modcall(component,
+ g->children,
+ request);
+ if (myresult == MOD_ACTION_RETURN) {
+ myresult = RLM_MODULE_OK;
+ break;
+ }
+ vp = pairfind(vp->next,
+ vp->attribute,
+ vp->vendor);
+ }
+
+ /*
+ * Delete the cached attribute.
+ */
+ request_data_get(request, radius_get_vp, depth);
+ }
+
+ myresult = RLM_MODULE_OK;
+ goto handle_result;
+ }
#endif
if (child->type == MOD_REFERENCE) {
case MOD_ELSE:
case MOD_ELSIF:
case MOD_CASE:
+ case MOD_FOREACH:
#endif
case MOD_GROUP:
case MOD_POLICY: /* same as MOD_GROUP */
case MOD_ELSE:
case MOD_ELSIF:
case MOD_CASE:
+ case MOD_FOREACH:
#endif
case MOD_GROUP:
case MOD_POLICY: /* same as MOD_GROUP */
*/
if (!stack.children[stack.pointer]) {
do_return:
- rad_assert(stack.pointer > 0);
myresult = stack.result[stack.pointer];
+ if (stack.pointer == 0) break;
stack.pointer--;
-
if (stack.pointer == 0) break;
RDEBUG2("%.*s- %s %s returns %s",
}
if (!cf_item_find_next(cs, NULL)) {
- cf_log_err(cf_sectiontoitem(cs), "'switch' statments cannot be empty.");
+ cf_log_err(cf_sectiontoitem(cs), "'switch' statements cannot be empty.");
return NULL;
}
csingle->type = MOD_SWITCH;
return csingle;
}
+
+static modcallable *do_compile_modforeach(modcallable *parent,
+ int component, CONF_SECTION *cs,
+ const char *name2)
+{
+ modcallable *csingle;
+
+ component = component; /* -Wunused */
+
+ if (!cf_section_name2(cs)) {
+ cf_log_err(cf_sectiontoitem(cs),
+ "You must specify an attribute to loop over in 'foreach'.");
+ return NULL;
+ }
+
+ if (!cf_item_find_next(cs, NULL)) {
+ cf_log_err(cf_sectiontoitem(cs), "'foreach' blocks cannot be empty.");
+ return NULL;
+ }
+
+ csingle= do_compile_modgroup(parent, component, cs,
+ GROUPTYPE_SIMPLE, GROUPTYPE_SIMPLE);
+ if (!csingle) return NULL;
+ csingle->name = name2;
+ csingle->type = MOD_FOREACH;
+ return csingle;
+}
#endif
static modcallable *do_compile_modserver(modcallable *parent,
}
return csingle;
+
+ } else if (strcmp(modrefname, "foreach") == 0) {
+ *modname = name2;
+
+ csingle = do_compile_modforeach(parent, component, cs,
+ name2);
+ if (!csingle) return NULL;
+
+ return csingle;
#endif
} /* else it's something like sql { fail = 1 ...} */
"outer.reply",
"outer.control",
NULL};
+#ifdef WITH_UNLANG
+static const char * const xlat_foreach_names[] = {"Foreach-Variable-0",
+ "Foreach-Variable-1",
+ "Foreach-Variable-2",
+ "Foreach-Variable-3",
+ "Foreach-Variable-4",
+ "Foreach-Variable-5",
+ "Foreach-Variable-6",
+ "Foreach-Variable-7",
+ "Foreach-Variable-8",
+ "Foreach-Variable-9",
+ NULL};
+#endif
#if REQUEST_MAX_REGEX > 8
#error Please fix the following line
return snprintf(out, outlen, "%u", vp->vp_integer);
}
+
+#ifdef WITH_UNLANG
+/*
+ * Dynamically translate for check:, request:, reply:, etc.
+ */
+static size_t xlat_foreach(void *instance, REQUEST *request,
+ char *fmt, char *out, size_t outlen,
+ RADIUS_ESCAPE_STRING func)
+{
+ VALUE_PAIR *vp;
+
+ /*
+ * See modcall, "FOREACH" for how this works.
+ */
+ vp = request_data_reference(request, radius_get_vp, *(int*) instance);
+ if (!vp) {
+ *out = '\0';
+ return 0;
+ }
+
+ return valuepair2str(out, outlen, vp, vp->type, func);
+}
+#endif
+
/*
* Print data as string, if possible.
*/
c = xlat_find(internal_xlat[i]);
rad_assert(c != NULL);
c->internal = TRUE;
+
+#ifdef WITH_UNLANG
+ xlat_register(xlat_foreach_names[i],
+ xlat_foreach, &xlat_inst[i]);
+ c = xlat_find(xlat_foreach_names[i]);
+ rad_assert(c != NULL);
+ c->internal = TRUE;
+#endif
}
/*