MAJOR: acl: make use of the new argument parsing framework

The ACL parser now uses the argument parser to build a typed argument list.
Right now arguments are all strings and only one argument is supported since
this is what ACLs currently support.
diff --git a/include/types/acl.h b/include/types/acl.h
index 35a22a0..14e4f84 100644
--- a/include/types/acl.h
+++ b/include/types/acl.h
@@ -2,7 +2,7 @@
  * include/types/acl.h
  * This file provides structures and types for ACLs.
  *
- * Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu
+ * Copyright (C) 2000-2012 Willy Tarreau - w@1wt.eu
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -26,6 +26,7 @@
 #include <common/config.h>
 #include <common/mini-clist.h>
 
+#include <types/arg.h>
 #include <types/auth.h>
 #include <types/proxy.h>
 #include <types/server.h>
@@ -302,12 +303,7 @@
 struct acl_expr {
 	struct list list;           /* chaining */
 	struct acl_keyword *kw;     /* back-reference to the keyword */
-	union {                     /* optional argument of the subject (eg: header or cookie name) */
-		char *str;
-		struct userlist *ul;
-		struct server *srv; /* must be initialised by acl_find_targets */
-	} arg;
-	int arg_len;                /* optional argument length */
+	struct arg *args;           /* optional argument list (eg: header or cookie name) */
 	struct list patterns;       /* list of acl_patterns */
 	struct eb_root pattern_tree;  /* may be used for lookup in large datasets */
 };
diff --git a/src/acl.c b/src/acl.c
index 507516f..84de6ac 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -22,6 +22,7 @@
 #include <types/global.h>
 
 #include <proto/acl.h>
+#include <proto/arg.h>
 #include <proto/auth.h>
 #include <proto/buffers.h>
 #include <proto/log.h>
@@ -446,6 +447,8 @@
 
 /* Fetch the RDP cookie identified in the expression.
  * Note: this decoder only works with non-wrapping data.
+ * Accepts either 0 or 1 argument. Argument is a string (cookie name), other
+ * types will lead to undefined behaviour.
  */
 int
 acl_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -480,17 +483,17 @@
 		bleft--;
 	}
 
