struct sample_expr *smp = NULL;
int idx = 0;
char *ckw = NULL;
- const char *begw;
- const char *endw;
const char *endt;
int cur_type;
int nbargs;
memprintf(err, "in argument to '%s', %s", aclkw->kw, *err);
goto out_free_smp;
}
- arg = endt;
/* look for the beginning of the converters list. Those directly attached
- * to the ACL keyword are found just after <arg> which points to the comma.
+ * to the ACL keyword are found just after the comma.
* If we find any converter, then we don't use the ACL keyword's match
* anymore but the one related to the converter's output type.
*/
- cur_type = smp->fetch->out_type;
- while (*arg) {
- struct sample_conv *conv;
- struct sample_conv_expr *conv_expr;
- int err_arg;
- int argcnt;
-
- if (*arg && *arg != ',') {
- if (ckw)
- memprintf(err, "ACL keyword '%s' : missing comma after converter '%s'.",
- aclkw->kw, ckw);
- else
- memprintf(err, "ACL keyword '%s' : missing comma after fetch keyword.",
- aclkw->kw);
- goto out_free_smp;
- }
-
- /* FIXME: how long should we support such idiocies ? Maybe we
- * should already warn ?
- */
- while (*arg == ',') /* then trailing commas */
- arg++;
-
- begw = arg; /* start of converter keyword */
-
- if (!*begw)
- /* none ? end of converters */
- break;
-
- for (endw = begw; is_idchar(*endw); endw++)
- ;
-
- free(ckw);
- ckw = my_strndup(begw, endw - begw);
-
- conv = find_sample_conv(begw, endw - begw);
- if (!conv) {
- /* Unknown converter method */
- memprintf(err, "ACL keyword '%s' : unknown converter '%s'.",
- aclkw->kw, ckw);
- goto out_free_smp;
- }
-
- arg = endw;
-
- if (conv->in_type >= SMP_TYPES || conv->out_type >= SMP_TYPES) {
- memprintf(err, "ACL keyword '%s' : returns type of converter '%s' is unknown.",
- aclkw->kw, ckw);
- goto out_free_smp;
- }
-
- /* If impossible type conversion */
- if (!sample_casts[cur_type][conv->in_type]) {
- memprintf(err, "ACL keyword '%s' : converter '%s' cannot be applied.",
- aclkw->kw, ckw);
- goto out_free_smp;
- }
-
- cur_type = conv->out_type;
- conv_expr = calloc(1, sizeof(*conv_expr));
- if (!conv_expr)
- goto out_free_smp;
-
- LIST_APPEND(&(smp->conv_exprs), &(conv_expr->list));
- conv_expr->conv = conv;
- acl_conv_found = 1;
-
- 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.",
- aclkw->kw, err_arg+1, ckw, *err);
- goto out_free_smp;
- }
-
- if (argcnt && !conv->arg_mask) {
- memprintf(err, "converter '%s' does not support any args", ckw);
- goto out_free_smp;
- }
-
- if (!conv_expr->arg_p)
- conv_expr->arg_p = empty_arg_list;
-
- if (conv->val_args && !conv->val_args(conv_expr->arg_p, conv, file, line, err)) {
- memprintf(err, "ACL keyword '%s' : invalid args in converter '%s' : %s.",
- aclkw->kw, ckw, *err);
- goto out_free_smp;
- }
+ if (!sample_parse_expr_cnv((char **)args, NULL, NULL, err, al, file, line, smp, endt)) {
+ if (err)
+ memprintf(err, "ACL keyword '%s' : %s", aclkw->kw, *err);
+ goto out_free_smp;
}
- ha_free(&ckw);
+ acl_conv_found = !LIST_ISEMPTY(&smp->conv_exprs);
}
else {
/* This is not an ACL keyword, so we hope this is a sample fetch
memprintf(err, "%s in ACL expression '%s'", *err, *args);
goto out_return;
}
- cur_type = smp_expr_output_type(smp);
}
+ /* get last effective output type for smp */
+ cur_type = smp_expr_output_type(smp);
+
expr = calloc(1, sizeof(*expr));
if (!expr) {
memprintf(err, "out of memory when parsing ACL expression");
/* METH */ { c_none, NULL, NULL, NULL, NULL, NULL, c_meth2str, c_meth2str, c_none, }
};
-/*
- * Parse a sample expression configuration:
- * fetch keyword followed by format conversion keywords.
- * Returns a pointer on allocated sample expression structure.
- * <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.
+/* Process the converters (if any) for a sample expr after the first fetch
+ * keyword. We have two supported syntaxes for the converters, which can be
+ * combined:
+ * - comma-delimited list of converters just after the keyword and args ;
+ * - one converter per keyword (if <idx> != NULL)
+ * FIXME: should we continue to support this old syntax?
+ * The combination allows to have each keyword being a comma-delimited
+ * series of converters.
+ *
+ * We want to process the former first, then the latter. For this we start
+ * from the beginning of the supposed place in the exiting conv chain, which
+ * starts at the last comma (<start> which is then referred to as endt).
+ *
* 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.
+ *
+ * <expr> should point the the sample expression that is already initialized
+ * with the sample fetch that precedes the converters chain.
+ *
+ * The function returns a positive value for success and 0 for failure, in which
+ * case <err_msg> will point to an allocated string that brings some info
+ * about the failure. It is the caller's responsibility to free it.
*/
-struct sample_expr *sample_parse_expr(char **str, int *idx, const char *file, int line, char **err_msg, struct arg_list *al, char **endptr)
+int sample_parse_expr_cnv(char **str, int *idx, char **endptr, char **err_msg, struct arg_list *al, const char *file, int line,
+ struct sample_expr *expr, const char *start)
{
- const char *begw; /* beginning of word */
- const char *endw; /* end of word */
- const char *endt; /* end of term */
- struct sample_expr *expr = NULL;
- struct sample_fetch *fetch;
struct sample_conv *conv;
- unsigned long prev_type;
- char *fkw = NULL;
+ const char *endt = start; /* end of term */
+ const char *begw; /* beginning of word */
+ const char *endw; /* end of word */
char *ckw = NULL;
- int err_arg;
-
- begw = str[*idx];
- for (endw = begw; is_idchar(*endw); endw++)
- ;
-
- if (endw == begw) {
- memprintf(err_msg, "missing fetch method");
- goto out_error;
- }
-
- /* keep a copy of the current fetch keyword for error reporting */
- fkw = my_strndup(begw, endw - begw);
-
- fetch = find_sample_fetch(begw, endw - begw);
- if (!fetch) {
- memprintf(err_msg, "unknown fetch method '%s'", fkw);
- goto out_error;
- }
-
- /* At this point, we have :
- * - begw : beginning of the keyword
- * - endw : end of the keyword, first character not part of keyword
- */
-
- if (fetch->out_type >= SMP_TYPES) {
- memprintf(err_msg, "returns type of fetch method '%s' is unknown", fkw);
- goto out_error;
- }
- prev_type = fetch->out_type;
-
- expr = calloc(1, sizeof(*expr));
- if (!expr)
- goto out_error;
-
- LIST_INIT(&(expr->conv_exprs));
- expr->fetch = fetch;
- expr->arg_p = empty_arg_list;
-
- /* Note that we call the argument parser even with an empty string,
- * this allows it to automatically create entries for mandatory
- * implicit arguments (eg: local proxy name).
- */
- 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;
- }
-
- /* now endt is our first char not part of the arg list, typically the
- * comma after the sample fetch name or after the closing parenthesis,
- * or the NUL char.
- */
-
- if (!expr->arg_p) {
- expr->arg_p = empty_arg_list;
- }
- else if (fetch->val_args && !fetch->val_args(expr->arg_p, err_msg)) {
- memprintf(err_msg, "invalid args in fetch method '%s' : %s", fkw, *err_msg);
- goto out_error;
- }
-
- /* Now process the converters if any. We have two supported syntaxes
- * for the converters, which can be combined :
- * - comma-delimited list of converters just after the keyword and args ;
- * - one converter per keyword
- * The combination allows to have each keyword being a comma-delimited
- * series of converters.
- *
- * We want to process the former first, then the latter. For this we start
- * from the beginning of the supposed place in the exiting conv chain, which
- * starts at the last comma (endt).
- */
+ unsigned long prev_type = expr->fetch->out_type;
+ int success = 1;
while (1) {
struct sample_conv_expr *conv_expr;
if (ckw)
memprintf(err_msg, "missing comma after converter '%s'", ckw);
else
- memprintf(err_msg, "missing comma after fetch keyword '%s'", fkw);
+ memprintf(err_msg, "missing comma after fetch keyword");
goto out_error;
}
begw = endt; /* start of converter */
if (!*begw) {
- /* none ? skip to next string */
+ /* none ? skip to next string if idx is set */
+ if (!idx)
+ break; /* end of converters */
(*idx)++;
begw = str[*idx];
if (!begw || !*begw)
for (endw = begw; is_idchar(*endw); endw++)
;
- free(ckw);
+ ha_free(&ckw);
ckw = my_strndup(begw, endw - begw);
conv = find_sample_conv(begw, endw - begw);
if (!conv) {
/* we found an isolated keyword that we don't know, it's not ours */
- if (begw == str[*idx]) {
+ if (idx && begw == str[*idx]) {
endt = begw;
break;
}
}
if (conv->in_type >= SMP_TYPES || conv->out_type >= SMP_TYPES) {
- memprintf(err_msg, "returns type of converter '%s' is unknown", ckw);
+ memprintf(err_msg, "return type of converter '%s' is unknown", ckw);
goto out_error;
}
/* end found, let's stop here */
*endptr = (char *)endt;
}
+ out:
+ free(ckw);
+ return success;
+
+ out_error:
+ success = 0;
+ goto out;
+}
+
+/*
+ * Parse a sample expression configuration:
+ * fetch keyword followed by format conversion keywords.
+ *
+ * <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.
+ *
+ * Returns a pointer on allocated sample expression structure or NULL in case
+ * of error, in which case <err_msg> will point to an allocated string that
+ * brings some info about the failure. It is the caller's responsibility to
+ * free it.
+ */
+struct sample_expr *sample_parse_expr(char **str, int *idx, const char *file, int line, char **err_msg, struct arg_list *al, char **endptr)
+{
+ const char *begw; /* beginning of word */
+ const char *endw; /* end of word */
+ const char *endt; /* end of term */
+ struct sample_expr *expr = NULL;
+ struct sample_fetch *fetch;
+ char *fkw = NULL;
+ int err_arg;
+
+ begw = str[*idx];
+ for (endw = begw; is_idchar(*endw); endw++)
+ ;
+
+ if (endw == begw) {
+ memprintf(err_msg, "missing fetch method");
+ goto out_error;
+ }
+
+ /* keep a copy of the current fetch keyword for error reporting */
+ fkw = my_strndup(begw, endw - begw);
+
+ fetch = find_sample_fetch(begw, endw - begw);
+ if (!fetch) {
+ memprintf(err_msg, "unknown fetch method '%s'", fkw);
+ goto out_error;
+ }
+
+ /* At this point, we have :
+ * - begw : beginning of the keyword
+ * - endw : end of the keyword, first character not part of keyword
+ */
+
+ if (fetch->out_type >= SMP_TYPES) {
+ memprintf(err_msg, "returns type of fetch method '%s' is unknown", fkw);
+ goto out_error;
+ }
+
+ expr = calloc(1, sizeof(*expr));
+ if (!expr)
+ goto out_error;
+
+ LIST_INIT(&(expr->conv_exprs));
+ expr->fetch = fetch;
+ expr->arg_p = empty_arg_list;
+
+ /* Note that we call the argument parser even with an empty string,
+ * this allows it to automatically create entries for mandatory
+ * implicit arguments (eg: local proxy name).
+ */
+ 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;
+ }
+
+ /* now endt is our first char not part of the arg list, typically the
+ * comma after the sample fetch name or after the closing parenthesis,
+ * or the NUL char.
+ */
+
+ if (!expr->arg_p) {
+ expr->arg_p = empty_arg_list;
+ }
+ else if (fetch->val_args && !fetch->val_args(expr->arg_p, err_msg)) {
+ memprintf(err_msg, "invalid args in fetch method '%s' : %s", fkw, *err_msg);
+ goto out_error;
+ }
+
+ if (!sample_parse_expr_cnv(str, idx, endptr, err_msg, al, file, line, expr, endt))
+ goto out_error;
out:
free(fkw);
- free(ckw);
return expr;
out_error: