diff --git a/src/common.c b/src/common.c index ab6f67b0..65ec0db4 100644 --- a/src/common.c +++ b/src/common.c @@ -550,7 +550,7 @@ strip_arg_quotes(const char* const input) // Unescape and strip quotes GString* unescaped = g_string_new(""); for (const char* p = input; *p; p++) { - if (*p == '\\' && (*(p + 1) == '"' || *(p + 1) == '\\')) { + if (*p == '\\' && (*(p + 1) != '\0')) { p++; g_string_append_c(unescaped, *p); } else if (*p == '"') { diff --git a/src/tools/autocomplete.c b/src/tools/autocomplete.c index 7cbd9141..990df83f 100644 --- a/src/tools/autocomplete.c +++ b/src/tools/autocomplete.c @@ -237,7 +237,7 @@ autocomplete_complete(Autocomplete ac, const gchar* search_str, gboolean quote, // unescape search string GString* unescaped = g_string_new(""); for (const char* p = search_str; *p; p++) { - if (*p == '\\' && (*(p + 1) == '"' || *(p + 1) == '\\')) { + if (*p == '\\' && (*(p + 1) != '\0')) { p++; } g_string_append_c(unescaped, *p); diff --git a/src/tools/parser.c b/src/tools/parser.c index 0cfdb122..ab5c77cb 100644 --- a/src/tools/parser.c +++ b/src/tools/parser.c @@ -272,6 +272,15 @@ count_tokens(const char* const string) if (curr_uni == ' ') { if (!in_quotes) { num_tokens++; + while (i + 1 < length) { + gchar* next_ch = g_utf8_offset_to_pointer(string, i + 1); + gunichar next_uni = g_utf8_get_char(next_ch); + if (next_uni == ' ') { + i++; + } else { + break; + } + } } } else if (curr_uni == '"') { if (in_quotes) { @@ -319,6 +328,18 @@ get_start(const char* const string, int tokens) if (curr_uni == ' ') { if (!in_quotes) { num_tokens++; + while (i + 1 < length) { + gchar* next_ch = g_utf8_offset_to_pointer(string, i + 1); + gunichar next_uni = g_utf8_get_char(next_ch); + if (next_uni == ' ') { + if (num_tokens <= tokens) { + g_string_append_unichar(result, next_uni); + } + i++; + } else { + break; + } + } } } else if (curr_uni == '"') { if (in_quotes) { diff --git a/tests/unittests/tools/test_autocomplete.c b/tests/unittests/tools/test_autocomplete.c index 45f03ebe..8c81c45b 100644 --- a/tests/unittests/tools/test_autocomplete.c +++ b/tests/unittests/tools/test_autocomplete.c @@ -345,3 +345,31 @@ autocomplete_complete__returns__regular_ascii(void** state) autocomplete_free(ac); free(result); } + +void +autocomplete_complete__handles__escaped_spaces(void** state) +{ + Autocomplete ac = autocomplete_new(); + autocomplete_add(ac, "Thor Odinson"); + + char* result = autocomplete_complete(ac, "Thor\\ ", TRUE, FALSE); + + assert_string_equal("\"Thor Odinson\"", result); + + autocomplete_free(ac); + free(result); +} + +void +autocomplete_complete__handles__escaped_quotes(void** state) +{ + Autocomplete ac = autocomplete_new(); + autocomplete_add(ac, "Thor \"The Thunderer\" Odinson"); + + char* result = autocomplete_complete(ac, "Thor \\\"", TRUE, FALSE); + + assert_string_equal("\"Thor \\\"The Thunderer\\\" Odinson\"", result); + + autocomplete_free(ac); + free(result); +} diff --git a/tests/unittests/tools/test_autocomplete.h b/tests/unittests/tools/test_autocomplete.h index 09ec200d..c923eb8e 100644 --- a/tests/unittests/tools/test_autocomplete.h +++ b/tests/unittests/tools/test_autocomplete.h @@ -4,6 +4,8 @@ void autocomplete_complete__returns__null_when_empty(void** state); void autocomplete_reset__updates__after_create(void** state); void autocomplete_complete__returns__null_after_create(void** state); +void autocomplete_complete__handles__escaped_spaces(void** state); +void autocomplete_complete__handles__escaped_quotes(void** state); void autocomplete_create_list__returns__null_after_create(void** state); void autocomplete_add__updates__one_and_complete(void** state); void autocomplete_complete__returns__first_of_two(void** state); diff --git a/tests/unittests/tools/test_parser.c b/tests/unittests/tools/test_parser.c index e415bd03..13248184 100644 --- a/tests/unittests/tools/test_parser.c +++ b/tests/unittests/tools/test_parser.c @@ -342,6 +342,20 @@ parse_args_with_freetext__returns__quoted_freetext(void** state) g_strfreev(args); } +void +parse_args_with_freetext__returns__quoted_start_of_freetext(void** state) +{ + char* inp = "/cmd arg1 \"arg2 with space\" more text"; + gboolean result = FALSE; + gchar** args = parse_args_with_freetext(inp, 1, 2, &result); + + assert_true(result); + assert_int_equal(2, g_strv_length(args)); + assert_string_equal("arg1", args[0]); + assert_string_equal("\"arg2 with space\" more text", args[1]); + g_strfreev(args); +} + void parse_args_with_freetext__returns__third_arg_quoted(void** state) { @@ -504,6 +518,15 @@ count_tokens__handles__escapes(void** state) assert_int_equal(2, result); } +void +count_tokens__handles__multiple_spaces(void** state) +{ + char* inp = "one two"; + int result = count_tokens(inp); + + assert_int_equal(2, result); +} + void get_start__returns__first_of_one(void** state) { @@ -574,6 +597,16 @@ get_start__handles__escapes(void** state) g_free(result); } +void +get_start__handles__multiple_spaces(void** state) +{ + char* inp = "one two"; + char* result = get_start(inp, 2); + + assert_string_equal("one ", result); + g_free(result); +} + void parse_options__returns__empty_hashmap_when_none(void** state) { diff --git a/tests/unittests/tools/test_parser.h b/tests/unittests/tools/test_parser.h index 738b0f95..f0633142 100644 --- a/tests/unittests/tools/test_parser.h +++ b/tests/unittests/tools/test_parser.h @@ -40,6 +40,7 @@ void count_tokens__returns__two_tokens_first_quoted(void** state); void count_tokens__returns__two_tokens_second_quoted(void** state); void count_tokens__returns__two_tokens_both_quoted(void** state); void count_tokens__handles__escapes(void** state); +void count_tokens__handles__multiple_spaces(void** state); void get_start__returns__first_of_one(void** state); void get_start__returns__first_of_two(void** state); void get_start__returns__first_two_of_three(void** state); @@ -47,6 +48,7 @@ void get_start__returns__first_two_of_three_first_quoted(void** state); void get_start__returns__first_two_of_three_second_quoted(void** state); void get_start__returns__first_two_of_three_first_and_second_quoted(void** state); void get_start__handles__escapes(void** state); +void get_start__handles__multiple_spaces(void** state); void parse_options__returns__empty_hashmap_when_none(void** state); void parse_options__returns__error_when_opt1_no_val(void** state); void parse_options__returns__map_when_one(void** state); diff --git a/tests/unittests/unittests.c b/tests/unittests/unittests.c index 6e57ef16..17e3774e 100644 --- a/tests/unittests/unittests.c +++ b/tests/unittests/unittests.c @@ -120,6 +120,8 @@ main(int argc, char* argv[]) cmocka_unit_test(autocomplete_complete__returns__null_when_empty), cmocka_unit_test(autocomplete_reset__updates__after_create), cmocka_unit_test(autocomplete_complete__returns__null_after_create), + cmocka_unit_test(autocomplete_complete__handles__escaped_spaces), + cmocka_unit_test(autocomplete_complete__handles__escaped_quotes), cmocka_unit_test(autocomplete_create_list__returns__null_after_create), cmocka_unit_test(autocomplete_add__updates__one_and_complete), cmocka_unit_test(autocomplete_complete__returns__first_of_two), @@ -218,6 +220,7 @@ main(int argc, char* argv[]) cmocka_unit_test(count_tokens__returns__two_tokens_second_quoted), cmocka_unit_test(count_tokens__returns__two_tokens_both_quoted), cmocka_unit_test(count_tokens__handles__escapes), + cmocka_unit_test(count_tokens__handles__multiple_spaces), cmocka_unit_test(get_start__returns__first_of_one), cmocka_unit_test(get_start__returns__first_of_two), cmocka_unit_test(get_start__returns__first_two_of_three), @@ -225,6 +228,7 @@ main(int argc, char* argv[]) cmocka_unit_test(get_start__returns__first_two_of_three_second_quoted), cmocka_unit_test(get_start__returns__first_two_of_three_first_and_second_quoted), cmocka_unit_test(get_start__handles__escapes), + cmocka_unit_test(get_start__handles__multiple_spaces), cmocka_unit_test(parse_options__returns__empty_hashmap_when_none), cmocka_unit_test(parse_options__returns__error_when_opt1_no_val), cmocka_unit_test(parse_options__returns__map_when_one),