With the plethora of new command-line options, it is starting to get
difficult to remember them all. This commit introduces shell completions
for bash and zsh for the convenience of the user.

Instead of writing the completion files by hand (a tedious task), the
files are generated automatically from a Python spec using shtab [1].
The latter is stored as a git submodule, making it possible to generate
the completion files at build time without having to install shtab
(which is typically not available in package managers).

[1] https://github.com/iterative/shtab

Signed-off-by: Michael Adler <[email protected]>
---
 .gitmodules                      |  3 ++
 Makefile.am                      | 39 +++++++++++++++++++++++++-
 completion/.gitignore            |  2 ++
 completion/bg_printenv/cli.py    | 31 +++++++++++++++++++++
 completion/bg_printenv/common.py |  1 +
 completion/bg_setenv/cli.py      | 47 ++++++++++++++++++++++++++++++++
 completion/bg_setenv/common.py   |  1 +
 completion/common.py             | 23 ++++++++++++++++
 completion/shtab                 |  1 +
 tools/bg_envtools.h              |  1 +
 tools/bg_printenv.c              |  1 +
 tools/bg_setenv.c                |  1 +
 12 files changed, 150 insertions(+), 1 deletion(-)
 create mode 100644 completion/.gitignore
 create mode 100644 completion/bg_printenv/cli.py
 create mode 120000 completion/bg_printenv/common.py
 create mode 100644 completion/bg_setenv/cli.py
 create mode 120000 completion/bg_setenv/common.py
 create mode 100644 completion/common.py
 create mode 160000 completion/shtab

diff --git a/.gitmodules b/.gitmodules
index 80ad11c..143b8cb 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
 [submodule "tests/fff"]
        path = tests/fff
        url = https://github.com/meekrosoft/fff
+[submodule "completion/shtab"]
+       path = completion/shtab
+       url = https://github.com/iterative/shtab.git
diff --git a/Makefile.am b/Makefile.am
index 8081839..385d326 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -38,6 +38,8 @@ ARFLAGS = cr
 EXTRA_DIST = autogen.sh README LICENSE
 CLEANFILES =
 
+PYTHON ?= python3
+
 define filechk
        $(AM_V_at)set -e;                       \
        echo '  CHK      $@';                   \
@@ -253,7 +255,42 @@ bg_printenvdir = $(top_srcdir)
 bg_printenv: $(bg_setenv)
        $(AM_V_at)$(LN_S) -f bg_setenv bg_printenv
 
-all-local: bg_printenv
+BASH_COMPLETION_FILES := $(top_builddir)/completion/bash/bg_setenv.bash 
$(top_builddir)/completion/bash/bg_printenv.bash
+ZSH_COMPLETION_FILES := $(top_builddir)/completion/zsh/_bg_setenv 
$(top_builddir)/completion/zsh/_bg_printenv
+
+bashcompletiondir = ${datarootdir}/efibootguard/completion/bash
+bashcompletion_DATA = $(BASH_COMPLETION_FILES)
+
+zshcompletiondir = ${datarootdir}/efibootguard/completion/zsh
+zshcompletion_DATA = $(ZSH_COMPLETION_FILES)
+
+.PHONY: bash-completion
+bash-completion: $(BASH_COMPLETION_FILES)
+
+.PHONY: zsh-completion
+zsh-completion: $(ZSH_COMPLETION_FILES)
+
+$(top_builddir)/completion/bash/bg_setenv.bash: 
${top_srcdir}/completion/bg_setenv/cli.py
+       @mkdir -p $(@D)
+       -env PYTHONPATH=${top_srcdir}/completion/shtab:${top_srcdir}/completion 
\
+               $(PYTHON) -m shtab --shell=bash -u "bg_setenv.cli.bg_setenv" >$@
+
+$(top_builddir)/completion/bash/bg_printenv.bash: 
${top_srcdir}/completion/bg_printenv/cli.py
+       @mkdir -p $(@D)
+       -env PYTHONPATH=${top_srcdir}/completion/shtab:${top_srcdir}/completion 
\
+               $(PYTHON) -m shtab --shell=bash "bg_printenv.cli.bg_printenv" 
>$@
+
+$(top_builddir)/completion/zsh/_bg_setenv: 
${top_srcdir}/completion/bg_setenv/cli.py
+       @mkdir -p $(@D)
+       -env PYTHONPATH=${top_srcdir}/completion/shtab:${top_srcdir}/completion 
\
+               $(PYTHON) -m shtab --shell=zsh -u "bg_setenv.cli.bg_setenv" >$@
+
+$(top_builddir)/completion/zsh/_bg_printenv: 
${top_srcdir}/completion/bg_printenv/cli.py
+       @mkdir -p $(@D)
+       -env PYTHONPATH=${top_srcdir}/completion/shtab:${top_srcdir}/completion 
\
+               $(PYTHON) -m shtab --shell=zsh -u "bg_printenv.cli.bg_printenv" 
>$@
+
+all-local: bg_printenv bash-completion zsh-completion
 
 CLEANFILES += bg_printenv
 