-	if (expr->arg_len) {
+	if (expr->args) {
 
-		if (bleft <= expr->arg_len)
+		if (bleft <= expr->args->data.str.len)
 			goto too_short;
 
-		if ((data[expr->arg_len] != '=') ||
-		    strncasecmp(expr->arg.str, (const char *)data, expr->arg_len) != 0)
+		if ((data[expr->args->data.str.len] != '=') ||
+		    strncasecmp(expr->args->data.str.str, (const char *)data, expr->args->data.str.len) != 0)
 			goto not_cookie;
 
-		data += expr->arg_len + 1;
-		bleft -= expr->arg_len + 1;
+		data += expr->args->data.str.len + 1;
+		bleft -= expr->args->data.str.len + 1;
 	} else {
 		while (bleft > 0 && *data != '=') {
 			if (*data == '\r' || *data == '\n')
@@ -1241,11 +1244,25 @@
 
 static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
 {
+	struct arg *arg;
+
 	free_pattern_list(&expr->patterns);
 	free_pattern_tree(&expr->pattern_tree);
 	LIST_INIT(&expr->patterns);
-	if (expr->arg_len && expr->arg.str)
-		free(expr->arg.str);
+
+	for (arg = expr->args; arg; arg++) {
+		if (arg->type == ARGT_STOP)
+			break;
+		if (arg->type == ARGT_FE || arg->type == ARGT_BE ||
+		    arg->type == ARGT_TAB || arg->type == ARGT_SRV ||
+		    arg->type == ARGT_USR || arg->type == ARGT_STR) {
+			free(arg->data.str.str);
+			arg->data.str.str = NULL;
+		}
+		arg++;
+	}
+
+	free(expr->args);
 	expr->kw->use_cnt--;
 	return expr;
 }
@@ -1355,22 +1372,26 @@
 	aclkw->use_cnt++;
 	LIST_INIT(&expr->patterns);
 	expr->pattern_tree = EB_ROOT_UNIQUE;
-	expr->arg.str = NULL;
-	expr->arg_len = 0;
 
 	arg = strchr(args[0], '(');
 	if (arg != NULL) {
-		char *end, *arg2;
-		/* there is an argument in the form "subject(arg)" */
+		char *end;
+		int nbargs;
+
+		/* there is an argument in the form "subject(arg[,arg]*)" */
 		arg++;
 		end = strchr(arg, ')');
 		if (!end)
 			goto out_free_expr;
-		arg2 = my_strndup(arg, end - arg);
-		if (!arg2)
+
+		/* Parse the arguments. Note that currently we have no way to
+		 * report parsing errors, hence the NULL in the error pointers.
+		 * At the moment only one string arg is supported.
+		 */
+		nbargs = make_arg_list(arg, end - arg, ARG1(STR), &expr->args,
+				       NULL, NULL, NULL);
+		if (nbargs < 0)
 			goto out_free_expr;
-		expr->arg_len = end - arg;
-		expr->arg.str = arg2;
 	}
 
 	args++;
@@ -1935,14 +1956,17 @@
 				struct server *srv;
 				char *pname, *sname;
 
-				if (!expr->arg.str || !*expr->arg.str) {
+				/* FIXME: at the moment we check argument types from the keyword,
+				 * but later we'll simlpy inspect argument types.
+				 */
+				if (!expr->args || !expr->args->data.str.len) {
 					Alert("proxy %s: acl %s %s(): missing server name.\n",
 						p->id, acl->name, expr->kw->kw);
 					cfgerr++;
 					continue;
 				}
 
-				pname = expr->arg.str;
+				pname = expr->args->data.str.str;
 				sname = strrchr(pname, '/');
 
 				if (sname)
@@ -1971,15 +1995,17 @@
 					continue;
 				}
 
-				free(expr->arg.str);
-				expr->arg_len = 0;
-				expr->arg.srv = srv;
+				free(expr->args->data.str.str);
+				expr->args->data.srv = srv;
 				continue;
 			}
 
 			if (strstr(expr->kw->kw, "http_auth") == expr->kw->kw) {
 
-				if (!expr->arg.str || !*expr->arg.str) {
+				/* FIXME: at the moment we check argument types from the keyword,
+				 * but later we'll simlpy inspect argument types.
+				 */
+				if (!expr->args || !expr->args->data.str.len) {
 					Alert("proxy %s: acl %s %s(): missing userlist name.\n",
 						p->id, acl->name, expr->kw->kw);
 					cfgerr++;
@@ -1987,20 +2013,20 @@
 				}
 
 				if (p->uri_auth && p->uri_auth->userlist &&
-				    !strcmp(p->uri_auth->userlist->name, expr->arg.str))
+				    !strcmp(p->uri_auth->userlist->name, expr->args->data.str.str))
 					ul = p->uri_auth->userlist;
 				else
-					ul = auth_find_userlist(expr->arg.str);
+					ul = auth_find_userlist(expr->args->data.str.str);
 
 				if (!ul) {
 					Alert("proxy %s: acl %s %s(%s): unable to find userlist.\n",
-						p->id, acl->name, expr->kw->kw, expr->arg.str);
+						p->id, acl->name, expr->kw->kw, expr->args->data.str.str);
 					cfgerr++;
 					continue;
 				}
 
-				expr->arg_len = 0;
-				expr->arg.ul  = ul;
+				free(expr->args->data.str.str);
+				expr->args->data.usr = ul;
 			}
 
 
@@ -2014,7 +2040,7 @@
 				}
 
 				list_for_each_entry(pattern, &expr->patterns, list) {
-					pattern->val.group_mask = auth_resolve_groups(expr->arg.ul, pattern->ptr.str);
+					pattern->val.group_mask = auth_resolve_groups(expr->args->data.usr, pattern->ptr.str);
 
 					free(pattern->ptr.str);
 					pattern->ptr.str = NULL;
diff --git a/src/backend.c b/src/backend.c
index b4fa21d..74bee83 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -28,6 +28,7 @@
 #include <types/global.h>
 
 #include <proto/acl.h>
+#include <proto/arg.h>
 #include <proto/backend.h>
 #include <proto/frontend.h>
 #include <proto/lb_chash.h>
@@ -393,6 +394,7 @@
 		return map_get_server_hash(px, hash);
 }
 
