REORG: cli: move map and acl code to map.c

Move map and acl CLI functions to map.c and use the cli keyword API to
register actions on the CLI. Then remove the now unused individual
"add" and "del" keywords.
diff --git a/include/types/cli.h b/include/types/cli.h
index 4f1a84d..8503e84 100644
--- a/include/types/cli.h
+++ b/include/types/cli.h
@@ -117,9 +117,6 @@
 	STAT_CLI_O_CLR,      /* clear tables */
 	STAT_CLI_O_SET,      /* set entries in tables */
 	STAT_CLI_O_STAT,     /* dump stats */
-	STAT_CLI_O_PATS,     /* list all pattern reference available */
-	STAT_CLI_O_PAT,      /* list all entries of a pattern */
-	STAT_CLI_O_MLOOK,    /* lookup a map entry */
 	STAT_CLI_O_POOLS,    /* dump memory pools */
 	STAT_CLI_O_RESOLVERS,/* dump a resolver's section nameservers counters */
 	STAT_CLI_O_SERVERS_STATE, /* dump server state and changing information */
diff --git a/src/cli.c b/src/cli.c
index 7565c80..3901051 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -143,9 +143,6 @@
 static int stats_dump_errors_to_buffer(struct stream_interface *si);
 static int stats_table_request(struct stream_interface *si, int show);
 static int stats_dump_resolvers_to_buffer(struct stream_interface *si);
-static int stats_pats_list(struct stream_interface *si);
-static int stats_pat_list(struct stream_interface *si);
-static int stats_map_lookup(struct stream_interface *si);
 
 static int dump_servers_state(struct stream_interface *si, struct chunk *buf);
 
@@ -179,17 +176,6 @@
 	"  disable        : put a server or frontend in maintenance mode\n"
 	"  enable         : re-enable a server or frontend which is in maintenance mode\n"
 	"  shutdown       : kill a session or a frontend (eg:to release listening ports)\n"
-	"  show acl [id]  : report available acls or dump an acl's contents\n"
-	"  get acl        : reports the patterns matching a sample for an ACL\n"
-	"  add acl        : add acl entry\n"
-	"  del acl        : delete acl entry\n"
-	"  clear acl <id> : clear the content of this acl\n"
-	"  show map [id]  : report available maps or dump a map's contents\n"
-	"  get map        : reports the keys and values matching a sample for a map\n"
-	"  set map        : modify map entry\n"
-	"  add map        : add map entry\n"
-	"  del map        : delete map entry\n"
-	"  clear map <id> : clear the content of this map\n"
 	"";
 
 static const char stats_permission_denied_msg[] =
@@ -992,67 +978,6 @@
 	return sv;
 }
 
-
-/* This function is used with map and acl management. It permits to browse
- * each reference. The variable <getnext> must contain the current node,
- * <end> point to the root node and the <flags> permit to filter required
- * nodes.
- */
-static inline
-struct pat_ref *pat_list_get_next(struct pat_ref *getnext, struct list *end,
-                                  unsigned int flags)
-{
-	struct pat_ref *ref = getnext;
-
-	while (1) {
-
-		/* Get next list entry. */
-		ref = LIST_NEXT(&ref->list, struct pat_ref *, list);
-
-		/* If the entry is the last of the list, return NULL. */
-		if (&ref->list == end)
-			return NULL;
-
-		/* If the entry match the flag, return it. */
-		if (ref->flags & flags)
-			return ref;
-	}
-}
-
-static inline
-struct pat_ref *pat_ref_lookup_ref(const char *reference)
-{
-	int id;
-	char *error;
-
-	/* If the reference starts by a '#', this is numeric id. */
-	if (reference[0] == '#') {
-		/* Try to convert the numeric id. If the conversion fails, the lookup fails. */
-		id = strtol(reference + 1, &error, 10);
-		if (*error != '\0')
-			return NULL;
-
-		/* Perform the unique id lookup. */
-		return pat_ref_lookupid(id);
-	}
-
-	/* Perform the string lookup. */
-	return pat_ref_lookup(reference);
-}
-
-/* This function is used with map and acl management. It permits to browse
- * each reference.
- */
-static inline
-struct pattern_expr *pat_expr_get_next(struct pattern_expr *getnext, struct list *end)
-{
-	struct pattern_expr *expr;
-	expr = LIST_NEXT(&getnext->list, struct pattern_expr *, list);
-	if (&expr->list == end)
-		return NULL;
-	return expr;
-}
-
 /* Processes the stats interpreter on the statistics socket. This function is
  * called from an applet running in a stream interface. The function returns 1
  * if the request was understood, otherwise zero. It sets appctx->st0 to a value
@@ -1257,36 +1182,6 @@
 		else if (strcmp(args[1], "table") == 0) {
 			stats_sock_table_request(si, args, STAT_CLI_O_TAB);
 		}
-		else if (strcmp(args[1], "map") == 0 ||
-		         strcmp(args[1], "acl") == 0) {
-
-			/* Set ACL or MAP flags. */
-			if (args[1][0] == 'm')
-				appctx->ctx.map.display_flags = PAT_REF_MAP;
-			else
-				appctx->ctx.map.display_flags = PAT_REF_ACL;
-
-			/* no parameter: display all map available */
-			if (!*args[2]) {
-				appctx->st2 = STAT_ST_INIT;
-				appctx->st0 = STAT_CLI_O_PATS;
-				return 1;
-			}
-
-			/* lookup into the refs and check the map flag */
-			appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
-			if (!appctx->ctx.map.ref ||
-			    !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
-				if (appctx->ctx.map.display_flags == PAT_REF_MAP)
-					appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
-				else
-					appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
-				appctx->st0 = STAT_CLI_PRINT;
-				return 1;
-			}
-			appctx->st2 = STAT_ST_INIT;
-			appctx->st0 = STAT_CLI_O_PAT;
-		}
 		else { /* neither "stat" nor "info" nor "sess" nor "errors" nor "table" */
 			return 0;
 		}
@@ -1355,42 +1250,6 @@
 			/* end of processing */
 			return 1;
 		}
