[PATCH] bg_setenv/bg_printenv: bash and zsh completion

41 views
Skip to first unread message

Michael Adler

unread,
Nov 10, 2021, 8:24:47 AM11/10/21
to efibootg...@googlegroups.com, Michael Adler
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.

The completion files have been auto-generated using clap_generate [1]
via its YAML support [2]. The YAML files are also part of this commit to
support generating future completion files.

[1] https://docs.rs/clap_generate
[2] https://github.com/clap-rs/clap/tree/00a0c4ee148ae4106e3c405043b7f0bd52524ca8#using-yaml

Signed-off-by: Michael Adler <michae...@siemens.com>
---
Makefile.am | 6 ++
share/completion/bash/bg_printenv.bash | 62 ++++++++++++++
share/completion/bash/bg_setenv.bash | 110 +++++++++++++++++++++++++
share/completion/bg_printenv.yaml | 54 ++++++++++++
share/completion/bg_setenv.yaml | 82 ++++++++++++++++++
share/completion/zsh/_bg_printenv | 44 ++++++++++
share/completion/zsh/_bg_setenv | 57 +++++++++++++
7 files changed, 415 insertions(+)
create mode 100644 share/completion/bash/bg_printenv.bash
create mode 100644 share/completion/bash/bg_setenv.bash
create mode 100644 share/completion/bg_printenv.yaml
create mode 100644 share/completion/bg_setenv.yaml
create mode 100644 share/completion/zsh/_bg_printenv
create mode 100644 share/completion/zsh/_bg_setenv

diff --git a/Makefile.am b/Makefile.am
index 8081839..b32d19f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -218,6 +218,12 @@ efibootguard_DATA = $(efi_loadername)
CLEANFILES += $(efi_objects) $(efi_solib) $(efi_loadername)
EXTRA_DIST += $(efi_sources)

