From: Alan T. DeKok Date: Wed, 11 Jul 2018 14:12:36 +0000 (-0400) Subject: add tab expansion for commands X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7b75e22e21f7d994bd40c8f8255cf018dfabbd8a;p=thirdparty%2Ffreeradius-server.git add tab expansion for commands right now, only for fixed strings, and not for syntax in commands. But it's a nice step forward. --- diff --git a/src/include/command.h b/src/include/command.h index 97ae4363bc7..2934afc53a8 100644 --- a/src/include/command.h +++ b/src/include/command.h @@ -87,6 +87,9 @@ int fr_command_clear(int new_argc, fr_cmd_info_t *info) CC_HINT(nonnull); void fr_command_list(FILE *fp, int max_depth, fr_cmd_t *head, int options); void fr_command_info_init(TALLOC_CTX *ctx, fr_cmd_info_t *info); +int fr_command_complete(fr_cmd_t *head, char const *text, int start, + int max_expansions, char const **expansions); + #ifdef __cplusplus } #endif diff --git a/src/main/command.c b/src/main/command.c index f187aab3490..158e6f84a63 100644 --- a/src/main/command.c +++ b/src/main/command.c @@ -1935,3 +1935,103 @@ void fr_command_info_init(TALLOC_CTX *ctx, fr_cmd_info_t *info) info->box = talloc_zero_array(ctx, fr_value_box_t *, CMD_MAX_ARGV); info->cmd = talloc_zero_array(ctx, fr_cmd_t *, CMD_MAX_ARGV); } + + +/** Do readline-style command completions + * + * Most useful as part of readline tab expansions. + * + * @todo - call strdup() on the names, because that's what readline + * expects. + * + * @param head of the command tree + * @param text the text to check + * @param start offset in the text where the completions should start + * @param max_expansions how many entries in the "expansions" array. + * @param expansions where the expansions are stored. + */ +int fr_command_complete(fr_cmd_t *head, char const *text, int start, + int max_expansions, char const **expansions) +{ + int count; + char const *word, *p, *q; + fr_cmd_t *cmd; + + cmd = head; + word = text; + count = 0; + + /* + * Try to do this without mangling "text". + */ + while (cmd) { + while (isspace((int) *word)) word++; + + /* + * End of the input. Tab expand everything here. + */ + if (!*word) { + expand: + while (cmd && (count < max_expansions)) { + expansions[count] = cmd->name; + count++; + cmd = cmd->next; + } + return count; + } + + /* + * Try to find a matching cmd->name + */ + p = word; + q = cmd->name; + + while (*p == *q) { + p++; + q++; + } + + /* + * We're supposed to expand the text at this + * location, go do so. Even if it doesn't match. + */ + if (((text + start) >= word) && ((text + start) <= p)) { + goto expand; + } + + /* + * The only matching exit condition is *p is a + * space, and *q is the NUL character. + */ + if (!(isspace((int) *p) && !*q)) { + cmd = cmd->next; + continue; + } + + if (cmd->intermediate) { + rad_assert(cmd->child != NULL); + word = p; + cmd = cmd->child; + continue; + } + + break; + } + + /* + * No match, can't do anything. + */ + if (!cmd) return count; + + /* + * No syntax, can't do anything. + */ + if (!cmd->syntax) return count; + + /* + * @todo - loop over the command syntax, looking for + * matches. + */ + + return count; +} diff --git a/src/main/radmin.c b/src/main/radmin.c index a0e55307b4e..714215f3559 100644 --- a/src/main/radmin.c +++ b/src/main/radmin.c @@ -131,10 +131,52 @@ static void add_history(UNUSED char *line) static fr_cmd_t *radmin_cmd = NULL; #define CMD_MAX_ARGV (32) +#define CMD_MAX_EXPANSIONS (128) static int cmd_help(FILE *fp, FILE *fp_err, void *ctx, fr_cmd_info_t const *info); static int cmd_exit(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info); +static int radmin_num_expansions; +static char const *radmin_expansions[CMD_MAX_EXPANSIONS] = {0}; + +static char * +radmin_expansion_walk(const char *text, int state) +{ + static int current, len; + char const *name; + + if (!state) { + current = 0; + len = strlen(text); + } + + if (current >= radmin_num_expansions) return 0; + + while ((name = radmin_expansions[current++])) { + if (strncmp(name, text, len) == 0) { + return strdup(name); + } + } + + return NULL; +} + + +static char ** +radmin_completion(const char *text, int start, UNUSED int end) +{ + int num; + + rl_attempted_completion_over = 1; + + num = fr_command_complete(radmin_cmd, rl_line_buffer, start, CMD_MAX_EXPANSIONS, radmin_expansions); + if (num <= 0) return NULL; + + radmin_num_expansions = num; + + return rl_completion_matches(text, radmin_expansion_walk); +} + static void *fr_radmin(UNUSED void *input_ctx) { int argc; @@ -161,6 +203,8 @@ static void *fr_radmin(UNUSED void *input_ctx) fflush(stdout); + rl_attempted_completion_function = radmin_completion; + while (true) { char *line;