@@ -397,6 +397,23 @@ _comp_split()
397397    (( _new_size >  _old_size)) 
398398}
399399
400+ #  Helper function for _comp_compgen
401+ #  @var[in] $?
402+ #  @var[in] _var
403+ #  @var[in] _append
404+ #  @return original $?
405+ _comp_compgen__error_fallback ()
406+ {
407+     local  _status=$? 
408+     if  [[ $_append  ]];  then 
409+         #  make sure existence of variable
410+         eval  -- " $_var +=()" 
411+     else 
412+         eval  -- " $_var =()" 
413+     fi 
414+     return  " $_status " 
415+ }
416+ 
400417#  Provide a common interface to generate completion candidates in COMPREPLY or
401418#  in a specified array.
402419#  OPTIONS
@@ -414,6 +431,7 @@ _comp_split()
414431#    -c cur  Set a word used as a prefix to filter the completions.  The default
415432#            is ${cur-}.
416433#    -R      The same as -c ''.  Use raw outputs without filtering.
434+ #    -C dir  Evaluate compgen/generator in the specified directory.
417435#  @var[in,opt] cur  Used as the default value of a prefix to filter the
418436#    completions.
419437# 
@@ -437,10 +455,10 @@ _comp_split()
437455#    as `-v arr` as a part of the `_comp_compgen` options.
438456# 
439457#  Usage #2: _comp_compgen [-alR|-v arr|-c cur] name args...
440- #  Call `_comp_compgen_NAME ARGS...` with the specified options.  This provides 
441- #  a common interface to call the functions `_comp_compgen_NAME`, which produce 
442- #  completion candidates, with custom options [-alR|-v arr|-c cur].  The option 
443- #  `-F sep` is not used with this usage.
458+ #  Call the generator  `_comp_compgen_NAME ARGS...` with the specified options.
459+ #  This provides  a common interface to call the functions `_comp_compgen_NAME`,
460+ #  which produce  completion candidates, with custom options [-alR|-v arr|-c
461+ #  cur].  The option  `-F sep` is not used with this usage.
444462#  @param $1... name args  Calls the function _comp_compgen_NAME with the
445463#    specified ARGS (if $1 does not start with a hyphen `-`).  The options
446464#    [-alR|-v arr|-c cur] are inherited by the child calls of `_comp_compgen`
@@ -484,10 +502,15 @@ _comp_compgen()
484502    local  _append=${_comp_compgen__append-} 
485503    local  _var=${_comp_compgen__var-COMPREPLY} 
486504    local  _cur=${_comp_compgen__cur-${cur-} } 
487-     local  _ifs=$'  \t\n ' 
505+     local  _ifs=$'  \t\n '  _dir= " " 
488506
507+     local  _old_nocasematch=" " 
508+     if  shopt  -q nocasematch;  then 
509+         _old_nocasematch=set
510+         shopt  -u nocasematch
511+     fi 
489512    local  OPTIND=1 OPTARG=" " 
490-     while  getopts  ' :alF:v:Rc:' " $@ " ;  do 
513+     while  getopts  ' :alF:v:Rc:C: ' " $@ " ;  do 
491514        case  $_opt  in 
492515            a) _append=set ;;
493516            v)
@@ -501,12 +524,20 @@ _comp_compgen()
501524            F) _ifs=$OPTARG  ;;
502525            c) _cur=$OPTARG  ;;
503526            R) _cur=" " 
527+             C)
528+                 if  [[ !  $OPTARG  ]];  then 
529+                     printf  ' bash_completion: %s: -C: invalid directory name `%s' \' ' .\n' " $FUNCNAME " " $OPTARG " >&2 
530+                     return  2
531+                 fi 
532+                 _dir=$OPTARG 
533+                 ;;
504534            * )
505535                printf  ' bash_completion: %s: usage error\n' " $FUNCNAME " >&2 
506536                return  2
507537                ;;
508538        esac 
509539    done 
540+     [[ $_old_nocasematch  ]] &&  shopt  -s nocasematch
510541    shift  " $(( OPTIND -  1 )) " 
511542    if  (( $#  ==  0 )) ;  then 
512543        printf  ' bash_completion: %s: unexpected number of arguments.\n' " $FUNCNAME " >&2 
@@ -521,14 +552,35 @@ _comp_compgen()
521552            return  2
522553        fi 
523554
555+         if  [[ $_dir  ]];  then 
556+             local  _original_pwd=$PWD 
557+             local  PWD=${PWD-}  OLDPWD=${OLDPWD-} 
558+             #  Note: We also redirect stdout because `cd` may output the target
559+             #  directory to stdout when CDPATH is set.
560+             command  cd  -- " $_dir " & > /dev/null || 
561+                 {
562+                     _comp_compgen__error_fallback
563+                     return 
564+                 }
565+         fi 
566+ 
524567        local  _comp_compgen__append=$_append 
525568        local  _comp_compgen__var=$_var 
526569        local  _comp_compgen__cur=$_cur  cur=$_cur 
527570        #  Note: we use $1 as a part of a function name, and we use $2... as
528571        #  arguments to the function if any.
529572        #  shellcheck disable=SC2145
530573        _comp_compgen_" $@ " 
531-         return 
574+         local  _status=$? 
575+ 
576+         #  Go back to the original directory.
577+         #  Note: Failure of this line results in the change of the current
578+         #  directory visible to the user.  We intentionally do not redirect
579+         #  stderr so that the error message appear in the terminal.
580+         #  shellcheck disable=SC2164
581+         [[ $_dir  ]] &&  command  cd  -- " $_original_pwd " 
582+ 
583+         return  " $_status " 
532584    fi 
533585
534586    #  usage: _comp_compgen [options] -- [compgen_options]
@@ -550,16 +602,15 @@ _comp_compgen()
550602
551603    local  _result
552604    _result=$( 
605+         if  [[ $_dir  ]];  then  
606+             #  Note: We also redirect stdout because `cd` may output the target 
607+             #  directory to stdout when CDPATH is set. 
608+             command  cd  -- " $_dir " & > /dev/null ||  return  
609+         fi  
553610        IFS=$_ifs  compgen  " $@ " ${_cur: +-- " $_cur "   
554611    )   ||  {
555-         local  _status=$? 
556-         if  [[ $_append  ]];  then 
557-             #  make sure existence of variable
558-             eval  -- " $_var +=()" 
559-         else 
560-             eval  -- " $_var =()" 
561-         fi 
562-         return  " $_status " 
612+         _comp_compgen__error_fallback
613+         return 
563614    }
564615
565616    _comp_split -l ${_append: +-a}  " $_var " " $_result " 
0 commit comments