+bashcompletiondir = ${datarootdir}/efibootguard/completion/bash
+bashcompletion_DATA = share/completion/bash/bg_setenv.bash share/completion/bash/bg_printenv.bash
+
+zshcompletiondir = ${datarootdir}/efibootguard/completion/zsh
+zshcompletion_DATA = share/completion/zsh/_bg_setenv share/completion/zsh/_bg_printenv
+
$(top_builddir)/%.o: $(top_srcdir)/%.c
@$(MKDIR_P) $(shell dirname $@)/
$(AM_V_CC)$(GNUEFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@
diff --git a/share/completion/bash/bg_printenv.bash b/share/completion/bash/bg_printenv.bash
new file mode 100644
index 0000000..a2e8e06
--- /dev/null
+++ b/share/completion/bash/bg_printenv.bash
@@ -0,0 +1,62 @@
+_bg_printenv() {
+ local i cur prev opts cmds
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+ cmd=""
+ opts=""
+
+ for i in ${COMP_WORDS[@]}
+ do
+ case "${i}" in
+ bg_printenv)
+ cmd="bg_printenv"
+ ;;
+ *)
+ ;;
+ esac
+ done
+
+ case "${cmd}" in
+ bg_printenv)
+ opts="-? -c -f -o -p -r -V -v --help --current --filepath --output --part --raw --usage --version --verbose"
+ if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
+ COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
+ return 0
+ fi
+ case "${prev}" in
+ --filepath)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ -f)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ --output)
+ COMPREPLY=($(compgen -W "in_progress revision kernel kernelargs watchdog_timeout ustate user" -- "${cur}"))
+ return 0
+ ;;
+ -o)
+ COMPREPLY=($(compgen -W "in_progress revision kernel kernelargs watchdog_timeout ustate user" -- "${cur}"))
+ return 0
+ ;;
+ --part)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ -p)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ *)
+ COMPREPLY=()
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
+ return 0
+ ;;
+ esac
+}
+
+complete -F _bg_printenv -o bashdefault -o default bg_printenv
diff --git a/share/completion/bash/bg_setenv.bash b/share/completion/bash/bg_setenv.bash
new file mode 100644
index 0000000..63e221a
--- /dev/null
+++ b/share/completion/bash/bg_setenv.bash
@@ -0,0 +1,110 @@
+_bg_setenv() {
+ local i cur prev opts cmds
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+ cmd=""
+ opts=""
+
+ for i in ${COMP_WORDS[@]}
+ do
+ case "${i}" in
+ bg_setenv)
+ cmd="bg_setenv"
+ ;;
+ *)
+ ;;
+ esac
+ done
+
+ case "${cmd}" in
+ bg_setenv)
+ opts="-? -f -p -v -V -P -k -a -r -s -w -c -u -x -i --help --filepath --part --verbose --version --preserve --kernel --args --revision --ustate --watchdog --confirm --update --uservar --in_progress"
+ if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
+ COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
+ return 0
+ fi
+ case "${prev}" in
+ --filepath)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ -f)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ --part)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ -p)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ --kernel)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ -k)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ --args)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ -a)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ --revision)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ -r)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ --ustate)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ -s)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ --watchdog)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ -w)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ --uservar)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ -x)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ --in_progress)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ -i)
+ COMPREPLY=($(compgen -f "${cur}"))
+ return 0
+ ;;
+ *)
+ COMPREPLY=()
+ ;;
+ esac
+ COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
+ return 0
+ ;;
+ esac
+}
+
+complete -F _bg_setenv -o bashdefault -o default bg_setenv
diff --git a/share/completion/bg_printenv.yaml b/share/completion/bg_printenv.yaml
new file mode 100644
index 0000000..813f321
--- /dev/null
+++ b/share/completion/bg_printenv.yaml
@@ -0,0 +1,54 @@
+# input for https://crates.io/crates/clap_generate
+name: bg_printenv
+about: Environment tool for the EFI Boot Guard
+args:
+ - help:
+ short: "?"
+ long: help
+ about: Show help
+ - current:
+ short: c
+ long: current
+ about: Only print values from the current environment
+ - filepath:
+ short: f
+ long: filepath
+ takes_value: true
+ value_name: ENVFILE
+ about: Environment to use
+ - output:
+ short: o
+ long: output
+ takes_value: true
+ multiple_values: true
+ use_delimiter: true
+ possible_values:
+ - in_progress
+ - revision
+ - kernel
+ - kernelargs
+ - watchdog_timeout
+ - ustate
+ - user
+ about: Comma-separated list of fields which are printed
+ - part:
+ short: p
+ long: part
+ takes_value: true
+ value_name: ENV_PART
+ about: Set environment partition to update
+ - raw:
+ short: r
+ long: raw
+ about: Raw output mode
+ - usage:
+ long: usage
+ about: Give a short usage message
+ - version:
+ short: V
+ long: version
+ about: Print version
+ - verbose:
+ short: "v"
+ long: verbose
+ about: Be verbose
diff --git a/share/completion/bg_setenv.yaml b/share/completion/bg_setenv.yaml
new file mode 100644
index 0000000..b3a8ff1
--- /dev/null
+++ b/share/completion/bg_setenv.yaml
@@ -0,0 +1,82 @@
+# input for https://crates.io/crates/clap_generate
+name: bg_setenv
+about: Environment tool for the EFI Boot Guard
+args:
+ - help:
+ short: "?"
+ long: help
+ about: Show help
+ - filepath:
+ short: "f"
+ long: "filepath"
+ about: "Environment to use. Expects a file name, usually called BGENV.DAT."
+ takes_value: true
+ value_name: "ENVFILE"
+ - part:
+ short: "p"
+ long: "part"
+ about: "Set environment partition to update. If no partition is specified, the one with the smallest revision value above zero is updated."
+ takes_value: true
+ value_name: "ENV_PART"
+ - verbose:
+ short: "v"
+ long: "verbose"
+ about: "Be verbose"
+ - version:
+ short: "V"
+ long: "version"
+ about: "Print version"
+ - preserve:
+ short: "P"
+ long: "preserve"
+ about: "Preserve existing entries"
+ - kernel:
+ short: "k"
+ long: "kernel"
+ about: "Set kernel to load"
+ takes_value: true
+ value_name: "KERNEL"
+ - args:
+ short: "a"
+ long: "args"
+ about: "Set kernel arguments"
+ takes_value: true
+ value_name: "KERNEL_ARGS"
+ - revision:
+ short: "r"
+ long: "revision"
+ about: "Set revision value"
+ takes_value: true
+ value_name: "REVISION"
+ - ustate:
+ short: "s"
+ long: "ustate"
+ about: "Set update status for environment"
+ takes_value: true
+ value_name: "USTATE"
+ - watchdog:
+ short: "w"
+ long: "watchdog"
+ about: "Watchdog timeout in seconds"
+ takes_value: true
+ value_name: "WATCHDOG_TIMEOUT"
+ - confirm:
+ short: "c"
+ long: "confirm"
+ about: "Confirm working environment"
+ - update:
+ short: "u"
+ long: "update"
+ about: "Automatically update oldest revision"
+ - uservar:
+ short: "x"
+ long: "uservar"
+ about: "Set user-defined string variable. For setting multiple variables, use this option multiple times."
+ takes_value: true
+ value_name: "KEY=VAL"
+ - in_progress:
+ short: "i"
+ long: "in_progress"
+ about: "Set in_progress variable to simulate a running update process."
+ takes_value: true
+ value_name: "IN_PROGRESS"
diff --git a/share/completion/zsh/_bg_printenv b/share/completion/zsh/_bg_printenv
new file mode 100644
index 0000000..76fcca2
--- /dev/null
+++ b/share/completion/zsh/_bg_printenv
@@ -0,0 +1,44 @@
+#compdef bg_printenv
+
+autoload -U is-at-least
+
+_bg_printenv() {
+ typeset -A opt_args
+ typeset -a _arguments_options
+ local ret=1
+
+ if is-at-least 5.2; then
+ _arguments_options=(-s -S -C)
+ else
+ _arguments_options=(-s -C)
+ fi
+
+ local context curcontext="$curcontext" state line
+ _arguments "${_arguments_options[@]}" \
+'-f+[Environment to use]:ENVFILE: ' \
+'--filepath=[Environment to use]:ENVFILE: ' \
+'-o+[Comma-separated list of fields which are printed]: :(in_progress revision kernel kernelargs watchdog_timeout ustate user)' \
+'--output=[Comma-separated list of fields which are printed]: :(in_progress revision kernel kernelargs watchdog_timeout ustate user)' \
+'-p+[Set environment partition to update]:ENV_PART: ' \
+'--part=[Set environment partition to update]:ENV_PART: ' \
+'-?[Show help]' \
+'--help[Show help]' \
+'-c[Only print values from the current environment]' \
+'--current[Only print values from the current environment]' \
+'-r[Raw output mode]' \
+'--raw[Raw output mode]' \
+'--usage[Give a short usage message]' \
+'-V[Print version]' \
+'--version[Print version]' \
+'-v[Be verbose]' \
+'--verbose[Be verbose]' \
+&& ret=0
+}
+
+(( $+functions[_bg_printenv_commands] )) ||
+_bg_printenv_commands() {
+ local commands; commands=()
+ _describe -t commands 'bg_printenv commands' commands "$@"
+}
+
+_bg_printenv "$@"
\ No newline at end of file
diff --git a/share/completion/zsh/_bg_setenv b/share/completion/zsh/_bg_setenv
new file mode 100644
index 0000000..a3bc236
--- /dev/null
+++ b/share/completion/zsh/_bg_setenv
@@ -0,0 +1,57 @@
+#compdef bg_setenv
+
+autoload -U is-at-least
+
+_bg_setenv() {
+ typeset -A opt_args
+ typeset -a _arguments_options
+ local ret=1
+
+ if is-at-least 5.2; then
+ _arguments_options=(-s -S -C)
+ else
+ _arguments_options=(-s -C)
+ fi
+
+ local context curcontext="$curcontext" state line
+ _arguments "${_arguments_options[@]}" \
+'-f+[Environment to use. Expects a file name, usually called BGENV.DAT.]:ENVFILE: ' \
+'--filepath=[Environment to use. Expects a file name, usually called BGENV.DAT.]:ENVFILE: ' \
+'-p+[Set environment partition to update. If no partition is specified, the one with the smallest revision value above zero is updated.]:ENV_PART: ' \
+'--part=[Set environment partition to update. If no partition is specified, the one with the smallest revision value above zero is updated.]:ENV_PART: ' \
+'-k+[Set kernel to load]:KERNEL: ' \
+'--kernel=[Set kernel to load]:KERNEL: ' \
+'-a+[Set kernel arguments]:KERNEL_ARGS: ' \
+'--args=[Set kernel arguments]:KERNEL_ARGS: ' \
+'-r+[Set revision value]:REVISION: ' \
+'--revision=[Set revision value]:REVISION: ' \
+'-s+[Set update status for environment]:USTATE: ' \
+'--ustate=[Set update status for environment]:USTATE: ' \
+'-w+[Watchdog timeout in seconds]:WATCHDOG_TIMEOUT: ' \
+'--watchdog=[Watchdog timeout in seconds]:WATCHDOG_TIMEOUT: ' \
+'-x+[Set user-defined string variable. For setting multiple variables, use this option multiple times.]:KEY=VAL: ' \
+'--uservar=[Set user-defined string variable. For setting multiple variables, use this option multiple times.]:KEY=VAL: ' \
+'-i+[Set in_progress variable to simulate a running update process.]:IN_PROGRESS: ' \
+'--in_progress=[Set in_progress variable to simulate a running update process.]:IN_PROGRESS: ' \
+'-?[Show help]' \
+'--help[Show help]' \
+'-v[Be verbose]' \
+'--verbose[Be verbose]' \
+'-V[Print version]' \
+'--version[Print version]' \
+'-P[Preserve existing entries]' \
+'--preserve[Preserve existing entries]' \
+'-c[Confirm working environment]' \
+'--confirm[Confirm working environment]' \
+'-u[Automatically update oldest revision]' \
+'--update[Automatically update oldest revision]' \
+&& ret=0
+}
+
+(( $+functions[_bg_setenv_commands] )) ||
+_bg_setenv_commands() {
+ local commands; commands=()
+ _describe -t commands 'bg_setenv commands' commands "$@"
+}
+
+_bg_setenv "$@"
\ No newline at end of file
--
2.33.1

