MINOR: cfgcond: support terms made of parenthesis around expressions

Now it's possible to form a term using parenthesis around an expression.
This will soon allow to build more complex expressions. For now they're
still pretty limited but parenthesis do work.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index a66350f..1b42a3c 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -811,6 +811,7 @@
   - the integer zero ('0'), always returns "false"
   - a non-nul integer (e.g. '1'), always returns "true".
   - a predicate optionally followed by argument(s) in parenthesis.
+  - a condition placed between a pair of parenthesis '(' and ')'
   - a question mark ('!') preceeding any of the non-empty elements above, and
     which will negate its status.
   - expressions combined with a logical AND ('&&'), which will be evaluated
@@ -861,7 +862,7 @@
      .endif
    .endif
 
-   .if streq("$WITH_SSL",yes) && feature(OPENSSL)
+   .if feature(OPENSSL) && (streq("$WITH_SSL",yes) || streq("$SSL_ONLY",yes))
           bind :443 ssl crt ...
    .endif
 
diff --git a/include/haproxy/cfgcond-t.h b/include/haproxy/cfgcond-t.h
index f4f2796..bb19612 100644
--- a/include/haproxy/cfgcond-t.h
+++ b/include/haproxy/cfgcond-t.h
@@ -58,6 +58,7 @@
 	CCTT_FALSE,
 	CCTT_TRUE,
 	CCTT_PRED,
+	CCTT_PAREN, // '(' EXPR ')'
 };
 
 /* keyword for a condition predicate */
@@ -74,6 +75,7 @@
 	int neg;                      // 0: direct result; 1: negate
 	union {
 		const struct cond_pred_kw *pred; // predicate (function)
+		struct cfg_cond_expr *expr;      // expression for CCTT_PAREN
 	};
 };
 
diff --git a/src/cfgcond.c b/src/cfgcond.c
index a96832f..fd03196 100644
--- a/src/cfgcond.c
+++ b/src/cfgcond.c
@@ -51,6 +51,9 @@
 	if (!term || !*term)
 		return;
 
+	if ((*term)->type == CCTT_PAREN)
+		cfg_free_cond_expr(&(*term)->expr);
+
 	free_args((*term)->args);
 	free((*term)->args);
 	ha_free(term);
@@ -104,6 +107,32 @@
 		return 1;
 	}
 
+	/* Try to parse '(' EXPR ')' */
+	if (*in == '(') {
+		int ret;
+
+		t->type = CCTT_PAREN;
+		t->args = NULL;
+
+		do { in++; } while (*in == ' ' || *in == '\t');
+		ret = cfg_parse_cond_expr(&in, &t->expr, err, errptr);
+		if (ret == -1)
+			goto fail2;
+		if (ret == 0)
+			goto fail0;
+
+		/* find the closing ')' */
+		while (*in == ' ' || *in == '\t')
+			in++;
+		if (*in != ')') {
+			memprintf(err, "expected ')' after conditional expression '%s'", *text);
+			goto fail1;
+		}
+		do { in++; } while (*in == ' ' || *in == '\t');
+		*text = in;
+		return 1;
+	}
+
 	/* below we'll likely all make_arg_list() so we must return only via
 	 * the <done> label which frees the arg list.
 	 */
@@ -123,6 +152,7 @@
 		return 1;
 	}
 
+ fail0:
 	memprintf(err, "unparsable conditional expression '%s'", *text);
  fail1:
 	if (errptr)
@@ -196,6 +226,9 @@
 			break;
 		}
 	}
+	else if (term->type == CCTT_PAREN) {
+		ret = cfg_eval_cond_expr(term->expr, err);
+	}
 	else {
 		memprintf(err, "internal error: unhandled condition term type %d", (int)term->type);
 	}