| #!/bin/sh | |
| if [ "$1" = "--help" -o $# -ne 2 -o -f "$1" ]; then | |
| echo Usage: relpath.sh FROM TO | |
| echo Print the relative path from FROM to TO | |
| echo FROM must be a directory, but need not exist | |
| exit 0 | |
| fi | |
| from="${1%%/}" | |
| to="${2%%/}" | |
| # The parent directory of a pathname, handling .. | |
| parent() { | |
| name=$(basename "$1") | |
| path=$(dirname "$1") | |
| top=$(basename "$path") | |
| if [ "$top" = ".." ]; then | |
| path=$(parent "$path") | |
| fi | |
| if [ "$name" = ".." ]; then | |
| path=$(parent "$path") | |
| fi | |
| echo $path | |
| } | |
| # Canonicalize a directory that contains '..'. | |
| canonicalize() { | |
| path=$1 | |
| suffix= | |
| while ! [ -d "$path" ]; do | |
| name=$(basename "$path") | |
| path=$(parent "$path") | |
| suffix="/$name$suffix" | |
| done | |
| if [ -d "$path" ]; then | |
| echo $(cd "$path"; pwd)$suffix | |
| else | |
| echo $1 | |
| fi | |
| } | |
| case "$to$from" in | |
| *..* ) | |
| from=$(canonicalize "$from") | |
| to=$(canonicalize "$to") | |
| ;; | |
| esac | |
| case "$to$from" in | |
| *..* ) | |
| echo unable to canonicalize .. >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| back= | |
| while [ "${to#$from}" = "$to" ]; do | |
| #echo $from too long | |
| from=$(dirname $from); | |
| back=../$back | |
| if [ "$from" = / ] && [ "${to#/}" = "$to" ]; then | |
| echo no common ancestor between $1 and $2 >&2 | |
| exit 1 | |
| elif [ "$from" = . ]; then | |
| echo no common ancestor between $1 and $2 >&2 | |
| exit 1 | |
| fi | |
| done | |
| to=${to#$from} | |
| to=${to##/} | |
| back=${back%%/} | |
| if [ -n "$to" ] && [ -n "$back" ]; then | |
| echo $back/$to | |
| elif [ -n "$back$to" ]; then | |
| echo $back$to | |
| else | |
| echo . | |
| fi |