Skip to content

feat(_comp_compgen_split): use _comp_compgen_split for -W '$(...)' #989

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 29, 2023
Merged
110 changes: 75 additions & 35 deletions bash_completion
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,46 @@ _comp_compgen_set()
eval -- "$_var${_append:++}=(\"\$@\")"
}

# Simply split the text and generate completions. This function should be used
# instead of `_comp_compgen -- -W "$(command)"`, which is vulnerable because
# option -W evaluates the shell expansions included in the option argument.
# Options:
# -F sep Specify the separators. The default is $' \t\n'
# -l The same as -F $'\n'
# -X arg The same as the compgen option -X.
# -S arg The same as the compgen option -S.
# -P arg The same as the compgen option -P.
# -o arg The same as the compgen option -o.
# @param $1 String to split
# @since 2.12
_comp_compgen_split()
{
local _ifs=$' \t\n'
local -a _compgen_options=()

local OPTIND=1 OPTARG="" OPTERR=0 _opt
while getopts ':lF:X:S:P:o:' _opt "$@"; do
case $_opt in
l) _ifs=$'\n' ;;
F) _ifs=$OPTARG ;;
[XSPo]) _compgen_options+=("-$_opt" "$OPTARG") ;;
*)
printf 'bash_completion: usage: %s [-l|-F sep] [--] str\n' "$FUNCNAME" >&2
return 2
;;
esac
done
shift "$((OPTIND - 1))"
if (($# != 1)); then
printf 'bash_completion: %s: unexpected number of arguments.\n' "$FUNCNAME" >&2
printf 'usage: %s [-l|-F sep] [--] str' "$FUNCNAME" >&2
return 2
fi

local _split_input=$1 IFS=$' \t\n'
_comp_compgen -F "$_ifs" -- ${_compgen_options[@]+"${_compgen_options[@]}"} -W '$_split_input'
}

# Check if the argument looks like a path.
# @param $1 thing to check
# @return True (0) if it does, False (> 0) otherwise
Expand Down Expand Up @@ -1143,7 +1183,7 @@ _comp_variable_assignments()
_terms
;;
LANG | LC_*)
_comp_compgen -- -W '$(locale -a 2>/dev/null)'
_comp_compgen_split -- "$(locale -a 2>/dev/null)"
;;
LANGUAGE)
_comp_delimited : -W '$(locale -a 2>/dev/null)'
Expand Down Expand Up @@ -1484,23 +1524,23 @@ _configured_interfaces()
# Debian system
_comp_expand_glob files '/etc/network/interfaces /etc/network/interfaces.d/*'
((${#files[@]})) || return 0
_comp_compgen -- -W "$(command sed -ne 's|^iface \([^ ]\{1,\}\).*$|\1|p' \
"${files[@]}" 2>/dev/null)"
_comp_compgen_split -- "$(command sed -ne \
's|^iface \([^ ]\{1,\}\).*$|\1|p' "${files[@]}" 2>/dev/null)"
elif [[ -f /etc/SuSE-release ]]; then
# SuSE system
_comp_expand_glob files '/etc/sysconfig/network/ifcfg-*'
((${#files[@]})) || return 0
_comp_compgen -- -W "$(printf '%s\n' "${files[@]}" |
_comp_compgen_split -- "$(printf '%s\n' "${files[@]}" |
command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p')"
elif [[ -f /etc/pld-release ]]; then
# PLD Linux
_comp_compgen -- -W "$(command ls -B /etc/sysconfig/interfaces |
_comp_compgen_split -- "$(command ls -B /etc/sysconfig/interfaces |
command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p')"
else
# Assume Red Hat
_comp_expand_glob files '/etc/sysconfig/network-scripts/ifcfg-*'
((${#files[@]})) || return 0
_comp_compgen -- -W "$(printf '%s\n' "${files[@]}" |
_comp_compgen_split -- "$(printf '%s\n' "${files[@]}" |
command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p')"
fi
}
Expand Down Expand Up @@ -1536,7 +1576,7 @@ _ip_addresses()
# TODO:API: rename per conventions
_kernel_versions()
{
_comp_compgen -- -W '$(command ls /lib/modules)'
_comp_compgen_split -- "$(command ls /lib/modules)"
}

# This function completes on all available network interfaces
Expand Down Expand Up @@ -1654,26 +1694,26 @@ if [[ $OSTYPE == *@(solaris|aix)* ]]; then
# This function completes on process IDs.
_pids()
{
_comp_compgen -- -W '$(command ps -efo pid | command sed 1d)'
_comp_compgen_split -- "$(command ps -efo pid | command sed 1d)"
}

_pgids()
{
_comp_compgen -- -W '$(command ps -efo pgid | command sed 1d)'
_comp_compgen_split -- "$(command ps -efo pgid | command sed 1d)"
}
_pnames()
{
_comp_compgen -- -X '<defunct>' -W '$(command ps -efo comm | \
command sed -e 1d -e "s:.*/::" -e "s/^-//" | sort -u)'
_comp_compgen_split -X '<defunct>' -- "$(command ps -efo comm |
command sed -e 1d -e 's:.*/::' -e 's/^-//' | sort -u)"
}
else
_pids()
{
_comp_compgen -- -W '$(command ps ax -o pid=)'
_comp_compgen_split -- "$(command ps ax -o pid=)"
}
_pgids()
{
_comp_compgen -- -W '$(command ps ax -o pgid=)'
_comp_compgen_split -- "$(command ps ax -o pgid=)"
}
# @param $1 if -s, don't try to avoid truncated command names
_pnames()
Expand Down Expand Up @@ -1732,12 +1772,12 @@ fi
_uids()
{
if type getent &>/dev/null; then
_comp_compgen -- -W '$(getent passwd | cut -d: -f3)'
_comp_compgen_split -- "$(getent passwd | cut -d: -f3)"
elif type perl &>/dev/null; then
_comp_compgen -- -W '$(perl -e '"'"'while (($uid) = (getpwent)[2]) { print $uid . "\n" }'"'"')'
_comp_compgen_split -- "$(perl -e 'while (($uid) = (getpwent)[2]) { print $uid . "\n" }')"
else
# make do with /etc/passwd
_comp_compgen -- -W '$(cut -d: -f3 /etc/passwd)'
_comp_compgen_split -- "$(cut -d: -f3 /etc/passwd)"
fi
}

Expand All @@ -1747,12 +1787,12 @@ _uids()
_gids()
{
if type getent &>/dev/null; then
_comp_compgen -- -W '$(getent group | cut -d: -f3)'
_comp_compgen_split -- "$(getent group | cut -d: -f3)"
elif type perl &>/dev/null; then
_comp_compgen -- -W '$(perl -e '"'"'while (($gid) = (getgrent)[2]) { print $gid . "\n" }'"'"')'
_comp_compgen_split -- "$(perl -e 'while (($gid) = (getgrent)[2]) { print $gid . "\n" }')"
else
# make do with /etc/group
_comp_compgen -- -W '$(cut -d: -f3 /etc/group)'
_comp_compgen_split -- "$(cut -d: -f3 /etc/group)"
fi
}

Expand Down Expand Up @@ -1822,9 +1862,9 @@ _service()
else
local sysvdirs
_comp_sysvdirs
_comp_compgen -l -- -W '$(command sed -e "y/|/ /" \
-ne "s/^.*\(U\|msg_u\)sage.*{\(.*\)}.*$/\2/p" \
${sysvdirs[0]}/${prev##*/} 2>/dev/null) start stop'
_comp_compgen_split -l -- "$(command sed -e 'y/|/ /' \
-ne 's/^.*\(U\|msg_u\)sage.*{\(.*\)}.*$/\2/p' \
"${sysvdirs[0]}/${prev##*/}" 2>/dev/null) start stop"
fi
} &&
complete -F _service service
Expand All @@ -1849,7 +1889,7 @@ _modules()
{
local modpath
modpath=/lib/modules/$1
_comp_compgen -- -W "$(command ls -RL "$modpath" 2>/dev/null |
_comp_compgen_split -- "$(command ls -RL "$modpath" 2>/dev/null |
command sed -ne 's/^\(.*\)\.k\{0,1\}o\(\.[gx]z\)\{0,1\}$/\1/p' \
-e 's/^\(.*\)\.ko\.zst$/\1/p')"
}
Expand All @@ -1859,7 +1899,7 @@ _modules()
# TODO:API: rename per conventions (+ include "kernel" in the name)
_installed_modules()
{
_comp_compgen -c "$1" -- -W "$(PATH="$PATH:/sbin" lsmod |
_comp_compgen -c "$1" split -- "$(PATH="$PATH:/sbin" lsmod |
awk '{if (NR != 1) print $1}')"
}

Expand Down Expand Up @@ -1921,8 +1961,8 @@ _allowed_users()
if _complete_as_root; then
_comp_compgen -c "${1:-$cur}" -- -u
else
_comp_compgen -c "${1:-$cur}" -- -W \
"$(id -un 2>/dev/null || whoami 2>/dev/null)"
_comp_compgen -c "${1:-$cur}" split -- "$(id -un 2>/dev/null ||
whoami 2>/dev/null)"
fi
}

Expand All @@ -1932,17 +1972,16 @@ _allowed_groups()
if _complete_as_root; then
_comp_compgen -c "$1" -- -g
else
_comp_compgen -c "$1" -- -W \
"$(id -Gn 2>/dev/null || groups 2>/dev/null)"
_comp_compgen -c "$1" split -- "$(id -Gn 2>/dev/null ||
groups 2>/dev/null)"
fi
}

# @since 2.12
_comp_selinux_users()
{
_comp_compgen -a -- -W '$(
semanage user -nl 2>/dev/null | awk "{ print \$1 }"
)'
_comp_compgen -a split -- "$(semanage user -nl 2>/dev/null |
awk '{ print $1 }')"
}

