Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions src/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) != '\0')) {
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
Expand Down
21 changes: 19 additions & 2 deletions src/tools/autocomplete.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) != '\0')) {
p++;
}
g_string_append_c(unescaped, *p);
}
ac->search_str = g_string_free(unescaped, FALSE);

found = _search(ac, ac->items, quote, NEXT);

return found;
Expand Down Expand Up @@ -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);
Expand Down
123 changes: 89 additions & 34 deletions src/tools/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = &copy[0];
int token_size = 0;
GString* current_token = g_string_new("");
int num_tokens = 0;
GSList* tokens = NULL;

Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -245,9 +264,23 @@ 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++;
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) {
Expand Down Expand Up @@ -276,15 +309,37 @@ 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) {
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) {
Expand Down
12 changes: 12 additions & 0 deletions tests/unittests/test_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
1 change: 1 addition & 0 deletions tests/unittests/test_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
28 changes: 28 additions & 0 deletions tests/unittests/tools/test_autocomplete.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
2 changes: 2 additions & 0 deletions tests/unittests/tools/test_autocomplete.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading
Loading