diff --git a/src/cmdline.c b/src/cmdline.c index 7ed0491..0fb2c6f 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -7,6 +7,7 @@ enum State { FINAL, WHITESPACE, TOKEN, + BACKSLASHED, }; kernaux_bool kernaux_cmdline_parse( @@ -52,6 +53,7 @@ kernaux_bool kernaux_cmdline_parse( switch (state) { case FINAL: break; // Case break; loop break after switch. + case INITIAL: if (cur == '\0') { state = FINAL; @@ -59,6 +61,15 @@ kernaux_bool kernaux_cmdline_parse( else if (cur == ' ') { state = WHITESPACE; } + else if (cur == '\\') { + if (*argc >= argv_count_max) { + kernaux_strncpy(error_msg, "too many args", 13); + goto fail; + } + + state = BACKSLASHED; + argv[(*argc)++] = buffer; + } else { if (*argc >= argv_count_max) { kernaux_strncpy(error_msg, "too many args", 13); @@ -76,12 +87,22 @@ kernaux_bool kernaux_cmdline_parse( ++buffer_size; } break; + case WHITESPACE: if (cur == '\0') { state = FINAL; } else if (cur == ' ') { } + else if (cur == '\\') { + if (*argc >= argv_count_max) { + kernaux_strncpy(error_msg, "too many args", 13); + goto fail; + } + + state = BACKSLASHED; + argv[(*argc)++] = buffer; + } else { if (*argc >= argv_count_max) { kernaux_strncpy(error_msg, "too many args", 13); @@ -99,6 +120,7 @@ kernaux_bool kernaux_cmdline_parse( ++buffer_size; } break; + case TOKEN: if (cur == '\0') { if (buffer_size >= arg_size_max) { @@ -120,6 +142,9 @@ kernaux_bool kernaux_cmdline_parse( *(buffer++) = '\0'; buffer_size = 0; } + else if (cur == '\\') { + state = BACKSLASHED; + } else { if (buffer_size >= arg_size_max) { kernaux_strncpy(error_msg, "arg too long", 12); @@ -130,6 +155,23 @@ kernaux_bool kernaux_cmdline_parse( ++buffer_size; } break; + + case BACKSLASHED: + if (cur == '\0') { + kernaux_strncpy(error_msg, "EOL after backslash", 19); + goto fail; + } + else { + if (buffer_size >= arg_size_max) { + kernaux_strncpy(error_msg, "arg too long", 12); + goto fail; + } + + state = TOKEN; + *(buffer++) = cur; + ++buffer_size; + } + break; } if (state == FINAL) { diff --git a/tests/test_cmdline.c b/tests/test_cmdline.c index ee6d403..d6f3a3a 100644 --- a/tests/test_cmdline.c +++ b/tests/test_cmdline.c @@ -26,6 +26,54 @@ static const char *const argv_foo_bar[] = {"foo", "bar"}; static const char *const argv_foo_bar_car[] = {"foo", "bar", "car"}; +static const char *const argv_space[] = {" "}; + +static const char *const argv_backslash[] = {"\\"}; + +static const char *const argv_foospace[] = {"foo "}; + +static const char *const argv_foobackslash[] = {"foo\\"}; + +static const char *const argv_spacefoo[] = {" foo"}; + +static const char *const argv_backslashfoo[] = {"\\foo"}; + +static const char *const argv_spacefoospace[] = {" foo "}; + +static const char *const argv_backslashfoobackslash[] = {"\\foo\\"}; + +static const char *const argv_foospacebar[] = {"foo bar"}; + +static const char *const argv_foobackslashbar[] = {"foo\\bar"}; + +static const char *const argv_spaceX3_X3[] = {" ", " ", " "}; + +static const char *const argv_backslashX3_X3[] = {"\\\\\\", "\\\\\\", "\\\\\\"}; + +static const char *const argv_spacefoo_bar[] = {" foo", "bar"}; + +static const char *const argv_backslashfoo_bar[] = {"\\foo", "bar"}; + +static const char *const argv_foospace_bar[] = {"foo ", "bar"}; + +static const char *const argv_foobackslash_bar[] = {"foo\\", "bar"}; + +static const char *const argv_spacefoospace_bar[] = {" foo ", "bar"}; + +static const char *const argv_backslashfoobackslash_bar[] = {"\\foo\\", "bar"}; + +static const char *const argv_foo_spacebar[] = {"foo", " bar"}; + +static const char *const argv_foo_backslashbar[] = {"foo", "\\bar"}; + +static const char *const argv_foo_barspace[] = {"foo", "bar "}; + +static const char *const argv_foo_barbackslash[] = {"foo", "bar\\"}; + +static const char *const argv_foo_spacebarspace[] = {"foo", " bar "}; + +static const char *const argv_foo_backslashbarbackslash[] = {"foo", "\\bar\\"}; + int main() { test("", 0, 0, true, "", 0, argv0); @@ -49,6 +97,43 @@ int main() test("foo bar car", 0, 3, false, "arg too long", 0, argv0); test("foo bar car", 2, 3, false, "arg too long", 0, argv0); + test("\\ ", 0, 0, true, "", 1, argv_space); + test("\\\\", 0, 0, true, "", 1, argv_backslash); + test("foo\\ ", 0, 0, true, "", 1, argv_foospace); + test("foo\\\\", 0, 0, true, "", 1, argv_foobackslash); + test("\\ foo", 0, 0, true, "", 1, argv_spacefoo); + test("\\\\foo", 0, 0, true, "", 1, argv_backslashfoo); + test("\\ foo\\ ", 0, 0, true, "", 1, argv_spacefoospace); + test("\\\\foo\\\\", 0, 0, true, "", 1, argv_backslashfoobackslash); + test("foo\\ bar", 0, 0, true, "", 1, argv_foospacebar); + test("foo\\\\bar", 0, 0, true, "", 1, argv_foobackslashbar); + test("\\ foo bar", 0, 0, true, "", 2, argv_spacefoo_bar); + test("\\\\foo bar", 0, 0, true, "", 2, argv_backslashfoo_bar); + test("foo\\ bar", 0, 0, true, "", 2, argv_foospace_bar); + test("foo\\\\ bar", 0, 0, true, "", 2, argv_foobackslash_bar); + test("\\ foo\\ bar", 0, 0, true, "", 2, argv_spacefoospace_bar); + test("\\\\foo\\\\ bar", 0, 0, true, "", 2, argv_backslashfoobackslash_bar); + test("foo \\ bar", 0, 0, true, "", 2, argv_foo_spacebar); + test("foo \\\\bar", 0, 0, true, "", 2, argv_foo_backslashbar); + test("foo bar\\ ", 0, 0, true, "", 2, argv_foo_barspace); + test("foo bar\\\\", 0, 0, true, "", 2, argv_foo_barbackslash); + test("foo \\ bar\\ ", 0, 0, true, "", 2, argv_foo_spacebarspace); + test("foo \\\\bar\\\\", 0, 0, true, "", 2, argv_foo_backslashbarbackslash); + + test("\\ \\ \\ \\ \\ \\ \\ \\ \\ ", 3, 0, true, "", 3, argv_spaceX3_X3); + test("\\\\\\\\\\\\ \\\\\\\\\\\\ \\\\\\\\\\\\", 3, 0, true, "", 3, argv_backslashX3_X3); + test("\\ \\ \\ \\ \\ \\ \\ \\ \\ ", 0, 4, true, "", 3, argv_spaceX3_X3); + test("\\\\\\\\\\\\ \\\\\\\\\\\\ \\\\\\\\\\\\", 0, 4, true, "", 3, argv_backslashX3_X3); + test("\\ \\ \\ \\ \\ \\ \\ \\ \\ ", 3, 4, true, "", 3, argv_spaceX3_X3); + test("\\\\\\\\\\\\ \\\\\\\\\\\\ \\\\\\\\\\\\", 3, 4, true, "", 3, argv_backslashX3_X3); + + test("\\ \\ \\ \\ \\ \\ \\ \\ \\ ", 2, 0, false, "too many args", 0, argv0); + test("\\\\\\\\\\\\ \\\\\\\\\\\\ \\\\\\\\\\\\", 2, 0, false, "too many args", 0, argv0); + test("\\ \\ \\ \\ \\ \\ \\ \\ \\ ", 0, 3, false, "arg too long", 0, argv0); + test("\\\\\\\\\\\\ \\\\\\\\\\\\ \\\\\\\\\\\\", 0, 3, false, "arg too long", 0, argv0); + test("\\ \\ \\ \\ \\ \\ \\ \\ \\ ", 2, 3, false, "arg too long", 0, argv0); + test("\\\\\\\\\\\\ \\\\\\\\\\\\ \\\\\\\\\\\\", 2, 3, false, "arg too long", 0, argv0); + return 0; }