@@ -381,51 +381,96 @@ _comp_split()
381
381
(( _new_size > _old_size))
382
382
}
383
383
384
- # Call `compgen` with the specified arguments and store the results in the
385
- # specified array.
386
- # Usage: _comp_compgen [-alR|-F sep|-v arr|-c cur] -- args...
387
- # This function essentially performs arr=($(compgen args...)) but properly
388
- # handles shell options, IFS, etc. using _comp_split. This function is
389
- # equivalent to `_comp_split [-a] -l arr "$(IFS=sep; compgen args... -- cur)"`,
390
- # but this pattern is frequent in the codebase and is good to separate out as a
391
- # function for the possible future implementation change.
384
+ # Provide a common interface to generate completion candidates in COMPREPLY or
385
+ # in a specified array.
392
386
# OPTIONS
393
387
# -a Append to the array
394
388
# -v arr Store the results to the array ARR. The default is `COMPREPLY`.
389
+ # The array name should not start with an underscores "_", which is
390
+ # internally used. The array name should not be either "IFS" or
391
+ # "OPT{IND,ARG,ERR}".
395
392
# -F sep Set a set of separator characters (used as IFS in evaluating
396
393
# `compgen'). The default separator is $' \t\n'. Note that this is
397
394
# not the set of separators to delimit output of `compgen', but the
398
395
# separators in evaluating the expansions of `-W '...'`, etc. The
399
396
# delimiter of the output of `compgen` is always a newline.
400
- # -l The same as -F $'\n'.
397
+ # -l The same as -F $'\n'. Use lines as words in evaluating compgen.
401
398
# -c cur Set a word used as a prefix to filter the completions. The default
402
399
# is ${cur-}.
403
- # -R The same as -c ''.
404
- # @param $1 array_name The array name
405
- # The array name should not start with an underscores "_", which is
406
- # internally used. The array name should not be either "IFS" or
407
- # "OPT{IND,ARG,ERR}".
408
- # @param $2... args The arguments that are passed to compgen
400
+ # -R The same as -c ''. Use raw outputs without filtering.
401
+ # @var[in,opt] cur Used as the default value of a prefix to filter the
402
+ # completions.
403
+ #
404
+ # Usage #1: _comp_compgen [-alR|-F sep|-v arr|-c cur] -- options...
405
+ # Call `compgen` with the specified arguments and store the results in the
406
+ # specified array. This function essentially performs arr=($(compgen args...))
407
+ # but properly handles shell options, IFS, etc. using _comp_split. This
408
+ # function is equivalent to `_comp_split [-a] -l arr "$(IFS=sep; compgen
409
+ # args... -- cur)"`, but this pattern is frequent in the codebase and is good
410
+ # to separate out as a function for the possible future implementation change.
411
+ # @param $1... options Arguments that are passed to compgen (if $1 starts with
412
+ # a hyphen `-`).
413
+ #
409
414
# Note: References to positional parameters $1, $2, ... (such as -W '$1')
410
415
# will not work as expected because these reference the arguments of
411
416
# `_comp_compgen' instead of those of the caller function. When there are
412
417
# needs to reference them, save the arguments to an array and reference the
413
- # array instead. The array option `-V arr` in bash >= 5.3 should be instead
414
- # specified as `-v arr` as a part of `_comp_compgen` options.
415
- # @var[in] cur Used as the default value of a prefix to filter the
416
- # completions.
418
+ # array instead.
419
+ #
420
+ # Note: The array option `-V arr` in bash >= 5.3 should be instead specified
421
+ # as `-v arr` as a part of the `_comp_compgen` options.
422
+ #
423
+ # Usage #2: _comp_compgen [-alR|-v arr|-c cur] name args...
424
+ # Call `_comp_compgen_NAME ARGS...` with the specified options. This provides
425
+ # a common interface to call the functions `_comp_compgen_NAME`, which produce
426
+ # completion candidates, with custom options [-alR|-v arr|-cur]. The option
427
+ # `-F sep` is not used with this usage.
428
+ # @param $1... name args Calls the function _comp_compgen_NAME with the
429
+ # specified ARGS (if $1 does not start with a hyphen `-`). The options
430
+ # [-alR|-v arr|-c cur] are inherited by the child calls of `_comp_compgen`
431
+ # inside `_comp_compgen_NAME` unless the child call `_comp_compgen` receives
432
+ # overriding options.
433
+ # @var[in,opt,internal] _comp_compgen__append
434
+ # @var[in,opt,internal] _comp_compgen__var
435
+ # @var[in,opt,internal] _comp_compgen__cur
436
+ # These variables are internally used to pass the effect of the options
437
+ # [-alR|-v arr|-c cur] to the child calls of `_comp_compgen` in
438
+ # `_comp_compgen_NAME`.
439
+ #
440
+ # @remarks Design `_comp_compgen_NAME`: a function that produce completions can
441
+ # be defined with the name _comp_compgen_NAME. The function is supposed to
442
+ # generate completions by calling `_comp_compgen`. To reflect the options
443
+ # specified to the outer calls of `_comp_compgen`, the function should not
444
+ # directly modify `COMPREPLY`. To add words, one can call
445
+ #
446
+ # _comp_compgen -- -W '"${words[@]}"'
447
+ #
448
+ # To directly add words without filtering by `cur`, one can call
449
+ #
450
+ # _comp_compgen -R -- -W '"${words[@]}"'
451
+ #
452
+ # or use the utility `_comp_compgen_set`:
453
+ #
454
+ # _comp_compgen_set "${words[@]}"
455
+ #
456
+ # Other nested calls of _comp_compgen can also be used. The function is
457
+ # supposed to replace the existing content of the array by default to allow the
458
+ # caller control whether to replace or append by the option `-a`.
459
+ #
417
460
_comp_compgen ()
418
461
{
419
- local _append=" " _var=COMPREPLY _cur=${cur-} _ifs=$' \t\n '
420
- local -a _split_options=(-l)
462
+ local _append=${_comp_compgen__append-}
463
+ local _var=${_comp_compgen__var-COMPREPLY}
464
+ local _cur=${_comp_compgen__cur-${cur-} }
465
+ local _ifs=$' \t\n '
421
466
422
467
local OPTIND=1 OPTARG=" " OPTERR=0 _opt
423
468
while getopts ' :alF:v:Rc:' _opt " $@ " ; do
424
469
case $_opt in
425
- a) _append=set _split_options+=(-a) ;;
470
+ a) _append=set ;;
426
471
v)
427
472
if [[ $OPTARG == @ (* [^_a-zA-Z0-9]* | [0-9]* | ' ' | _* | IFS| OPTIND| OPTARG| OPTERR) ]]; then
428
- printf ' bash_completion: %s: invalid array name `%s' \' ' .\n' " $FUNCNAME " " -v $OPTARG " >&2
473
+ printf ' bash_completion: %s: -v: invalid array name `%s' \' ' .\n' " $FUNCNAME " " $OPTARG " >&2
429
474
return 2
430
475
fi
431
476
_var=$OPTARG
@@ -445,17 +490,38 @@ _comp_compgen()
445
490
printf ' bash_completion: %s: unexpected number of arguments.\n' " $FUNCNAME " >&2
446
491
printf ' usage: %s [-alR|-F SEP|-v ARR|-c CUR] -- ARGS...' " $FUNCNAME " >&2
447
492
return 2
448
- elif
449
- # Note: $* in the below checks would be affected by uncontrolled IFS in
450
- # bash >= 5.0, so we need to set IFS to the normal value. The behavior
451
- # in bash < 5.0, where unquoted $* in conditional command did not honor
452
- # IFS, was a bug.
453
- local IFS=$' \t\n '
454
- [[ $* == * \$ [0-9]* || $* == * \$\{ [0-9]* ]]
455
- then
456
- # Note: extglob *\$?(\{)[0-9]* can be extremely slow when the string
457
- # "${*:2:_nopt}" becomes longer, so we test \$[0-9] and \$\{[0-9]
458
- # separately.
493
+ fi
494
+
495
+ if [[ $1 != -* ]]; then
496
+ # usage: _comp_compgen [options] NAME args
497
+ if ! declare -F " _comp_compgen_$1 " & > /dev/null; then
498
+ printf ' bash_completion: %s: unrecognized category `%s' \' ' (function _comp_compgen_%s not found).\n' " $FUNCNAME " " $1 " " $1 " >&2
499
+ return 2
500
+ fi
501
+
502
+ local _comp_compgen__append=$_append
503
+ local _comp_compgen__var=$_var
504
+ local _comp_compgen__cur=$_cur cur=$_cur
505
+ # Note: we use $1 as a part of a function name, and we use $2... as
506
+ # arguments to the function if any.
507
+ # shellcheck disable=SC2145
508
+ _comp_compgen_" $@ "
509
+ return
510
+ fi
511
+
512
+ # usage: _comp_compgen [options] -- [compgen_options]
513
+
514
+ # Note: $* in the below checks would be affected by uncontrolled IFS in
515
+ # bash >= 5.0, so we need to set IFS to the normal value. The behavior in
516
+ # bash < 5.0, where unquoted $* in conditional command did not honor IFS,
517
+ # was a bug.
518
+ # Note: Also, ${_cur:+-- "$_cur"} and ${_append:+-a} would be affected by
519
+ # uncontrolled IFS.
520
+ local IFS=$' \t\n '
521
+ # Note: extglob *\$?(\{)[0-9]* can be extremely slow when the string
522
+ # "${*:2:_nopt}" becomes longer, so we test \$[0-9] and \$\{[0-9]
523
+ # separately.
524
+ if [[ $* == * \$ [0-9]* || $* == * \$\{ [0-9]* ]]; then
459
525
printf ' bash_completion: %s: positional parameter $1, $2, ... do not work inside this function.\n' " $FUNCNAME " >&2
460
526
return 2
461
527
fi
@@ -475,7 +541,23 @@ _comp_compgen()
475
541
return " $_status "
476
542
}
477
543
478
- _comp_split " ${_split_options[@]} " " $_var " " $_result "
544
+ _comp_split -l ${_append: +-a} " $_var " " $_result "
545
+ }
546
+
547
+ # usage: _comp_compgen_set [words...]
548
+ # Reset COMPREPLY with the specified WORDS. If no arguments are specified, the
549
+ # array is cleared.
550
+ #
551
+ # When an array name is specified by `-v VAR` in a caller _comp_compgen, the
552
+ # array is reset instead of COMPREPLY. When the `-a` flag is specified in a
553
+ # caller _comp_compgen, the words are appended to the existing elements of the
554
+ # array instead of replacing the existing elements. This function ignores
555
+ # ${cur-} or the prefix specified by `-v CUR`.
556
+ _comp_compgen_set ()
557
+ {
558
+ local _append=${_comp_compgen__append-}
559
+ local _var=${_comp_compgen__var-COMPREPLY}
560
+ eval -- " $_var ${_append: ++} =(\"\$ @\" )"
479
561
}
480
562
481
563
# Check if the argument looks like a path.
@@ -766,10 +848,9 @@ _comp_quote_compgen()
766
848
# completions with `.$1' and the uppercase version of it as file
767
849
# extension.
768
850
#
769
- # TODO: rename per API conventions
770
- _filedir ()
851
+ _comp_compgen_filedir ()
771
852
{
772
- _tilde " ${cur-} " || return
853
+ _comp_compgen_tilde && return
773
854
774
855
local -a toks
775
856
local arg=${1-}
@@ -805,11 +886,15 @@ _filedir()
805
886
fi
806
887
807
888
if (( ${# toks[@]} != 0 )) ; then
808
- # 2>/dev/null for direct invocation, e.g. in the _filedir unit test
889
+ # 2>/dev/null for direct invocation, e.g. in the _comp_compgen_filedir unit test
809
890
compopt -o filenames 2> /dev/null
810
- COMPREPLY+=(" ${toks[@]} " )
811
891
fi
812
- } # _filedir()
892
+
893
+ # Note: bash < 4.4 has a bug that all the elements are connected with
894
+ # ${v-"${a[@]}"} when IFS does not contain whitespace.
895
+ local IFS=$' \t\n '
896
+ _comp_compgen_set ${toks[@]+" ${toks[@]} " }
897
+ } # _comp_compgen_filedir()
813
898
814
899
# This function splits $cur=--foo=bar into $prev=--foo, $cur=bar, making it
815
900
# easier to support both "--foo bar" and "--foo=bar" style completions.
@@ -955,7 +1040,7 @@ _comp_variable_assignments()
955
1040
case $prev in
956
1041
TZ)
957
1042
cur=/usr/share/zoneinfo/$cur
958
- _filedir
1043
+ _comp_compgen -a filedir
959
1044
if (( ${# COMPREPLY[@]} )) ; then
960
1045
for i in " ${! COMPREPLY[@]} " ; do
961
1046
if [[ ${COMPREPLY[i]} == * .tab ]]; then
@@ -980,7 +1065,7 @@ _comp_variable_assignments()
980
1065
;;
981
1066
* )
982
1067
_variables && return 0
983
- _filedir
1068
+ _comp_compgen -a filedir
984
1069
;;
985
1070
esac
986
1071
@@ -995,9 +1080,12 @@ _comp_variable_assignments()
995
1080
#
996
1081
# Options:
997
1082
# -n EXCLUDE Passed to _comp_get_words -n with redirection chars
998
- # -e XSPEC Passed to _filedir as first arg for stderr redirections
999
- # -o XSPEC Passed to _filedir as first arg for other output redirections
1000
- # -i XSPEC Passed to _filedir as first arg for stdin redirections
1083
+ # -e XSPEC Passed to _comp_compgen_filedir as first arg for stderr
1084
+ # redirections
1085
+ # -o XSPEC Passed to _comp_compgen_filedir as first arg for other output
1086
+ # redirections
1087
+ # -i XSPEC Passed to _comp_compgen_filedir as first arg for stdin
1088
+ # redirections
1001
1089
# -s Split long options with _comp__split_longopt, implies -n =
1002
1090
# @param $1...$3 args Original arguments specified to the completion function.
1003
1091
# The first argument $1 is command name. The second
@@ -1008,9 +1096,9 @@ _comp_variable_assignments()
1008
1096
# @var[out] prev Reconstructed previous word
1009
1097
# @var[out] words Reconstructed words
1010
1098
# @var[out] cword Current word index in `words`
1011
- # @var[out] comp_args Original arguments specified to the completion function
1012
- # are saved in this array, if the arguments $1...$3 is
1013
- # specified.
1099
+ # @var[out] comp_args Original arguments specified to the completion
1100
+ # function are saved in this array, if the arguments
1101
+ # $1...$3 is specified.
1014
1102
# @var[out,opt] was_split When "-s" is specified, `"set"/""` is set depending
1015
1103
# on whether the split happened.
1016
1104
# @return True (0) if completion needs further processing,
@@ -1066,7 +1154,7 @@ _comp_initialize()
1066
1154
;;
1067
1155
esac
1068
1156
cur=${cur## " $redir " }
1069
- _filedir " $xspec "
1157
+ _comp_compgen filedir " $xspec "
1070
1158
return 1
1071
1159
fi
1072
1160
@@ -1370,21 +1458,21 @@ _ncpus()
1370
1458
}
1371
1459
1372
1460
# Perform tilde (~) completion
1373
- # @return True (0 ) if completion needs further processing,
1374
- # False (1 ) if tilde is followed by a valid username, completions are
1461
+ # @return False (1 ) if completion needs further processing,
1462
+ # True (0 ) if tilde is followed by a valid username, completions are
1375
1463
# put in COMPREPLY and no further processing is necessary.
1376
- # TODO: rename per API conventions
1377
- _tilde ()
1464
+ _comp_compgen_tilde ()
1378
1465
{
1379
- if [[ ${1 -} == \~ * && $1 != * /* ]]; then
1466
+ if [[ ${cur -} == \~ * && $cur != * /* ]]; then
1380
1467
# Try generate ~username completions
1381
- if _comp_compgen -c " ${1# \~ } " -- -P ' ~' -u; then
1382
- # 2>/dev/null for direct invocation, e.g. in the _tilde unit test
1468
+ if _comp_compgen -c " ${cur# \~ } " -- -P ' ~' -u; then
1469
+ # 2>/dev/null for direct invocation, e.g. in the
1470
+ # _comp_compgen_tilde unit test
1383
1471
compopt -o filenames 2> /dev/null
1384
- return 1
1472
+ return 0
1385
1473
fi
1386
1474
fi
1387
- return 0
1475
+ return 1
1388
1476
}
1389
1477
1390
1478
# Expand variable starting with tilde (~)
@@ -1431,7 +1519,7 @@ _expand()
1431
1519
__expand_tilde_by_ref cur
1432
1520
;;
1433
1521
~ * )
1434
- _tilde " $cur " ||
1522
+ _comp_compgen -v COMPREPLY tilde &&
1435
1523
eval " COMPREPLY[0]=$( printf ~ %q " ${COMPREPLY[0]# \~ } " ) "
1436
1524
return ${# COMPREPLY[@]}
1437
1525
;;
@@ -2412,23 +2500,23 @@ _comp_longopt()
2412
2500
return
2413
2501
;;
2414
2502
--! (no-* )dir* )
2415
- _filedir -d
2503
+ _comp_compgen -a filedir -d
2416
2504
return
2417
2505
;;
2418
2506
--! (no-* )@ (file| path)* )
2419
- _filedir
2507
+ _comp_compgen -a filedir
2420
2508
return
2421
2509
;;
2422
2510
--+ ([-a-z0-9_]))
2423
2511
local argtype=$( LC_ALL=C $1 --help 2>&1 | command sed -ne \
2424
2512
" s|.*$prev \[\{0,1\}=[<[]\{0,1\}\([-A-Za-z0-9_]\{1,\}\).*|\1|p" )
2425
2513
case ${argtype,,} in
2426
2514
* dir* )
2427
- _filedir -d
2515
+ _comp_compgen -a filedir -d
2428
2516
return
2429
2517
;;
2430
2518
* file* | * path* )
2431
- _filedir
2519
+ _comp_compgen -a filedir
2432
2520
return
2433
2521
;;
2434
2522
esac
@@ -2445,10 +2533,10 @@ _comp_longopt()
2445
2533
done)"
2446
2534
[[ ${COMPREPLY-} == *= ]] && compopt -o nospace
2447
2535
elif [[ $1 == *@(rmdir|chroot) ]]; then
2448
- _filedir -d
2536
+ _comp_compgen -a filedir -d
2449
2537
else
2450
2538
[[ $1 == *mkdir ]] && compopt -o nospace
2451
- _filedir
2539
+ _comp_compgen -a filedir
2452
2540
fi
2453
2541
}
2454
2542
# makeinfo and texi2dvi are defined elsewhere.
@@ -2468,7 +2556,7 @@ _filedir_xspec()
2468
2556
local cur prev words cword comp_args
2469
2557
_comp_initialize -- " $@ " || return
2470
2558
2471
- _tilde " $cur " || return
2559
+ _comp_compgen_tilde && return
2472
2560
2473
2561
local ret
2474
2562
_comp_quote_compgen " $cur "
0 commit comments