Jan Kiszka

unread,
Nov 10, 2021, 8:54:39 AM11/10/21
to Michael Adler, efibootg...@googlegroups.com
On 10.11.21 14:24, Michael Adler wrote:
> 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.
>

Great stuff!

> The completion files have been auto-generated using clap_generate [1]
> via its YAML support [2]. The YAML files are also part of this commit to
> support generating future completion files.
>

Hmm, that is not that nice. We now have one file too much. Either the
generated one if people will recall this commit message and continue to
use clap_generate, or the yaml file because they just edit the generated
ones and let the yaml files rot.

How hard is it to use clap_generate for our build? Or are there any
alternatives that are packaged? Anything from python?

Jan

--
Siemens AG, T RDA IOT
Corporate Competence Center Embedded Linux

Michael Adler

unread,
Nov 10, 2021, 10:38:26 AM11/10/21
to Jan Kiszka, efibootg...@googlegroups.com
> How hard is it to use clap_generate for our build?

clap_generate is just a library, so it cannot be used directly (which is a shame!), which is why I have written a tiny
wrapper in Rust (30 LoC). The wrapper takes precisely one argument (the yaml file) and generates the bash and zsh
completions. So for clap_generate, I see the following options:

1) Maybe the wrapper could be brought upstream since it would be potentially useful to *any* non-Rust project which wants to
generate shell completions.

2) Alternatively, the wrapper could be a project of its own, with the benefit of providing statically linked binaries in some
registry, e.g. GitHub.

3) The wrapper could be stored in this repository, e.g. in a `contrib` directory.

For the build process this means we would either have to compile the Rust source code (meaning we would pull in Rust as
a new *build* dependency) or we would download the (statically linked) binary from GitHub (or some other place).

> Or are there any alternatives that are packaged? Anything from python?

I'm only aware of argcomplete [1] which is packaged for both Arch and Debian. However, I would argue that it is
technically *inferior*: it does not generate proper zsh completions (instead relying on bashcompinit) but even worse, it
seems that argcomplete is a *runtime* dependency.

> Hmm, that is not that nice. We now have one file too much.

If we go with the clap_generate approach, we could keep the generated files (it makes packaging much easier) but use CI
to ensure that the yaml and generated files are in sync.

Last but not least, we could drop the yaml files and from this point forward maintain the completion files directly :)

[1] https://github.com/kislyuk/argcomplete

--
Michael Adler

Siemens AG
T RDA IOT SES-DE
Otto-Hahn-Ring 6
81739 München, Deutschland

Siemens Aktiengesellschaft: Vorsitzender des Aufsichtsrats: Jim Hagemann Snabe; Vorstand: Roland Busch, Vorsitzender; Klaus Helmrich, Cedrik Neike, Matthias Rebellius, Ralf P. Thomas, Judith Wiese; Sitz der Gesellschaft: Berlin und München, Deutschland; Registergericht: Berlin-Charlottenburg, HRB 12300, München, HRB 6684; WEEE-Reg.-Nr. DE 23691322

Jan Kiszka

unread,
Nov 10, 2021, 12:30:40 PM11/10/21
to Michael Adler, efibootg...@googlegroups.com
On 10.11.21 16:36, Michael Adler wrote:
>> How hard is it to use clap_generate for our build?
>
> clap_generate is just a library, so it cannot be used directly (which is a shame!), which is why I have written a tiny
> wrapper in Rust (30 LoC). The wrapper takes precisely one argument (the yaml file) and generates the bash and zsh
> completions. So for clap_generate, I see the following options:
>
> 1) Maybe the wrapper could be brought upstream since it would be potentially useful to *any* non-Rust project which wants to
> generate shell completions.
>
> 2) Alternatively, the wrapper could be a project of its own, with the benefit of providing statically linked binaries in some
> registry, e.g. GitHub.
>
> 3) The wrapper could be stored in this repository, e.g. in a `contrib` directory.
>
> For the build process this means we would either have to compile the Rust source code (meaning we would pull in Rust as
> a new *build* dependency) or we would download the (statically linked) binary from GitHub (or some other place).
>
>> Or are there any alternatives that are packaged? Anything from python?
>
> I'm only aware of argcomplete [1] which is packaged for both Arch and Debian. However, I would argue that it is
> technically *inferior*: it does not generate proper zsh completions (instead relying on bashcompinit) but even worse, it
> seems that argcomplete is a *runtime* dependency.
>
>> Hmm, that is not that nice. We now have one file too much.
>
> If we go with the clap_generate approach, we could keep the generated files (it makes packaging much easier) but use CI
> to ensure that the yaml and generated files are in sync.

Then let's start with sharing the bits and pieces needed to generate in
form of installation instructions and a generator script. That could
then be translated into CI instructions as well.

>
> Last but not least, we could drop the yaml files and from this point forward maintain the completion files directly :)
>
> [1] https://github.com/kislyuk/argcomplete
>

In any case: Your new files are missing copyright headers.

Michael Adler

unread,
Nov 11, 2021, 6:02:12 AM11/11/21
to efibootg...@googlegroups.com, Michael Adler
This is a major rewrite of patch v1: it uses shtab [1] instead of clap_generate to generate the completions.
The reason is that shtab is easier to set up (the only dependency is python3) and does an equivalent job.

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

Michael Adler (1):
bg_setenv/bg_printenv: bash and zsh completion

.github/workflows/main.yaml | 9 ++
.gitignore | 4 +
Makefile.am | 6 +
share/completion/Makefile | 38 +++++
share/completion/common.py | 23 +++
.../generated/bash/bg_printenv.bash | 139 +++++++++++++++++
.../completion/generated/bash/bg_setenv.bash | 142 ++++++++++++++++++
share/completion/generated/zsh/_bg_printenv | 37 +++++
share/completion/generated/zsh/_bg_setenv | 43 ++++++
9 files changed, 441 insertions(+)
create mode 100644 share/completion/Makefile
create mode 100644 share/completion/common.py
create mode 100644 share/completion/generated/bash/bg_printenv.bash
create mode 100644 share/completion/generated/bash/bg_setenv.bash
create mode 100644 share/completion/generated/zsh/_bg_printenv
create mode 100644 share/completion/generated/zsh/_bg_setenv

