{-# LANGUAGE CPP #-} {-# LANGUAGE TupleSections #-} module SensibleDir where import System.Directory #ifdef VERSION_directory_xdg import System.DirectoryXdg #endif import System.FilePath import System.IO import System.Info import System.Environment import Control.Arrow import Control.Applicative import Data.List runningInBuildDir :: IO Bool runningInBuildDir = any (=="build") . take 2 . reverse . splitDirectories . takeDirectory <$> getExecutablePath runningInCabalDir :: IO Bool runningInCabalDir = any (==".cabal") . take 2 . reverse . splitDirectories . takeDirectory <$> getExecutablePath runningInSysBin = do exedir <- splitDirectories . takeDirectory <$> getExecutablePath return $ case take 3 (drop 1 exedir) of ["bin"] -> True ["sbin"] -> True ["usr","bin"] -> True ["usr","sbin"] -> True _ -> False runningInLocalBin = do exedir <- splitDirectories . takeDirectory <$> getExecutablePath return $ case take 3 (drop 1 exedir) of ["usr","local","bin"] -> True ["usr","local","sbin"] -> True _ -> False -- scenarios data InstallScenario = PrefixInstall -- ^ installed in /opt or some other prefix, or running inside source folder | SystemInstall -- ^ installed system wide | LocalInstall -- ^ installed in /usr/local/ | UserInstall -- ^ installed for a given user | NotInstalled -- ^ not installed, built by cabal or make, running from dist folder deriving (Show,Enum,Eq) -- | careful, assumes unix-style FHS -- windows aware contributions welcome -- Detects the likely install scenario based on the path to the exectuable. detectLikelyInstall :: IO InstallScenario detectLikelyInstall = do bBuildDir <- fmap (,NotInstalled) runningInBuildDir bCabalDir <- fmap (,UserInstall) runningInCabalDir bLocalDir <- fmap (,LocalInstall) runningInLocalBin bSysDir <- fmap (,SystemInstall) runningInSysBin case dropWhile (not . fst) [bBuildDir,bCabalDir,bLocalDir,bSysDir] of (True,scenario):_ -> return scenario _ -> return PrefixInstall isUnix os = not $ "mingw" `isPrefixOf` os -- | Try to get the cache directory appropriate to the install scenario. -- On windows it just calls getAppUserDataDirectory and appends "cache". -- (windows behavior subject to change, contributions welcome) sensibleCacheDir suffix = do let ifunix x = if isUnix os then x else PrefixInstall install <- ifunix <$> detectLikelyInstall case install of PrefixInstall -> do if isUnix os then do dirs <- splitDirectories . takeDirectory <$> getExecutablePath homedir <- getHomeDirectory let rdir = reverse dirs case take 1 rdir of ["bin"] -> do let dir = foldl1 combine $ reverse (drop 1 rdir) if homedir == dir then getXdgDirectory XdgCache suffix else return (dir "cache" suffix) _ -> do let dir = foldl1 combine dirs if homedir == dir then getXdgDirectory XdgCache suffix else return (dir "cache" suffix) else getAppUserDataDirectory ("cache" suffix) SystemInstall -> return ("/var/cache" suffix) LocalInstall -> return ("/var/local/cache" suffix) UserInstall -> getXdgDirectory XdgCache suffix NotInstalled -> do dir <- splitDirectories . takeDirectory <$> getExecutablePath let rdir = reverse dir return . foldl1 combine $ reverse ("cache": drop 2 rdir) -- | like 'sensibleCacheDir', but creates it if needed sensibleCacheDirCreateIfMissing suffix = do dir <- sensibleCacheDir suffix createDirectoryIfMissing True dir return dir -- | Try to get something approximately equivalent to /var/lib -- but appropriate to install scenario.. sensibleVarLib suffix = do let ifunix x = if isUnix os then x else PrefixInstall install <- ifunix <$> detectLikelyInstall case install of PrefixInstall -> do if isUnix os then do dirs <- splitDirectories . takeDirectory <$> getExecutablePath homedir <- getHomeDirectory let rdir = reverse dirs case take 1 rdir of ["bin"] -> do let dir = foldl1 combine $ reverse (drop 1 rdir) if homedir == dir then getXdgDirectory XdgData suffix else return (dir "share" suffix) _ -> do let dir = foldl1 combine dirs if homedir == dir then getXdgDirectory XdgData suffix else return (dir "share" suffix) else getAppUserDataDirectory ("share" suffix) SystemInstall -> return ("/var/lib" suffix) LocalInstall -> return ("/var/lib" suffix) -- not using /var/local/lib because it should be easy to find UserInstall -> getXdgDirectory XdgCache suffix NotInstalled -> do dir <- splitDirectories . takeDirectory <$> getExecutablePath let rdir = reverse dir return . foldl1 combine $ reverse ("share": drop 2 rdir)