From 16693771102e2efafeb06def8b89ee864955b437 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 23 Aug 2018 17:56:21 +0200 Subject: [PATCH 1/3] cli: fix connection type completion on connection add The array returned by the completion function follows a special convention. If the first element is set, it is used as the completion. Otherwise, the remaining entries are the possible completions. _meta_abstract_complete() just returned an array of matching words and so the first element was always used as completion. Instead, we must use rl_completion_matches() to generate the array passing a generator function. https://bugzilla.redhat.com/show_bug.cgi?id=1588952 --- clients/cli/connections.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 3808b86dd2..dda40993ca 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -801,6 +801,7 @@ typedef struct { NMConnection *connection; NMSetting *setting; const char *property; + char **words; } TabCompletionInfo; static TabCompletionInfo nmc_tab_completion; @@ -3677,6 +3678,18 @@ _meta_abstract_complete (const NMMetaAbstractInfo *abstract_info, const char *te return NULL; } +static char * +_meta_abstract_generator (const char *text, int state) +{ + if (nmc_tab_completion.words) { + return nmc_rl_gen_func_basic (text, + state, + (const char *const *) nmc_tab_completion.words); + } + + return NULL; +} + static void _meta_abstract_get (const NMMetaAbstractInfo *abstract_info, const NMMetaSettingInfoEditor **out_setting_info, @@ -4744,6 +4757,7 @@ nmcli_con_add_tab_completion (const char *text, int start, int end) rl_compentry_func_t *generator_func = NULL; gs_free char *no = g_strdup_printf ("[%s]: ", _("no")); gs_free char *yes = g_strdup_printf ("[%s]: ", _("yes")); + const NMMetaAbstractInfo *info; /* Disable readline's default filename completion */ rl_attempted_completion_over = 1; @@ -4778,12 +4792,13 @@ nmcli_con_add_tab_completion (const char *text, int start, int end) } else { if ( property_info->prompt && g_str_has_prefix (rl_prompt, property_info->prompt)) { - char **values; - - values = _meta_abstract_complete ((const NMMetaAbstractInfo *) property_info, text); - if (values) - return values; - goto next; + info = (const NMMetaAbstractInfo *) property_info; + nmc_tab_completion.words = _meta_abstract_complete (info, text); + if (nmc_tab_completion.words) { + match_array = rl_completion_matches (text, _meta_abstract_generator); + nm_clear_pointer (&nmc_tab_completion.words, g_strfreev); + } + return match_array; } } } @@ -8931,6 +8946,7 @@ nmcli_con_tab_completion (const char *text, int start, int end) { char **match_array = NULL; rl_compentry_func_t *generator_func = NULL; + const NMMetaAbstractInfo *info; /* Disable readline's default filename completion */ rl_attempted_completion_over = 1; @@ -8948,7 +8964,9 @@ nmcli_con_tab_completion (const char *text, int start, int end) } else if (g_strcmp0 (rl_prompt, PROMPT_ACTIVE_CONNECTIONS) == 0) { generator_func = gen_func_active_connection_names; } else if (g_strcmp0 (rl_prompt, NM_META_TEXT_PROMPT_VPN_TYPE) == 0) { - return _meta_abstract_complete ((const NMMetaAbstractInfo *) nm_meta_property_info_vpn_service_type, text); + info = (const NMMetaAbstractInfo *) nm_meta_property_info_vpn_service_type; + nmc_tab_completion.words = _meta_abstract_complete (info, text); + generator_func = _meta_abstract_generator; } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_FILE) == 0) { rl_attempted_completion_over = 0; rl_complete_with_tilde_expansion = 1; @@ -8959,6 +8977,7 @@ nmcli_con_tab_completion (const char *text, int start, int end) if (generator_func) match_array = rl_completion_matches (text, generator_func); + g_clear_pointer (&nmc_tab_completion.words, g_strfreev); return match_array; } From 2f60fdf19e2d0e29eedff8cff6574c0499373993 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 24 Aug 2018 09:52:09 +0200 Subject: [PATCH 2/3] cli: autocomplete connection type even if it has an alias Before, we would not autocomplete connection types that have an alias: Connection type: 6lowpan cdma macvlan vlan 802-11-olpc-mesh dummy olpc-mesh vpn 802-11-wireless ethernet ovs-bridge vxlan 802-3-ethernet generic ovs-interface wifi adsl gsm ovs-port wimax bluetooth infiniband pppoe wpan bond ip-tunnel team bridge macsec tun Connection type: 8 [-> no completion] Don't treat the default connection type (for example, "802-3-ethernet") in a special way and allow it to be autocompleted, because we already display it when the user did not enter any text. --- clients/common/nm-meta-setting-desc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 29eb98a775..09f30641fa 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -2485,11 +2485,9 @@ _complete_fcn_connection_type (ARGS_COMPLETE_FCN) if (!text || strncmp (text, v, text_len) == 0) result[j++] = g_strdup (v); } - if (!text || !*text || !v) { - v = setting_info->general->setting_name; - if (!text || strncmp (text, v, text_len) == 0) - result[j++] = g_strdup (v); - } + v = setting_info->general->setting_name; + if (!text || strncmp (text, v, text_len) == 0) + result[j++] = g_strdup (v); } if (j) result[j++] = NULL; From d868788ee43c6b57c06ba013097bf86f9173eb3f Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 24 Aug 2018 11:21:47 +0200 Subject: [PATCH 3/3] cli: fix autocompletion for connection commands Autocompletion doesn't work in some cases because we present a prompt ending with ":", but compare it with the string without ":" in the autocomplete function. Fix this. While at it, also add missing colon after prompt where needed. --- clients/cli/connections.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/clients/cli/connections.c b/clients/cli/connections.c index dda40993ca..ad0b767215 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -67,10 +67,10 @@ struct _OptionInfo { /* define some other prompts */ -#define PROMPT_CONNECTION _("Connection (name, UUID, or path)") -#define PROMPT_VPN_CONNECTION _("VPN connection (name, UUID, or path)") -#define PROMPT_CONNECTIONS _("Connection(s) (name, UUID, or path)") -#define PROMPT_ACTIVE_CONNECTIONS _("Connection(s) (name, UUID, path or apath)") +#define PROMPT_CONNECTION _("Connection (name, UUID, or path): ") +#define PROMPT_VPN_CONNECTION _("VPN connection (name, UUID, or path): ") +#define PROMPT_CONNECTIONS _("Connection(s) (name, UUID, or path): ") +#define PROMPT_ACTIVE_CONNECTIONS _("Connection(s) (name, UUID, path or apath): ") #define BASE_PROMPT "nmcli> " @@ -2723,7 +2723,7 @@ do_connection_up (NmCli *nmc, int argc, char **argv) /* nmc_do_cmd() should not call this with argc=0. */ g_assert (!nmc->complete); - line = nmc_readline ("%s: ", PROMPT_CONNECTION); + line = nmc_readline (PROMPT_CONNECTION); nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); g_free (line); argv_ptr = &arg_arr; @@ -8320,7 +8320,7 @@ do_connection_clone (NmCli *nmc, int argc, char **argv) /* nmc_do_cmd() should not call this with argc=0. */ g_assert (!nmc->complete); - line = nmc_readline ("%s: ", PROMPT_CONNECTION); + line = nmc_readline (PROMPT_CONNECTION); nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); g_free (line); argv_ptr = &arg_arr; @@ -8430,7 +8430,7 @@ do_connection_delete (NmCli *nmc, int argc, char **argv) /* nmc_do_cmd() should not call this with argc=0. */ g_assert (!nmc->complete); - line = nmc_readline ("%s: ", PROMPT_CONNECTIONS); + line = nmc_readline (PROMPT_CONNECTIONS); nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); g_free (line); arg_ptr = arg_arr; @@ -8681,7 +8681,7 @@ do_connection_import (NmCli *nmc, int argc, char **argv) g_assert (!nmc->complete); if (nmc->ask) { - type_ask = nmc_readline (gettext (NM_META_TEXT_PROMPT_VPN_TYPE)); + type_ask = nmc_readline ("%s: ", gettext (NM_META_TEXT_PROMPT_VPN_TYPE)); filename_ask = nmc_readline (gettext (PROMPT_IMPORT_FILE)); type = type_ask = type_ask ? g_strstrip (type_ask) : NULL; filename = filename_ask = filename_ask ? g_strstrip (filename_ask) : NULL; @@ -8806,7 +8806,7 @@ do_connection_export (NmCli *nmc, int argc, char **argv) /* nmc_do_cmd() should not call this with argc=0. */ g_assert (!nmc->complete); - line = nmc_readline ("%s: ", PROMPT_VPN_CONNECTION); + line = nmc_readline (PROMPT_VPN_CONNECTION); nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); g_free (line); argv_ptr = &arg_arr; @@ -8963,7 +8963,7 @@ nmcli_con_tab_completion (const char *text, int start, int end) generator_func = gen_func_connection_names; } else if (g_strcmp0 (rl_prompt, PROMPT_ACTIVE_CONNECTIONS) == 0) { generator_func = gen_func_active_connection_names; - } else if (g_strcmp0 (rl_prompt, NM_META_TEXT_PROMPT_VPN_TYPE) == 0) { + } else if (rl_prompt && g_str_has_prefix (rl_prompt, NM_META_TEXT_PROMPT_VPN_TYPE)) { info = (const NMMetaAbstractInfo *) nm_meta_property_info_vpn_service_type; nmc_tab_completion.words = _meta_abstract_complete (info, text); generator_func = _meta_abstract_generator;