--
2.33.1

Michael Adler

unread,
Nov 11, 2021, 6:02:13 AM11/11/21
to efibootg...@googlegroups.com, Michael Adler
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].
To ease the integration into the main Makefile (automake), the generated
completions are stored in git as well. This has the advantage that 'make
install' will install the completion files. A CI job ensures that the
generated files are in sync with the spec (i.e. python files).

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

Signed-off-by: Michael Adler <michae...@siemens.com>
---
.github/workflows/main.yaml | 9 ++
.gitignore | 4 +
Makefile.am | 6 +
share/completion/.gitignore | 2 +
share/completion/Makefile | 38 +++++
share/completion/bg_printenv/cli.py | 31 ++++
share/completion/bg_printenv/common.py | 1 +
share/completion/bg_setenv/cli.py | 47 ++++++
share/completion/bg_setenv/common.py | 1 +
share/completion/common.py | 23 +++
.../generated/bash/bg_printenv.bash | 139 +++++++++++++++++
.../completion/generated/bash/bg_setenv.bash | 142 ++++++++++++++++++
share/completion/generated/zsh/_bg_printenv | 37 +++++
share/completion/generated/zsh/_bg_setenv | 43 ++++++
14 files changed, 523 insertions(+)
create mode 100644 share/completion/.gitignore
create mode 100644 share/completion/Makefile
create mode 100644 share/completion/bg_printenv/cli.py
create mode 120000 share/completion/bg_printenv/common.py
create mode 100644 share/completion/bg_setenv/cli.py
create mode 120000 share/completion/bg_setenv/common.py
create mode 100644 share/completion/common.py
create mode 100644 share/completion/generated/bash/bg_printenv.bash
create mode 100644 share/completion/generated/bash/bg_setenv.bash
create mode 100644 share/completion/generated/zsh/_bg_printenv
create mode 100644 share/completion/generated/zsh/_bg_setenv

diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml
index 10b2a2d..78f6ce4 100644
--- a/.github/workflows/main.yaml
+++ b/.github/workflows/main.yaml
@@ -160,3 +160,12 @@ jobs:
# files are provided. Compare 'cppcheck --help'.
cppcheck -f -q --error-exitcode=2 $enable $suppress $ignore \
$cpp_conf $includes .
+
+ - name: Generate shell completions
+ if: ${{ matrix.target == 'amd64' }}
+ run: |
+ sudo apt-get install -y --no-install-recommends make python3 pip git
+ sudo pip install shtab
+ rm -rf share/completion/generated
+ make -C share/completion
+ git diff --exit-code share/completion
diff --git a/.gitignore b/.gitignore
index 1943fe0..f472f03 100644
--- a/.gitignore
+++ b/.gitignore
@@ -92,3 +92,7 @@ test_ebgenv_api
test_ebgenv_api_internal
test_probe_config_file
test_probe_config_partitions
+
+### Python ###
+__pycache__
+*.pyc
diff --git a/Makefile.am b/Makefile.am
index 8081839..9351488 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -218,6 +218,12 @@ efibootguard_DATA = $(efi_loadername)
CLEANFILES += $(efi_objects) $(efi_solib) $(efi_loadername)
EXTRA_DIST += $(efi_sources)