+/* RDP Cookie HASH.  */
 struct server *get_server_rch(struct session *s)
 {
 	unsigned long    hash = 0;
@@ -402,6 +404,7 @@
 	int              ret;
 	struct acl_expr  expr;
 	struct acl_test  test;
+	struct arg       args[2];
 
 	/* tot_weight appears to mean srv_count */
 	if (px->lbprm.tot_weight == 0)
@@ -410,8 +413,12 @@
 	memset(&expr, 0, sizeof(expr));
 	memset(&test, 0, sizeof(test));
 
-	expr.arg.str = px->hh_name;
-	expr.arg_len = px->hh_len;
+	args[0].type = ARGT_STR;
+	args[0].data.str.str = px->hh_name;
+	args[0].data.str.len = px->hh_len;
+	args[1].type = ARGT_STOP;
+
+	expr.args = args;
 
 	ret = acl_fetch_rdp_cookie(px, s, NULL, ACL_DIR_REQ, &expr, &test);
 	len = temp_pattern.data.str.len;
@@ -1111,6 +1118,7 @@
 	struct server *srv = px->srv;
 	struct sockaddr_in addr;
 	char *p;
+	struct arg       args[2];
 
 	DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bh=%d analysers=%02x\n",
 		now_ms, __FUNCTION__,
@@ -1127,8 +1135,12 @@
 	memset(&expr, 0, sizeof(expr));
 	memset(&test, 0, sizeof(test));
 
+	args[0].type = ARGT_STR;
+	args[0].data.str.str = s->be->rdp_cookie_name;
+	args[0].data.str.len = s->be->rdp_cookie_len;
+	args[1].type = ARGT_STOP;
+
-	expr.arg.str = s->be->rdp_cookie_name;
-	expr.arg_len = s->be->rdp_cookie_len;
+	expr.args = args;
 
 	ret = acl_fetch_rdp_cookie(px, s, NULL, ACL_DIR_REQ, &expr, &test);
 	if (ret == 0 || (test.flags & ACL_TEST_F_MAY_CHANGE) || temp_pattern.data.str.len == 0)
@@ -1365,16 +1377,19 @@
 /*             All supported keywords must be declared here.            */
 /************************************************************************/
 
-/* set temp integer to the number of enabled servers on the proxy */
+/* set temp integer to the number of enabled servers on the proxy.
+ * Accepts either 0 or 1 argument. Argument is a string, other types will lead to
+ * undefined behaviour.
+ */
 static int
 acl_fetch_nbsrv(struct proxy *px, struct session *l4, void *l7, int dir,
                 struct acl_expr *expr, struct acl_test *test)
 {
 	test->flags = ACL_TEST_F_VOL_TEST;
-	if (expr->arg_len) {
+	if (expr->args) {
 		/* another proxy was designated, we must look for it */
 		for (px = proxy; px; px = px->next)
-			if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->arg.str))
+			if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->args->data.str.str))
 				break;
 	}
 	if (!px)
