Fix handling of empty quoted string in shlex
This commit is contained in:
parent
e942bc817b
commit
2093fb1310
2 changed files with 15 additions and 8 deletions
|
|
@ -15,7 +15,7 @@ typedef enum { NORMAL, WORD, STRING_WITHOUT_ESCAPES, STRING_WITH_ESCAPES, ANSI_C
|
|||
|
||||
typedef struct {
|
||||
const char *src;
|
||||
bool support_ansi_c_quoting;
|
||||
bool support_ansi_c_quoting, allow_empty;
|
||||
char *buf;
|
||||
size_t src_sz, src_pos, word_start, buf_pos;
|
||||
ShlexEnum state;
|
||||
|
|
@ -90,6 +90,7 @@ static size_t
|
|||
get_word(ShlexState *self) {
|
||||
size_t ans = self->buf_pos; self->buf_pos = 0;
|
||||
self->buf[ans] = 0;
|
||||
self->allow_empty = false;
|
||||
return ans;
|
||||
}
|
||||
|
||||
|
|
@ -201,7 +202,7 @@ next_word(ShlexState *self) {
|
|||
break;
|
||||
case WORD:
|
||||
switch(ch) {
|
||||
case WHITESPACE: set_state(self, NORMAL); if (self->buf_pos) return get_word(self); break;
|
||||
case WHITESPACE: set_state(self, NORMAL); if (self->buf_pos || self->allow_empty) return get_word(self); break;
|
||||
case STRING_WITH_ESCAPES_DELIM: set_state(self, STRING_WITH_ESCAPES); break;
|
||||
case STRING_WITHOUT_ESCAPES_DELIM:
|
||||
if (self->support_ansi_c_quoting && prev_word_ch == '$') { self->buf_pos--; set_state(self, ANSI_C_QUOTED); }
|
||||
|
|
@ -212,18 +213,18 @@ next_word(ShlexState *self) {
|
|||
} break;
|
||||
case STRING_WITHOUT_ESCAPES:
|
||||
switch(ch) {
|
||||
case STRING_WITHOUT_ESCAPES_DELIM: set_state(self, WORD); break;
|
||||
case STRING_WITHOUT_ESCAPES_DELIM: set_state(self, WORD); self->allow_empty = true; break;
|
||||
default: write_ch(self, ch); break;
|
||||
} break;
|
||||
case STRING_WITH_ESCAPES:
|
||||
switch(ch) {
|
||||
case STRING_WITH_ESCAPES_DELIM: set_state(self, WORD); break;
|
||||
case STRING_WITH_ESCAPES_DELIM: set_state(self, WORD); self->allow_empty = true; break;
|
||||
case ESCAPE_CHAR: write_escaped_or_fail(); break;
|
||||
default: write_ch(self, ch); break;
|
||||
} break;
|
||||
case ANSI_C_QUOTED:
|
||||
switch(ch) {
|
||||
case STRING_WITHOUT_ESCAPES_DELIM: set_state(self, WORD); break;
|
||||
case STRING_WITHOUT_ESCAPES_DELIM: set_state(self, WORD); self->allow_empty = true; break;
|
||||
case ESCAPE_CHAR: if (!write_ansi_escape_ch(self)) return -1; break;
|
||||
default: write_ch(self, ch); break;
|
||||
} break;
|
||||
|
|
@ -232,7 +233,7 @@ next_word(ShlexState *self) {
|
|||
switch (self->state) {
|
||||
case WORD:
|
||||
self->state = NORMAL;
|
||||
if (self->buf_pos) return get_word(self);
|
||||
if (self->buf_pos || self->allow_empty) return get_word(self);
|
||||
break;
|
||||
case STRING_WITH_ESCAPES: case STRING_WITHOUT_ESCAPES: case ANSI_C_QUOTED:
|
||||
self->err = "Unterminated string at the end of input";
|
||||
|
|
|
|||
|
|
@ -683,6 +683,12 @@ def test_shlex_split(self):
|
|||
tuple(shlex_split(bad))
|
||||
|
||||
for q, expected in {
|
||||
'a""': ((0, 'a'),),
|
||||
'a""b': ((0, 'ab'),),
|
||||
'-1 "" 2': ((0, '-1'), (3, ''), (6, '2')),
|
||||
"-1 '' 2": ((0, '-1'), (3, ''), (6, '2')),
|
||||
'a ""': ((0, 'a'), (2, '')),
|
||||
'""': ((0, ''),),
|
||||
'"ab"': ((0, 'ab'),),
|
||||
r'x "ab"y \m': ((0, 'x'), (2, 'aby'), (8, 'm')),
|
||||
r'''x'y"\z'1''': ((0, 'xy"\\z1'),),
|
||||
|
|
@ -694,11 +700,11 @@ def test_shlex_split(self):
|
|||
'😀 a': ((0, '😀'), (2, 'a')),
|
||||
' \t😀a': ((2, '😀a'),),
|
||||
}.items():
|
||||
actual = tuple(shlex_split_with_positions(q))
|
||||
self.ae(expected, actual, f'Failed for text: {q!r}')
|
||||
ex = tuple(x[1] for x in expected)
|
||||
actual = tuple(shlex_split(q))
|
||||
self.ae(ex, actual, f'Failed for text: {q!r}')
|
||||
actual = tuple(shlex_split_with_positions(q))
|
||||
self.ae(expected, actual, f'Failed for text: {q!r}')
|
||||
|
||||
for q, expected in {
|
||||
"$'ab'": ((0, 'ab'),),
|
||||
|
|
|
|||
Loading…
Reference in a new issue