+bashcompletiondir = ${datarootdir}/efibootguard/completion/bash
+bashcompletion_DATA = share/completion/generated/bash/bg_setenv.bash share/completion/generated/bash/bg_printenv.bash
+
+zshcompletiondir = ${datarootdir}/efibootguard/completion/zsh
+zshcompletion_DATA = share/completion/generated/zsh/_bg_setenv share/completion/generated/zsh/_bg_printenv
+
$(top_builddir)/%.o: $(top_srcdir)/%.c
@$(MKDIR_P) $(shell dirname $@)/
$(AM_V_CC)$(GNUEFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@
diff --git a/share/completion/.gitignore b/share/completion/.gitignore
new file mode 100644
index 0000000..b731cd1
--- /dev/null
+++ b/share/completion/.gitignore
@@ -0,0 +1,2 @@
+!bg_printenv
+!bg_setenv
diff --git a/share/completion/Makefile b/share/completion/Makefile
new file mode 100644
index 0000000..21105bb
--- /dev/null
+++ b/share/completion/Makefile
@@ -0,0 +1,38 @@
+# Copyright (c) Siemens AG, 2021
+#
+# Authors:
+# Michael Adler <michae...@siemens.com>
+#
+# 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
+PYTHON ?= python3
+
+all: bash-completion zsh-completion
+
+bash-completion: generated/bash/bg_setenv.bash generated/bash/bg_printenv.bash
+
+zsh-completion: generated/zsh/_bg_setenv generated/zsh/_bg_printenv
+
+generated/bash/bg_setenv.bash: bg_setenv/cli.py
+ @echo "Generating $@"
+ @mkdir -p $(@D)
+ @$(PYTHON) -m shtab --shell=bash -u "bg_setenv.cli.bg_setenv" >$@
+
+generated/bash/bg_printenv.bash: bg_printenv/cli.py
+ @echo "Generating $@"
+ @mkdir -p $(@D)
+ @-$(PYTHON) -m shtab --shell=bash -u "bg_printenv.cli.bg_printenv" >$@
+
+generated/zsh/_bg_setenv: bg_setenv/cli.py
+ @echo "Generating $@"
+ @mkdir -p $(@D)
+ @-$(PYTHON) -m shtab --shell=zsh -u "bg_setenv.cli.bg_setenv" >$@
+
+generated/zsh/_bg_printenv: bg_printenv/cli.py
+ @echo "Generating $@"
+ @mkdir -p $(@D)
+ @-$(PYTHON) -m shtab --shell=zsh -u "bg_printenv.cli.bg_printenv" >$@
+
+.PHONY: all bash-completion zsh-completion
diff --git a/share/completion/bg_printenv/cli.py b/share/completion/bg_printenv/cli.py
new file mode 100644
index 0000000..570faf9
--- /dev/null
+++ b/share/completion/bg_printenv/cli.py
@@ -0,0 +1,31 @@
+#
+# Copyright (c) Siemens AG, 2021
+#
+# Authors:
+# Michael Adler <michae...@siemens.com>
+#
+# 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/share/completion/bg_printenv/common.py b/share/completion/bg_printenv/common.py
new file mode 120000
index 0000000..a11703e
--- /dev/null
+++ b/share/completion/bg_printenv/common.py
@@ -0,0 +1 @@
+../common.py
\ No newline at end of file
diff --git a/share/completion/bg_setenv/cli.py b/share/completion/bg_setenv/cli.py
new file mode 100644
index 0000000..9698882
--- /dev/null
+++ b/share/completion/bg_setenv/cli.py
@@ -0,0 +1,47 @@
+#
+# Copyright (c) Siemens AG, 2021
+#
+# Authors:
+# Michael Adler <michae...@siemens.com>
+#
+# 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/share/completion/bg_setenv/common.py b/share/completion/bg_setenv/common.py
new file mode 120000
index 0000000..a11703e
--- /dev/null
+++ b/share/completion/bg_setenv/common.py
@@ -0,0 +1 @@
+../common.py
\ No newline at end of file
diff --git a/share/completion/common.py b/share/completion/common.py
new file mode 100644
index 0000000..8134aef
--- /dev/null
+++ b/share/completion/common.py
@@ -0,0 +1,23 @@
+#
+# Copyright (c) Siemens AG, 2021
+#
+# Authors:
+# Michael Adler <michae...@siemens.com>
+#
+# 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/share/completion/generated/bash/bg_printenv.bash b/share/completion/generated/bash/bg_printenv.bash
new file mode 100644
index 0000000..3c6dd6c
--- /dev/null
+++ b/share/completion/generated/bash/bg_printenv.bash
@@ -0,0 +1,139 @@
+#!/usr/bin/env bash
+# AUTOMATCALLY GENERATED by `shtab`
+
+
+
+_shtab_bg_printenv_option_strings=('-f' '--filepath' '-p' '--part' '-v' '--verbose' '-V' '--version' '--help' '-c' '--current' '-o' '--output' '-r' '--raw' '--usage')
+
+_shtab_bg_printenv__f_COMPGEN=_shtab_compgen_files
+_shtab_bg_printenv___filepath_COMPGEN=_shtab_compgen_files
+
+_shtab_bg_printenv__o_choices='in_progress revision kernel kernelargs watchdog_timeout ustate user'
+_shtab_bg_printenv___output_choices='in_progress revision kernel kernelargs watchdog_timeout ustate user'
+
+_shtab_bg_printenv__v_nargs=0
+_shtab_bg_printenv___verbose_nargs=0
+_shtab_bg_printenv__V_nargs=0
+_shtab_bg_printenv___version_nargs=0
+_shtab_bg_printenv___help_nargs=0
+_shtab_bg_printenv__c_nargs=0
+_shtab_bg_printenv___current_nargs=0
+_shtab_bg_printenv__r_nargs=0
+_shtab_bg_printenv___raw_nargs=0
+_shtab_bg_printenv___usage_nargs=0
+
+
+# $1=COMP_WORDS[1]
+_shtab_compgen_files() {
+ compgen -f -- $1 # files
+}
+
+# $1=COMP_WORDS[1]
+_shtab_compgen_dirs() {
+ compgen -d -- $1 # recurse into subdirs
+}
+
+# $1=COMP_WORDS[1]
+_shtab_replace_nonword() {
+ echo "${1//[^[:word:]]/_}"
+}
+
+# set default values (called for the initial parser & any subparsers)
+_set_parser_defaults() {
+ local subparsers_var="${prefix}_subparsers[@]"
+ sub_parsers=${!subparsers_var}
+
+ local current_option_strings_var="${prefix}_option_strings[@]"
+ current_option_strings=${!current_option_strings_var}
+
+ completed_positional_actions=0
+
+ _set_new_action "pos_${completed_positional_actions}" true
+}
+
+# $1=action identifier
+# $2=positional action (bool)
+# set all identifiers for an action's parameters
+_set_new_action() {
+ current_action="${prefix}_$(_shtab_replace_nonword $1)"
+
+ local current_action_compgen_var=${current_action}_COMPGEN
+ current_action_compgen="${!current_action_compgen_var}"
+
+ local current_action_choices_var="${current_action}_choices"
+ current_action_choices="${!current_action_choices_var}"
+
+ local current_action_nargs_var="${current_action}_nargs"
+ if [ -n "${!current_action_nargs_var}" ]; then
+ current_action_nargs="${!current_action_nargs_var}"
+ else
+ current_action_nargs=1
+ fi
+
+ current_action_args_start_index=$(( $word_index + 1 ))
+
+ current_action_is_positional=$2
+}
+
+# Notes:
+# `COMPREPLY`: what will be rendered after completion is triggered
+# `completing_word`: currently typed word to generate completions for
+# `${!var}`: evaluates the content of `var` and expand its content as a variable
+# hello="world"
+# x="hello"
+# ${!x} -> ${hello} -> "world"
+_shtab_bg_printenv() {
+ local completing_word="${COMP_WORDS[COMP_CWORD]}"
+ COMPREPLY=()
+
+ prefix=_shtab_bg_printenv
+ word_index=0
+ _set_parser_defaults
+ word_index=1
+
+ # determine what arguments are appropriate for the current state
+ # of the arg parser
+ while [ $word_index -ne $COMP_CWORD ]; do
+ local this_word="${COMP_WORDS[$word_index]}"
+
+ if [[ -n $sub_parsers && " ${sub_parsers[@]} " =~ " ${this_word} " ]]; then
+ # valid subcommand: add it to the prefix & reset the current action
+ prefix="${prefix}_$(_shtab_replace_nonword $this_word)"
+ _set_parser_defaults
+ fi
+
+ if [[ " ${current_option_strings[@]} " =~ " ${this_word} " ]]; then
+ # a new action should be acquired (due to recognised option string or
+ # no more input expected from current action);
+ # the next positional action can fill in here
+ _set_new_action $this_word false
+ fi
+
+ if [[ "$current_action_nargs" != "*" ]] && \
+ [[ "$current_action_nargs" != "+" ]] && \
+ [[ "$current_action_nargs" != *"..." ]] && \
+ (( $word_index + 1 - $current_action_args_start_index >= \
+ $current_action_nargs )); then
+ $current_action_is_positional && let "completed_positional_actions += 1"
+ _set_new_action "pos_${completed_positional_actions}" true
+ fi
+
+ let "word_index+=1"
+ done
+
+ # Generate the completions
+
+ if [[ "${completing_word}" == -* ]]; then
+ # optional argument started: use option strings
+ COMPREPLY=( $(compgen -W "${current_option_strings[*]}" -- "${completing_word}") )
+ else
+ # use choices & compgen
+ COMPREPLY=( $(compgen -W "${current_action_choices}" -- "${completing_word}"; \
+ [ -n "${current_action_compgen}" ] \
+ && "${current_action_compgen}" "${completing_word}") )
+ fi
+
+ return 0
+}
+
+complete -o filenames -F _shtab_bg_printenv bg_printenv
diff --git a/share/completion/generated/bash/bg_setenv.bash b/share/completion/generated/bash/bg_setenv.bash
new file mode 100644
index 0000000..66a7332
--- /dev/null
+++ b/share/completion/generated/bash/bg_setenv.bash
@@ -0,0 +1,142 @@
+#!/usr/bin/env bash
+# AUTOMATCALLY GENERATED by `shtab`
+
+
+
+_shtab_bg_setenv_option_strings=('-f' '--filepath' '-p' '--part' '-v' '--verbose' '-V' '--version' '--help' '-P' '--preserve' '-k' '--kernel' '-a' '--args' '-r' '--revision' '-s' '--ustate' '-w' '--watchdog' '-c' '--confirm' '-u' '--update' '-x' '--uservar' '-i' '--in_progress')
+
+_shtab_bg_setenv__f_COMPGEN=_shtab_compgen_files
+_shtab_bg_setenv___filepath_COMPGEN=_shtab_compgen_files
+
+_shtab_bg_setenv__s_choices='OK INSTALLED TESTING FAILED UNKNOWN'
+_shtab_bg_setenv___ustate_choices='OK INSTALLED TESTING FAILED UNKNOWN'
+_shtab_bg_setenv__i_choices='0 1'
+_shtab_bg_setenv___in_progress_choices='0 1'
+
+_shtab_bg_setenv__v_nargs=0
+_shtab_bg_setenv___verbose_nargs=0
+_shtab_bg_setenv__V_nargs=0
+_shtab_bg_setenv___version_nargs=0
+_shtab_bg_setenv___help_nargs=0
+_shtab_bg_setenv__P_nargs=0
+_shtab_bg_setenv___preserve_nargs=0
+_shtab_bg_setenv__c_nargs=0
+_shtab_bg_setenv___confirm_nargs=0
+_shtab_bg_setenv__u_nargs=0
+_shtab_bg_setenv___update_nargs=0
+
+
+# $1=COMP_WORDS[1]
+_shtab_compgen_files() {
+ compgen -f -- $1 # files
+}
+
+# $1=COMP_WORDS[1]
+_shtab_compgen_dirs() {
+ compgen -d -- $1 # recurse into subdirs
+}
+
+# $1=COMP_WORDS[1]
+_shtab_replace_nonword() {
+ echo "${1//[^[:word:]]/_}"
+}
+
+# set default values (called for the initial parser & any subparsers)
+_set_parser_defaults() {
+ local subparsers_var="${prefix}_subparsers[@]"
+ sub_parsers=${!subparsers_var}
+
+ local current_option_strings_var="${prefix}_option_strings[@]"
+ current_option_strings=${!current_option_strings_var}
+
+ completed_positional_actions=0
+
+ _set_new_action "pos_${completed_positional_actions}" true
+}
+
+# $1=action identifier
+# $2=positional action (bool)
+# set all identifiers for an action's parameters
+_set_new_action() {
+ current_action="${prefix}_$(_shtab_replace_nonword $1)"
+
+ local current_action_compgen_var=${current_action}_COMPGEN
+ current_action_compgen="${!current_action_compgen_var}"
+
+ local current_action_choices_var="${current_action}_choices"
+ current_action_choices="${!current_action_choices_var}"
+
+ local current_action_nargs_var="${current_action}_nargs"
+ if [ -n "${!current_action_nargs_var}" ]; then
+ current_action_nargs="${!current_action_nargs_var}"
+ else
+ current_action_nargs=1
+ fi
+
+ current_action_args_start_index=$(( $word_index + 1 ))
+
+ current_action_is_positional=$2
+}
+
+# Notes:
+# `COMPREPLY`: what will be rendered after completion is triggered
+# `completing_word`: currently typed word to generate completions for
+# `${!var}`: evaluates the content of `var` and expand its content as a variable
+# hello="world"
+# x="hello"
+# ${!x} -> ${hello} -> "world"
+_shtab_bg_setenv() {
+ local completing_word="${COMP_WORDS[COMP_CWORD]}"
+ COMPREPLY=()
+
+ prefix=_shtab_bg_setenv
+ word_index=0
+ _set_parser_defaults
+ word_index=1
+
+ # determine what arguments are appropriate for the current state
+ # of the arg parser
+ while [ $word_index -ne $COMP_CWORD ]; do
+ local this_word="${COMP_WORDS[$word_index]}"
+
+ if [[ -n $sub_parsers && " ${sub_parsers[@]} " =~ " ${this_word} " ]]; then
+ # valid subcommand: add it to the prefix & reset the current action
+ prefix="${prefix}_$(_shtab_replace_nonword $this_word)"
+ _set_parser_defaults
+ fi
+
+ if [[ " ${current_option_strings[@]} " =~ " ${this_word} " ]]; then
+ # a new action should be acquired (due to recognised option string or
+ # no more input expected from current action);
+ # the next positional action can fill in here
+ _set_new_action $this_word false
+ fi
+
+ if [[ "$current_action_nargs" != "*" ]] && \
+ [[ "$current_action_nargs" != "+" ]] && \
+ [[ "$current_action_nargs" != *"..." ]] && \
+ (( $word_index + 1 - $current_action_args_start_index >= \
+ $current_action_nargs )); then
+ $current_action_is_positional && let "completed_positional_actions += 1"
+ _set_new_action "pos_${completed_positional_actions}" true
+ fi
+
+ let "word_index+=1"
+ done
+
+ # Generate the completions
+
+ if [[ "${completing_word}" == -* ]]; then
+ # optional argument started: use option strings
+ COMPREPLY=( $(compgen -W "${current_option_strings[*]}" -- "${completing_word}") )
+ else
+ # use choices & compgen
+ COMPREPLY=( $(compgen -W "${current_action_choices}" -- "${completing_word}"; \
+ [ -n "${current_action_compgen}" ] \
+ && "${current_action_compgen}" "${completing_word}") )
+ fi
+
+ return 0
+}
+
+complete -o filenames -F _shtab_bg_setenv bg_setenv
diff --git a/share/completion/generated/zsh/_bg_printenv b/share/completion/generated/zsh/_bg_printenv
new file mode 100644
index 0000000..9545483
--- /dev/null
+++ b/share/completion/generated/zsh/_bg_printenv
@@ -0,0 +1,37 @@
+#compdef bg_printenv
+
+# AUTOMATCALLY GENERATED by `shtab`
+
+_shtab_bg_printenv_options_=(
+ {-f,--filepath}"[Environment to use. Expects a file name, usually called BGENV.DAT.]:filepath:_files"
+ {-p,--part}"[Set environment partition to update]:part:"
+ {-v,--verbose}"[Be verbose]"
+ {-V,--version}"[Print version]"
+ "--help[Show help]"
+ {-c,--current}"[Only print values from the current environment]"
+ {-o,--output}"[Comma-separated list of fields which are printed]:output:(in_progress revision kernel kernelargs watchdog_timeout ustate user)"
+ {-r,--raw}"[Raw output mode]"
+ "--usage[Give a short usage message]"
+)
+
+_shtab_bg_printenv_commands_() {
+ local _commands=(
+
+ )
+
+ _describe 'bg_printenv commands' _commands
+}
+
+
+typeset -A opt_args
+local context state line curcontext="$curcontext"
+
+_arguments \
+ $_shtab_bg_printenv_options_ \
+ \
+ ': :_shtab_bg_printenv_commands_' \
+ '*::args:->args'
+
+case $words[1] in
+
+esac
diff --git a/share/completion/generated/zsh/_bg_setenv b/share/completion/generated/zsh/_bg_setenv
new file mode 100644
index 0000000..1f3af92
--- /dev/null
+++ b/share/completion/generated/zsh/_bg_setenv
@@ -0,0 +1,43 @@
+#compdef bg_setenv
+
+# AUTOMATCALLY GENERATED by `shtab`
+
+_shtab_bg_setenv_options_=(
+ {-f,--filepath}"[Environment to use. Expects a file name, usually called BGENV.DAT.]:filepath:_files"
+ {-p,--part}"[Set environment partition to update]:part:"
+ {-v,--verbose}"[Be verbose]"
+ {-V,--version}"[Print version]"
+ "--help[Show help]"
+ {-P,--preserve}"[Preserve existing entries]"
+ {-k,--kernel}"[Set kernel to load]:kernel:"
+ {-a,--args}"[Set kernel arguments]:args:"
+ {-r,--revision}"[Set revision value]:revision:"
+ {-s,--ustate}"[Set update status for environment]:ustate:(OK INSTALLED TESTING FAILED UNKNOWN)"
+ {-w,--watchdog}"[Watchdog timeout in seconds]:watchdog:"
+ {-c,--confirm}"[Confirm working environment]"
+ {-u,--update}"[Automatically update oldest revision]"
+ {-x,--uservar}"[Set user-defined string variable. For setting multiple variables, use this option multiple times.]:uservar:"
+ {-i,--in_progress}"[Set in_progress variable to simulate a running update process.]:in_progress:(0 1)"
+)
+
+_shtab_bg_setenv_commands_() {
+ local _commands=(
+
+ )
+
+ _describe 'bg_setenv commands' _commands
+}
+
+
+typeset -A opt_args
+local context state line curcontext="$curcontext"
+
+_arguments \
+ $_shtab_bg_setenv_options_ \
+ \
+ ': :_shtab_bg_setenv_commands_' \
+ '*::args:->args'
+
+case $words[1] in
+
+esac
--
2.33.1

Jan Kiszka

unread,
Nov 12, 2021, 2:55:57 AM11/12/21
to Michael Adler, efibootg...@googlegroups.com
Why "share/"? Can't we just use completion/ as subfolder?
Given that Python is a standard package and we only need that single
shtab thing from pip, why not doing the generation during build in a
virtual env, using a pinned version of shtab that is fetched on demand,
maybe with the option to run in the host env if the user prefers to
install shtab manually?

> + rm -rf share/completion/generated

There should be a clean target for this in [share/]completion/Makefile
clean is missing, see above.
So we are replicating the command line options from the C tools in
Python stubs - ok. But please leave a comment in the affected C files
where the Python files should be updated as well when changing options.

Michael Adler

unread,
Nov 12, 2021, 5:34:03 AM11/12/21
to efibootg...@googlegroups.com, Michael Adler
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 <michae...@siemens.com>
---
.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
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
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 <michae...@siemens.com>
+#
+# 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

Michael Adler

unread,
Nov 12, 2021, 5:34:04 AM11/12/21
to Jan Kiszka, efibootg...@googlegroups.com
> Given that Python is a standard package and we only need that single
> shtab thing from pip, why not doing the generation during build in a
> virtual env, using a pinned version of shtab that is fetched on demand,
> maybe with the option to run in the host env if the user prefers to
> install shtab manually?

This got me thinking and I have now a version which generates the completion files at build time - not just in CI, but
*everywhere*, thus we don't have to put the generated files into version control.

> So we are replicating the command line options from the C tools in
> Python stubs - ok. But please leave a comment in the affected C files
> where the Python files should be updated as well when changing options.

Yes, initially I used a hack to generate the Python code from the C code and then fine-tuned the output (in particular
I have added 'choices' and shorter doc strings). This could be further automated if we use a custom `struct` to store the CLI
and then convert it to the structs required by argp). The process to obtain the completion files would then be:
"compile c helper" -> "run c helper" -> "run shtab". I guess this could also be integrated in the Makefile.am of patch
v3. I do wonder if it's worth the effort though, considering the seldom changes to the CLI.

