]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
bash-completion: add improvements to unshare(1) completions
authorChristian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Fri, 27 Feb 2026 23:29:27 +0000 (18:29 -0500)
committerChristian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Wed, 18 Mar 2026 13:25:32 +0000 (09:25 -0400)
This change adds the ability to complete the command of the
executable that the unshare command is going to execute.

Additionally, it includes support for completions of long
options that have optional arguments and are thus specified
with a suffixed '=' sign, which delimits the option name
from the actual option argument.

Lastly, it also adds file/directory completions for options that
require file or directory paths as arguments.

Addresses: #4073
Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
bash-completion/unshare

index fdd40e5cbea14167c8f69cd9718a1c2de22ba59c..75fe07eda175f812478fb7174a32630f98a35160 100644 (file)
 _unshare_module()
 {
-       local cur prev OPTS
-       COMPREPLY=()
-       cur="${COMP_WORDS[COMP_CWORD]}"
-       prev="${COMP_WORDS[COMP_CWORD-1]}"
-       case $prev in
-               '--propagation')
-                       COMPREPLY=( $(compgen -W "slave shared private unchanged" -- $cur) )
-                       return 0
-                       ;;
-               '-s'|'--setgroups')
-                       COMPREPLY=( $(compgen -W "allow deny" -- $cur) )
-                       return 0
-                       ;;
-               '-h'|'--help'|'-V'|'--version')
-                       return 0
-                       ;;
-       esac
-       case $cur in
-               -*)
-                       OPTS="--mount
-                               --uts
-                               --ipc
-                               --net
-                               --pid
-                               --user
-                               --cgroup
-                               --time
-                               --fork
-                               --forward-signals
-                               --kill-child
-                               --keep-caps
-                               --mount-proc
-                               --map-current-user
-                               --map-root-user
-                               --owner
-                               --propagation
-                               --setgroups
-                               --help
-                               --version
-                               --root
-                               --wd
-                               --load-interp
-                               --map-auto
-                               --map-group
-                               --map-groups
-                               --map-user
-                               --map-users
-                               --map-subids
-                               --mount-binfmt
-                               --monotonic
-                               --boottime
-                               --setuid
-                               --setgid"
+       local cur prev words cword
+       local OPTS NOARGOPTS OPTARGOPTS REQARGOPTS REQARGOPTS_SHORT NOARGOPTS_SHORT
+       _init_completion -n = -- "$@" || return
+
+       OPTARGOPTS="--mount=|--uts=|--ipc=|--net=|--pid=|--user=|--cgroup=|--time=|--kill-child=|\
+       --mount-proc=|--mount-binfmt="
+
+       REQARGOPTS="--owner|--propagation|--setgroups|--root|--wd|--load-interp|--map-group|\
+       --map-groups|--map-user|--map-users|--monotonic|--boottime|--setuid|--setgid"
+
+       REQARGOPTS_SHORT="-l|-R|-w|-S|-G"
+
+       NOARGOPTS="--fork|--forward-signals|--map-root-user|--map-current-user|--map-auto|\
+       --map-subids|--keep-caps|--help|--version|${OPTARGOPTS//=/}"
+
+       NOARGOPTS_SHORT="-r|-f|-c|-h|-V|-m|-u|-i|-n|-p|-U|-C|-T"
+
+       OPTS="${NOARGOPTS//[|]/ } ${NOARGOPTS_SHORT//[|]/ } ${OPTARGOPTS//[|]/ }\
+               ${REQARGOPTS//[|]/ } ${REQARGOPTS_SHORT//[|]/ }"
+
+       # Check if we are still completing unshare(1) and set 
+       # the offset so that it points to the command that is
+       # going to be executed, in order to complete it with
+       # its own completion specification.
+       local offset=0 i
+       for ((i = 1; i < cword; i++)); do
+               if [[ "${words[i]}" =~ ^--$ ]]; then
+                       ((i++))
+               elif [[ "${words[i]}" =~ ^($NOARGOPTS|$NOARGOPTS_SHORT)$ ]]; then
+                       continue
+               elif [[ "${words[i]}" =~ ^($OPTARGOPTS).* ]]; then
+                       continue
+               elif [[ "${words[i]}" =~ ^($REQARGOPTS|$REQARGOPTS_SHORT)$ ]]; then
+                       [[ "${words[$((i+1))]}" != -* ]] && ((i++))
+                       continue
+               fi
+               offset=$i
+               break
+       done
+
+       if (( offset > 0 )); then
+               # Find completion specification for the wrapped command
+               _command_offset $offset
+       else
+               # $OPTARGOPTS deserve special treatment due
+               # to the '=' that delimits the actual argument
+               # that we wish to complete and the option name
+               if [[ "$cur" =~ ^($OPTARGOPTS).* ]]; then
+                       # Cut also backslash before '=' in case it ended up there
+                       # for some reason.
+                       prev=${cur%%?(\\)=*}
+                       cur=${cur#*=}
+
+                       compopt -o nospace
+                       case $prev in
+                               '--kill-child')
+                                       COMPREPLY=( $(compgen -A signal -- $cur) )
+                                       return 0
+                                       ;;
+                               *)
+                                       COMPREPLY=( $(compgen -f -d -- $cur) )
+                                       return 0
+                                       ;;
+                       esac
+               fi
+
+               case $prev in
+                       '--propagation')
+                               COMPREPLY=( $(compgen -W "slave shared private unchanged" -- $cur) )
+                               return 0
+                               ;;
+                       '--setgroups')
+                               COMPREPLY=( $(compgen -W "allow deny" -- $cur) )
+                               return 0
+                               ;;
+                       '-w'|'--wd'|'-l'|'--load-interp'|'-R'|'--root')
+                               # It's better than simply using 'compgen -f',
+                               # because it honours spaces in filenames.
+                               _filedir
+                               return 0
+                               ;;
+                       '-S'|'--setuid')
+                               local uids
+                               uids="$(awk -F: '{print $3}' /etc/passwd | sort --numeric-sort)"
+                               COMPREPLY=( $(compgen -W "$uids" -- $cur) )
+                               return 0
+                               ;;
+                       '-G'|'--setgid')
+                               local gids
+                               gids="$(awk -F: '{print $3}' /etc/group | sort --numeric-sort)"
+                               COMPREPLY=( $(compgen -W "$gids" -- $cur) )
+                               return 0
+                               ;;
+                       '-h'|'--help'|'-V'|'--version')
+                               return 0
+                               ;;
+               esac
+
+               if [[ "$cur" == -* ]]; then
                        COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
                        return 0
-                       ;;
-       esac
-       compopt -o bashdefault
-       COMPREPLY=( $(compgen -c -- $cur) )
+               else
+                       COMPREPLY=( $(compgen -c -- "$cur") )
+               fi
+       fi
        return 0
 }
-complete -F _unshare_module unshare
+complete -F _unshare_module -o bashdefault -o default unshare