267 lines
6.3 KiB
Bash
Executable file
267 lines
6.3 KiB
Bash
Executable file
#!/bin/sh
|
|
|
|
set -eu
|
|
|
|
# we use the full path to realpath so that the coreutils version of realpath is
|
|
# used even when run with busybox in $PATH
|
|
realpath_m() {
|
|
REPLY=$(/usr/bin/realpath -m "$1")
|
|
}
|
|
|
|
realpath_m2() {
|
|
REPLY=$("$origpwd/realpath_m.sh" "$1")
|
|
}
|
|
|
|
assert() {
|
|
expected=$1
|
|
shift
|
|
if "$@" && [ "$REPLY" = "$expected" ]; then
|
|
numok=$((numok+1))
|
|
else
|
|
numfail=$((numfail+1))
|
|
echo "$@: expected '$expected', got '$REPLY'"
|
|
fi
|
|
}
|
|
|
|
compare() {
|
|
cmd=$1
|
|
shift
|
|
assert "$("$cmd" "$@")" realpath_"$cmd" "$@"
|
|
}
|
|
|
|
origpwd=$(pwd)
|
|
numokay=0
|
|
numfail=0
|
|
|
|
# realpath_dirname and realpath_basename should produce the same results as their operating system counterparts.
|
|
|
|
. "./realpath.sh"
|
|
|
|
for p1 in '' / // . ..; do
|
|
for p2 in '' /; do
|
|
for p3 in foo . ..; do
|
|
for p4 in '' /bar /. /..; do
|
|
for p5 in '' / // /// /. /..; do
|
|
compare basename "$p1$p2$p3$p4$p5"
|
|
compare dirname "$p1$p2$p3$p4$p5"
|
|
done
|
|
done
|
|
done
|
|
done
|
|
done
|
|
|
|
## realpath_absolute
|
|
|
|
# Outputs PWD with no args, joins relative args to PWD, removes empty parts, and
|
|
# canonicalizes ./..:
|
|
|
|
assert "$PWD" realpath_absolute
|
|
assert "$PWD" realpath_absolute .
|
|
assert "$PWD/x" realpath_absolute x
|
|
assert "$PWD/x/y" realpath_absolute x y
|
|
assert "$PWD/x/y" realpath_absolute x/z ../y
|
|
assert "$PWD/x/y/z" realpath_absolute x y//z
|
|
assert "$PWD/x/y/z" realpath_absolute x y '' z
|
|
assert "$PWD/x/y/z" realpath_absolute x y '' z/.
|
|
assert "$PWD/x/y/z" realpath_absolute x y '' z/. q .. r .. .. z
|
|
|
|
# Ignores arguments to the left of an absolute path:
|
|
|
|
assert /etc realpath_absolute /etc
|
|
assert /etc/z/xq realpath_absolute x y z /etc/z q/../xq
|
|
|
|
# Preserves double (but not triple+) slashes at the beginning of an absolute
|
|
# path:
|
|
|
|
assert //server/share/blah realpath_absolute //server share blah/
|
|
assert /server/share/blah realpath_absolute ///server/share blah
|
|
assert /server/share/blah realpath_absolute ////server/share blah
|
|
|
|
## realpath_relative path dir
|
|
|
|
# Outputs the relative path from dir to path:
|
|
|
|
assert y realpath_relative y .
|
|
assert y realpath_relative x/y x
|
|
assert ../y realpath_relative y x
|
|
assert ../y/z realpath_relative a/y/z a/x
|
|
assert ../../y realpath_relative y a/x
|
|
assert ../../../y realpath_relative y a/b/x
|
|
|
|
# Avoids redundancy for common path elements:
|
|
|
|
assert . realpath_relative y y
|
|
assert .. realpath_relative y y/x
|
|
assert ../.. realpath_relative y y/z/x
|
|
assert ../../.. realpath_relative /y /y/z/x/a
|
|
|
|
# Produces the same outputs as python's os.path.relpath:
|
|
|
|
relative() { /usr/bin/python3 -c 'import sys,os.path; sys.stdout.write(os.path.relpath(*sys.argv[1:3]))' "$@"; }
|
|
compare relative /q /q/r/s
|
|
compare relative /q/r /q/r/s
|
|
compare relative /q/r/s /q/r/s
|
|
compare relative /q/r/s/t /q/r/s
|
|
compare relative /q/r/s/t/u /q/r/s
|
|
compare relative /q/r/t /q/r/s
|
|
compare relative /q/r/t/u /q/r/s
|
|
compare relative /q/t /q/r/s
|
|
compare relative /q/t/u /q/r/s
|
|
compare relative /t/u/v /q/r/s
|
|
|
|
## realpath_follow
|
|
|
|
# Returns first non-symlink:
|
|
|
|
assert "x" realpath_follow x
|
|
ln -s y x
|
|
assert "y" realpath_follow x
|
|
ln -s z y
|
|
assert "z" realpath_follow x
|
|
|
|
# or last symlink before recursion sets in:
|
|
|
|
ln -s x z
|
|
assert "z" realpath_follow x
|
|
assert "y" realpath_follow z
|
|
assert "x" realpath_follow y
|
|
|
|
# and relative-pathed symlinks are normalized to the symlink directory
|
|
|
|
mkdir q
|
|
assert "q/../z" realpath_follow q/../x
|
|
rm x y z; rmdir q
|
|
|
|
## realpath_canonical
|
|
|
|
# Current directory:
|
|
|
|
for cmd in realpath_canonical realpath_m realpath_m2; do
|
|
assert "$PWD" "$cmd" .
|
|
assert "$PWD" "$cmd" "$PWD"
|
|
done
|
|
|
|
# Nonexistent file:
|
|
|
|
for cmd in realpath_canonical realpath_m realpath_m2; do
|
|
assert "$PWD/x" "$cmd" x
|
|
assert "$PWD/x" "$cmd" "$PWD/x"
|
|
done
|
|
|
|
# Symlink to non-existent target:
|
|
|
|
ln -s y x
|
|
for cmd in realpath_canonical realpath_m realpath_m2; do
|
|
assert "$PWD/y" "$cmd" x
|
|
assert "$PWD/y" "$cmd" "$PWD/x"
|
|
done
|
|
|
|
|
|
# Symlink chain:
|
|
|
|
ln -s z y
|
|
for cmd in realpath_canonical realpath_m realpath_m2; do
|
|
assert "$PWD/z" "$cmd" x
|
|
assert "$PWD/z" "$cmd" "$PWD/x"
|
|
done
|
|
|
|
|
|
# Symlink loop breaks on the looper:
|
|
|
|
ln -s y z
|
|
# difference to realpath -m: returned path is the target
|
|
assert "$PWD/z" realpath_canonical x
|
|
assert "$PWD/y" realpath_m x
|
|
assert "$PWD/z" realpath_m2 x
|
|
rm x y z
|
|
|
|
|
|
# Absolute links and directories:
|
|
|
|
touch y
|
|
mkdir z
|
|
ln -s $PWD/y x
|
|
ln -s $PWD/x z/x
|
|
|
|
for cmd in realpath_canonical realpath_m realpath_m2; do
|
|
assert "$PWD/y" "$cmd" x
|
|
assert "$PWD/y" "$cmd" z/x
|
|
done
|
|
|
|
ln -sf ../x z/x
|
|
for cmd in realpath_canonical realpath_m realpath_m2; do
|
|
assert "$PWD/y" "$cmd" x
|
|
assert "$PWD/y" "$cmd" z/x
|
|
done
|
|
|
|
ln -s z q
|
|
for cmd in realpath_canonical realpath_m realpath_m2; do
|
|
assert "$PWD/z" "$cmd" q
|
|
assert "$PWD/y" "$cmd" q/../x
|
|
assert "$PWD/z/a" "$cmd" q/a
|
|
done
|
|
|
|
## realpath_location
|
|
|
|
# Returns the absolute (but not canonical) location of the directory physically
|
|
# containing its target. Is equivalent to `realpath_absolute` if target isn't
|
|
# a symlink:
|
|
|
|
assert "$PWD" realpath_location x
|
|
assert "$PWD" realpath_location z
|
|
assert "$PWD/z" realpath_location z/a
|
|
assert "$PWD" realpath_location q/x # -> ../x
|
|
assert "$PWD/q" realpath_location q/foo
|
|
assert "$PWD" realpath_location q/../foo
|
|
assert "$PWD/q/foo" realpath_location q/foo/bar
|
|
|
|
### Missing elements
|
|
|
|
for cmd in realpath_canonical realpath_m realpath_m2; do
|
|
assert "$PWD/z/foo/bar/baz" "$cmd" q/foo/bar/baz
|
|
assert "$PWD/z/foo/bar" "$cmd" q/foo/bar
|
|
assert /non-existent "$cmd" /non-existent
|
|
assert /etc/non-existent "$cmd" /etc/non-existent
|
|
done
|
|
|
|
### Subdirectories, accessible and inaccessible
|
|
|
|
# Works while the directory is accesible:
|
|
|
|
mkdir foo
|
|
cd foo
|
|
for cmd in realpath_canonical realpath_m realpath_m2; do
|
|
assert "$OLDPWD/z" "$cmd" "$OLDPWD/q"
|
|
assert "$OLDPWD/y" "$cmd" "$OLDPWD/q/x"
|
|
assert "$OLDPWD/y" "$cmd" "../q/x"
|
|
done
|
|
|
|
# realpath -m cannot operate on inaccessible $PWD
|
|
# We can follow symlinks even if $PWD is inaccessible:
|
|
|
|
rmdir ../foo
|
|
for cmd in realpath_canonical; do
|
|
assert "$OLDPWD/z" "$cmd" "$OLDPWD/q"
|
|
assert "$OLDPWD/y" "$cmd" "../q/x"
|
|
done
|
|
|
|
# And we can do non-symlink canonicalizing on the base dir of the results:
|
|
|
|
for cmd in realpath_canonical; do
|
|
assert "$OLDPWD/z" "$cmd" "../z"
|
|
assert "$OLDPWD/z/foo" "$cmd" "../z/foo"
|
|
assert "$OLDPWD/foo" "$cmd" "../foo"
|
|
assert "$OLDPWD/z" "$cmd" "../q"
|
|
done
|
|
|
|
cd ..
|
|
|
|
rm x y q z/x
|
|
rmdir z
|
|
|
|
echo "number of successes: $numok"
|
|
echo "number of failures: $numfail"
|
|
|
|
if [ "$numfail" -gt 0 ]; then
|
|
exit 1
|
|
fi
|