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.hs,v 1.1 2002/02/05 17:32: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 )
74 -----------------------------------------------------------------------------
77 -- The Permissions type is used to record whether certain
78 -- operations are permissible on a file/directory:
79 -- [to whom? - presumably the "current user"]
84 executable, searchable :: Bool
85 } deriving (Eq, Ord, Read, Show)
87 -----------------------------------------------------------------------------
90 -- `createDirectory dir' creates a new directory dir which is
91 -- initially empty, or as near to empty as the operating system
94 -- The operation may fail with:
98 \item @isPermissionError@ / @PermissionDenied@
99 The process has insufficient privileges to perform the operation.
101 \item @isAlreadyExistsError@ / @AlreadyExists@
102 The operand refers to a directory that already exists.
104 \item @HardwareFault@
105 A physical I/O error has occurred.
107 \item @InvalidArgument@
108 The operand is not a valid directory name.
109 @[ENAMETOOLONG, ELOOP]@
111 There is no path to the directory.
113 \item @ResourceExhausted@
114 Insufficient resources (virtual memory, process file descriptors,
115 physical disk space, etc.) are available to perform the operation.
116 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
117 \item @InappropriateType@
118 The path refers to an existing non-directory object.
123 createDirectory :: FilePath -> IO ()
124 createDirectory path = do
125 withCString path $ \s -> do
126 throwErrnoIfMinus1Retry_ "createDirectory" $
130 @removeDirectory dir@ removes an existing directory {\em dir}. The
131 implementation may specify additional constraints which must be
132 satisfied before a directory can be removed (e.g. the directory has to
133 be empty, or may not be in use by other processes). It is not legal
134 for an implementation to partially remove a directory unless the
135 entire directory is removed. A conformant implementation need not
136 support directory removal in all situations (e.g. removal of the root
139 The operation may fail with:
141 \item @HardwareFault@
142 A physical I/O error has occurred.
144 \item @InvalidArgument@
145 The operand is not a valid directory name.
146 @[ENAMETOOLONG, ELOOP]@
147 \item @isDoesNotExist@ / @NoSuchThing@
148 The directory does not exist.
150 \item @isPermissionError@ / @PermissionDenied@
151 The process has insufficient privileges to perform the operation.
152 @[EROFS, EACCES, EPERM]@
153 \item @UnsatisfiedConstraints@
154 Implementation-dependent constraints are not satisfied.
155 @[EBUSY, ENOTEMPTY, EEXIST]@
156 \item @UnsupportedOperation@
157 The implementation does not support removal in this situation.
159 \item @InappropriateType@
160 The operand refers to an existing non-directory object.
165 removeDirectory :: FilePath -> IO ()
166 removeDirectory path = do
167 withCString path $ \s ->
168 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
171 @Removefile file@ removes the directory entry for an existing file
172 {\em file}, where {\em file} is not itself a directory. The
173 implementation may specify additional constraints which must be
174 satisfied before a file can be removed (e.g. the file may not be in
175 use by other processes).
177 The operation may fail with:
179 \item @HardwareFault@
180 A physical I/O error has occurred.
182 \item @InvalidArgument@
183 The operand is not a valid file name.
184 @[ENAMETOOLONG, ELOOP]@
185 \item @isDoesNotExist@ / @NoSuchThing@
186 The file does not exist.
188 \item @isPermissionError@ / @PermissionDenied@
189 The process has insufficient privileges to perform the operation.
190 @[EROFS, EACCES, EPERM]@
191 \item @UnsatisfiedConstraints@
192 Implementation-dependent constraints are not satisfied.
194 \item @InappropriateType@
195 The operand refers to an existing directory.
200 removeFile :: FilePath -> IO ()
202 withCString path $ \s ->
203 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
206 @renameDirectory@ {\em old} {\em new} changes the name of an existing
207 directory from {\em old} to {\em new}. If the {\em new} directory
208 already exists, it is atomically replaced by the {\em old} directory.
209 If the {\em new} directory is neither the {\em old} directory nor an
210 alias of the {\em old} directory, it is removed as if by
211 $removeDirectory$. A conformant implementation need not support
212 renaming directories in all situations (e.g. renaming to an existing
213 directory, or across different physical devices), but the constraints
216 The operation may fail with:
218 \item @HardwareFault@
219 A physical I/O error has occurred.
221 \item @InvalidArgument@
222 Either operand is not a valid directory name.
223 @[ENAMETOOLONG, ELOOP]@
224 \item @isDoesNotExistError@ / @NoSuchThing@
225 The original directory does not exist, or there is no path to the target.
227 \item @isPermissionError@ / @PermissionDenied@
228 The process has insufficient privileges to perform the operation.
229 @[EROFS, EACCES, EPERM]@
230 \item @ResourceExhausted@
231 Insufficient resources are available to perform the operation.
232 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
233 \item @UnsatisfiedConstraints@
234 Implementation-dependent constraints are not satisfied.
235 @[EBUSY, ENOTEMPTY, EEXIST]@
236 \item @UnsupportedOperation@
237 The implementation does not support renaming in this situation.
239 \item @InappropriateType@
240 Either path refers to an existing non-directory object.
245 renameDirectory :: FilePath -> FilePath -> IO ()
246 renameDirectory opath npath =
247 withFileStatus opath $ \st -> do
248 is_dir <- isDirectory st
250 then ioException (IOError Nothing InappropriateType "renameDirectory"
251 ("not a directory") (Just opath))
254 withCString opath $ \s1 ->
255 withCString npath $ \s2 ->
256 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
259 @renameFile@ {\em old} {\em new} changes the name of an existing file system
260 object from {\em old} to {\em new}. If the {\em new} object already
261 exists, it is atomically replaced by the {\em old} object. Neither
262 path may refer to an existing directory. A conformant implementation
263 need not support renaming files in all situations (e.g. renaming
264 across different physical devices), but the constraints must be
267 The operation may fail with:
269 \item @HardwareFault@
270 A physical I/O error has occurred.
272 \item @InvalidArgument@
273 Either operand is not a valid file name.
274 @[ENAMETOOLONG, ELOOP]@
275 \item @isDoesNotExistError@ / @NoSuchThing@
276 The original file does not exist, or there is no path to the target.
278 \item @isPermissionError@ / @PermissionDenied@
279 The process has insufficient privileges to perform the operation.
280 @[EROFS, EACCES, EPERM]@
281 \item @ResourceExhausted@
282 Insufficient resources are available to perform the operation.
283 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
284 \item @UnsatisfiedConstraints@
285 Implementation-dependent constraints are not satisfied.
287 \item @UnsupportedOperation@
288 The implementation does not support renaming in this situation.
290 \item @InappropriateType@
291 Either path refers to an existing directory.
292 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
296 renameFile :: FilePath -> FilePath -> IO ()
297 renameFile opath npath =
298 withFileOrSymlinkStatus opath $ \st -> do
299 is_dir <- isDirectory st
301 then ioException (IOError Nothing InappropriateType "renameFile"
302 "is a directory" (Just opath))
305 withCString opath $ \s1 ->
306 withCString npath $ \s2 ->
307 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
310 @getDirectoryContents dir@ returns a list of {\em all} entries
313 The operation may fail with:
315 \item @HardwareFault@
316 A physical I/O error has occurred.
318 \item @InvalidArgument@
319 The operand is not a valid directory name.
320 @[ENAMETOOLONG, ELOOP]@
321 \item @isDoesNotExistError@ / @NoSuchThing@
322 The directory does not exist.
324 \item @isPermissionError@ / @PermissionDenied@
325 The process has insufficient privileges to perform the operation.
327 \item @ResourceExhausted@
328 Insufficient resources are available to perform the operation.
330 \item @InappropriateType@
331 The path refers to an existing non-directory object.
336 getDirectoryContents :: FilePath -> IO [FilePath]
337 getDirectoryContents path = do
338 alloca $ \ ptr_dEnt -> do
339 p <- withCString path $ \s ->
340 throwErrnoIfNullRetry "getDirectoryContents" (c_opendir s)
343 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
344 loop ptr_dEnt dir = do
346 r <- readdir dir ptr_dEnt
349 dEnt <- peek ptr_dEnt
353 entry <- (d_name dEnt >>= peekCString)
355 entries <- loop ptr_dEnt dir
356 return (entry:entries)
357 else do errno <- getErrno
358 if (errno == eINTR) then loop ptr_dEnt dir else do
359 throwErrnoIfMinus1_ "getDirectoryContents" $ c_closedir dir
360 let (Errno eo) = errno
361 if (eo == end_of_dir)
363 else throwErrno "getDirectoryContents"
368 If the operating system has a notion of current directories,
369 @getCurrentDirectory@ returns an absolute path to the
370 current directory of the calling process.
372 The operation may fail with:
374 \item @HardwareFault@
375 A physical I/O error has occurred.
377 \item @isDoesNotExistError@ / @NoSuchThing@
378 There is no path referring to the current directory.
379 @[EPERM, ENOENT, ESTALE...]@
380 \item @isPermissionError@ / @PermissionDenied@
381 The process has insufficient privileges to perform the operation.
383 \item @ResourceExhausted@
384 Insufficient resources are available to perform the operation.
385 \item @UnsupportedOperation@
386 The operating system has no notion of current directory.
390 getCurrentDirectory :: IO FilePath
391 getCurrentDirectory = do
392 p <- mallocBytes path_max
394 where go p bytes = do
395 p' <- c_getcwd p (fromIntegral bytes)
397 then do s <- peekCString p'
400 else do errno <- getErrno
402 then do let bytes' = bytes * 2
403 p' <- reallocBytes p bytes'
405 else throwErrno "getCurrentDirectory"
408 If the operating system has a notion of current directories,
409 @setCurrentDirectory dir@ changes the current
410 directory of the calling process to {\em dir}.
412 The operation may fail with:
414 \item @HardwareFault@
415 A physical I/O error has occurred.
417 \item @InvalidArgument@
418 The operand is not a valid directory name.
419 @[ENAMETOOLONG, ELOOP]@
420 \item @isDoesNotExistError@ / @NoSuchThing@
421 The directory does not exist.
423 \item @isPermissionError@ / @PermissionDenied@
424 The process has insufficient privileges to perform the operation.
426 \item @UnsupportedOperation@
427 The operating system has no notion of current directory, or the
428 current directory cannot be dynamically changed.
429 \item @InappropriateType@
430 The path refers to an existing non-directory object.
435 setCurrentDirectory :: FilePath -> IO ()
436 setCurrentDirectory path = do
437 withCString path $ \s ->
438 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
439 -- ToDo: add path to error
442 To clarify, @doesDirectoryExist@ returns True if a file system object
443 exist, and it's a directory. @doesFileExist@ returns True if the file
444 system object exist, but it's not a directory (i.e., for every other
445 file system object that is not a directory.)
448 doesDirectoryExist :: FilePath -> IO Bool
449 doesDirectoryExist name =
451 (withFileStatus name $ \st -> isDirectory st)
452 (\ _ -> return False)
454 doesFileExist :: FilePath -> IO Bool
455 doesFileExist name = do
457 (withFileStatus name $ \st -> do b <- isDirectory st; return (not b))
458 (\ _ -> return False)
460 getModificationTime :: FilePath -> IO ClockTime
461 getModificationTime name =
462 withFileStatus name $ \ st ->
465 getPermissions :: FilePath -> IO Permissions
466 getPermissions name = do
467 withCString name $ \s -> do
468 read <- c_access s r_OK
469 write <- c_access s w_OK
470 exec <- c_access s x_OK
471 withFileStatus name $ \st -> do
472 is_dir <- isDirectory st
473 is_reg <- isRegularFile st
476 readable = read == 0,
477 writable = write == 0,
478 executable = not is_dir && exec == 0,
479 searchable = not is_reg && exec == 0
483 setPermissions :: FilePath -> Permissions -> IO ()
484 setPermissions name (Permissions r w e s) = do
486 read = if r then s_IRUSR else emptyCMode
487 write = if w then s_IWUSR else emptyCMode
488 exec = if e || s then s_IXUSR else emptyCMode
490 mode = read `unionCMode` (write `unionCMode` exec)
492 withCString name $ \s ->
493 throwErrnoIfMinus1_ "setPermissions" $ c_chmod s mode
495 withFileStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
496 withFileStatus name f = do
497 allocaBytes sizeof_stat $ \p ->
498 withCString name $ \s -> do
499 throwErrnoIfMinus1Retry_ "withFileStatus" (c_stat s p)
502 withFileOrSymlinkStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
503 withFileOrSymlinkStatus name f = do
504 allocaBytes sizeof_stat $ \p ->
505 withCString name $ \s -> do
506 throwErrnoIfMinus1Retry_ "withFileOrSymlinkStatus" (lstat s p)
509 modificationTime :: Ptr CStat -> IO ClockTime
510 modificationTime stat = do
511 mtime <- st_mtime stat
512 return (TOD (toInteger (mtime :: CTime)) 0)
514 isDirectory :: Ptr CStat -> IO Bool
515 isDirectory stat = do
517 return (s_isdir mode)
519 isRegularFile :: Ptr CStat -> IO Bool
520 isRegularFile stat = do
522 return (s_isreg mode)
527 unionCMode :: CMode -> CMode -> CMode
531 foreign import ccall unsafe "__hscore_path_max"
534 foreign import ccall unsafe "__hscore_readdir"
535 readdir :: Ptr CDir -> Ptr (Ptr CDirent) -> IO CInt
537 foreign import ccall unsafe "__hscore_free_dirent"
538 freeDirEnt :: Ptr CDirent -> IO ()
540 foreign import ccall unsafe "__hscore_end_of_dir"
543 foreign import ccall unsafe "__hscore_d_name"
544 d_name :: Ptr CDirent -> IO CString
546 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
547 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
548 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
550 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
551 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
552 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode