Fix tab completion for commands like: ``` cd "[2025] Tax Documents" ``` while still allowing tab completion for commands like: ``` copy file1+file2 file3 ``` to work.
Without this change, tab completion for cd "[2025] Tax Documents" yields cd "[2025][2025] Tax Documents". Completed text is inserted into the incorrect location due to the closing ']' which was erroneously interpreted as a delimiter before this change.
-- v3: cmd: tab completion: Treat most delimiters as literals if user specified quotes.
From: Joe Souza jsouza@yahoo.com
--- programs/cmd/wcmdmain.c | 43 ++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index fbec86a481a..996d438164d 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -193,12 +193,25 @@ static void find_insert_pos(const WCHAR *inputBuffer, int len, SEARCH_CONTEXT *s /* Handle paths here. Find last '\' or other delimiter. * If not found then insert pos is the same as search pos. */ - while (cc > sc->search_pos && !wcschr(INTRA_PATH_DELIMS, inputBuffer[cc])) { - cc--; - } + if (sc->user_specified_quotes) { + /* If the user specified quotes then treat the usual delimiters as literals + * and ignore them. + */ + while (cc > sc->search_pos && inputBuffer[cc] != L'\') { + cc--; + }
- if (inputBuffer[cc] == L'"' || wcschr(INTRA_PATH_DELIMS, inputBuffer[cc])) { - cc++; + if (inputBuffer[cc] == L'"' || inputBuffer[cc] == L'\') { + cc++; + } + } else { + while (cc > sc->search_pos && !wcschr(INTRA_PATH_DELIMS, inputBuffer[cc])) { + cc--; + } + + if (inputBuffer[cc] == L'"' || wcschr(INTRA_PATH_DELIMS, inputBuffer[cc])) { + cc++; + } }
sc->insert_pos = cc; @@ -257,6 +270,18 @@ static void get_next_matching_directory_entry(SEARCH_CONTEXT *sc, BOOL reverse) } }
+static BOOL has_delimiters(WCHAR *path, const WCHAR *delims) +{ + BOOL ret = FALSE; + int cc = 0; + + while (path[cc] && !ret) { + ret = wcschr(delims, path[cc++]) ? TRUE : FALSE; + } + + return ret; +} + static void update_input_buffer(WCHAR *inputBuffer, const DWORD inputBufferLength, SEARCH_CONTEXT *sc) { BOOL needQuotes = FALSE; @@ -266,19 +291,19 @@ static void update_input_buffer(WCHAR *inputBuffer, const DWORD inputBufferLengt /* We have found the insert position for the results. Terminate the string here. */ inputBuffer[sc->insert_pos] = L'\0';
- /* If there are no spaces in the path then we can remove quotes when appending + /* If there are no spaces or delimiters in the path then we can remove quotes when appending * the search result, unless the search result itself requires them. */ - if (sc->have_quotes && !sc->user_specified_quotes && !wcschr(&inputBuffer[sc->search_pos], L' ')) { + if (sc->have_quotes && !sc->user_specified_quotes && !has_delimiters(&inputBuffer[sc->search_pos], PATH_SEPARATION_DELIMS)) { TRACE("removeQuotes = TRUE\n"); removeQuotes = TRUE; }
/* Online documentation states that paths or filenames should be quoted if they are long * file names or contain spaces. In practice, modern Windows seems to quote paths/files - * only if they contain spaces. + * only if they contain spaces or delimiters. */ - needQuotes = wcschr(sc->fd[sc->current_entry].cFileName, L' ') ? TRUE : FALSE; + needQuotes = has_delimiters(sc->fd[sc->current_entry].cFileName, PATH_SEPARATION_DELIMS); len = lstrlenW(inputBuffer); /* Remove starting quotes, if able. */ if (removeQuotes && !needQuotes) {
Also fixed cases where tab completion filename results should be quoted. Prior to the change, quotes were added only if the filename contained spaces. After the change, quotes are added if the filename contains spaces or delimiters. For example, native Windows will add quotes around a file named "fil]e3". Now Wine does the same.