[systemd-devel] [PATCH 2/2] verbs: allow matching by prefix
Dave Reisner
dreisner at archlinux.org
Sat Dec 20 08:19:51 PST 2014
Be nice to command line users and allow matching prefixes of verbs,
similar to the way getopt_long operates. If the prefix cannot be
resolved to a singular verb, log the ambiguity and return an error.
---
I'm not thrilled about adding this to every manpage out of concern that it
might get out of sync, but I don't really see a better way.
man/bootctl.xml | 3 ++-
man/busctl.xml | 3 ++-
man/coredumpctl.xml | 3 ++-
man/hostnamectl.xml | 3 ++-
man/kernel-install.xml | 3 ++-
man/localectl.xml | 3 ++-
man/loginctl.xml | 3 ++-
man/systemctl.xml | 3 ++-
man/telinit.xml | 3 ++-
src/shared/verbs.c | 58 ++++++++++++++++++++++++++++++++++++++------------
src/test/test-verbs.c | 9 ++++++++
11 files changed, 71 insertions(+), 23 deletions(-)
diff --git a/man/bootctl.xml b/man/bootctl.xml
index 5254022..93363bd 100644
--- a/man/bootctl.xml
+++ b/man/bootctl.xml
@@ -79,7 +79,8 @@
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
- <para>The following commands are understood:</para>
+ <para>Commands can be specified by exact match or unambiguous prefix match.
+ The following commands are understood:</para>
<variablelist>
<varlistentry>
diff --git a/man/busctl.xml b/man/busctl.xml
index 285725e..a215a7e 100644
--- a/man/busctl.xml
+++ b/man/busctl.xml
@@ -260,7 +260,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
<refsect1>
<title>Commands</title>
- <para>The following commands are understood:</para>
+ <para>Commands can be specified by exact match or unambiguous prefix match.
+ The following commands are understood:</para>
<variablelist>
<varlistentry>
diff --git a/man/coredumpctl.xml b/man/coredumpctl.xml
index ed84621..28cce85 100644
--- a/man/coredumpctl.xml
+++ b/man/coredumpctl.xml
@@ -111,7 +111,8 @@
</variablelist>
- <para>The following commands are understood:</para>
+ <para>Commands can be specified by exact match or unambiguous prefix match.
+ The following commands are understood:</para>
<variablelist>
<varlistentry>
diff --git a/man/hostnamectl.xml b/man/hostnamectl.xml
index de15402..51cb995 100644
--- a/man/hostnamectl.xml
+++ b/man/hostnamectl.xml
@@ -135,7 +135,8 @@
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
- <para>The following commands are understood:</para>
+ <para>Commands can be specified by exact match or unambiguous prefix match.
+ The following commands are understood:</para>
<variablelist>
<varlistentry>
diff --git a/man/kernel-install.xml b/man/kernel-install.xml
index d4d0180..aa6aca2 100644
--- a/man/kernel-install.xml
+++ b/man/kernel-install.xml
@@ -79,7 +79,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
<refsect1>
<title>Commands</title>
- <para>The following commands are understood:</para>
+ <para>Commands can be specified by exact match or unambiguous prefix match.
+ The following commands are understood:</para>
<variablelist>
<varlistentry>
<term><command>add <replaceable>KERNEL-VERSION</replaceable> <replaceable>KERNEL-IMAGE</replaceable></command></term>
diff --git a/man/localectl.xml b/man/localectl.xml
index b472b6b..8f5b442 100644
--- a/man/localectl.xml
+++ b/man/localectl.xml
@@ -113,7 +113,8 @@
<xi:include href="standard-options.xml" xpointer="no-pager" />
</variablelist>
- <para>The following commands are understood:</para>
+ <para>Commands can be specified by exact match or unambiguous prefix match.
+ The following commands are understood:</para>
<variablelist>
<varlistentry>
diff --git a/man/loginctl.xml b/man/loginctl.xml
index 749db92..f2162ac 100644
--- a/man/loginctl.xml
+++ b/man/loginctl.xml
@@ -165,7 +165,8 @@
<xi:include href="standard-options.xml" xpointer="no-pager" />
</variablelist>
- <para>The following commands are understood:</para>
+ <para>Commands can be specified by exact match or unambiguous prefix match.
+ The following commands are understood:</para>
<variablelist>
<varlistentry>
diff --git a/man/systemctl.xml b/man/systemctl.xml
index d1991e0..91fcba3 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -557,7 +557,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
<refsect1>
<title>Commands</title>
- <para>The following commands are understood:</para>
+ <para>Commands can be specified by exact match or unambiguous prefix match.
+ The following commands are understood:</para>
<refsect2>
<title>Unit Commands</title>
diff --git a/man/telinit.xml b/man/telinit.xml
index 33ea118..20cb9ac 100644
--- a/man/telinit.xml
+++ b/man/telinit.xml
@@ -86,7 +86,8 @@
</varlistentry>
</variablelist>
- <para>The following commands are understood:</para>
+ <para>Commands can be specified by exact match or unambiguous prefix match.
+ The following commands are understood:</para>
<variablelist>
<varlistentry>
diff --git a/src/shared/verbs.c b/src/shared/verbs.c
index bc1ae43..da3f2b3 100644
--- a/src/shared/verbs.c
+++ b/src/shared/verbs.c
@@ -19,14 +19,28 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "strv.h"
#include "util.h"
#include "verbs.h"
+static int log_ambiguous(const char *opname, char **verbs) {
+ _cleanup_free_ char *possibilities = NULL;
+
+ possibilities = strv_join(verbs, ", ");
+ if (possibilities == NULL)
+ return -ENOMEM;
+
+ log_error("Operation %s is ambiguous; possibilities: %s\n", opname, possibilities);
+ return -EBADMSG;
+}
+
int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
const Verb *verb;
const char *name;
unsigned i;
int left;
+ bool exact_match = false;
+ _cleanup_strv_free_ char **prefix_matches = NULL;
assert(verbs);
assert(verbs[0].dispatch);
@@ -37,26 +51,42 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
left = argc - optind;
name = argv[optind];
- for (i = 0;; i++) {
- bool found;
+ for (i = 0; verbs[i].dispatch; i++) {
+ if (name) {
+ exact_match = streq(name, verbs[i].verb);
+ if (!exact_match)
+ if (startswith(verbs[i].verb, name)) {
+ int r;
+
+ /* Assume this is our candidate. We'll
+ * catch any ambiguity later. */
+ verb = &verbs[i];
+
+ r = strv_extend(&prefix_matches, (char *)verbs[i].verb);
+ if (r < 0)
+ return r;
+ }
+ } else
+ exact_match = !!(verbs[i].flags & VERB_DEFAULT);
+
+ if (exact_match) {
+ verb = &verbs[i];
+ break;
+ }
+ }
- /* At the end of the list? */
- if (!verbs[i].dispatch) {
+ if (!exact_match) {
+ switch (strv_length(prefix_matches)) {
+ case 0:
if (name)
log_error("Unknown operation %s.", name);
else
log_error("Requires operation parameter.");
return -EINVAL;
- }
-
- if (name)
- found = streq(name, verbs[i].verb);
- else
- found = !!(verbs[i].flags & VERB_DEFAULT);
-
- if (found) {
- verb = &verbs[i];
+ case 1:
break;
+ default:
+ return log_ambiguous(name, prefix_matches);
}
}
@@ -73,7 +103,7 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
if (verb->max_args != VERB_ANY &&
(unsigned) left > verb->max_args) {
- log_error("Too many argument.s");
+ log_error("Too many arguments.");
return -EINVAL;
}
diff --git a/src/test/test-verbs.c b/src/test/test-verbs.c
index 0fcdd9e..afd9ba8 100644
--- a/src/test/test-verbs.c
+++ b/src/test/test-verbs.c
@@ -59,6 +59,15 @@ static void test_verbs(void) {
/* no verb, but a default is set */
test_dispatch_one(STRV_MAKE_EMPTY, verbs, 0);
+
+ /* prefix match */
+ test_dispatch_one(STRV_MAKE("lo", "foo"), verbs, 0);
+
+ /* exact match with prefix match */
+ test_dispatch_one(STRV_MAKE("list", "foo"), verbs, 0);
+
+ /* ambiguous prefix match */
+ test_dispatch_one(STRV_MAKE("l"), verbs, -EBADMSG);
}
static void test_verbs_no_default(void) {
--
2.2.0
More information about the systemd-devel
mailing list