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]>
---
 .gitignore                       |  8 ++++++
 .gitmodules                      |  3 ++
 Makefile.am                      | 41 ++++++++++++++++++++++++++--
 completion/.gitignore            |  2 ++
 completion/bg_printenv/cli.py    | 29 ++++++++++++++++++++
 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 +
 configure.ac                     |  7 +++++
 docs/COMPILE.md                  |  4 +--
 tools/bg_envtools.h              |  1 +
 tools/bg_printenv.c              |  1 +
 tools/bg_setenv.c                |  1 +
 15 files changed, 166 insertions(+), 4 deletions(-)
 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/.gitignore b/.gitignore
index 1943fe0..ae0d682 100644
--- a/.gitignore
+++ b/.gitignore
@@ -92,3 +92,11 @@ test_ebgenv_api
 test_ebgenv_api_internal
 test_probe_config_file
 test_probe_config_partitions
+
+### Python ###
+__pycache__
+*.pyc
+
+### Completion ###
+completion/bash
+completion/zsh
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..2d056f9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -38,6 +38,12 @@ ARFLAGS = cr
 EXTRA_DIST = autogen.sh README LICENSE
 CLEANFILES =
 
+SHTAB := env \
+       PYTHONPATH=${top_srcdir}/completion/shtab:${top_srcdir}/completion \
+       @PYTHON@ -m shtab
+SHTAB_BASH := $(SHTAB) --shell=bash -u
+SHTAB_ZSH := $(SHTAB) --shell=zsh -u
+
 define filechk
        $(AM_V_at)set -e;                       \
        echo '  CHK      $@';                   \
@@ -253,9 +259,40 @@ 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)
+       $(AM_V_GEN)$(SHTAB_BASH) bg_setenv.cli.bg_setenv >$@
+
+$(top_builddir)/completion/zsh/_bg_setenv: 
${top_srcdir}/completion/bg_setenv/cli.py
+       @$(MKDIR_P) $(@D)
+       $(AM_V_GEN)$(SHTAB_ZSH) bg_setenv.cli.bg_setenv >$@
+
+$(top_builddir)/completion/bash/bg_printenv.bash: 
${top_srcdir}/completion/bg_printenv/cli.py
+       @$(MKDIR_P) $(@D)
+       $(AM_V_GEN)$(SHTAB_BASH) bg_printenv.cli.bg_printenv >$@
+
+$(top_builddir)/completion/zsh/_bg_printenv: 
${top_srcdir}/completion/bg_printenv/cli.py
+       @$(MKDIR_P) $(@D)
+       $(AM_V_GEN)$(SHTAB_ZSH) bg_printenv.cli.bg_printenv >$@
+
+all-local: bg_printenv bash-completion zsh-completion
 
-CLEANFILES += bg_printenv
+CLEANFILES += bg_printenv $(BASH_COMPLETION_FILES) $(ZSH_COMPLETION_FILES)
 
 # Tests depend on libraries being built - start with "."
 SUBDIRS = . tools/tests
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..a50bc43
--- /dev/null
+++ b/completion/bg_printenv/cli.py
@@ -0,0 +1,29 @@
+#
+# 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/configure.ac b/configure.ac
index 6dcd386..4d7e87e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -181,6 +181,13 @@ fi
 PKG_PROG_PKG_CONFIG()
 PKG_CHECK_MODULES(LIBCHECK, check)
 PKG_CHECK_MODULES(LIBPCI, libpci)
+
+dnl Python
+AC_PATH_PROGS(PYTHON, [python3 python3.9 python3.8 python3.7 python3.6 
python2.7 python2], no)
+if test "x$PYTHON" = "xno"; then
+       AC_MSG_ERROR([You need to install python])
+fi
+
 # 
------------------------------------------------------------------------------
 AC_CONFIG_FILES([
        Makefile
diff --git a/docs/COMPILE.md b/docs/COMPILE.md
index d2fbaf2..101c9d6 100644
--- a/docs/COMPILE.md
+++ b/docs/COMPILE.md
@@ -9,7 +9,7 @@
 pacman -S gnu-efi-libs pciutils
 
 # build tools
-pacman -S gcc make automake autoconf libtool pkg-config
+pacman -S gcc make automake autoconf libtool pkg-config python
 
 # test dependencies
 pacman -S check bash-bats
@@ -24,7 +24,7 @@ Debian 8 or newer:
 apt-get install gnu-efi libpci-dev
 
 # build tools
-apt-get install make automake autoconf libtool pkg-config
+apt-get install make automake autoconf libtool pkg-config python3
 
 # test dependencies
 apt-get install check bats
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/20211112142911.673236-1-michael.adler%40siemens.com.

Reply via email to