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)
|