diff --git a/completion/.gitignore b/completion/.gitignore
new file mode 100644
index 0000000..b731cd1
--- /dev/null
+++ b/completion/.gitignore
@@ -0,0 +1,2 @@
+!bg_printenv
+!bg_setenv
diff --git a/completion/bg_printenv/cli.py b/completion/bg_printenv/cli.py
new file mode 100644
index 0000000..570faf9
--- /dev/null
+++ b/completion/bg_printenv/cli.py
@@ -0,0 +1,31 @@
+#
+# Copyright (c) Siemens AG, 2021
+#
+# Authors:
+#  Michael Adler <[email protected]>
+#
+# This work is licensed under the terms of the GNU GPL, version 2.  See
+# the COPYING file in the top-level directory.
+#
+# SPDX-License-Identifier:     GPL-2.0
+
+import argparse
+
+from .common import add_common_opts
+
+
+def bg_printenv():
+    parser = argparse.ArgumentParser(prog="bg_printenv", add_help=False)
+    add_common_opts(parser)
+    parser.add_argument("-c", "--current", action="store_true", help="Only 
print values from the current environment")
+    parser.add_argument(
+        "-o",
+        "--output",
+        choices=["in_progress", "revision", "kernel", "kernelargs", 
"watchdog_timeout", "ustate", "user"],
+        help="Comma-separated list of fields which are printed",
+    )
+    parser.add_argument("-r", "--raw", action="store_true", help="Raw output 
mode")
+    parser.add_argument("--usage", action="store_true", help="Give a short 
usage message")
+    return parser
+
+
diff --git a/completion/bg_printenv/common.py b/completion/bg_printenv/common.py
new file mode 120000
index 0000000..a11703e
--- /dev/null
+++ b/completion/bg_printenv/common.py
@@ -0,0 +1 @@
+../common.py
\ No newline at end of file
diff --git a/completion/bg_setenv/cli.py b/completion/bg_setenv/cli.py
new file mode 100644
index 0000000..9698882
--- /dev/null
+++ b/completion/bg_setenv/cli.py
@@ -0,0 +1,47 @@
+#
+# Copyright (c) Siemens AG, 2021
+#
+# Authors:
+#  Michael Adler <[email protected]>
+#
+# This work is licensed under the terms of the GNU GPL, version 2.  See
+# the COPYING file in the top-level directory.
+#
+# SPDX-License-Identifier:     GPL-2.0
+
+import argparse
+
+from .common import add_common_opts
+
+
+def bg_setenv():
+    parser = argparse.ArgumentParser(prog="bg_setenv", add_help=False)
+    add_common_opts(parser)
+    parser.add_argument("-P", "--preserve", action="store_true", 
help="Preserve existing entries")
+    parser.add_argument("-k", "--kernel", metavar="KERNEL", help="Set kernel 
to load")
+    parser.add_argument("-a", "--args", metavar="KERNEL_ARGS", help="Set 
kernel arguments")
+    parser.add_argument("-r", "--revision", metavar="REVISION", help="Set 
revision value")
+    parser.add_argument(
+        "-s",
+        "--ustate",
+        choices=["OK", "INSTALLED", "TESTING", "FAILED", "UNKNOWN"],
+        metavar="USTATE",
+        help="Set update status for environment",
+    )
+    parser.add_argument("-w", "--watchdog", metavar="WATCHDOG_TIMEOUT", 
help="Watchdog timeout in seconds")
+    parser.add_argument("-c", "--confirm", action="store_true", help="Confirm 
working environment")
+    parser.add_argument("-u", "--update", action="store_true", 
help="Automatically update oldest revision")
+    parser.add_argument(
+        "-x",
+        "--uservar",
+        metavar="KEY=VAL",
+        help="Set user-defined string variable. For setting multiple 
variables, use this option multiple times.",
+    )
+    parser.add_argument(
+        "-i",
+        "--in_progress",
+        metavar="IN_PROGRESS",
+        choices=["0", "1"],
+        help="Set in_progress variable to simulate a running update process.",
+    )
+    return parser
diff --git a/completion/bg_setenv/common.py b/completion/bg_setenv/common.py
new file mode 120000
index 0000000..a11703e
--- /dev/null
+++ b/completion/bg_setenv/common.py
@@ -0,0 +1 @@
+../common.py
\ No newline at end of file
diff --git a/completion/common.py b/completion/common.py
new file mode 100644
index 0000000..8134aef
--- /dev/null
+++ b/completion/common.py
@@ -0,0 +1,23 @@
+#
+# Copyright (c) Siemens AG, 2021
+#
+# Authors:
+#  Michael Adler <[email protected]>
+#
+# This work is licensed under the terms of the GNU GPL, version 2.  See
+# the COPYING file in the top-level directory.
+#
+# SPDX-License-Identifier:     GPL-2.0
+
+import shtab
+
+
+def add_common_opts(parser):
+    parser.add_argument(
+        "-f", "--filepath", metavar="ENVFILE", help="Environment to use. 
Expects a file name, usually called BGENV.DAT."
+    ).complete = shtab.FILE
+    parser.add_argument("-p", "--part", metavar="ENV_PART", type=int, 
help="Set environment partition to update")
+    parser.add_argument("-v", "--verbose", action="store_true", help="Be 
verbose")
+    parser.add_argument("-V", "--version", action="store_true", help="Print 
version")
+    # there is a bug in shtab which currently prohibits "-?"
+    parser.add_argument("--help", action="store_true", help="Show help")
diff --git a/completion/shtab b/completion/shtab
new file mode 160000
index 0000000..5af77eb
--- /dev/null
+++ b/completion/shtab
@@ -0,0 +1 @@
+Subproject commit 5af77eb7ead3fa55346887ada65097b03c359c28
diff --git a/tools/bg_envtools.h b/tools/bg_envtools.h
index a397ca4..c7d42e5 100644
--- a/tools/bg_envtools.h
+++ b/tools/bg_envtools.h
@@ -23,6 +23,7 @@
                name, key, arg, flags, doc                                     \
        }
 
+/* if you change these, do not forget to update completion/common.py */
 #define BG_CLI_OPTIONS_COMMON                                                  
\
        OPT("filepath", 'f', "ENVFILE", 0,                                     \
            "Environment to use. Expects a file name, "                        \
diff --git a/tools/bg_printenv.c b/tools/bg_printenv.c
index 61152dc..40dd893 100644
--- a/tools/bg_printenv.c
+++ b/tools/bg_printenv.c
@@ -21,6 +21,7 @@
 static char tool_doc[] =
        "bg_printenv - Environment tool for the EFI Boot Guard";
 
+/* if you change these, do not forget to update completion/bg_printenv/cli.py 
*/
 static struct argp_option options_printenv[] = {
        BG_CLI_OPTIONS_COMMON,
        OPT("current", 'c', 0, 0,
diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index ab9673e..c789fcc 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -25,6 +25,7 @@
 static char tool_doc[] =
        "bg_setenv - Environment tool for the EFI Boot Guard";
 
+ /* if you change these, do not forget to update completion/bg_setenv/cli.py */
 static struct argp_option options_setenv[] = {
        BG_CLI_OPTIONS_COMMON,
        OPT("preserve", 'P', 0, 0, "Preserve existing entries"),
-- 
2.33.1

-- 
You received this message because you are subscribed to the Google Groups "EFI 
Boot Guard" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/efibootguard-dev/20211112102803.391261-1-michael.adler%40siemens.com.

Reply via email to