-		else if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
-			/* Set ACL or MAP flags. */
-			if (args[1][0] == 'm')
-				appctx->ctx.map.display_flags = PAT_REF_MAP;
-			else
-				appctx->ctx.map.display_flags = PAT_REF_ACL;
-
-			/* no parameter */
-			if (!*args[2]) {
-				if (appctx->ctx.map.display_flags == PAT_REF_MAP)
-					appctx->ctx.cli.msg = "Missing map identifier.\n";
-				else
-					appctx->ctx.cli.msg = "Missing ACL identifier.\n";
-				appctx->st0 = STAT_CLI_PRINT;
-				return 1;
-			}
-
-			/* lookup into the refs and check the map flag */
-			appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
-			if (!appctx->ctx.map.ref ||
-			    !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
-				if (appctx->ctx.map.display_flags == PAT_REF_MAP)
-					appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
-				else
-					appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
-				appctx->st0 = STAT_CLI_PRINT;
-				return 1;
-			}
-
-			/* Clear all. */
-			pat_ref_prune(appctx->ctx.map.ref);
-
-			/* return response */
-			appctx->st0 = STAT_CLI_PROMPT;
-			return 1;
-		}
 		else {
 			/* unknown "clear" argument */
 			return 0;
@@ -1427,51 +1286,6 @@
 
 			return 1;
 		}
-		else if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
-			/* Set flags. */
-			if (args[1][0] == 'm')
-				appctx->ctx.map.display_flags = PAT_REF_MAP;
-			else
-				appctx->ctx.map.display_flags = PAT_REF_ACL;
-
-			/* No parameter. */
-			if (!*args[2] || !*args[3]) {
-				if (appctx->ctx.map.display_flags == PAT_REF_MAP)
-					appctx->ctx.cli.msg = "Missing map identifier and/or key.\n";
-				else
-					appctx->ctx.cli.msg = "Missing ACL identifier and/or key.\n";
-				appctx->st0 = STAT_CLI_PRINT;
-				return 1;
-			}
-
-			/* lookup into the maps */
-			appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
-			if (!appctx->ctx.map.ref) {
-				if (appctx->ctx.map.display_flags == PAT_REF_MAP)
-					appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
-				else
-					appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
-				appctx->st0 = STAT_CLI_PRINT;
-				return 1;
-			}
-
-			/* copy input string. The string must be allocated because
-			 * it may be used over multiple iterations. It's released
-			 * at the end and upon abort anyway.
-			 */
-			appctx->ctx.map.chunk.len = strlen(args[3]);
-			appctx->ctx.map.chunk.size = appctx->ctx.map.chunk.len + 1;
-			appctx->ctx.map.chunk.str = strdup(args[3]);
-			if (!appctx->ctx.map.chunk.str) {
-				appctx->ctx.cli.msg = "Out of memory error.\n";
-				appctx->st0 = STAT_CLI_PRINT;
-				return 1;
-			}
-
-			/* prepare response */
-			appctx->st2 = STAT_ST_INIT;
-			appctx->st0 = STAT_CLI_O_MLOOK;
-		}
 		else { /* not "get weight" */
 			return 0;
 		}
@@ -1875,79 +1689,6 @@
 		}
 		else if (strcmp(args[1], "table") == 0) {
 			stats_sock_table_request(si, args, STAT_CLI_O_SET);
-		}
-		else if (strcmp(args[1], "map") == 0) {
-			char *err;
-
-			/* Set flags. */
-			appctx->ctx.map.display_flags = PAT_REF_MAP;
-
-			/* Expect three parameters: map name, key and new value. */
-			if (!*args[2] || !*args[3] || !*args[4]) {
-				appctx->ctx.cli.msg = "'set map' expects three parameters: map identifier, key and value.\n";
-				appctx->st0 = STAT_CLI_PRINT;
-				return 1;
-			}
-
-			/* Lookup the reference in the maps. */
-			appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
-			if (!appctx->ctx.map.ref) {
-				appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
-				appctx->st0 = STAT_CLI_PRINT;
-				return 1;
-			}
-
-			/* If the entry identifier start with a '#', it is considered as
-			 * pointer id
-			 */
-			if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
-				struct pat_ref_elt *ref;
-				long long int conv;
-				char *error;
-
-				/* Convert argument to integer value. */
-				conv = strtoll(&args[3][1], &error, 16);
-				if (*error != '\0') {
-					appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
-					appctx->st0 = STAT_CLI_PRINT;
-					return 1;
-				}
-
-				/* Convert and check integer to pointer. */
-				ref = (struct pat_ref_elt *)(long)conv;
-				if ((long long int)(long)ref != conv) {
-					appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
-					appctx->st0 = STAT_CLI_PRINT;
-					return 1;
-				}
-
-				/* Try to delete the entry. */
-				err = NULL;
-				if (!pat_ref_set_by_id(appctx->ctx.map.ref, ref, args[4], &err)) {
-					if (err)
-						memprintf(&err, "%s.\n", err);
-					appctx->ctx.cli.err = err;
-					appctx->st0 = STAT_CLI_PRINT_FREE;
-					return 1;
-				}
-			}
-			else {
-				/* Else, use the entry identifier as pattern
-				 * string, and update the value.
-				 */
-				err = NULL;
-				if (!pat_ref_set(appctx->ctx.map.ref, args[3], args[4], &err)) {
-					if (err)
-						memprintf(&err, "%s.\n", err);
-					appctx->ctx.cli.err = err;
-					appctx->st0 = STAT_CLI_PRINT_FREE;
-					return 1;
-				}
-			}
-
-			/* The set is done, send message. */
-			appctx->st0 = STAT_CLI_PROMPT;
-			return 1;
 		} else { /* unknown "set" parameter */
 			return 0;
 		}
@@ -2172,169 +1913,6 @@
 			return 1;
 		}
 	}
