New completion generators responsible for advances command and command options completions. vshReadlineCommandCompletionGenerator - generator for advanced command completions. This function will call some vshCmdCompleter function (e.g. vshDomainCompleter), which will return relevant data used for autocompletion (e.g. domain names). vshReadlineOptionsCompletionGenerator - almost the same as the vshReadlineCommandCompletionGenerator, but this one completes cmd options. vshReadlineCompletion() has become much more complex because we now have more generator functions and therefore more states to choose from. --- v2 * vshMalloc is now used in vshDetermineCommandName * vshStrdup instead of vshMalloc + snprintf * joined if's in vshReadlineOptionsCompletionGenerator v3 * fixed typo * removed useless if's .gnulib | 2 +- tools/virsh.c | 386 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 365 insertions(+), 23 deletions(-) diff --git a/.gnulib b/.gnulib index 0ba0877..644c404 160000 --- a/.gnulib +++ b/.gnulib @@ -1 +1 @@ -Subproject commit 0ba087759d2797c8f7d3c34bef6268ba3fd212cb +Subproject commit 644c40496cf7d5a705a73c9dd32b035fcecc2ab1 diff --git a/tools/virsh.c b/tools/virsh.c index 2ea44a6..5be8e7e 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2515,6 +2515,25 @@ vshCloseLogFile(vshControl *ctl) * ----------------- */ +static const vshCmdDef * +vshDetermineCommandName(void) +{ + const vshCmdDef *cmd = NULL; + char *p; + char *cmdname; + + if (!(p = strchr(rl_line_buffer, ' '))) + return NULL; + + cmdname = vshMalloc(NULL, (p - rl_line_buffer) + 1); + memcpy(cmdname, rl_line_buffer, p - rl_line_buffer); + + cmd = vshCmddefSearch(cmdname); + VIR_FREE(cmdname); + + return cmd; +} + /* * Generator function for command completion. STATE lets us * know whether to start from scratch; without any state @@ -2562,25 +2581,14 @@ vshReadlineCommandGenerator(const char *text, int state) static char * vshReadlineOptionsGenerator(const char *text, int state) { - static int list_index, len; static const vshCmdDef *cmd = NULL; + static int list_index, len; const char *name; if (!state) { - /* determine command name */ - char *p; - char *cmdname; - - if (!(p = strchr(rl_line_buffer, ' '))) - return NULL; - - cmdname = vshCalloc(NULL, (p - rl_line_buffer) + 1, 1); - memcpy(cmdname, rl_line_buffer, p - rl_line_buffer); - - cmd = vshCmddefSearch(cmdname); + cmd = vshDetermineCommandName(); list_index = 0; len = strlen(text); - VIR_FREE(cmdname); } if (!cmd) @@ -2612,22 +2620,356 @@ vshReadlineOptionsGenerator(const char *text, int state) return NULL; } +/* + * Generator function for command completion, but unlike + * the vshReadlineCommandGenerator which completes command name, this function + * provides more advanced completion for commands by calling specific command + * completers (e.g. vshDomainCompleter). + */ +static char * +vshReadlineCommandCompletionGenerator(const char *text, int state) +{ + static const vshCmdDef *cmd = NULL; + static int list_index, len; + char **completed_names = NULL; + char *name; + + if (!state) { + cmd = vshDetermineCommandName(); + list_index = 0; + len = strlen(text); + } + + if (!cmd) + return NULL; + + if (!cmd->completer) + return NULL; + + completed_names = cmd->completer(cmd->completer_flags); + + if (!completed_names) + return NULL; + + while ((name = completed_names[list_index])) { + char *res; + list_index++; + + if (STRNEQLEN(name, text, len)) + /* Skip irrelevant names */ + continue; + + res = vshStrdup(NULL, name); + VIR_FREE(name); + return res; + } + VIR_FREE(completed_names); + + return NULL; +} + +/* + * Generator function for command option completion. Provides advances + * completion for command options. + */ +static char * +vshReadlineOptionsCompletionGenerator(const char *text ATTRIBUTE_UNUSED, + int state ATTRIBUTE_UNUSED) +{ + static const vshCmdDef *cmd = NULL; + static const vshCmdOptDef *opt = NULL; + static int list_index, len; + unsigned long int opt_index = 0; + size_t i; + char **completed_names = NULL; + char *name; + char *ptr = NULL; + + if (!state) { + cmd = vshDetermineCommandName(); + list_index = 0; + len = strlen(text); + } + + if (!cmd) + return NULL; + + if (!cmd->opts) + return NULL; + + for (i = 0; cmd->opts[i].name; i++) { + if ((ptr = strstr(rl_line_buffer, cmd->opts[i].name)) && + (opt_index < (ptr - rl_line_buffer))) { + opt_index = ptr - rl_line_buffer; + opt = &cmd->opts[i]; + } + } + + if (!opt) + return NULL; + + if (!opt->completer) + return NULL; + + completed_names = opt->completer(opt->completer_flags); + + if (!completed_names) + return NULL; + + while ((name = completed_names[list_index])) { + char *res; + list_index++; + + if (STRNEQLEN(name, text, len)) + /* Skip irrelevant names */ + continue; + + res = vshStrdup(NULL, name); + VIR_FREE(name); + return res; + } + VIR_FREE(completed_names); + + return NULL; +} + +/* + * Returns current valid command name present in the rl_line_buffer. + */ +static char * +vshCurrentCmd(void) +{ + const char *name; + const vshCmdGrp *grp; + const vshCmdDef *cmds; + size_t grp_list_index, cmd_list_index; + char *found_cmd = NULL; + char *rl_copy = NULL; + char *pch; + + grp_list_index = 0; + cmd_list_index = 0; + grp = cmdGroups; + + while (grp[grp_list_index].name) { + cmds = grp[grp_list_index].commands; + + if (cmds[cmd_list_index].name) { + while ((name = cmds[cmd_list_index].name)) { + cmd_list_index++; + + if (VIR_STRDUP(rl_copy, rl_line_buffer) < 0) + return NULL; + + char *saveptr; + pch = strtok_r(rl_copy, " ", &saveptr); + + while (pch != NULL) { + if (STREQ(pch, name)) + if (VIR_STRDUP(found_cmd, name) < 0) + goto cleanup; + pch = strtok_r(NULL, " ", &saveptr); + } + } + } else { + cmd_list_index = 0; + grp_list_index++; + } + } + + if (!found_cmd) + goto cleanup; + + return found_cmd; + +cleanup: + VIR_FREE(rl_copy); + return NULL; +} + +/* + * Returns current valid command option name present in the rl_line_buffer. + */ +static char * +vshCurrentOpt(const char *cmd_name) +{ + const vshCmdDef *cmd = NULL; + const char *name; + unsigned long int opt_index = 0; + size_t cmd_opt_list_index; + char *found_opt = NULL; + char *ptr = NULL; + + if (!cmd_name) + return NULL; + + cmd = vshCmddefSearch(cmd_name); + + if (!cmd) + return NULL; + + if (!cmd->opts) + return NULL; + + cmd_opt_list_index = 0; + + while ((name = cmd->opts[cmd_opt_list_index].name)) { + cmd_opt_list_index++; + + if ((ptr = strstr(rl_line_buffer, name))) { + if (opt_index < (ptr - rl_line_buffer)) { + opt_index = ptr - rl_line_buffer; + if (VIR_STRDUP(found_opt, name) < 0) + return NULL; + } + } + } + + return found_opt; +} + +/* + * Returns name of the command completion currently present in the rl_line_buffer. + */ +static char* +vshCurrentCmdCom(const char *cmd_name) +{ + const vshCmdDef *cmd = NULL; + size_t i; + char **completed_names = NULL; + char *found_cmd_com = NULL; + + if (!cmd_name) + return NULL; + + cmd = vshCmddefSearch(cmd_name); + + if (!cmd) + return NULL; + + if (!cmd->completer) + return NULL; + + completed_names = cmd->completer(cmd->completer_flags); + + if (!completed_names) + return NULL; + + for (i = 0; completed_names[i]; i++) { + if (strstr(rl_line_buffer, completed_names[i])) { + if (VIR_STRDUP(found_cmd_com, completed_names[i]) < 0) + return NULL; + } + } + + return found_cmd_com; +} + +/* + * Returns name of the option completion currently present in the rl_line_buffer. + */ +static char * +vshCurrentOptCom(const char *cmd_name) +{ + const vshCmdDef *cmd = NULL; + const vshCmdOptDef *opt = NULL; + unsigned long int opt_index = 0; + size_t i; + char **completed_names = NULL; + char *found_opt_com = NULL; + char *ptr = NULL; + + if (!cmd_name) + return NULL; + + cmd = vshCmddefSearch(cmd_name); + + if (!cmd) + return NULL; + + if (!cmd->opts) + return NULL; + + for (i = 0; cmd->opts[i].name; i++) { + if ((ptr = strstr(rl_line_buffer, cmd->opts[i].name))) { + if (opt_index < (ptr - rl_line_buffer)) { + opt_index = ptr - rl_line_buffer; + opt = &cmd->opts[i]; + } + } + } + + if (!opt) + return NULL; + + if (!opt->completer) + return NULL; + + completed_names = opt->completer(opt->completer_flags); + + if (!completed_names) + return NULL; + + for (i = 0; completed_names[i]; i++) { + if (strstr(rl_line_buffer, completed_names[i])) { + if (VIR_STRDUP(found_opt_com, completed_names[i]) < 0) + return NULL; + } + } + + return found_opt_com; +} + static char ** -vshReadlineCompletion(const char *text, int start, - int end ATTRIBUTE_UNUSED) +vshReadlineCompletion(const char *text, int start, int end ATTRIBUTE_UNUSED) { - char **matches = (char **) NULL; + const char *cmd = vshCurrentCmd(); + const char *cmd_com = vshCurrentCmdCom(cmd); + const char *opt = vshCurrentOpt(cmd); + const char *opt_com = vshCurrentOptCom(cmd); + char **matches = (char **)NULL; - if (start == 0) - /* command name generator */ + if (start == 0) { + /* Command name generator. */ matches = rl_completion_matches(text, vshReadlineCommandGenerator); - else - /* commands options */ - matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else { + /* Command completer, commands options and commands options completer + * generators. + */ + if (strstr(text, "-")) { + /* When user wants to see options. */ + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else if (!cmd_com && !opt && !opt_com) { + matches = rl_completion_matches(text, vshReadlineCommandCompletionGenerator); + if (!matches) + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else if (cmd_com && !opt && !opt_com) { + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else if (cmd_com && opt && !opt_com) { + matches = rl_completion_matches(text, vshReadlineOptionsCompletionGenerator); + if (!matches) + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else if (!cmd_com && opt && !opt_com) { + matches = rl_completion_matches(text, vshReadlineCommandCompletionGenerator); + if (!matches) + matches = rl_completion_matches(text, vshReadlineOptionsCompletionGenerator); + if (!matches) + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else if (!cmd_com && opt && opt_com) { + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else if (cmd_com && opt && opt_com) { + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } + } + + VIR_FREE(cmd); + VIR_FREE(cmd_com); + VIR_FREE(opt); + VIR_FREE(opt_com); + return matches; } - static int vshReadlineInit(vshControl *ctl) { -- 1.8.3.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list