From 21256a8ac1da47a2ddd18a5cc1528b365fe56985 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 20 Oct 2025 20:40:32 +0200 Subject: [PATCH] bin: add bash completion for copy command Add bash completion for the common datastores, like we already do in the CLI, and update the usage text accordingly. Also, make sure to install to /usr/bin, not /bin since we've now merged the hierarchies since a while back. Signed-off-by: Joachim Wiberg --- package/bin/bin.mk | 2 +- src/bin/Makefile.am | 4 ++ src/bin/copy.bash | 93 +++++++++++++++++++++++++++++++++++++++++++++ src/bin/copy.c | 24 ++++++++---- 4 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 src/bin/copy.bash diff --git a/package/bin/bin.mk b/package/bin/bin.mk index 5ac94ac9..15d276a9 100644 --- a/package/bin/bin.mk +++ b/package/bin/bin.mk @@ -11,7 +11,7 @@ BIN_LICENSE = BSD-3-Clause BIN_LICENSE_FILES = LICENSE BIN_REDISTRIBUTE = NO BIN_DEPENDENCIES = sysrepo libite -BIN_CONF_OPTS = --prefix= --disable-silent-rules +BIN_CONF_OPTS = --disable-silent-rules BIN_AUTORECONF = YES define BIN_CONF_ENV diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index 1345414c..f33dd3e9 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -3,6 +3,10 @@ ACLOCAL_AMFLAGS = -I m4 bin_PROGRAMS = copy erase files +# Bash completion +bashcompdir = $(datadir)/bash-completion/completions +dist_bashcomp_DATA = copy.bash + copy_SOURCES = copy.c util.c util.h copy_CPPFLAGS = -D_DEFAULT_SOURCE -D_GNU_SOURCE copy_CFLAGS = -W -Wall -Wextra diff --git a/src/bin/copy.bash b/src/bin/copy.bash new file mode 100644 index 00000000..f46b54be --- /dev/null +++ b/src/bin/copy.bash @@ -0,0 +1,93 @@ +# bash completion for copy command +# SPDX-License-Identifier: ISC + +_copy_completion() +{ + local cur prev opts + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + # Options for the copy command + opts="-h -n -q -s -t -u -v" + + local datastores_dst="running-config startup-config" + local datastores_src="running-config startup-config factory-config" + + case "${prev}" in + -t) + # Timeout expects a number, no completion + return 0 + ;; + -u) + # Username, could complete with getent passwd but keep it simple + return 0 + ;; + esac + + # If current word starts with -, complete options + if [[ ${cur} == -* ]]; then + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + + # Determine position (source or destination) + # Count non-option arguments before current position + local arg_count=0 + local i + for ((i=1; i < COMP_CWORD; i++)); do + case "${COMP_WORDS[i]}" in + -h|-n|-q|-s|-v) + # Flag without argument + ;; + -t|-u) + # Flag with argument, skip next word + ((i++)) + ;; + -*) + # Unknown flag, might have argument + ;; + *) + # Non-option argument + ((arg_count++)) + ;; + esac + done + + # Helper function to add non-hidden files/dirs, filtering out dotfiles unless explicitly requested + _add_files() { + local IFS=$'\n' + local files + + # If user is explicitly typing a dotfile path, include dotfiles + if [[ ${cur} == .* || ${cur} == */.* ]]; then + files=( $(compgen -f -- "${cur}") ) + else + # Only show non-hidden files and directories + files=( $(compgen -f -- "${cur}" | grep -v '/\.[^/]*$' | grep -v '^\.[^/]') ) + fi + + COMPREPLY+=( "${files[@]}" ) + } + + case ${arg_count} in + 0) + # First argument (source): complete with files and all datastores including factory-config + COMPREPLY=( $(compgen -W "${datastores_src}" -- ${cur}) ) + _add_files + ;; + 1) + # Second argument (destination): complete with files and limited datastores (no factory-config) + COMPREPLY=( $(compgen -W "${datastores_dst}" -- ${cur}) ) + _add_files + ;; + *) + # No more arguments expected + return 0 + ;; + esac + + return 0 +} + +complete -F _copy_completion copy diff --git a/src/bin/copy.c b/src/bin/copy.c index 6f667d8d..bc3d14cd 100644 --- a/src/bin/copy.c +++ b/src/bin/copy.c @@ -503,13 +503,23 @@ static int usage(int rc) printf("Usage: %s [OPTIONS] SRC DST\n" "\n" "Options:\n" - " -h This help text\n" - " -n Dry-run, validate configuration without applying\n" - " -q Quiet mode, suppress informational messages\n" - " -s Sanitize paths for CLI use (restrict path traversal)\n" - " -u USER Username for remote commands, like scp\n" - " -t SEEC Timeout for the operation, or default %d sec\n" - " -v Show version\n", prognm, timeout); + " -h This help text\n" + " -n Dry-run, validate configuration without applying\n" + " -q Quiet mode, suppress informational messages\n" + " -s Sanitize paths for CLI use (restrict path traversal)\n" + " -t SEC Timeout for the operation, or default %d sec\n" + " -u USER Username for remote commands, like scp\n" + " -v Show version\n" + "\n" + "Files:\n" + " SRC JSON configuration file, or a datastore\n" + " DST A file or datastore, except factory-config\n" + "\n" + "Datastores:\n" + " running-config The running datastore, current active config\n" + " startup-config The non-volatile config used at startup\n" + " factory-config The device's factory default configuration\n" + "\n", prognm, timeout); return rc; }