@@ -1392,12 +1407,14 @@
 
 /* report in test->flags a success or failure depending on the designated
  * server's state. There is no match function involved since there's no pattern.
+ * Accepts exactly 1 argument. Argument is a server, other types will lead to
+ * undefined behaviour.
  */
 static int
 acl_fetch_srv_is_up(struct proxy *px, struct session *l4, void *l7, int dir,
 		    struct acl_expr *expr, struct acl_test *test)
 {
-	struct server *srv = expr->arg.srv;
+	struct server *srv = expr->args->data.srv;
 
 	test->flags = ACL_TEST_F_VOL_TEST;
 	if (!(srv->state & SRV_MAINTAIN) &&
@@ -1408,17 +1425,20 @@
 	return 1;
 }
 
-/* set temp integer to the number of enabled servers on the proxy */
+/* set temp integer to the number of enabled servers on the proxy.
+ * Accepts either 0 or 1 argument. Argument is a string, other types will lead to
+ * undefined behaviour.
+ */
 static int
 acl_fetch_connslots(struct proxy *px, struct session *l4, void *l7, int dir,
 		    struct acl_expr *expr, struct acl_test *test)
 {
 	struct server *iterator;
 	test->flags = ACL_TEST_F_VOL_TEST;
-	if (expr->arg_len) {
+	if (expr->args) {
 		/* another proxy was designated, we must look for it */
 		for (px = proxy; px; px = px->next)
-			if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->arg.str))
+			if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->args->data.str.str))
 				break;
 	}
 	if (!px)
@@ -1470,16 +1490,19 @@
 	return 1;
 }
 
-/* set temp integer to the number of connections per second reaching the backend */
+/* set temp integer to the number of connections per second reaching the backend.
+ * Accepts either 0 or 1 argument. Argument is a string, other types will lead to
+ * undefined behaviour.
+ */
 static int
 acl_fetch_be_sess_rate(struct proxy *px, struct session *l4, void *l7, int dir,
                        struct acl_expr *expr, struct acl_test *test)
 {
 	test->flags = ACL_TEST_F_VOL_TEST;
-	if (expr->arg_len) {
+	if (expr->args) {
 		/* another proxy was designated, we must look for it */
 		for (px = proxy; px; px = px->next)
-			if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->arg.str))
+			if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->args->data.str.str))
 				break;
 	}
 	if (!px)
@@ -1489,16 +1512,19 @@
 	return 1;
 }
 
-/* set temp integer to the number of concurrent connections on the backend */
+/* set temp integer to the number of concurrent connections on the backend.
+ * Accepts either 0 or 1 argument. Argument is a string, other types will lead to
+ * undefined behaviour.
+ */
 static int
 acl_fetch_be_conn(struct proxy *px, struct session *l4, void *l7, int dir,
 		  struct acl_expr *expr, struct acl_test *test)
 {
 	test->flags = ACL_TEST_F_VOL_TEST;
-	if (expr->arg_len) {
+	if (expr->args) {
 		/* another proxy was designated, we must look for it */
 		for (px = proxy; px; px = px->next)
-			if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->arg.str))
+			if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->args->data.str.str))
 				break;
 	}
 	if (!px)
@@ -1508,16 +1534,19 @@
 	return 1;
 }
 
-/* set temp integer to the total number of queued connections on the backend */
+/* set temp integer to the total number of queued connections on the backend.
+ * Accepts either 0 or 1 argument. Argument is a string, other types will lead to
+ * undefined behaviour.
+ */
 static int
 acl_fetch_queue_size(struct proxy *px, struct session *l4, void *l7, int dir,
 		   struct acl_expr *expr, struct acl_test *test)
 {
 	test->flags = ACL_TEST_F_VOL_TEST;
-	if (expr->arg_len) {
+	if (expr->args) {
 		/* another proxy was designated, we must look for it */
 		for (px = proxy; px; px = px->next)
-			if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->arg.str))
+			if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->args->data.str.str))
 				break;
 	}
 	if (!px)
@@ -1532,6 +1561,8 @@
  * server, we return twice the total, just as if we had half a running server.
  * This is more or less correct anyway, since we expect the last server to come
  * back soon.
