@@ -309,6 +309,135 @@ _comp_expand_glob()
309309 return 0
310310}
311311
312+ # Filter the array elements with the specified condition.
313+ # @param $1 Array name (that is not "value" or other internal variable names)
314+ # @param $2 When none of the options -EFG are specified, this is used as the
315+ # command that tests the array element. The command is supposed to exit with
316+ # status 0 when the element should be preserved, and 1 when the element
317+ # should be removed. The other exit status will cancel the array filtering.
318+ # If this is an existing function name, the function is called with the value
319+ # of the array element. Otherwise, this shall be the shell command that
320+ # tests the array-element value stored in the shell variable "value".
321+ #
322+ # Options:
323+ # -E $2 is interpreted as a POSIX extended regular expression.
324+ # The default anchoring is `-m` (see below).
325+ # -F $2 is interpreted as a fixed string. The default anchoring
326+ # is `-m` (see below).
327+ # -G $2 is interpreted as a glob pattern. The default anchoring
328+ # is `-x` (see below).
329+ #
330+ # -p Combined with -EFG, it performs the prefix matching.
331+ # -s Combined with -EFG, it performs the suffix matching.
332+ # -m Combined with -EFG, it performs the middle matching.
333+ # -x Combined with -EFG, it performs the exact matching.
334+ #
335+ # -r Revert the condition, i.e., remove elements that satisfy
336+ # the original condition.
337+ # -C Array compaction is not performed.
338+ #
339+ # @return 2 with a wrong usage, 1 when any elements are removed, 0 when the set
340+ # of array elements are unchanged. [ Note: the compaction will be performed
341+ # (without the option -C) even when the set of array elements are
342+ # unchanged. ]
343+ _comp_array_filter ()
344+ {
345+ local _comp_local_flags=' ' _comp_local_pattype=' ' _comp_local_anchoring=' '
346+ local OPTIND=1 OPTARG=' ' OPTERR=0 _comp_local_opt=' '
347+ while getopts ' EFGpsmxrC' _comp_local_opt " $@ " ; do
348+ case $_comp_local_opt in
349+ [EFG]) _comp_local_pattype=$_comp_local_opt ;;
350+ [psmx]) _comp_local_anchoring=$_comp_local_opt ;;
351+ [rC]) _comp_local_flags=$_comp_local_opt$_comp_local_flags ;;
352+ * )
353+ printf ' bash_completion: %s: %s\n' " $FUNCNAME " ' usage error' >&2
354+ printf ' usage: %s %s\n' " $FUNCNAME " " [-EFGpsmxrC] ARRAY_NAME CONDITION" >&2
355+ return 2
356+ ;;
357+ esac
358+ done
359+
360+ shift $(( OPTIND - 1 ))
361+ if (( $# != 2 )) ; then
362+ printf ' bash_completion: %s: %s\n' " $FUNCNAME " " unexpected number of arguments: $# " >&2
363+ printf ' usage: %s %s\n' " $FUNCNAME " " [-EFGpsmxrC] ARRAY_NAME CONDITION" >&2
364+ return 2
365+ elif [[ $1 != [a-zA-Z_]* ([a-zA-Z_0-9]) ]]; then
366+ printf ' bash_completion: %s: %s\n' " $FUNCNAME " " invalid array name '$1 '." >&2
367+ return 2
368+ elif [[ $1 == @ (_comp_local_* | OPTIND| OPTARG| OPTERR) ]]; then
369+ printf ' bash_completion: %s: %s\n' " $FUNCNAME " " array name '$1 ' is reserved for internal uses" >&2
370+ return 2
371+ elif [[ ! $_comp_local_pattype && $1 == value ]]; then
372+ printf ' bash_completion: %s: %s\n' " $FUNCNAME " " array name '$1 ' cannot be used for the predicate" >&2
373+ return 2
374+ fi
375+ # When the array is empty:
376+ eval " ((\$ {#$1 [@]}))" || return 0
377+
378+ local _comp_local_predicate=' ' _comp_local_pattern=$2
379+ case $_comp_local_pattype in
380+ E)
381+ case $_comp_local_anchoring in
382+ p) _comp_local_predicate=' [[ $_comp_local_value =~ ^($_comp_local_pattern) ]]' ;;
383+ s) _comp_local_predicate=' [[ $_comp_local_value =~ ($_comp_local_pattern)$ ]]' ;;
384+ x) _comp_local_predicate=' [[ $_comp_local_value =~ ^($_comp_local_pattern)$ ]]' ;;
385+ * ) _comp_local_predicate=' [[ $_comp_local_value =~ $_comp_local_pattern ]]' ;;
386+ esac
387+ ;;
388+ F)
389+ case $_comp_local_anchoring in
390+ p) _comp_local_predicate=' [[ $_comp_local_value == "$_comp_local_pattern"* ]]' ;;
391+ s) _comp_local_predicate=' [[ $_comp_local_value == *"$_comp_local_pattern" ]]' ;;
392+ x) _comp_local_predicate=' [[ $_comp_local_value == "$_comp_local_pattern" ]]' ;;
393+ * ) _comp_local_predicate=' [[ $_comp_local_value == *"$_comp_local_pattern"* ]]' ;;
394+ esac
395+ ;;
396+ G)
397+ case $_comp_local_anchoring in
398+ p) _comp_local_predicate=' [[ $_comp_local_value == $_comp_local_pattern* ]]' ;;
399+ s) _comp_local_predicate=' [[ $_comp_local_value == *$_comp_local_pattern ]]' ;;
400+ m) _comp_local_predicate=' [[ $_comp_local_value == *$_comp_local_pattern* ]]' ;;
401+ * ) _comp_local_predicate=' [[ $_comp_local_value == $_comp_local_pattern ]]' ;;
402+ esac
403+ ;;
404+ * )
405+ if declare -F " $2 " & > /dev/null; then
406+ _comp_local_predicate=" $2 \"\$ _comp_local_value\" "
407+ else
408+ _comp_local_predicate=" local value=\$ _comp_local_value; $2 "
409+ fi
410+ ;;
411+ esac
412+
413+ local _comp_local_unset=' ' _comp_local_expected_status=0
414+ [[ $_comp_local_flags == * r* ]] && _comp_local_expected_status=1
415+
416+ local _comp_local_indices _comp_local_index _comp_local_value
417+ eval " _comp_local_indices=(\"\$ {!$1 [@]}\" )"
418+ for _comp_local_index in " ${_comp_local_indices[@]} " ; do
419+ eval " _comp_local_value=\$ {$1 [\$ _comp_local_index]}; $_comp_local_predicate "
420+ case $? in
421+ " $_comp_local_expected_status " ) continue ;;
422+ [01])
423+ unset -v " $1 [\$ _comp_local_index]"
424+ _comp_local_unset=1
425+ ;;
426+ * )
427+ printf ' bash_completion: %s: %s\n' " $FUNCNAME " \
428+ " the filter condition broken '${_comp_local_pattype: +-$_comp_local_pattype } $2 '" >&2
429+ return 2
430+ ;;
431+ esac
432+ done
433+
434+ # Compaction of the sparse array
435+ [[ $_comp_local_flags == * C* ]] ||
436+ eval " ((\$ {#$1 [@]})) && $1 =(\"\$ {$1 [@]}\" )"
437+
438+ [[ ! $_comp_local_unset ]]
439+ }
440+
312441# Reassemble command line words, excluding specified characters from the
313442# list of word completion separators (COMP_WORDBREAKS).
314443# @param $1 chars Characters out of $COMP_WORDBREAKS which should
0 commit comments