The GOptionContext API has the benefit over getopt_long that it will
automatically handle --help output formatting.
Signed-off-by: Daniel P. Berrangé <berrange(a)redhat.com>
---
tools/virt-admin.c | 210 ++++++++++++++++++++++-----------------------
1 file changed, 101 insertions(+), 109 deletions(-)
diff --git a/tools/virt-admin.c b/tools/virt-admin.c
index e549ec1f83..bbb0cbb112 100644
--- a/tools/virt-admin.c
+++ b/tools/virt-admin.c
@@ -21,8 +21,6 @@
#include <config.h>
#include "virt-admin.h"
-#include <getopt.h>
-
#if WITH_READLINE
# include <readline/readline.h>
# include <readline/history.h>
@@ -1212,44 +1210,34 @@ vshAdmDeinit(vshControl *ctl)
/*
* Print usage
*/
-static void
-vshAdmUsage(void)
+static char *
+vshAdmBuildDescription(void)
{
const vshCmdGrp *grp;
const vshCmdDef *cmd;
-
- fprintf(stdout, _("\n%s [options]... [<command_string>]"
- "\n%s [options]... <command> [args...]\n\n"
- " options:\n"
- " -c | --connect=URI daemon admin connection
URI\n"
- " -d | --debug=NUM debug level [0-4]\n"
- " -h | --help this help\n"
- " -l | --log=FILE output logging to file\n"
- " -q | --quiet quiet mode\n"
- " -v short version\n"
- " -V long version\n"
- " --version[=TYPE] version, TYPE is short or long
(default short)\n"
- " commands (non interactive mode):\n\n"), progname,
- progname);
+ GString *str = g_string_new("Commands (non interactive mode):\n\n");
for (grp = cmdGroups; grp->name; grp++) {
- fprintf(stdout, _(" %s (help keyword '%s')\n"),
- grp->name, grp->keyword);
+ g_string_append_printf(str,
+ _(" %s (help keyword '%s')\n"),
+ grp->name, grp->keyword);
for (cmd = grp->commands; cmd->name; cmd++) {
if (cmd->flags & VSH_CMD_FLAG_ALIAS)
continue;
- fprintf(stdout,
- " %-30s %s\n", cmd->name,
- _(vshCmddefGetInfo(cmd, "help")));
+ g_string_append_printf(str,
+ " %-30s %s\n",
+ cmd->name,
+ _(vshCmddefGetInfo(cmd, "help")));
}
- fprintf(stdout, "\n");
+ g_string_append_printf(str, "\n");
}
- fprintf(stdout, "%s",
- _("\n (specify help <group> for details about the commands in the
group)\n"));
- fprintf(stdout, "%s",
- _("\n (specify help <command> for details about the
command)\n\n"));
- return;
+ g_string_append_printf(str,
+ _("Specify help <group> for details about the
commands in the group)\n"));
+ g_string_append_printf(str,
+ _("Specify help <command> for details about the
command)\n"));
+
+ return g_string_free(str, FALSE);
}
/*
@@ -1275,103 +1263,107 @@ vshAdmShowVersion(vshControl *ctl ATTRIBUTE_UNUSED)
vshPrint(ctl, "\n");
}
+static gboolean
+vshAdmVersion(const gchar *option_name G_GNUC_UNUSED,
+ const gchar *value,
+ gpointer data,
+ GError **error G_GNUC_UNUSED)
+{
+ vshControl *ctl = data;
+
+ if (STREQ(option_name, "-V") || STREQ_NULLABLE(value, "long"))
+ vshAdmShowVersion(ctl);
+ else
+ puts(VERSION);
+
+ exit(EXIT_SUCCESS);
+}
+
+/*
+ * argv[]: virsh [options] [command]
+ *
+ */
static bool
vshAdmParseArgv(vshControl *ctl, int argc, char **argv)
{
- int arg, debug;
- size_t i;
- int longindex = -1;
- struct option opt[] = {
- {"connect", required_argument, NULL, 'c'},
- {"debug", required_argument, NULL, 'd'},
- {"help", no_argument, NULL, 'h'},
- {"log", required_argument, NULL, 'l'},
- {"quiet", no_argument, NULL, 'q'},
- {"version", optional_argument, NULL, 'v'},
- {NULL, 0, NULL, 0}
+ int debug = 0;
+ char *logfile = NULL;
+ GOptionEntry opt[] = {
+ { "connect", 'c', 0,
+ G_OPTION_ARG_STRING, &ctl->connname,
+ _("hypervisor connection URI"), "URI" },
+ { "debug", 'd', 0,
+ G_OPTION_ARG_INT, &debug,
+ _("debug level [0-4]\n"), "LEVEL" },
+ { "log", 'l', 0,
+ G_OPTION_ARG_STRING, &logfile,
+ _("output logging to file"), "FILENAME" },
+ { "quiet", 'q', 0,
+ G_OPTION_ARG_NONE, &ctl->quiet,
+ _("quite mode"), NULL },
+ { "version", 'v', G_OPTION_FLAG_OPTIONAL_ARG,
+ G_OPTION_ARG_CALLBACK, vshAdmVersion,
+ _("print short version"), "[short]" },
+ { "version", 'V', G_OPTION_FLAG_OPTIONAL_ARG,
+ G_OPTION_ARG_CALLBACK, vshAdmVersion,
+ _("print long version"), "long" },
+ { NULL, 0, 0, 0, NULL, NULL, NULL },
};
+ g_autoptr(GOptionContext) optctx = NULL;
+ GOptionGroup *optgrp;
+ g_autoptr(GError) error = NULL;
+
+ optctx = g_option_context_new(_("[COMMAND [OPTION…] - libvirt admin
shell"));
+ optgrp = g_option_group_new(NULL, NULL, NULL, ctl, NULL);
+ g_option_group_set_translation_domain(optgrp, PACKAGE);
+ g_option_group_add_entries(optgrp, opt);
+ g_option_context_set_main_group(optctx, optgrp);
+ g_option_context_set_strict_posix(optctx, true);
+ g_option_context_set_description(optctx,
+ vshAdmBuildDescription());
+
+ if (!g_option_context_parse(optctx, &argc, &argv, &error)) {
+ vshError(ctl, _("option parsing failed: %s\n"), error->message);
+ exit(EXIT_FAILURE);
+ }
- /* Standard (non-command) options. The leading + ensures that no
- * argument reordering takes place, so that command options are
- * not confused with top-level virt-admin options. */
- while ((arg = getopt_long(argc, argv, "+:c:d:hl:qvV", opt, &longindex))
!= -1) {
- switch (arg) {
- case 'c':
- VIR_FREE(ctl->connname);
- ctl->connname = vshStrdup(ctl, optarg);
- break;
- case 'd':
- if (virStrToLong_i(optarg, NULL, 10, &debug) < 0) {
- vshError(ctl, _("option %s takes a numeric argument"),
- longindex == -1 ? "-d" : "--debug");
- exit(EXIT_FAILURE);
- }
- if (debug < VSH_ERR_DEBUG || debug > VSH_ERR_ERROR)
- vshError(ctl, _("ignoring debug level %d out of range
[%d-%d]"),
- debug, VSH_ERR_DEBUG, VSH_ERR_ERROR);
- else
- ctl->debug = debug;
- break;
- case 'h':
- vshAdmUsage();
- exit(EXIT_SUCCESS);
- break;
- case 'l':
- vshCloseLogFile(ctl);
- ctl->logfile = vshStrdup(ctl, optarg);
- vshOpenLogFile(ctl);
- break;
- case 'q':
- ctl->quiet = true;
- break;
- case 'v':
- if (STRNEQ_NULLABLE(optarg, "long")) {
- puts(VERSION);
- exit(EXIT_SUCCESS);
- }
- ATTRIBUTE_FALLTHROUGH;
- case 'V':
- vshAdmShowVersion(ctl);
- exit(EXIT_SUCCESS);
- case ':':
- for (i = 0; opt[i].name != NULL; i++) {
- if (opt[i].val == optopt)
- break;
- }
- if (opt[i].name)
- vshError(ctl, _("option '-%c'/'--%s' requires an
argument"),
- optopt, opt[i].name);
- else
- vshError(ctl, _("option '-%c' requires an argument"),
optopt);
- exit(EXIT_FAILURE);
- case '?':
- if (optopt)
- vshError(ctl, _("unsupported option '-%c'. See
--help."), optopt);
- else
- vshError(ctl, _("unsupported option '%s'. See
--help."), argv[optind - 1]);
- exit(EXIT_FAILURE);
- default:
- vshError(ctl, _("unknown option"));
- exit(EXIT_FAILURE);
- }
- longindex = -1;
+ /* GOptionContext doesn't support -- explicitly, but
+ * we told it to stop at first unknown option, or
+ * first non-option, so we'll see '--' and can discard it
+ */
+ if (argc >= 2 && STREQ(argv[1], "--")) {
+ argc--;
+ argv++;
}
- if (argc == optind) {
+ if (debug < VSH_ERR_DEBUG || debug > VSH_ERR_ERROR) {
+ vshError(ctl, _("debug level %d out of range [%d-%d]"),
+ debug, VSH_ERR_DEBUG, VSH_ERR_ERROR);
+ exit(EXIT_FAILURE);
+ }
+
+ if (logfile) {
+ vshCloseLogFile(ctl);
+ ctl->logfile = logfile;
+ vshOpenLogFile(ctl);
+ }
+
+ if (argc == 1) {
ctl->imode = true;
} else {
/* parse command */
ctl->imode = false;
- if (argc - optind == 1) {
- vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n",
argv[optind]);
- return vshCommandStringParse(ctl, argv[optind], NULL);
+ if (argc == 2) {
+ vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n",
argv[1]);
+ return vshCommandStringParse(ctl, argv[1], NULL);
} else {
- return vshCommandArgvParse(ctl, argc - optind, argv + optind);
+ return vshCommandArgvParse(ctl, argc - 1, argv + 1);
}
}
return true;
}
+
static const vshCmdDef vshAdmCmds[] = {
VSH_CMD_CD,
VSH_CMD_ECHO,
--
2.21.0