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 <jubalh@iodoru.org>
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user