+ * Accepts either 0 or 1 argument. Argument is a string, other types will lead to
+ * undefined behaviour.
  */
 static int
 acl_fetch_avg_queue_size(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -1540,10 +1571,10 @@
 	int nbsrv;
 
 	test->flags = ACL_TEST_F_VOL_TEST;
-	if (expr->arg_len) {
+	if (expr->args) {
 		/* another proxy was designated, we must look for it */
 		for (px = proxy; px; px = px->next)
-			if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->arg.str))
+			if ((px->cap & PR_CAP_BE) && !strcmp(px->id, expr->args->data.str.str))
 				break;
 	}
 	if (!px)
@@ -1564,12 +1595,15 @@
 	return 1;
 }
 
-/* set temp integer to the number of concurrent connections on the server in the backend */
+/* set temp integer to the number of concurrent connections on the server in the backend.
+ * Accepts exactly 1 argument. Argument is a server, other types will lead to
+ * undefined behaviour.
+ */
 static int
 acl_fetch_srv_conn(struct proxy *px, struct session *l4, void *l7, int dir,
 		  struct acl_expr *expr, struct acl_test *test)
 {
-	struct server *srv = expr->arg.srv;
+	struct server *srv = expr->args->data.srv;
 
 	temp_pattern.data.integer = srv->cur_sess;
 	return 1;
diff --git a/src/frontend.c b/src/frontend.c
index c18cc66..ac6027c 100644
--- a/src/frontend.c
+++ b/src/frontend.c
@@ -506,16 +506,19 @@
 	return 1;
 }
 
