util: New flag UNQUOTE_UNESCAPE_RELAX for unquote_first_word

The new flag UNQUOTE_UNESCAPE_RELAX preserves unrecognized escape
sequences verbatim in unquote_first_word, either when it's a trailing
backslash (similar to UNQUOTE_RELAX, but in this case keep the extra
backslash in the output) or in the middle of a sequence string.

Add unit test cases to ensure the new flag works as expected and to
prevent regressions from being introduced.

Tested with a follow up commit converting config_parse_exec() to start
using unquote_first_word, in which case this flags makes it possible to
preserve unrecognized escape sequences.

Relevant bug: https://bugs.freedesktop.org/show_bug.cgi?id=90794
This commit is contained in:
Filipe Brandenburger 2015-06-02 21:08:24 -07:00
parent 05654e712f
commit d6293c070e
3 changed files with 120 additions and 6 deletions

View file

@ -5246,21 +5246,39 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
case SINGLE_QUOTE_ESCAPE:
case DOUBLE_QUOTE_ESCAPE:
case VALUE_ESCAPE:
if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
if (c == 0) {
if ((flags & UNQUOTE_CUNESCAPE_RELAX) &&
(state == VALUE_ESCAPE || flags & UNQUOTE_RELAX)) {
/* If we find an unquoted trailing backslash and we're in
* UNQUOTE_CUNESCAPE_RELAX mode, keep it verbatim in the
* output.
*
* Unbalanced quotes will only be allowed in UNQUOTE_RELAX
* mode, UNQUOTE_CUNESCAP_RELAX mode does not allow them.
*/
s[sz++] = '\\';
goto finish;
}
if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
if (flags & UNQUOTE_CUNESCAPE) {
uint32_t u;
r = cunescape_one(*p, (size_t) -1, &c, &u);
if (r < 0)
if (r < 0) {
if (flags & UNQUOTE_CUNESCAPE_RELAX) {
s[sz++] = '\\';
s[sz++] = c;
goto end_escape;
}
return -EINVAL;
}
(*p) += r - 1;
@ -5271,6 +5289,7 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
} else
s[sz++] = c;
end_escape:
state = (state == SINGLE_QUOTE_ESCAPE) ? SINGLE_QUOTE :
(state == DOUBLE_QUOTE_ESCAPE) ? DOUBLE_QUOTE :
VALUE;

View file

@ -839,8 +839,9 @@ int is_dir(const char *path, bool follow);
int is_device_node(const char *path);
typedef enum UnquoteFlags {
UNQUOTE_RELAX = 1,
UNQUOTE_CUNESCAPE = 2,
UNQUOTE_RELAX = 1,
UNQUOTE_CUNESCAPE = 2,
UNQUOTE_CUNESCAPE_RELAX = 4,
} UnquoteFlags;
int unquote_first_word(const char **p, char **ret, UnquoteFlags flags);

View file

@ -1304,6 +1304,100 @@ static void test_unquote_first_word(void) {
assert_se(streq(t, "pi\360\237\222\251le"));
free(t);
assert_se(p == original + 32);
p = original = "fooo\\";
assert_se(unquote_first_word(&p, &t, UNQUOTE_RELAX) > 0);
assert_se(streq(t, "fooo"));
free(t);
assert_se(p == original + 5);
p = original = "fooo\\";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX) > 0);
assert_se(streq(t, "fooo\\"));
free(t);
assert_se(p == original + 5);
p = original = "fooo\\";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX|UNQUOTE_RELAX) > 0);
assert_se(streq(t, "fooo\\"));
free(t);
assert_se(p == original + 5);
p = original = "fooo\\";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX) > 0);
assert_se(streq(t, "fooo\\"));
free(t);
assert_se(p == original + 5);
p = original = "\"foo\\";
assert_se(unquote_first_word(&p, &t, 0) == -EINVAL);
assert_se(p == original + 5);
p = original = "\"foo\\";
assert_se(unquote_first_word(&p, &t, UNQUOTE_RELAX) > 0);
assert_se(streq(t, "foo"));
free(t);
assert_se(p == original + 5);
p = original = "\"foo\\";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX) == -EINVAL);
assert_se(p == original + 5);
p = original = "\"foo\\";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX|UNQUOTE_RELAX) > 0);
assert_se(streq(t, "foo\\"));
free(t);
assert_se(p == original + 5);
p = original = "\"foo\\";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX|UNQUOTE_RELAX) > 0);
assert_se(streq(t, "foo\\"));
free(t);
assert_se(p == original + 5);
p = original = "fooo\\ bar quux";
assert_se(unquote_first_word(&p, &t, UNQUOTE_RELAX) > 0);
assert_se(streq(t, "fooo bar"));
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX) > 0);
assert_se(streq(t, "fooo bar"));
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX|UNQUOTE_RELAX) > 0);
assert_se(streq(t, "fooo bar"));
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) == -EINVAL);
assert_se(p == original + 5);
p = original = "fooo\\ bar quux";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX) > 0);
assert_se(streq(t, "fooo\\ bar"));
free(t);
assert_se(p == original + 10);
p = original = "\\w+@\\K[\\d.]+";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) == -EINVAL);
assert_se(p == original + 1);
p = original = "\\w+@\\K[\\d.]+";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX) > 0);
assert_se(streq(t, "\\w+@\\K[\\d.]+"));
free(t);
assert_se(p == original + 12);
p = original = "\\w+\\b";
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX) > 0);
assert_se(streq(t, "\\w+\b"));
free(t);
assert_se(p == original + 5);
}
static void test_unquote_many_words(void) {