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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
--- Library for accessing the directory structure of the
--- underlying operating system.
---
--- @author Michael Hanus
--- @version January 2013

module System.Directory
  ( doesFileExist, doesDirectoryExist, getFileSize, getModificationTime
  , getCurrentDirectory, setCurrentDirectory
  , getDirectoryContents, createDirectory, createDirectoryIfMissing
  , removeDirectory, renameDirectory
  , getHomeDirectory, getTemporaryDirectory
  , getAbsolutePath
  , removeFile, renameFile, copyFile
  , findFileWithSuffix, getFileWithSuffix
  ) where

import System.FilePath    ( FilePath, (</>), splitDirectories, isAbsolute
                          , normalise, pathSeparator, searchPathSeparator)
import System.Environment ( getEnv, isWindows )
import Data.List          ( isPrefixOf, scanl1, last, intersperse )
import Data.Time          ( ClockTime )


--- Returns true if the argument is the name of an existing file.
doesFileExist :: FilePath -> IO Bool
doesFileExist fname = prim_doesFileExist $## fname

prim_doesFileExist :: FilePath -> IO Bool
prim_doesFileExist external

--- Returns true if the argument is the name of an existing directory.
doesDirectoryExist :: FilePath -> IO Bool
doesDirectoryExist dir = prim_doesDirectoryExist $## dir

prim_doesDirectoryExist :: FilePath -> IO Bool
prim_doesDirectoryExist external

--- Returns the size of the file.
getFileSize :: FilePath -> IO Int
getFileSize fname = prim_fileSize $## fname

prim_fileSize :: FilePath -> IO Int
prim_fileSize external

--- Returns the modification time of the file.
getModificationTime :: FilePath -> IO ClockTime
getModificationTime fname = prim_getModificationTime $## fname

prim_getModificationTime :: FilePath -> IO ClockTime
prim_getModificationTime external

--- Returns the current working directory.
getCurrentDirectory :: IO FilePath
getCurrentDirectory external

--- Sets the current working directory.
setCurrentDirectory :: FilePath -> IO ()
setCurrentDirectory dir = prim_setCurrentDirectory $## dir

prim_setCurrentDirectory :: FilePath -> IO ()
prim_setCurrentDirectory external

--- Returns the list of all entries in a directory.
getDirectoryContents :: FilePath -> IO [FilePath]
getDirectoryContents dir = prim_getDirectoryContents $## dir

prim_getDirectoryContents :: FilePath -> IO [FilePath]
prim_getDirectoryContents external

--- Creates a new directory with the given name.
createDirectory :: FilePath -> IO ()
createDirectory dir = prim_createDirectory $## dir

prim_createDirectory :: FilePath -> IO ()
prim_createDirectory external

--- Creates a new directory with the given name if it does not already exist.
--- If the first parameter is `True` it will also create all missing
--- parent directories.
createDirectoryIfMissing :: Bool -> FilePath -> IO ()
createDirectoryIfMissing createParents path
  = if createParents then createDirs parents
                     else createDirs [last parents]
 where
  parents = scanl1 (</>) $ splitDirectories $ path

  createDirs []     = return ()
  createDirs (d:ds) = do
    exists <- doesDirectoryExist d
    if exists then return () else createDirectory d
    createDirs ds

--- Deletes a directory from the file system.
removeDirectory :: FilePath -> IO ()
removeDirectory dir = prim_removeDirectory $## dir

prim_removeDirectory :: FilePath -> IO ()
prim_removeDirectory external

--- Renames a directory.
renameDirectory :: FilePath -> FilePath -> IO ()
renameDirectory dir1 dir2 = (prim_renameDirectory $## dir1) $## dir2

prim_renameDirectory :: FilePath -> FilePath -> IO ()
prim_renameDirectory external

--- Returns the home directory of the current user.
getHomeDirectory :: IO FilePath
getHomeDirectory = if isWindows
                      then getEnv "USERPROFILE"
                      else getEnv "HOME"

--- Returns the temporary directory of the operating system.
getTemporaryDirectory :: IO FilePath
getTemporaryDirectory = if isWindows then getEnv "TMP" else return "/tmp"

--- Convert a path name into an absolute one.
--- For instance, a leading `~` is replaced by the current home directory.
getAbsolutePath :: FilePath -> IO FilePath
getAbsolutePath path
  | isAbsolute path        = return (normalise path)
  | path == "~"            = getHomeDirectory
  | "~/" `isPrefixOf` path = do homedir <- getHomeDirectory
                                return (normalise (homedir </> drop 2 path))
  | otherwise              = do curdir <- getCurrentDirectory
                                return (normalise (curdir </> path))

--- Deletes a file from the file system.
removeFile :: FilePath -> IO ()
removeFile file = prim_removeFile $## file

prim_removeFile :: FilePath -> IO ()
prim_removeFile external

--- Renames a file.
renameFile :: FilePath -> FilePath -> IO ()
renameFile file1 file2 = (prim_renameFile $## file1) $## file2

prim_renameFile :: FilePath -> FilePath -> IO ()
prim_renameFile external

--- Copy the contents from one file to another file
copyFile :: FilePath -> FilePath -> IO ()
copyFile src dest = readFile src >>= writeFile dest

--- Looks up the first file with a possible suffix in a list of directories.
--- Returns Nothing if such a file does not exist.
findFileWithSuffix :: String -> [String] -> [String] -> IO (Maybe String)
findFileWithSuffix file suffixes path =
  if isAbsolute file
  then lookupFirstFileWithSuffix file suffixes
  else lookupFirstFile path
 where
   lookupFirstFile [] = return Nothing
   lookupFirstFile (dir:dirs) = do
     mbfile <- lookupFirstFileWithSuffix (dir++pathSeparator:file) suffixes
     maybe (lookupFirstFile dirs) (return . Just) mbfile

   lookupFirstFileWithSuffix _ [] = return Nothing
   lookupFirstFileWithSuffix f (suf:sufs) = do
     let fsuf = f++suf
     exfile <- doesFileExist fsuf
     if exfile then return (Just fsuf)
               else lookupFirstFileWithSuffix f sufs

--- Gets the first file with a possible suffix in a list of directories.
--- An error message is delivered if there is no such file.
getFileWithSuffix :: String -> [String] -> [String] -> IO String
getFileWithSuffix file suffixes path = do
  mbfile <- findFileWithSuffix file suffixes path
  maybe (error $ "File "++file++" not found in path "++
                 concat (intersperse [searchPathSeparator] path))
        return
        mbfile