Skip to content

Commit ac68847

Browse files
authored
Merge pull request #923 from scop/refactor/comp-realcommand
refactor: rename `_realcommand` => `_comp_realcommand`
2 parents dbae115 + 42d8269 commit ac68847

File tree

6 files changed

+149
-43
lines changed

6 files changed

+149
-43
lines changed

bash_completion

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,23 +1660,34 @@ _fstypes()
16601660
}
16611661

16621662
# Get real command.
1663-
# - arg: $1 Command
1664-
# - stdout: Filename of command in PATH with possible symbolic links resolved.
1665-
# Empty string if command not found.
1666-
# - return: True (0) if command found, False (> 0) if not.
1667-
_realcommand()
1668-
{
1669-
type -P "$1" >/dev/null && {
1670-
if type -p realpath >/dev/null; then
1671-
realpath "$(type -P "$1")"
1672-
elif type -p greadlink >/dev/null; then
1673-
greadlink -f "$(type -P "$1")"
1674-
elif type -p readlink >/dev/null; then
1675-
readlink -f "$(type -P "$1")"
1676-
else
1677-
type -P "$1"
1663+
# Command is the filename of command in PATH with possible symlinks resolved
1664+
# (if resolve tooling available), empty string if command not found.
1665+
# @param $1 Command
1666+
# @var[out] ret Resulting string
1667+
# @return True (0) if command found, False (> 0) if not.
1668+
_comp_realcommand()
1669+
{
1670+
ret=""
1671+
local file
1672+
file=$(type -P "$1") || return $?
1673+
if type -p realpath >/dev/null; then
1674+
ret=$(realpath "$file")
1675+
elif type -p greadlink >/dev/null; then
1676+
ret=$(greadlink -f "$file")
1677+
elif type -p readlink >/dev/null; then
1678+
ret=$(readlink -f "$file")
1679+
else
1680+
ret=$file
1681+
if [[ $ret == */* ]]; then
1682+
if [[ $ret == ./* ]]; then
1683+
ret=$PWD/${file:2}
1684+
elif [[ $ret == ../* ]]; then
1685+
ret=$PWD/${file:3}
1686+
elif [[ $ret != /* ]]; then
1687+
ret=$PWD/$file
1688+
fi
16781689
fi
1679-
}
1690+
fi
16801691
}
16811692

16821693
# This function returns the first argument, excluding options
@@ -2515,8 +2526,8 @@ __load_completion()
25152526
fi
25162527
25172528
# 3) From bin directories extracted from $(realpath "$cmd") and PATH
2518-
dir=$(_realcommand "$1")
2519-
paths=("${dir%/*}")
2529+
local ret
2530+
_comp_realcommand "$1" && paths=("${ret%/*}") || paths=()
25202531
_comp_split -aF : paths "$PATH"
25212532
for dir in "${paths[@]%/}"; do
25222533
if [[ -d $dir && $dir == ?*/@(bin|sbin) ]]; then

bash_completion.d/000_bash_completion_compat.bash

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,19 @@ _get_pword()
165165
fi
166166
}
167167

168+
# Get real command.
169+
# @deprecated Use `_comp_realcommand` instead.
170+
# Note that `_comp_realcommand` stores the result in the variable `ret`
171+
# instead of writing it to stdout.
172+
_realcommand()
173+
{
174+
local ret
175+
_comp_realcommand "$1"
176+
local rc=$?
177+
printf "%s\n" "$ret"
178+
return $rc
179+
}
180+
168181
# @deprecated Use the variable `_comp_backup_glob` instead. This is the
169182
# backward-compatibility name.
170183
# shellcheck disable=SC2154 # defined in the main "bash_completion"

completions/gcc

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,31 +58,21 @@ _comp_cmd_gcc()
5858
} &&
5959
complete -F _comp_cmd_gcc gcc{,-5,-6,-7,-8} g++{,-5,-6,-7,-8} g77 g95 \
6060
gccgo{,-5,-6,-7,-8} gcj gfortran{,-5,-6,-7,-8} gpc &&
61+
_comp_cmd_gcc__setup_cmd()
6162
{
62-
if cc --version 2>/dev/null | command grep -q GCC ||
63-
[[ $(_realcommand cc) == *gcc* ]]; then
64-
complete -F _comp_cmd_gcc cc
63+
local ret
64+
_comp_realcommand "$1"
65+
if [[ $ret == *$2* ]] ||
66+
"$1" --version 2>/dev/null | command grep -q GCC; then
67+
complete -F _comp_cmd_gcc "$1"
6568
else
66-
complete -F _minimal cc
69+
complete -F _minimal "$1"
6770
fi
68-
if c++ --version 2>/dev/null | command grep -q GCC ||
69-
[[ $(_realcommand c++) == *g++* ]]; then
70-
complete -F _comp_cmd_gcc c++
71-
else
72-
complete -F _minimal c++
73-
fi
74-
if f77 --version 2>/dev/null | command grep -q GCC ||
75-
[[ $(_realcommand f77) == *gfortran* ]]; then
76-
complete -F _comp_cmd_gcc f77
77-
else
78-
complete -F _minimal f77
79-
fi
80-
if f95 --version 2>/dev/null | command grep -q GCC ||
81-
[[ $(_realcommand f95) == *gfortran* ]]; then
82-
complete -F _comp_cmd_gcc f95
83-
else
84-
complete -F _minimal f95
85-
fi
86-
}
71+
} &&
72+
_comp_cmd_gcc__setup_cmd cc gcc &&
73+
_comp_cmd_gcc__setup_cmd c++ g++ &&
74+
_comp_cmd_gcc__setup_cmd f77 gfortran &&
75+
_comp_cmd_gcc__setup_cmd f95 gfortran &&
76+
unset -f _comp_cmd_gcc__setup_cmd
8777

