You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

81 lines
1.7 KiB
Bash

#!/bin/sh
set -eu
realpath_canonical() {
# resolve symlink (if there is one) and use $@ to detect symlink loops
while [ -L "$1" ] && target=$(readlink -- "$1"); do
REPLY=$(dirname "$1")
# Resolve relative to symlink's directory
if [ "$REPLY" = "." ]; then
REPLY=$target
else
case "$target" in
/*)
REPLY=$target;;
*)
REPLY=$REPLY/$target;;
esac
fi
# Break out if we found a symlink loop
for target in "$@"; do [ "$REPLY" = "$target" ] && break 2; done
# Add to the loop-detect list and tail-recurse
set -- "$REPLY" "$@"
done
# recurse unless root
REPLY=$(dirname "$1")
if [ "$REPLY" != "/" ] && [ "$REPLY" != "." ]; then
REPLY=$(realpath_canonical "$REPLY");
fi
# set $1 to dirname and $2 to basename
set -- "$REPLY" "$(basename "$1")"
# combine canon parent w/basename
REPLY=$PWD
while [ $# -gt 0 ]; do
# handle case of component starting with double slash and no
# other slashes separately because it cannot be solved by
# globbing
if echo "$1" | grep -E '^//[^/]*$' >/dev/null 2>&1; then
REPLY=//
first=$(echo "$1" | cut -c3-)
shift
set -- "$first" "$@"
continue
fi
# all other cases can be expressed by globbing
case $1 in
/*)
REPLY=/
first=$(echo "$1" | sed -e 's/^\/\+//')
shift
set -- "$first" "$@"
;;
*/*)
front=${1%%/*}
back=$(echo "$1" | sed -e "s/^$front\\/\\+//")
shift
set -- "$front" "$back" "$@"
;;
''|.)
shift
;;
..)
REPLY=$(dirname "$REPLY")
shift
;;
*)
REPLY="${REPLY%/}/$1"
shift
;;
esac
done
echo "$REPLY"
}
if [ $# -ne 1 ]; then
echo "usage: $0 path"
exit 1
fi
realpath_canonical "$1"