MINOR: server: Add 'server-template' new keyword supported in backend sections.

This patch makes backend sections support 'server-template' new keyword.
Such 'server-template' objects are parsed similarly to a 'server' object
by parse_server() function, but its first arguments are as follows:
    server-template <ID prefix> <nb | range> <ip | fqdn>:<port> ...

The remaining arguments are the same as for 'server' lines.

With such server template declarations, servers may be allocated with IDs
built from <ID prefix> and <nb | range> arguments.

For instance declaring:
    server-template foo 1-5 google.com:80 ...
or
    server-template foo 5 google.com:80 ...

would be equivalent to declare:
    server foo1 google.com:80 ...
    server foo2 google.com:80 ...
    server foo3 google.com:80 ...
    server foo4 google.com:80 ...
    server foo5 google.com:80 ...
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 348b9e8..d44949a 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2859,7 +2859,9 @@
 	curproxy->conf.args.line = linenum;
 
 	/* Now let's parse the proxy-specific keywords */
-	if (!strcmp(args[0], "server") || !strcmp(args[0], "default-server")) {
+	if (!strcmp(args[0], "server")         ||
+	    !strcmp(args[0], "default-server") ||
+	    !strcmp(args[0], "server-template")) {
 		err_code |= parse_server(file, linenum, args, curproxy, &defproxy);
 		if (err_code & ERR_FATAL)
 			goto out;
diff --git a/src/server.c b/src/server.c
index f25a7a0..9f07788 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1969,18 +1969,53 @@
 	return 0;
 }
 
+/*
+ * Parse as much as possible such a range string argument: low[-high]
+ * Set <nb_low> and <nb_high> values so that they may be reused by this loop
+ * for(int i = nb_low; i <= nb_high; i++)... with nb_low >= 1.
+ * Fails if 'low' < 0 or 'high' is present and not higher than 'low'.
+ * Returns 0 if succeeded, -1 if not.
+ */
+static int srv_tmpl_parse_range(struct server *srv, const char *arg, int *nb_low, int *nb_high)
+{
+	char *nb_high_arg;
+
+	*nb_high = 0;
+	chunk_printf(&trash, "%s", arg);
+	*nb_low = atoi(trash.str);
+
+	if ((nb_high_arg = strchr(trash.str, '-'))) {
+		*nb_high_arg++ = '\0';
+		*nb_high = atoi(nb_high_arg);
+	}
+	else {
+		*nb_high += *nb_low;
+		*nb_low = 1;
+	}
+
+	if (*nb_low < 0 || *nb_high < *nb_low)
+		return -1;
+
+	return 0;
+}
+
 int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, struct proxy *defproxy)
 {
 	struct server *newsrv = NULL;
-	const char *err;
+	const char *err = NULL;
 	char *errmsg = NULL;
 	int err_code = 0;
 	unsigned val;
 	char *fqdn = NULL;
 
-	if (!strcmp(args[0], "server") || !strcmp(args[0], "default-server")) {  /* server address */
+	if (!strcmp(args[0], "server")         ||
+	    !strcmp(args[0], "default-server") ||
+	    !strcmp(args[0], "server-template")) {
 		int cur_arg;
 		int defsrv = (*args[0] == 'd');
+		int srv = !defsrv && !strcmp(args[0], "server");
+		int srv_tmpl = !defsrv && !srv;
+		int tmpl_range_low = 0, tmpl_range_high = 0;
 
 		if (!defsrv && curproxy == defproxy) {
 			Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
@@ -1990,21 +2025,49 @@
 		else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
 			err_code |= ERR_ALERT | ERR_FATAL;
 
-		if (!defsrv && !*args[2]) {
-			Alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
-			      file, linenum, args[0]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
+		/* There is no mandatory first arguments for default server. */
+		if (srv) {
+			if (!*args[2]) {
+				/* 'server' line number of argument check. */
+				Alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
+					  file, linenum, args[0]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+
+			err = invalid_char(args[1]);
+		}
+		else if (srv_tmpl) {
+			if (!*args[3]) {
+				/* 'server-template' line number of argument check. */
+				Alert("parsing [%s:%d] : '%s' expects <prefix> <nb | range> <addr>[:<port>] as arguments.\n",
+					  file, linenum, args[0]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+
+			err = invalid_prefix_char(args[1]);
 		}
 
-		err = invalid_char(args[1]);
-		if (err && !defsrv) {
-			Alert("parsing [%s:%d] : character '%c' is not permitted in server name '%s'.\n",
-			      file, linenum, *err, args[1]);
+		if (err) {
+			Alert("parsing [%s:%d] : character '%c' is not permitted in %s %s '%s'.\n",
+			      file, linenum, *err, args[0], srv ? "name" : "prefix", args[1]);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
 
+		cur_arg = 2;
+		if (srv_tmpl) {
+			/* Parse server-template <nb | range> arg. */
+			if (srv_tmpl_parse_range(newsrv, args[cur_arg], &tmpl_range_low, &tmpl_range_high) < 0) {
+				Alert("parsing [%s:%d] : Wrong %s number or range arg '%s'.\n",
+					  file, linenum, args[0], args[cur_arg]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+			cur_arg++;
+		}
+
 		if (!defsrv) {
 			struct sockaddr_storage *sk;
 			int port1, port2, port;
@@ -2018,12 +2081,24 @@
 				goto out;
 			}
 
+			if (srv_tmpl) {
+				newsrv->tmpl_info.nb_low = tmpl_range_low;
+				newsrv->tmpl_info.nb_high = tmpl_range_high;
+			}
+
 			/* the servers are linked backwards first */
 			newsrv->next = curproxy->srv;
 			curproxy->srv = newsrv;
 			newsrv->conf.file = strdup(file);
 			newsrv->conf.line = linenum;
-			newsrv->id = strdup(args[1]);
+			/* Note: for a server template, its id is its prefix.
+			 * This is a temporary id which will be used for server allocations to come
+			 * after parsing.
+			 */
+			if (srv)
+				newsrv->id = strdup(args[1]);
+			else
+				newsrv->tmpl_info.prefix = strdup(args[1]);
 
 			/* several ways to check the port component :
 			 *  - IP    => port=+0, relative (IPv4 only)
@@ -2032,7 +2107,7 @@
 			 *  - IP:+N => port=+N, relative
 			 *  - IP:-N => port=-N, relative
 			 */
-			sk = str2sa_range(args[2], &port, &port1, &port2, &errmsg, NULL, &fqdn, 0);
+			sk = str2sa_range(args[cur_arg], &port, &port1, &port2, &errmsg, NULL, &fqdn, 0);
 			if (!sk) {
 				Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
 				err_code |= ERR_ALERT | ERR_FATAL;
@@ -2073,7 +2148,7 @@
 				goto skip_name_resolution;
 			if ((dns_str_to_dn_label(newsrv->hostname, curr_resolution->hostname_dn, curr_resolution->hostname_dn_len + 1)) == NULL) {
 				Alert("parsing [%s:%d] : Invalid hostname '%s'\n",
-				      file, linenum, args[2]);
+				      file, linenum, args[cur_arg]);
 				err_code |= ERR_ALERT | ERR_FATAL;
 				goto out;
 			}
@@ -2093,14 +2168,14 @@
 
 			if (!newsrv->hostname && !protocol_by_family(newsrv->addr.ss_family)) {
 				Alert("parsing [%s:%d] : Unknown protocol family %d '%s'\n",
-				      file, linenum, newsrv->addr.ss_family, args[2]);
+				      file, linenum, newsrv->addr.ss_family, args[cur_arg]);
 				err_code |= ERR_ALERT | ERR_FATAL;
 				goto out;
 			}
 
 			/* Copy default server settings to new server settings. */
 			srv_settings_cpy(newsrv, &curproxy->defsrv);
-			cur_arg = 3;
+			cur_arg++;
 		} else {
 			newsrv = &curproxy->defsrv;
 			cur_arg = 1;
diff --git a/src/standard.c b/src/standard.c
index 99f7066..6abedb4 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -592,17 +592,18 @@
 }
 
 /*
- * Checks <domainname> for invalid characters. Valid chars are [A-Za-z0-9_.-].
+ * Checks <name> for invalid characters. Valid chars are [_.-] and those
+ * accepted by <f> function.
  * If an invalid character is found, a pointer to it is returned.
  * If everything is fine, NULL is returned.
  */
-const char *invalid_domainchar(const char *name) {
+static inline const char *__invalid_char(const char *name, int (*f)(int)) {
 
 	if (!*name)
 		return name;
 
 	while (*name) {
-		if (!isalnum((int)(unsigned char)*name) && *name != '.' &&
+		if (!f((int)(unsigned char)*name) && *name != '.' &&
 		    *name != '_' && *name != '-')
 			return name;
 
@@ -613,6 +614,24 @@
 }
 
 /*
+ * Checks <name> for invalid characters. Valid chars are [A-Za-z0-9_.-].
+ * If an invalid character is found, a pointer to it is returned.
+ * If everything is fine, NULL is returned.
+ */
+const char *invalid_domainchar(const char *name) {
+	return __invalid_char(name, isalnum);
+}
+
+/*
+ * Checks <name> for invalid characters. Valid chars are [A-Za-z_.-].
+ * If an invalid character is found, a pointer to it is returned.
+ * If everything is fine, NULL is returned.
+ */
+const char *invalid_prefix_char(const char *name) {
+	return __invalid_char(name, isalpha);
+}
+
+/*
  * converts <str> to a struct sockaddr_storage* provided by the caller. The
  * caller must have zeroed <sa> first, and may have set sa->ss_family to force
  * parse a specific address format. If the ss_family is 0 or AF_UNSPEC, then