summaryrefslogtreecommitdiff
path: root/src/SensibleDir.hs
blob: 615150010ce866c3ce20b4d930b4a1e14258916b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
{-# 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