On Saturday 11 September 2010 13:20:08, Andrew Coppin wrote:
> Suppose that I'm in foo/bar/box1/top and I want to access a file in
> foo/bar/box2/side. How do I construct the shortest possible relative
> path from one to the other?
> The filepath package doesn't seem to provide
> a function to do this. (The correct answer is obviously
> "../../box2/side" in this instance,
No. The shortest relative path might be "baz" or "../side" or ".".
Links make handling file paths fun. Not.
So in the presence of links, to be certain to have the shortest possible
relative path, you'd have to search more or less the entire file system.
And "../../box2/side" need not even be a path from "foo/bar/box1/top" to
"foo/bar/box2/side" (if top in foo/bar/box1 is a symbolic link to
foo/bar/box2, ".." might take you from /foo/bar/box1/top to foo/bar instead
of foo/bar/box1, I remember having encountered such behaviour, I think it
was on Solaris).
> but how do I compute that in general?)
>
Ignoring links and the problems they cause for this task and assuming a
common root for both paths,
import System.FilePath
dropCommonPrefix :: Eq a => [a] -> [a] -> ([a],[a])
dropCommonPrefix xxs@(x:xs) yys@(y:ys)
| x == y = dropCommonPrefix xs ys
dropCommonPrefix xs ys = (xs,ys)
shortRelativePath :: FilePath -> FilePath -> FilePath
shortRelativePath from to =
case dropCommonPrefix (splitDirectories from) (splitDirectories to) of
(stuff, path) -> joinPath (map (const "..") stuff ++ path)
you might want to normalise and/or canonicalise the paths before, a ".." in
`stuff' would screw up the results (and generally, a "baz/.." in either
path could be shortened [beware of links, of course]).