8878
# ex: filetype=sh

completions/vncviewer

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
_vncviewer_bootstrap()
44
{
5-
local fname
6-
case $(_realcommand vncviewer) in
5+
local fname ret
6+
_comp_realcommand vncviewer
7+
case $ret in
78
*xvnc4viewer) fname=_xvnc4viewer ;;
89
*tightvncviewer) fname=_tightvncviewer ;;
910
*) fname=_known_hosts ;;

test/t/unit/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ EXTRA_DIST = \
2222
test_unit_pnames.py \
2323
test_unit_quote.py \
2424
test_unit_quote_compgen.py \
25+
test_unit_realcommand.py \
2526
test_unit_split.py \
2627
test_unit_tilde.py \
2728
test_unit_unlocal.py \

test/t/unit/test_unit_realcommand.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import pytest
2+
3+
from conftest import assert_bash_exec, bash_env_saved
4+
5+
6+
@pytest.mark.bashcomp(
7+
cmd=None, cwd="shared", ignore_env=r"^\+declare -f __tester$"
8+
)
9+
class TestUnitRealCommand:
10+
@pytest.fixture
11+
def functions(self, bash):
12+
assert_bash_exec(
13+
bash,
14+
(
15+
"__tester() { "
16+
"local ret rc; "
17+
'_comp_realcommand "$1"; '
18+
"rc=$?; "
19+
'printf %s "$ret"; '
20+
"return $rc; "
21+
"}"
22+
),
23+
)
24+
25+
def test_non_pollution(self, bash):
26+
"""Test environment non-pollution, detected at teardown."""
27+
assert_bash_exec(
28+
bash,
29+
"foo() { local ret=; _comp_realcommand bar; }; foo; unset -f foo",
30+
want_output=None,
31+
)
32+
33+
def test_basename(self, bash, functions):
34+
with bash_env_saved(bash) as bash_env:
35+
bash_env.write_variable("PATH", "$PWD/bin:$PATH", quote=False)
36+
output = assert_bash_exec(
37+
bash,
38+
"__tester arp",
39+
want_output=True,
40+
want_newline=False,
41+
)
42+
assert output.strip().endswith("/shared/bin/arp")
43+
44+
def test_basename_nonexistent(self, bash, functions):
45+
filename = "non-existent-file-for-bash-completion-tests"
46+
skipif = "! type -P %s" % filename
47+
try:
48+
assert_bash_exec(bash, skipif, want_output=None)
49+
except AssertionError:
50+
pytest.skipif(skipif)
51+
output = assert_bash_exec(
52+
bash,
53+
"! __tester %s" % filename,
54+
want_output=False,
55+
)
56+
assert output.strip() == ""
57+
58+
def test_relative(self, bash, functions):
59+
output = assert_bash_exec(
60+
bash,
61+
"__tester bin/arp",
62+
want_output=True,
63+
want_newline=False,
64+
)
65+
assert output.strip().endswith("/shared/bin/arp")
66+
67+
def test_relative_nonexistent(self, bash, functions):
68+
output = assert_bash_exec(
69+
bash,
70+
"! __tester bin/non-existent",
71+
want_output=False,
72+
)
73+
assert output.strip() == ""
74+
75+
def test_absolute(self, bash, functions):
76+
output = assert_bash_exec(
77+
bash,
78+
'__tester "$PWD/bin/arp"',
79+
want_output=True,
80+
want_newline=False,
81+
)
82+
assert output.strip().endswith("/shared/bin/arp")
83+
84+
def test_absolute_nonexistent(self, bash, functions):
85+
output = assert_bash_exec(
86+
bash,
87+
'! __tester "$PWD/bin/non-existent"',
88+
want_output=False,
89+
)
90+
assert output.strip() == ""

0 commit comments

Comments
 (0)