> Why "share/"? Can't we just use completion/ as subfolder?

Yes sure, moved in v3.

> There should be a clean target for this in [share/]completion/Makefile

The Makefile is gone in v3.

Michael Adler

unread,
Nov 12, 2021, 5:53:46 AM11/12/21
to efibootg...@googlegroups.com
Note: I will add '$(BASH_COMPLETION_FILES) $(ZSH_COMPLETION_FILES)' to CLEANFILES (automake) in v4.

Jan Kiszka

unread,
Nov 12, 2021, 6:24:29 AM11/12/21
to Michael Adler, efibootg...@googlegroups.com
This will force recipes to do git-submodule checkouts. While there is an
implementation of that in bitbake, it says:

"The Git Submodules fetcher is not a complete fetcher implementation.
The fetcher has known issues where it does not use the normal source
mirroring infrastructure properly."

That was ok for fff as just being a test dependency, but it would become
a must-have when needing this for regular build & install.

I'm not totally against going this way, at least for now, but maybe we
should already allow for using a locally installed shtab version. Or
will that work already?
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Definitely better this way.

Michael Adler

unread,
Nov 12, 2021, 6:46:48 AM11/12/21
to Jan Kiszka, efibootg...@googlegroups.com
> but maybe we should already allow for using a locally installed shtab version. Or will that work already?

