[Haskell-cafe] Relative files paths

Daniel Fischer daniel.is.fischer at web.de
Sat Sep 11 08:52:16 EDT 2010

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]). 

More information about the Haskell-Cafe mailing list