MEDIUM: vars: add a new "set-var-fmt" action

The set-var() action is convenient because it preserves the input type
but it's a pain to deal with when trying to concatenate values. The
most recurring example is when it's needed to build a variable composed
of the source address and the source port. Usually it ends up like this:

    tcp-request session set-var(sess.port) src_port
    tcp-request session set-var(sess.addr) src,concat(":",sess.port)

This is even worse when trying to aggregate multiple fields from stick-table
data for example. Due to this a lot of users instead abuse headers from HTTP
rules:

    http-request set-header(x-addr) %[src]:%[src_port]

But this requires some careful cleanups to make sure they won't leak, and
it's significantly more expensive to deal with. And generally speaking it's
not clean. Plus it must be performed for each and every request, which is
expensive for this common case of ip+port that doesn't change for the whole
session.

This patch addresses this limitation by implementing a new "set-var-fmt"
action which performs the same work as "set-var" but takes a format string
in argument instead of an expression. This way it becomes pretty simple to
just write:

    tcp-request session set-var-fmt(sess.addr) %[src]:%[src_port]

It is usable in all rulesets that already support the "set-var" action.
It is not yet implemented for the global "set-var" directive (which already
takes a string) and the CLI's "set var" command, which would definitely
benefit from it but currently uses its own parser and engine, thus it
must be reworked.

The doc and regtests were updated.
diff --git a/reg-tests/sample_fetches/vars.vtc b/reg-tests/sample_fetches/vars.vtc
index 01e25b9..95ab68d 100644
--- a/reg-tests/sample_fetches/vars.vtc
+++ b/reg-tests/sample_fetches/vars.vtc
@@ -1,5 +1,5 @@
 varnishtest "Test a few set-var() in global, tcp and http rule sets, at different scopes"
-#REQUIRE_VERSION=2.4
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev5)'"
 
 feature ignore_unknown_macro
 
@@ -7,7 +7,9 @@
     global
         set-var proc.int12 int(12)
         set-var proc.int5  str(60),div(proc.int12)
-        set-var proc.str   str("this is")
+        set-var proc.str1  str("this is")
+        set-var proc.str2  str("a string")
+        set-var proc.str   var(proc.str1)
         set-var proc.str   str(""),concat("",proc.str," a string")
         set-var proc.uuid  uuid()
 
@@ -19,11 +21,14 @@
 
     frontend fe1
         bind "fd@${fe1}"
+        tcp-request session set-var-fmt(sess.str3) "%[var(proc.str1)] %[var(proc.str2)]"
         tcp-request session set-var(sess.int5) var(proc.int5)
         tcp-request session set-var(proc.int5) var(proc.int5),add(sess.int5) ## proc. becomes 10
+        tcp-request content set-var-fmt(req.str4) "%[var(sess.str3),regsub(is a,is also a)]"
+        http-request set-var-fmt(txn.str5)  "%[var(req.str4)]"
         http-request set-var(req.int5)  var(sess.int5)
         http-request set-var(sess.int5) var(sess.int5),add(req.int5) ## sess. becomes 10 first time, then 15...
-	http-request return status 200 hdr x-var "proc=%[var(proc.int5)] sess=%[var(sess.int5)] req=%[var(req.int5)] str=%[var(proc.str)] uuid=%[var(proc.uuid)]"
+	http-request return status 200 hdr x-var "proc=%[var(proc.int5)] sess=%[var(sess.int5)] req=%[var(req.int5)] str=%[var(proc.str)] str5=%[var(txn.str5)] uuid=%[var(proc.uuid)]"
 } -start
 
 haproxy h1 -cli {
@@ -35,12 +40,12 @@
     txreq -req GET -url /req1_1
     rxresp
     expect resp.status == 200
-    expect resp.http.x-var ~ "proc=10 sess=10 req=5 str=this is a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+    expect resp.http.x-var ~ "proc=10 sess=10 req=5 str=this is a string str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
 
     txreq -req GET -url /req1_2
     rxresp
     expect resp.status == 200
-    expect resp.http.x-var ~ "proc=10 sess=20 req=10 str=this is a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+    expect resp.http.x-var ~ "proc=10 sess=20 req=10 str=this is a string str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
 } -run
 
 haproxy h1 -cli {
@@ -52,12 +57,12 @@
     txreq -req GET -url /req2_1
     rxresp
     expect resp.status == 200
-    expect resp.http.x-var ~ "proc=20 sess=20 req=10 str=this is a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+    expect resp.http.x-var ~ "proc=20 sess=20 req=10 str=this is a string str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
 
     txreq -req GET -url /req2_2
     rxresp
     expect resp.status == 200
-    expect resp.http.x-var ~ "proc=20 sess=40 req=20 str=this is a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+    expect resp.http.x-var ~ "proc=20 sess=40 req=20 str=this is a string str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
 } -run
 
 haproxy h1 -cli {
@@ -74,5 +79,5 @@
     txreq -req GET -url /req3_1
     rxresp
     expect resp.status == 200
-    expect resp.http.x-var ~ "proc=40 sess=40 req=20 str=updated uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+    expect resp.http.x-var ~ "proc=40 sess=40 req=20 str=updated str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
 } -run