From nobody Sun Feb 8 19:39:50 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1569604685; cv=none; d=zoho.com; s=zohoarc; b=KbyWGR0ltq57YjBl+M72ov9PaWKtktItk4J7ABu/A/BCmfB1xXTpMGibxJIt5JOuQR1WMCrygU8z9gMk4960xLj6BzQN+i2RuERdAT9oh9Yl7rhP2/OQVnAJuwNbfoDg1TlzVpy+7dytZXxQJFWkGk/V75evTtusWYPAx9lXHOY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1569604685; h=Content-Type:Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=e7jLoe8wcptDxh0GStEi/TiAisrW42RbRsBjLzxoOgw=; b=bKUYRLJ8E0rpjpoW0wGJ89DpWvV1Bed+x7+CuvMmeJsnPeqjbtb/raFTc0R/6gog7HNOyExBFkV3ZzGNDbWJ8rGB5f1W7GWZ3zsNVhrivbgaG5IjBs3to+K7WHDrrJ91g6jHbZqyQveT/RZHeqno2nE2iu0zUXXcrhXm9v6oe5I= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1569604685873588.8461308245019; Fri, 27 Sep 2019 10:18:05 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 4BD1C18ABC29; Fri, 27 Sep 2019 17:18:04 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D2FA21000337; Fri, 27 Sep 2019 17:18:03 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 7E54A4EEB8; Fri, 27 Sep 2019 17:18:03 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x8RHHqbC016601 for ; Fri, 27 Sep 2019 13:17:52 -0400 Received: by smtp.corp.redhat.com (Postfix) id 0CE32600D1; Fri, 27 Sep 2019 17:17:52 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-112-55.ams2.redhat.com [10.36.112.55]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8D195600C6; Fri, 27 Sep 2019 17:17:50 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: libvir-list@redhat.com Date: Fri, 27 Sep 2019 18:17:31 +0100 Message-Id: <20190927171733.10842-10-berrange@redhat.com> In-Reply-To: <20190927171733.10842-1-berrange@redhat.com> References: <20190927171733.10842-1-berrange@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-loop: libvir-list@redhat.com Subject: [libvirt] [PATCH 09/11] virsh: convert command line parsing to use GOptionContext X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.6.2 (mx1.redhat.com [10.5.110.62]); Fri, 27 Sep 2019 17:18:04 +0000 (UTC) The GOptionContext API has the benefit over getopt_long that it will automatically handle --help output formatting. Signed-off-by: Daniel P. Berrang=C3=A9 --- tools/virsh.c | 303 ++++++++++++++++++++++---------------------------- 1 file changed, 135 insertions(+), 168 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index ec20f35a77..6c469ff576 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -23,7 +23,6 @@ =20 #include #include -#include #include #include #include @@ -445,53 +444,36 @@ virshDeinit(vshControl *ctl) } =20 /* - * Print usage + * Build help description for commands */ -static void -virshUsage(void) +static char * +virshBuildDescription(void) { const vshCmdGrp *grp; const vshCmdDef *cmd; - - fprintf(stdout, _("\n%s [options]... []" - "\n%s [options]... [args...]\n\n" - " options:\n" - " -c | --connect=3DURI hypervisor connection= URI\n" - " -d | --debug=3DNUM debug level [0-4]\n" - " -e | --escape set escape sequence for= console\n" - " -h | --help this help\n" - " -k | --keepalive-interval=3DNUM\n" - " keepalive interval in s= econds, 0 for disable\n" - " -K | --keepalive-count=3DNUM\n" - " number of possible miss= ed keepalive messages\n" - " -l | --log=3DFILE output logging to fil= e\n" - " -q | --quiet quiet mode\n" - " -r | --readonly connect readonly\n" - " -t | --timing print timing informatio= n\n" - " -v short version\n" - " -V long version\n" - " --version[=3DTYPE] version, TYPE is shor= t or long (default short)\n" - " commands (non interactive mode):\n\n"), progname, - progname); + GString *str =3D g_string_new("Commands (non interactive mode):\n\n"); =20 for (grp =3D 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 =3D 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"); } =20 - fprintf(stdout, "%s", - _("\n (specify help for details about the commands in= the group)\n")); - fprintf(stdout, "%s", - _("\n (specify help for details about the command)\= n\n")); - return; + g_string_append_printf(str, + _("Specify help for details about the c= ommands in the group)\n")); + g_string_append_printf(str, + _("Specify help for details about the= command)\n")); + + return g_string_free(str, FALSE); } =20 /* @@ -647,6 +629,22 @@ virshAllowedEscapeChar(char c) ('@' <=3D c && c <=3D '_'); } =20 +static gboolean +virshVersion(const gchar *option_name G_GNUC_UNUSED, + const gchar *value, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + vshControl *ctl =3D data; + + if (value =3D=3D NULL || STRNEQ(value, "long")) + puts(VERSION); + else + virshShowVersion(ctl); + + exit(EXIT_SUCCESS); +} + /* * argv[]: virsh [options] [command] * @@ -654,152 +652,121 @@ virshAllowedEscapeChar(char c) static bool virshParseArgv(vshControl *ctl, int argc, char **argv) { - int arg, len, debug, keepalive; - size_t i; - int longindex =3D -1; + int debug =3D 0; + bool version =3D false; + size_t len; virshControlPtr priv =3D ctl->privData; - struct option opt[] =3D { - {"connect", required_argument, NULL, 'c'}, - {"debug", required_argument, NULL, 'd'}, - {"escape", required_argument, NULL, 'e'}, - {"help", no_argument, NULL, 'h'}, - {"keepalive-interval", required_argument, NULL, 'k'}, - {"keepalive-count", required_argument, NULL, 'K'}, - {"log", required_argument, NULL, 'l'}, - {"quiet", no_argument, NULL, 'q'}, - {"readonly", no_argument, NULL, 'r'}, - {"timing", no_argument, NULL, 't'}, - {"version", optional_argument, NULL, 'v'}, - {NULL, 0, NULL, 0} + char *logfile =3D NULL; + int keepalive_interval =3D INT_MAX; + int keepalive_count =3D INT_MAX; + GOptionEntry opt[] =3D { + { "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" }, + { "escape", 'e', 0, + G_OPTION_ARG_STRING, &priv->escapeChar, + _("set escape sequence for console"), "ESCAPE" }, + { "keepalive-interval", 'k', 0, + G_OPTION_ARG_INT, &keepalive_interval, + _("keepalive interval in seconds, 0 for disable"), "SECS" }, + { "keepalive-count", 'K', 0, + G_OPTION_ARG_INT, &keepalive_count, + _("number of possible missed keepalive messages"), "NUM" }, + { "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 }, + { "readonly", 'r', 0, + G_OPTION_ARG_NONE, &priv->readonly, + _("connect readonly"), NULL }, + { "timing", 't', 0, + G_OPTION_ARG_NONE, &ctl->timing, + _("print timing information"), NULL }, + { "version", 'v', G_OPTION_FLAG_OPTIONAL_ARG, + G_OPTION_ARG_CALLBACK, virshVersion, + _("print short version"), "[short]" }, + { "version", 'V', 0, + G_OPTION_ARG_NONE, &version, + _("print long version"), "long" }, + { NULL, 0, 0, 0, NULL, NULL, NULL }, }; + g_autoptr(GOptionContext) optctx =3D NULL; + GOptionGroup *optgrp; + g_autoptr(GError) error =3D NULL; + + optctx =3D g_option_context_new(_("[COMMAND [OPTION=E2=80=A6] - libvir= t shell")); + optgrp =3D 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, + virshBuildDescription()); + + if (!g_option_context_parse(optctx, &argc, &argv, &error)) { + vshError(ctl, _("option parsing failed: %s\n"), error->message); + exit(EXIT_FAILURE); + } =20 - /* Standard (non-command) options. The leading + ensures that no - * argument reordering takes place, so that command options are - * not confused with top-level virsh options. */ - while ((arg =3D getopt_long(argc, argv, "+:c:d:e:hk:K:l:qrtvV", opt, &= longindex)) !=3D -1) { - switch (arg) { - case 'c': - VIR_FREE(ctl->connname); - ctl->connname =3D vshStrdup(ctl, optarg); - break; - case 'd': - if (virStrToLong_i(optarg, NULL, 10, &debug) < 0) { - vshError(ctl, _("option %s takes a numeric argument"), - longindex =3D=3D -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 =3D debug; - break; - case 'e': - len =3D strlen(optarg); - - if ((len =3D=3D 2 && *optarg =3D=3D '^' && - virshAllowedEscapeChar(optarg[1])) || - (len =3D=3D 1 && *optarg !=3D '^')) { - priv->escapeChar =3D optarg; - } else { - vshError(ctl, _("Invalid string '%s' for escape sequence"), - optarg); - exit(EXIT_FAILURE); - } - break; - case 'h': - virshUsage(); - exit(EXIT_SUCCESS); - break; - case 'k': - if (virStrToLong_i(optarg, NULL, 0, &keepalive) < 0) { - vshError(ctl, - _("Invalid value for option %s"), - longindex =3D=3D -1 ? "-k" : "--keepalive-interva= l"); - exit(EXIT_FAILURE); - } - - if (keepalive < 0) { - vshError(ctl, - _("option %s requires a positive integer argument= "), - longindex =3D=3D -1 ? "-k" : "--keepalive-interva= l"); - exit(EXIT_FAILURE); - } - ctl->keepalive_interval =3D keepalive; - break; - case 'K': - if (virStrToLong_i(optarg, NULL, 0, &keepalive) < 0) { - vshError(ctl, - _("Invalid value for option %s"), - longindex =3D=3D -1 ? "-K" : "--keepalive-count"); - exit(EXIT_FAILURE); - } + 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); + } =20 - if (keepalive < 0) { - vshError(ctl, - _("option %s requires a positive integer argument= "), - longindex =3D=3D -1 ? "-K" : "--keepalive-count"); - exit(EXIT_FAILURE); - } - ctl->keepalive_count =3D keepalive; - break; - case 'l': - vshCloseLogFile(ctl); - ctl->logfile =3D vshStrdup(ctl, optarg); - vshOpenLogFile(ctl); - break; - case 'q': - ctl->quiet =3D true; - break; - case 't': - ctl->timing =3D true; - break; - case 'r': - priv->readonly =3D true; - break; - case 'v': - if (STRNEQ_NULLABLE(optarg, "long")) { - puts(VERSION); - exit(EXIT_SUCCESS); - } - ATTRIBUTE_FALLTHROUGH; - case 'V': - virshShowVersion(ctl); - exit(EXIT_SUCCESS); - case ':': - for (i =3D 0; opt[i].name !=3D NULL; i++) { - if (opt[i].val =3D=3D 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"), opto= pt); + if (keepalive_interval !=3D INT_MAX) { + if (keepalive_interval < 0) { + vshError(ctl, + _("keepalive interval requires a positive integer")); exit(EXIT_FAILURE); - case '?': - if (optopt) - vshError(ctl, _("unsupported option '-%c'. See --help."), = optopt); - else - vshError(ctl, _("unsupported option '%s'. See --help."), a= rgv[optind - 1]); - exit(EXIT_FAILURE); - default: - vshError(ctl, _("unknown option")); + } + ctl->keepalive_interval =3D keepalive_interval; + } + + if (keepalive_count !=3D INT_MAX) { + if (keepalive_count < 0) { + vshError(ctl, + _("keepalive count requires a positive integer")); exit(EXIT_FAILURE); } - longindex =3D -1; + ctl->keepalive_count =3D keepalive_count; + } + + len =3D strlen(priv->escapeChar); + if (!((len =3D=3D 2 && *priv->escapeChar =3D=3D '^' && + virshAllowedEscapeChar(priv->escapeChar[1])) || + (len =3D=3D 1 && *priv->escapeChar !=3D '^'))) { + vshError(ctl, _("Invalid string '%s' for escape sequence"), + priv->escapeChar); + exit(EXIT_FAILURE); + } + + if (version) { + virshShowVersion(ctl); + exit(EXIT_SUCCESS); + } + + if (logfile) { + vshCloseLogFile(ctl); + ctl->logfile =3D logfile; + vshOpenLogFile(ctl); } =20 - if (argc =3D=3D optind) { + if (argc =3D=3D 1) { ctl->imode =3D true; } else { /* parse command */ ctl->imode =3D false; - if (argc - optind =3D=3D 1) { - vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n", argv[optind]= ); - return vshCommandStringParse(ctl, argv[optind], NULL); + if (argc =3D=3D 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; --=20 2.21.0 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list