* vshMalloc is now used in vshDetermineCommandName * vshStrdup instead of vshMalloc + snprintf * joined if's in vshReadlineOptionsCompletionGenerator --- tools/virsh.c | 395 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 373 insertions(+), 22 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 15f529e..af6e939 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2508,6 +2508,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 @@ -2555,25 +2574,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) @@ -2605,22 +2613,365 @@ vshReadlineOptionsGenerator(const char *text, int state) return NULL; } +/* + * Generator function for command completion, but unlike + * the vshRaadlineCommandGenerator 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; + } + } + } + + if (!found_opt) + 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; + } + } + + if (!found_cmd_com) + 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; + } + } + + if (!found_opt_com) + 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