@@ -1713,6 +1713,25 @@ _fstypes()
1713
1713
[[ $fss ]] && _comp_compgen -a COMPREPLY -W " $fss " -- " $cur "
1714
1714
}
1715
1715
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
+
1716
1735
# Get real command.
1717
1736
# Command is the filename of command in PATH with possible symlinks resolved
1718
1737
# (if resolve tooling available), empty string if command not found.
@@ -1731,16 +1750,7 @@ _comp_realcommand()
1731
1750
elif type -p readlink > /dev/null; then
1732
1751
ret=$( readlink -f " $file " )
1733
1752
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 "
1744
1754
fi
1745
1755
}
1746
1756
@@ -2526,9 +2536,24 @@ complete -F _minimal ''
2526
2536
2527
2537
__load_completion()
2528
2538
{
2529
- local cmd=" $ {1##*/ }" dir compfile
2539
+ local cmd=$1 cmdname= $ {1##*/ } dir compfile
2530
2540
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
2532
2557
2533
2558
local -a dirs=()
2534
2559
@@ -2553,68 +2578,75 @@ __load_completion()
2553
2578
dirs+=(./completions)
2554
2579
fi
2555
2580
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%/* } " )
2557
2585
local ret
2558
- _comp_realcommand " $1 " && paths=(" ${ret%/* } " ) || paths=( )
2586
+ _comp_realcommand " $cmd " && paths+ =(" ${ret%/* } " )
2559
2587
_comp_split -aF : paths " $PATH "
2560
2588
for dir in " ${paths[@]%/ } " ; do
2561
- if [[ -d $dir && $dir == ?*/@(bin|sbin) ]]; then
2589
+ [[ $dir == ?*/@(bin|sbin) ]] &&
2562
2590
dirs+=(" ${dir%/* } /share/bash-completion/completions" )
2563
- fi
2564
2591
done
2565
2592
2566
2593
# 4) From XDG_DATA_DIRS or system dirs (e.g. /usr/share, /usr/local/share):
2567
2594
# Completions in the system data dirs.
2568
2595
_comp_split -F : paths " ${XDG_DATA_DIRS:-/ usr/ local/ share:/ usr/ share} " &&
2569
2596
dirs+=(" ${paths[@]/%// bash-completion/ completions} " )
2570
2597
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.
2580
2600
local IFS=$' \t\n'
2581
2601
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
2595
2611
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
2596
2644
done
2597
2645
done
2598
2646
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
-
2615
2647
# 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
2618
2650
2619
2651
return 1
2620
2652
}
0 commit comments