MEDIUM: cfgparse: introduce weak and strong quoting

This patch introduces quoting which allows to write configuration string
including spaces without escaping them.

Strong (with single quotes) and weak (with double quotes) quoting are
supported. Weak quoting supports escaping and special characters when
strong quoting does not interpret anything.

This patch could break configuration files where ' and " where used.
This commit is contained in:
William Lallemand 2015-05-05 17:37:14 +02:00 committed by Willy Tarreau
parent e428b08ee7
commit f9873ba63a
2 changed files with 95 additions and 12 deletions

View File

@ -39,8 +39,9 @@ Summary
2. Configuring HAProxy
2.1. Configuration file format
2.2. Time format
2.3. Examples
2.2. Quoting and escaping
2.3. Time format
2.4. Examples
3. Global parameters
3.1. Process management and security
@ -366,12 +367,61 @@ HAProxy's configuration process involves 3 major sources of parameters :
The configuration file syntax consists in lines beginning with a keyword
referenced in this manual, optionally followed by one or several parameters
delimited by spaces. If spaces have to be entered in strings, then they must be
preceded by a backslash ('\') to be escaped. Backslashes also have to be
escaped by doubling them.
delimited by spaces.
2.2. Time format
2.2. Quoting and escaping
-------------------------
HAProxy's configuration introduces a quoting and escaping system similar to
many programming languages. The configuration file supports 3 types: escaping
with a backslash, weak quoting with double quotes, and strong quoting with
single quotes.
If spaces have to be entered in strings, then they must be escaped by preceding
them by a backslash ('\') or by quoting them. Backslashes also have to be
escaped by doubling or strong quoting them.
Escaping is achieved by preceding a special character by a backslash ('\'):
\ to mark a space and differentiate it from a delimiter
\# to mark a hash and differentiate it from a comment
\\ to use a backslash
\' to use a single quote and differentiate it from strong quoting
\" to use a double quote and differentiate it from weak quoting
Weak quoting is achieved by using double quotes (""). Weak quoting prevents
the interpretation of:
space as a parameter separator
' single quote as a strong quoting delimiter
# hash as a comment start
But interpretation of escaping and special characters are not prevented by weak
quoting.
Strong quoting is achieved by using single quotes (''). Inside single quotes,
nothing is interpreted, it's the efficient way to quote regexes.
Quoted and escaped strings are replaced in memory by their interpreted
equivalent, it allows you to perform concatenation.
Example:
# those are equivalents:
log-format %{+Q}o\ %t\ %s\ %{-Q}r
log-format "%{+Q}o %t %s %{-Q}r"
log-format '%{+Q}o %t %s %{-Q}r'
log-format "%{+Q}o %t"' %s %{-Q}r'
log-format "%{+Q}o %t"' %s'\ %{-Q}r
# those are equivalents:
reqrep "^([^\ :]*)\ /static/(.*)" \1\ /\2
reqrep "^([^ :]*)\ /static/(.*)" '\1 /\2'
reqrep "^([^ :]*)\ /static/(.*)" "\1 /\2"
reqrep "^([^ :]*)\ /static/(.*)" "\1\ /\2"
2.3. Time format
----------------
Some parameters involve values representing time, such as timeouts. These
@ -388,7 +438,7 @@ for every keyword. Supported units are :
- d : days. 1d = 24h = 1440m = 86400s = 86400000ms
2.3. Examples
2.4. Examples
-------------
# Simple configuration for an HTTP proxy listening on port 80 on all

View File

@ -6207,6 +6207,8 @@ int readcfgfile(const char *file)
char *end;
char *args[MAX_LINE_ARGS + 1];
char *line = thisline;
int dquote = 0; /* double quote */
int squote = 0; /* simple quote */
linenum++;
@ -6224,15 +6226,31 @@ int readcfgfile(const char *file)
/* skip leading spaces */
while (isspace((unsigned char)*line))
line++;
arg = 0;
args[arg] = line;
while (*line && arg < MAX_LINE_ARGS) {
if (*line == '"' && !squote) { /* double quote outside single quotes */
if (dquote)
dquote = 0;
else
dquote = 1;
memmove(line, line + 1, end - (line + 1));
end--;
}
else if (*line == '\'' && !dquote) { /* single quote outside double quotes */
if (squote)
squote = 0;
else
squote = 1;
memmove(line, line + 1, end - (line + 1));
end--;
}
else if (*line == '\\' && !squote) {
/* first, we'll replace \\, \<space>, \#, \r, \n, \t, \xXX with their
* C equivalent value. Other combinations left unchanged (eg: \1).
*/
if (*line == '\\') {
int skip = 0;
if (line[1] == ' ' || line[1] == '\\' || line[1] == '#') {
*line = line[1];
@ -6241,7 +6259,7 @@ int readcfgfile(const char *file)
else if (line[1] == 'r') {
*line = '\r';
skip = 1;
}
}
else if (line[1] == 'n') {
*line = '\n';
skip = 1;
@ -6264,6 +6282,12 @@ int readcfgfile(const char *file)
Alert("parsing [%s:%d] : invalid or incomplete '\\x' sequence in '%s'.\n", file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
}
} else if (line[1] == '"') {
*line = '"';
skip = 1;
} else if (line[1] == '\'') {
*line = '\'';
skip = 1;
}
if (skip) {
memmove(line + 1, line + 1 + skip, end - (line + skip));
@ -6271,12 +6295,12 @@ int readcfgfile(const char *file)
}
line++;
}
else if (*line == '#' || *line == '\n' || *line == '\r') {
else if ((!squote && !dquote && *line == '#') || *line == '\n' || *line == '\r') {
/* end of string, end of loop */
*line = 0;
break;
}
else if (isspace((unsigned char)*line)) {
else if (!squote && !dquote && isspace((unsigned char)*line)) {
/* a non-escaped space is an argument separator */
*line++ = '\0';
while (isspace((unsigned char)*line))
@ -6287,6 +6311,15 @@ int readcfgfile(const char *file)
line++;
}
}
if (dquote) {
Alert("parsing [%s:%d] : Mismatched double quotes.\n", file, linenum);
err_code |= ERR_ALERT | ERR_FATAL;
}
if (squote) {
Alert("parsing [%s:%d] : Mismatched simple quotes.\n", file, linenum);
err_code |= ERR_ALERT | ERR_FATAL;
}
/* empty line */
if (!**args)