# This function completes on valid shells
Expand Down Expand Up @@ -2067,15 +2106,16 @@ _count_args()
# TODO:API: rename per conventions
_pci_ids()
{
_comp_compgen -a -- -W "$(PATH="$PATH:/sbin" lspci -n | awk '{print $3}')"
_comp_compgen -a split -- "$(PATH="$PATH:/sbin" lspci -n |
awk '{print $3}')"
}

# This function completes on USB IDs
#
# TODO:API: rename per conventions
_usb_ids()
{
_comp_compgen -a -- -W "$(PATH="$PATH:/sbin" lsusb | awk '{print $6}')"
_comp_compgen -a split -- "$(PATH="$PATH:/sbin" lsusb | awk '{print $6}')"
}

# CD device names
Expand All @@ -2096,7 +2136,7 @@ _dvd_devices()
# TODO:API: rename per conventions
_terms()
{
_comp_compgen -a -- -W "$({
_comp_compgen -a split -- "$({
command sed -ne 's/^\([^[:space:]#|]\{2,\}\)|.*/\1/p' /etc/termcap
{
toe -a || toe
Expand Down Expand Up @@ -2654,7 +2694,7 @@ _comp_longopt()
[[ $was_split ]] && return

if [[ $cur == -* ]]; then
_comp_compgen -- -W "$(LC_ALL=C $1 --help 2>&1 |
_comp_compgen_split -- "$(LC_ALL=C $1 --help 2>&1 |
while read -r line; do
[[ $line =~ --[A-Za-z0-9]+([-_][A-Za-z0-9]+)*=? ]] &&
printf '%s\n' "${BASH_REMATCH[0]}"
Expand Down
5 changes: 3 additions & 2 deletions completions/2to3
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ _comp_cmd_2to3()
return
;;
-f | --fix | -x | --nofix)
COMPREPLY=($(compgen -W \
"$("$1" --list-fixes 2>/dev/null | command sed -e 1d)" -- "$cur"))
_comp_compgen_split -- "$(
"$1" --list-fixes 2>/dev/null | command sed -e 1d
)"
return
;;
-j | --processes)
Expand Down
8 changes: 4 additions & 4 deletions completions/7z
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ _comp_cmd_7z()
_comp_compgen -a filedir '@(7z?(.001)|arj|bz2|cab|cb7|chm|cpio|deb|dmg|flv|gem|img|iso|lz[ah]|lzma?(86)|msi|pmd|[rx]ar|rpm|sw[fm]|?(g)tar|taz|?(t)[bglx]z|tb?(z)2|vhd|wim|Z)'
else
if [[ ${words[1]} == d ]]; then
local IFS=$'\n'
COMPREPLY=($(compgen -W "$("$1" l "${words[2]}" \
-slt 2>/dev/null | command sed -n '/^Path =/s/^Path = \(.*\)$/\1/p' \
2>/dev/null | tail -n+2)" -- "$cur"))
_comp_compgen_split -l -- "$(
"$1" l "${words[2]}" -slt 2>/dev/null | command sed -n \
'/^Path =/s/^Path = \(.*\)$/\1/p' 2>/dev/null | tail -n+2
)"
compopt -o filenames
else
_comp_compgen_filedir
Expand Down
2 changes: 1 addition & 1 deletion completions/_look
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ _comp_cmd_look()
_comp_initialize -- "$@" || return

