diff --git a/config.h.meson b/config.h.meson index 432402d5b4..5979793e5c 100644 --- a/config.h.meson +++ b/config.h.meson @@ -255,3 +255,9 @@ #mesondefine HAVE_PIDFD_OPEN #mesondefine HAVE_PIDFD_SEND_SIGNAL #mesondefine HAVE_RT_SIGQUEUEINFO + +/* Define to 1 if you want to use -ledit, otherwise 0 for default -lreadline. */ +#mesondefine HAVE_EDITLINE_READLINE + +/* Define to 1 if you have history support from -lreadline. */ +#mesondefine HAVE_READLINE_HISTORY diff --git a/configure.ac b/configure.ac index c4e345224c..8f40066be0 100644 --- a/configure.ac +++ b/configure.ac @@ -1043,10 +1043,24 @@ fi PKG_CHECK_MODULES(LIBNDP, [libndp]) +AC_ARG_WITH(readline, + AS_HELP_STRING([--with-readline=auto|libreadline|libedit|none], [Using libreadline (auto) or libedit]), + [], [with_readline=auto]) + +if test "$with_readline" != "none" -a "$with_readline" != "auto" -a "$with_readline" != "libreadline" -a "$with_readline" != "libedit"; then + AC_MSG_ERROR([invalid --with-readline option]) +fi + +if test "$with_readline" != "none"; then + AX_LIB_READLINE + if test -z "${READLINE_LIBS}"; then + AC_MSG_FAILURE([readline library not found]) + fi +fi + AC_ARG_WITH(nmcli, AS_HELP_STRING([--with-nmcli=yes|no], [Build nmcli])) if test "$with_nmcli" != no; then - AX_LIB_READLINE build_nmcli=yes else build_nmcli=no @@ -1420,4 +1434,5 @@ echo " crypto: $with_crypto (have-gnutls: $have_crypto_gnutls, have-nss: $have_ echo " sanitizers: $sanitizers" echo " Mozilla Public Suffix List: $with_libpsl" echo " eBPF: $have_ebpf" +echo " readline: $with_readline" echo diff --git a/m4/ax_lib_readline.m4 b/m4/ax_lib_readline.m4 index 7efa947e47..3365e0b979 100644 --- a/m4/ax_lib_readline.m4 +++ b/m4/ax_lib_readline.m4 @@ -65,7 +65,16 @@ AC_DEFUN([AX_LIB_READLINE], [ AC_CACHE_CHECK([for a readline compatible library], ax_cv_lib_readline, [ ORIG_LIBS="$LIBS" - for readline_lib in readline edit editline; do + + search_readlines="readline edit" + if test "$with_readline" = "libreadline"; then + search_readlines="readline" + fi + if test "$with_readline" = "libedit"; then + search_readlines="edit" + fi + for readline_lib in $search_readlines; do + # prefer ncurses since we use it for nmtui too for termcap_lib in "" termcap curses ncurses; do if test -z "$termcap_lib"; then TRY_LIB="-l$readline_lib" @@ -93,16 +102,33 @@ AC_DEFUN([AX_LIB_READLINE], [ AC_SUBST(READLINE_LIBS) AC_DEFINE(HAVE_LIBREADLINE, 1, [Define if you have a readline compatible library]) - AC_CHECK_HEADERS(readline.h readline/readline.h) + + if test "$with_readline" = "libedit"; then + AC_DEFINE(HAVE_EDITLINE_READLINE, 1, + [Explicitly set to 1 when libedit shall be used]) + else + AC_DEFINE(HAVE_EDITLINE_READLINE, 0, + [By default the libreadline is used as readline library]) + + fi + + ORIG_LIBS="$LIBS" + LIBS="$ORIG_LIBS $ax_cv_lib_readline" AC_CACHE_CHECK([whether readline supports history], ax_cv_lib_readline_history, [ ax_cv_lib_readline_history="no" - AC_LINK_IFELSE([AC_LANG_CALL([], [add_history])], [ax_cv_lib_readline_history="yes"]) + AC_LINK_IFELSE([AC_LANG_CALL([], [history_set_history_state])], + [ax_cv_lib_readline_history="yes"]) ]) + LIBS=$ORIG_LIBS + if test "$ax_cv_lib_readline_history" = "yes"; then AC_DEFINE(HAVE_READLINE_HISTORY, 1, - [Define if your readline library has \`add_history']) - AC_CHECK_HEADERS(history.h readline/history.h) + [Define if your readline library has \`history_set_history_state']) + AC_CHECK_HEADERS(readline/history.h histedit.h) + else + AC_DEFINE(HAVE_READLINE_HISTORY, 0, + [Explicitly set to 0 when libreadline shall not be used]) fi fi ])dnl diff --git a/meson.build b/meson.build index 6082828194..0c5a28fbc6 100644 --- a/meson.build +++ b/meson.build @@ -733,24 +733,33 @@ if enable_concheck endif config_h.set10('WITH_CONCHECK', enable_concheck) +config_h.set10('HAVE_READLINE_HISTORY', false) +config_h.set10('HAVE_EDITLINE_READLINE', false) +enable_readline = get_option('readline') +if enable_readline != 'none' + if enable_readline == 'libreadline' or enable_readline == 'auto' + readline = cc.find_library('readline') + if readline.found() + readline_dep = declare_dependency(link_args: '-lreadline') + config_h.set10('HAVE_READLINE_HISTORY', true) + else + assert(enable_readline == 'auto', 'libreadline was not found') + endif + endif + if enable_readline == 'libedit' or (enable_readline == 'auto' and not readline.found()) + edit = dependency('libedit') + if edit.found() + readline_dep = declare_dependency(link_args: '-ledit') + config_h.set10('HAVE_EDITLINE_READLINE', true) + else + assert(enable_readline == 'auto', 'libedit was not found') + endif + endif +endif + enable_nmcli = get_option('nmcli') if enable_nmcli - # FIXME: check for readline - # AX_LIB_READLINE - readline_dep = declare_dependency(link_args: '-lreadline') - ''' - foreach readline_lib: ['-lreadline', '-ledit', '-leditline'] - if not is_variable('readline_dep') - foreach termcap_lib: ['', '-lncurses', '-ltermcap', '-lcurses'] - test_dep = declare_dependency(link_args: ' '.join([readline_lib, termcap_lib])) - if cc.has_function('readline', dependencies: test_dep) and cc.has_header('readline', dependencies: test_dep) - readline_dep = test_dep - endif - endforeach - endif - endforeach - ''' - assert(readline_dep.found(), 'readline library with terminfo support is required (one of readline, edit, or editline, AND one of ncurses, curses, or termcap)') + assert(enable_readline != 'none', 'readline library with terminfo support is required (one of readline, edit, or editline, AND one of ncurses, curses, or termcap)') endif enable_nmtui = get_option('nmtui') @@ -1086,4 +1095,5 @@ output += ' sanitizers: ' + get_option('b_sanitize') + '\n' output += ' Mozilla Public Suffix List: ' + enable_libpsl.to_string() + '\n' output += ' vapi: ' + enable_vapi.to_string() + '\n' output += ' ebpf: ' + enable_ebpf.to_string() + '\n' +output += ' readline: ' + enable_readline + '\n' message(output) diff --git a/meson_options.txt b/meson_options.txt index 14ed4077a0..76a131ad05 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -72,3 +72,4 @@ option('ld_gc', type: 'boolean', value: true, description: 'Enable garbage colle option('libpsl', type: 'boolean', value: true, description: 'Link against libpsl') option('crypto', type: 'combo', choices: ['nss', 'gnutls'], value: 'nss', description: 'Cryptography library to use for certificate and key operations') option('qt', type: 'boolean', value: true, description: 'enable Qt examples') +option('readline', type: 'combo', choices: ['auto', 'libreadline', 'libedit', 'none'], description: 'Using readline (auto) or libedit)') diff --git a/src/nmcli/agent.c b/src/nmcli/agent.c index a0b23dd19b..842843995a 100644 --- a/src/nmcli/agent.c +++ b/src/nmcli/agent.c @@ -7,9 +7,12 @@ #include #include +#if HAVE_EDITLINE_READLINE +#include +#else #include #include - +#endif #include "common.h" #include "utils.h" #include "libnmc-base/nm-secret-agent-simple.h" @@ -53,8 +56,7 @@ usage_agent_all(void) /* for pre-filling a string to readline prompt */ static char *pre_input_deftext; -static int -set_deftext(void) +static int set_deftext(_NMC_RL_STARTUPHOOK_ARGS) { if (pre_input_deftext && rl_startup_hook) { rl_insert_text(pre_input_deftext); diff --git a/src/nmcli/common.c b/src/nmcli/common.c index a01a2f7247..67bbb6a179 100644 --- a/src/nmcli/common.c +++ b/src/nmcli/common.c @@ -10,9 +10,12 @@ #include #include #include -#include +#if HAVE_EDITLINE_READLINE +#include +#else #include - +#include +#endif #include "libnm-client-aux-extern/nm-libnm-aux.h" #include "libnmc-base/nm-vpn-helpers.h" @@ -1007,10 +1010,14 @@ nmc_readline_echo(const NmcConfig *nmc_config, gboolean echo_on, const char *pro va_list args; gs_free char *prompt = NULL; char * str; +#if HAVE_READLINE_HISTORY nm_auto_free HISTORY_STATE *saved_history = NULL; HISTORY_STATE passwd_history = { 0, }; +#else + int start, curpos; +#endif va_start(args, prompt_fmt); prompt = g_strdup_vprintf(prompt_fmt, args); @@ -1020,8 +1027,12 @@ nmc_readline_echo(const NmcConfig *nmc_config, gboolean echo_on, const char *pro /* Hide the actual password */ if (!echo_on) { +#if HAVE_READLINE_HISTORY saved_history = history_get_history_state(); history_set_history_state(&passwd_history); +#else + start = where_history(); +#endif /* stifling history is important as it tells readline to * not store anything, otherwise sensitive data could be * leaked */ @@ -1034,7 +1045,13 @@ nmc_readline_echo(const NmcConfig *nmc_config, gboolean echo_on, const char *pro /* Restore the non-hiding behavior */ if (!echo_on) { rl_redisplay_function = rl_redisplay; +#if HAVE_READLINE_HISTORY history_set_history_state(saved_history); +#else + curpos = where_history(); + while (curpos > start) + remove_history(curpos--); +#endif } return str; @@ -1136,8 +1153,7 @@ nmc_rl_gen_func_ifnames(const char *text, int state) /* for pre-filling a string to readline prompt */ char *nmc_rl_pre_input_deftext; -int -nmc_rl_set_deftext(void) +int nmc_rl_set_deftext(_NMC_RL_STARTUPHOOK_ARGS) { if (nmc_rl_pre_input_deftext && rl_startup_hook) { rl_insert_text(nmc_rl_pre_input_deftext); diff --git a/src/nmcli/common.h b/src/nmcli/common.h index a479a45569..908dad0672 100644 --- a/src/nmcli/common.h +++ b/src/nmcli/common.h @@ -50,9 +50,17 @@ char * nmc_rl_gen_func_ifnames(const char *text, int state); gboolean nmc_get_in_readline(void); void nmc_set_in_readline(gboolean in_readline); +#if HAVE_EDITLINE_READLINE +/* libedit has different signature for rl_startup_hook function */ +#define _NMC_RL_STARTUPHOOK_ARGS const char *c, int i +#else +/* By default the libreadline shall be used */ +#define _NMC_RL_STARTUPHOOK_ARGS void +#endif + /* for pre-filling a string to readline prompt */ extern char *nmc_rl_pre_input_deftext; -int nmc_rl_set_deftext(void); +int nmc_rl_set_deftext(_NMC_RL_STARTUPHOOK_ARGS); char *nmc_parse_lldp_capabilities(guint value); diff --git a/src/nmcli/connections.c b/src/nmcli/connections.c index 87cd2a8b94..70fa89608b 100644 --- a/src/nmcli/connections.c +++ b/src/nmcli/connections.c @@ -11,8 +11,12 @@ #include #include #include +#if HAVE_EDITLINE_READLINE +#include +#else #include #include +#endif #include #include "libnm-glib-aux/nm-dbus-aux.h" @@ -5755,6 +5759,9 @@ finish: /*****************************************************************************/ /* Functions for readline TAB completion in editor */ +#if HAVE_EDITLINE_READLINE +#define uuid_display_hook ((void (*)(void)) NULL) +#else static void uuid_display_hook(char **array, int len, int max_len) { @@ -5778,6 +5785,7 @@ uuid_display_hook(char **array, int len, int max_len) rl_display_match_list(array, len, max_len + max + 3); rl_forced_update_display(); } +#endif static char * gen_nmcli_cmds_menu(const char *text, int state) @@ -6449,8 +6457,10 @@ gen_property_values(const char *text, int state) return nmc_rl_gen_func_basic(text, state, avals); } +#if !HAVE_EDITLINE_READLINE /* from readline */ extern int rl_complete_with_tilde_expansion; +#endif /* * Attempt to complete on the contents of TEXT. START and END show the @@ -6478,8 +6488,10 @@ nmcli_editor_tab_completion(const char *text, int start, int end) /* Disable default filename completion */ rl_attempted_completion_over = 1; +#if !HAVE_EDITLINE_READLINE /* Enable tilde expansion when filenames are completed */ rl_complete_with_tilde_expansion = 1; +#endif /* Filter out possible ANSI color escape sequences */ prompt_tmp = nmc_filter_out_colors((const char *) rl_prompt); @@ -9613,8 +9625,10 @@ nmcli_con_tab_completion(const char *text, int start, int end) nmc_tab_completion.words = _meta_abstract_complete(info, text); generator_func = _meta_abstract_generator; } else if (nm_streq0(rl_prompt, PROMPT_IMPORT_FILE)) { - rl_attempted_completion_over = 0; + rl_attempted_completion_over = 0; +#if !HAVE_EDITLINE_READLINE rl_complete_with_tilde_expansion = 1; +#endif } else if (nm_streq0(rl_prompt, PROMPT_VPN_CONNECTION)) { generator_func = gen_vpn_ids; } diff --git a/src/nmcli/devices.c b/src/nmcli/devices.c index 33be4e7a6d..00339868b6 100644 --- a/src/nmcli/devices.c +++ b/src/nmcli/devices.c @@ -9,7 +9,11 @@ #include #include +#if HAVE_EDITLINE_READLINE +#include +#else #include +#endif #include #include "libnm-glib-aux/nm-secret-utils.h" diff --git a/src/nmcli/nmcli.c b/src/nmcli/nmcli.c index 9c6a791187..b2b2cd8143 100644 --- a/src/nmcli/nmcli.c +++ b/src/nmcli/nmcli.c @@ -14,8 +14,12 @@ #include #include #include +#if HAVE_EDITLINE_READLINE +#include +#else #include #include +#endif #include "libnmc-base/nm-client-utils.h"