On a Tuesday in 2021, Michal Privoznik wrote:
The way our completer callbacks work is that they return all
possible candidates and then vshCompleterFilter() is called to
prune the list of all candidates removing those which don't match
user's input. This allows us to have simpler completer callbacks
as their only job is to fetch all possible candidates.
Anyway, if the completion candidate we're returning contains a
space, it has to be escaped (shell like escaping), unless there
is already a quote character (single quote or double quote).
But ordering is critical. Completer callback returns string
without any escaping, but the filter function sees the user input
escaped. For instance, if user's input is "domain with
space<TAB>" then the filtering function gets "domain\ with\
space" as user's input but completer returns "domain with space".
Since these two strings don't match the filtering function
removes this candidate from the list. What we need to do is to
escape strings before calling the filtering function. This way,
the filtering function will see two same strings.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
tools/vsh.c | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/tools/vsh.c b/tools/vsh.c
index abbd323e24..27c201389d 100644
--- a/tools/vsh.c
+++ b/tools/vsh.c
@@ -2751,10 +2751,26 @@ vshReadlineParse(const char *text, int state)
partial,
opt->completer_flags);
+ /* Escape completions, if needed (i.e. argument
+ * we completing wasn't started with a quote
we are completing
Jano
+ * character). This also enables filtering done
+ * below to work properly. */
+ if (completer_list &&
+ !rl_completion_quote_character) {
+ size_t i;
+
+ for (i = 0; completer_list[i]; i++) {
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+ virBufferEscape(&buf, '\\', " ",
"%s", completer_list[i]);
+ VIR_FREE(completer_list[i]);
+ completer_list[i] = virBufferContentAndReset(&buf);
+ }
+ }
+
/* For string list returned by completer we have to do
* filtering based on @text because completer returns all
* possible strings. */
-
if (completer_list &&
(vshCompleterFilter(&completer_list, text) < 0 ||
virStringListMerge(&list, &completer_list) < 0)) {