@@ -1713,6 +1713,25 @@ _fstypes()
17131713 [[ $fss ]] && _comp_compgen -a COMPREPLY -W " $fss " -- " $cur "
17141714}
17151715
1716+ # Get absolute path to a file, with rudimentary canonicalization.
1717+ # No symlink resolution or existence checks are done;
1718+ # see `_comp_realcommand` for those.
1719+ # @param $1 The file
1720+ # @var[out] ret The path
1721+ _comp_abspath ()
1722+ {
1723+ ret=$1
1724+ case $ret in
1725+ /* ) ;;
1726+ ../* ) ret=$PWD /${ret: 3} ;;
1727+ * ) ret=$PWD /$ret ;;
1728+ esac
1729+ while [[ $ret == * /./* ]]; do
1730+ ret=${ret// \/ .\/ / \/ }
1731+ done
1732+ ret=${ret// +(\/ )/ \/ }
1733+ }
1734+
17161735# Get real command.
17171736# Command is the filename of command in PATH with possible symlinks resolved
17181737# (if resolve tooling available), empty string if command not found.
@@ -1731,16 +1750,7 @@ _comp_realcommand()
17311750 elif type -p readlink > /dev/null; then
17321751 ret=$( readlink -f " $file " )
17331752 else
1734- ret=$file
1735- if [[ $ret == * /* ]]; then
1736- if [[ $ret == ./* ]]; then
1737- ret=$PWD /${file: 2}
1738- elif [[ $ret == ../* ]]; then
1739- ret=$PWD /${file: 3}
1740- elif [[ $ret != /* ]]; then
1741- ret=$PWD /$file
1742- fi
1743- fi
1753+ _comp_abspath " $file "
17441754 fi
17451755}
17461756
@@ -2526,9 +2536,24 @@ complete -F _minimal ''
25262536
25272537__load_completion()
25282538{
2529- local cmd=" $ {1##*/ }" dir compfile
2539+ local cmd=$1 cmdname= $ {1##*/ } dir compfile
25302540 local -a paths
2531- [[ $cmd ]] || return 1
2541+ [[ $cmdname ]] || return 1
2542+
2543+ local backslash=
2544+ if [[ $cmd == \\ * ]]; then
2545+ cmd=${cmd: 1}
2546+ # If we already have a completion for the " real" command, use it
2547+ $( complete -p " $cmd " 2> /dev/null || echo false) " \\ $cmd " && return 0
2548+ backslash=\\
2549+ fi
2550+
2551+ # Resolve absolute path to $cmd
2552+ local ret pathcmd origcmd=$cmd
2553+ if pathcmd=$( type -P " $cmd " ) ; then
2554+ _comp_abspath " $pathcmd "
2555+ cmd=$ret
2556+ fi
25322557
25332558 local -a dirs=()
25342559
@@ -2553,68 +2578,75 @@ __load_completion()
25532578 dirs+=(./completions)
25542579 fi
25552580
2556- # 3) From bin directories extracted from $( realpath " $cmd " ) and PATH
2581+ # 3) From bin directories extracted from the specified path to the command,
2582+ # the real path to the command, and $PATH
2583+ paths=()
2584+ [[ $cmd == /* ]] && paths+=(" ${cmd%/* } " )
25572585 local ret
2558- _comp_realcommand " $1 " && paths=(" ${ret%/* } " ) || paths=( )
2586+ _comp_realcommand " $cmd " && paths+ =(" ${ret%/* } " )
25592587 _comp_split -aF : paths " $PATH "
25602588 for dir in " ${paths[@]%/ } " ; do
2561- if [[ -d $dir && $dir == ?*/@(bin|sbin) ]]; then
2589+ [[ $dir == ?*/@(bin|sbin) ]] &&
25622590 dirs+=(" ${dir%/* } /share/bash-completion/completions" )
2563- fi
25642591 done
25652592
25662593 # 4) From XDG_DATA_DIRS or system dirs (e.g. /usr/share, /usr/local/share):
25672594 # Completions in the system data dirs.
25682595 _comp_split -F : paths " ${XDG_DATA_DIRS:-/ usr/ local/ share:/ usr/ share} " &&
25692596 dirs+=(" ${paths[@]/%// bash-completion/ completions} " )
25702597
2571- local backslash=
2572- if [[ $cmd == \\ * ]]; then
2573- cmd=${cmd: 1}
2574- # If we already have a completion for the " real" command, use it
2575- $( complete -p " $cmd " 2> /dev/null || echo false) " \\ $cmd " && return 0
2576- backslash=\\
2577- fi
2578-
2579- # For loading 3rd party completions wrapped in shopt reset
2598+ # Set up default $IFS in case loaded completions depend on it,
2599+ # as well as for $compspec invocation below.
25802600 local IFS=$' \t\n'
25812601
2582- for dir in " ${dirs[@]} " ; do
2583- [[ -d $dir ]] || continue
2584- for compfile in " $cmd " " $cmd .bash" ; do
2585- compfile=" $dir /$compfile "
2586- # Avoid trying to source dirs as long as we support bash < 4.3
2587- # to avoid an fd leak; https://bugzilla.redhat.com/903540
2588- if [[ -d $compfile ]]; then
2589- # Do not warn with . or .. (especially the former is common)
2590- [[ $compfile == */.?(.) ]] ||
2591- echo " bash_completion: $compfile : is a directory" >&2
2592- elif [[ -e $compfile ]] && . " $compfile " ; then
2593- [[ $backslash ]] && $( complete -p " $cmd " ) " \\ $cmd "
2594- return 0
2602+ # Look up and source
2603+ shift
2604+ local i prefix compspec
2605+ for prefix in " " _; do # Regular from all dirs first, then fallbacks
2606+ for i in ${! dirs[*]} ; do
2607+ dir=${dirs[i]}
2608+ if [[ ! -d $dir ]]; then
2609+ unset -v 'dirs[i]'
2610+ continue
25952611 fi
2612+ for compfile in " $prefix$cmdname " " $prefix$cmdname .bash" ; do
2613+ compfile=" $dir /$compfile "
2614+ # Avoid trying to source dirs as long as we support bash < 4.3
2615+ # to avoid an fd leak; https://bugzilla.redhat.com/903540
2616+ if [[ -d $compfile ]]; then
2617+ # Do not warn with . or .. (especially the former is common)
2618+ [[ $compfile == */.?(.) ]] ||
2619+ echo " bash_completion: $compfile : is a directory" >&2
2620+ elif [[ -e $compfile ]] && . " $compfile " " $cmd " " $@ " ; then
2621+ # At least $cmd is expected to have a completion set when
2622+ # we return successfully; see if it already does
2623+ if compspec=$( complete -p " $cmd " 2> /dev/null) ; then
2624+ local -a extspecs=()
2625+ # $cmd is the case in which we do backslash processing
2626+ [[ $backslash ]] && extspecs+=(" $backslash$cmd " )
2627+ # If invoked without path, that one should be set, too
2628+ # ...but let's not overwrite an existing one, if any
2629+ [[ $origcmd != */* ]] &&
2630+ ! complete -p " $origcmd " &>/dev/null &&
2631+ extspecs+=(" $origcmd " )
2632+ ((${# extspecs[*]} != 0)) && $compspec " ${extspecs[@]} "
2633+ return 0
2634+ fi
2635+ # If not, see if we got one for $cmdname
2636+ if [[ $cmdname != " $cmd " ]] && compspec=$( complete -p " $cmdname " 2> /dev/null) ; then
2637+ # Use that for $cmd too, if we have a full path to it
2638+ [[ $cmd == /* ]] && $compspec " $cmd "
2639+ return 0
2640+ fi
2641+ # Nothing expected was set, continue lookup
2642+ fi
2643+ done
25962644 done
25972645 done
25982646
2599- # Search fallback completions named " _$cmd "
2600- for dir in " ${dirs[@]} " ; do
2601- [[ -d $dir ]] || continue
2602- compfile=" $dir /_$cmd "
2603- # Avoid trying to source dirs as long as we support bash < 4.3
2604- # to avoid an fd leak; https://bugzilla.redhat.com/903540
2605- if [[ -d $compfile ]]; then
2606- # Do not warn with . or .. (especially the former is common)
2607- [[ $compfile == */.?(.) ]] ||
2608- echo " bash_completion: $compfile : is a directory" >&2
2609- elif [[ -e $compfile ]] && . " $compfile " " $cmd " ; then
2610- [[ $backslash ]] && $( complete -p " $cmd " ) " \\ $cmd "
2611- return 0
2612- fi
2613- done
2614-
26152647 # Look up simple " xspec" completions
2616- [[ -v _xspecs[$cmd ] ]] &&
2617- complete -F _filedir_xspec " $cmd " " $backslash$cmd " && return 0
2648+ [[ -v _xspecs[$cmdname ] ]] &&
2649+ complete -F _filedir_xspec " $cmdname " " $backslash$cmdname " && return 0
26182650
26192651 return 1
26202652}
0 commit comments