MEDIUM: pattern: add indexation function.

Before this patch, the indexation function check the declared patttern
matching function and index the data according with this function. This
is not useful to add some indexation mode.

This commit adds dedicated indexation function. Each struct pattern is
associated with one indexation function. This function permit to index
data according with the type of pattern and with the type of match.
diff --git a/src/acl.c b/src/acl.c
index 26c58e7..78c3f30 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -130,10 +130,9 @@
  */
 struct acl_expr *parse_acl_expr(const char **args, char **err, struct arg_list *al)
 {
-	__label__ out_return, out_free_expr, out_free_pattern;
+	__label__ out_return, out_free_expr;
 	struct acl_expr *expr;
 	struct acl_keyword *aclkw;
-	struct pattern_list *pattern;
 	int patflags;
 	const char *arg;
 	struct sample_expr *smp = NULL;
@@ -350,6 +349,7 @@
 
 	expr->kw = aclkw ? aclkw->kw : smp->fetch->kw;
 	expr->pat.parse = aclkw ? aclkw->parse : NULL;
+	expr->pat.index = aclkw ? aclkw->index : NULL;
 	expr->pat.match = aclkw ? aclkw->match : NULL;
 	expr->smp = smp;
 	smp = NULL;
@@ -360,16 +360,19 @@
 		switch (expr->smp ? expr->smp->fetch->out_type : aclkw->smp->out_type) {
 		case SMP_T_BOOL:
 			expr->pat.parse = pat_parse_fcts[PAT_MATCH_BOOL];
+			expr->pat.index = pat_index_fcts[PAT_MATCH_BOOL];
 			expr->pat.match = pat_match_fcts[PAT_MATCH_BOOL];
 			break;
 		case SMP_T_SINT:
 		case SMP_T_UINT:
 			expr->pat.parse = pat_parse_fcts[PAT_MATCH_INT];
+			expr->pat.index = pat_index_fcts[PAT_MATCH_INT];
 			expr->pat.match = pat_match_fcts[PAT_MATCH_INT];
 			break;
 		case SMP_T_IPV4:
 		case SMP_T_IPV6:
 			expr->pat.parse = pat_parse_fcts[PAT_MATCH_IP];
+			expr->pat.index = pat_index_fcts[PAT_MATCH_IP];
 			expr->pat.match = pat_match_fcts[PAT_MATCH_IP];
 			break;
 		}
@@ -429,6 +432,7 @@
 				goto out_free_expr;
 			}
 			expr->pat.parse = pat_parse_fcts[idx];
+			expr->pat.index = pat_index_fcts[idx];
 			expr->pat.match = pat_match_fcts[idx];
 			args++;
 		}
@@ -447,7 +451,6 @@
 	}
 
 	/* now parse all patterns */
-	pattern = NULL;
 	while (**args) {
 		arg = *args;
 
@@ -513,11 +516,11 @@
 				if (expr->pat.parse == pat_parse_dotted_ver && have_dot) {
 					if (strl2llrc(dot+1, strlen(dot+1), &minor) != 0) {
 						memprintf(err, "'%s' is neither a number nor a supported operator", arg);
-						goto out_free_pattern;
+						goto out_free_expr;
 					}
 					if (minor >= 65536) {
 						memprintf(err, "'%s' contains too large a minor value", arg);
-						goto out_free_pattern;
+						goto out_free_expr;
 					}
 				}
 
@@ -526,12 +529,12 @@
 				 */
 				if (strl2llrc(arg, dot - arg, &value) != 0) {
 					memprintf(err, "'%s' is neither a number nor a supported operator", arg);
-					goto out_free_pattern;
+					goto out_free_expr;
 				}
 				if (expr->pat.parse == pat_parse_dotted_ver)  {
 					if (value >= 65536) {
 						memprintf(err, "'%s' contains too large a major value", arg);
-						goto out_free_pattern;
+						goto out_free_expr;
 					}
 					value = (value << 16) | (minor & 0xffff);
 				}
@@ -540,7 +543,7 @@
 
 				case STD_OP_EQ: /* this case is not possible. */
 					memprintf(err, "internal error");
-					goto out_free_pattern;
+					goto out_free_expr;
 
 				case STD_OP_GT:
 					value++; /* gt = ge + 1 */
@@ -569,15 +572,13 @@
 			}
 		}
 
-		if (!pattern_register(&expr->pat, arg, NULL, &pattern, patflags, err))
-			goto out_free_pattern;
+		if (!pattern_register(&expr->pat, arg, NULL, patflags, err))
+			goto out_free_expr;
 		args++;
 	}
 
 	return expr;
 
- out_free_pattern:
-	pattern_free(pattern);
  out_free_expr:
 	prune_acl_expr(expr);
 	free(expr);