if ((cword == 1)); then
COMPREPLY=($(compgen -W '$(look "$cur" 2>/dev/null)' -- "$cur"))
_comp_compgen_split -- "$(look "$cur" 2>/dev/null)"
fi
} &&
complete -F _comp_cmd_look -o default look
Expand Down
7 changes: 3 additions & 4 deletions completions/_mock
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ _comp_cmd_mock()
return
;;
-r | --root)
COMPREPLY=($(compgen -W "$(command ls "$cfgdir")" -- "$cur"))
_comp_compgen_split -- "$(command ls "$cfgdir")"
COMPREPLY=(${COMPREPLY[@]/%.cfg/})
return
;;
Expand All @@ -44,9 +44,8 @@ _comp_cmd_mock()
# (e.g. ix86 chroot builds in x86_64 mock host)
# This would actually depend on what the target root
# can be used to build for...
COMPREPLY=($(compgen -W "$(command rpm --showrc |
command sed -ne 's/^\s*compatible\s\s*archs\s*:\s*\(.*\)/\1/i p')" \
-- "$cur"))
_comp_compgen_split -- "$(command rpm --showrc | command sed -ne \
's/^\s*compatible\s\s*archs\s*:\s*\(.*\)/\1/i p')"
return
;;
--enable-plugin | --disable-plugin)
Expand Down
25 changes: 16 additions & 9 deletions completions/_mount
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ _comp_cmd_mount()
if [[ $cur == *:* ]]; then
for sm in "$(type -P showmount)" {,/usr}/{,s}bin/showmount; do
[[ -x $sm ]] || continue
COMPREPLY=($(compgen -W "$("$sm" -e ${cur%%:*} |
awk 'NR>1 {print $1}')" -- "${cur#*:}"))
_comp_compgen -c "${cur#*:}" split -- "$(
"$sm" -e ${cur%%:*} | awk 'NR>1 {print $1}'
)"
return
done
fi
Expand All @@ -43,21 +44,27 @@ _comp_cmd_mount()
host=${cur#//}
host=${host%%/*}
if [[ $host ]]; then
COMPREPLY=($(compgen -P "//$host" -W \
"$(smbclient -d 0 -NL "$host" 2>/dev/null |
_comp_compgen -c "${cur#//"$host"}" split -P "//$host" -- "$(
smbclient -d 0 -NL "$host" 2>/dev/null |
command sed -ne '/^[[:blank:]]*Sharename/,/^$/p' |
command sed -ne '3,$s|^[^A-Za-z]*\([^[:blank:]]*\).*$|/\1|p')" \
-- "${cur#//"$host"}"))
command sed -ne '3,$s|^[^A-Za-z]*\([^[:blank:]]*\).*$|/\1|p'
)"
fi
elif [[ -r /etc/vfstab ]]; then
# Solaris
COMPREPLY=($(compgen -W "$(awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' /etc/vfstab)" -- "$cur"))
_comp_compgen_split -- "$(
awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' /etc/vfstab
)"
elif [[ ! -e /etc/fstab ]]; then
# probably Cygwin
COMPREPLY=($(compgen -W "$("$1" | awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}')" -- "$cur"))
_comp_compgen_split -- "$(
"$1" | awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}'
)"
else
# probably BSD
COMPREPLY=($(compgen -W "$(awk '! /^[ \t]*#/ {if ($2 ~ /\//) print $2}' /etc/fstab)" -- "$cur"))
_comp_compgen_split -- "$(
awk '! /^[ \t]*#/ {if ($2 ~ /\//) print $2}' /etc/fstab
)"
fi
} &&
complete -F _comp_cmd_mount -o default -o dirnames mount
Expand Down
13 changes: 7 additions & 6 deletions completions/_mount.linux
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,9 @@ _comp_cmd_mount()
if [[ $cur == *:* ]]; then
for sm in "$(type -P showmount)" {,/usr}/{,s}bin/showmount; do
[[ -x $sm ]] || continue
COMPREPLY=($(compgen -W "$("$sm" -e ${cur%%:*} |
awk 'NR>1 {print $1}')" -- "${cur#*:}"))
_comp_compgen -c "${cur#*:}" split -- "$(
"$sm" -e ${cur%%:*} | awk 'NR>1 {print $1}'
)"
return
done
fi
Expand All @@ -233,11 +234,11 @@ _comp_cmd_mount()
host=${cur#//}
host=${host%%/*}
if [[ $host ]]; then
COMPREPLY=($(compgen -P "//$host" -W \
"$(smbclient -d 0 -NL "$host" 2>/dev/null |
_comp_compgen -c "${cur#//"$host"}" split -P "//$host" -- "$(
smbclient -d 0 -NL "$host" 2>/dev/null |
command sed -ne '/^[[:blank:]]*Sharename/,/^$/p' |
command sed -ne '3,$s|^[^A-Za-z]*\([^[:blank:]]*\).*$|/\1|p')" \
-- "${cur#//"$host"}"))
command sed -ne '3,$s|^[^A-Za-z]*\([^[:blank:]]*\).*$|/\1|p'
)"
fi
fi

Expand Down
Loading