[MEDIUM] acl: add ability to insert patterns in trees

The code is now ready to support loading pattern from filesinto trees. For
that, it will be required that the ACL keyword has a flag ACL_MAY_LOOKUP
and that the expr is case sensitive. When that is true, the pattern will
have a flag ACL_PAT_F_TREE_OK to indicate that it is possible to feed the
tree instead of a usual pattern if the parsing function is able to do this.
The tree's root is pre-initialized in the pattern's value so that the
function can easily find it. At that point, if the parsing function decides
to use the tree, it just sets ACL_PAT_F_TREE in the return flags so that
the caller knows the tree has been used and the pattern can be recycled.

That way it will be possible to load some patterns into the tree when it
is compatible, and other ones as linear linked lists. A good example of
this might be IPv4 network entries : right now we support holes in masks,
but this very rare feature is not compatible with binary lookup in trees.
So the parser will be able to decide itself whether the pattern can go to
the tree or not.
diff --git a/include/types/acl.h b/include/types/acl.h
index 957f214..831b4d1 100644
--- a/include/types/acl.h
+++ b/include/types/acl.h
@@ -220,6 +220,7 @@
 		} ipv4;                         /* IPv4 address */
 		struct acl_time time;           /* valid hours and days */
 		unsigned int group_mask;
+		struct eb_root *tree;           /* tree storing all values if any */
 	} val;                                  /* direct value */
 	union {
 		void *ptr;              /* any data */
diff --git a/src/acl.c b/src/acl.c
index a4ea068..ad3feaf 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -650,9 +650,21 @@
 		free_pattern(pat);
 }
 
+static void free_pattern_tree(struct eb_root *root)
+{
+	struct eb_node *node, *next;
+	node = eb_first(root);
+	while (node) {
+		next = eb_next(node);
+		free(node);
+		node = next;
+	}
+}
+
 static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
 {
 	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);
@@ -679,6 +691,7 @@
 	 * The pattern stops at the first CR, LF or EOF encountered.
 	 */
 	opaque = 0;
+	pattern = NULL;
 	args[0] = trash;
 	args[1] = "";
 	while (fgets(trash, sizeof(trash), file) != NULL) {
@@ -688,15 +701,34 @@
 			c++;
 		*c = 0;
 
-		pattern = (struct acl_pattern *)calloc(1, sizeof(*pattern));
+		/* we keep the previous pattern along iterations as long as it's not used */
+		if (!pattern)
+			pattern = (struct acl_pattern *)malloc(sizeof(*pattern));
 		if (!pattern)
 			goto out_close;
+
+		memset(pattern, 0, sizeof(*pattern));
 		pattern->flags = patflags;
 
+		if ((aclkw->requires & ACL_MAY_LOOKUP) && !(pattern->flags & ACL_PAT_F_IGNORE_CASE)) {
+			/* 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.
+			 */
+			pattern->flags |= ACL_PAT_F_TREE_OK;
+			pattern->val.tree = &expr->pattern_tree;
+		}
+
 		if (!aclkw->parse(args, pattern, &opaque))
 			goto out_free_pattern;
-		LIST_ADDQ(&expr->patterns, &pattern->list);
+
+		/* if the parser did not feed the tree, let's chain the pattern to the list */
+		if (!(pattern->flags & ACL_PAT_F_TREE)) {
+			LIST_ADDQ(&expr->patterns, &pattern->list);
+			pattern = NULL; /* get a new one */
+		}
 	}
+	if (pattern)
+		free_pattern(pattern);
 	return 1;
 
  out_free_pattern:
@@ -730,6 +762,7 @@
 	expr->kw = aclkw;
 	aclkw->use_cnt++;
 	LIST_INIT(&expr->patterns);
+	expr->pattern_tree = EB_ROOT_UNIQUE;
 	expr->arg.str = NULL;
 	expr->arg_len = 0;
 
@@ -1193,12 +1226,13 @@
 				else {
 					/* call the match() function for all tests on this value */
 					list_for_each_entry(pattern, &expr->patterns, list) {
-						acl_res |= expr->kw->match(&test, pattern);
 						if (acl_res == ACL_PAT_PASS)
 							break;
+						acl_res |= expr->kw->match(&test, pattern);
 					}
 
-					if ((test.flags & ACL_TEST_F_NULL_MATCH) && LIST_ISEMPTY(&expr->patterns))
+					if ((test.flags & ACL_TEST_F_NULL_MATCH) &&
+					    LIST_ISEMPTY(&expr->patterns) && !expr->pattern_tree.b[EB_LEFT])
 						acl_res |= expr->kw->match(&test, NULL);
 				}
 				/*