diff --git a/src/dumpstats.c b/src/dumpstats.c
index ba0a7a5..0eb5639 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -1920,7 +1920,6 @@
 	}
 	else if (strcmp(args[0], "add") == 0) {
 		if (strcmp(args[1], "map") == 0) {
-			struct pattern_list *pat;
 			struct map_entry *ent;
 			struct sample_storage *smp;
 
@@ -1993,23 +1992,7 @@
 					continue;
 				}
 
-				/* If the value can be indexed, get the first pattern. If
-				 * the return entry is not the indexed entry, new 'pattern' is
-				 * created by the function pattern_register(). If the 'pattern'
-				 * is NULL, new entry is created. This is ugly because the
-				 * following code interfers with the own code of the function
-				 * pattern_register().
-				 */
-				if (appctx->ctx.map.desc->pat->match == pat_match_str ||
-				    appctx->ctx.map.desc->pat->match == pat_match_ip) {
-					pat = LIST_NEXT(&appctx->ctx.map.desc->pat->patterns, struct pattern_list *, list);
-					if (&pat->list == &appctx->ctx.map.desc->pat->patterns)
-						pat = NULL;
-				}
-				else
-					pat = NULL;
-
-				if (!pattern_register(appctx->ctx.map.desc->pat, args[3], smp, &pat, 0, NULL)) {
+				if (!pattern_register(appctx->ctx.map.desc->pat, args[3], smp, 0, NULL)) {
 					free(smp);
 					continue;
 				}
diff --git a/src/map.c b/src/map.c
index 7e546c7..52f077d 100644
--- a/src/map.c
+++ b/src/map.c
@@ -301,7 +301,6 @@
  * <patflags> must be PAT_F_*.
  */
 static int map_parse_and_index(struct map_descriptor *desc,
-                               struct pattern_list **pattern,
                                struct map_entry *ent,
                                int patflags,
                                char **err)
@@ -321,7 +320,7 @@
 	}
 
 	/* register key */
-	if (!pattern_register(desc->pat, ent->key, smp, pattern, patflags, err))
+	if (!pattern_register(desc->pat, ent->key, smp, patflags, err))
 		return 0;
 
 	return 1;
@@ -337,7 +336,6 @@
 {
 	struct map_reference *ref;
 	struct map_descriptor *desc;
-	struct pattern_list *pattern;
 	struct map_entry *ent;
 	struct pattern_expr *pat = NULL;
 
@@ -412,11 +410,11 @@
 		/* set the match method */
 		desc->pat->match = pat_match_fcts[conv->private];
 		desc->pat->parse = pat_parse_fcts[conv->private];
+		desc->pat->index = pat_index_fcts[conv->private];
 
 		/* parse each line of the file */
-		pattern = NULL;
 		list_for_each_entry(ent, &ref->entries, list)
-			if (!map_parse_and_index(desc, &pattern, ent, 0, err))
+			if (!map_parse_and_index(desc, ent, 0, err))
 				return 0;
 	}
 
diff --git a/src/pattern.c b/src/pattern.c
index 6ff6e26..e652190 100644
--- a/src/pattern.c
+++ b/src/pattern.c
@@ -56,6 +56,22 @@
 	[PAT_MATCH_REG]   = pat_parse_reg,
 };
 
+int (*pat_index_fcts[PAT_MATCH_NUM])(struct pattern_expr *, struct pattern *, char **) = {
+	[PAT_MATCH_FOUND] = pat_idx_list_val,
+	[PAT_MATCH_BOOL]  = pat_idx_list_val,
+	[PAT_MATCH_INT]   = pat_idx_list_val,
+	[PAT_MATCH_IP]    = pat_idx_tree_ip,
+	[PAT_MATCH_BIN]   = pat_idx_list_ptr,
+	[PAT_MATCH_LEN]   = pat_idx_list_val,
+	[PAT_MATCH_STR]   = pat_idx_tree_str,
+	[PAT_MATCH_BEG]   = pat_idx_list_str,
+	[PAT_MATCH_SUB]   = pat_idx_list_str,
+	[PAT_MATCH_DIR]   = pat_idx_list_str,
+	[PAT_MATCH_DOM]   = pat_idx_list_str,
+	[PAT_MATCH_END]   = pat_idx_list_str,
+	[PAT_MATCH_REG]   = pat_idx_list_reg,
+};
+
 enum pat_match_res (*pat_match_fcts[PAT_MATCH_NUM])(struct sample *, struct pattern *) = {
 	[PAT_MATCH_FOUND] = NULL,
 	[PAT_MATCH_BOOL]  = pat_match_nothing,
@@ -786,138 +802,236 @@
 	expr->pattern_tree = EB_ROOT_UNIQUE;
 }
 
-/* return 1 if the process is ok
- * return -1 if the parser fail. The err message is filled.
- * return -2 if out of memory
+/*
+ *
+ * The following functions are used for the pattern indexation
+ *
  */
