From 5504054a2299afe4180fccf68acb4c73982bfadb Mon Sep 17 00:00:00 2001 From: Alex Kotov Date: Sun, 12 Dec 2021 22:10:56 +0500 Subject: [PATCH] Implement escaping inside quotes in cmd line parser --- src/cmdline.c | 21 +++++++++ tests/test_cmdline.c | 106 +++++++++++++++++++++++++++++-------------- 2 files changed, 94 insertions(+), 33 deletions(-) diff --git a/src/cmdline.c b/src/cmdline.c index 5d29a86..886c683 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -14,6 +14,7 @@ enum State { TOKEN, BACKSLASH, QUOTE, + QUOTE_BACKSLASH, }; bool kernaux_cmdline_parse( @@ -206,6 +207,9 @@ bool kernaux_cmdline_parse( kernaux_strncpy(error_msg, "EOL inside quote", 16); goto fail; } + else if (cur == '\\') { + state = QUOTE_BACKSLASH; + } else if (cur == '"') { if (buffer_size >= arg_size_max) { kernaux_strncpy(error_msg, "arg too long", 12); @@ -226,6 +230,23 @@ bool kernaux_cmdline_parse( ++buffer_size; } break; + + case QUOTE_BACKSLASH: + if (cur == '\0') { + kernaux_strncpy(error_msg, "EOL after backslash inside quote", 32); + goto fail; + } + else { + if (buffer_size >= arg_size_max) { + kernaux_strncpy(error_msg, "arg too long", 12); + goto fail; + } + + state = QUOTE; + *(buffer++) = cur; + ++buffer_size; + } + break; } if (state == FINAL) { diff --git a/tests/test_cmdline.c b/tests/test_cmdline.c index 51deb17..eca767b 100644 --- a/tests/test_cmdline.c +++ b/tests/test_cmdline.c @@ -119,39 +119,72 @@ 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("\\\"", 0, 0, true, "", 1, argv_quotmark); - test("foo\\ ", 0, 0, true, "", 1, argv_foospace); - test("foo\\\\", 0, 0, true, "", 1, argv_foobackslash); - test("foo\\\"", 0, 0, true, "", 1, argv_fooquotmark); - test("\\ foo", 0, 0, true, "", 1, argv_spacefoo); - test("\\\\foo", 0, 0, true, "", 1, argv_backslashfoo); - test("\\\"foo", 0, 0, true, "", 1, argv_quotmarkfoo); - test("\\ foo\\ ", 0, 0, true, "", 1, argv_spacefoospace); - test("\\\\foo\\\\", 0, 0, true, "", 1, argv_backslashfoobackslash); - test("\\\"foo\\\"", 0, 0, true, "", 1, argv_quotmarkfooquotmark); - test("foo\\ bar", 0, 0, true, "", 1, argv_foospacebar); - test("foo\\\\bar", 0, 0, true, "", 1, argv_foobackslashbar); - test("foo\\\"bar", 0, 0, true, "", 1, argv_fooquotmarkbar); - 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_quotmarkfoo_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_fooquotmark_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_quotmarkfooquotmark_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_quotmarkbar); - 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_barquotmark); - test("foo \\ bar\\ ", 0, 0, true, "", 2, argv_foo_spacebarspace); - test("foo \\\\bar\\\\", 0, 0, true, "", 2, argv_foo_backslashbarbackslash); - test("foo \\\"bar\\\"", 0, 0, true, "", 2, argv_foo_quotmarkbarquotmark); + test("\\ ", 0, 0, true, "", 1, argv_space); + test("\"\\ \"", 0, 0, true, "", 1, argv_space); + test("\\\\", 0, 0, true, "", 1, argv_backslash); + test("\"\\\\\"", 0, 0, true, "", 1, argv_backslash); + test("\\\"", 0, 0, true, "", 1, argv_quotmark); + test("\"\\\"\"", 0, 0, true, "", 1, argv_quotmark); + test("foo\\ ", 0, 0, true, "", 1, argv_foospace); + test("\"foo\\ \"", 0, 0, true, "", 1, argv_foospace); + test("foo\\\\", 0, 0, true, "", 1, argv_foobackslash); + test("\"foo\\\\\"", 0, 0, true, "", 1, argv_foobackslash); + test("foo\\\"", 0, 0, true, "", 1, argv_fooquotmark); + test("\"foo\\\"\"", 0, 0, true, "", 1, argv_fooquotmark); + test("\\ foo", 0, 0, true, "", 1, argv_spacefoo); + test("\"\\ foo\"", 0, 0, true, "", 1, argv_spacefoo); + test("\\\\foo", 0, 0, true, "", 1, argv_backslashfoo); + test("\"\\\\foo\"", 0, 0, true, "", 1, argv_backslashfoo); + test("\\\"foo", 0, 0, true, "", 1, argv_quotmarkfoo); + test("\"\\\"foo\"", 0, 0, true, "", 1, argv_quotmarkfoo); + test("\\ foo\\ ", 0, 0, true, "", 1, argv_spacefoospace); + test("\"\\ foo\\ \"", 0, 0, true, "", 1, argv_spacefoospace); + test("\\\\foo\\\\", 0, 0, true, "", 1, argv_backslashfoobackslash); + test("\"\\\\foo\\\\\"", 0, 0, true, "", 1, argv_backslashfoobackslash); + test("\\\"foo\\\"", 0, 0, true, "", 1, argv_quotmarkfooquotmark); + test("\"\\\"foo\\\"\"", 0, 0, true, "", 1, argv_quotmarkfooquotmark); + test("foo\\ bar", 0, 0, true, "", 1, argv_foospacebar); + test("\"foo\\ bar\"", 0, 0, true, "", 1, argv_foospacebar); + test("foo\\\\bar", 0, 0, true, "", 1, argv_foobackslashbar); + test("\"foo\\\\bar\"", 0, 0, true, "", 1, argv_foobackslashbar); + test("foo\\\"bar", 0, 0, true, "", 1, argv_fooquotmarkbar); + test("\"foo\\\"bar\"", 0, 0, true, "", 1, argv_fooquotmarkbar); + test("\\ foo bar", 0, 0, true, "", 2, argv_spacefoo_bar); + 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_backslashfoo_bar); + test("\\\"foo bar", 0, 0, true, "", 2, argv_quotmarkfoo_bar); + test("\"\\\"foo\" bar", 0, 0, true, "", 2, argv_quotmarkfoo_bar); + test("foo\\ bar", 0, 0, true, "", 2, argv_foospace_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_foobackslash_bar); + test("foo\\\" bar", 0, 0, true, "", 2, argv_fooquotmark_bar); + test("\"foo\\\"\" bar", 0, 0, true, "", 2, argv_fooquotmark_bar); + test("\\ foo\\ bar", 0, 0, true, "", 2, argv_spacefoospace_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_backslashfoobackslash_bar); + test("\\\"foo\\\" bar", 0, 0, true, "", 2, argv_quotmarkfooquotmark_bar); + test("\"\\\"foo\\\"\" bar", 0, 0, true, "", 2, argv_quotmarkfooquotmark_bar); + test("foo \\ bar", 0, 0, true, "", 2, argv_foo_spacebar); + 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_backslashbar); + test("foo \\\"bar", 0, 0, true, "", 2, argv_foo_quotmarkbar); + test("foo \"\\\"bar\"", 0, 0, true, "", 2, argv_foo_quotmarkbar); + test("foo bar\\ ", 0, 0, true, "", 2, argv_foo_barspace); + 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_barbackslash); + test("foo bar\\\"", 0, 0, true, "", 2, argv_foo_barquotmark); + test("foo \"bar\\\"\"", 0, 0, true, "", 2, argv_foo_barquotmark); + test("foo \\ bar\\ ", 0, 0, true, "", 2, argv_foo_spacebarspace); + test("foo \"\\ bar\\ \"", 0, 0, true, "", 2, argv_foo_spacebarspace); + test("foo \\\\bar\\\\", 0, 0, true, "", 2, argv_foo_backslashbarbackslash); + test("foo \"\\\\bar\\\\\"", 0, 0, true, "", 2, argv_foo_backslashbarbackslash); + test("foo \\\"bar\\\"", 0, 0, true, "", 2, argv_foo_quotmarkbarquotmark); + test("foo \"\\\"bar\\\"\"", 0, 0, true, "", 2, argv_foo_quotmarkbarquotmark); test("foo\\ bar car", 0, 0, true, "", 2, argv_foospacebar_car); test("\"foo bar\" car", 0, 0, true, "", 2, argv_foospacebar_car); @@ -187,6 +220,13 @@ int main() test("\\\"\\", 0, 0, false, "EOL after backslash", 0, argv0); test("foo\\", 0, 0, false, "EOL after backslash", 0, argv0); + test("\"\\", 0, 0, false, "EOL after backslash inside quote", 0, argv0); + test("\" \\", 0, 0, false, "EOL after backslash inside quote", 0, argv0); + test("\"\\ \\", 0, 0, false, "EOL after backslash inside quote", 0, argv0); + test("\"\\\\\\", 0, 0, false, "EOL after backslash inside quote", 0, argv0); + test("\"\\\"\\", 0, 0, false, "EOL after backslash inside quote", 0, argv0); + test("\"foo\\", 0, 0, false, "EOL after backslash inside quote", 0, argv0); + test("foo\"", 0, 0, false, "unescaped quotation mark", 0, argv0); test("foo\"bar", 0, 0, false, "unescaped quotation mark", 0, argv0);