* 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