Yes, this does work already (I have also tested it) :)

Michael Adler

unread,
Nov 12, 2021, 6:48:29 AM11/12/21
to efibootg...@googlegroups.com, Michael Adler
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 <michae...@siemens.com>
---
.gitignore | 8 ++++++
.gitmodules | 3 ++
Makefile.am | 41 +++++++++++++++++++++++++--
completion/.gitignore | 2 ++
completion/bg_printenv/cli.py | 30 ++++++++++++++++++++
completion/bg_printenv/common.py | 1 +
completion/bg_setenv/cli.py | 48 ++++++++++++++++++++++++++++++++
completion/bg_setenv/common.py | 1 +
completion/common.py | 24 ++++++++++++++++
completion/shtab | 1 +
docs/COMPILE.md | 4 +--
tools/bg_envtools.h | 1 +
tools/bg_printenv.c | 1 +
tools/bg_setenv.c | 1 +
14 files changed, 162 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..0368901 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,9 +255,44 @@ bg_printenvdir = $(top_srcdir)
-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..c8f2854
--- /dev/null
+++ b/completion/bg_printenv/cli.py
@@ -0,0 +1,30 @@
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..529e2e2
--- /dev/null
+++ b/completion/bg_setenv/cli.py
@@ -0,0 +1,48 @@
+
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..a53d653
--- /dev/null
+++ b/completion/common.py
@@ -0,0 +1,24 @@
+#
+# Copyright (c) Siemens AG, 2021
+#
+# Authors:
+# Michael Adler <michae...@siemens.com>
+#
+# 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/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
--
2.33.1

