1 -----------------------------------------------------------------------------
3 -- Module : System.Directory
4 -- Copyright : (c) The University of Glasgow 2001
5 -- License : BSD-style (see the file libraries/core/LICENSE)
7 -- Maintainer : libraries@haskell.org
8 -- Stability : provisional
9 -- Portability : portable
11 -- $Id: Directory.hsc,v 1.1 2001/08/17 12:45:27 simonmar Exp $
13 -- System-independent interface to directory manipulation.
15 -----------------------------------------------------------------------------
18 A directory contains a series of entries, each of which is a named
19 reference to a file system object (file, directory etc.). Some
20 entries may be hidden, inaccessible, or have some administrative
21 function (e.g. "." or ".." under POSIX), but in this standard all such
22 entries are considered to form part of the directory contents.
23 Entries in sub-directories are not, however, considered to form part
24 of the directory contents.
26 Each file system object is referenced by a {\em path}. There is
27 normally at least one absolute path to each file system object. In
28 some operating systems, it may also be possible to have paths which
29 are relative to the current directory.
32 module System.Directory
36 readable, -- :: Permissions -> Bool
37 writable, -- :: Permissions -> Bool
38 executable, -- :: Permissions -> Bool
39 searchable -- :: Permissions -> Bool
42 , createDirectory -- :: FilePath -> IO ()
43 , removeDirectory -- :: FilePath -> IO ()
44 , renameDirectory -- :: FilePath -> FilePath -> IO ()
46 , getDirectoryContents -- :: FilePath -> IO [FilePath]
47 , getCurrentDirectory -- :: IO FilePath
48 , setCurrentDirectory -- :: FilePath -> IO ()
50 , removeFile -- :: FilePath -> IO ()
51 , renameFile -- :: FilePath -> FilePath -> IO ()
53 , doesFileExist -- :: FilePath -> IO Bool
54 , doesDirectoryExist -- :: FilePath -> IO Bool
56 , getPermissions -- :: FilePath -> IO Permissions
57 , setPermissions -- :: FilePath -> Permissions -> IO ()
59 , getModificationTime -- :: FilePath -> IO ClockTime
64 import System.Time ( ClockTime(..) )
69 #ifdef __GLASGOW_HASKELL__
71 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
79 -----------------------------------------------------------------------------
82 -- The Permissions type is used to record whether certain
83 -- operations are permissible on a file/directory:
84 -- [to whom? - presumably the "current user"]
89 executable, searchable :: Bool
90 } deriving (Eq, Ord, Read, Show)
92 -----------------------------------------------------------------------------
95 -- `createDirectory dir' creates a new directory dir which is
96 -- initially empty, or as near to empty as the operating system
99 -- The operation may fail with:
103 \item @isPermissionError@ / @PermissionDenied@
104 The process has insufficient privileges to perform the operation.
106 \item @isAlreadyExistsError@ / @AlreadyExists@
107 The operand refers to a directory that already exists.
109 \item @HardwareFault@
110 A physical I/O error has occurred.
112 \item @InvalidArgument@
113 The operand is not a valid directory name.
114 @[ENAMETOOLONG, ELOOP]@
116 There is no path to the directory.
118 \item @ResourceExhausted@
119 Insufficient resources (virtual memory, process file descriptors,
120 physical disk space, etc.) are available to perform the operation.
121 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
122 \item @InappropriateType@
123 The path refers to an existing non-directory object.
128 createDirectory :: FilePath -> IO ()
129 createDirectory path = do
130 withCString path $ \s -> do
131 throwErrnoIfMinus1Retry_ "createDirectory" $
132 #if defined(mingw32_TARGET_OS)
139 @removeDirectory dir@ removes an existing directory {\em dir}. The
140 implementation may specify additional constraints which must be
141 satisfied before a directory can be removed (e.g. the directory has to
142 be empty, or may not be in use by other processes). It is not legal
143 for an implementation to partially remove a directory unless the
144 entire directory is removed. A conformant implementation need not
145 support directory removal in all situations (e.g. removal of the root
148 The operation may fail with:
150 \item @HardwareFault@
151 A physical I/O error has occurred.
153 \item @InvalidArgument@
154 The operand is not a valid directory name.
155 @[ENAMETOOLONG, ELOOP]@
156 \item @isDoesNotExist@ / @NoSuchThing@
157 The directory does not exist.
159 \item @isPermissionError@ / @PermissionDenied@
160 The process has insufficient privileges to perform the operation.
161 @[EROFS, EACCES, EPERM]@
162 \item @UnsatisfiedConstraints@
163 Implementation-dependent constraints are not satisfied.
164 @[EBUSY, ENOTEMPTY, EEXIST]@
165 \item @UnsupportedOperation@
166 The implementation does not support removal in this situation.
168 \item @InappropriateType@
169 The operand refers to an existing non-directory object.
174 removeDirectory :: FilePath -> IO ()
175 removeDirectory path = do
176 withCString path $ \s ->
177 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
180 @Removefile file@ removes the directory entry for an existing file
181 {\em file}, where {\em file} is not itself a directory. The
182 implementation may specify additional constraints which must be
183 satisfied before a file can be removed (e.g. the file may not be in
184 use by other processes).
186 The operation may fail with:
188 \item @HardwareFault@
189 A physical I/O error has occurred.
191 \item @InvalidArgument@
192 The operand is not a valid file name.
193 @[ENAMETOOLONG, ELOOP]@
194 \item @isDoesNotExist@ / @NoSuchThing@
195 The file does not exist.
197 \item @isPermissionError@ / @PermissionDenied@
198 The process has insufficient privileges to perform the operation.
199 @[EROFS, EACCES, EPERM]@
200 \item @UnsatisfiedConstraints@
201 Implementation-dependent constraints are not satisfied.
203 \item @InappropriateType@
204 The operand refers to an existing directory.
209 removeFile :: FilePath -> IO ()
211 withCString path $ \s ->
212 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
215 @renameDirectory@ {\em old} {\em new} changes the name of an existing
216 directory from {\em old} to {\em new}. If the {\em new} directory
217 already exists, it is atomically replaced by the {\em old} directory.
218 If the {\em new} directory is neither the {\em old} directory nor an
219 alias of the {\em old} directory, it is removed as if by
220 $removeDirectory$. A conformant implementation need not support
221 renaming directories in all situations (e.g. renaming to an existing
222 directory, or across different physical devices), but the constraints
225 The operation may fail with:
227 \item @HardwareFault@
228 A physical I/O error has occurred.
230 \item @InvalidArgument@
231 Either operand is not a valid directory name.
232 @[ENAMETOOLONG, ELOOP]@
233 \item @isDoesNotExistError@ / @NoSuchThing@
234 The original directory does not exist, or there is no path to the target.
236 \item @isPermissionError@ / @PermissionDenied@
237 The process has insufficient privileges to perform the operation.
238 @[EROFS, EACCES, EPERM]@
239 \item @ResourceExhausted@
240 Insufficient resources are available to perform the operation.
241 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
242 \item @UnsatisfiedConstraints@
243 Implementation-dependent constraints are not satisfied.
244 @[EBUSY, ENOTEMPTY, EEXIST]@
245 \item @UnsupportedOperation@
246 The implementation does not support renaming in this situation.
248 \item @InappropriateType@
249 Either path refers to an existing non-directory object.
254 renameDirectory :: FilePath -> FilePath -> IO ()
255 renameDirectory opath npath =
256 withFileStatus opath $ \st -> do
257 is_dir <- isDirectory st
259 then ioException (IOError Nothing InappropriateType "renameDirectory"
260 ("not a directory") (Just opath))
263 withCString opath $ \s1 ->
264 withCString npath $ \s2 ->
265 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
268 @renameFile@ {\em old} {\em new} changes the name of an existing file system
269 object from {\em old} to {\em new}. If the {\em new} object already
270 exists, it is atomically replaced by the {\em old} object. Neither
271 path may refer to an existing directory. A conformant implementation
272 need not support renaming files in all situations (e.g. renaming
273 across different physical devices), but the constraints must be
276 The operation may fail with:
278 \item @HardwareFault@
279 A physical I/O error has occurred.
281 \item @InvalidArgument@
282 Either operand is not a valid file name.
283 @[ENAMETOOLONG, ELOOP]@
284 \item @isDoesNotExistError@ / @NoSuchThing@
285 The original file does not exist, or there is no path to the target.
287 \item @isPermissionError@ / @PermissionDenied@
288 The process has insufficient privileges to perform the operation.
289 @[EROFS, EACCES, EPERM]@
290 \item @ResourceExhausted@
291 Insufficient resources are available to perform the operation.
292 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
293 \item @UnsatisfiedConstraints@
294 Implementation-dependent constraints are not satisfied.
296 \item @UnsupportedOperation@
297 The implementation does not support renaming in this situation.
299 \item @InappropriateType@
300 Either path refers to an existing directory.
301 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
305 renameFile :: FilePath -> FilePath -> IO ()
306 renameFile opath npath =
307 withFileStatus opath $ \st -> do
308 is_dir <- isDirectory st
310 then ioException (IOError Nothing InappropriateType "renameFile"
311 "is a directory" (Just opath))
314 withCString opath $ \s1 ->
315 withCString npath $ \s2 ->
316 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
319 @getDirectoryContents dir@ returns a list of {\em all} entries
322 The operation may fail with:
324 \item @HardwareFault@
325 A physical I/O error has occurred.
327 \item @InvalidArgument@
328 The operand is not a valid directory name.
329 @[ENAMETOOLONG, ELOOP]@
330 \item @isDoesNotExistError@ / @NoSuchThing@
331 The directory does not exist.
333 \item @isPermissionError@ / @PermissionDenied@
334 The process has insufficient privileges to perform the operation.
336 \item @ResourceExhausted@
337 Insufficient resources are available to perform the operation.
339 \item @InappropriateType@
340 The path refers to an existing non-directory object.
345 getDirectoryContents :: FilePath -> IO [FilePath]
346 getDirectoryContents path = do
347 p <- withCString path $ \s ->
348 throwErrnoIfNullRetry "getDirectoryContents" (c_opendir s)
351 loop :: Ptr CDir -> IO [String]
357 #ifdef mingw32_TARGET_OS
358 entryp <- (#peek struct dirent,d_name) p
359 entry <- peekCString entryp -- on mingwin it's a char *, not a char []
361 entry <- peekCString ((#ptr struct dirent,d_name) p)
364 return (entry:entries)
365 else do errno <- getErrno
366 if (errno == eINTR) then loop dir else do
367 throwErrnoIfMinus1_ "getDirectoryContents" $ c_closedir dir
368 #ifdef mingw32_TARGET_OS
369 if (errno == eNOENT) -- mingwin (20001111) cunningly sets errno to ENOENT when it runs out of files
374 else throwErrno "getDirectoryContents"
377 If the operating system has a notion of current directories,
378 @getCurrentDirectory@ returns an absolute path to the
379 current directory of the calling process.
381 The operation may fail with:
383 \item @HardwareFault@
384 A physical I/O error has occurred.
386 \item @isDoesNotExistError@ / @NoSuchThing@
387 There is no path referring to the current directory.
388 @[EPERM, ENOENT, ESTALE...]@
389 \item @isPermissionError@ / @PermissionDenied@
390 The process has insufficient privileges to perform the operation.
392 \item @ResourceExhausted@
393 Insufficient resources are available to perform the operation.
394 \item @UnsupportedOperation@
395 The operating system has no notion of current directory.
399 getCurrentDirectory :: IO FilePath
400 getCurrentDirectory = do
401 p <- mallocBytes (#const PATH_MAX)
402 go p (#const PATH_MAX)
403 where go p bytes = do
404 p' <- c_getcwd p (fromIntegral bytes)
406 then do s <- peekCString p'
409 else do errno <- getErrno
411 then do let bytes' = bytes * 2
412 p' <- reallocBytes p bytes'
414 else throwErrno "getCurrentDirectory"
417 If the operating system has a notion of current directories,
418 @setCurrentDirectory dir@ changes the current
419 directory of the calling process to {\em dir}.
421 The operation may fail with:
423 \item @HardwareFault@
424 A physical I/O error has occurred.
426 \item @InvalidArgument@
427 The operand is not a valid directory name.
428 @[ENAMETOOLONG, ELOOP]@
429 \item @isDoesNotExistError@ / @NoSuchThing@
430 The directory does not exist.
432 \item @isPermissionError@ / @PermissionDenied@
433 The process has insufficient privileges to perform the operation.
435 \item @UnsupportedOperation@
436 The operating system has no notion of current directory, or the
437 current directory cannot be dynamically changed.
438 \item @InappropriateType@
439 The path refers to an existing non-directory object.
444 setCurrentDirectory :: FilePath -> IO ()
445 setCurrentDirectory path = do
446 withCString path $ \s ->
447 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
448 -- ToDo: add path to error
451 To clarify, @doesDirectoryExist@ returns True if a file system object
452 exist, and it's a directory. @doesFileExist@ returns True if the file
453 system object exist, but it's not a directory (i.e., for every other
454 file system object that is not a directory.)
457 doesDirectoryExist :: FilePath -> IO Bool
458 doesDirectoryExist name =
460 (withFileStatus name $ \st -> isDirectory st)
461 (\ _ -> return False)
463 doesFileExist :: FilePath -> IO Bool
464 doesFileExist name = do
466 (withFileStatus name $ \st -> do b <- isDirectory st; return (not b))
467 (\ _ -> return False)
469 getModificationTime :: FilePath -> IO ClockTime
470 getModificationTime name =
471 withFileStatus name $ \ st ->
474 getPermissions :: FilePath -> IO Permissions
475 getPermissions name = do
476 withCString name $ \s -> do
477 read <- c_access s (#const R_OK)
478 write <- c_access s (#const W_OK)
479 exec <- c_access s (#const X_OK)
480 withFileStatus name $ \st -> do
481 is_dir <- isDirectory st
482 is_reg <- isRegularFile st
485 readable = read == 0,
486 writable = write == 0,
487 executable = not is_dir && exec == 0,
488 searchable = not is_reg && exec == 0
492 setPermissions :: FilePath -> Permissions -> IO ()
493 setPermissions name (Permissions r w e s) = do
495 read = if r then (#const S_IRUSR) else emptyCMode
496 write = if w then (#const S_IWUSR) else emptyCMode
497 exec = if e || s then (#const S_IXUSR) else emptyCMode
499 mode = read `unionCMode` (write `unionCMode` exec)
501 withCString name $ \s ->
502 throwErrnoIfMinus1_ "setPermissions" $ c_chmod s mode
504 withFileStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
505 withFileStatus name f = do
506 allocaBytes (#const sizeof(struct stat)) $ \p ->
507 withCString name $ \s -> do
508 throwErrnoIfMinus1Retry_ "withFileStatus" (c_stat s p)
511 modificationTime :: Ptr CStat -> IO ClockTime
512 modificationTime stat = do
513 mtime <- (#peek struct stat, st_mtime) stat
514 return (TOD (toInteger (mtime :: CTime)) 0)
516 isDirectory :: Ptr CStat -> IO Bool
517 isDirectory stat = do
518 mode <- (#peek struct stat, st_mode) stat
519 return (s_ISDIR mode /= 0)
521 isRegularFile :: Ptr CStat -> IO Bool
522 isRegularFile stat = do
523 mode <- (#peek struct stat, st_mode) stat
524 return (s_ISREG mode /= 0)
526 foreign import ccall unsafe s_ISDIR :: CMode -> Int
527 #def inline HsInt s_ISDIR(m) {return S_ISDIR(m);}
529 foreign import ccall unsafe s_ISREG :: CMode -> Int
530 #def inline HsInt s_ISREG(m) {return S_ISREG(m);}
535 unionCMode :: CMode -> CMode -> CMode