-	else if (strcmp(args[0], "del") == 0) {
-		if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
-			if (args[1][0] == 'm')
-				appctx->ctx.map.display_flags = PAT_REF_MAP;
-			else
-				appctx->ctx.map.display_flags = PAT_REF_ACL;
-
-			/* Expect two parameters: map name and key. */
-			if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
-				if (!*args[2] || !*args[3]) {
-					appctx->ctx.cli.msg = "This command expects two parameters: map identifier and key.\n";
-					appctx->st0 = STAT_CLI_PRINT;
-					return 1;
-				}
-			}
-
-			else {
-				if (!*args[2] || !*args[3]) {
-					appctx->ctx.cli.msg = "This command expects two parameters: ACL identifier and key.\n";
-					appctx->st0 = STAT_CLI_PRINT;
-					return 1;
-				}
-			}
-
-			/* Lookup the reference in the maps. */
-			appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
-			if (!appctx->ctx.map.ref ||
-			    !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
-				appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
-				appctx->st0 = STAT_CLI_PRINT;
-				return 1;
-			}
-
-			/* If the entry identifier start with a '#', it is considered as
-			 * pointer id
-			 */
-			if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
-				struct pat_ref_elt *ref;
-				long long int conv;
-				char *error;
-
-				/* Convert argument to integer value. */
-				conv = strtoll(&args[3][1], &error, 16);
-				if (*error != '\0') {
-					appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
-					appctx->st0 = STAT_CLI_PRINT;
-					return 1;
-				}
-
-				/* Convert and check integer to pointer. */
-				ref = (struct pat_ref_elt *)(long)conv;
-				if ((long long int)(long)ref != conv) {
-					appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
-					appctx->st0 = STAT_CLI_PRINT;
-					return 1;
-				}
-
-				/* Try to delete the entry. */
-				if (!pat_ref_delete_by_id(appctx->ctx.map.ref, ref)) {
-					/* The entry is not found, send message. */
-					appctx->ctx.cli.msg = "Key not found.\n";
-					appctx->st0 = STAT_CLI_PRINT;
-					return 1;
-				}
-			}
-			else {
-				/* Else, use the entry identifier as pattern
-				 * string and try to delete the entry.
-				 */
-				if (!pat_ref_delete(appctx->ctx.map.ref, args[3])) {
-					/* The entry is not found, send message. */
-					appctx->ctx.cli.msg = "Key not found.\n";
-					appctx->st0 = STAT_CLI_PRINT;
-					return 1;
-				}
-			}
-
-			/* The deletion is done, send message. */
-			appctx->st0 = STAT_CLI_PROMPT;
-			return 1;
-		}
-		else { /* unknown "del" parameter */
-			appctx->ctx.cli.msg = "'del' only supports 'map' or 'acl'.\n";
-			appctx->st0 = STAT_CLI_PRINT;
-			return 1;
-		}
-	}
-	else if (strcmp(args[0], "add") == 0) {
-		if (strcmp(args[1], "map") == 0 ||
-		    strcmp(args[1], "acl") == 0) {
-			int ret;
-			char *err;
-
-			/* Set flags. */
-			if (args[1][0] == 'm')
-				appctx->ctx.map.display_flags = PAT_REF_MAP;
-			else
-				appctx->ctx.map.display_flags = PAT_REF_ACL;
-
-			/* If the keywork is "map", we expect three parameters, if it
-			 * is "acl", we expect only two parameters
-			 */
-			if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
-				if (!*args[2] || !*args[3] || !*args[4]) {
-					appctx->ctx.cli.msg = "'add map' expects three parameters: map identifier, key and value.\n";
-					appctx->st0 = STAT_CLI_PRINT;
-					return 1;
-				}
-			}
-			else {
-				if (!*args[2] || !*args[3]) {
-					appctx->ctx.cli.msg = "'add acl' expects two parameters: ACL identifier and pattern.\n";
-					appctx->st0 = STAT_CLI_PRINT;
-					return 1;
-				}
-			}
-
-			/* Lookup for the reference. */
-			appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
-			if (!appctx->ctx.map.ref) {
-				if (appctx->ctx.map.display_flags == PAT_REF_MAP)
-					appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
-				else
-					appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
-				appctx->st0 = STAT_CLI_PRINT;
-				return 1;
-			}
-
-			/* The command "add acl" is prohibited if the reference
-			 * use samples.
-			 */
-			if ((appctx->ctx.map.display_flags & PAT_REF_ACL) &&
-			    (appctx->ctx.map.ref->flags & PAT_REF_SMP)) {
-				appctx->ctx.cli.msg = "This ACL is shared with a map containing samples. "
-				                      "You must use the command 'add map' to add values.\n";
-				appctx->st0 = STAT_CLI_PRINT;
-				return 1;
-			}
-
-			/* Add value. */
-			err = NULL;
-			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
-				ret = pat_ref_add(appctx->ctx.map.ref, args[3], args[4], &err);
-			else
-				ret = pat_ref_add(appctx->ctx.map.ref, args[3], NULL, &err);
-			if (!ret) {
-				if (err)
-					memprintf(&err, "%s.\n", err);
-				appctx->ctx.cli.err = err;
-				appctx->st0 = STAT_CLI_PRINT_FREE;
-				return 1;
-			}
-
-			/* The add is done, send message. */
-			appctx->st0 = STAT_CLI_PROMPT;
-			return 1;
-		}
-		else { /* unknown "del" parameter */
-			appctx->ctx.cli.msg = "'add' only supports 'map'.\n";
-			appctx->st0 = STAT_CLI_PRINT;
-			return 1;
-		}
-	}
 	else { /* not "show" nor "clear" nor "get" nor "set" nor "enable" nor "disable" */
 		return 0;
 	}
@@ -2509,18 +2087,6 @@
 				if (stats_table_request(si, appctx->st0))
 					appctx->st0 = STAT_CLI_PROMPT;
 				break;
-			case STAT_CLI_O_PATS:
-				if (stats_pats_list(si))
-					appctx->st0 = STAT_CLI_PROMPT;
-				break;
-			case STAT_CLI_O_PAT:
-				if (stats_pat_list(si))
-					appctx->st0 = STAT_CLI_PROMPT;
-				break;
-			case STAT_CLI_O_MLOOK:
-				if (stats_map_lookup(si))
-					appctx->st0 = STAT_CLI_PROMPT;
-				break;
 			case STAT_CLI_O_POOLS:
 				if (stats_dump_pools_to_buffer(si))
 					appctx->st0 = STAT_CLI_PROMPT;
@@ -3282,239 +2848,6 @@
 	return 1;
 }
 
