On Thu, May 26, 2011 at 02:17:35PM +0200, Alan DeKok wrote:
A better fix would be to be able to do loop over attributes, doing checks for each one. But that's getting into the area of a real programming language...
But it would be really useful. Here is a real-life example. I have a database with Cisco-AVPair attributes of the form Cisco-AVPair = "ip:vrf-id=XXX" Cisco-AVPair = "ip:ip-unnumbered=YYY" Cisco-AVPair = "ip:addr-pool=ZZZ" But when replying to certain older devices I need to rewrite them to the obsolete form: Cisco-AVPair = "lcp:interface-config=ip vrf forwarding XXX\nip unnumbered YYY\npeer default ip address pool ZZZ" I do this in unlang today, but it's ugly. update reply { Tmp-String-9 !* "" } if ("%{reply:Cisco-AVPair[*]}" =~ /(^|\n)(ip:vrf-id=([^\n]*))/) { update reply { Tmp-String-9 += "ip vrf forwarding %{3}" Cisco-AVPair -= "%{2}" } } if ("%{reply:Cisco-AVPair[*]}" =~ /(^|\n)(ip:ip-unnumbered=([^\n]*))/) { update reply { Tmp-String-9 += "ip unnumbered %{3}" Cisco-AVPair -= "%{2}" } } if ("%{reply:Cisco-AVPair[*]}" =~ /(^|\n)(ip:addr-pool=([^\n]*))/) { update reply { Tmp-String-9 += "peer default ip address pool %{3}" Cisco-AVPair -= "%{2}" } } if ("%{reply:Tmp-String-9}") { update reply { Cisco-AVPair += "lcp:interface-config=%{reply:Tmp-String-9[*]}" Tmp-String-9 !* "" } } The payoff is avoiding having to link in perl/ruby (whose thread-safety worries me), or use a custom C module for rewriting. Now in the above case, if Foo =~ /pattern/ iterated over the values of Foo and stopped on the first match, that would be good enough for me. But actually that's not very general. Users might require: Foo =~ /pattern/ # first match only Foo =~ /pattern/ # process every match Foo =~ /pattern/ # only true if all values match Foo !~ /pattern/ # first one which doesn't match Foo !~ /pattern/ # process all which don't match Foo !~ /pattern/ # only true if no values match A simple approach might be: if (any Foo =~ /pattern/) { ... } if (all Foo =~ /pattern/) { ... } (but it's not clear how that interacts with string expansions, especially if an expression refers to multiple attributes) Else you have an explicit loop: e.g. foreach Foo { if (Foo =~ /pattern/) { ... last Foo # early termination (if required) } } Within the foreach Foo construct, Foo would expand to the n'th instance instead of the 0'th instance. This would work with string expansions too, but you'd still need to be able to specify the list to iterate on, e.g. foreach reply:Foo { ... } Aside: I would also like to see direct support for accessing control and reply lists without string expansions. Then you could write if (reply:Service-Type == Framed-User) (I don't think that's possible today). It would also make the unwieldy 'update' block obsolete: if (reply:Service-Type == Framed-User) { reply:Framed-Protocol = PPP } radius 3.x perhaps? :-) Regards, Brian. P.S. One other thing for multi-valued attributes: Foo[*] always uses \n as separator and adds \n to the end. It would be useful to be able to join with another character, e.g. Foo[*,] - and be able to suppress the trailing one.