module System.CanonicalizePath
( canonicalizePath
, normalisePath
) where
#ifdef mingw32_HOST_OS
import qualified System.Win32 as Win32
#endif
import Control.Applicative
import Control.Monad
import Data.List.Split (splitOn)
import System.FilePath ((</>), isAbsolute, takeDirectory, pathSeparator)
import System.Directory (getCurrentDirectory)
import System.Posix.Files (readSymbolicLink)
normalisePath :: FilePath -> IO FilePath
normalisePath path = do
absPath <- makeAbsolute path
return $ foldl combinePath "/" $ splitPath absPath
canonicalizePath :: FilePath -> IO FilePath
canonicalizePath path = do
#if !defined(mingw32_HOST_OS)
absPath <- makeAbsolute path
foldM (\x y -> expandSym $ combinePath x y) "/" $ splitPath absPath
#else
Win32.getFullPathName . normalise
#endif
expandSym :: FilePath -> IO FilePath
expandSym fpath = do
deref <- catch (Just <$> readSymbolicLink fpath) (\_ -> return Nothing)
case deref of
Just slink -> if isAbsolute slink then expandSym slink
else expandSym $ foldl combinePath (takeDirectory fpath) $ splitPath slink
Nothing -> return fpath
makeAbsolute :: FilePath -> IO FilePath
makeAbsolute f
| not (null f) && head f `elem` ['~', pathSeparator] = return f
| otherwise = fmap (</> f) getCurrentDirectory
combinePath :: FilePath -> String -> FilePath
combinePath x "." = x
combinePath x ".." = takeDirectory x
combinePath x y = x </> y
splitPath :: FilePath -> [String]
splitPath = filter (not . null) . splitOn [pathSeparator]