}
static char *
-vshReadlineOptionsGenerator(const char *text, int state)
+vshReadlineOptionsGenerator(const char *text, int state, const vshCmdDef *cmd_parsed)
{
static unsigned int list_index;
static size_t 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 = cmd_parsed;
list_index = 0;
len = strlen(text);
- VIR_FREE(cmdname);
}
if (!cmd)
continue;
if (len > 2) {
+ /* provide auto-complete only when the text starts with -- */
+ if (STRNEQLEN(text, "--", 2))
+ return NULL;
if (STRNEQLEN(name, text + 2, len - 2))
continue;
+ } else if (STRNEQLEN(text, "--", len)) {
+ return NULL;
}
res = vshMalloc(NULL, strlen(name) + 3);
snprintf(res, strlen(name) + 3, "--%s", name);
return NULL;
}
+static char *
+vshReadlineParse(const char *text, int state)
+{
+ static vshCommandParser parser, sanitizer;
+ vshCommandToken tk;
+ static const vshCmdDef *cmd;
+ const vshCmdOptDef *opt;
+ char *tkdata, *optstr, *const_tkdata, *res;
+ static char *ctext, *sanitized_text;
+ static uint64_t const_opts_need_arg, const_opts_required, const_opts_seen;
+ uint64_t opts_need_arg, opts_required, opts_seen;
+ unsigned int opt_index;
+ static bool cmd_exists, opts_filled, opt_exists;
+ static bool non_bool_opt_exists, data_acomplete;
+
+ if (!state) {
+ parser.pos = rl_line_buffer;
+ parser.getNextArg = vshCommandStringGetArg;
+
+ ctext = vshStrdup(NULL, text);
+ sanitizer.pos = ctext;
+ sanitizer.getNextArg = vshCommandStringGetArg;
+
+ const_tkdata = NULL;
+ tkdata = NULL;
+ sanitized_text = NULL;
+ optstr = NULL;
+ res = NULL;
+
+ /* Sanitize/de-quote the autocomplete text */
+ tk = sanitizer.getNextArg(NULL, &sanitizer, &sanitized_text, false);
+
+ /* No autocomplete if sanitized text is a token error or token end */
+ if (tk == VSH_TK_ERROR)
+ goto error;
+
+ tk = parser.getNextArg(NULL, &parser, &const_tkdata, false);
+
+ if (tk == VSH_TK_ERROR)
+ goto error;
+
+ /* Free-ing purposes */
+ tkdata = const_tkdata;
+ /* Skip leading space */
+ virSkipSpaces((const char**)&tkdata);
+
+ /* Handle ';'s */
+ while (tk == VSH_TK_SUBCMD_END) {
+ tk = parser.getNextArg(NULL, &parser, &const_tkdata, false);
+ tkdata = const_tkdata;
+ }
+
+ /* Skip trailing space after ;*/
+ virSkipSpaces((const char**)&tkdata);
+
+ cmd_exists = false;
+ opts_filled = false;
+ non_bool_opt_exists = false;
+ data_acomplete = false;
+
+ const_opts_need_arg = 0;
+ const_opts_required = 0;
+ const_opts_seen = 0;
+
+ opt_index = 0;
+
+ cmd = NULL;
+ opt = NULL;
+
+ /* Parse till text to be auto-completed is reached */
+ while (STRNEQ(tkdata, sanitized_text)) {
+ if (!cmd) {
+ if (!(cmd = vshCmddefSearch(tkdata)))
+ goto error;
+ cmd_exists = true;
+
+ if (vshCmddefOptFill(cmd, &const_opts_need_arg,
+ &const_opts_required) < 0)
+ goto error;
+ opts_filled = true;
+ } else if (tkdata[0] == '-' && tkdata[1] == '-' &&
+ c_isalnum(tkdata[2])) {
+ /* Command retrieved successfully, move to options */
+ opts_need_arg = const_opts_need_arg;
+ opts_required = const_opts_required;
+ opts_seen = const_opts_seen;
+ optstr = strchr(tkdata + 2, '=');
+ opt_index = 0;
+
+ if (optstr) {
+ *optstr = '\0';
+ optstr = vshStrdup(NULL, optstr + 1);
+ }
+
+ if (!(opt = vshCmddefGetOption(NULL, cmd, tkdata + 2,
+ &opts_seen, &opt_index,
+ &optstr, false))) {
+ /* Parsing failed wrt autocomplete */
+ VIR_FREE(optstr);
+ goto error;
+ }
+ opt_exists = true;
+ VIR_FREE(const_tkdata);
+ if (opt->type != VSH_OT_BOOL) {
+ non_bool_opt_exists = true;
+ /* Opt exists and check for option data */
+ if (optstr) {
+ const_tkdata = optstr;
+ tkdata = const_tkdata;
+ } else {
+ VIR_FREE(const_tkdata);
+ tk = parser.getNextArg(NULL, &parser, &const_tkdata,
+ false);
+
+ if (tk == VSH_TK_ERROR)
+ goto error;
+
+ tkdata = const_tkdata;
+ if (STREQ(tkdata, sanitized_text)) {
+ /* auto-complete non-bool option */
+ data_acomplete = true;
+ break;
+ }
+ }
+ if (opt->type != VSH_OT_ARGV)
+ opts_need_arg &= ~(1ULL << opt_index);
+ } else {
+ tkdata = NULL;
+ /* opt type is BOOL */
+ if (optstr) {
+ VIR_FREE(optstr);
+ goto error;
+ }
+ }
+ } else {
+ /* No -- option provided and some other token given */
+ if (!opt_exists) {
+ goto error;
+ } else if (non_bool_opt_exists) {
+ /* TODO
+ * -- non bool option present, so parse the next arg
+ * or call completer on it if it is to be completed
+ */
+ return NULL;
+ }
+ }
+
+ VIR_FREE(const_tkdata);
+ tk = parser.getNextArg(NULL, &parser, &const_tkdata, false);
+
+ if (tk == VSH_TK_ERROR)
+ goto error;
+
+ while (tk == VSH_TK_SUBCMD_END) {
+ cmd = NULL;
+ cmd_exists = false;
+ opts_filled = false;
+ opt = NULL;
+ non_bool_opt_exists = false;
+ tk = parser.getNextArg(NULL, &parser, &const_tkdata, false);
+ }
+
+ tkdata = const_tkdata;
+
+ virSkipSpaces((const char**)&tkdata);
+ }
+ VIR_FREE(const_tkdata);
+ }
+
+ if (!cmd_exists)
+ res = vshReadlineCommandGenerator(sanitized_text, state);
+ else if (opts_filled && !non_bool_opt_exists)
+ res = vshReadlineOptionsGenerator(sanitized_text, state, cmd);
+
+ if (!res) {
+ VIR_FREE(sanitized_text);
+ VIR_FREE(ctext);
+ }
+
+ return res;
+
+ error:
+ VIR_FREE(const_tkdata);
+ VIR_FREE(sanitized_text);
+ VIR_FREE(ctext);
+ return NULL;
+
+}
+
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;
- if (start == 0)
- /* command name generator */
- matches = rl_completion_matches(text, vshReadlineCommandGenerator);
- else
- /* commands options */
- matches = rl_completion_matches(text, vshReadlineOptionsGenerator);
+ matches = rl_completion_matches(text, vshReadlineParse);
return matches;
}
/* Tell the completer that we want a crack first. */
rl_attempted_completion_function = vshReadlineCompletion;
+ rl_basic_word_break_characters = " \t\n\\`@$><=;|&{(";
+
if (virStringToUpper(&name_capitalized, ctl->name) < 0 ||
!(histsize_env = virStringJoin(strings, "_")))
goto cleanup;