----
...
(0) ldap - Reserved connection (0)
-(0) ldap - EXPAND (uid=%{%{Stripped-User-Name}:-%{User-Name}})
+(0) ldap - EXPAND (uid=%{%{Stripped-User-Name} || %{User-Name}})
(0) ldap - --> (uid=john)
(0) ldap - Performing search in "dc=example,dc=com" with filter "(uid=john)", scope "sub"
(0) ldap - Waiting for search result...
[source,log]
----
(0) ldap - Reserved connection (0)
-(0) ldap - EXPAND (uid=%{%{Stripped-User-Name}:-%{User-Name}})
+(0) ldap - EXPAND (uid=%{%{Stripped-User-Name} || %{User-Name}})
(0) ldap - --> (uid=john)
(0) ldap - Performing search in "dc=example,dc=com" with filter "(uid=john)", scope "sub"
(0) ldap - Waiting for search result...
[source,log]
----
(0) ldap - Reserved connection (0)
-(0) ldap - EXPAND (uid=%{%{Stripped-User-Name}:-%{User-Name}})
+(0) ldap - EXPAND (uid=%{%{Stripped-User-Name} || %{User-Name}})
(0) ldap - --> (uid=john)
(0) ldap - Performing search in "dc=example,dc=com" with filter "(uid=john)", scope "sub"
(0) ldap - Waiting for search result...
*/
fr_value_box_list_t out; //!< Head of the result of a nested
///< expansion.
- bool alternate; //!< record which alternate branch we
- ///< previously took.
xlat_func_t resume; //!< called on resume
xlat_func_signal_t signal; //!< called on signal
fr_signal_t sigmask; //!< Signals to block
}
xa = xlat_frame_eval_repeat(state->ctx, &state->values, &child,
- &state->alternate, request, state->head, &state->exp, state->env_data, &state->out);
+ request, state->head, &state->exp, state->env_data, &state->out);
switch (xa) {
case XLAT_ACTION_PUSH_CHILD:
fr_assert(child);
* Free existing lists if present
*/
if (node->type != 0) switch (node->type) {
- case XLAT_ALTERNATE:
- TALLOC_FREE(node->alternate[0]);
- TALLOC_FREE(node->alternate[1]);
- break;
-
case XLAT_GROUP:
TALLOC_FREE(node->group);
break;
* Alloc new lists to match the type
*/
switch (type) {
- case XLAT_ALTERNATE:
- node->alternate[0] = _xlat_exp_head_alloc(NDEBUG_LOCATION_VALS node);
- node->alternate[1] = _xlat_exp_head_alloc(NDEBUG_LOCATION_VALS node);
- break;
-
case XLAT_GROUP:
node->group = _xlat_exp_head_alloc(NDEBUG_LOCATION_VALS node);
break;
* need to allocate for this node type.
*/
switch (type) {
- case XLAT_ALTERNATE:
- extra_hdrs = 2;
- extra = sizeof(xlat_exp_head_t) * 2;
- break;
-
case XLAT_GROUP:
extra_hdrs = 1;
extra = sizeof(xlat_exp_head_t);
* Ensure the format string is valid... At this point
* they should all be talloc'd strings.
*/
- if (p->type == XLAT_ALTERNATE) {
- /* Alternates don't have format strings */
- MEM(node = xlat_exp_alloc_null(ctx));
- xlat_exp_set_type(node, XLAT_ALTERNATE);
- } else {
- MEM(node = xlat_exp_alloc(ctx, p->type,
- talloc_get_type_abort_const(p->fmt, char), talloc_array_length(p->fmt) - 1));
- }
+ MEM(node = xlat_exp_alloc(ctx, p->type,
+ talloc_get_type_abort_const(p->fmt, char), talloc_array_length(p->fmt) - 1));
node->quote = p->quote;
node->flags = p->flags;
break;
#endif
- case XLAT_ALTERNATE:
- if (unlikely(_xlat_copy_internal(NDEBUG_LOCATION_VALS
- node, node->alternate[0], p->alternate[0]) < 0)) goto error;
- if (unlikely(_xlat_copy_internal(NDEBUG_LOCATION_VALS
- node, node->alternate[1], p->alternate[1]) < 0)) goto error;
- break;
-
case XLAT_GROUP:
if (unlikely(_xlat_copy_internal(NDEBUG_LOCATION_VALS
node, node->group, p->group) < 0)) goto error;
(void)talloc_get_type_abort_const(node, xlat_exp_t);
switch (node->type) {
- case XLAT_ALTERNATE:
- xlat_exp_head_verify(node->alternate[0]);
- xlat_exp_head_verify(node->alternate[1]);
- fr_assert(!node->fmt);
- return;
-
case XLAT_GROUP:
xlat_exp_head_verify(node->group);
(void)talloc_get_type_abort_const(node->fmt, char);
return fr_sbuff_set(out, &our_out);
}
- case XLAT_ALTERNATE:
- {
- fr_sbuff_t our_out = FR_SBUFF(out);
- fr_slen_t slen;
-
- FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "%{");
-
- xlat_exp_foreach(node->alternate[0], child) {
- slen = xlat_fmt_print(&our_out, child);
- if (slen < 0) return slen - fr_sbuff_used(&our_out);
- }
-
- FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, ":-");
-
- xlat_exp_foreach(node->alternate[1], child) {
- slen = xlat_fmt_print(&our_out, child);
- if (slen < 0) return slen - fr_sbuff_used(&our_out);
- }
-
- FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "}");
-
- return fr_sbuff_set(out, &our_out);
- }
-
default:
return 0;
}
* Once evaluation is complete, the caller
* should call us with the same #xlat_exp_t and the
* result of the nested evaluation in result.
- * @param[in,out] alternate Whether we processed, or have previously processed
- * the alternate.
* @param[in] request the current request.
* @param[in] head of the list to evaluate
* @param[in,out] in xlat node to evaluate. Advanced as we process
* @param[in] result of a previous nested evaluation.
*/
xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_dcursor_t *out,
- xlat_exp_head_t const **child, bool *alternate,
+ xlat_exp_head_t const **child,
request_t *request, xlat_exp_head_t const *head, xlat_exp_t const **in,
void *env_data, fr_value_box_list_t *result)
{
}
break;
- case XLAT_ALTERNATE:
- {
- fr_assert(alternate);
-
- /*
- * No result from the first child, try the alternate
- */
- if (fr_value_box_list_empty(result)) {
- /*
- * Already tried the alternate
- */
- if (*alternate) {
- XLAT_DEBUG("** [%i] %s(alt-second) - string empty, null expansion, continuing...",
- unlang_interpret_stack_depth(request), __FUNCTION__);
- *alternate = false; /* Reset */
-
- xlat_debug_log_expansion(request, *in, NULL, __LINE__);
- xlat_debug_log_result(request, *in, NULL); /* Record the fact it's NULL */
- break;
- }
-
- XLAT_DEBUG("** [%i] %s(alt-first) - string empty, evaluating alternate",
- unlang_interpret_stack_depth(request), __FUNCTION__);
- *child = (*in)->alternate[1];
- *alternate = true;
-
- return XLAT_ACTION_PUSH_CHILD;
- }
-
- *alternate = false; /* Reset */
-
- xlat_debug_log_expansion(request, *in, NULL, __LINE__);
- xlat_debug_log_list_result(request, *in, result);
-
- fr_value_box_list_move((fr_value_box_list_t *)out->dlist, result);
- }
- break;
-
case XLAT_GROUP:
{
fr_value_box_t *arg;
* If there's no children we can just
* call the function directly.
*/
- xa = xlat_frame_eval_repeat(ctx, out, child, NULL, request, head, in, NULL, &result);
+ xa = xlat_frame_eval_repeat(ctx, out, child, request, head, in, NULL, &result);
if (xa != XLAT_ACTION_DONE || (!*in)) goto finish;
continue;
continue;
#endif
- case XLAT_ALTERNATE:
- XLAT_DEBUG("** [%i] %s(alternate)", unlang_interpret_stack_depth(request), __FUNCTION__);
- fr_assert(node->alternate[0] != NULL);
- fr_assert(node->alternate[1] != NULL);
-
- *child = node->alternate[0];
- xa = XLAT_ACTION_PUSH_CHILD;
- goto finish;
-
case XLAT_GROUP:
XLAT_DEBUG("** [%i] %s(child) - %%{%s ...}", unlang_interpret_stack_depth(request), __FUNCTION__,
node->fmt);
}
break;
- case XLAT_ALTERNATE:
- if (!type || (type & XLAT_ALTERNATE)) {
- ret = walker(node, uctx);
- if (ret < 0) return ret;
- if (ret > 0) continue;
- }
-
- /*
- * Evaluate the first child
- */
- ret = xlat_eval_walk(node->alternate[0], walker, type, uctx);
- if (ret < 0) return ret;
-
- /*
- * Evaluate the alternate expansion path
- */
- ret = xlat_eval_walk(node->alternate[1], walker, type, uctx);
- if (ret < 0) return ret;
- break;
-
case XLAT_GROUP:
if (!type || (type & XLAT_GROUP)) {
ret = walker(node, uctx);
#ifdef HAVE_REGEX
XLAT_REGEX = 0x0080, //!< regex reference %{1}, etc.
#endif
- XLAT_ALTERNATE = 0x0100, //!< xlat conditional syntax :-
XLAT_GROUP = 0x0200 //!< encapsulated string of xlats
} xlat_type_t;
#endif
union {
- xlat_exp_head_t *alternate[2]; //!< alternate expansions
-
xlat_exp_head_t *group; //!< children of a group
/** An tmpl_t reference
fr_value_box_list_t *result, xlat_func_t resume, void *rctx);
xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_dcursor_t *out,
- xlat_exp_head_t const **child, bool *alternate,
+ xlat_exp_head_t const **child,
request_t *request, xlat_exp_head_t const *head, xlat_exp_t const **in,
void *env_data, fr_value_box_list_t *result) CC_HINT(nonnull(1,2,3,5));
node->flags = node->group->flags;
break;
-
- case XLAT_ALTERNATE:
- if (node->alternate[0]->flags.can_purify) {
- rcode = xlat_purify_list(node->alternate[0], request);
- if (rcode < 0) return rcode;
- }
- node->flags = node->alternate[0]->flags;
-
- /*
- * @todo - If the RHS of the alternation
- * is now pure, then we can statically
- * evaluate it, and replace this node
- * with the children. But only if the
- * child list is not empty.
- */
-
- if (node->alternate[1]->flags.can_purify) {
- rcode = xlat_purify_list(node->alternate[1], request);
- if (rcode < 0) return rcode;
- }
- xlat_flags_merge(&node->flags, &node->alternate[1]->flags);
- break;
-
case XLAT_FUNC:
/*
* If the node is not pure, then maybe there's a callback to purify it, OR maybe
static int xlat_tokenize_input(xlat_exp_head_t *head, fr_sbuff_t *in,
fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules);
-static inline int xlat_tokenize_alternation(xlat_exp_head_t *head, fr_sbuff_t *in,
- tmpl_rules_t const *t_rules, bool func_args)
-{
- xlat_exp_t *node;
-
- XLAT_DEBUG("ALTERNATE <-- %pV", fr_box_strvalue_len(fr_sbuff_current(in), fr_sbuff_remaining(in)));
-
- node = xlat_exp_alloc(head, XLAT_ALTERNATE, NULL, 0);
- if (func_args) {
- if (xlat_tokenize_function_args(node->alternate[0], in, t_rules) < 0) {
- error:
- talloc_free(node);
- return -1;
- }
- } else {
- if (xlat_tokenize_expansion(node->alternate[0], in, t_rules) < 0) goto error;
- }
-
- if (!fr_sbuff_adv_past_str_literal(in, ":-")) {
- fr_strerror_const("Expected ':-' after first expansion");
- goto error;
- }
- node->flags = node->alternate[0]->flags;
-
- /*
- * Allow the RHS to be empty as a special case.
- */
- if (fr_sbuff_next_if_char(in, '}')) goto done;
-
- /*
- * Parse the alternate expansion.
- */
- if (xlat_tokenize_input(node->alternate[1], in,
- &xlat_expansion_rules, t_rules) < 0) goto error;
-
- if (!fr_sbuff_next_if_char(in, '}')) {
- fr_strerror_const("Missing closing brace");
- goto error;
- }
-
- if (!xlat_exp_head(node->alternate[1])) {
- fr_strerror_const("Empty expansion is invalid");
- goto error;
- }
-
- xlat_flags_merge(&node->flags, &node->alternate[1]->flags);
-
-done:
- xlat_exp_insert_tail(head, node);
-
- return 0;
-}
-
#ifdef HAVE_REGEX
/** Parse an xlat reference
*
XLAT_DEBUG("EXPANSION <-- %.*s", (int) fr_sbuff_remaining(in), fr_sbuff_current(in));
- /*
- * %{...}:-bar}
- */
- if (fr_sbuff_adv_past_str_literal(in, "%{")) {
- return xlat_tokenize_alternation(head, in, t_rules, false);
- }
-
- /*
- * %(...):-bar}
- */
- if (fr_sbuff_adv_past_str_literal(in, "%(")) {
- return xlat_tokenize_alternation(head, in, t_rules, true);
- }
-
- /*
- * :-bar}
- */
- if (fr_sbuff_is_str_literal(in, ":-")) {
- fr_strerror_const("First item in alternation cannot be empty");
- return -2;
- }
-
#ifdef HAVE_REGEX
fr_sbuff_marker(&s_m, in);
len = fr_sbuff_adv_past_allowed(in, SIZE_MAX, sbuff_char_class_uint, NULL);
break;
#endif
- case XLAT_ALTERNATE:
- DEBUG("XLAT-IF {");
- _xlat_debug_head(node->alternate[0], depth);
- DEBUG("}");
- DEBUG("XLAT-ELSE {");
- _xlat_debug_head(node->alternate[1], depth);
- DEBUG("}");
- break;
-
case XLAT_INVALID:
DEBUG("XLAT-INVALID");
break;
}
break;
- case XLAT_ALTERNATE:
- slen = xlat_print(out, node->alternate[0], &xlat_escape);
- if (slen < 0) return slen;
-
- FR_SBUFF_IN_STRCPY_LITERAL_RETURN(out, ":-");
- slen = xlat_print(out, node->alternate[1], &xlat_escape);
- if (slen < 0) return slen;
- break;
-
- fr_assert_fail(NULL);
- break;
-
case XLAT_INVALID:
case XLAT_BOX:
case XLAT_ONE_LETTER:
node->flags = node->group->flags;
break;
- /*
- * Alternate expansion a || b
- *
- * Do resolution for a OR b
- */
- case XLAT_ALTERNATE:
- if ((xlat_resolve(node->alternate[0], xr_rules) < 0) ||
- (xlat_resolve(node->alternate[1], xr_rules) < 0)) return -1;
-
- node->flags = node->alternate[0]->flags;
- xlat_flags_merge(&node->flags, &node->alternate[1]->flags);
- break;
-
/*
* An unresolved function.
*/
#
# Multiple things in an alternation
#
-&Tmp-String-2 := "%{%{Tmp-String-0}:-%{Tmp-String-1} foo}"
+&Tmp-String-2 := %{%{Tmp-String-0} || "%{Tmp-String-1} foo"}
if (!(&Tmp-String-2 == 'bar foo')) {
test_fail
}
&Tmp-String-0 := "foo"
&Tmp-String-1 := "bar"
-if (!(%{%(test.passthrough:%{Tmp-String-0}):-%{Tmp-String-1}} == 'foo')) {
+if (!(%{%(test.passthrough:%{Tmp-String-0}) || %{Tmp-String-1}} == 'foo')) {
test_fail
}
-if (!(%{%(test.passthrough:%{Tmp-String-2}):-%{Tmp-String-1}} == 'bar')) {
+if (!(%{%(test.passthrough:%{Tmp-String-2}) || %{Tmp-String-1}} == 'bar')) {
test_fail
}
-if (!(%{%{Tmp-String-0}:-%(test.passthrough:%{Tmp-String-1})} == 'foo')) {
+if (!(%{%{Tmp-String-0} || %(test.passthrough:%{Tmp-String-1})} == 'foo')) {
test_fail
}
-if (!(%{%{Tmp-String-2}:-%(test.passthrough:%{Tmp-String-1})} == 'bar')) {
+if (!(%{%{Tmp-String-2} || %(test.passthrough:%{Tmp-String-1})} == 'bar')) {
test_fail
}
-if (!(%{%(test.passthrough:%{Tmp-String-0}):-%(test.passthrough:%{Tmp-String-1})} == 'foo')) {
+if (!(%{%(test.passthrough:%{Tmp-String-0}) || %(test.passthrough:%{Tmp-String-1})} == 'foo')) {
test_fail
}
-if (!(%{%(test.passthrough:%{Tmp-String-2}):-%(test.passthrough:%{Tmp-String-1})} == 'bar')) {
+if (!(%{%(test.passthrough:%{Tmp-String-2}) || %(test.passthrough:%{Tmp-String-1})} == 'bar')) {
test_fail
}
# Filter for user objects, should be specific enough
# to identify a single user object.
- filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
+ filter = "(uid=%{%{Stripped-User-Name} || %{User-Name}})"
# SASL parameters to use for user binds
#
# Filter to find group objects a user is a member of.
# That is, group objects with attributes that
# identify members (the inverse of membership_attribute).
- membership_filter = "(|(member=%{control.Ldap-UserDn})(memberUid=%{%{Stripped-User-Name}:-%{User-Name}}))"
+ membership_filter = "(|(member=%{control.Ldap-UserDn})(memberUid=%{%{Stripped-User-Name} || %{User-Name}}))"
# The attribute in user objects which contain the names
# or DNs of groups a user is a member of.
user {
base_dn = "ou=people,${..base_dn}"
- filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
+ filter = "(uid=%{%{Stripped-User-Name} || %{User-Name}})"
sasl {
}
filter = '(objectClass=groupOfNames)'
scope = 'sub'
name_attribute = cn
- membership_filter = "(|(member=%{control.Ldap-UserDn})(memberUid=%{%{Stripped-User-Name}:-%{User-Name}}))"
+ membership_filter = "(|(member=%{control.Ldap-UserDn})(memberUid=%{%{Stripped-User-Name} || %{User-Name}}))"
membership_attribute = 'memberOf'
cacheable_name = no
cacheable_dn = no
user {
base_dn = "ou=people,${..base_dn}"
- filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
+ filter = "(uid=%{%{Stripped-User-Name} || %{User-Name}})"
sasl {
}
filter = '(objectClass=groupOfNames)'
scope = 'sub'
name_attribute = cn
- membership_filter = "(|(member=%{control.Ldap-UserDn})(memberUid=%{%{Stripped-User-Name}:-%{User-Name}}))"
+ membership_filter = "(|(member=%{control.Ldap-UserDn})(memberUid=%{%{Stripped-User-Name} || %{User-Name}}))"
membership_attribute = 'memberOf'
cacheable_name = no
cacheable_dn = no
user {
base_dn = "ou=people,${..base_dn}"
- filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
+ filter = "(uid=%{%{Stripped-User-Name} || %{User-Name}})"
sasl {
mech = 'DIGEST-MD5'
authname = &User-Name
user {
base_dn = "ou=people,${..base_dn}"
- filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
+ filter = "(uid=%{%{Stripped-User-Name} || %{User-Name}})"
sasl {
mech = 'DIGEST-MD5'
authname = &User-Name
user {
base_dn = "ou=people,${..base_dn}"
- filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
+ filter = "(uid=%{%{Stripped-User-Name} || %{User-Name}})"
}
options {
--- /dev/null
+proto-dictionary radius
+
+#
+# Tests which do %{ ... }
+#
+# but which print out as ( ... )
+#
+xlat literal%{%{User-Password} || 'literal'}
+match literal%{%{User-Password} || 'literal'}
+
+xlat %{%{User-Name} || "bar"}
+match %{%{User-Name} || "bar"}
+
+xlat foo %{%{User-Name} || 'bar'} baz
+match foo %{%{User-Name} || 'bar'} baz
+
+xlat %{%{test:bar} || %{User-Name}}
+match %{%{test:bar} || %{User-Name}}
+
+xlat %{%{test:bar} || %{%{User-Name} || 'bar'}}
+match %{%{test:bar} || %{%{User-Name} || 'bar'}}
+
+xlat %{%{User-Name} || }
+match ERROR offset 19: No operand found. Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value
+
+xlat %{%{Operator-Name} || }
+match ERROR offset 23: No operand found. Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value
+
+xlat %{%{%{User-Name} || 'foo'} || 'bar'}
+match %{%{%{User-Name} || 'foo'} || 'bar'}
+
+xlat %{%{%{User-Name} || 'foo'} || %{%{test:bar} || %{User-Name}}}
+match %{%{%{User-Name} || 'foo'} || %{%{test:bar} || %{User-Name}}}
+
+xlat %{ || }
+match ERROR offset 4: No operand found. Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value
+
+xlat %{ || %{User-Name}}
+match ERROR offset 4: No operand found. Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value
+
+xlat %{%{} || }
+match ERROR offset 5: Empty expression is invalid
+
+xlat %{%{} || foo}
+match ERROR offset 5: Empty expression is invalid
+
+xlat %{%{User-Name} ||
+match ERROR offset 19: No operand found. Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value
+
+# Discuss - Not sure the offset/message is correct here, but not sure if we can determine the correct offset either
+xlat %{%{User-Name} || 'foo'
+match ERROR offset 24: Missing closing brace
+
+xlat %{%{User-Name}:}
+match %{User-Name}:
+
+count
+match foo
xlat \%{literal%{User-Password}literal}
match \%{literal%{User-Password}literal}
-xlat literal%{%{User-Password}:-literal}
-match literal%{%{User-Password}:-literal}
-
-xlat literal%{%{User-Password}:-\%{lit}eral}
-match literal%{%{User-Password}:-\%{lit}eral}
-
-xlat literal%{%{User-Password}:-%{User-Name}\%literal}
-match literal%{%{User-Password}:-%{User-Name}\%literal}
-
-xlat literal%{%{User-Password}:-\%literal}
-match literal%{%{User-Password}:-\%literal}
#
# Regex capture groups
#
xlat \"%t\t%{NAS-IP-Address}\"
match \"%t\t%{NAS-IP-Address}\"
-xlat \"foo %{test:foo}\"
-match \"foo %{test:foo}\"
+xlat \"foo %test('foo')\"
+match \"foo %{test:'foo'}\"
#
# Alternations
#
-xlat %{%{foo}:-%{bar}}
+xlat %{%{foo} || %{bar}}
match ERROR offset 5: Unresolved attributes not allowed in expansions here
-xlat %{%{User-Name}:-%{bar}}
-match ERROR offset 19: Unresolved attributes not allowed in expansions here
-
-xlat %{%{User-Name}:-bar}
-match %{%{User-Name}:-bar}
-
-xlat foo %{%{User-Name}:-bar} baz
-match foo %{%{User-Name}:-bar} baz
-
-xlat %{%{test:bar}:-%{User-Name}}
-match %{%{test:bar}:-%{User-Name}}
-
-xlat %{%{test:bar}:-%{%{User-Name}:-bar}}
-match %{%{test:bar}:-%{%{User-Name}:-bar}}
-
-xlat %{%{User-Name}:-}
-match %{%{User-Name}:-}
-
-xlat %{%{Operator-Name}:-}
-match %{%{Operator-Name}:-}
-
-xlat %{%{%{User-Name}:-foo}:-bar}
-match %{%{%{User-Name}:-foo}:-bar}
-
-xlat %{%{%{User-Name}:-foo}:-%{%{test:bar}:-%{User-Name}}}
-match %{%{%{User-Name}:-foo}:-%{%{test:bar}:-%{User-Name}}}
-
-xlat %{:-}
-match ERROR offset 3: First item in alternation cannot be empty
-
-xlat %{:-%{User-Name}}
-match ERROR offset 3: First item in alternation cannot be empty
-
-xlat %{%{}:-}
-match ERROR offset 5: Empty expression is invalid
-
-xlat %{%{}:-foo}
-match ERROR offset 5: Empty expression is invalid
-
-xlat %{%{User-Name}:-
-match ERROR offset 17: Missing closing brace
-
-# Discuss - Not sure the offset/message is correct here, but not sure if we can determine the correct offset either
-xlat %{%{User-Name}:-foo
-match ERROR offset 20: Missing closing brace
-
-xlat %{%{User-Name}:}
-match ERROR offset 15: Expected ':-' after first expansion
-
-xlat %{%{User-Name}}
-match ERROR offset 15: Expected ':-' after first expansion
-
-xlat %{%{User-Name}:-}
-match %{%{User-Name}:-}
-
+xlat %{%{User-Name} || %{bar}}
+match ERROR offset 21: Unresolved attributes not allowed in expansions here
#
# Empty and malformed expansions
# Issue seen in the wild that caused an SEGV during pass2
xlat %{%{control.IP-Pool.Name}:%{reply.IP-Pool.Range}
-match ERROR offset 26: Expected ':-' after first expansion
+match ERROR offset 49: Missing closing brace
#
# API to split xlat strings into argv-style arguments.
match %{md5:'arg"'}
count
-match 199
+match 157