diff --git a/bash_completion b/bash_completion index 2aec4dc4169..b9a85fedd05 100644 --- a/bash_completion +++ b/bash_completion @@ -245,6 +245,124 @@ _upvars() done } +# Filter the array elements with the specified condition. +# @param $1 Array name (that is not "value" or other internal variable names) +# @param $2 When none of the options -EFG are specified, this is used as the +# command that tests the array element. If this is an existing function +# name, the function is called with the value of the array element. +# Otherwise, this shall be the shell command that tests the array-element +# value stored in the shell variable "value". +# +# Options: +# -E $2 is interpreted as a POSIX extended regular expression. +# This is always the partial matching unless ^, $ is included +# in $2. +# -F $2 is interpreted as a fixed string. +# -G $2 is interpreted as a glob pattern. +# +# -p Combined with -F or -G, it performs the prefix matching. +# -s Combined with -F or -G, it performs the suffix matching. +# -m Combined with -F or -G, it performs the middle matching. +# -r Revert the condition, i.e., remove elements that satisfy +# the original condition. +# +# -C Array compaction is not performed. +# +# @return 2 with a wrong usage, 1 when any elements are removed, 0 when the set +# of array elements are unchanged. [ Note: the compaction will be performed +# (without the option -C) even when the set of array elements are +# unchanged. ] +_comp_array_filter() +{ + local __comp_flags='' __comp_pattype='' __comp_anchoring='' + local OPTIND=1 OPTARG='' OPTERR=0 __comp_opt='' + while getopts 'EFGpsmrC' __comp_opt "$@"; do + case $__comp_opt in + [EFG]) __comp_pattype=$__comp_opt ;; + [psm]) __comp_anchoring=$__comp_opt ;; + [rC]) __comp_flags=$__comp_opt$__comp_flags ;; + *) + echo "bash_completion: $FUNCNAME: usage error" >&2 + return 2 + ;; + esac + done + + shift $((OPTIND - 1)) + if (($# != 2)); then + printf 'bash_completion: %s: %s\n' "$FUNCNAME" "unexpected number of arguments." >&2 + printf 'usage: %s %s\n' "$FUNCNAME" "[-EFGpsmrC] ARRAY_NAME CONDITION" >&2 + return 2 + elif [[ $1 != [a-zA-Z_]*([a-zA-Z_0-9]) ]]; then + printf 'bash_completion: %s: %s\n' "$FUNCNAME" "invalid array name '$1'." >&2 + return 2 + elif [[ $1 == @(__comp_*|OPTIND|OPTARG|OPTERR) ]]; then + printf 'bash_completion: %s: %s\n' "$FUNCNAME" "array name '$1' is reserved for internal uses." >&2 + return 2 + elif [[ ! $__comp_pattype && $1 == value ]]; then + printf 'bash_completion: %s: %s\n' "$FUNCNAME" "array name '$1' cannot be used for the predicate." >&2 + return 2 + fi + # When the array is empty: + eval "((\${#$1[@]}))" || return 0 + + local __comp_predicate='' __comp_pattern=$2 + case $__comp_pattype in + E) + __comp_predicate='[[ $__comp_value == $__comp_pattern ]]' + ;; + F) + case $__comp_anchoring in + p) __comp_predicate='[[ $__comp_value == "$__comp_pattern"* ]]' ;; + s) __comp_predicate='[[ $__comp_value == *"$__comp_pattern" ]]' ;; + m) __comp_predicate='[[ $__comp_value == *"$__comp_pattern"* ]]' ;; + *) __comp_predicate='[[ $__comp_value == "$__comp_pattern" ]]' ;; + esac + ;; + G) + case $__comp_anchoring in + p) __comp_predicate='[[ $__comp_value == $__comp_pattern* ]]' ;; + s) __comp_predicate='[[ $__comp_value == *$__comp_pattern ]]' ;; + m) __comp_predicate='[[ $__comp_value == *$__comp_pattern* ]]' ;; + *) __comp_predicate='[[ $__comp_value == $__comp_pattern ]]' ;; + esac + ;; + *) + if declare -F "$2" &>/dev/null; then + _comp_predicate="$2 \"\$__comp_value\"" + else + _comp_predicate="local value=\$__comp_value; $2" + fi + ;; + esac + + local __comp_unset='' __comp_expected_status=0 + [[ $__comp_flags == *r* ]] && __comp_expected_status=1 + + local __comp_indices __comp_index __comp_value + eval "__comp_indices=(\"\${!$1[@]}\")" + for __comp_index in "${__comp_indices[@]}"; do + eval "__comp_value=\${$1[\$__comp_index]}; $__comp_predicate" + case $? in + "$__comp_expected_status") continue ;; + [01]) + unset -v "$1[\$__comp_index]" + __comp_unset=1 + ;; + *) + printf 'bash_completion: %s: %s\n' "$FUNCNAME" "the filter condition broken." >&2 + return 2 + ;; + esac + done + + # Compaction of the sparse array + [[ $__comp_flags == *C* ]] || + eval -- "((\${#$1[@]})) && $1=(\"\${$1[@]}\")" + + [[ ! $__comp_unset ]] +} + # Reassemble command line words, excluding specified characters from the # list of word completion separators (COMP_WORDBREAKS). # @param $1 chars Characters out of $COMP_WORDBREAKS which should