-int pattern_register(struct pattern_expr *expr, const char *arg,
-                     struct sample_storage *smp,
-                     struct pattern_list **pattern,
-                     int patflags, char **err)
+
+int pat_idx_list_val(struct pattern_expr *expr, struct pattern *pat, char **err)
 {
-	unsigned int mask = 0;
-	struct pat_idx_elt *node;
-	int len;
-	int ret;
+	struct pattern_list *patl;
 
-	/* we keep the previous pattern along iterations as long as it's not used */
-	if (!*pattern)
-		*pattern = (struct pattern_list *)malloc(sizeof(**pattern));
-	if (!*pattern) {
-		memprintf(err, "out of memory while loading pattern");
+	/* allocate pattern */
+	patl = calloc(1, sizeof(*patl));
+	if (!patl) {
+		memprintf(err, "out of memory while indexing pattern");
 		return 0;
 	}
 
-	memset(*pattern, 0, sizeof(**pattern));
-	(*pattern)->pat.flags = patflags;
+	/* duplicate pattern */
+	memcpy(&patl->pat, pat, sizeof(*pat));
 
-	ret = expr->parse(arg, &(*pattern)->pat, PAT_U_COMPILE, err);
-	if (!ret)
+	/* chain pattern in the expression */
+	LIST_ADDQ(&expr->patterns, &patl->list);
+
+	/* that's ok */
+	return 1;
+}
+
+int pat_idx_list_ptr(struct pattern_expr *expr, struct pattern *pat, char **err)
+{
+	struct pattern_list *patl;
+
+	/* allocate pattern */
+	patl = calloc(1, sizeof(*patl));
+	if (!patl)
 		return 0;
 
-	if (expr->match == pat_match_str) {
-		/* SMP_T_CSTR tree indexation.
-		 * The match "pat_match_str()" can use trees.
-		 */
-		if ((*pattern)->pat.flags & PAT_F_IGNORE_CASE) {
-			/* If the flag PAT_F_IGNORE_CASE is set, we cannot use trees */
-			goto just_chain_the_pattern;
-		}
+	/* duplicate pattern */
+	memcpy(&patl->pat, pat, sizeof(*pat));
+	patl->pat.ptr.ptr = malloc(patl->pat.len);
+	if (!patl->pat.ptr.ptr) {
+		free(patl);
+		memprintf(err, "out of memory while indexing pattern");
+		return 0;
+	}
+	memcpy(patl->pat.ptr.ptr, pat->ptr.ptr, pat->len);
 
-		/* Process the key len */
-		len = strlen((*pattern)->pat.ptr.str) + 1;
+	/* chain pattern in the expression */
+	LIST_ADDQ(&expr->patterns, &patl->list);
 
-		/* node memory allocation */
-		node = calloc(1, sizeof(*node) + len);
-		if (!node) {
-			memprintf(err, "out of memory while loading pattern");
-			return 0;
-		}
+	/* that's ok */
+	return 1;
+}
 
-		/* copy the pointer to sample associated to this node */
-		node->smp = smp;
+int pat_idx_list_str(struct pattern_expr *expr, struct pattern *pat, char **err)
+{
+	struct pattern_list *patl;
 
-		/* copy the string */
-		memcpy(node->node.key, (*pattern)->pat.ptr.str, len);
+	/* allocate pattern */
+	patl = calloc(1, sizeof(*patl));
+	if (!patl) {
+		memprintf(err, "out of memory while indexing pattern");
+		return 0;
+	}
+
+	/* duplicate pattern */
+	memcpy(&patl->pat, pat, sizeof(*pat));
+	patl->pat.ptr.str = malloc(patl->pat.len + 1);
+	if (!patl->pat.ptr.str) {
+		free(patl);
+		memprintf(err, "out of memory while indexing pattern");
+		return 0;
+	}
+	memcpy(patl->pat.ptr.ptr, pat->ptr.ptr, pat->len);
+	patl->pat.ptr.str[patl->pat.len] = '\0';
 
-		/* the "map_parser_str()" function always duplicate string information */
-		free((*pattern)->pat.ptr.str);
-		(*pattern)->pat.ptr.str = NULL;
+	/* chain pattern in the expression */
+	LIST_ADDQ(&expr->patterns, &patl->list);
 
-		/* we pre-set the data pointer to the tree's head so that functions
-		 * which are able to insert in a tree know where to do that.
-		 *
-		 * because "val" is an "union", the previous data are crushed.
-		 */
-		(*pattern)->pat.flags |= PAT_F_TREE;
-		(*pattern)->pat.val.tree = &expr->pattern_tree;
+	/* that's ok */
+	return 1;
+}
+
+int pat_idx_list_reg(struct pattern_expr *expr, struct pattern *pat, char **err)
+{
+	struct pattern_list *patl;
 
-		/* index the new node */
-		if (ebst_insert((*pattern)->pat.val.tree, &node->node) != &node->node)
-			free(node); /* was a duplicate */
+	/* allocate pattern */
+	patl = calloc(1, sizeof(*patl));
+	if (!patl) {
+		memprintf(err, "out of memory while indexing pattern");
+		return 0;
 	}
-	else if (expr->match == pat_match_ip) {
-		/* SMP_T_IPV4 tree indexation
-		 * The match "pat_match_ip()" can use tree.
-		 */
-		if ((*pattern)->pat.type != SMP_T_IPV4) {
-			/* Only IPv4 can be indexed */
-			goto just_chain_the_pattern;
-		}
 
+	/* duplicate pattern */
+	memcpy(&patl->pat, pat, sizeof(*pat));
+
+	/* allocate regex */
+	patl->pat.ptr.reg = calloc(1, sizeof(*patl->pat.ptr.reg));
+	if (!patl->pat.ptr.reg) {
+		free(patl);
+		memprintf(err, "out of memory while indexing pattern");
+		return 0;
+	}
+
+	/* compile regex */
+	if (!regex_comp(pat->ptr.reg->regstr, patl->pat.ptr.reg, !(patl->pat.flags & PAT_F_IGNORE_CASE), 0, err)) {
+		free(patl);
+		free(patl->pat.ptr.reg);
+		return 0;
+	}
+
+	/* free pattern method */
+	patl->pat.freeptrbuf = &pat_free_reg;
+
+	/* chain pattern in the expression */
+	LIST_ADDQ(&expr->patterns, &patl->list);
+
+	/* that's ok */
+	return 1;
+}
+
+int pat_idx_tree_ip(struct pattern_expr *expr, struct pattern *pat, char **err)
+{
+	unsigned int mask;
+	struct pat_idx_elt *node;
+
+	/* Only IPv4 can be indexed */
+	if (pat->type == SMP_T_IPV4) {
 		/* in IPv4 case, check if the mask is contiguous so that we can
 		 * insert the network into the tree. A continuous mask has only
 		 * ones on the left. This means that this mask + its lower bit
 		 * added once again is null.
 		 */
-		mask = ntohl((*pattern)->pat.val.ipv4.mask.s_addr);
-		if (mask + (mask & -mask) != 0)
-			goto just_chain_the_pattern;
-		mask = mask ? 33 - flsnz(mask & -mask) : 0; /* equals cidr value */
+		mask = ntohl(pat->val.ipv4.mask.s_addr);
+		if (mask + (mask & -mask) == 0) {
+			mask = mask ? 33 - flsnz(mask & -mask) : 0; /* equals cidr value */
 
-		/* node memory allocation */
-		node = calloc(1, sizeof(*node) + 4);
-		if (!node) {
-			memprintf(err, "out of memory while loading pattern");
-			return 0;
-		}
+			/* node memory allocation */
+			node = calloc(1, sizeof(*node) + 4);
+			if (!node) {
+				memprintf(err, "out of memory while loading pattern");
+				return 0;
+			}
 
-		/* copy the pointer to sample associated to this node */
-		node->smp = smp;
+			/* copy the pointer to sample associated to this node */
+			node->smp = pat->smp;
 
-		/* FIXME: insert <addr>/<mask> into the tree here */
-		memcpy(node->node.key, &(*pattern)->pat.val.ipv4.addr, 4); /* network byte order */
+			/* FIXME: insert <addr>/<mask> into the tree here */
+			memcpy(node->node.key, &pat->val.ipv4.addr, 4); /* network byte order */
+			node->node.node.pfx = mask;
+			if (ebmb_insert_prefix(&expr->pattern_tree, &node->node, 4) != &node->node)
+				free(node); /* was a duplicate */
 
-		/* we pre-set the data pointer to the tree's head so that functions
-		 * which are able to insert in a tree know where to do that.
-		 *
-		 * because "val" is an "union", the previous data are crushed.
-		 */
-		(*pattern)->pat.flags |= PAT_F_TREE;
-		(*pattern)->pat.val.tree = &expr->pattern_tree;
+			/* that's ok */
+			return 1;
+		}
+	}
 
-		/* Index the new node
-		 * FIXME: insert <addr>/<mask> into the tree here
-		 */
-		node->node.node.pfx = mask;
-		if (ebmb_insert_prefix((*pattern)->pat.val.tree, &node->node, 4) != &node->node)
-			free(node); /* was a duplicate */
+	/* If the value cannot be indexed, just add it to the list */
+	return pat_idx_list_val(expr, pat, err);
+}
+
+int pat_idx_tree_str(struct pattern_expr *expr, struct pattern *pat, char **err)
+{
+	int len;
+	struct pat_idx_elt *node;
+
+	/* Only string can be indexed */
+	if (pat->type != SMP_T_CSTR && pat->type != SMP_T_STR) {
+		memprintf(err, "internal error: string expected, but the type is '%s'",
+		          smp_to_type[pat->type]);
+		return 0;
 	}
-	else {
-just_chain_the_pattern:
-		/* if the parser did not feed the tree, let's chain the pattern to the list */
-		LIST_ADDQ(&expr->patterns, &(*pattern)->list);
 
-		/* copy the pointer to sample associated to this node */
-		(*pattern)->pat.smp = smp;
+	/* If the flag PAT_F_IGNORE_CASE is set, we cannot use trees */
+	if (pat->flags & PAT_F_IGNORE_CASE)
+		return pat_idx_list_str(expr, pat, err);
 
-		/* get a new one */
-		*pattern = NULL;
+	/* Process the key len */
+	len = strlen(pat->ptr.str) + 1;
+
+	/* node memory allocation */
+	node = calloc(1, sizeof(*node) + len);
+	if (!node) {
+		memprintf(err, "out of memory while loading pattern");
+		return 0;
 	}
 
+	/* copy the pointer to sample associated to this node */
+	node->smp = pat->smp;
+
+	/* copy the string */
+	memcpy(node->node.key, pat->ptr.str, len);
+
+	/* index the new node */
+	if (ebst_insert(&expr->pattern_tree, &node->node) != &node->node)
+		free(node); /* was a duplicate */
+
+	/* that's ok */
 	return 1;
 }
 
+/* return 1 if the process is ok
+ * return -1 if the parser fail. The err message is filled.
+ * return -2 if out of memory
+ */
+int pattern_register(struct pattern_expr *expr, const char *arg,
+                     struct sample_storage *smp,
+                     int patflags, char **err)
+{
+	int ret;
+	struct pattern pattern;
+
+	/* initialise pattern */
+	memset(&pattern, 0, sizeof(pattern));
+	pattern.flags = patflags;
+	pattern.smp = smp;
+
+	/* parse pattern */
+	ret = expr->parse(arg, &pattern, PAT_U_LOOKUP, err);
+	if (!ret)
+		return 0;
+
+	/* index pattern */
+	if (!expr->index(expr, &pattern, err))
+		return 0;
+
+	return 1;
+}
+
 /* Reads patterns from a file. If <err_msg> is non-NULL, an error message will
  * be returned there on errors and the caller will have to free it.
  */
@@ -928,7 +1042,6 @@
 	FILE *file;
 	char *c;
 	char *arg;
-	struct pattern_list *pattern;
 	int ret = 0;
 	int line = 0;
 	int code;
@@ -943,7 +1056,6 @@
 	 * line. If the line contains spaces, they will be part of the pattern.
 	 * The pattern stops at the first CR, LF or EOF encountered.
 	 */
-	pattern = NULL;
 	while (fgets(trash.str, trash.size, file) != NULL) {
 		line++;
 		c = trash.str;
@@ -966,21 +1078,19 @@
 		if (c == arg)
 			continue;
 
-		code = pattern_register(expr, arg, NULL, &pattern, patflags, err);
+		code = pattern_register(expr, arg, NULL, patflags, err);
 		if (code == -2) {
 			memprintf(err, "out of memory when loading patterns from file <%s>", filename);
 			goto out_close;
 		}
 		else if (code < 0) {
 			memprintf(err, "%s when loading patterns from file <%s>", *err, filename);
-			goto out_free_pattern;
+			goto out_close;
 		}
 	}
 
 	ret = 1; /* success */
 
- out_free_pattern:
-	pattern_free(pattern);
  out_close:
 	fclose(file);
 	return ret;
diff --git a/src/payload.c b/src/payload.c
index 1db1a7f..d9d66f9 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -681,13 +681,13 @@
  * Please take care of keeping this list alphabetically sorted.
  */
 static struct acl_kw_list acl_kws = {ILH, {
-	{ "payload",            "req.payload",        pat_parse_bin,        pat_match_bin     },
-	{ "payload_lv",         "req.payload_lv",     pat_parse_bin,        pat_match_bin     },
-	{ "req_rdp_cookie",     "req.rdp_cookie",     pat_parse_str,        pat_match_str     },
-	{ "req_rdp_cookie_cnt", "req.rdp_cookie_cnt", pat_parse_int,        pat_match_int     },
-	{ "req_ssl_sni",        "req.ssl_sni",        pat_parse_str,        pat_match_str     },
-	{ "req_ssl_ver",        "req.ssl_ver",        pat_parse_dotted_ver, pat_match_int     },
-	{ "req.ssl_ver",        "req.ssl_ver",        pat_parse_dotted_ver, pat_match_int     },
+	{ "payload",            "req.payload",        pat_parse_bin,        pat_idx_list_ptr, pat_match_bin },
+	{ "payload_lv",         "req.payload_lv",     pat_parse_bin,        pat_idx_list_ptr, pat_match_bin },
+	{ "req_rdp_cookie",     "req.rdp_cookie",     pat_parse_str,        pat_idx_tree_str, pat_match_str },
+	{ "req_rdp_cookie_cnt", "req.rdp_cookie_cnt", pat_parse_int,        pat_idx_list_val, pat_match_int },
+	{ "req_ssl_sni",        "req.ssl_sni",        pat_parse_str,        pat_idx_tree_str, pat_match_str },
+	{ "req_ssl_ver",        "req.ssl_ver",        pat_parse_dotted_ver, pat_idx_list_val, pat_match_int },
+	{ "req.ssl_ver",        "req.ssl_ver",        pat_parse_dotted_ver, pat_idx_list_val, pat_match_int },
 	{ /* END */ },
 }};
 
diff --git a/src/proto_http.c b/src/proto_http.c
index fefcf64..8bd10b7 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -10346,84 +10346,84 @@
  * Please take care of keeping this list alphabetically sorted.
  */
 static struct acl_kw_list acl_kws = {ILH, {
-	{ "base",            "base",          pat_parse_str,     pat_match_str     },
-	{ "base_beg",        "base",          pat_parse_str,     pat_match_beg     },
-	{ "base_dir",        "base",          pat_parse_str,     pat_match_dir     },
-	{ "base_dom",        "base",          pat_parse_str,     pat_match_dom     },
-	{ "base_end",        "base",          pat_parse_str,     pat_match_end     },
-	{ "base_len",        "base",          pat_parse_int,     pat_match_len     },
-	{ "base_reg",        "base",          pat_parse_reg,     pat_match_reg     },
-	{ "base_sub",        "base",          pat_parse_str,     pat_match_sub     },
+	{ "base",            "base",     pat_parse_str,  pat_idx_tree_str, pat_match_str  },
+	{ "base_beg",        "base",     pat_parse_str,  pat_idx_list_str, pat_match_beg  },
+	{ "base_dir",        "base",     pat_parse_str,  pat_idx_list_str, pat_match_dir  },
+	{ "base_dom",        "base",     pat_parse_str,  pat_idx_list_str, pat_match_dom  },
+	{ "base_end",        "base",     pat_parse_str,  pat_idx_list_str, pat_match_end  },
+	{ "base_len",        "base",     pat_parse_int,  pat_idx_list_val, pat_match_len  },
+	{ "base_reg",        "base",     pat_parse_reg,  pat_idx_list_reg, pat_match_reg  },
+	{ "base_sub",        "base",     pat_parse_str,  pat_idx_list_str, pat_match_sub  },
 
-	{ "cook",            "req.cook",      pat_parse_str,     pat_match_str     },
-	{ "cook_beg",        "req.cook",      pat_parse_str,     pat_match_beg     },
-	{ "cook_dir",        "req.cook",      pat_parse_str,     pat_match_dir     },
-	{ "cook_dom",        "req.cook",      pat_parse_str,     pat_match_dom     },
-	{ "cook_end",        "req.cook",      pat_parse_str,     pat_match_end     },
-	{ "cook_len",        "req.cook",      pat_parse_int,     pat_match_len     },
-	{ "cook_reg",        "req.cook",      pat_parse_reg,     pat_match_reg     },
-	{ "cook_sub",        "req.cook",      pat_parse_str,     pat_match_sub     },
+	{ "cook",            "req.cook", pat_parse_str,  pat_idx_tree_str, pat_match_str  },
+	{ "cook_beg",        "req.cook", pat_parse_str,  pat_idx_list_str, pat_match_beg  },
+	{ "cook_dir",        "req.cook", pat_parse_str,  pat_idx_list_str, pat_match_dir  },
+	{ "cook_dom",        "req.cook", pat_parse_str,  pat_idx_list_str, pat_match_dom  },
+	{ "cook_end",        "req.cook", pat_parse_str,  pat_idx_list_str, pat_match_end  },
+	{ "cook_len",        "req.cook", pat_parse_int,  pat_idx_list_val, pat_match_len  },
+	{ "cook_reg",        "req.cook", pat_parse_reg,  pat_idx_list_reg, pat_match_reg  },
+	{ "cook_sub",        "req.cook", pat_parse_str,  pat_idx_list_str, pat_match_sub  },
 
-	{ "hdr",             "req.hdr",       pat_parse_str,     pat_match_str     },
-	{ "hdr_beg",         "req.hdr",       pat_parse_str,     pat_match_beg     },
-	{ "hdr_dir",         "req.hdr",       pat_parse_str,     pat_match_dir     },
-	{ "hdr_dom",         "req.hdr",       pat_parse_str,     pat_match_dom     },
-	{ "hdr_end",         "req.hdr",       pat_parse_str,     pat_match_end     },
-	{ "hdr_len",         "req.hdr",       pat_parse_int,     pat_match_len     },
-	{ "hdr_reg",         "req.hdr",       pat_parse_reg,     pat_match_reg     },
-	{ "hdr_sub",         "req.hdr",       pat_parse_str,     pat_match_sub     },
+	{ "hdr",             "req.hdr",  pat_parse_str,  pat_idx_tree_str, pat_match_str  },
+	{ "hdr_beg",         "req.hdr",  pat_parse_str,  pat_idx_list_str, pat_match_beg  },
+	{ "hdr_dir",         "req.hdr",  pat_parse_str,  pat_idx_list_str, pat_match_dir  },
+	{ "hdr_dom",         "req.hdr",  pat_parse_str,  pat_idx_list_str, pat_match_dom  },
+	{ "hdr_end",         "req.hdr",  pat_parse_str,  pat_idx_list_str, pat_match_end  },
+	{ "hdr_len",         "req.hdr",  pat_parse_int,  pat_idx_list_val, pat_match_len  },
+	{ "hdr_reg",         "req.hdr",  pat_parse_reg,  pat_idx_list_reg, pat_match_reg  },
+	{ "hdr_sub",         "req.hdr",  pat_parse_str,  pat_idx_list_str, pat_match_sub  },
 
-	{ "http_auth_group", NULL,            pat_parse_str,     pat_match_auth    },
+	{ "http_auth_group", NULL,       pat_parse_str,  pat_idx_list_str, pat_match_auth },
 
-	{ "method",          NULL,            pat_parse_meth,    pat_match_meth    },
+	{ "method",          NULL,       pat_parse_meth, pat_idx_list_str, pat_match_meth },
 
-	{ "path",            "path",          pat_parse_str,     pat_match_str     },
-	{ "path_beg",        "path",          pat_parse_str,     pat_match_beg     },
-	{ "path_dir",        "path",          pat_parse_str,     pat_match_dir     },
-	{ "path_dom",        "path",          pat_parse_str,     pat_match_dom     },
-	{ "path_end",        "path",          pat_parse_str,     pat_match_end     },
-	{ "path_len",        "path",          pat_parse_int,     pat_match_len     },
-	{ "path_reg",        "path",          pat_parse_reg,     pat_match_reg     },
-	{ "path_sub",        "path",          pat_parse_str,     pat_match_sub     },
+	{ "path",            "path",     pat_parse_str,  pat_idx_tree_str, pat_match_str  },
+	{ "path_beg",        "path",     pat_parse_str,  pat_idx_list_str, pat_match_beg  },
+	{ "path_dir",        "path",     pat_parse_str,  pat_idx_list_str, pat_match_dir  },
+	{ "path_dom",        "path",     pat_parse_str,  pat_idx_list_str, pat_match_dom  },
+	{ "path_end",        "path",     pat_parse_str,  pat_idx_list_str, pat_match_end  },
+	{ "path_len",        "path",     pat_parse_int,  pat_idx_list_val, pat_match_len  },
+	{ "path_reg",        "path",     pat_parse_reg,  pat_idx_list_reg, pat_match_reg  },
+	{ "path_sub",        "path",     pat_parse_str,  pat_idx_list_str, pat_match_sub  },
 
-	{ "req_ver",         "req.ver",       pat_parse_str,     pat_match_str     },
-	{ "resp_ver",        "res.ver",       pat_parse_str,     pat_match_str     },
+	{ "req_ver",         "req.ver",  pat_parse_str,  pat_idx_tree_str, pat_match_str  },
+	{ "resp_ver",        "res.ver",  pat_parse_str,  pat_idx_tree_str, pat_match_str  },
 
-	{ "scook",           "res.cook",      pat_parse_str,     pat_match_str     },
-	{ "scook_beg",       "res.cook",      pat_parse_str,     pat_match_beg     },
-	{ "scook_dir",       "res.cook",      pat_parse_str,     pat_match_dir     },
-	{ "scook_dom",       "res.cook",      pat_parse_str,     pat_match_dom     },
-	{ "scook_end",       "res.cook",      pat_parse_str,     pat_match_end     },
-	{ "scook_len",       "res.cook",      pat_parse_int,     pat_match_len     },
-	{ "scook_reg",       "res.cook",      pat_parse_reg,     pat_match_reg     },
-	{ "scook_sub",       "res.cook",      pat_parse_str,     pat_match_sub     },
+	{ "scook",           "res.cook", pat_parse_str,  pat_idx_tree_str, pat_match_str  },
+	{ "scook_beg",       "res.cook", pat_parse_str,  pat_idx_list_str, pat_match_beg  },
+	{ "scook_dir",       "res.cook", pat_parse_str,  pat_idx_list_str, pat_match_dir  },
+	{ "scook_dom",       "res.cook", pat_parse_str,  pat_idx_list_str, pat_match_dom  },
+	{ "scook_end",       "res.cook", pat_parse_str,  pat_idx_list_str, pat_match_end  },
+	{ "scook_len",       "res.cook", pat_parse_int,  pat_idx_list_val, pat_match_len  },
+	{ "scook_reg",       "res.cook", pat_parse_reg,  pat_idx_list_reg, pat_match_reg  },
+	{ "scook_sub",       "res.cook", pat_parse_str,  pat_idx_list_str, pat_match_sub  },
 
-	{ "shdr",            "res.hdr",       pat_parse_str,     pat_match_str     },
-	{ "shdr_beg",        "res.hdr",       pat_parse_str,     pat_match_beg     },
-	{ "shdr_dir",        "res.hdr",       pat_parse_str,     pat_match_dir     },
-	{ "shdr_dom",        "res.hdr",       pat_parse_str,     pat_match_dom     },
-	{ "shdr_end",        "res.hdr",       pat_parse_str,     pat_match_end     },
-	{ "shdr_len",        "res.hdr",       pat_parse_int,     pat_match_len     },
-	{ "shdr_reg",        "res.hdr",       pat_parse_reg,     pat_match_reg     },
-	{ "shdr_sub",        "res.hdr",       pat_parse_str,     pat_match_sub     },
+	{ "shdr",            "res.hdr",  pat_parse_str,  pat_idx_tree_str, pat_match_str  },
+	{ "shdr_beg",        "res.hdr",  pat_parse_str,  pat_idx_list_str, pat_match_beg  },
+	{ "shdr_dir",        "res.hdr",  pat_parse_str,  pat_idx_list_str, pat_match_dir  },
+	{ "shdr_dom",        "res.hdr",  pat_parse_str,  pat_idx_list_str, pat_match_dom  },
+	{ "shdr_end",        "res.hdr",  pat_parse_str,  pat_idx_list_str, pat_match_end  },
+	{ "shdr_len",        "res.hdr",  pat_parse_int,  pat_idx_list_val, pat_match_len  },
+	{ "shdr_reg",        "res.hdr",  pat_parse_reg,  pat_idx_list_reg, pat_match_reg  },
+	{ "shdr_sub",        "res.hdr",  pat_parse_str,  pat_idx_list_str, pat_match_sub  },
 
-	{ "url",             "url",           pat_parse_str,     pat_match_str     },
-	{ "url_beg",         "url",           pat_parse_str,     pat_match_beg     },
-	{ "url_dir",         "url",           pat_parse_str,     pat_match_dir     },
-	{ "url_dom",         "url",           pat_parse_str,     pat_match_dom     },
-	{ "url_end",         "url",           pat_parse_str,     pat_match_end     },
-	{ "url_len",         "url",           pat_parse_int,     pat_match_len     },
-	{ "url_reg",         "url",           pat_parse_reg,     pat_match_reg     },
-	{ "url_sub",         "url",           pat_parse_str,     pat_match_sub     },
+	{ "url",             "url",      pat_parse_str,  pat_idx_tree_str, pat_match_str  },
+	{ "url_beg",         "url",      pat_parse_str,  pat_idx_list_str, pat_match_beg  },
+	{ "url_dir",         "url",      pat_parse_str,  pat_idx_list_str, pat_match_dir  },
+	{ "url_dom",         "url",      pat_parse_str,  pat_idx_list_str, pat_match_dom  },
+	{ "url_end",         "url",      pat_parse_str,  pat_idx_list_str, pat_match_end  },
+	{ "url_len",         "url",      pat_parse_int,  pat_idx_list_val, pat_match_len  },
+	{ "url_reg",         "url",      pat_parse_reg,  pat_idx_list_reg, pat_match_reg  },
+	{ "url_sub",         "url",      pat_parse_str,  pat_idx_list_str, pat_match_sub  },
 
-	{ "urlp",            "urlp",          pat_parse_str,     pat_match_str     },
-	{ "urlp_beg",        "urlp",          pat_parse_str,     pat_match_beg     },
-	{ "urlp_dir",        "urlp",          pat_parse_str,     pat_match_dir     },
-	{ "urlp_dom",        "urlp",          pat_parse_str,     pat_match_dom     },
-	{ "urlp_end",        "urlp",          pat_parse_str,     pat_match_end     },
-	{ "urlp_len",        "urlp",          pat_parse_int,     pat_match_len     },
-	{ "urlp_reg",        "urlp",          pat_parse_reg,     pat_match_reg     },
-	{ "urlp_sub",        "urlp",          pat_parse_str,     pat_match_sub     },
+	{ "urlp",            "urlp",     pat_parse_str,  pat_idx_tree_str, pat_match_str  },
+	{ "urlp_beg",        "urlp",     pat_parse_str,  pat_idx_list_str, pat_match_beg  },
+	{ "urlp_dir",        "urlp",     pat_parse_str,  pat_idx_list_str, pat_match_dir  },
+	{ "urlp_dom",        "urlp",     pat_parse_str,  pat_idx_list_str, pat_match_dom  },
+	{ "urlp_end",        "urlp",     pat_parse_str,  pat_idx_list_str, pat_match_end  },
+	{ "urlp_len",        "urlp",     pat_parse_int,  pat_idx_list_val, pat_match_len  },
+	{ "urlp_reg",        "urlp",     pat_parse_reg,  pat_idx_list_reg, pat_match_reg  },
+	{ "urlp_sub",        "urlp",     pat_parse_str,  pat_idx_list_str, pat_match_sub  },
 
 	{ /* END */ },
 }};
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index b095f8c..188c1e3 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -3540,31 +3540,31 @@
  * Please take care of keeping this list alphabetically sorted.
  */
 static struct acl_kw_list acl_kws = {ILH, {
-	{ "ssl_c_i_dn",             NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_c_key_alg",          NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_c_notafter",         NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_c_notbefore",        NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_c_sig_alg",          NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_c_s_dn",             NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_c_serial",           NULL,         pat_parse_bin,     pat_match_bin     },
-	{ "ssl_f_i_dn",             NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_f_key_alg",          NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_f_notafter",         NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_f_notbefore",        NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_f_sig_alg",          NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_f_s_dn",             NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_f_serial",           NULL,         pat_parse_bin,     pat_match_bin     },
-	{ "ssl_fc_cipher",          NULL,         pat_parse_str,     pat_match_str     },
+	{ "ssl_c_i_dn",             NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_c_key_alg",          NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_c_notafter",         NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_c_notbefore",        NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_c_sig_alg",          NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_c_s_dn",             NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_c_serial",           NULL,         pat_parse_bin, pat_idx_list_ptr, pat_match_bin     },
+	{ "ssl_f_i_dn",             NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_f_key_alg",          NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_f_notafter",         NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_f_notbefore",        NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_f_sig_alg",          NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_f_s_dn",             NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_f_serial",           NULL,         pat_parse_bin, pat_idx_list_ptr, pat_match_bin     },
+	{ "ssl_fc_cipher",          NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
 #ifdef OPENSSL_NPN_NEGOTIATED
-	{ "ssl_fc_npn",             NULL,         pat_parse_str,     pat_match_str     },
+	{ "ssl_fc_npn",             NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
 #endif
 #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
-	{ "ssl_fc_alpn",            NULL,         pat_parse_str,     pat_match_str     },
+	{ "ssl_fc_alpn",            NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
 #endif
-	{ "ssl_fc_protocol",        NULL,         pat_parse_str,     pat_match_str     },
-	{ "ssl_fc_sni",             "ssl_fc_sni", pat_parse_str,     pat_match_str     },
-	{ "ssl_fc_sni_end",         "ssl_fc_sni", pat_parse_str,     pat_match_end     },
-	{ "ssl_fc_sni_reg",         "ssl_fc_sni", pat_parse_reg,     pat_match_reg     },
+	{ "ssl_fc_protocol",        NULL,         pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_fc_sni",             "ssl_fc_sni", pat_parse_str, pat_idx_tree_str, pat_match_str     },
+	{ "ssl_fc_sni_end",         "ssl_fc_sni", pat_parse_str, pat_idx_list_str, pat_match_end     },
+	{ "ssl_fc_sni_reg",         "ssl_fc_sni", pat_parse_reg, pat_idx_list_reg, pat_match_reg     },
 	{ /* END */ },
 }};