MINOR: map/acl: add the "prepare map/acl" CLI command

This command allocates a new version for the map/acl, that will be usable
later to prepare the addition of new values to atomically replace existing
ones. Technically speaking the operation consists in atomically incrementing
the next version. There's no "undo" operation here, if a version is not
committed, it will automatically be trashed when committing a newer version.
diff --git a/doc/management.txt b/doc/management.txt
index 6ccd468..96660e7 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1806,6 +1806,30 @@
   added to a directory or a crt-list. This command should be used in
   combination with "set ssl cert" and "add ssl crt-list".
 
+prepare acl <acl>
+  Allocate a new version number in ACL <acl> for atomic replacement. <acl> is
+  the #<id> or the <file> returned by "show acl". The new version number is
+  shown in response after "New version created:". This number will then be
+  usable to prepare additions of new entries into the ACL which will then
+  atomically replace the current ones once committed. It is reported as
+  "next_ver" in "show acl". There is no impact of allocating new versions, as
+  unused versions will automatically be removed once a more recent version is
+  committed. Version numbers are unsigned 32-bit values which wrap at the end,
+  so care must be taken when comparing them in an external program. This
+  command cannot be used if the reference <acl> is a file also used as a map.
+  In this case, the "prepare map" command must be used instead.
+
+prepare map <map>
+  Allocate a new version number in map <map> for atomic replacement. <map> is
+  the #<id> or the <file> returned by "show map". The new version number is
+  shown in response after "New version created:". This number will then be
+  usable to prepare additions of new entries into the map which will then
+  atomically replace the current ones once committed. It is reported as
+  "next_ver" in "show map". There is no impact of allocating new versions, as
+  unused versions will automatically be removed once a more recent version is
+  committed. Version numbers are unsigned 32-bit values which wrap at the end,
+  so care must be taken when comparing them in an external program.
+
 prompt
   Toggle the prompt at the beginning of the line and enter or leave interactive
   mode. In interactive mode, the connection is not closed after a command
diff --git a/src/map.c b/src/map.c
index 42b3bdc..8ac261a 100644
--- a/src/map.c
+++ b/src/map.c
@@ -11,6 +11,7 @@
  */
 
 #include <stdio.h>
+#include <syslog.h>
 
 #include <haproxy/api.h>
 #include <haproxy/applet-t.h>
@@ -631,6 +632,35 @@
 	return 1;
 }
 
+static int cli_parse_prepare_map(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	if (strcmp(args[1], "map") == 0 ||
+	    strcmp(args[1], "acl") == 0) {
+		uint next_gen;
+		char *msg = NULL;
+
+		/* 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;
+
+		/* 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)
+				return cli_err(appctx, "Unknown map identifier. Please use #<id> or <file>.\n");
+			else
+				return cli_err(appctx, "Unknown ACL identifier. Please use #<id> or <file>.\n");
+		}
+		next_gen = pat_ref_newgen(appctx->ctx.map.ref);
+		return cli_dynmsg(appctx, LOG_INFO, memprintf(&msg, "New version created: %u\n", next_gen));
+	}
+
+	return 0;
+}
+
 static void cli_release_show_map(struct appctx *appctx)
 {
 	if (appctx->st2 == STAT_ST_LIST) {
@@ -1017,11 +1047,13 @@
 	{ { "clear", "acl", NULL }, "clear acl [@ver] <id> : clear the content of this acl", cli_parse_clear_map, cli_io_handler_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 },
+	{ { "prepare","acl",NULL }, "prepare acl <id>: prepare a new version for atomic ACL replacement", cli_parse_prepare_map, NULL },
 	{ { "show",  "acl", NULL }, "show acl [@ver] [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 [@ver] <id> : clear the content of this map", cli_parse_clear_map, cli_io_handler_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, cli_io_handler_map_lookup, cli_release_mlook },
+	{ { "prepare","map",NULL }, "prepare map <id>: prepare a new version for atomic map replacement", cli_parse_prepare_map, NULL },
 	{ { "set",   "map", NULL }, "set map        : modify map entry", cli_parse_set_map, NULL },
 	{ { "show",  "map", NULL }, "show map [@ver] [id] : report available maps or dump a map's contents", cli_parse_show_map, NULL },
 	{ { NULL }, NULL, NULL, NULL }