* tools/virsh.c (cmdEcho): New command. (commands): Add it. (vshCmdOptType): Add VSH_OT_ARGV. (vshCmddefGetData): Special case new opt flag. (vshCommandOptArgv): New function. --- Not complete yet, but this shows what I'm thinking of. Adding the echo command has two benefits: 1. It will let me add unit tests for the recent virsh command line improvements - echo back arbitrary strings to make sure quoting is as desired. This part works with what I have here, before I ran out of time to finish this today. 2. Make it easier for a user on the command line to conver an arbitrary string into something safe for shell evalution and/or XML usage, by munging the input in a way that it can be reused in the desired context. Not yet implemented; hence the RFC. It exploits the fact that "--" is consumed as the end-of-options, hence, there is no way for "" to be recognized as a valid option name, so the only way we can encounter VSH_OT_ARGV is via the new argv handling, at which point we can handle all remaining command line arguments. tools/virsh.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 80 insertions(+), 8 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 89c2e1e..f361658 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -119,11 +119,12 @@ typedef enum { * vshCmdOptType - command option type */ typedef enum { - VSH_OT_NONE = 0, /* none */ - VSH_OT_BOOL, /* boolean option */ - VSH_OT_STRING, /* string option */ - VSH_OT_INT, /* int option */ - VSH_OT_DATA /* string data (as non-option) */ + VSH_OT_NONE = 0, /* none */ + VSH_OT_BOOL, /* boolean option */ + VSH_OT_STRING, /* string option */ + VSH_OT_INT, /* int option */ + VSH_OT_DATA, /* string data (as non-option) */ + VSH_OT_ARGV /* remaining arguments, opt->name should be "" */ } vshCmdOptType; /* @@ -230,6 +231,7 @@ static char *vshCommandOptString(const vshCmd *cmd, const char *name, static long long vshCommandOptLongLong(const vshCmd *cmd, const char *name, int *found); static int vshCommandOptBool(const vshCmd *cmd, const char *name); +static char *vshCommandOptArgv(const vshCmd *cmd, int count); #define VSH_BYID (1 << 1) #define VSH_BYUUID (1 << 2) @@ -8917,6 +8919,54 @@ cmdPwd(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) #endif /* + * "echo" command + */ +static const vshCmdInfo info_echo[] = { + {"help", N_("echo arguments")}, + {"desc", N_("Echo back arguments, possibly with quoting.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_echo[] = { + {"shell", VSH_OT_BOOL, 0, N_("escape for shell use")}, + {"xml", VSH_OT_BOOL, 0, N_("escape for XML use")}, + {"", VSH_OT_ARGV, 0, N_("arguments to echo")}, + {NULL, 0, 0, NULL} +}; + +/* Exists mainly for debugging virsh, but also handy for adding back + * quotes for later evaluation. + */ +static int +cmdEcho (vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd) +{ + bool shell = false; + bool xml = false; + int count = 0; + char *arg; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (vshCommandOptBool(cmd, "shell")) + shell = true; + if (vshCommandOptBool(cmd, "xml")) + xml = true; + + while ((arg = vshCommandOptArgv(cmd, count)) != NULL) { + /* TODO - use buf */ + if (xml) { + /* TODO - use virBufferEscapeString */ + } + if (shell) { + /* TODO - add '' and escape embedded ' */ + } + vshPrint(ctl, "%s%s", count ? " " : "", arg); + count++; + } + + return TRUE; +} + +/* * "edit" command */ static const vshCmdInfo info_edit[] = { @@ -9545,6 +9595,7 @@ static const vshCmdDef commands[] = { {"domxml-from-native", cmdDomXMLFromNative, opts_domxmlfromnative, info_domxmlfromnative}, {"domxml-to-native", cmdDomXMLToNative, opts_domxmltonative, info_domxmltonative}, {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml}, + {"echo", cmdEcho, opts_echo, info_echo}, {"edit", cmdEdit, opts_edit, info_edit}, {"find-storage-pool-sources", cmdPoolDiscoverSources, opts_find_storage_pool_sources, info_find_storage_pool_sources}, @@ -9707,8 +9758,8 @@ vshCmddefGetData(const vshCmdDef * cmd, int data_ct) const vshCmdOptDef *opt; for (opt = cmd->opts; opt && opt->name; opt++) { - if (opt->type == VSH_OT_DATA) { - if (data_ct == 0) + if (opt->type >= VSH_OT_DATA) { + if (data_ct == 0 || opt->type == VSH_OT_ARGV) return opt; else data_ct--; @@ -9970,6 +10021,27 @@ vshCommandOptBool(const vshCmd *cmd, const char *name) return vshCommandOpt(cmd, name) ? TRUE : FALSE; } +/* + * Returns the COUNT argv argument, or NULL after last argument. + * + * Requires that a VSH_OT_ARGV option with the name "" be last in the + * list of supported options in CMD->def->opts. + */ +static char * +vshCommandOptArgv(const vshCmd *cmd, int count) +{ + vshCmdOpt *opt = cmd->opts; + + while (opt) { + if (opt->def && opt->def->type == VSH_OT_ARGV) { + if (count-- == 0) + return opt->data; + } + opt = opt->next; + } + return NULL; +} + /* Determine whether CMD->opts includes an option with name OPTNAME. If not, give a diagnostic and return false. If so, return true. */ -- 1.7.2.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list