diff --git a/ChangeLog.txt b/ChangeLog.txt index 95ccf3b07..35fc1750b 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1368,3 +1368,6 @@ * apps/system/readline: Add support for Unix-style tab complete toi readline. This currently works only for built-in functions.i Contributed by Nghia (2015-07-28). + * apps/system/readline and apps/nshlib: Extended the tab-completion + support to also expand NSH command names (2015-07-30). + diff --git a/include/readline.h b/include/readline.h index 1f0424341..bc2bbf175 100644 --- a/include/readline.h +++ b/include/readline.h @@ -47,6 +47,27 @@ * Pre-processor Definitions ****************************************************************************/ +#ifndef CONFIG_READLINE_MAX_BUILTINS +# define CONFIG_READLINE_MAX_BUILTINS 64 +#endif + +#ifndef CONFIG_READLINE_MAX_EXTCMDS +# define CONFIG_READLINE_MAX_EXTCMDS 64 +#endif + + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#if defined(CONFIG_READLINE_TABCOMPLETION) && defined(CONFIG_READLINE_HAVE_EXTMATCH) +struct extmatch_vtable_s +{ + CODE int (*count_matches)(FAR char *name, FAR int *matches, int namelen); + CODE FAR const char *(*getname)(int index); +}; +#endif + /**************************************************************************** * Public Data ****************************************************************************/ @@ -72,10 +93,11 @@ extern "C" * to reprint the prompt string. * * Input Parameters: - * prompt - The prompt string. + * prompt - The prompt string. This function may then be + * called with that value in order to restore the previous vtable. * * Returned values: - * None + * Returns the previous value of the prompt string * * Assumptions: * The prompt string is statically allocated a global. readline will @@ -87,11 +109,40 @@ extern "C" **************************************************************************/ #ifdef CONFIG_READLINE_TABCOMPLETION -void readline_prompt(FAR const char *prompt); +FAR const char *readline_prompt(FAR const char *prompt); #else # define readline_prompt(p) #endif +/**************************************************************************** + * Name: readline_extmatch + * + * If the applications supports a command set, then it may call this + * function in order to provide support for tab complete on these\ + * "external" commands + * + * Input Parameters: + * vtbl - Callbacks to access the external names. + * + * Returned values: + * Returns the previous vtable pointer. This function may then be + * called with that value in order to restore the previous vtable. + * + * Assumptions: + * The vtbl string is statically allocated a global. readline will + * simply remember the pointer to the structure. The structure must stay + * allocated and available. Only one instance of such a structure is + * upported. If there are multiple clients of readline, they must all + * share the same tab-completion logic (with exceptions in the case of + * the kernel build). + * + **************************************************************************/ + +#if defined(CONFIG_READLINE_TABCOMPLETION) && defined(CONFIG_READLINE_HAVE_EXTMATCH) +FAR const struct extmatch_vtable_s * + readline_extmatch(FAR const struct extmatch_vtable_s *vtbl); +#endif + /**************************************************************************** * Name: readline * diff --git a/nshlib/Kconfig b/nshlib/Kconfig index 348d55d64..d6a17d23b 100644 --- a/nshlib/Kconfig +++ b/nshlib/Kconfig @@ -8,6 +8,7 @@ config NSH_LIBRARY default n select NETUTILS_NETLIB if NET select LIBC_NETDB if NET + select READLINE_HAVE_EXTMATCH ---help--- Build the NSH support library. This is used, for example, by examples/nsh in order to implement the full NuttShell (NSH). diff --git a/nshlib/nsh.h b/nshlib/nsh.h index c8a087a8b..e87177eb4 100644 --- a/nshlib/nsh.h +++ b/nshlib/nsh.h @@ -1055,4 +1055,49 @@ void nsh_usbtrace(void); # endif #endif +/**************************************************************************** + * Name: nsh_extmatch_count + * + * Description: + * This support function is used to provide support for realine tab- + * completion logic nsh_extmatch_count() counts the number of matching + * nsh command names + * + * Input Parameters: + * name - A point to the name containing the name to be matched. + * matches - A table is size CONFIG_READLINE_MAX_EXTCMDS that can + * be used to remember matching name indices. + * namelen - The lenght of the name to match + * + * Returned Values: + * The number commands that match to the first namelen characters. + * + ****************************************************************************/ + +#if defined(CONFIG_NSH_READLINE) && defined(CONFIG_READLINE_TABCOMPLETION) && \ + defined(CONFIG_READLINE_HAVE_EXTMATCH) +int nsh_extmatch_count(FAR char *name, FAR int *matches, int namelen); +#endif + +/**************************************************************************** + * Name: nsh_extmatch_getname + * + * Description: + * This support function is used to provide support for realine tab- + * completion logic nsh_extmatch_getname() will return the full command + * string from an index that was previously saved by nsh_exmatch_count(). + * + * Input Parameters: + * index - The index of the command name to be returned. + * + * Returned Values: + * The numb + * + ****************************************************************************/ + +#if defined(CONFIG_NSH_READLINE) && defined(CONFIG_READLINE_TABCOMPLETION) && \ + defined(CONFIG_READLINE_HAVE_EXTMATCH) +FAR const char *nsh_extmatch_getname(int index); +#endif + #endif /* __APPS_NSHLIB_NSH_H */ diff --git a/nshlib/nsh_command.c b/nshlib/nsh_command.c index 0d18905d1..315983522 100644 --- a/nshlib/nsh_command.c +++ b/nshlib/nsh_command.c @@ -45,6 +45,10 @@ # include #endif +#if defined(CONFIG_SYSTEM_READLINE) && defined(CONFIG_READLINE_HAVE_EXTMATCH) +# include +#endif + #include "nsh.h" #include "nsh_console.h" @@ -824,3 +828,73 @@ int nsh_command(FAR struct nsh_vtbl_s *vtbl, int argc, char *argv[]) ret = handler(vtbl, argc, argv); return ret; } + +/**************************************************************************** + * Name: nsh_extmatch_count + * + * Description: + * This support function is used to provide support for realine tab- + * completion logic nsh_extmatch_count() counts the number of matching + * nsh command names + * + * Input Parameters: + * name - A point to the name containing the name to be matched. + * matches - A table is size CONFIG_READLINE_MAX_EXTCMDS that can + * be used to remember matching name indices. + * namelen - The lenght of the name to match + * + * Returned Values: + * The number commands that match to the first namelen characters. + * + ****************************************************************************/ + +#if defined(CONFIG_NSH_READLINE) && defined(CONFIG_READLINE_TABCOMPLETION) && \ + defined(CONFIG_READLINE_HAVE_EXTMATCH) +int nsh_extmatch_count(FAR char *name, FAR int *matches, int namelen) +{ + int nr_matches = 0; + int i; + + for (i = 0; i < NUM_CMDS; i++) + { + if (strncmp(name, g_cmdmap[i].cmd, namelen) == 0) + { + matches[nr_matches] = i; + nr_matches++; + + if (nr_matches >= CONFIG_READLINE_MAX_EXTCMDS) + { + break; + } + } + } + + return nr_matches; +} +#endif + +/**************************************************************************** + * Name: nsh_extmatch_getname + * + * Description: + * This support function is used to provide support for realine tab- + * completion logic nsh_extmatch_getname() will return the full command + * string from an index that was previously saved by nsh_exmatch_count(). + * + * Input Parameters: + * index - The index of the command name to be returned. + * + * Returned Values: + * The numb + * + ****************************************************************************/ + +#if defined(CONFIG_NSH_READLINE) && defined(CONFIG_READLINE_TABCOMPLETION) && \ + defined(CONFIG_READLINE_HAVE_EXTMATCH) +FAR const char *nsh_extmatch_getname(int index) +{ + DEBUGASSERT(index > 0 && index <= NUM_CMDS); + return g_cmdmap[index].cmd; +} +#endif + diff --git a/nshlib/nsh_init.c b/nshlib/nsh_init.c index d60f308f9..2bd29b8c5 100644 --- a/nshlib/nsh_init.c +++ b/nshlib/nsh_init.c @@ -40,6 +40,7 @@ #include #include + #include #include @@ -61,7 +62,16 @@ * Private Data ****************************************************************************/ -/**************************************************************************** +#if defined(CONFIG_NSH_READLINE) && defined(CONFIG_READLINE_TABCOMPLETION) && \ + defined(CONFIG_READLINE_HAVE_EXTMATCH) +static const struct extmatch_vtable_s g_nsh_extmatch = +{ + nsh_extmatch_count, /* count_matches */ + nsh_extmatch_getname /* getname */ +}; +#endif + + /************************************************************************** * Public Data ****************************************************************************/ @@ -105,9 +115,15 @@ void nsh_initialize(void) (void)nsh_netinit(); -#ifdef CONFIG_READLINE_TABCOMPLETION +#if defined(CONFIG_NSH_READLINE) && defined(CONFIG_READLINE_TABCOMPLETION) /* Configure the NSH prompt */ - readline_prompt(g_nshprompt); + (void)readline_prompt(g_nshprompt); + +#ifdef CONFIG_READLINE_HAVE_EXTMATCH + /* Set up for tab completion on NSH commands */ + + (void)readline_extmatch(&g_nsh_extmatch); +#endif #endif } diff --git a/system/readline/Kconfig b/system/readline/Kconfig index be3777d8f..96fff757d 100644 --- a/system/readline/Kconfig +++ b/system/readline/Kconfig @@ -9,6 +9,10 @@ menuconfig SYSTEM_READLINE ---help--- Enable support for the readline() function. +config READLINE_HAVE_EXTMATCH + bool + default n + if SYSTEM_READLINE config READLINE_ECHO @@ -23,9 +27,28 @@ config READLINE_ECHO config READLINE_TABCOMPLETION bool "Tab completion" default n - depends on BUILD_FLAT && BUILTIN + depends on (BUILD_FLAT && BUILTIN) || READLINE_HAVE_EXTMATCH ---help--- Build in support for Unix-style tab completion. This feature was - provided by Nghia. + originally provided by Nghia. -endif +if READLINE_TABCOMPLETION + +config READLINE_MAX_BUILTINS + int "Maximum built-in matches" + default 64 + depends on BUILTIN + ---help--- + This the maximum number of matching names of builtin commands that + will be displayed. + +config READLINE_MAX_EXTCMDS + int "Maximum built-in matches" + default 64 + depends on READLINE_HAVE_EXTMATCH + ---help--- + This the maximum number of matching names of builtin commands that + will be displayed. + +endif # READLINE_TABCOMPLETION +endif # SYSTEM_READLINE diff --git a/system/readline/readline_common.c b/system/readline/readline_common.c index 6bf518b1c..ca46406a8 100644 --- a/system/readline/readline_common.c +++ b/system/readline/readline_common.c @@ -1,7 +1,7 @@ /**************************************************************************** * apps/system/readline/readline_common.c * - * Copyright (C) 2007-2008, 2011-2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2008, 2011-2013, 2015 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -64,12 +64,56 @@ static const char g_erasetoeol[] = VT100_CLEAREOL; /* Prompt string to present at the beginning of the line */ static FAR const char *g_readline_prompt = NULL; + +#ifdef CONFIG_READLINE_HAVE_EXTMATCH +static FAR const struct extmatch_vtable_s *g_extmatch_vtbl = NULL; +#endif #endif /**************************************************************************** * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: count_builtin_maches + * + * Description: + * Count the number of builtin commands + * + * Input Parameters: + * matches - Array to save builtin command index. + * len - The length of the matching name to try + * + * Returned Value: + * The number of matching names + * + **************************************************************************/ + +#if defined(CONFIG_READLINE_TABCOMPLETION) && defined(CONFIG_BUILTIN) +static int count_builtin_maches(FAR char *buf, FAR int *matches, int namelen) +{ + FAR const char *name; + int nr_matches = 0; + int i; + + for (i = 0; (name = builtin_getname(i)) != NULL; i++) + { + if (strncmp(buf, name, namelen) == 0) + { + matches[nr_matches] = i; + nr_matches++; + + if (nr_matches >= CONFIG_READLINE_MAX_BUILTINS) + { + break; + } + } + } + + return nr_matches; +} +#endif + /**************************************************************************** * Name: tab_completion * @@ -87,36 +131,83 @@ static FAR const char *g_readline_prompt = NULL; **************************************************************************/ #ifdef CONFIG_READLINE_TABCOMPLETION -void tab_completion(FAR struct rl_common_s *vtbl, char *buf, int *nch) +static void tab_completion(FAR struct rl_common_s *vtbl, char *buf, + int *nch) { FAR const char *name = NULL; - int num_matches = 0; - int matches[128]; +#ifdef CONFIG_BUILTIN + int nr_builtin_matches = 0; + int builtin_matches[CONFIG_READLINE_MAX_BUILTINS]; +#endif +#ifdef CONFIG_BUILTIN + int nr_ext_matches = 0; + int ext_matches[CONFIG_READLINE_MAX_EXTCMDS]; +#endif + int nr_matches; int len = *nch; int i; int j; if (len >= 1) { - for (i = 0; (name = builtin_getname(i)) != NULL; i++) - { - if (!strncmp(buf, name, len)) - { - matches[num_matches] = i; - num_matches++; +#ifdef CONFIG_BUILTIN + /* Count the matching builtin commands */ - if (num_matches >= sizeof(matches) / sizeof(int)) - { - break; - } - } + nr_builtin_matches = count_builtin_maches(buf, builtin_matches, len); + nr_matches = nr_builtin_matches; +#else + nr_matches = 0; +#endif + +#ifdef CONFIG_READLINE_HAVE_EXTMATCH + /* Is there registered external handling logic? */ + + nr_ext_matches = 0; + if (g_extmatch_vtbl != NULL) + { + /* Count the number of external commands */ + + nr_ext_matches = g_extmatch_vtbl->count_matches(buf, ext_matches, len); + nr_matches += nr_ext_matches; } - if (num_matches == 1) - { - name = builtin_getname(matches[0]); +#endif - int name_len = strlen(name); + /* Is there only one matching name? */ + + if (nr_matches == 1) + { + int name_len; + + /* Yes... that that is the one we want. Was it a match with a + * builtin command? Or with an external command. + */ + +#ifdef CONFIG_BUILTIN +#ifdef CONFIG_READLINE_HAVE_EXTMATCH + if (nr_builtin_matches == 1) +#endif + { + /* It is a match with a builtin command */ + + name = builtin_getname(builtin_matches[0]); + } +#endif + +#ifdef CONFIG_READLINE_HAVE_EXTMATCH +#ifdef CONFIG_BUILTIN + else +#endif + { + /* It is a match with an external command */ + + name = g_extmatch_vtbl->getname(ext_matches[0]); + } +#endif + + /* Copy the name to the command buffer and to the display. */ + + name_len = strlen(name); for (j = len; j < name_len; j++) { @@ -124,22 +215,26 @@ void tab_completion(FAR struct rl_common_s *vtbl, char *buf, int *nch) RL_PUTC(vtbl, name[j]); } - /* Don't remove extra characters after the completed word, if any */ + /* Don't remove extra characters after the completed word, if any. */ if (len < name_len) { *nch = name_len; } } - else if (num_matches > 1) + + /* Are there multiple matching names? */ + + else if (nr_matches > 1) { RL_PUTC(vtbl, '\n'); - /* possible completion */ +#ifdef CONFIG_READLINE_HAVE_EXTMATCH + /* Show the possible external completions */ - for (i = 0; i < num_matches; i++) + for (i = 0; i < nr_ext_matches; i++) { - name = builtin_getname(matches[i]); + name = g_extmatch_vtbl->getname(ext_matches[i]); RL_PUTC(vtbl, ' '); RL_PUTC(vtbl, ' '); @@ -151,6 +246,26 @@ void tab_completion(FAR struct rl_common_s *vtbl, char *buf, int *nch) RL_PUTC(vtbl, '\n'); } +#endif + +#ifdef CONFIG_BUILTIN + /* Show the possible builtin completions */ + + for (i = 0; i < nr_builtin_matches; i++) + { + name = builtin_getname(builtin_matches[i]); + + RL_PUTC(vtbl, ' '); + RL_PUTC(vtbl, ' '); + + for (j = 0; j < strlen(name); j++) + { + RL_PUTC(vtbl, name[j]); + } + + RL_PUTC(vtbl, '\n'); + } +#endif /* Output the original prompt */ @@ -184,10 +299,11 @@ void tab_completion(FAR struct rl_common_s *vtbl, char *buf, int *nch) * to reprint the prompt string. * * Input Parameters: - * prompt - The prompt string. + * prompt - The prompt string. This function may then be + * called with that value in order to restore the previous vtable. * * Returned values: - * None + * Returns the previous value of the prompt string * * Assumptions: * The prompt string is statically allocated a global. readline will @@ -199,9 +315,45 @@ void tab_completion(FAR struct rl_common_s *vtbl, char *buf, int *nch) **************************************************************************/ #ifdef CONFIG_READLINE_TABCOMPLETION -void readline_prompt(FAR const char *prompt) +FAR const char *readline_prompt(FAR const char *prompt) { + FAR const char *ret = g_readline_prompt; g_readline_prompt = prompt; + return ret; +} +#endif + +/**************************************************************************** + * Name: readline_extmatch + * + * If the applications supports a command set, then it may call this + * function in order to provide support for tab complete on these\ + * "external" commands + * + * Input Parameters: + * vtbl - Callbacks to access the external names. + * + * Returned values: + * Returns the previous vtable pointer. This function may then be + * called with that value in order to restore the previous vtable. + * + * Assumptions: + * The vtbl string is statically allocated a global. readline will + * simply remember the pointer to the structure. The structure must stay + * allocated and available. Only one instance of such a structure is + * upported. If there are multiple clients of readline, they must all + * share the same tab-completion logic (with exceptions in the case of + * the kernel build). + * + **************************************************************************/ + +#if defined(CONFIG_READLINE_TABCOMPLETION) && defined(CONFIG_READLINE_HAVE_EXTMATCH) +FAR const struct extmatch_vtable_s * + readline_extmatch(FAR const struct extmatch_vtable_s *vtbl) +{ + FAR const struct extmatch_vtable_s *ret = g_extmatch_vtbl; + g_extmatch_vtbl = vtbl; + return ret; } #endif