MINOR: arg: Be able to forbid unresolved args when building an argument list

In make_arg_list() function, unresolved dependencies are pushed in an
argument list to be resolved later, during the configuration validity
check. It is now possible to forbid such unresolved dependencies by omitting
<al> parameter (setting it to NULL). It is usefull when the parsing context
is not the same than the running context or when the parsing context is lost
after the startup stage. For instance, an argument may be defined in
defaults section during parsing and executed in a frontend/backend section.

(cherry picked from commit 35926a16ac087ac737026206a8420a65a9fce0ca)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/acl.c b/src/acl.c
index 193788c..e3fcf62 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -123,7 +123,8 @@
 /* Parse an ACL expression starting at <args>[0], and return it. If <err> is
  * not NULL, it will be filled with a pointer to an error message in case of
  * error. This pointer must be freeable or NULL. <al> is an arg_list serving
- * as a list head to report missing dependencies.
+ * as a list head to report missing dependencies. It may be NULL if such
+ * dependencies are not allowed.
  *
  * Right now, the only accepted syntax is :
  * <subject> [<value>...]
@@ -164,9 +165,11 @@
 	 * keyword.
 	 */
 
-	al->ctx  = ARGC_ACL;   // to report errors while resolving args late
-	al->kw   = *args;
-	al->conv = NULL;
+	if (al) {
+		al->ctx  = ARGC_ACL;   // to report errors while resolving args late
+		al->kw   = *args;
+		al->conv = NULL;
+	}
 
 	aclkw = find_acl_kw(args[0]);
 	if (aclkw) {
@@ -282,8 +285,10 @@
 			conv_expr->conv = conv;
 			acl_conv_found = 1;
 
-			al->kw = smp->fetch->kw;
-			al->conv = conv_expr->conv->kw;
+			if (al) {
+				al->kw = smp->fetch->kw;
+				al->conv = conv_expr->conv->kw;
+			}
 			argcnt = make_arg_list(endw, -1, conv->arg_mask, &conv_expr->arg_p, err, &arg, &err_arg, al);
 			if (argcnt < 0) {
 				memprintf(err, "ACL keyword '%s' : invalid arg %d in converter '%s' : %s.",
@@ -667,7 +672,7 @@
  * an anonymous one and it won't be merged with any other one. If <err> is not
  * NULL, it will be filled with an appropriate error. This pointer must be
  * freeable or NULL. <al> is the arg_list serving as a head for unresolved
- * dependencies.
+ * dependencies. It may be NULL if such dependencies are not allowed.
  *
  * args syntax: <aclname> <acl_expr>
  */
@@ -780,7 +785,8 @@
  * If <known_acl> is not NULL, the ACL will be queued at its tail. If <err> is
  * not NULL, it will be filled with an error message if an error occurs. This
  * pointer must be freeable or NULL. <al> is an arg_list serving as a list head
- * to report missing dependencies.
+ * to report missing dependencies. It may be NULL if such dependencies are not
+ * allowed.
  */
 static struct acl *find_acl_default(const char *acl_name, struct list *known_acl,
                                     char **err, struct arg_list *al,
@@ -860,7 +866,8 @@
  * <err> is not NULL, it will be filled with a pointer to an error message in
  * case of error, that the caller is responsible for freeing. The initial
  * location must either be freeable or NULL. The list <al> serves as a list head
- * for unresolved dependencies.
+ * for unresolved dependencies. It may be NULL if such dependencies are not
+ * allowed.
  */
 struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl,
                                 enum acl_cond_pol pol, char **err, struct arg_list *al,
diff --git a/src/arg.c b/src/arg.c
index d44f268..4e40aae 100644
--- a/src/arg.c
+++ b/src/arg.c
@@ -96,14 +96,15 @@
  * The returned array is always terminated by an arg of type ARGT_STOP (0),
  * unless the mask indicates that no argument is supported. Unresolved arguments
  * are appended to arg list <al>, which also serves as a template to create new
- * entries. The mask is composed of a number of mandatory arguments in its lower
- * ARGM_BITS bits, and a concatenation of each argument type in each subsequent
- * ARGT_BITS-bit sblock. If <err_msg> is not NULL, it must point to a freeable
- * or NULL pointer. The caller is expected to restart the parsing from the new
- * pointer set in <end_ptr>, which is the first character considered as not
- * being part of the arg list. The input string ends on the first between <len>
- * characters (when len is positive) or the first NUL character. Placing -1 in
- * <len> will make it virtually unbounded (~2GB long strings).
+ * entries. <al> may be NULL if unresolved arguments are not allowed. The mask
+ * is composed of a number of mandatory arguments in its lower ARGM_BITS bits,
+ * and a concatenation of each argument type in each subsequent ARGT_BITS-bit
+ * sblock. If <err_msg> is not NULL, it must point to a freeable or NULL
+ * pointer. The caller is expected to restart the parsing from the new pointer
+ * set in <end_ptr>, which is the first character considered as not being part
+ * of the arg list. The input string ends on the first between <len> characters
+ * (when len is positive) or the first NUL character. Placing -1 in <len> will
+ * make it virtually unbounded (~2GB long strings).
  */
 int make_arg_list(const char *in, int len, uint64_t mask, struct arg **argp,
                   char **err_msg, const char **end_ptr, int *err_arg,
@@ -244,6 +245,8 @@
 			/* These argument types need to be stored as strings during
 			 * parsing then resolved later.
 			 */
+			if (!al)
+				goto resolve_err;
 			arg->unresolved = 1;
 			new_al = arg_list_add(al, arg, pos);
 
@@ -445,4 +448,9 @@
 alloc_err:
 	memprintf(err_msg, "out of memory");
 	goto err;
+
+ resolve_err:
+	memprintf(err_msg, "unresolved argument of type '%s' at position %d not allowed",
+	          arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1);
+	goto err;
 }
diff --git a/src/sample.c b/src/sample.c
index 075763a..c010580 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -861,7 +861,9 @@
  * Parse a sample expression configuration:
  *        fetch keyword followed by format conversion keywords.
  * Returns a pointer on allocated sample expression structure.
- * The caller must have set al->ctx.
+ * <al> is an arg_list serving as a list head to report missing dependencies.
+ * It may be NULL if such dependencies are not allowed. Otherwise, the caller
+ * must have set al->ctx if al is set.
  * If <endptr> is non-nul, it will be set to the first unparsed character
  * (which may be the final '\0') on success. If it is nul, the expression
  * must be properly terminated by a '\0' otherwise an error is reported.
@@ -920,8 +922,10 @@
 	 * this allows it to automatically create entries for mandatory
 	 * implicit arguments (eg: local proxy name).
 	 */
-	al->kw = expr->fetch->kw;
-	al->conv = NULL;
+	if (al) {
+		al->kw = expr->fetch->kw;
+		al->conv = NULL;
+	}
 	if (make_arg_list(endw, -1, fetch->arg_mask, &expr->arg_p, err_msg, &endt, &err_arg, al) < 0) {
 		memprintf(err_msg, "fetch method '%s' : %s", fkw, *err_msg);
 		goto out_error;
@@ -1021,8 +1025,10 @@
 		LIST_APPEND(&(expr->conv_exprs), &(conv_expr->list));
 		conv_expr->conv = conv;
 
-		al->kw = expr->fetch->kw;
-		al->conv = conv_expr->conv->kw;
+		if (al) {
+			al->kw = expr->fetch->kw;
+			al->conv = conv_expr->conv->kw;
+		}
 		argcnt = make_arg_list(endw, -1, conv->arg_mask, &conv_expr->arg_p, err_msg, &endt, &err_arg, al);
 		if (argcnt < 0) {
 			memprintf(err_msg, "invalid arg %d in converter '%s' : %s", err_arg+1, ckw, *err_msg);