1 -----------------------------------------------------------------------------
3 -- Module : System.Directory
4 -- Copyright : (c) The University of Glasgow 2001
5 -- License : BSD-style (see the file libraries/base/LICENSE)
7 -- Maintainer : libraries@haskell.org
8 -- Stability : provisional
9 -- Portability : portable
11 -- System-independent interface to directory manipulation.
13 -----------------------------------------------------------------------------
16 A directory contains a series of entries, each of which is a named
17 reference to a file system object (file, directory etc.). Some
18 entries may be hidden, inaccessible, or have some administrative
19 function (e.g. "." or ".." under POSIX), but in this standard all such
20 entries are considered to form part of the directory contents.
21 Entries in sub-directories are not, however, considered to form part
22 of the directory contents.
24 Each file system object is referenced by a {\em path}. There is
25 normally at least one absolute path to each file system object. In
26 some operating systems, it may also be possible to have paths which
27 are relative to the current directory.
30 module System.Directory
34 readable, -- :: Permissions -> Bool
35 writable, -- :: Permissions -> Bool
36 executable, -- :: Permissions -> Bool
37 searchable -- :: Permissions -> Bool
40 , createDirectory -- :: FilePath -> IO ()
41 , removeDirectory -- :: FilePath -> IO ()
42 , renameDirectory -- :: FilePath -> FilePath -> IO ()
44 , getDirectoryContents -- :: FilePath -> IO [FilePath]
45 , getCurrentDirectory -- :: IO FilePath
46 , setCurrentDirectory -- :: FilePath -> IO ()
48 , removeFile -- :: FilePath -> IO ()
49 , renameFile -- :: FilePath -> FilePath -> IO ()
51 , doesFileExist -- :: FilePath -> IO Bool
52 , doesDirectoryExist -- :: FilePath -> IO Bool
54 , getPermissions -- :: FilePath -> IO Permissions
55 , setPermissions -- :: FilePath -> Permissions -> IO ()
57 , getModificationTime -- :: FilePath -> IO ClockTime
62 import System.Time ( ClockTime(..) )
67 #ifdef __GLASGOW_HASKELL__
69 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
72 -----------------------------------------------------------------------------
75 -- The Permissions type is used to record whether certain
76 -- operations are permissible on a file/directory:
77 -- [to whom? - presumably the "current user"]
82 executable, searchable :: Bool
83 } deriving (Eq, Ord, Read, Show)
85 -----------------------------------------------------------------------------
88 -- `createDirectory dir' creates a new directory dir which is
89 -- initially empty, or as near to empty as the operating system
92 -- The operation may fail with:
96 \item @isPermissionError@ / @PermissionDenied@
97 The process has insufficient privileges to perform the operation.
99 \item @isAlreadyExistsError@ / @AlreadyExists@
100 The operand refers to a directory that already exists.
102 \item @HardwareFault@
103 A physical I/O error has occurred.
105 \item @InvalidArgument@
106 The operand is not a valid directory name.
107 @[ENAMETOOLONG, ELOOP]@
109 There is no path to the directory.
111 \item @ResourceExhausted@
112 Insufficient resources (virtual memory, process file descriptors,
113 physical disk space, etc.) are available to perform the operation.
114 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
115 \item @InappropriateType@
116 The path refers to an existing non-directory object.
121 createDirectory :: FilePath -> IO ()
122 createDirectory path = do
123 withCString path $ \s -> do
124 throwErrnoIfMinus1Retry_ "createDirectory" $
128 @removeDirectory dir@ removes an existing directory {\em dir}. The
129 implementation may specify additional constraints which must be
130 satisfied before a directory can be removed (e.g. the directory has to
131 be empty, or may not be in use by other processes). It is not legal
132 for an implementation to partially remove a directory unless the
133 entire directory is removed. A conformant implementation need not
134 support directory removal in all situations (e.g. removal of the root
137 The operation may fail with:
139 \item @HardwareFault@
140 A physical I/O error has occurred.
142 \item @InvalidArgument@
143 The operand is not a valid directory name.
144 @[ENAMETOOLONG, ELOOP]@
145 \item @isDoesNotExist@ / @NoSuchThing@
146 The directory does not exist.
148 \item @isPermissionError@ / @PermissionDenied@
149 The process has insufficient privileges to perform the operation.
150 @[EROFS, EACCES, EPERM]@
151 \item @UnsatisfiedConstraints@
152 Implementation-dependent constraints are not satisfied.
153 @[EBUSY, ENOTEMPTY, EEXIST]@
154 \item @UnsupportedOperation@
155 The implementation does not support removal in this situation.
157 \item @InappropriateType@
158 The operand refers to an existing non-directory object.
163 removeDirectory :: FilePath -> IO ()
164 removeDirectory path = do
165 withCString path $ \s ->
166 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
169 @Removefile file@ removes the directory entry for an existing file
170 {\em file}, where {\em file} is not itself a directory. The
171 implementation may specify additional constraints which must be
172 satisfied before a file can be removed (e.g. the file may not be in
173 use by other processes).
175 The operation may fail with:
177 \item @HardwareFault@
178 A physical I/O error has occurred.
180 \item @InvalidArgument@
181 The operand is not a valid file name.
182 @[ENAMETOOLONG, ELOOP]@
183 \item @isDoesNotExist@ / @NoSuchThing@
184 The file does not exist.
186 \item @isPermissionError@ / @PermissionDenied@
187 The process has insufficient privileges to perform the operation.
188 @[EROFS, EACCES, EPERM]@
189 \item @UnsatisfiedConstraints@
190 Implementation-dependent constraints are not satisfied.
192 \item @InappropriateType@
193 The operand refers to an existing directory.
198 removeFile :: FilePath -> IO ()
200 withCString path $ \s ->
201 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
204 @renameDirectory@ {\em old} {\em new} changes the name of an existing
205 directory from {\em old} to {\em new}. If the {\em new} directory
206 already exists, it is atomically replaced by the {\em old} directory.
207 If the {\em new} directory is neither the {\em old} directory nor an
208 alias of the {\em old} directory, it is removed as if by
209 $removeDirectory$. A conformant implementation need not support
210 renaming directories in all situations (e.g. renaming to an existing
211 directory, or across different physical devices), but the constraints
214 The operation may fail with:
216 \item @HardwareFault@
217 A physical I/O error has occurred.
219 \item @InvalidArgument@
220 Either operand is not a valid directory name.
221 @[ENAMETOOLONG, ELOOP]@
222 \item @isDoesNotExistError@ / @NoSuchThing@
223 The original directory does not exist, or there is no path to the target.
225 \item @isPermissionError@ / @PermissionDenied@
226 The process has insufficient privileges to perform the operation.
227 @[EROFS, EACCES, EPERM]@
228 \item @ResourceExhausted@
229 Insufficient resources are available to perform the operation.
230 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
231 \item @UnsatisfiedConstraints@
232 Implementation-dependent constraints are not satisfied.
233 @[EBUSY, ENOTEMPTY, EEXIST]@
234 \item @UnsupportedOperation@
235 The implementation does not support renaming in this situation.
237 \item @InappropriateType@
238 Either path refers to an existing non-directory object.
243 renameDirectory :: FilePath -> FilePath -> IO ()
244 renameDirectory opath npath =
245 withFileStatus opath $ \st -> do
246 is_dir <- isDirectory st
248 then ioException (IOError Nothing InappropriateType "renameDirectory"
249 ("not a directory") (Just opath))
252 withCString opath $ \s1 ->
253 withCString npath $ \s2 ->
254 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
257 @renameFile@ {\em old} {\em new} changes the name of an existing file system
258 object from {\em old} to {\em new}. If the {\em new} object already
259 exists, it is atomically replaced by the {\em old} object. Neither
260 path may refer to an existing directory. A conformant implementation
261 need not support renaming files in all situations (e.g. renaming
262 across different physical devices), but the constraints must be
265 The operation may fail with:
267 \item @HardwareFault@
268 A physical I/O error has occurred.
270 \item @InvalidArgument@
271 Either operand is not a valid file name.
272 @[ENAMETOOLONG, ELOOP]@
273 \item @isDoesNotExistError@ / @NoSuchThing@
274 The original file does not exist, or there is no path to the target.
276 \item @isPermissionError@ / @PermissionDenied@
277 The process has insufficient privileges to perform the operation.
278 @[EROFS, EACCES, EPERM]@
279 \item @ResourceExhausted@
280 Insufficient resources are available to perform the operation.
281 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
282 \item @UnsatisfiedConstraints@
283 Implementation-dependent constraints are not satisfied.
285 \item @UnsupportedOperation@
286 The implementation does not support renaming in this situation.
288 \item @InappropriateType@
289 Either path refers to an existing directory.
290 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
294 renameFile :: FilePath -> FilePath -> IO ()
295 renameFile opath npath =
296 withFileOrSymlinkStatus opath $ \st -> do
297 is_dir <- isDirectory st
299 then ioException (IOError Nothing InappropriateType "renameFile"
300 "is a directory" (Just opath))
303 withCString opath $ \s1 ->
304 withCString npath $ \s2 ->
305 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
308 @getDirectoryContents dir@ returns a list of {\em all} entries
311 The operation may fail with:
313 \item @HardwareFault@
314 A physical I/O error has occurred.
316 \item @InvalidArgument@
317 The operand is not a valid directory name.
318 @[ENAMETOOLONG, ELOOP]@
319 \item @isDoesNotExistError@ / @NoSuchThing@
320 The directory does not exist.
322 \item @isPermissionError@ / @PermissionDenied@
323 The process has insufficient privileges to perform the operation.
325 \item @ResourceExhausted@
326 Insufficient resources are available to perform the operation.
328 \item @InappropriateType@
329 The path refers to an existing non-directory object.
334 getDirectoryContents :: FilePath -> IO [FilePath]
335 getDirectoryContents path = do
336 alloca $ \ ptr_dEnt -> do
337 p <- withCString path $ \s ->
338 throwErrnoIfNullRetry "getDirectoryContents" (c_opendir s)
341 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
342 loop ptr_dEnt dir = do
344 r <- readdir dir ptr_dEnt
347 dEnt <- peek ptr_dEnt
351 entry <- (d_name dEnt >>= peekCString)
353 entries <- loop ptr_dEnt dir
354 return (entry:entries)
355 else do errno <- getErrno
356 if (errno == eINTR) then loop ptr_dEnt dir else do
357 throwErrnoIfMinus1_ "getDirectoryContents" $ c_closedir dir
358 let (Errno eo) = errno
359 if (eo == end_of_dir)
361 else throwErrno "getDirectoryContents"
366 If the operating system has a notion of current directories,
367 @getCurrentDirectory@ returns an absolute path to the
368 current directory of the calling process.
370 The operation may fail with:
372 \item @HardwareFault@
373 A physical I/O error has occurred.
375 \item @isDoesNotExistError@ / @NoSuchThing@
376 There is no path referring to the current directory.
377 @[EPERM, ENOENT, ESTALE...]@
378 \item @isPermissionError@ / @PermissionDenied@
379 The process has insufficient privileges to perform the operation.
381 \item @ResourceExhausted@
382 Insufficient resources are available to perform the operation.
383 \item @UnsupportedOperation@
384 The operating system has no notion of current directory.
388 getCurrentDirectory :: IO FilePath
389 getCurrentDirectory = do
390 p <- mallocBytes path_max
392 where go p bytes = do
393 p' <- c_getcwd p (fromIntegral bytes)
395 then do s <- peekCString p'
398 else do errno <- getErrno
400 then do let bytes' = bytes * 2
401 p' <- reallocBytes p bytes'
403 else throwErrno "getCurrentDirectory"
406 If the operating system has a notion of current directories,
407 @setCurrentDirectory dir@ changes the current
408 directory of the calling process to {\em dir}.
410 The operation may fail with:
412 \item @HardwareFault@
413 A physical I/O error has occurred.
415 \item @InvalidArgument@
416 The operand is not a valid directory name.
417 @[ENAMETOOLONG, ELOOP]@
418 \item @isDoesNotExistError@ / @NoSuchThing@
419 The directory does not exist.
421 \item @isPermissionError@ / @PermissionDenied@
422 The process has insufficient privileges to perform the operation.
424 \item @UnsupportedOperation@
425 The operating system has no notion of current directory, or the
426 current directory cannot be dynamically changed.
427 \item @InappropriateType@
428 The path refers to an existing non-directory object.
433 setCurrentDirectory :: FilePath -> IO ()
434 setCurrentDirectory path = do
435 withCString path $ \s ->
436 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
437 -- ToDo: add path to error
440 To clarify, @doesDirectoryExist@ returns True if a file system object
441 exist, and it's a directory. @doesFileExist@ returns True if the file
442 system object exist, but it's not a directory (i.e., for every other
443 file system object that is not a directory.)
446 doesDirectoryExist :: FilePath -> IO Bool
447 doesDirectoryExist name =
449 (withFileStatus name $ \st -> isDirectory st)
450 (\ _ -> return False)
452 doesFileExist :: FilePath -> IO Bool
453 doesFileExist name = do
455 (withFileStatus name $ \st -> do b <- isDirectory st; return (not b))
456 (\ _ -> return False)
458 getModificationTime :: FilePath -> IO ClockTime
459 getModificationTime name =
460 withFileStatus name $ \ st ->
463 getPermissions :: FilePath -> IO Permissions
464 getPermissions name = do
465 withCString name $ \s -> do
466 read <- c_access s r_OK
467 write <- c_access s w_OK
468 exec <- c_access s x_OK
469 withFileStatus name $ \st -> do
470 is_dir <- isDirectory st
473 readable = read == 0,
474 writable = write == 0,
475 executable = not is_dir && exec == 0,
476 searchable = is_dir && exec == 0
480 setPermissions :: FilePath -> Permissions -> IO ()
481 setPermissions name (Permissions r w e s) = do
483 read = if r then s_IRUSR else emptyCMode
484 write = if w then s_IWUSR else emptyCMode
485 exec = if e || s then s_IXUSR else emptyCMode
487 mode = read `unionCMode` (write `unionCMode` exec)
489 withCString name $ \s ->
490 throwErrnoIfMinus1_ "setPermissions" $ c_chmod s mode
492 withFileStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
493 withFileStatus name f = do
494 allocaBytes sizeof_stat $ \p ->
495 withCString name $ \s -> do
496 throwErrnoIfMinus1Retry_ "withFileStatus" (c_stat s p)
499 withFileOrSymlinkStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
500 withFileOrSymlinkStatus name f = do
501 allocaBytes sizeof_stat $ \p ->
502 withCString name $ \s -> do
503 throwErrnoIfMinus1Retry_ "withFileOrSymlinkStatus" (lstat s p)
506 modificationTime :: Ptr CStat -> IO ClockTime
507 modificationTime stat = do
508 mtime <- st_mtime stat
509 return (TOD (toInteger (mtime :: CTime)) 0)
511 isDirectory :: Ptr CStat -> IO Bool
512 isDirectory stat = do
514 return (s_isdir mode)
519 unionCMode :: CMode -> CMode -> CMode
523 foreign import ccall unsafe "__hscore_path_max"
526 foreign import ccall unsafe "__hscore_readdir"
527 readdir :: Ptr CDir -> Ptr (Ptr CDirent) -> IO CInt
529 foreign import ccall unsafe "__hscore_free_dirent"
530 freeDirEnt :: Ptr CDirent -> IO ()
532 foreign import ccall unsafe "__hscore_end_of_dir"
535 foreign import ccall unsafe "__hscore_d_name"
536 d_name :: Ptr CDirent -> IO CString
538 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
539 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
540 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
542 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
543 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
544 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode