and "switch" is then marked as the break point.
Also update the "break" checks to use the flags instead of
unlang types
----
The `break` statement is used to exit an enclosing
-xref:unlang/foreach.adoc[foreach] loop. The `break` statement only be used
-inside of a xref:unlang/foreach.adoc[foreach] loop.
+xref:unlang/foreach.adoc[foreach] loop or a
+xref:unlang/case.adoc[case] statement. The `break` statement cannot
+be used in any other location.
-.Example
+In this example, a `break` is used to exit a
+xref:unlang/foreach.adoc[foreach] loop when a particular condition
+matches.
+
+.Example of break within foreach
[source,unlang]
----
foreach i (Class) {
}
----
-// Copyright (C) 2021 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+In the next example, a `break` is used to exit a
+xref:unlang/case.adoc[case] statement, which then also exits the
+parent xref:unlang/switch.adoc[switch] statement
+
+.Example of break within case / switch
+[source,unlang]
+----
+switch User-Name {
+ case 'bob' {
+ if (NAS-IP-Address == 192.0.2.1) {
+ break
+ }
+
+ reject
+ }
+
+ default {
+ ok
+ }
+}
+----
+
+// Copyright (C) 2025 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
// This documentation was developed by Network RADIUS SAS.
xref:xlat/index.adoc[string].
The keyword `default` can be used to specify the default action to
-take inside of a xref:unlang/switch.adoc[switch] statement.
-
-If no _<match>_ text is given, it means that the `case` statement is
-the "default" and will match all which is not matched by another
-`case` statement inside of the same xref:unlang/switch.adoc[switch].
-This syntax is deprecated, and will be removed in a future release.
+take inside of a xref:unlang/switch.adoc[switch] statement. The older
+syntax of using `case { ... }` is deprecated, and will be removed un a
+future release.
+
+It is possible to xref:unlang/break.adoc[break] out of `case`
+statement. Any xref:unlang/break.adoc[break] in a `case` statement
+will cause the interpreter to exit both the current `case` statement,
+and also the parent xref:unlang/switch.adoc[switch] statement.
.Example
[source,unlang]
static unlang_t *compile_break(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
{
- unlang_t *foreach;
+ unlang_t *unlang;
static unlang_ext_t const break_ext = {
.type = UNLANG_TYPE_BREAK,
.type_name = "unlang_group_t",
};
- for (foreach = parent; foreach != NULL; foreach = foreach->parent) {
+ for (unlang = parent; unlang != NULL; unlang = unlang->parent) {
/*
- * A "break" inside of a "policy" is an error.
- * We CANNOT allow "break" inside of a policy to
- * affect a "foreach" loop outside of that
- * policy.
+ * "break" doesn't go past a return point.
*/
- if (foreach->type == UNLANG_TYPE_POLICY) goto error;
+ if ((unlang_ops[unlang->type].flag & UNLANG_OP_FLAG_RETURN_POINT) != 0) goto error;
- if (foreach->type == UNLANG_TYPE_FOREACH) break;
+ if ((unlang_ops[unlang->type].flag & UNLANG_OP_FLAG_BREAK_POINT) != 0) break;
}
- if (!foreach) {
+ if (!unlang) {
error:
- cf_log_err(ci, "'break' can only be used in a 'foreach' section");
+ cf_log_err(ci, "Invalid location for 'break' - it can only be used inside 'foreach' or 'switch'");
cf_log_err(ci, DOC_KEYWORD_REF(break));
return NULL;
}
&(unlang_op_t){
.name = "case",
.interpret = unlang_case,
- .flag = UNLANG_OP_FLAG_DEBUG_BRACES
+ .flag = UNLANG_OP_FLAG_DEBUG_BRACES | UNLANG_OP_FLAG_BREAK_POINT
});
}
UNLANG_TYPE_SWITCH, //!< Switch section.
UNLANG_TYPE_CASE, //!< Case section (within a #UNLANG_TYPE_SWITCH).
UNLANG_TYPE_FOREACH, //!< Foreach section.
- UNLANG_TYPE_BREAK, //!< Break statement (within a #UNLANG_TYPE_FOREACH).
+ UNLANG_TYPE_BREAK, //!< Break statement (within a #UNLANG_TYPE_FOREACH or #UNLANG_TYPE_CASE).
UNLANG_TYPE_RETURN, //!< Return statement.
UNLANG_TYPE_MAP, //!< Mapping section (like #UNLANG_TYPE_UPDATE, but uses
//!< values from a #map_proc_t call).
--- /dev/null
+#
+# PRE: switch
+#
+
+switch User-Name {
+ case "bob" {
+ if (User-Password == "hello") {
+ success
+ break
+ }
+
+ test_fail
+ }
+
+ case "doug" {
+ Filter-Id := "doug"
+ test_fail
+ }
+
+ default {
+ Filter-Id := "default"
+ test_fail
+ }
+}