-static int stats_pats_list(struct stream_interface *si)
-{
-	struct appctx *appctx = __objt_appctx(si->end);
-
-	switch (appctx->st2) {
-	case STAT_ST_INIT:
-		/* Display the column headers. If the message cannot be sent,
-		 * quit the fucntion with returning 0. The function is called
-		 * later and restart at the state "STAT_ST_INIT".
-		 */
-		chunk_reset(&trash);
-		chunk_appendf(&trash, "# id (file) description\n");
-		if (bi_putchk(si_ic(si), &trash) == -1) {
-			si_applet_cant_put(si);
-			return 0;
-		}
-
-		/* Now, we start the browsing of the references lists.
-		 * Note that the following call to LIST_ELEM return bad pointer. The only
-		 * available field of this pointer is <list>. It is used with the function
-		 * pat_list_get_next() for retruning the first available entry
-		 */
-		appctx->ctx.map.ref = LIST_ELEM(&pattern_reference, struct pat_ref *, list);
-		appctx->ctx.map.ref = pat_list_get_next(appctx->ctx.map.ref, &pattern_reference,
-		                                        appctx->ctx.map.display_flags);
-		appctx->st2 = STAT_ST_LIST;
-		/* fall through */
-
-	case STAT_ST_LIST:
-		while (appctx->ctx.map.ref) {
-			chunk_reset(&trash);
-
-			/* Build messages. If the reference is used by another category than
-			 * the listed categorie, display the information in the massage.
-			 */
-			chunk_appendf(&trash, "%d (%s) %s\n", appctx->ctx.map.ref->unique_id,
-			              appctx->ctx.map.ref->reference ? appctx->ctx.map.ref->reference : "",
-			              appctx->ctx.map.ref->display);
-
-			if (bi_putchk(si_ic(si), &trash) == -1) {
-				/* let's try again later from this stream. We add ourselves into
-				 * this stream's users so that it can remove us upon termination.
-				 */
-				si_applet_cant_put(si);
-				return 0;
-			}
-
-			/* get next list entry and check the end of the list */
-			appctx->ctx.map.ref = pat_list_get_next(appctx->ctx.map.ref, &pattern_reference,
-			                                        appctx->ctx.map.display_flags);
-		}
-
-		appctx->st2 = STAT_ST_FIN;
-		/* fall through */
-
-	default:
-		appctx->st2 = STAT_ST_FIN;
-		return 1;
-	}
-	return 0;
-}
-
-static int stats_map_lookup(struct stream_interface *si)
-{
-	struct appctx *appctx = __objt_appctx(si->end);
-	struct sample sample;
-	struct pattern *pat;
-	int match_method;
-
-	switch (appctx->st2) {
-	case STAT_ST_INIT:
-		/* Init to the first entry. The list cannot be change */
-		appctx->ctx.map.expr = LIST_ELEM(&appctx->ctx.map.ref->pat, struct pattern_expr *, list);
-		appctx->ctx.map.expr = pat_expr_get_next(appctx->ctx.map.expr, &appctx->ctx.map.ref->pat);
-		appctx->st2 = STAT_ST_LIST;
-		/* fall through */
-
-	case STAT_ST_LIST:
-		/* for each lookup type */
-		while (appctx->ctx.map.expr) {
-			/* initialise chunk to build new message */
-			chunk_reset(&trash);
-
-			/* execute pattern matching */
-			sample.data.type = SMP_T_STR;
-			sample.flags = SMP_F_CONST;
-			sample.data.u.str.len = appctx->ctx.map.chunk.len;
-			sample.data.u.str.str = appctx->ctx.map.chunk.str;
-			if (appctx->ctx.map.expr->pat_head->match &&
-			    sample_convert(&sample, appctx->ctx.map.expr->pat_head->expect_type))
-				pat = appctx->ctx.map.expr->pat_head->match(&sample, appctx->ctx.map.expr, 1);
-			else
-				pat = NULL;
-
-			/* build return message: set type of match */
-			for (match_method=0; match_method<PAT_MATCH_NUM; match_method++)
-				if (appctx->ctx.map.expr->pat_head->match == pat_match_fcts[match_method])
-					break;
-			if (match_method >= PAT_MATCH_NUM)
-				chunk_appendf(&trash, "type=unknown(%p)", appctx->ctx.map.expr->pat_head->match);
-			else
-				chunk_appendf(&trash, "type=%s", pat_match_names[match_method]);
-
-			/* case sensitive */
-			if (appctx->ctx.map.expr->mflags & PAT_MF_IGNORE_CASE)
-				chunk_appendf(&trash, ", case=insensitive");
-			else
-				chunk_appendf(&trash, ", case=sensitive");
-
-			/* Display no match, and set default value */
-			if (!pat) {
-				if (appctx->ctx.map.display_flags == PAT_REF_MAP)
-					chunk_appendf(&trash, ", found=no");
-				else
-					chunk_appendf(&trash, ", match=no");
-			}
-
-			/* Display match and match info */
-			else {
-				/* display match */
-				if (appctx->ctx.map.display_flags == PAT_REF_MAP)
-					chunk_appendf(&trash, ", found=yes");
-				else
-					chunk_appendf(&trash, ", match=yes");
-
-				/* display index mode */
-				if (pat->sflags & PAT_SF_TREE)
-					chunk_appendf(&trash, ", idx=tree");
-				else
-					chunk_appendf(&trash, ", idx=list");
-
-				/* display pattern */
-				if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
-					if (pat->ref && pat->ref->pattern)
-						chunk_appendf(&trash, ", key=\"%s\"", pat->ref->pattern);
-					else
-						chunk_appendf(&trash, ", key=unknown");
-				}
-				else {
-					if (pat->ref && pat->ref->pattern)
-						chunk_appendf(&trash, ", pattern=\"%s\"", pat->ref->pattern);
-					else
-						chunk_appendf(&trash, ", pattern=unknown");
-				}
-
-				/* display return value */
-				if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
-					if (pat->data && pat->ref && pat->ref->sample)
-						chunk_appendf(&trash, ", value=\"%s\", type=\"%s\"", pat->ref->sample,
-						              smp_to_type[pat->data->type]);
-					else
-						chunk_appendf(&trash, ", value=none");
-				}
-			}
-
-			chunk_appendf(&trash, "\n");
-
-			/* display response */
-			if (bi_putchk(si_ic(si), &trash) == -1) {
-				/* let's try again later from this stream. We add ourselves into
-				 * this stream's users so that it can remove us upon termination.
-				 */
-				si_applet_cant_put(si);
-				return 0;
-			}
-
-			/* get next entry */
-			appctx->ctx.map.expr = pat_expr_get_next(appctx->ctx.map.expr,
-			                                         &appctx->ctx.map.ref->pat);
-		}
-
-		appctx->st2 = STAT_ST_FIN;
-		/* fall through */
-
-	default:
-		appctx->st2 = STAT_ST_FIN;
-		free(appctx->ctx.map.chunk.str);
-		return 1;
-	}
-}
-
-static int stats_pat_list(struct stream_interface *si)
-{
-	struct appctx *appctx = __objt_appctx(si->end);
-
-	switch (appctx->st2) {
-
-	case STAT_ST_INIT:
-		/* Init to the first entry. The list cannot be change */
-		appctx->ctx.map.elt = LIST_NEXT(&appctx->ctx.map.ref->head,
-		                                struct pat_ref_elt *, list);
-		if (&appctx->ctx.map.elt->list == &appctx->ctx.map.ref->head)
-			appctx->ctx.map.elt = NULL;
-		appctx->st2 = STAT_ST_LIST;
-		/* fall through */
-
-	case STAT_ST_LIST:
-		while (appctx->ctx.map.elt) {
-			chunk_reset(&trash);
-
-			/* build messages */
-			if (appctx->ctx.map.elt->sample)
-				chunk_appendf(&trash, "%p %s %s\n",
-				              appctx->ctx.map.elt, appctx->ctx.map.elt->pattern,
-				              appctx->ctx.map.elt->sample);
-			else
-				chunk_appendf(&trash, "%p %s\n",
-				              appctx->ctx.map.elt, appctx->ctx.map.elt->pattern);
-
-			if (bi_putchk(si_ic(si), &trash) == -1) {
-				/* let's try again later from this stream. We add ourselves into
-				 * this stream's users so that it can remove us upon termination.
-				 */
-				si_applet_cant_put(si);
-				return 0;
-			}
-
-			/* get next list entry and check the end of the list */
-			appctx->ctx.map.elt = LIST_NEXT(&appctx->ctx.map.elt->list,
-			                                struct pat_ref_elt *, list);
-			if (&appctx->ctx.map.elt->list == &appctx->ctx.map.ref->head)
-				break;
-		}
-
-		appctx->st2 = STAT_ST_FIN;
-		/* fall through */
-
-	default:
-		appctx->st2 = STAT_ST_FIN;
-		return 1;
-	}
-}
-
 /* This function dumps all streams' states onto the stream interface's
  * read buffer. It returns 0 if the output buffer is full and it needs
  * to be called again, otherwise non-zero. It is designed to be called
@@ -3757,10 +3090,6 @@
 		free(appctx->ctx.cli.err);
 		appctx->ctx.cli.err = NULL;
 	}
-	else if (appctx->st0 == STAT_CLI_O_MLOOK) {
-		free(appctx->ctx.map.chunk.str);
-		appctx->ctx.map.chunk.str = NULL;
-	}
 }
 
 /* This function is used to either dump tables states (when action is set
diff --git a/src/map.c b/src/map.c
index ac1c00a..aabaa60 100644
--- a/src/map.c
+++ b/src/map.c
@@ -15,13 +15,19 @@
 
 #include <common/standard.h>
 
+#include <types/applet.h>
+#include <types/cli.h>
 #include <types/global.h>
 #include <types/map.h>
 #include <types/pattern.h>
+#include <types/stats.h>
 
+#include <proto/applet.h>
 #include <proto/arg.h>
+#include <proto/cli.h>
 #include <proto/map.h>
 #include <proto/pattern.h>
+#include <proto/stream_interface.h>
 #include <proto/sample.h>
 
 /* Parse an IPv4 or IPv6 address and store it into the sample.
@@ -230,6 +236,688 @@
 	return 1;
 }
 
+/* This function is used with map and acl management. It permits to browse
+ * each reference. The variable <getnext> must contain the current node,
+ * <end> point to the root node and the <flags> permit to filter required
+ * nodes.
+ */
+static inline
+struct pat_ref *pat_list_get_next(struct pat_ref *getnext, struct list *end,
+                                  unsigned int flags)
+{
+	struct pat_ref *ref = getnext;
+
+	while (1) {
+
+		/* Get next list entry. */
+		ref = LIST_NEXT(&ref->list, struct pat_ref *, list);
+
+		/* If the entry is the last of the list, return NULL. */
+		if (&ref->list == end)
+			return NULL;
+
+		/* If the entry match the flag, return it. */
+		if (ref->flags & flags)
+			return ref;
+	}
+}
+
+static inline
+struct pat_ref *pat_ref_lookup_ref(const char *reference)
+{
+	int id;
+	char *error;
+
+	/* If the reference starts by a '#', this is numeric id. */
+	if (reference[0] == '#') {
+		/* Try to convert the numeric id. If the conversion fails, the lookup fails. */
+		id = strtol(reference + 1, &error, 10);
+		if (*error != '\0')
+			return NULL;
+
+		/* Perform the unique id lookup. */
+		return pat_ref_lookupid(id);
+	}
+
+	/* Perform the string lookup. */
+	return pat_ref_lookup(reference);
+}
+
+/* This function is used with map and acl management. It permits to browse
+ * each reference.
+ */
+static inline
+struct pattern_expr *pat_expr_get_next(struct pattern_expr *getnext, struct list *end)
+{
+	struct pattern_expr *expr;
+	expr = LIST_NEXT(&getnext->list, struct pattern_expr *, list);
+	if (&expr->list == end)
+		return NULL;
+	return expr;
+}
+
+static int cli_io_handler_pat_list(struct appctx *appctx)
+{
+	struct stream_interface *si = appctx->owner;
+
+	switch (appctx->st2) {
+
+	case STAT_ST_INIT:
+		/* Init to the first entry. The list cannot be change */
+		appctx->ctx.map.elt = LIST_NEXT(&appctx->ctx.map.ref->head,
+		                                struct pat_ref_elt *, list);
+		if (&appctx->ctx.map.elt->list == &appctx->ctx.map.ref->head)
+			appctx->ctx.map.elt = NULL;
+		appctx->st2 = STAT_ST_LIST;
+		/* fall through */
+
+	case STAT_ST_LIST:
+		while (appctx->ctx.map.elt) {
+			chunk_reset(&trash);
+
+			/* build messages */
+			if (appctx->ctx.map.elt->sample)
+				chunk_appendf(&trash, "%p %s %s\n",
+				              appctx->ctx.map.elt, appctx->ctx.map.elt->pattern,
+				              appctx->ctx.map.elt->sample);
+			else
+				chunk_appendf(&trash, "%p %s\n",
+				              appctx->ctx.map.elt, appctx->ctx.map.elt->pattern);
+
+			if (bi_putchk(si_ic(si), &trash) == -1) {
+				/* let's try again later from this stream. We add ourselves into
+				 * this stream's users so that it can remove us upon termination.
+				 */
+				si_applet_cant_put(si);
+				return 0;
+			}
+
+			/* get next list entry and check the end of the list */
+			appctx->ctx.map.elt = LIST_NEXT(&appctx->ctx.map.elt->list,
+			                                struct pat_ref_elt *, list);
+			if (&appctx->ctx.map.elt->list == &appctx->ctx.map.ref->head)
+				break;
+		}
+
+		appctx->st2 = STAT_ST_FIN;
+		/* fall through */
+
+	default:
+		appctx->st2 = STAT_ST_FIN;
+		return 1;
+	}
+}
+
+static int cli_io_handler_pats_list(struct appctx *appctx)
+{
+	struct stream_interface *si = appctx->owner;
+
+	switch (appctx->st2) {
+	case STAT_ST_INIT:
+		/* Display the column headers. If the message cannot be sent,
+		 * quit the fucntion with returning 0. The function is called
+		 * later and restart at the state "STAT_ST_INIT".
+		 */
+		chunk_reset(&trash);
+		chunk_appendf(&trash, "# id (file) description\n");
+		if (bi_putchk(si_ic(si), &trash) == -1) {
+			si_applet_cant_put(si);
+			return 0;
+		}
+
+		/* Now, we start the browsing of the references lists.
+		 * Note that the following call to LIST_ELEM return bad pointer. The only
+		 * available field of this pointer is <list>. It is used with the function
+		 * pat_list_get_next() for retruning the first available entry
+		 */
+		appctx->ctx.map.ref = LIST_ELEM(&pattern_reference, struct pat_ref *, list);
+		appctx->ctx.map.ref = pat_list_get_next(appctx->ctx.map.ref, &pattern_reference,
+		                                        appctx->ctx.map.display_flags);
+		appctx->st2 = STAT_ST_LIST;
+		/* fall through */
+
+	case STAT_ST_LIST:
+		while (appctx->ctx.map.ref) {
+			chunk_reset(&trash);
+
+			/* Build messages. If the reference is used by another category than
+			 * the listed categorie, display the information in the massage.
+			 */
+			chunk_appendf(&trash, "%d (%s) %s\n", appctx->ctx.map.ref->unique_id,
+			              appctx->ctx.map.ref->reference ? appctx->ctx.map.ref->reference : "",
+			              appctx->ctx.map.ref->display);
+
+			if (bi_putchk(si_ic(si), &trash) == -1) {
+				/* let's try again later from this stream. We add ourselves into
+				 * this stream's users so that it can remove us upon termination.
+				 */
+				si_applet_cant_put(si);
+				return 0;
+			}
+
+			/* get next list entry and check the end of the list */
+			appctx->ctx.map.ref = pat_list_get_next(appctx->ctx.map.ref, &pattern_reference,
+			                                        appctx->ctx.map.display_flags);
+		}
+
+		appctx->st2 = STAT_ST_FIN;
+		/* fall through */
+
+	default:
+		appctx->st2 = STAT_ST_FIN;
+		return 1;
+	}
+	return 0;
+}
+
+static int cli_io_handler_map_lookup(struct appctx *appctx)
+{
+	struct stream_interface *si = appctx->owner;
+	struct sample sample;
+	struct pattern *pat;
+	int match_method;
+
+	switch (appctx->st2) {
+	case STAT_ST_INIT:
+		/* Init to the first entry. The list cannot be change */
+		appctx->ctx.map.expr = LIST_ELEM(&appctx->ctx.map.ref->pat, struct pattern_expr *, list);
+		appctx->ctx.map.expr = pat_expr_get_next(appctx->ctx.map.expr, &appctx->ctx.map.ref->pat);
+		appctx->st2 = STAT_ST_LIST;
+		/* fall through */
+
+	case STAT_ST_LIST:
+		/* for each lookup type */
+		while (appctx->ctx.map.expr) {
+			/* initialise chunk to build new message */
+			chunk_reset(&trash);
+
+			/* execute pattern matching */
+			sample.data.type = SMP_T_STR;
+			sample.flags = SMP_F_CONST;
+			sample.data.u.str.len = appctx->ctx.map.chunk.len;
+			sample.data.u.str.str = appctx->ctx.map.chunk.str;
+			if (appctx->ctx.map.expr->pat_head->match &&
+			    sample_convert(&sample, appctx->ctx.map.expr->pat_head->expect_type))
+				pat = appctx->ctx.map.expr->pat_head->match(&sample, appctx->ctx.map.expr, 1);
+			else
+				pat = NULL;
+
+			/* build return message: set type of match */
+			for (match_method=0; match_method<PAT_MATCH_NUM; match_method++)
+				if (appctx->ctx.map.expr->pat_head->match == pat_match_fcts[match_method])
+					break;
+			if (match_method >= PAT_MATCH_NUM)
+				chunk_appendf(&trash, "type=unknown(%p)", appctx->ctx.map.expr->pat_head->match);
+			else
+				chunk_appendf(&trash, "type=%s", pat_match_names[match_method]);
+
+			/* case sensitive */
+			if (appctx->ctx.map.expr->mflags & PAT_MF_IGNORE_CASE)
+				chunk_appendf(&trash, ", case=insensitive");
+			else
+				chunk_appendf(&trash, ", case=sensitive");
+
+			/* Display no match, and set default value */
+			if (!pat) {
+				if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+					chunk_appendf(&trash, ", found=no");
+				else
+					chunk_appendf(&trash, ", match=no");
+			}
+
+			/* Display match and match info */
+			else {
+				/* display match */
+				if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+					chunk_appendf(&trash, ", found=yes");
+				else
+					chunk_appendf(&trash, ", match=yes");
+
+				/* display index mode */
+				if (pat->sflags & PAT_SF_TREE)
+					chunk_appendf(&trash, ", idx=tree");
+				else
+					chunk_appendf(&trash, ", idx=list");
+
+				/* display pattern */
+				if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
+					if (pat->ref && pat->ref->pattern)
+						chunk_appendf(&trash, ", key=\"%s\"", pat->ref->pattern);
+					else
+						chunk_appendf(&trash, ", key=unknown");
+				}
+				else {
+					if (pat->ref && pat->ref->pattern)
+						chunk_appendf(&trash, ", pattern=\"%s\"", pat->ref->pattern);
+					else
+						chunk_appendf(&trash, ", pattern=unknown");
+				}
+
+				/* display return value */
+				if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
+					if (pat->data && pat->ref && pat->ref->sample)
+						chunk_appendf(&trash, ", value=\"%s\", type=\"%s\"", pat->ref->sample,
+						              smp_to_type[pat->data->type]);
+					else
+						chunk_appendf(&trash, ", value=none");
+				}
+			}
+
+			chunk_appendf(&trash, "\n");
+
+			/* display response */
+			if (bi_putchk(si_ic(si), &trash) == -1) {
+				/* let's try again later from this stream. We add ourselves into
+				 * this stream's users so that it can remove us upon termination.
+				 */
+				si_applet_cant_put(si);
+				return 0;
+			}
+
+			/* get next entry */
+			appctx->ctx.map.expr = pat_expr_get_next(appctx->ctx.map.expr,
+			                                         &appctx->ctx.map.ref->pat);
+		}
+
+		appctx->st2 = STAT_ST_FIN;
+		/* fall through */
+
+	default:
+		appctx->st2 = STAT_ST_FIN;
+		free(appctx->ctx.map.chunk.str);
+		return 1;
+	}
+}
+
+static void cli_release_mlook(struct appctx *appctx)
+{
+	free(appctx->ctx.map.chunk.str);
+	appctx->ctx.map.chunk.str = NULL;
+}
+
+
+static int cli_parse_get_map(char **args, struct appctx *appctx, void *private)
+{
+	if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
+		/* Set flags. */
+		if (args[1][0] == 'm')
+			appctx->ctx.map.display_flags = PAT_REF_MAP;
+		else
+			appctx->ctx.map.display_flags = PAT_REF_ACL;
+
+		/* No parameter. */
+		if (!*args[2] || !*args[3]) {
+			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+				appctx->ctx.cli.msg = "Missing map identifier and/or key.\n";
+			else
+				appctx->ctx.cli.msg = "Missing ACL identifier and/or key.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+
+		/* lookup into the maps */
+		appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
+		if (!appctx->ctx.map.ref) {
+			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+				appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
+			else
+				appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+
+		/* copy input string. The string must be allocated because
+		 * it may be used over multiple iterations. It's released
+		 * at the end and upon abort anyway.
+		 */
+		appctx->ctx.map.chunk.len = strlen(args[3]);
+		appctx->ctx.map.chunk.size = appctx->ctx.map.chunk.len + 1;
+		appctx->ctx.map.chunk.str = strdup(args[3]);
+		if (!appctx->ctx.map.chunk.str) {
+			appctx->ctx.cli.msg = "Out of memory error.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+
+		return 0;
+	}
+	return 1;
+}
+
+static int cli_parse_show_map(char **args, struct appctx *appctx, void *private)
+{
+	if (strcmp(args[1], "map") == 0 ||
+	    strcmp(args[1], "acl") == 0) {
+
+		/* Set ACL or MAP flags. */
+		if (args[1][0] == 'm')
+			appctx->ctx.map.display_flags = PAT_REF_MAP;
+		else
+			appctx->ctx.map.display_flags = PAT_REF_ACL;
+
+		/* no parameter: display all map available */
+		if (!*args[2]) {
+			appctx->st2 = STAT_ST_INIT;
+			appctx->st0 = STAT_CLI_O_CUSTOM;
+			appctx->io_handler = cli_io_handler_pats_list;
+			return 0;
+		}
+
+		/* lookup into the refs and check the map flag */
+		appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
+		if (!appctx->ctx.map.ref ||
+		    !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
+			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+				appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
+			else
+				appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+		appctx->st2 = STAT_ST_INIT;
+		appctx->st0 = STAT_CLI_O_CUSTOM;
+		appctx->io_handler = cli_io_handler_pat_list;
+		return 0;
+	}
+
+	return 0;
+}
+
+static int cli_parse_set_map(char **args, struct appctx *appctx, void *private)
+{
+	if (strcmp(args[1], "map") == 0) {
+		char *err;
+
+		/* Set flags. */
+		appctx->ctx.map.display_flags = PAT_REF_MAP;
+
+		/* Expect three parameters: map name, key and new value. */
+		if (!*args[2] || !*args[3] || !*args[4]) {
+			appctx->ctx.cli.msg = "'set map' expects three parameters: map identifier, key and value.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+
+		/* Lookup the reference in the maps. */
+		appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
+		if (!appctx->ctx.map.ref) {
+			appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+
+		/* If the entry identifier start with a '#', it is considered as
+		 * pointer id
+		 */
+		if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
+			struct pat_ref_elt *ref;
+			long long int conv;
+			char *error;
+
+			/* Convert argument to integer value. */
+			conv = strtoll(&args[3][1], &error, 16);
+			if (*error != '\0') {
+				appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
+				appctx->st0 = STAT_CLI_PRINT;
+				return 1;
+			}
+
+			/* Convert and check integer to pointer. */
+			ref = (struct pat_ref_elt *)(long)conv;
+			if ((long long int)(long)ref != conv) {
+				appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
+				appctx->st0 = STAT_CLI_PRINT;
+				return 1;
+			}
+
+			/* Try to delete the entry. */
+			err = NULL;
+			if (!pat_ref_set_by_id(appctx->ctx.map.ref, ref, args[4], &err)) {
+				if (err)
+					memprintf(&err, "%s.\n", err);
+				appctx->ctx.cli.err = err;
+				appctx->st0 = STAT_CLI_PRINT_FREE;
+				return 1;
+			}
+		}
+		else {
+			/* Else, use the entry identifier as pattern
+			 * string, and update the value.
+			 */
+			err = NULL;
+			if (!pat_ref_set(appctx->ctx.map.ref, args[3], args[4], &err)) {
+				if (err)
+					memprintf(&err, "%s.\n", err);
+				appctx->ctx.cli.err = err;
+				appctx->st0 = STAT_CLI_PRINT_FREE;
+				return 1;
+			}
+		}
+
+		/* The set is done, send message. */
+		appctx->st0 = STAT_CLI_PROMPT;
+		return 0;
+	}
+	return 1;
+}
+
+static int cli_parse_add_map(char **args, struct appctx *appctx, void *private)
+{
+	if (strcmp(args[1], "map") == 0 ||
+	    strcmp(args[1], "acl") == 0) {
+		int ret;
+		char *err;
+
+		/* Set flags. */
+		if (args[1][0] == 'm')
+			appctx->ctx.map.display_flags = PAT_REF_MAP;
+		else
+			appctx->ctx.map.display_flags = PAT_REF_ACL;
+
+		/* If the keywork is "map", we expect three parameters, if it
+		 * is "acl", we expect only two parameters
+		 */
+		if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
+			if (!*args[2] || !*args[3] || !*args[4]) {
+				appctx->ctx.cli.msg = "'add map' expects three parameters: map identifier, key and value.\n";
+				appctx->st0 = STAT_CLI_PRINT;
+				return 1;
+			}
+		}
+		else {
+			if (!*args[2] || !*args[3]) {
+				appctx->ctx.cli.msg = "'add acl' expects two parameters: ACL identifier and pattern.\n";
+				appctx->st0 = STAT_CLI_PRINT;
+				return 1;
+			}
+		}
+
+		/* Lookup for the reference. */
+		appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
+		if (!appctx->ctx.map.ref) {
+			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+				appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
+			else
+				appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+
+		/* The command "add acl" is prohibited if the reference
+		 * use samples.
+		 */
+		if ((appctx->ctx.map.display_flags & PAT_REF_ACL) &&
+		    (appctx->ctx.map.ref->flags & PAT_REF_SMP)) {
+			appctx->ctx.cli.msg = "This ACL is shared with a map containing samples. "
+				"You must use the command 'add map' to add values.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+
+		/* Add value. */
+		err = NULL;
+		if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+			ret = pat_ref_add(appctx->ctx.map.ref, args[3], args[4], &err);
+		else
+			ret = pat_ref_add(appctx->ctx.map.ref, args[3], NULL, &err);
+		if (!ret) {
+			if (err)
+				memprintf(&err, "%s.\n", err);
+			appctx->ctx.cli.err = err;
+			appctx->st0 = STAT_CLI_PRINT_FREE;
+			return 1;
+		}
+
+		/* The add is done, send message. */
+		appctx->st0 = STAT_CLI_PROMPT;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int cli_parse_del_map(char **args, struct appctx *appctx, void *private)
+{
+	if (args[1][0] == 'm')
+		appctx->ctx.map.display_flags = PAT_REF_MAP;
+	else
+		appctx->ctx.map.display_flags = PAT_REF_ACL;
+
+	/* Expect two parameters: map name and key. */
+	if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
+		if (!*args[2] || !*args[3]) {
+			appctx->ctx.cli.msg = "This command expects two parameters: map identifier and key.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+	}
+
+	else {
+		if (!*args[2] || !*args[3]) {
+			appctx->ctx.cli.msg = "This command expects two parameters: ACL identifier and key.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+	}
+
+	/* Lookup the reference in the maps. */
+	appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
+	if (!appctx->ctx.map.ref ||
+	    !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
+		appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
+		appctx->st0 = STAT_CLI_PRINT;
+		return 1;
+	}
+
+	/* If the entry identifier start with a '#', it is considered as
+	 * pointer id
+	 */
+	if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
+		struct pat_ref_elt *ref;
+		long long int conv;
+		char *error;
+
+		/* Convert argument to integer value. */
+		conv = strtoll(&args[3][1], &error, 16);
+		if (*error != '\0') {
+			appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+
+		/* Convert and check integer to pointer. */
+		ref = (struct pat_ref_elt *)(long)conv;
+		if ((long long int)(long)ref != conv) {
+			appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+
+		/* Try to delete the entry. */
+		if (!pat_ref_delete_by_id(appctx->ctx.map.ref, ref)) {
+			/* The entry is not found, send message. */
+			appctx->ctx.cli.msg = "Key not found.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+	}
+	else {
+		/* Else, use the entry identifier as pattern
+		 * string and try to delete the entry.
+		 */
+		if (!pat_ref_delete(appctx->ctx.map.ref, args[3])) {
+			/* The entry is not found, send message. */
+			appctx->ctx.cli.msg = "Key not found.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+	}
+
+	/* The deletion is done, send message. */
+	appctx->st0 = STAT_CLI_PROMPT;
+	return 1;
+}
+
+
+static int cli_parse_clear_map(char **args, struct appctx *appctx, void *private)
+{
+	if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
+		/* Set ACL or MAP flags. */
+		if (args[1][0] == 'm')
+			appctx->ctx.map.display_flags = PAT_REF_MAP;
+		else
+			appctx->ctx.map.display_flags = PAT_REF_ACL;
+
+		/* no parameter */
+		if (!*args[2]) {
+			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+				appctx->ctx.cli.msg = "Missing map identifier.\n";
+			else
+				appctx->ctx.cli.msg = "Missing ACL identifier.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+
+		/* lookup into the refs and check the map flag */
+		appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
+		if (!appctx->ctx.map.ref ||
+		    !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
+			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+				appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
+			else
+				appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
+			appctx->st0 = STAT_CLI_PRINT;
+			return 1;
+		}
+
+		/* Clear all. */
+		pat_ref_prune(appctx->ctx.map.ref);
+
+		/* return response */
+		appctx->st0 = STAT_CLI_PROMPT;
+		return 1;
+	}
+	return 0;
+}
+
+/* register cli keywords */
+
+static struct cli_kw_list cli_kws = {{ },{
+	{ { "add",   "acl", NULL }, "add acl        : add acl entry", cli_parse_add_map, NULL },
+	{ { "clear", "acl", NULL }, "clear acl <id> : clear the content of this acl", cli_parse_clear_map, NULL },
+	{ { "del",   "acl", NULL }, "del acl        : delete acl entry", cli_parse_del_map, NULL },
+	{ { "get",   "acl", NULL }, "get acl        : report the patterns matching a sample for an ACL", cli_parse_get_map, cli_io_handler_map_lookup, cli_release_mlook },
+	{ { "show",  "acl", NULL }, "show acl [id]  : report available acls or dump an acl's contents", cli_parse_show_map, NULL },
+	{ { "add",   "map", NULL }, "add map        : add map entry", cli_parse_add_map, NULL },
+	{ { "clear", "map", NULL }, "clear map <id> : clear the content of this map", cli_parse_clear_map, NULL },
+	{ { "del",   "map", NULL }, "del map        : delete map entry", cli_parse_del_map, NULL },
+	{ { "get",   "map", NULL }, "get map        : report the keys and values matching a sample for a map", cli_parse_get_map, NULL },
+	{ { "set",   "map", NULL }, "set map        : modify map entry", cli_parse_set_map, NULL },
+	{ { "show",  "map", NULL }, "show map [id]  : report available maps or dump a map's contents", cli_parse_show_map, NULL },
+	{ { NULL }, NULL, NULL, NULL }
+}};
+
+
 /* Note: must not be declared <const> as its list will be overwritten
  *
  * For the map_*_int keywords, the output is declared as SMP_T_UINT, but the converter function
@@ -287,4 +975,5 @@
 {
 	/* register format conversion keywords */
 	sample_register_convs(&sample_conv_kws);
+	cli_register_kw(&cli_kws);
 }