MINOR: tools: support for word expansion of environment in parse_line
Allow the syntax "${...[*]}" to expand an environment variable
containing several values separated by spaces as individual arguments. A
new flag PARSE_OPT_WORD_EXPAND has been added to toggle this feature on
parse_line invocation. In case of an invalid syntax, a new error
PARSE_ERR_WRONG_EXPAND will be triggered.
This feature has been asked on the github issue #165.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 87f35e9..db4a4a7 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -475,7 +475,10 @@
configuration parsing. Variable names must be preceded by a dollar ("$") and
optionally enclosed with braces ("{}") similarly to what is done in Bourne
shell. Variable names can contain alphanumerical characters or the character
-underscore ("_") but should not start with a digit.
+underscore ("_") but should not start with a digit. If the variable contains a
+list of several values separated by spaces, it can be expanded as individual
+arguments by enclosing the variable with braces and appending the suffix '[*]'
+before the closing brace.
Example:
diff --git a/include/haproxy/tools-t.h b/include/haproxy/tools-t.h
index b1e7249..34f79be 100644
--- a/include/haproxy/tools-t.h
+++ b/include/haproxy/tools-t.h
@@ -57,6 +57,7 @@
#define PARSE_OPT_DQUOTE 0x00000008 // '"' encloses a string
#define PARSE_OPT_ENV 0x00000010 // '$' is followed by environment variables
#define PARSE_OPT_INPLACE 0x00000020 // parse and tokenize in-place (src == dst)
+#define PARSE_OPT_WORD_EXPAND 0x00000040 // '[*]' suffix to expand an environment variable as several individual arguments
/* return error flags from parse_line() */
#define PARSE_ERR_TOOLARGE 0x00000001 // result is too large for initial outlen
@@ -66,6 +67,7 @@
#define PARSE_ERR_HEX 0x00000010 // unparsable hex sequence (at errptr)
#define PARSE_ERR_VARNAME 0x00000020 // invalid variable name (at errptr)
#define PARSE_ERR_OVERLAP 0x00000040 // output overlaps with input, need to allocate
+#define PARSE_ERR_WRONG_EXPAND 0x00000080 // unparsable word expansion sequence
/* special return values for the time parser (parse_time_err()) */
#define PARSE_TIME_UNDER ((char *)1)
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 768fea3..cb48fef 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1908,7 +1908,8 @@
outlen = outlinesize;
err = parse_line(line, outline, &outlen, args, &arg,
PARSE_OPT_ENV | PARSE_OPT_DQUOTE | PARSE_OPT_SQUOTE |
- PARSE_OPT_BKSLASH | PARSE_OPT_SHARP, &errptr);
+ PARSE_OPT_BKSLASH | PARSE_OPT_SHARP | PARSE_OPT_WORD_EXPAND,
+ &errptr);
if (err & PARSE_ERR_QUOTE) {
size_t newpos = sanitize_for_printing(line, errptr - line, 80);
@@ -1950,6 +1951,16 @@
goto next_line;
}
+ if (err & PARSE_ERR_WRONG_EXPAND) {
+ size_t newpos = sanitize_for_printing(line, errptr - line, 80);
+
+ ha_alert("parsing [%s:%d]: truncated or invalid word expansion sequence at position %d:\n"
+ " %s\n %*s\n", file, linenum, (int)(errptr-thisline+1), line, (int)(newpos+1), "^");
+ err_code |= ERR_ALERT | ERR_FATAL;
+ fatal++;
+ goto next_line;
+ }
+
if (err & (PARSE_ERR_TOOLARGE|PARSE_ERR_OVERLAP)) {
outlinesize = (outlen + 1023) & -1024;
outline = realloc(outline, outlinesize);
diff --git a/src/tools.c b/src/tools.c
index 53d2486..c2a9664 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -4911,6 +4911,7 @@
{
char *quote = NULL;
char *brace = NULL;
+ char *word_expand = NULL;
unsigned char hex1, hex2;
size_t outmax = *outlen;
int argsmax = *nbargs - 1;
@@ -5089,6 +5090,19 @@
value = getenv(var_name);
*in = save_char;
+ /* support for '[*]' sequence to force word expansion,
+ * only available inside braces */
+ if (*in == '[' && brace && (opts & PARSE_OPT_WORD_EXPAND)) {
+ word_expand = in++;
+
+ if (*in++ != '*' || *in++ != ']') {
+ err |= PARSE_ERR_WRONG_EXPAND;
+ if (errptr)
+ *errptr = word_expand;
+ goto leave;
+ }
+ }
+
if (brace) {
if (*in != '}') {
/* unmatched brace */
@@ -5102,9 +5116,25 @@
}
if (value) {
- while (*value)
- EMIT_CHAR(*value++);
+ while (*value) {
+ /* expand as individual parameters on a space character */
+ if (word_expand && isspace(*value)) {
+ EMIT_CHAR(0);
+ ++arg;
+ if (arg < argsmax)
+ args[arg] = out + outpos;
+ else
+ err |= PARSE_ERR_TOOMANY;
+
+ /* skip consecutive spaces */
+ while (isspace(*++value))
+ ;
+ } else {
+ EMIT_CHAR(*value++);
+ }
+ }
}
+ word_expand = NULL;
}
else {
/* any other regular char */