From 772cfefad72f4c2ec5a72bc73f75a2921fe6054b Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Sat, 9 Jun 2018 17:01:06 +0100 Subject: [PATCH 2/2] Add simple bash completion script. So far this can complete plugin names: $ nbdkit curl example4 libvirt perl ruby vddk example1 file memory python split xz example2 guestfs nbd python2 streaming example3 gzip null python3 tar Plugin parameters: $ nbdkit curl password= sslverify= timeout= url= user= Filter names: $ nbdkit --filter= cache cow delay offset partition General options (short or long or both): $ nbdkit - --dump-config --new-style --single --dump-plugin --newstyle --stdin -e --no-fork -t --exit-with-parent -o --threads --export --old-style --tls --export-name --oldstyle --tls-certificates --exportname -p --tls-verify-peer -f -P -u --filter --pid-file -U --foreground --pidfile --unix -g --port --user --group -r -v --help --read-only -V -i --readonly --verbose --ip-addr --run --version --ipaddr -s -n --selinux-label And --tls options: $ nbdkit --tls= off on require There is still quite a lot more which it doesn't do, such as completing filter parameters, and smarter completion for options. --- Makefile.am | 1 + README | 4 ++ TODO | 2 - bash/Makefile.am | 40 ++++++++++++++++++++ bash/README | 8 ++++ bash/nbdkit | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 15 ++++++++ src/main.c | 17 +++++++++ 8 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 bash/Makefile.am create mode 100644 bash/README create mode 100644 bash/nbdkit diff --git a/Makefile.am b/Makefile.am index 9c5b4c3..9d097f5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -44,6 +44,7 @@ EXTRA_DIST = \ noinst_SCRIPTS = nbdkit SUBDIRS = \ + bash \ docs \ include \ src diff --git a/README b/README index e58df8b..a589d77 100644 --- a/README +++ b/README @@ -91,6 +91,10 @@ For the OCaml plugin: - OCaml >= 4.02.2 which has support for shared libraries, see: http://caml.inria.fr/mantis/view.php?id=6693 +For bash tab completion: + + - bash-completion >= 1.99 + To run the test suite: - bash diff --git a/TODO b/TODO index fe0af0c..07f40cc 100644 --- a/TODO +++ b/TODO @@ -10,8 +10,6 @@ General ideas for improvements * Performance - measure and improve it. -* Bash tab completion. - * Exit on last connection (the default behaviour of qemu-nbd unless you use -t). diff --git a/bash/Makefile.am b/bash/Makefile.am new file mode 100644 index 0000000..2a2799a --- /dev/null +++ b/bash/Makefile.am @@ -0,0 +1,40 @@ +# nbdkit +# Copyright (C) 2018 Red Hat Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Red Hat nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +EXTRA_DIST = README + +if HAVE_BASH_COMPLETION + +bashcompdir = @bashcompdir@ +dist_bashcomp_DATA = nbdkit + +endif diff --git a/bash/README b/bash/README new file mode 100644 index 0000000..21a4f71 --- /dev/null +++ b/bash/README @@ -0,0 +1,8 @@ +This directory contains the scripts for tab-completing commands in +bash. Note these new-style demand-loaded scripts require +bash-completion >= 1.99. + +Tip: To test the bash completions without having to install them, +simply start a new shell and do: + + source ./bash/nbdkit; PATH=$PWD:$PATH diff --git a/bash/nbdkit b/bash/nbdkit new file mode 100644 index 0000000..3db531d --- /dev/null +++ b/bash/nbdkit @@ -0,0 +1,113 @@ +# nbdkit bash completion script -*- shell-script -*- +# Copyright (C) 2010-2018 Red Hat Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Red Hat nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +_nbdkit_list_plugins () +{ + local plugindir + plugindir="$( + nbdkit --dump-config | grep ^plugindir= | sed 's/^plugindir=//' + )" + ls -1 "$plugindir" | sed 's/^nbdkit-//' | sed 's/-plugin.*//' +} + +_nbdkit_list_filters () +{ + local filterdir + filterdir="$( + nbdkit --dump-config | grep ^filterdir= | sed 's/^filterdir=//' + )" + ls -1 "$filterdir" | sed 's/^nbdkit-//' | sed 's/-filter.*//' +} + +# This handler function is called when the user presses tab. +_nbdkit () +{ + local cur prev words cword split + local shortopts longopts plugin plugins filters args i + + _init_completion -s || return + + # Did we get the plugin name yet? + # This is only a heuristic because it can be confused by + # long opt parameters with an arguments. XXX + plugin= + for (( i=1; i < ${#words[@]}; ++i)) ; do + if [[ "${words[i]}" =~ ^[a-zA-Z0-9]+$ ]]; then + plugin="${words[i]}" + break + fi + done + + # Previous item on the current line is a completable flag or plugin name? + case "$prev" in + --filter) + filters="$(_nbdkit_list_filters)" + COMPREPLY=( $(compgen -W "$filters" "$cur") ) + return ;; + --tls) + COMPREPLY=( $(compgen -W "off on require" "$cur") ) + return ;; + # Could complete -u and -g options too. XXX + esac + + # Current item is an option we can expand? + case "$cur" in + --*) + longopts="$(nbdkit --long-options)" + COMPREPLY=( $(compgen -W "$longopts" -- "$cur") ) + return ;; + -*) + shortopts="$(nbdkit --short-options)" + longopts="$(nbdkit --long-options)" + COMPREPLY=( $(compgen -W "$shortopts $longopts" -- "$cur") ) + return ;; + *) + if [ "$plugin" = "" ]; then + # Complete plugin name. + plugins="$(_nbdkit_list_plugins)" + COMPREPLY=( $(compgen -W "$plugins" "$cur") ) + return + else + # Complete plugin args. + args="$( + nbdkit $plugin --help 2>/dev/null | + grep -E '^[a-z0-9]+=' | sed 's/=.*/=/' + )" + COMPREPLY=( $(compgen -W "$args" "$cur") ) + return + fi + ;; + esac +} + +# Install the handler function. +complete -o default -F _nbdkit nbdkit diff --git a/configure.ac b/configure.ac index 0ef9673..abf7048 100644 --- a/configure.ac +++ b/configure.ac @@ -164,6 +164,20 @@ AS_IF([test "$GNUTLS_LIBS" != ""],[ dnl Check for valgrind. AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind],[no]) +dnl Bash completion. +PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], [ + bash_completion=yes + AC_MSG_CHECKING([for bash-completions directory]) + PKG_CHECK_VAR(bashcompdir, [bash-completion], [completionsdir], , + bashcompdir="${sysconfdir}/bash_completion.d") + AC_MSG_RESULT([$bashcompdir]) + AC_SUBST([bashcompdir]) +],[ + bash_completion=no + AC_MSG_WARN([bash-completion not installed]) +]) +AM_CONDITIONAL([HAVE_BASH_COMPLETION],[test "x$bash_completion" = "xyes"]) + dnl Check for Perl POD. AC_CHECK_PROG([POD2MAN], [pod2man], [pod2man], [no]) AS_IF([test "x$POD2MAN" != "xno"],[ @@ -526,6 +540,7 @@ AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([nbdkit], [chmod +x,-w nbdkit]) AC_CONFIG_FILES([Makefile + bash/Makefile docs/Makefile include/Makefile plugins/Makefile diff --git a/src/main.c b/src/main.c index d2e5674..660d036 100644 --- a/src/main.c +++ b/src/main.c @@ -126,6 +126,7 @@ static const struct option long_options[] = { { "group", 1, NULL, 'g' }, { "ip-addr", 1, NULL, 'i' }, { "ipaddr", 1, NULL, 'i' }, + { "long-options", 0, NULL, 0 }, { "new-style", 0, NULL, 'n' }, { "newstyle", 0, NULL, 'n' }, { "old-style", 0, NULL, 'o' }, @@ -137,6 +138,7 @@ static const struct option long_options[] = { { "readonly", 0, NULL, 'r' }, { "run", 1, NULL, 0 }, { "selinux-label", 1, NULL, 0 }, + { "short-options", 0, NULL, 0 }, { "single", 0, NULL, 's' }, { "stdin", 0, NULL, 's' }, { "threads", 1, NULL, 't' }, @@ -263,6 +265,14 @@ main (int argc, char *argv[]) t->filename = optarg; filter_filenames = t; } + else if (strcmp (long_options[option_index].name, "long-options") == 0) { + for (i = 0; long_options[i].name != NULL; ++i) { + if (strcmp (long_options[i].name, "long-options") != 0 && + strcmp (long_options[i].name, "short-options") != 0) + printf ("--%s\n", long_options[i].name); + } + exit (EXIT_SUCCESS); + } else if (strcmp (long_options[option_index].name, "run") == 0) { if (socket_activation) { fprintf (stderr, "%s: cannot use socket activation with --run flag\n", @@ -276,6 +286,13 @@ main (int argc, char *argv[]) selinux_label = optarg; break; } + else if (strcmp (long_options[option_index].name, "short-options") == 0) { + for (i = 0; short_options[i]; ++i) { + if (short_options[i] != ':') + printf ("-%c\n", short_options[i]); + } + exit (EXIT_SUCCESS); + } else if (strcmp (long_options[option_index].name, "tls") == 0) { tls_set_on_cli = 1; if (strcmp (optarg, "off") == 0 || strcmp (optarg, "0") == 0) -- 2.16.2