Jan Kiszka

unread,
Nov 12, 2021, 6:48:30 AM11/12/21
to Michael Adler, efibootg...@googlegroups.com
On 12.11.21 12:46, Michael Adler wrote:
>> but maybe we should already allow for using a locally installed shtab version. Or will that work already?
>
> Yes, this does work already (I have also tested it) :)
>

Ok, then let's keep this submodule and leave it up to the packager how
to deal with it.

Jan

PS: Maybe create a packaging proposal for shtab in Debian...

Jan Kiszka

unread,
Nov 12, 2021, 6:56:04 AM11/12/21
to Michael Adler, efibootg...@googlegroups.com
These rules could be consolidated with a macro and, at that chance, also
slienced ("GEN ...") at that chance.
Spurious newline at EOF (2 more times below as well).
Jan

Michael Adler

unread,
Nov 12, 2021, 8:52:22 AM11/12/21
to efibootg...@googlegroups.com, Michael Adler
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 <michae...@siemens.com>
---
.gitignore | 8 ++++++
.gitmodules | 3 ++
Makefile.am | 39 ++++++++++++++++++++++++--
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, 164 insertions(+), 4 deletions(-)
index 8081839..41feb7f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -38,6 +38,10 @@ ARFLAGS = cr
EXTRA_DIST = autogen.sh README LICENSE
CLEANFILES =

+SHTAB ?= env \
+ PYTHONPATH=${top_srcdir}/completion/shtab:${top_srcdir}/completion \
+ @PYTHON@ -m shtab
+
define filechk
$(AM_V_at)set -e; \
echo ' CHK $@'; \
@@ -253,9 +257,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)
+ @-$(SHTAB) --shell=bash -u "bg_setenv.cli.bg_setenv" >$@
+
+$(top_builddir)/completion/zsh/_bg_setenv: ${top_srcdir}/completion/bg_setenv/cli.py
+ @$(MKDIR_P) $(@D)
+ @-$(SHTAB) --shell=zsh -u "bg_setenv.cli.bg_setenv" >$@
+
+$(top_builddir)/completion/bash/bg_printenv.bash: ${top_srcdir}/completion/bg_printenv/cli.py
+ @$(MKDIR_P) $(@D)
+ @-$(SHTAB) --shell=bash "bg_printenv.cli.bg_printenv" >$@
+
+$(top_builddir)/completion/zsh/_bg_printenv: ${top_srcdir}/completion/bg_printenv/cli.py
+ @$(MKDIR_P) $(@D)
+ @-$(SHTAB) --shell=zsh -u "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 @@
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 @@
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 <michae...@siemens.com>
+#
+# 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
--
2.33.1

Michael Adler

unread,
Nov 12, 2021, 8:52:23 AM11/12/21
to Jan Kiszka, efibootg...@googlegroups.com
> These rules could be consolidated with a macro and, at that chance, also
> slienced ("GEN ...") at that chance.

Hm, I'm not sure I completely understand what you mean. I have tried to make it a little more readable by using a SHTAB
variable and I have added auto-detection for Python to configure.ac. I'm not that experienced with autoconf & friends,
so in case you had something different in mind, could you point me to an example?

> Spurious newline at EOF (2 more times below as well).

Ah ok, then I misunderstood your review comment. I thought you wanted to have newlines. I have removed them in patch v5.

Jan Kiszka

unread,
Nov 12, 2021, 9:00:50 AM11/12/21
to Michael Adler, efibootg...@googlegroups.com
On 12.11.21 14:52, Michael Adler wrote:
>> These rules could be consolidated with a macro and, at that chance, also
>> slienced ("GEN ...") at that chance.
>
> Hm, I'm not sure I completely understand what you mean. I have tried to make it a little more readable by using a SHTAB
> variable and I have added auto-detection for Python to configure.ac. I'm not that experienced with autoconf & friends,
> so in case you had something different in mind, could you point me to an example?
>

See e.g.
https://github.com/siemens/efibootguard/commit/47d1194adad0c9ed98baacecdc4fb35a56f8ff41.
But that suppressed the output completely. Maybe something for the
mkdir. For shtab invocation, we rather want AM_V_GEN.

In addition, you can try to parameterize the two command further so that
you don't need to spell them out. Also to avoid bugs like forgetting
"-u" in the bg_printenv.bash command...

>> Spurious newline at EOF (2 more times below as well).
>
> Ah ok, then I misunderstood your review comment. I thought you wanted to have newlines. I have removed them in patch v5.
>

My comment was misleading, and it was wrong (that hunk was a symbolic
link, not real file).

Michael Adler

unread,
Nov 12, 2021, 9:29:24 AM11/12/21
to efibootg...@googlegroups.com, Michael Adler
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 <michae...@siemens.com>
---
.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(-)
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 >$@

Michael Adler

unread,
Nov 12, 2021, 9:31:13 AM11/12/21
to Jan Kiszka, efibootg...@googlegroups.com
> But that suppressed the output completely. Maybe something for the
> mkdir. For shtab invocation, we rather want AM_V_GEN.

[x] Done, see v6. I kept the @ for mkdir because other rules use it too.

> In addition, you can try to parameterize the two command further so that
> you don't need to spell them out. Also to avoid bugs like forgetting
> "-u" in the bg_printenv.bash command...

[x] Done, see v6.

Jan Kiszka

unread,
Nov 12, 2021, 11:21:16 AM11/12/21
to Michael Adler, efibootg...@googlegroups.com
Thanks, applied. I'll send another simplication of the makefile on top.
Reply all
Reply to author
Forward
0 new messages