mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-09 06:10:29 +01:00
Merge changes for readline support in nmcli (bgo #729846)
libreadline is now a build-time dependency. We now use it throughout nmcli when asking for user input, not only in the editor. That brings a better experience especially of 'nmcli --ask con add' and also allows TAB completion usage. https://bugzilla.gnome.org/show_bug.cgi?id=729846 https://bugzilla.redhat.com/show_bug.cgi?id=1007365
This commit is contained in:
commit
13b10607d4
8 changed files with 659 additions and 504 deletions
|
|
@ -33,6 +33,7 @@ nmcli_SOURCES = \
|
|||
nmcli_LDADD = \
|
||||
$(DBUS_LIBS) \
|
||||
$(GLIB_LIBS) \
|
||||
$(READLINE_LIBS) \
|
||||
$(top_builddir)/libnm-util/libnm-util.la \
|
||||
$(top_builddir)/libnm-glib/libnm-glib.la
|
||||
|
||||
|
|
|
|||
|
|
@ -16,16 +16,20 @@
|
|||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* (C) Copyright 2012 Red Hat, Inc.
|
||||
* (C) Copyright 2012 - 2014 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
|
@ -1026,3 +1030,81 @@ nmc_find_connection (GSList *list,
|
|||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* nmc_cleanup_readline:
|
||||
*
|
||||
* Cleanup readline when nmcli is terminated with a signal.
|
||||
* It makes sure the terminal is not garbled.
|
||||
*/
|
||||
void
|
||||
nmc_cleanup_readline (void)
|
||||
{
|
||||
rl_free_line_state ();
|
||||
rl_cleanup_after_signal ();
|
||||
}
|
||||
|
||||
/**
|
||||
* nmc_readline:
|
||||
* @prompt_fmt: prompt to print (telling user what to enter). It is standard
|
||||
* printf() format string
|
||||
* @...: a list of arguments according to the @prompt_fmt format string
|
||||
*
|
||||
* Wrapper around libreadline's readline() function.
|
||||
*
|
||||
* Returns: the user provided string. In case the user entered empty string,
|
||||
* this function returns NULL.
|
||||
*/
|
||||
char *
|
||||
nmc_readline (const char *prompt_fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *prompt, *str;
|
||||
|
||||
va_start (args, prompt_fmt);
|
||||
prompt = g_strdup_vprintf (prompt_fmt, args);
|
||||
va_end (args);
|
||||
|
||||
str = readline (prompt);
|
||||
/* Return NULL, not empty string */
|
||||
if (str && *str == '\0') {
|
||||
g_free (str);
|
||||
str = NULL;
|
||||
}
|
||||
|
||||
if (str && *str)
|
||||
add_history (str);
|
||||
|
||||
g_free (prompt);
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* nmc_rl_gen_func_basic:
|
||||
* @text: text to complete
|
||||
* @state: readline state; says whether start from scratch (state == 0)
|
||||
* @words: strings for completion
|
||||
*
|
||||
* Basic function generating list of completion strings for readline.
|
||||
* See e.g. http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC49
|
||||
*/
|
||||
char *
|
||||
nmc_rl_gen_func_basic (char *text, int state, const char **words)
|
||||
{
|
||||
static int list_idx, len;
|
||||
const char *name;
|
||||
|
||||
if (!state) {
|
||||
list_idx = 0;
|
||||
len = strlen (text);
|
||||
}
|
||||
|
||||
/* Return the next name which partially matches one from the 'words' list. */
|
||||
while ((name = words[list_idx])) {
|
||||
list_idx++;
|
||||
|
||||
if (strncmp (name, text, len) == 0)
|
||||
return g_strdup (name);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,4 +59,8 @@ NMConnection *nmc_find_connection (GSList *list,
|
|||
const char *filter_val,
|
||||
GSList **start);
|
||||
|
||||
void nmc_cleanup_readline (void);
|
||||
char *nmc_readline (const char *prompt_fmt, ...) G_GNUC_PRINTF (1, 2);
|
||||
char *nmc_rl_gen_func_basic (char *text, int state, const char **words);
|
||||
|
||||
#endif /* NMC_COMMON_H */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -14,7 +14,7 @@
|
|||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* (C) Copyright 2010 Red Hat, Inc.
|
||||
* (C) Copyright 2010 - 2014 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#ifndef NMC_CONNECTIONS_H
|
||||
|
|
@ -24,6 +24,4 @@
|
|||
|
||||
NMCResultCode do_connections (NmCli *nmc, int argc, char **argv);
|
||||
|
||||
void nmc_cleanup_readline (void);
|
||||
|
||||
#endif /* NMC_CONNECTIONS_H */
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <readline/readline.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
|
@ -65,6 +66,8 @@
|
|||
#include "common.h"
|
||||
#include "devices.h"
|
||||
|
||||
/* define some prompts */
|
||||
#define PROMPT_INTERFACE _("Interface: ")
|
||||
|
||||
/* Available fields for 'device status' */
|
||||
static NmcOutputField nmc_fields_dev_status[] = {
|
||||
|
|
@ -1381,10 +1384,9 @@ do_device_connect (NmCli *nmc, int argc, char **argv)
|
|||
nmc->timeout = 90;
|
||||
|
||||
if (argc == 0) {
|
||||
if (nmc->ask) {
|
||||
ifname = ifname_ask = nmc_get_user_input (_("Interface: "));
|
||||
// TODO: list available devices when just Enter is pressed ?
|
||||
}
|
||||
if (nmc->ask)
|
||||
ifname = ifname_ask = nmc_readline (PROMPT_INTERFACE);
|
||||
|
||||
if (!ifname_ask) {
|
||||
g_string_printf (nmc->return_text, _("Error: No interface specified."));
|
||||
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
|
||||
|
|
@ -1516,10 +1518,9 @@ do_device_disconnect (NmCli *nmc, int argc, char **argv)
|
|||
nmc->timeout = 10;
|
||||
|
||||
if (argc == 0) {
|
||||
if (nmc->ask) {
|
||||
ifname = ifname_ask = nmc_get_user_input (_("Interface: "));
|
||||
// TODO: list available devices when just Enter is pressed ?
|
||||
}
|
||||
if (nmc->ask)
|
||||
ifname = ifname_ask = nmc_readline (PROMPT_INTERFACE);
|
||||
|
||||
if (!ifname_ask) {
|
||||
g_string_printf (nmc->return_text, _("Error: No interface specified."));
|
||||
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
|
||||
|
|
@ -2022,7 +2023,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
|
|||
argv++;
|
||||
} else {
|
||||
if (nmc->ask) {
|
||||
ssid_ask = nmc_get_user_input (_("SSID or BSSID: "));
|
||||
ssid_ask = nmc_readline (_("SSID or BSSID: "));
|
||||
param_user = ssid_ask ? ssid_ask : "";
|
||||
bssid1_arr = nm_utils_hwaddr_atoba (param_user, ARPHRD_ETHER);
|
||||
}
|
||||
|
|
@ -2201,7 +2202,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
|
|||
if (ap_flags & NM_802_11_AP_FLAGS_PRIVACY) {
|
||||
/* Ask for missing password when one is expected and '--ask' is used */
|
||||
if (!password && nmc->ask)
|
||||
password = passwd_ask = nmc_get_user_input (_("Password: "));
|
||||
password = passwd_ask = nmc_readline (_("Password: "));
|
||||
|
||||
if (password) {
|
||||
if (!connection)
|
||||
|
|
@ -2560,11 +2561,82 @@ do_device_wimax (NmCli *nmc, int argc, char **argv)
|
|||
}
|
||||
#endif
|
||||
|
||||
static gboolean
|
||||
is_single_word (const char* line)
|
||||
{
|
||||
size_t n1, n2, n3;
|
||||
|
||||
n1 = strspn (line, " \t");
|
||||
n2 = strcspn (line+n1, " \t\0") + n1;
|
||||
n3 = strspn (line+n2, " \t");
|
||||
|
||||
if (n3 == 0)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Global variable defined in nmcli.c */
|
||||
extern NmCli nm_cli;
|
||||
|
||||
static char *
|
||||
gen_func_ifnames (char *text, int state)
|
||||
{
|
||||
int i, j = 0;
|
||||
const GPtrArray *devices;
|
||||
const char **ifnames;
|
||||
char *ret;
|
||||
|
||||
nm_cli.get_client (&nm_cli);
|
||||
devices = nm_client_get_devices (nm_cli.client);
|
||||
if (!devices || devices->len < 1)
|
||||
return NULL;
|
||||
|
||||
ifnames = g_new (const char *, devices->len + 1);
|
||||
for (i = 0; i < devices->len; i++) {
|
||||
NMDevice *dev = g_ptr_array_index (devices, i);
|
||||
const char *ifname = nm_device_get_iface (dev);
|
||||
ifnames[j++] = ifname;
|
||||
}
|
||||
ifnames[j] = NULL;
|
||||
|
||||
ret = nmc_rl_gen_func_basic (text, state, ifnames);
|
||||
|
||||
g_free (ifnames);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char **
|
||||
nmcli_device_tab_completion (char *text, int start, int end)
|
||||
{
|
||||
char **match_array = NULL;
|
||||
CPFunction *generator_func = NULL;
|
||||
|
||||
/* Disable readline's default filename completion */
|
||||
rl_attempted_completion_over = 1;
|
||||
|
||||
/* Disable appending space after completion */
|
||||
rl_completion_append_character = '\0';
|
||||
|
||||
if (!is_single_word (rl_line_buffer))
|
||||
return NULL;
|
||||
|
||||
if (g_strcmp0 (rl_prompt, PROMPT_INTERFACE) == 0)
|
||||
generator_func = gen_func_ifnames;
|
||||
|
||||
if (generator_func)
|
||||
match_array = rl_completion_matches (text, generator_func);
|
||||
|
||||
return match_array;
|
||||
}
|
||||
|
||||
NMCResultCode
|
||||
do_devices (NmCli *nmc, int argc, char **argv)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
rl_attempted_completion_function = (CPPFunction *) nmcli_device_tab_completion;
|
||||
|
||||
if (argc == 0) {
|
||||
if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
|
||||
goto opt_error;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
#include "nmcli.h"
|
||||
#include "utils.h"
|
||||
#include "common.h"
|
||||
#include "connections.h"
|
||||
#include "devices.h"
|
||||
#include "network-manager.h"
|
||||
|
|
|
|||
|
|
@ -387,6 +387,14 @@ PKG_CHECK_MODULES(UUID, uuid)
|
|||
AC_SUBST(UUID_CFLAGS)
|
||||
AC_SUBST(UUID_LIBS)
|
||||
|
||||
dnl
|
||||
dnl Checks for readline library - used by nmcli
|
||||
dnl
|
||||
#PKG_CHECK_MODULES(READLINE, readline)
|
||||
AC_CHECK_LIB(readline, readline, [READLINE_LIBS=-lreadline], [AC_MSG_ERROR(readline library is required)])
|
||||
AC_CHECK_HEADERS(readline/readline.h, [], [AC_MSG_ERROR(readline/readline.h - readline devel files required)])
|
||||
AC_SUBST(READLINE_LIBS)
|
||||
|
||||
# Intel WiMAX SDK checks
|
||||
PKG_CHECK_MODULES(IWMX_SDK, [libiWmxSdk-0 >= 1.5.1], [have_wimax=yes],[have_wimax=no])
|
||||
AC_ARG_ENABLE(wimax, AS_HELP_STRING([--enable-wimax], [enable WiMAX support]),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue