Returns the relative path needed to go from self to dest.

assert "/foo/bar".relpath("/foo/baz") == "../baz"
assert "/foo/bar".relpath("/baz/bar") == "../../baz/bar"

If self or dest is relative, they are considered relatively to getcwd.

In some cases, the result is still independent of the current directory:

assert "foo/bar".relpath("..") == "../../.."

In other cases, parts of the current directory may be exhibited:

var p = "../foo/bar".relpath("baz")
var c = getcwd.basename
assert p == "../../{c}/baz"

For path resolution independent of the current directory (eg. for paths in URL), or to use an other starting directory than the current directory, just force absolute paths:

var start = "/a/b/c/d"
var p2 = (start/"../foo/bar").relpath(start/"baz")
assert p2 == "../../d/baz"

Neither self or dest has to be real paths or to exist in directories since the resolution is only done with string manipulations and without any access to the underlying file system.

If self and dest are the same directory, the empty string is returned:

assert "foo".relpath("foo") == ""
assert "foo/../bar".relpath("bar") == ""

The empty string and "." designate both the current directory:

assert "".relpath("foo/bar")  == "foo/bar"
assert ".".relpath("foo/bar") == "foo/bar"
assert "foo/bar".relpath("")  == "../.."
assert "/" + "/".relpath(".") == getcwd

Property definitions

core :: file $ Text :: relpath
	# Returns the relative path needed to go from `self` to `dest`.
	#
	#     assert "/foo/bar".relpath("/foo/baz") == "../baz"
	#     assert "/foo/bar".relpath("/baz/bar") == "../../baz/bar"
	#
	# If `self` or `dest` is relative, they are considered relatively to `getcwd`.
	#
	# In some cases, the result is still independent of the current directory:
	#
	#     assert "foo/bar".relpath("..") == "../../.."
	#
	# In other cases, parts of the current directory may be exhibited:
	#
	#     var p = "../foo/bar".relpath("baz")
	#     var c = getcwd.basename
	#     assert p == "../../{c}/baz"
	#
	# For path resolution independent of the current directory (eg. for paths in URL),
	# or to use an other starting directory than the current directory,
	# just force absolute paths:
	#
	#     var start = "/a/b/c/d"
	#     var p2 = (start/"../foo/bar").relpath(start/"baz")
	#     assert p2 == "../../d/baz"
	#
	#
	# Neither `self` or `dest` has to be real paths or to exist in directories since
	# the resolution is only done with string manipulations and without any access to
	# the underlying file system.
	#
	# If `self` and `dest` are the same directory, the empty string is returned:
	#
	#     assert "foo".relpath("foo") == ""
	#     assert "foo/../bar".relpath("bar") == ""
	#
	# The empty string and "." designate both the current directory:
	#
	#     assert "".relpath("foo/bar")  == "foo/bar"
	#     assert ".".relpath("foo/bar") == "foo/bar"
	#     assert "foo/bar".relpath("")  == "../.."
	#     assert "/" + "/".relpath(".") == getcwd
	fun relpath(dest: String): String
	do
		# TODO windows support
		var cwd = getcwd
		var from = (cwd/self).simplify_path.split("/")
		if from.last.is_empty then from.pop # case for the root directory
		var to = (cwd/dest).simplify_path.split("/")
		if to.last.is_empty then to.pop # case for the root directory

		# Remove common prefixes
		while not from.is_empty and not to.is_empty and from.first == to.first do
			from.shift
			to.shift
		end

		# Result is going up in `from` with ".." then going down following `to`
		var from_len = from.length
		if from_len == 0 then return to.join("/")
		var up = "../"*(from_len-1) + ".."
		if to.is_empty then return up
		var res = up + "/" + to.join("/")
		return res
	end
lib/core/file.nit:1159,2--1222,4