summaryrefslogtreecommitdiff
path: root/sensible-directory/src/SensibleDir.hs
blob: 0133736dc7c7903a78341d90b5b3acae627af814 (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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
{-# 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)