Fix more inconsistent behaviour with symlinks in paths.

Both redo and minimal/do were doing slightly weird things with
symlinked directories, especially when combined with "..".  For
example, if x is a link to ., then x/x/x/x/../y should resolve to
"../y", which is quite non-obvious.

Added some tests to make sure this stays fixed.
This commit is contained in:
Avery Pennarun 2018-12-17 15:58:06 +00:00
commit 686c381109
13 changed files with 121 additions and 8 deletions

View file

@ -204,6 +204,38 @@ _normpath()
)
# Prints a "real" path, with all symlinks resolved where possible.
_realpath()
{
local path="$1" relto="$2" isabs= rest=
if _startswith "$path" "/"; then
isabs=1
else
path="${relto%/}/$path"
fi
(
for d in $(seq 100); do
#echo "Trying: $PWD--$path" >&2
if cd -P "$path" 2>/dev/null; then
# success
pwd=$(/bin/pwd)
#echo " chdir ok: $pwd--$rest" >&2
np=$(_normpath "${pwd%/}/$rest" "$relto")
if [ -n "$isabs" ]; then
echo "$np"
else
_relpath "$np" "$relto"
fi
break
fi
_dirsplit "${path%/}"
path=$_dirsplit_dir
rest="$_dirsplit_base/$rest"
done
)
}
# List the possible names for default*.do files in dir $1 matching the target
# pattern in $2. We stop searching when we find the first one that exists.
_find_dofiles_pwd()
@ -241,7 +273,7 @@ _find_dofiles()
[ -n "$dodir" ] && dodir=${dodir%/}/
#echo "_find_dofiles: '$dodir' '$dofile'" >&2
_find_dofiles_pwd "$dodir" "$dofile" && return 0
newdir=$(_normpath "${dodir}.." "$PWD")
newdir=$(_realpath "${dodir}.." "$PWD")
[ "$newdir" = "$dodir" ] && break
dodir=$newdir
done
@ -369,7 +401,7 @@ _redo()
i=$(_abspath "$i" "$startdir")
(
cd "$DO_STARTDIR" || return 99
i=$(_normpath "$(_relpath "$i" "$PWD")" "$PWD")
i=$(_realpath "$(_relpath "$i" "$PWD")" "$PWD")
_dirsplit "$i"
dir=$_dirsplit_dir base=$_dirsplit_base
_do "$dir" "$base"

View file

@ -130,6 +130,17 @@ check ".." _normpath ../ "$x"
check ".." _normpath .. "$x"
SECTION _realpath
rm -rf y
mkdir y
ln -s . y/x
check "/usr/__does_not/b" _realpath "/usr/__does_not/a/../b" "$x"
check "foo" _realpath "y/x/x/x/x/x/../foo" "$PWD"
check "$(/bin/pwd)/foo" _realpath "$PWD/y/x/x/x/x/x/../foo" "$PWD"
check "foo/blam" _realpath "y/x/x/x/x/x/../foo/spam/../blam" "$PWD"
check "$(/bin/pwd)/foo/blam" _realpath "$PWD/y/x/x/../foo/spam/../blam" "$PWD"
SECTION _find_dofile
check "test.do" _find_dofiles test
check "test.do" _find_dofile test