blob: 50ba3dfab8568e3d4147a70d5ec473f69a66a47c [file] [log] [blame]
Willy Tarreau66243b42021-07-16 15:39:28 +02001/*
2 * Configuration condition preprocessor
3 *
4 * Copyright 2000-2021 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <haproxy/api.h>
14#include <haproxy/arg.h>
15#include <haproxy/cfgcond.h>
16#include <haproxy/global.h>
17#include <haproxy/tools.h>
18
19/* supported condition predicates */
20const struct cond_pred_kw cond_predicates[] = {
21 { "defined", CFG_PRED_DEFINED, ARG1(1, STR) },
22 { "feature", CFG_PRED_FEATURE, ARG1(1, STR) },
23 { "streq", CFG_PRED_STREQ, ARG2(2, STR, STR) },
24 { "strneq", CFG_PRED_STRNEQ, ARG2(2, STR, STR) },
25 { "version_atleast", CFG_PRED_VERSION_ATLEAST, ARG1(1, STR) },
26 { "version_before", CFG_PRED_VERSION_BEFORE, ARG1(1, STR) },
27 { NULL, CFG_PRED_NONE, 0 }
28};
29
30/* looks up a cond predicate matching the keyword in <str>, possibly followed
31 * by a parenthesis. Returns a pointer to it or NULL if not found.
32 */
33const struct cond_pred_kw *cfg_lookup_cond_pred(const char *str)
34{
35 const struct cond_pred_kw *ret;
36 int len = strcspn(str, " (");
37
38 for (ret = &cond_predicates[0]; ret->word; ret++) {
39 if (len != strlen(ret->word))
40 continue;
41 if (strncmp(str, ret->word, len) != 0)
42 continue;
43 return ret;
44 }
45 return NULL;
46}
47
Willy Tarreau087b2d02021-07-16 14:27:20 +020048/* Frees <term> and its args. NULL is supported and does nothing. */
Willy Tarreauf1db20c2021-07-17 18:46:30 +020049void cfg_free_cond_term(struct cfg_cond_term *term)
Willy Tarreau087b2d02021-07-16 14:27:20 +020050{
Willy Tarreauf1db20c2021-07-17 18:46:30 +020051 if (!term)
Willy Tarreau087b2d02021-07-16 14:27:20 +020052 return;
53
Willy Tarreauf1db20c2021-07-17 18:46:30 +020054 if (term->type == CCTT_PAREN) {
55 cfg_free_cond_expr(term->expr);
56 term->expr = NULL;
57 }
Willy Tarreau316ea7e2021-07-16 14:56:59 +020058
Willy Tarreauf1db20c2021-07-17 18:46:30 +020059 free_args(term->args);
60 free(term->args);
61 free(term);
Willy Tarreau087b2d02021-07-16 14:27:20 +020062}
63
Willy Tarreauf8690952021-07-16 12:12:00 +020064/* Parse an indirect input text as a possible config condition term.
65 * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
Willy Tarreau087b2d02021-07-16 14:27:20 +020066 * success. <term> is allocated and filled with the parsed info, and <text>
67 * is updated on success to point to the first unparsed character, or is left
68 * untouched on failure. On success, the caller must free <term> using
69 * cfg_free_cond_term(). An error will be set in <err> on error, and only
Willy Tarreauf8690952021-07-16 12:12:00 +020070 * in this case. In this case the first bad character will be reported in
Willy Tarreaudc70c182021-07-20 17:58:34 +020071 * <errptr>. <maxdepth> corresponds to the maximum recursion depth permitted,
72 * it is decremented on each recursive call and the parsing will fail one
73 * reaching <= 0.
Willy Tarreau66243b42021-07-16 15:39:28 +020074 */
Willy Tarreaudc70c182021-07-20 17:58:34 +020075int cfg_parse_cond_term(const char **text, struct cfg_cond_term **term, char **err, const char **errptr, int maxdepth)
Willy Tarreau66243b42021-07-16 15:39:28 +020076{
Willy Tarreau087b2d02021-07-16 14:27:20 +020077 struct cfg_cond_term *t;
Willy Tarreauf8690952021-07-16 12:12:00 +020078 const char *in = *text;
Willy Tarreau66243b42021-07-16 15:39:28 +020079 const char *end_ptr;
Willy Tarreau66243b42021-07-16 15:39:28 +020080 int err_arg;
81 int nbargs;
Willy Tarreau66243b42021-07-16 15:39:28 +020082 char *end;
83 long val;
84
Willy Tarreauf8690952021-07-16 12:12:00 +020085 while (*in == ' ' || *in == '\t')
86 in++;
87
88 if (!*in) /* empty term does not parse */
Willy Tarreau66243b42021-07-16 15:39:28 +020089 return 0;
90
Willy Tarreaudc70c182021-07-20 17:58:34 +020091 *term = NULL;
92 if (maxdepth <= 0)
93 goto fail0;
94
Willy Tarreau087b2d02021-07-16 14:27:20 +020095 t = *term = calloc(1, sizeof(**term));
96 if (!t) {
97 memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
98 goto fail1;
99 }
100
101 t->type = CCTT_NONE;
102 t->args = NULL;
103 t->neg = 0;
104
Willy Tarreauca56d3d2021-07-16 13:56:54 +0200105 /* !<term> negates the term. White spaces permitted */
106 while (*in == '!') {
Willy Tarreau087b2d02021-07-16 14:27:20 +0200107 t->neg = !t->neg;
Willy Tarreauca56d3d2021-07-16 13:56:54 +0200108 do { in++; } while (*in == ' ' || *in == '\t');
109 }
110
Willy Tarreauf8690952021-07-16 12:12:00 +0200111 val = strtol(in, &end, 0);
112 if (end != in) {
Willy Tarreau087b2d02021-07-16 14:27:20 +0200113 t->type = val ? CCTT_TRUE : CCTT_FALSE;
Willy Tarreauf8690952021-07-16 12:12:00 +0200114 *text = end;
115 return 1;
116 }
Willy Tarreau66243b42021-07-16 15:39:28 +0200117
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200118 /* Try to parse '(' EXPR ')' */
119 if (*in == '(') {
120 int ret;
121
122 t->type = CCTT_PAREN;
123 t->args = NULL;
124
125 do { in++; } while (*in == ' ' || *in == '\t');
Willy Tarreaudc70c182021-07-20 17:58:34 +0200126 ret = cfg_parse_cond_expr(&in, &t->expr, err, errptr, maxdepth - 1);
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200127 if (ret == -1)
128 goto fail2;
129 if (ret == 0)
130 goto fail0;
131
132 /* find the closing ')' */
133 while (*in == ' ' || *in == '\t')
134 in++;
135 if (*in != ')') {
136 memprintf(err, "expected ')' after conditional expression '%s'", *text);
137 goto fail1;
138 }
139 do { in++; } while (*in == ' ' || *in == '\t');
140 *text = in;
141 return 1;
142 }
143
Willy Tarreau66243b42021-07-16 15:39:28 +0200144 /* below we'll likely all make_arg_list() so we must return only via
145 * the <done> label which frees the arg list.
146 */
Willy Tarreau087b2d02021-07-16 14:27:20 +0200147 t->pred = cfg_lookup_cond_pred(in);
148 if (t->pred) {
149 t->type = CCTT_PRED;
150 nbargs = make_arg_list(in + strlen(t->pred->word), -1,
151 t->pred->arg_mask, &t->args, err,
Willy Tarreau66243b42021-07-16 15:39:28 +0200152 &end_ptr, &err_arg, NULL);
Willy Tarreau66243b42021-07-16 15:39:28 +0200153 if (nbargs < 0) {
Willy Tarreau087b2d02021-07-16 14:27:20 +0200154 memprintf(err, "%s in argument %d of predicate '%s' used in conditional expression", *err, err_arg, t->pred->word);
Willy Tarreau66243b42021-07-16 15:39:28 +0200155 if (errptr)
156 *errptr = end_ptr;
Willy Tarreau087b2d02021-07-16 14:27:20 +0200157 goto fail2;
Willy Tarreau66243b42021-07-16 15:39:28 +0200158 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200159 *text = end_ptr;
160 return 1;
161 }
162
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200163 fail0:
Willy Tarreauf8690952021-07-16 12:12:00 +0200164 memprintf(err, "unparsable conditional expression '%s'", *text);
Willy Tarreau087b2d02021-07-16 14:27:20 +0200165 fail1:
Willy Tarreauf8690952021-07-16 12:12:00 +0200166 if (errptr)
167 *errptr = *text;
Willy Tarreau087b2d02021-07-16 14:27:20 +0200168 fail2:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200169 cfg_free_cond_term(*term);
170 *term = NULL;
Willy Tarreauf8690952021-07-16 12:12:00 +0200171 return -1;
172}
173
174/* evaluate a condition term on a .if/.elif line. The condition was already
175 * parsed in <term>. Returns -1 on error (in which case err is filled with a
176 * message, and only in this case), 0 if the condition is false, 1 if it's
177 * true.
178 */
179int cfg_eval_cond_term(const struct cfg_cond_term *term, char **err)
180{
181 int ret = -1;
Willy Tarreau66243b42021-07-16 15:39:28 +0200182
Willy Tarreauf8690952021-07-16 12:12:00 +0200183 if (term->type == CCTT_FALSE)
184 ret = 0;
185 else if (term->type == CCTT_TRUE)
186 ret = 1;
187 else if (term->type == CCTT_PRED) {
188 /* here we know we have a valid predicate with valid arguments
189 * placed in term->args (which the caller will free).
Willy Tarreau66243b42021-07-16 15:39:28 +0200190 */
Willy Tarreauf8690952021-07-16 12:12:00 +0200191 switch (term->pred->prd) {
Willy Tarreau66243b42021-07-16 15:39:28 +0200192 case CFG_PRED_DEFINED: // checks if arg exists as an environment variable
Willy Tarreauf8690952021-07-16 12:12:00 +0200193 ret = getenv(term->args[0].data.str.area) != NULL;
194 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200195
196 case CFG_PRED_FEATURE: { // checks if the arg matches an enabled feature
197 const char *p;
198
Willy Tarreauf8690952021-07-16 12:12:00 +0200199 ret = 0; // assume feature not found
200 for (p = build_features; (p = strstr(p, term->args[0].data.str.area)); p++) {
201 if (p > build_features &&
202 (p[term->args[0].data.str.data] == ' ' ||
203 p[term->args[0].data.str.data] == 0)) {
204 if (*(p-1) == '+') { // e.g. "+OPENSSL"
Willy Tarreau66243b42021-07-16 15:39:28 +0200205 ret = 1;
Willy Tarreauf8690952021-07-16 12:12:00 +0200206 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200207 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200208 else if (*(p-1) == '-') { // e.g. "-OPENSSL"
Willy Tarreau66243b42021-07-16 15:39:28 +0200209 ret = 0;
Willy Tarreauf8690952021-07-16 12:12:00 +0200210 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200211 }
212 /* it was a sub-word, let's restart from next place */
213 }
214 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200215 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200216 }
217 case CFG_PRED_STREQ: // checks if the two arg are equal
Willy Tarreauf8690952021-07-16 12:12:00 +0200218 ret = strcmp(term->args[0].data.str.area, term->args[1].data.str.area) == 0;
219 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200220
221 case CFG_PRED_STRNEQ: // checks if the two arg are different
Willy Tarreauf8690952021-07-16 12:12:00 +0200222 ret = strcmp(term->args[0].data.str.area, term->args[1].data.str.area) != 0;
223 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200224
225 case CFG_PRED_VERSION_ATLEAST: // checks if the current version is at least this one
Willy Tarreauf8690952021-07-16 12:12:00 +0200226 ret = compare_current_version(term->args[0].data.str.area) <= 0;
227 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200228
229 case CFG_PRED_VERSION_BEFORE: // checks if the current version is older than this one
Willy Tarreauf8690952021-07-16 12:12:00 +0200230 ret = compare_current_version(term->args[0].data.str.area) > 0;
231 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200232
233 default:
Willy Tarreauf8690952021-07-16 12:12:00 +0200234 memprintf(err, "internal error: unhandled conditional expression predicate '%s'", term->pred->word);
235 break;
Willy Tarreau66243b42021-07-16 15:39:28 +0200236 }
237 }
Willy Tarreau316ea7e2021-07-16 14:56:59 +0200238 else if (term->type == CCTT_PAREN) {
239 ret = cfg_eval_cond_expr(term->expr, err);
240 }
Willy Tarreauf8690952021-07-16 12:12:00 +0200241 else {
242 memprintf(err, "internal error: unhandled condition term type %d", (int)term->type);
243 }
Willy Tarreauca56d3d2021-07-16 13:56:54 +0200244
245 if (ret >= 0 && term->neg)
246 ret = !ret;
Willy Tarreauf8690952021-07-16 12:12:00 +0200247 return ret;
248}
249
250
Willy Tarreauca818872021-07-16 14:46:09 +0200251/* Frees <expr> and its terms and args. NULL is supported and does nothing. */
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200252void cfg_free_cond_and(struct cfg_cond_and *expr)
Willy Tarreauca818872021-07-16 14:46:09 +0200253{
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200254 struct cfg_cond_and *prev;
255
256 while (expr) {
257 cfg_free_cond_term(expr->left);
258 prev = expr;
259 expr = expr->right;
260 free(prev);
Willy Tarreauca818872021-07-16 14:46:09 +0200261 }
262}
263
264/* Frees <expr> and its terms and args. NULL is supported and does nothing. */
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200265void cfg_free_cond_expr(struct cfg_cond_expr *expr)
Willy Tarreauca818872021-07-16 14:46:09 +0200266{
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200267 struct cfg_cond_expr *prev;
268
269 while (expr) {
270 cfg_free_cond_and(expr->left);
271 prev = expr;
272 expr = expr->right;
273 free(prev);
Willy Tarreauca818872021-07-16 14:46:09 +0200274 }
275}
276
277/* Parse an indirect input text as a possible config condition sub-expr.
278 * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
279 * success. <expr> is filled with the parsed info, and <text> is updated on
280 * success to point to the first unparsed character, or is left untouched
281 * on failure. On success, the caller will have to free all lower-level
282 * allocated structs using cfg_free_cond_expr(). An error will be set in
283 * <err> on error, and only in this case. In this case the first bad
Willy Tarreaudc70c182021-07-20 17:58:34 +0200284 * character will be reported in <errptr>. <maxdepth> corresponds to the
285 * maximum recursion depth permitted, it is decremented on each recursive
286 * call and the parsing will fail one reaching <= 0.
Willy Tarreauca818872021-07-16 14:46:09 +0200287 */
Willy Tarreaudc70c182021-07-20 17:58:34 +0200288int cfg_parse_cond_and(const char **text, struct cfg_cond_and **expr, char **err, const char **errptr, int maxdepth)
Willy Tarreauca818872021-07-16 14:46:09 +0200289{
290 struct cfg_cond_and *e;
291 const char *in = *text;
292 int ret = -1;
293
294 if (!*in) /* empty expr does not parse */
295 return 0;
296
Willy Tarreaudc70c182021-07-20 17:58:34 +0200297 *expr = NULL;
298 if (maxdepth <= 0) {
299 memprintf(err, "unparsable conditional sub-expression '%s'", in);
300 if (errptr)
301 *errptr = in;
302 goto done;
303 }
304
Willy Tarreauca818872021-07-16 14:46:09 +0200305 e = *expr = calloc(1, sizeof(**expr));
306 if (!e) {
307 memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
308 goto done;
309 }
310
Willy Tarreaudc70c182021-07-20 17:58:34 +0200311 ret = cfg_parse_cond_term(&in, &e->left, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200312 if (ret == -1) // parse error, error already reported
313 goto done;
314
315 if (ret == 0) {
316 /* ret == 0, no other way to parse this */
317 memprintf(err, "unparsable conditional sub-expression '%s'", in);
318 if (errptr)
319 *errptr = in;
320 ret = -1;
321 goto done;
322 }
323
324 /* ret=1, we have a term in the left hand set */
325
326 /* find an optionnal '&&' */
327 while (*in == ' ' || *in == '\t')
328 in++;
329
330 *text = in;
331 if (in[0] != '&' || in[1] != '&')
332 goto done;
333
334 /* we have a '&&', let's parse the right handset's subexp */
335 in += 2;
336 while (*in == ' ' || *in == '\t')
337 in++;
338
Willy Tarreaudc70c182021-07-20 17:58:34 +0200339 ret = cfg_parse_cond_and(&in, &e->right, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200340 if (ret > 0)
341 *text = in;
342 done:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200343 if (ret < 0) {
344 cfg_free_cond_and(*expr);
345 *expr = NULL;
346 }
Willy Tarreauca818872021-07-16 14:46:09 +0200347 return ret;
348}
349
350/* Parse an indirect input text as a possible config condition term.
351 * Returns <0 on parsing error, 0 if the parser is desynchronized, or >0 on
352 * success. <expr> is filled with the parsed info, and <text> is updated on
353 * success to point to the first unparsed character, or is left untouched
354 * on failure. On success, the caller will have to free all lower-level
355 * allocated structs using cfg_free_cond_expr(). An error will be set in
356 * <err> on error, and only in this case. In this case the first bad
Willy Tarreaudc70c182021-07-20 17:58:34 +0200357 * character will be reported in <errptr>. <maxdepth> corresponds to the
358 * maximum recursion depth permitted, it is decremented on each recursive call
359 * and the parsing will fail one reaching <= 0.
Willy Tarreauca818872021-07-16 14:46:09 +0200360 */
Willy Tarreaudc70c182021-07-20 17:58:34 +0200361int cfg_parse_cond_expr(const char **text, struct cfg_cond_expr **expr, char **err, const char **errptr, int maxdepth)
Willy Tarreauca818872021-07-16 14:46:09 +0200362{
363 struct cfg_cond_expr *e;
364 const char *in = *text;
365 int ret = -1;
366
367 if (!*in) /* empty expr does not parse */
368 return 0;
369
Willy Tarreaudc70c182021-07-20 17:58:34 +0200370 *expr = NULL;
371 if (maxdepth <= 0) {
372 memprintf(err, "unparsable conditional expression '%s'", in);
373 if (errptr)
374 *errptr = in;
375 goto done;
376 }
377
Willy Tarreauca818872021-07-16 14:46:09 +0200378 e = *expr = calloc(1, sizeof(**expr));
379 if (!e) {
380 memprintf(err, "memory allocation error while parsing conditional expression '%s'", *text);
381 goto done;
382 }
383
Willy Tarreaudc70c182021-07-20 17:58:34 +0200384 ret = cfg_parse_cond_and(&in, &e->left, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200385 if (ret == -1) // parse error, error already reported
386 goto done;
387
388 if (ret == 0) {
389 /* ret == 0, no other way to parse this */
390 memprintf(err, "unparsable conditional expression '%s'", in);
391 if (errptr)
392 *errptr = in;
393 ret = -1;
394 goto done;
395 }
396
397 /* ret=1, we have a sub-expr in the left hand set */
398
399 /* find an optionnal '||' */
400 while (*in == ' ' || *in == '\t')
401 in++;
402
403 *text = in;
404 if (in[0] != '|' || in[1] != '|')
405 goto done;
406
407 /* we have a '||', let's parse the right handset's subexp */
408 in += 2;
409 while (*in == ' ' || *in == '\t')
410 in++;
411
Willy Tarreaudc70c182021-07-20 17:58:34 +0200412 ret = cfg_parse_cond_expr(&in, &e->right, err, errptr, maxdepth - 1);
Willy Tarreauca818872021-07-16 14:46:09 +0200413 if (ret > 0)
414 *text = in;
415 done:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200416 if (ret < 0) {
417 cfg_free_cond_expr(*expr);
418 *expr = NULL;
419 }
Willy Tarreauca818872021-07-16 14:46:09 +0200420 return ret;
421}
422
423/* evaluate an sub-expression on a .if/.elif line. The expression is valid and
424 * was already parsed in <expr>. Returns -1 on error (in which case err is
425 * filled with a message, and only in this case), 0 if the condition is false,
426 * 1 if it's true.
427 */
428int cfg_eval_cond_and(struct cfg_cond_and *expr, char **err)
429{
430 int ret;
431
432 /* AND: loop on terms and sub-exp's terms as long as they're TRUE
433 * (stop on FALSE and ERROR).
434 */
435 while ((ret = cfg_eval_cond_term(expr->left, err)) > 0 && expr->right)
436 expr = expr->right;
437 return ret;
438}
439
440/* evaluate an expression on a .if/.elif line. The expression is valid and was
441 * already parsed in <expr>. Returns -1 on error (in which case err is filled
442 * with a message, and only in this case), 0 if the condition is false, 1 if
443 * it's true.
444 */
445int cfg_eval_cond_expr(struct cfg_cond_expr *expr, char **err)
446{
447 int ret;
448
449 /* OR: loop on sub-exps as long as they're FALSE (stop on TRUE and ERROR) */
450 while ((ret = cfg_eval_cond_and(expr->left, err)) == 0 && expr->right)
451 expr = expr->right;
452 return ret;
453}
454
Willy Tarreauf8690952021-07-16 12:12:00 +0200455/* evaluate a condition on a .if/.elif line. The condition is already tokenized
456 * in <err>. Returns -1 on error (in which case err is filled with a message,
457 * and only in this case), 0 if the condition is false, 1 if it's true. If
458 * <errptr> is not NULL, it's set to the first invalid character on error.
459 */
460int cfg_eval_condition(char **args, char **err, const char **errptr)
461{
Willy Tarreauca818872021-07-16 14:46:09 +0200462 struct cfg_cond_expr *expr = NULL;
Willy Tarreauf8690952021-07-16 12:12:00 +0200463 const char *text = args[0];
464 int ret = -1;
465
466 if (!*text) /* note: empty = false */
467 return 0;
468
Willy Tarreaudc70c182021-07-20 17:58:34 +0200469 ret = cfg_parse_cond_expr(&text, &expr, err, errptr, MAX_CFG_RECURSION);
Willy Tarreauf8690952021-07-16 12:12:00 +0200470 if (ret != 0) {
471 if (ret == -1) // parse error, error already reported
472 goto done;
Willy Tarreau379ceea2021-07-16 16:18:03 +0200473 while (*text == ' ' || *text == '\t')
474 text++;
475
476 if (*text) {
477 ret = -1;
478 memprintf(err, "unexpected character '%c' at the end of conditional expression '%s'",
479 *text, args[0]);
480 goto fail;
481 }
482
Willy Tarreauca818872021-07-16 14:46:09 +0200483 ret = cfg_eval_cond_expr(expr, err);
Willy Tarreauf8690952021-07-16 12:12:00 +0200484 goto done;
485 }
Willy Tarreau66243b42021-07-16 15:39:28 +0200486
Willy Tarreauf8690952021-07-16 12:12:00 +0200487 /* ret == 0, no other way to parse this */
488 ret = -1;
Willy Tarreau66243b42021-07-16 15:39:28 +0200489 memprintf(err, "unparsable conditional expression '%s'", args[0]);
Willy Tarreau379ceea2021-07-16 16:18:03 +0200490 fail:
Willy Tarreau66243b42021-07-16 15:39:28 +0200491 if (errptr)
Willy Tarreauf8690952021-07-16 12:12:00 +0200492 *errptr = text;
Willy Tarreau66243b42021-07-16 15:39:28 +0200493 done:
Willy Tarreauf1db20c2021-07-17 18:46:30 +0200494 cfg_free_cond_expr(expr);
Willy Tarreau66243b42021-07-16 15:39:28 +0200495 return ret;
496}