From bda5b0f844f1b561da742ce9f55e3a5346ac3f1d Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Sun, 29 Mar 2026 00:15:23 +0100 Subject: [PATCH 1/2] fix(tools): implement backslash escaping for contact names Implement backslash based escaping in the command parser and autocompletion logic to handle contact names containing double quotes. This stops the parser from splitting nicknames into multiple tokens, which previously caused "Invalid usage" errors. The parser now sees \ as an escape character for quotes and spaces in _parse_args_helper, count_tokens and get_start. Autocomplete results are escaped when they contain spaces, and search prefixes are unescaped before matching. strip_arg_quotes() has also been updated to handle unescaping. Fixes: https://github.com/profanity-im/profanity/issues/1844 Signed-off-by: Michael Vetter --- src/common.c | 34 ++++++---- src/tools/autocomplete.c | 21 +++++- src/tools/parser.c | 102 ++++++++++++++++++---------- tests/unittests/test_common.c | 12 ++++ tests/unittests/test_common.h | 1 + tests/unittests/tools/test_parser.c | 61 +++++++++++++++++ tests/unittests/tools/test_parser.h | 5 ++ tests/unittests/unittests.c | 6 ++ 8 files changed, 192 insertions(+), 50 deletions(-) diff --git a/src/common.c b/src/common.c index eb1d1e8a0..ab6f67b0d 100644 --- a/src/common.c +++ b/src/common.c @@ -543,26 +543,32 @@ _get_file_or_linked(gchar* loc) char* strip_arg_quotes(const char* const input) { - char* unquoted = strdup(input); - if (unquoted == NULL) { + if (input == NULL) { return NULL; } - // Remove starting quote if it exists - if (strchr(unquoted, '"')) { - if (strchr(unquoted, ' ') + 1 == strchr(unquoted, '"')) { - memmove(strchr(unquoted, '"'), strchr(unquoted, '"') + 1, strchr(unquoted, '\0') - strchr(unquoted, '"')); - } - } - - // Remove ending quote if it exists - if (strchr(unquoted, '"')) { - if (strchr(unquoted, '\0') - 1 == strchr(unquoted, '"')) { - memmove(strchr(unquoted, '"'), strchr(unquoted, '"') + 1, strchr(unquoted, '\0') - strchr(unquoted, '"')); + // Unescape and strip quotes + GString* unescaped = g_string_new(""); + for (const char* p = input; *p; p++) { + if (*p == '\\' && (*(p + 1) == '"' || *(p + 1) == '\\')) { + p++; + g_string_append_c(unescaped, *p); + } else if (*p == '"') { + // Only strip if it's the first char or preceded by a space + if (p == input || *(p - 1) == ' ') { + continue; + } + // Or if it's the last char + if (*(p + 1) == '\0') { + continue; + } + g_string_append_c(unescaped, *p); + } else { + g_string_append_c(unescaped, *p); } } - return unquoted; + return g_string_free(unescaped, FALSE); } gboolean diff --git a/src/tools/autocomplete.c b/src/tools/autocomplete.c index cbbe856db..7cbd9141d 100644 --- a/src/tools/autocomplete.c +++ b/src/tools/autocomplete.c @@ -234,7 +234,16 @@ autocomplete_complete(Autocomplete ac, const gchar* search_str, gboolean quote, FREE_SET_NULL(ac->search_str); } - ac->search_str = strdup(search_str); + // unescape search string + GString* unescaped = g_string_new(""); + for (const char* p = search_str; *p; p++) { + if (*p == '\\' && (*(p + 1) == '"' || *(p + 1) == '\\')) { + p++; + } + g_string_append_c(unescaped, *p); + } + ac->search_str = g_string_free(unescaped, FALSE); + found = _search(ac, ac->items, quote, NEXT); return found; @@ -403,7 +412,15 @@ _search(Autocomplete ac, GList* curr, gboolean quote, search_direction direction // if contains space, quote before returning if (quote && g_strrstr(curr->data, " ")) { - return g_strdup_printf("\"%s\"", (gchar*)curr->data); + GString* escaped = g_string_new("\""); + for (const char* p = curr->data; *p; p++) { + if (*p == '"' || *p == '\\') { + g_string_append_c(escaped, '\\'); + } + g_string_append_c(escaped, *p); + } + g_string_append_c(escaped, '"'); + return g_string_free(escaped, FALSE); // otherwise just return the string } else { return strdup(curr->data); diff --git a/src/tools/parser.c b/src/tools/parser.c index 940680887..0cfdb122a 100644 --- a/src/tools/parser.c +++ b/src/tools/parser.c @@ -32,8 +32,7 @@ _parse_args_helper(const char* const inp, int min, int max, gboolean* result, gb gboolean in_token = FALSE; gboolean in_freetext = FALSE; gboolean in_quotes = FALSE; - char* token_start = ©[0]; - int token_size = 0; + GString* current_token = g_string_new(""); int num_tokens = 0; GSList* tokens = NULL; @@ -42,6 +41,23 @@ _parse_args_helper(const char* const inp, int min, int max, gboolean* result, gb gchar* curr_ch = g_utf8_offset_to_pointer(copy, i); gunichar curr_uni = g_utf8_get_char(curr_ch); + if (curr_uni == '\\' && (i + 1 < inp_size)) { + if (!in_token) { + in_token = TRUE; + if (with_freetext) { + num_tokens++; + } + if (with_freetext && (num_tokens == max + 1)) { + in_freetext = TRUE; + } + } + i++; + gchar* next_ch = g_utf8_offset_to_pointer(copy, i); + gunichar next_uni = g_utf8_get_char(next_ch); + g_string_append_unichar(current_token, next_uni); + continue; + } + if (!in_token) { if (curr_uni == ' ') { continue; @@ -55,58 +71,61 @@ _parse_args_helper(const char* const inp, int min, int max, gboolean* result, gb } else if (curr_uni == '"') { in_quotes = TRUE; i++; - gchar* next_ch = g_utf8_next_char(curr_ch); - gunichar next_uni = g_utf8_get_char(next_ch); - - if (next_uni == '"') { - tokens = g_slist_append(tokens, g_strndup(curr_ch, - 0)); - token_size = 0; + if (i < inp_size) { + gchar* next_ch = g_utf8_offset_to_pointer(copy, i); + gunichar next_uni = g_utf8_get_char(next_ch); + + if (next_uni == '"') { + tokens = g_slist_append(tokens, g_strdup("")); + in_token = FALSE; + in_quotes = FALSE; + } else if (next_uni == '\\' && (i + 1 < inp_size)) { + i++; + gchar* escaped_ch = g_utf8_offset_to_pointer(copy, i); + gunichar escaped_uni = g_utf8_get_char(escaped_ch); + g_string_append_unichar(current_token, escaped_uni); + } else { + g_string_append_unichar(current_token, next_uni); + } + } else { + // trailing quote + tokens = g_slist_append(tokens, g_strdup("")); in_token = FALSE; in_quotes = FALSE; - } else { - token_start = next_ch; - token_size += g_unichar_to_utf8(next_uni, NULL); } } - if (curr_uni == '"') { - gchar* next_ch = g_utf8_next_char(curr_ch); - token_start = next_ch; - } else { - token_start = curr_ch; - token_size += g_unichar_to_utf8(curr_uni, NULL); + if (in_token && curr_uni != '"') { + g_string_append_unichar(current_token, curr_uni); } } } else { if (in_quotes) { if (curr_uni == '"') { - tokens = g_slist_append(tokens, g_strndup(token_start, - token_size)); - token_size = 0; + tokens = g_slist_append(tokens, g_string_free(current_token, FALSE)); + current_token = g_string_new(""); in_token = FALSE; in_quotes = FALSE; } else { - if (curr_uni != '"') { - token_size += g_unichar_to_utf8(curr_uni, NULL); - } + g_string_append_unichar(current_token, curr_uni); } } else { if (with_freetext && in_freetext) { - token_size += g_unichar_to_utf8(curr_uni, NULL); + g_string_append_unichar(current_token, curr_uni); } else if (curr_uni == ' ') { - tokens = g_slist_append(tokens, g_strndup(token_start, - token_size)); - token_size = 0; + tokens = g_slist_append(tokens, g_string_free(current_token, FALSE)); + current_token = g_string_new(""); in_token = FALSE; } else { - token_size += g_unichar_to_utf8(curr_uni, NULL); + g_string_append_unichar(current_token, curr_uni); } } } } if (in_token) { - tokens = g_slist_append(tokens, g_strndup(token_start, token_size)); + tokens = g_slist_append(tokens, g_string_free(current_token, FALSE)); + } else { + g_string_free(current_token, TRUE); } int num = g_slist_length(tokens) - 1; @@ -245,6 +264,11 @@ count_tokens(const char* const string) gchar* curr_ch = g_utf8_offset_to_pointer(string, i); gunichar curr_uni = g_utf8_get_char(curr_ch); + if (curr_uni == '\\' && (i + 1 < length)) { + i++; + continue; + } + if (curr_uni == ' ') { if (!in_quotes) { num_tokens++; @@ -276,11 +300,21 @@ get_start(const char* const string, int tokens) gchar* curr_ch = g_utf8_offset_to_pointer(string, i); gunichar curr_uni = g_utf8_get_char(curr_ch); + if (curr_uni == '\\' && (i + 1 < length)) { + if (num_tokens < tokens) { + g_string_append_unichar(result, curr_uni); + } + i++; + curr_ch = g_utf8_offset_to_pointer(string, i); + curr_uni = g_utf8_get_char(curr_ch); + if (num_tokens < tokens) { + g_string_append_unichar(result, curr_uni); + } + continue; + } + if (num_tokens < tokens) { - auto_gchar gchar* uni_char = g_malloc(7); - int len = g_unichar_to_utf8(curr_uni, uni_char); - uni_char[len] = '\0'; - g_string_append(result, uni_char); + g_string_append_unichar(result, curr_uni); } if (curr_uni == ' ') { if (!in_quotes) { diff --git a/tests/unittests/test_common.c b/tests/unittests/test_common.c index 6a7bbb9f7..1458155b3 100644 --- a/tests/unittests/test_common.c +++ b/tests/unittests/test_common.c @@ -336,6 +336,18 @@ strip_arg_quotes__returns__stripped_both(void** state) free(result); } +void +strip_arg_quotes__unescapes(void** state) +{ + char* input = "\"Thor \\\"The Thunderer\\\" Odinson\""; + + char* result = strip_arg_quotes(input); + + assert_string_equal("Thor \"The Thunderer\" Odinson", result); + + free(result); +} + void valid_tls_policy_option__is__correct_for_various_inputs(void** state) { diff --git a/tests/unittests/test_common.h b/tests/unittests/test_common.h index c3e97bf84..b552aa9ef 100644 --- a/tests/unittests/test_common.h +++ b/tests/unittests/test_common.h @@ -43,6 +43,7 @@ void strip_arg_quotes__returns__original_when_no_quotes(void** state); void strip_arg_quotes__returns__stripped_first(void** state); void strip_arg_quotes__returns__stripped_last(void** state); void strip_arg_quotes__returns__stripped_both(void** state); +void strip_arg_quotes__unescapes(void** state); void prof_occurrences__tests__partial(void** state); void prof_occurrences__tests__whole(void** state); diff --git a/tests/unittests/tools/test_parser.c b/tests/unittests/tools/test_parser.c index 3a42c1694..e415bd03f 100644 --- a/tests/unittests/tools/test_parser.c +++ b/tests/unittests/tools/test_parser.c @@ -390,6 +390,48 @@ parse_args_with_freetext__returns__second_and_third_arg_quoted(void** state) g_strfreev(args); } +void +parse_args__returns__escaped_quotes(void** state) +{ + char* inp = "/cmd \"Thor \\\"The Thunderer\\\" Odinson\" arg2"; + gboolean result = FALSE; + gchar** args = parse_args(inp, 2, 2, &result); + + assert_true(result); + assert_int_equal(2, g_strv_length(args)); + assert_string_equal("Thor \"The Thunderer\" Odinson", args[0]); + assert_string_equal("arg2", args[1]); + g_strfreev(args); +} + +void +parse_args__returns__escaped_spaces(void** state) +{ + char* inp = "/cmd Thor\\ The\\ Thunderer\\ Odinson arg2"; + gboolean result = FALSE; + gchar** args = parse_args(inp, 2, 2, &result); + + assert_true(result); + assert_int_equal(2, g_strv_length(args)); + assert_string_equal("Thor The Thunderer Odinson", args[0]); + assert_string_equal("arg2", args[1]); + g_strfreev(args); +} + +void +parse_args__returns__escaped_backslash(void** state) +{ + char* inp = "/cmd \"Thor \\\\ Odinson\" arg2"; + gboolean result = FALSE; + gchar** args = parse_args(inp, 2, 2, &result); + + assert_true(result); + assert_int_equal(2, g_strv_length(args)); + assert_string_equal("Thor \\ Odinson", args[0]); + assert_string_equal("arg2", args[1]); + g_strfreev(args); +} + void count_tokens__returns__one_token(void** state) { @@ -453,6 +495,15 @@ count_tokens__returns__two_tokens_both_quoted(void** state) assert_int_equal(2, result); } +void +count_tokens__handles__escapes(void** state) +{ + char* inp = "one\\ two \"three \\\" four\""; + int result = count_tokens(inp); + + assert_int_equal(2, result); +} + void get_start__returns__first_of_one(void** state) { @@ -513,6 +564,16 @@ get_start__returns__first_two_of_three_first_and_second_quoted(void** state) g_free(result); } +void +get_start__handles__escapes(void** state) +{ + char* inp = "one\\ two three"; + char* result = get_start(inp, 2); + + assert_string_equal("one\\ two ", 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 49cfe7840..738b0f951 100644 --- a/tests/unittests/tools/test_parser.h +++ b/tests/unittests/tools/test_parser.h @@ -29,6 +29,9 @@ void parse_args_with_freetext__returns__quoted_freetext(void** state); void parse_args_with_freetext__returns__third_arg_quoted(void** state); void parse_args_with_freetext__returns__second_arg_quoted(void** state); void parse_args_with_freetext__returns__second_and_third_arg_quoted(void** state); +void parse_args__returns__escaped_quotes(void** state); +void parse_args__returns__escaped_spaces(void** state); +void parse_args__returns__escaped_backslash(void** state); void count_tokens__returns__one_token(void** state); void count_tokens__returns__one_token_quoted_no_whitespace(void** state); void count_tokens__returns__one_token_quoted_with_whitespace(void** state); @@ -36,12 +39,14 @@ void count_tokens__returns__two_tokens(void** state); 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 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); 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 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 f1d1464ab..6e57ef168 100644 --- a/tests/unittests/unittests.c +++ b/tests/unittests/unittests.c @@ -101,6 +101,7 @@ main(int argc, char* argv[]) cmocka_unit_test(strip_arg_quotes__returns__stripped_first), cmocka_unit_test(strip_arg_quotes__returns__stripped_last), cmocka_unit_test(strip_arg_quotes__returns__stripped_both), + cmocka_unit_test(strip_arg_quotes__unescapes), cmocka_unit_test(format_call_external_argv__tests__table_driven), cmocka_unit_test(unique_filename_from_url__tests__table_driven), cmocka_unit_test(string_to_verbosity__returns__correct_values), @@ -206,6 +207,9 @@ main(int argc, char* argv[]) cmocka_unit_test(parse_args_with_freetext__returns__third_arg_quoted), cmocka_unit_test(parse_args_with_freetext__returns__second_arg_quoted), cmocka_unit_test(parse_args_with_freetext__returns__second_and_third_arg_quoted), + cmocka_unit_test(parse_args__returns__escaped_quotes), + cmocka_unit_test(parse_args__returns__escaped_spaces), + cmocka_unit_test(parse_args__returns__escaped_backslash), cmocka_unit_test(count_tokens__returns__one_token), cmocka_unit_test(count_tokens__returns__one_token_quoted_no_whitespace), cmocka_unit_test(count_tokens__returns__one_token_quoted_with_whitespace), @@ -213,12 +217,14 @@ main(int argc, char* argv[]) cmocka_unit_test(count_tokens__returns__two_tokens_first_quoted), 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(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), cmocka_unit_test(get_start__returns__first_two_of_three_first_quoted), 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(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), From df1f1e5ef1f1cd93118317ad916e77511d01c9e9 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Mon, 30 Mar 2026 18:33:59 +0200 Subject: [PATCH 2/2] fix: ensure consistent unescaping and space handling Unescape any character following a backslash. This fixes autocompletion for names with escaped spaces. For example: `/msg Thor\ Odinson hello` Signed-off-by: Michael Vetter --- src/common.c | 2 +- src/tools/autocomplete.c | 2 +- src/tools/parser.c | 21 +++++++++++++++ tests/unittests/tools/test_autocomplete.c | 28 +++++++++++++++++++ tests/unittests/tools/test_autocomplete.h | 2 ++ tests/unittests/tools/test_parser.c | 33 +++++++++++++++++++++++ tests/unittests/tools/test_parser.h | 2 ++ tests/unittests/unittests.c | 4 +++ 8 files changed, 92 insertions(+), 2 deletions(-) diff --git a/src/common.c b/src/common.c index ab6f67b0d..65ec0db42 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 7cbd9141d..990df83f7 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 0cfdb122a..ab5c77cb2 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 45f03ebec..8c81c45bc 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 09ec200d3..c923eb8e0 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 e415bd03f..13248184e 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 738b0f951..f06331420 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 6e57ef168..17e3774e3 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),