From 2e688d96a43a094ade031f7cfb71e191ab896c73 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Mon, 6 Nov 2017 15:46:50 +0100 Subject: [PATCH] vshCommandStringParse: Allow retrieving partial result In the future, this function is going to be called from vshReadlineParse() to provide parsed input for completer callbacks. The idea is to allow the callbacks to provide more specific data. For instance, for the following input: virsh # domifaddr --domain fedora --interface the --interface completer callback is going to be called. Now, it is more user friendly if the completer offers only those interfaces found in 'fedora' domain. But in order to do that it needs to be able to retrieve partially parsed result. Signed-off-by: Michal Privoznik --- tools/virsh.c | 4 +- tools/virt-admin.c | 4 +- tools/vsh.c | 111 +++++++++++++++++++++++++++++++-------------- tools/vsh.h | 2 +- 4 files changed, 82 insertions(+), 39 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 89a2bea10c..933d6b4c98 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -817,7 +817,7 @@ virshParseArgv(vshControl *ctl, int argc, char **argv) ctl->imode = false; if (argc - optind == 1) { vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n", argv[optind]); - return vshCommandStringParse(ctl, argv[optind]); + return vshCommandStringParse(ctl, argv[optind], NULL); } else { return vshCommandArgvParse(ctl, argc - optind, argv + optind); } @@ -954,7 +954,7 @@ main(int argc, char **argv) #if WITH_READLINE add_history(ctl->cmdstr); #endif - if (vshCommandStringParse(ctl, ctl->cmdstr)) + if (vshCommandStringParse(ctl, ctl->cmdstr, NULL)) vshCommandRun(ctl, ctl->cmd); } VIR_FREE(ctl->cmdstr); diff --git a/tools/virt-admin.c b/tools/virt-admin.c index e529a28916..b8b33af193 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -1335,7 +1335,7 @@ vshAdmParseArgv(vshControl *ctl, int argc, char **argv) ctl->imode = false; if (argc - optind == 1) { vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n", argv[optind]); - return vshCommandStringParse(ctl, argv[optind]); + return vshCommandStringParse(ctl, argv[optind], NULL); } else { return vshCommandArgvParse(ctl, argc - optind, argv + optind); } @@ -1555,7 +1555,7 @@ main(int argc, char **argv) #if WITH_READLINE add_history(ctl->cmdstr); #endif - if (vshCommandStringParse(ctl, ctl->cmdstr)) + if (vshCommandStringParse(ctl, ctl->cmdstr, NULL)) vshCommandRun(ctl, ctl->cmd); } VIR_FREE(ctl->cmdstr); diff --git a/tools/vsh.c b/tools/vsh.c index 2366b7b719..5b78336c59 100644 --- a/tools/vsh.c +++ b/tools/vsh.c @@ -1386,26 +1386,34 @@ struct _vshCommandParser { }; static bool -vshCommandParse(vshControl *ctl, vshCommandParser *parser) +vshCommandParse(vshControl *ctl, vshCommandParser *parser, vshCmd **partial) { char *tkdata = NULL; vshCmd *clast = NULL; vshCmdOpt *first = NULL; + const vshCmdDef *cmd = NULL; - vshCommandFree(ctl->cmd); - ctl->cmd = NULL; + if (!partial) { + vshCommandFree(ctl->cmd); + ctl->cmd = NULL; + } while (1) { vshCmdOpt *last = NULL; - const vshCmdDef *cmd = NULL; vshCommandToken tk; bool data_only = false; uint64_t opts_need_arg = 0; uint64_t opts_required = 0; uint64_t opts_seen = 0; + cmd = NULL; first = NULL; + if (partial) { + vshCommandFree(*partial); + *partial = NULL; + } + while (1) { const vshCmdOptDef *opt = NULL; @@ -1422,7 +1430,8 @@ vshCommandParse(vshControl *ctl, vshCommandParser *parser) if (cmd == NULL) { /* first token must be command name */ if (!(cmd = vshCmddefSearch(tkdata))) { - vshError(ctl, _("unknown command: '%s'"), tkdata); + if (!partial) + vshError(ctl, _("unknown command: '%s'"), tkdata); goto syntaxError; /* ... or ignore this command only? */ } @@ -1434,9 +1443,10 @@ vshCommandParse(vshControl *ctl, vshCommandParser *parser) } if (vshCmddefOptParse(cmd, &opts_need_arg, &opts_required) < 0) { - vshError(ctl, - _("internal error: bad options in command: '%s'"), - tkdata); + if (!partial) + vshError(ctl, + _("internal error: bad options in command: '%s'"), + tkdata); goto syntaxError; } VIR_FREE(tkdata); @@ -1454,7 +1464,7 @@ vshCommandParse(vshControl *ctl, vshCommandParser *parser) /* Special case 'help' to ignore all spurious options */ if (!(opt = vshCmddefGetOption(ctl, cmd, tkdata + 2, &opts_seen, &opt_index, - &optstr, true))) { + &optstr, partial == NULL))) { VIR_FREE(optstr); if (STREQ(cmd->name, "help")) continue; @@ -1471,11 +1481,24 @@ vshCommandParse(vshControl *ctl, vshCommandParser *parser) if (tk == VSH_TK_ERROR) goto syntaxError; if (tk != VSH_TK_ARG) { - vshError(ctl, - _("expected syntax: --%s <%s>"), - opt->name, - opt->type == - VSH_OT_INT ? _("number") : _("string")); + if (partial) { + vshCmdOpt *arg = vshMalloc(ctl, sizeof(vshCmdOpt)); + arg->def = opt; + arg->data = tkdata; + tkdata = NULL; + arg->next = NULL; + if (!first) + first = arg; + if (last) + last->next = arg; + last = arg; + } else { + vshError(ctl, + _("expected syntax: --%s <%s>"), + opt->name, + opt->type == + VSH_OT_INT ? _("number") : _("string")); + } goto syntaxError; } if (opt->type != VSH_OT_ARGV) @@ -1483,8 +1506,9 @@ vshCommandParse(vshControl *ctl, vshCommandParser *parser) } else { tkdata = NULL; if (optstr) { - vshError(ctl, _("invalid '=' after option --%s"), - opt->name); + if (!partial) + vshError(ctl, _("invalid '=' after option --%s"), + opt->name); VIR_FREE(optstr); goto syntaxError; } @@ -1500,7 +1524,8 @@ vshCommandParse(vshControl *ctl, vshCommandParser *parser) if (!(opt = vshCmddefGetData(cmd, &opts_need_arg, &opts_seen)) && STRNEQ(cmd->name, "help")) { - vshError(ctl, _("unexpected data '%s'"), tkdata); + if (!partial) + vshError(ctl, _("unexpected data '%s'"), tkdata); goto syntaxError; } } @@ -1519,11 +1544,12 @@ vshCommandParse(vshControl *ctl, vshCommandParser *parser) last->next = arg; last = arg; - vshDebug(ctl, VSH_ERR_INFO, "%s: %s(%s): %s\n", - cmd->name, - opt->name, - opt->type != VSH_OT_BOOL ? _("optdata") : _("bool"), - opt->type != VSH_OT_BOOL ? arg->data : _("(none)")); + if (!partial) + vshDebug(ctl, VSH_ERR_INFO, "%s: %s(%s): %s\n", + cmd->name, + opt->name, + opt->type != VSH_OT_BOOL ? _("optdata") : _("bool"), + opt->type != VSH_OT_BOOL ? arg->data : _("(none)")); } } @@ -1555,17 +1581,24 @@ vshCommandParse(vshControl *ctl, vshCommandParser *parser) c->opts = first; c->def = cmd; c->next = NULL; + first = NULL; - if (vshCommandCheckOpts(ctl, c, opts_required, opts_seen) < 0) { + if (!partial && + vshCommandCheckOpts(ctl, c, opts_required, opts_seen) < 0) { VIR_FREE(c); goto syntaxError; } - if (!ctl->cmd) - ctl->cmd = c; - if (clast) - clast->next = c; - clast = c; + if (partial) { + vshCommandFree(*partial); + *partial = c; + } else { + if (!ctl->cmd) + ctl->cmd = c; + if (clast) + clast->next = c; + clast = c; + } } if (tk == VSH_TK_END) @@ -1575,9 +1608,19 @@ vshCommandParse(vshControl *ctl, vshCommandParser *parser) return true; syntaxError: - vshCommandFree(ctl->cmd); - ctl->cmd = NULL; - vshCommandOptFree(first); + if (partial) { + vshCmd *tmp; + + tmp = vshMalloc(ctl, sizeof(*tmp)); + tmp->opts = first; + tmp->def = cmd; + + *partial = tmp; + } else { + vshCommandFree(ctl->cmd); + ctl->cmd = NULL; + vshCommandOptFree(first); + } VIR_FREE(tkdata); return false; } @@ -1612,7 +1655,7 @@ vshCommandArgvParse(vshControl *ctl, int nargs, char **argv) parser.arg_pos = argv; parser.arg_end = argv + nargs; parser.getNextArg = vshCommandArgvGetArg; - return vshCommandParse(ctl, &parser); + return vshCommandParse(ctl, &parser, NULL); } /* ---------------------- @@ -1684,7 +1727,7 @@ vshCommandStringGetArg(vshControl *ctl, vshCommandParser *parser, char **res, } bool -vshCommandStringParse(vshControl *ctl, char *cmdstr) +vshCommandStringParse(vshControl *ctl, char *cmdstr, vshCmd **partial) { vshCommandParser parser; @@ -1693,7 +1736,7 @@ vshCommandStringParse(vshControl *ctl, char *cmdstr) parser.pos = cmdstr; parser.getNextArg = vshCommandStringGetArg; - return vshCommandParse(ctl, &parser); + return vshCommandParse(ctl, &parser, partial); } /** diff --git a/tools/vsh.h b/tools/vsh.h index ab755bccff..8f7df9ff83 100644 --- a/tools/vsh.h +++ b/tools/vsh.h @@ -299,7 +299,7 @@ int vshBlockJobOptionBandwidth(vshControl *ctl, unsigned long *bandwidth); bool vshCommandOptBool(const vshCmd *cmd, const char *name); bool vshCommandRun(vshControl *ctl, const vshCmd *cmd); -bool vshCommandStringParse(vshControl *ctl, char *cmdstr); +bool vshCommandStringParse(vshControl *ctl, char *cmdstr, vshCmd **partial); const vshCmdOpt *vshCommandOptArgv(vshControl *ctl, const vshCmd *cmd, const vshCmdOpt *opt);