Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: source files using absolute paths for absolute BASH_SOURCE #1221

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 31 additions & 18 deletions bash_completion
Original file line number Diff line number Diff line change
Expand Up @@ -2181,15 +2181,22 @@ _comp_compgen_fstypes()
_comp_abspath()
{
REPLY=$1
case $REPLY in
/*) ;;
../*) REPLY=$PWD/${REPLY:3} ;;
*) REPLY=$PWD/$REPLY ;;
esac
while [[ $REPLY == */./* ]]; do
REPLY=${REPLY//\/.\//\/}
done
[[ $REPLY == /* ]] || REPLY=$PWD/$REPLY
REPLY=${REPLY//+(\/)/\/}
while true; do
# Process "." and "..". To avoid reducing "/../../ => /", we convert
# "/*/../" one by one. "/.." at the beginning is ignored. Then, /*/../
# in the middle is processed. Finally, /*/.. at the end is removed.
case $REPLY in
*/./*) REPLY=${REPLY//\/.\//\/} ;;
*/.) REPLY=${REPLY%/.} ;;
/..?(/*)) REPLY=${REPLY#/..} ;;
*/+([^/])/../*) REPLY=${REPLY/\/+([^\/])\/..\//\/} ;;
*/+([^/])/..) REPLY=${REPLY%/+([^/])/..} ;;
*) break ;;
esac
done
[[ $REPLY ]] || REPLY=/
}

# Get real command.
Expand Down Expand Up @@ -3125,6 +3132,18 @@ _comp_complete_minimal()
# https://lists.gnu.org/archive/html/bug-bash/2012-01/msg00045.html
complete -F _comp_complete_minimal ''

# Initialize the variable "_comp__base_directory"
# @var[out] _comp__base_directory
_comp__init_base_directory()
{
local REPLY
_comp_abspath "${BASH_SOURCE[0]-./bash_completion}"
_comp__base_directory=${REPLY%/*}
[[ $_comp__base_directory ]] || _comp__base_directory=/
unset -f "$FUNCNAME"
}
_comp__init_base_directory

# @since 2.12
_comp_load()
{
Expand Down Expand Up @@ -3177,11 +3196,7 @@ _comp_load()
# we want to prefer in-tree completions over ones possibly coming with a
# system installed bash-completion. (Due to usual install layouts, this
# often hits the correct completions in system installations, too.)
if [[ $BASH_SOURCE == */* ]]; then
dirs+=("${BASH_SOURCE%/*}/completions")
else
dirs+=(./completions)
fi
dirs+=("$_comp__base_directory/completions")

# 3) From bin directories extracted from the specified path to the command,
# the real path to the command, and $PATH
Expand Down Expand Up @@ -3323,12 +3338,10 @@ _comp__init_collect_startup_configs()
# run-in-place-from-git-clone setups. Notably we do it after the
# system location here, in order to prefer in-tree variables and
# functions.
if [[ ${base_path%/*} == */share/bash-completion ]]; then
compat_dir=${base_path%/share/bash-completion/*}/etc/bash_completion.d
elif [[ $base_path == */* ]]; then
compat_dir="${base_path%/*}/bash_completion.d"
if [[ $_comp__base_directory == */share/bash-completion ]]; then
compat_dir=${_comp__base_directory%/share/bash-completion}/etc/bash_completion.d
else
compat_dir=./bash_completion.d
compat_dir=$_comp__base_directory/bash_completion.d
fi
[[ ${compat_dirs[0]} == "$compat_dir" ]] ||
compat_dirs+=("$compat_dir")
Expand Down
130 changes: 128 additions & 2 deletions test/t/unit/test_unit_abspath.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_relative(self, bash, functions):
)
assert output.strip().endswith("/shared/foo/bar")

def test_cwd(self, bash, functions):
def test_cwd1(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester ./foo/./bar",
Expand All @@ -55,7 +55,34 @@ def test_cwd(self, bash, functions):
)
assert output.strip().endswith("/shared/foo/bar")

def test_parent(self, bash, functions):
def test_cwd2(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /.",
want_output=True,
want_newline=False,
)
assert output.strip() == "/"

def test_cwd3(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /foo/.",
want_output=True,
want_newline=False,
)
assert output.strip() == "/foo"

def test_cwd4(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /././.",
want_output=True,
want_newline=False,
)
assert output.strip() == "/"

def test_parent1(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester ../shared/foo/bar",
Expand All @@ -65,3 +92,102 @@ def test_parent(self, bash, functions):
assert output.strip().endswith(
"/shared/foo/bar"
) and not output.strip().endswith("../shared/foo/bar")

def test_parent2(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /foo/..",
want_output=True,
want_newline=False,
)
assert output.strip() == "/"

def test_parent3(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /..",
want_output=True,
want_newline=False,
)
assert output.strip() == "/"

def test_parent4(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /../foo/bar",
want_output=True,
want_newline=False,
)
assert output.strip() == "/foo/bar"

def test_parent5(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /../../foo/bar",
want_output=True,
want_newline=False,
)
assert output.strip() == "/foo/bar"

def test_parent6(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /foo/../bar",
want_output=True,
want_newline=False,
)
assert output.strip() == "/bar"

def test_parent7(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /foo/../../bar",
want_output=True,
want_newline=False,
)
assert output.strip() == "/bar"

def test_parent8(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /dir1/dir2/dir3/../dir4/../../foo",
want_output=True,
want_newline=False,
)
assert output.strip() == "/dir1/foo"

def test_parent9(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester //dir1/dir2///../foo",
want_output=True,
want_newline=False,
)
assert output.strip() == "/dir1/foo"

def test_parent10(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /dir1/dir2/dir3/..",
want_output=True,
want_newline=False,
)
assert output.strip() == "/dir1/dir2"

def test_parent11(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /dir1/dir2/dir3/../..",
want_output=True,
want_newline=False,
)
assert output.strip() == "/dir1"

def test_parent12(self, bash, functions):
output = assert_bash_exec(
bash,
"__tester /dir1/dir2/dir3/../../../..",
want_output=True,
want_newline=False,
)
assert output.strip() == "/"