-/* set temp integer to the number of connections per second reaching the frontend */
+/* set temp integer to the number of connections per second reaching the frontend.
+ * Accepts either 0 or 1 argument. Argument is a string, other types will cause
+ * an undefined behaviour.
+ */
 static int
 acl_fetch_fe_sess_rate(struct proxy *px, struct session *l4, void *l7, int dir,
                        struct acl_expr *expr, struct acl_test *test)
 {
 	test->flags = ACL_TEST_F_VOL_TEST;
-	if (expr->arg_len) {
+	if (expr->args) {
 		/* another proxy was designated, we must look for it */
 		for (px = proxy; px; px = px->next)
-			if ((px->cap & PR_CAP_FE) && !strcmp(px->id, expr->arg.str))
+			if ((px->cap & PR_CAP_FE) && !strcmp(px->id, expr->args->data.str.str))
 				break;
 	}
 	if (!px)
@@ -525,16 +528,19 @@
 	return 1;
 }
 
-/* set temp integer to the number of concurrent connections on the frontend */
+/* set temp integer to the number of concurrent connections on the frontend
+ * Accepts either 0 or 1 argument. Argument is a string, other types will cause
+ * an undefined behaviour.
+ */
 static int
 acl_fetch_fe_conn(struct proxy *px, struct session *l4, void *l7, int dir,
 		  struct acl_expr *expr, struct acl_test *test)
 {
 	test->flags = ACL_TEST_F_VOL_TEST;
-	if (expr->arg_len) {
+	if (expr->args) {
 		/* another proxy was designated, we must look for it */
 		for (px = proxy; px; px = px->next)
-			if ((px->cap & PR_CAP_FE) && !strcmp(px->id, expr->arg.str))
+			if ((px->cap & PR_CAP_FE) && !strcmp(px->id, expr->args->data.str.str))
 				break;
 	}
 	if (!px)
diff --git a/src/proto_http.c b/src/proto_http.c
index b8b0af6..7f65be4 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -7836,6 +7836,7 @@
 }
 
 /* 5. Check on HTTP header. A pointer to the beginning of the value is returned.
+ * Accepts exactly 1 argument of type string.
  */
 static int
 acl_fetch_hdr(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -7846,13 +7847,16 @@
 	struct hdr_ctx *ctx = (struct hdr_ctx *)test->ctx.a;
 	const struct http_msg *msg = ((dir & ACL_DIR_MASK) == ACL_DIR_REQ) ? &txn->req : &txn->rsp;
 
+	if (!expr->args || expr->args->type != ARGT_STR)
+		return 0;
+
 	CHECK_HTTP_MESSAGE_FIRST();
 
 	if (!(test->flags & ACL_TEST_F_FETCH_MORE))
 		/* search for header from the beginning */
 		ctx->idx = 0;
 
-	if (http_find_header2(expr->arg.str, expr->arg_len, msg->buf->p + msg->sol, idx, ctx)) {
+	if (http_find_header2(expr->args->data.str.str, expr->args->data.str.len, msg->buf->p + msg->sol, idx, ctx)) {
 		test->flags |= ACL_TEST_F_FETCH_MORE;
 		test->flags |= ACL_TEST_F_VOL_HDR;
 		temp_pattern.data.str.str = (char *)ctx->line + ctx->val;
@@ -7867,6 +7871,7 @@
 }
 
 /* 6. Check on HTTP header count. The number of occurrences is returned.
+ * Accepts exactly 1 argument of type string.
  */
 static int
 acl_fetch_hdr_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -7878,11 +7883,14 @@
 	const struct http_msg *msg = ((dir & ACL_DIR_MASK) == ACL_DIR_REQ) ? &txn->req : &txn->rsp;
 	int cnt;
 
+	if (!expr->args || expr->args->type != ARGT_STR)
+		return 0;
+
 	CHECK_HTTP_MESSAGE_FIRST();
 
 	ctx.idx = 0;
 	cnt = 0;
-	while (http_find_header2(expr->arg.str, expr->arg_len, msg->buf->p + msg->sol, idx, &ctx))
+	while (http_find_header2(expr->args->data.str.str, expr->args->data.str.len, msg->buf->p + msg->sol, idx, &ctx))
 		cnt++;
 
 	temp_pattern.data.integer = cnt;
@@ -7982,17 +7990,21 @@
 	return 1;
 }
 
+/* Accepts exactly 1 argument of type userlist */
 static int
 acl_fetch_http_auth(struct proxy *px, struct session *l4, void *l7, int dir,
 		    struct acl_expr *expr, struct acl_test *test)
 {
 
+	if (!expr->args || expr->args->type != ARGT_USR)
+		return 0;
+
 	CHECK_HTTP_MESSAGE_FIRST();
 
 	if (!get_http_auth(l4))
 		return 0;
 
-	test->ctx.a[0] = expr->arg.ul;
+	test->ctx.a[0] = expr->args->data.usr;
 	test->ctx.a[1] = l4->txn.auth.user;
 	test->ctx.a[2] = l4->txn.auth.pass;
 
@@ -8100,7 +8112,8 @@
  * test->ctx.a[0] for the in-header position, test->ctx.a[1] for the
  * end-of-header-value, and test->ctx.a[2] for the hdr_idx. Depending on
  * the direction, multiple cookies may be parsed on the same line or not.
- * The cookie name is in expr->arg and the name length in expr->arg_len.
+ * The cookie name is in expr->arg and the name length in expr->args->data.str.len.
+ * Accepts exactly 1 argument of type string.
  */
 static int
 acl_fetch_cookie_value(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -8114,6 +8127,9 @@
 	int hdr_name_len;
 	char *sol;
 
+	if (!expr->args || expr->args->type != ARGT_STR)
+		return 0;
+
 	CHECK_HTTP_MESSAGE_FIRST();
 
 	if ((dir & ACL_DIR_MASK) == ACL_DIR_REQ) {
@@ -8141,7 +8157,7 @@
 			if (!http_find_header2(hdr_name, hdr_name_len, sol, idx, ctx))
 				goto out;
 
-			if (ctx->vlen < expr->arg_len + 1)
+			if (ctx->vlen < expr->args->data.str.len + 1)
 				continue;
 
 			test->ctx.a[0] = ctx->line + ctx->val;
@@ -8149,7 +8165,7 @@
 		}
 
 		test->ctx.a[0] = extract_cookie_value(test->ctx.a[0], test->ctx.a[1],
-						      expr->arg.str, expr->arg_len,
+						      expr->args->data.str.str, expr->args->data.str.len,
 						      (dir & ACL_DIR_MASK) == ACL_DIR_REQ,
 						      &temp_pattern.data.str.str,
 						      &temp_pattern.data.str.len);
@@ -8168,8 +8184,9 @@
 }
 
 /* Iterate over all cookies present in a request to count how many occurrences
- * match the name in expr->arg and expr->arg_len. If <multi> is non-null, then
+ * match the name in expr->arg and expr->args->data.str.len. If <multi> is non-null, then
  * multiple cookies may be parsed on the same line.
+ * Accepts exactly 1 argument of type string.
  */
 static int
 acl_fetch_cookie_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -8185,6 +8202,9 @@
 	char *val_beg, *val_end;
 	char *sol;
 
+	if (!expr->args || expr->args->type != ARGT_STR)
+		return 0;
+
 	CHECK_HTTP_MESSAGE_FIRST();
 
 	if ((dir & ACL_DIR_MASK) == ACL_DIR_REQ) {
@@ -8208,7 +8228,7 @@
 			if (!http_find_header2(hdr_name, hdr_name_len, sol, idx, &ctx))
 				break;
 
-			if (ctx.vlen < expr->arg_len + 1)
+			if (ctx.vlen < expr->args->data.str.len + 1)
 				continue;
 
 			val_beg = ctx.line + ctx.val;
@@ -8216,7 +8236,7 @@
 		}
 
 		while ((val_beg = extract_cookie_value(val_beg, val_end,
-						       expr->arg.str, expr->arg_len,
+						       expr->args->data.str.str, expr->args->data.str.len,
 						       (dir & ACL_DIR_MASK) == ACL_DIR_REQ,
 						       &temp_pattern.data.str.str,
 						       &temp_pattern.data.str.len))) {
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 1714416..d53cd52 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -1569,6 +1569,7 @@
 	int ret;
 	struct acl_expr  expr;
 	struct acl_test  test;
+	struct arg       args[2];
 
 	if (!l4)
 		return 0;
@@ -1576,8 +1577,12 @@
 	memset(&expr, 0, sizeof(expr));
 	memset(&test, 0, sizeof(test));
 
-	expr.arg.str = arg_p[0].data.str.str;
-	expr.arg_len = arg_p[0].data.str.len;
+	args[0].type = ARGT_STR;
+	args[0].data.str.str = arg_p[0].data.str.str;
+	args[0].data.str.len = arg_p[0].data.str.len;
+	args[1].type = ARGT_STOP;
+
+	expr.args = args;
 
 	ret = acl_fetch_rdp_cookie(px, l4, NULL, ACL_DIR_REQ, &expr, &test);
 	if (ret == 0 || (test.flags & ACL_TEST_F_MAY_CHANGE) || temp_pattern.data.str.len == 0)
diff --git a/src/session.c b/src/session.c
index e930daf..16b50c9 100644
--- a/src/session.c
+++ b/src/session.c
@@ -2346,6 +2346,7 @@
 
 /* set temp integer to the General Purpose Counter 0 value from the session's source
  * address in the table pointed to by expr.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_get_gpc0(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -2357,8 +2358,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -2409,6 +2410,7 @@
 
 /* Increment the General Purpose Counter 0 value from the session's source
  * address in the table pointed to by expr, and return it into temp integer.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_inc_gpc0(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -2420,8 +2422,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -2473,6 +2475,7 @@
 
 /* Clear the General Purpose Counter 0 value from the session's source address
  * in the table pointed to by expr, and return its previous value into temp integer.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_clr_gpc0(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -2484,8 +2487,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -2532,6 +2535,7 @@
 
 /* set temp integer to the cumulated number of connections from the session's source
  * address in the table pointed to by expr.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_conn_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -2543,8 +2547,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -2596,6 +2600,7 @@
 
 /* set temp integer to the connection rate from the session's source address in the
  * table pointed to by expr, over the configured period.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_conn_rate(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -2607,8 +2612,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -2618,6 +2623,7 @@
 
 /* set temp integer to the number of connections from the session's source address
  * in the table pointed to by expr, after updating it.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_updt_conn_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -2631,8 +2637,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -2690,6 +2696,7 @@
 
 /* set temp integer to the number of concurrent connections from the session's source
  * address in the table pointed to by expr.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_conn_cur(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -2701,8 +2708,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -2749,6 +2756,7 @@
 
 /* set temp integer to the cumulated number of session from the session's source
  * address in the table pointed to by expr.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_sess_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -2760,8 +2768,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -2813,6 +2821,7 @@
 
 /* set temp integer to the session rate from the session's source address in the
  * table pointed to by expr, over the configured period.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_sess_rate(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -2824,8 +2833,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -2872,6 +2881,7 @@
 
 /* set temp integer to the cumulated number of session from the session's source
  * address in the table pointed to by expr.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_http_req_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -2883,8 +2893,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -2936,6 +2946,7 @@
 
 /* set temp integer to the session rate from the session's source address in the
  * table pointed to by expr, over the configured period.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_http_req_rate(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -2947,8 +2958,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -2995,6 +3006,7 @@
 
 /* set temp integer to the cumulated number of session from the session's source
  * address in the table pointed to by expr.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_http_err_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -3006,8 +3018,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -3059,6 +3071,7 @@
 
 /* set temp integer to the session rate from the session's source address in the
  * table pointed to by expr, over the configured period.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_http_err_rate(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -3070,8 +3083,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -3123,6 +3136,7 @@
 
 /* set temp integer to the number of kbytes received from the session's source
  * address in the table pointed to by expr.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_kbytes_in(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -3134,8 +3148,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -3189,6 +3203,7 @@
 
 /* set temp integer to the bytes rate from clients from the session's source address
  * in the table pointed to by expr, over the configured period.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_bytes_in_rate(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -3200,8 +3215,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -3253,6 +3268,7 @@
 
 /* set temp integer to the number of kbytes sent to the session's source address in
  * the table pointed to by expr.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_kbytes_out(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -3264,8 +3280,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -3319,6 +3335,7 @@
 
 /* set temp integer to the bytes rate to client from the session's source address in
  * the table pointed to by expr, over the configured period.
+ * Accepts 0 or 1 argument of type string.
  */
 static int
 acl_fetch_src_bytes_out_rate(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -3330,8 +3347,8 @@
 	if (!key)
 		return 0;
 
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -3339,13 +3356,15 @@
 	return acl_fetch_bytes_out_rate(&px->table, test, stktable_lookup_key(&px->table, key));
 }
 
-/* set temp integer to the number of used entries in the table pointed to by expr. */
+/* set temp integer to the number of used entries in the table pointed to by expr.
+ * Accepts 0 or 1 argument of type string.
+ */
 static int
 acl_fetch_table_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
                        struct acl_expr *expr, struct acl_test *test)
 {
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */
@@ -3355,13 +3374,15 @@
 	return 1;
 }
 
-/* set temp integer to the number of free entries in the table pointed to by expr. */
+/* set temp integer to the number of free entries in the table pointed to by expr.
+ * Accepts 0 or 1 argument of type string.
+ */
 static int
 acl_fetch_table_avl(struct proxy *px, struct session *l4, void *l7, int dir,
                             struct acl_expr *expr, struct acl_test *test)
 {
-	if (expr->arg_len)
-		px = find_stktable(expr->arg.str);
+	if (expr->args)
+		px = find_stktable(expr->args->data.str.str);
 
 	if (!px)
 		return 0; /* table not found */