{-# 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 -- | Returns True if "build" occurs as any component of the path of the running executable. runningInBuildDir :: IO Bool runningInBuildDir = any (=="build") . take 2 . reverse . splitDirectories . takeDirectory <$> getExecutablePath -- | Returns True if ".cabal" occurs as any component of the path of the running executable. runningInCabalDir :: IO Bool runningInCabalDir = any (==".cabal") . take 2 . reverse . splitDirectories . takeDirectory <$> getExecutablePath -- | Returns True if running executable is in /bin, /sbin, or /usr/bin, or /usr/sbin. 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 -- | Returns True if executable is within /usr/local/bin or /usr/local/sbin. 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 :: FilePath -> IO FilePath 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 :: FilePath -> IO FilePath 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 :: FilePath -> IO FilePath 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) -- | Try to get something approximately equivalent to /etc -- but appropriate to install scenario.. sensibleConfig :: FilePath -> IO FilePath sensibleConfig 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 "config" suffix) _ -> do let dir = foldl1 combine dirs if homedir == dir then getXdgDirectory XdgData suffix else return (dir "config" suffix) else getAppUserDataDirectory ("config" suffix) SystemInstall -> return ("/etc" suffix) LocalInstall -> return ("/etc" suffix) UserInstall -> getXdgDirectory XdgConfig suffix NotInstalled -> do dir <- splitDirectories . takeDirectory <$> getExecutablePath let rdir = reverse dir return . foldl1 combine $ reverse ("config": drop 2 rdir)