1 -- -----------------------------------------------------------------------------
2 -- $Id: Directory.hsc,v 1.1 2001/06/28 14:15:04 simonmar Exp $
4 -- (c) The University of Glasgow, 1994-2000
7 -- The Directory Interface
10 A directory contains a series of entries, each of which is a named
11 reference to a file system object (file, directory etc.). Some
12 entries may be hidden, inaccessible, or have some administrative
13 function (e.g. "." or ".." under POSIX), but in this standard all such
14 entries are considered to form part of the directory contents.
15 Entries in sub-directories are not, however, considered to form part
16 of the directory contents.
18 Each file system object is referenced by a {\em path}. There is
19 normally at least one absolute path to each file system object. In
20 some operating systems, it may also be possible to have paths which
21 are relative to the current directory.
24 module System.IO.Directory
26 Permissions -- abstract
28 , readable -- :: Permissions -> Bool
29 , writable -- :: Permissions -> Bool
30 , executable -- :: Permissions -> Bool
31 , searchable -- :: Permissions -> Bool
33 , createDirectory -- :: FilePath -> IO ()
34 , removeDirectory -- :: FilePath -> IO ()
35 , renameDirectory -- :: FilePath -> FilePath -> IO ()
37 , getDirectoryContents -- :: FilePath -> IO [FilePath]
38 , getCurrentDirectory -- :: IO FilePath
39 , setCurrentDirectory -- :: FilePath -> IO ()
41 , removeFile -- :: FilePath -> IO ()
42 , renameFile -- :: FilePath -> FilePath -> IO ()
44 , doesFileExist -- :: FilePath -> IO Bool
45 , doesDirectoryExist -- :: FilePath -> IO Bool
47 , getPermissions -- :: FilePath -> IO Permissions
48 , setPermissions -- :: FilePath -> Permissions -> IO ()
50 , getModificationTime -- :: FilePath -> IO ClockTime
55 import System.Time ( ClockTime(..) )
60 #ifdef __GLASGOW_HASKELL__
62 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 withUnsafeCString path $ \s -> do
126 throwErrnoIfMinus1Retry_ "createDirectory" $
127 #if defined(mingw32_TARGET_OS)
134 @removeDirectory dir@ removes an existing directory {\em dir}. The
135 implementation may specify additional constraints which must be
136 satisfied before a directory can be removed (e.g. the directory has to
137 be empty, or may not be in use by other processes). It is not legal
138 for an implementation to partially remove a directory unless the
139 entire directory is removed. A conformant implementation need not
140 support directory removal in all situations (e.g. removal of the root
143 The operation may fail with:
145 \item @HardwareFault@
146 A physical I/O error has occurred.
148 \item @InvalidArgument@
149 The operand is not a valid directory name.
150 @[ENAMETOOLONG, ELOOP]@
151 \item @isDoesNotExist@ / @NoSuchThing@
152 The directory does not exist.
154 \item @isPermissionError@ / @PermissionDenied@
155 The process has insufficient privileges to perform the operation.
156 @[EROFS, EACCES, EPERM]@
157 \item @UnsatisfiedConstraints@
158 Implementation-dependent constraints are not satisfied.
159 @[EBUSY, ENOTEMPTY, EEXIST]@
160 \item @UnsupportedOperation@
161 The implementation does not support removal in this situation.
163 \item @InappropriateType@
164 The operand refers to an existing non-directory object.
169 removeDirectory :: FilePath -> IO ()
170 removeDirectory path = do
171 withUnsafeCString path $ \s ->
172 throwErrnoIfMinus1Retry_ "removeDirectory" (rmdir s)
175 @Removefile file@ removes the directory entry for an existing file
176 {\em file}, where {\em file} is not itself a directory. The
177 implementation may specify additional constraints which must be
178 satisfied before a file can be removed (e.g. the file may not be in
179 use by other processes).
181 The operation may fail with:
183 \item @HardwareFault@
184 A physical I/O error has occurred.
186 \item @InvalidArgument@
187 The operand is not a valid file name.
188 @[ENAMETOOLONG, ELOOP]@
189 \item @isDoesNotExist@ / @NoSuchThing@
190 The file does not exist.
192 \item @isPermissionError@ / @PermissionDenied@
193 The process has insufficient privileges to perform the operation.
194 @[EROFS, EACCES, EPERM]@
195 \item @UnsatisfiedConstraints@
196 Implementation-dependent constraints are not satisfied.
198 \item @InappropriateType@
199 The operand refers to an existing directory.
204 removeFile :: FilePath -> IO ()
206 withUnsafeCString path $ \s ->
207 throwErrnoIfMinus1Retry_ "removeFile" (unlink s)
210 @renameDirectory@ {\em old} {\em new} changes the name of an existing
211 directory from {\em old} to {\em new}. If the {\em new} directory
212 already exists, it is atomically replaced by the {\em old} directory.
213 If the {\em new} directory is neither the {\em old} directory nor an
214 alias of the {\em old} directory, it is removed as if by
215 $removeDirectory$. A conformant implementation need not support
216 renaming directories in all situations (e.g. renaming to an existing
217 directory, or across different physical devices), but the constraints
220 The operation may fail with:
222 \item @HardwareFault@
223 A physical I/O error has occurred.
225 \item @InvalidArgument@
226 Either operand is not a valid directory name.
227 @[ENAMETOOLONG, ELOOP]@
228 \item @isDoesNotExistError@ / @NoSuchThing@
229 The original directory does not exist, or there is no path to the target.
231 \item @isPermissionError@ / @PermissionDenied@
232 The process has insufficient privileges to perform the operation.
233 @[EROFS, EACCES, EPERM]@
234 \item @ResourceExhausted@
235 Insufficient resources are available to perform the operation.
236 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
237 \item @UnsatisfiedConstraints@
238 Implementation-dependent constraints are not satisfied.
239 @[EBUSY, ENOTEMPTY, EEXIST]@
240 \item @UnsupportedOperation@
241 The implementation does not support renaming in this situation.
243 \item @InappropriateType@
244 Either path refers to an existing non-directory object.
249 renameDirectory :: FilePath -> FilePath -> IO ()
250 renameDirectory opath npath =
251 withFileStatus opath $ \st -> do
252 is_dir <- isDirectory st
254 then ioException (IOError Nothing InappropriateType "renameDirectory"
255 ("not a directory") (Just opath))
258 withUnsafeCString opath $ \s1 ->
259 withUnsafeCString npath $ \s2 ->
260 throwErrnoIfMinus1Retry_ "renameDirectory" (rename s1 s2)
263 @renameFile@ {\em old} {\em new} changes the name of an existing file system
264 object from {\em old} to {\em new}. If the {\em new} object already
265 exists, it is atomically replaced by the {\em old} object. Neither
266 path may refer to an existing directory. A conformant implementation
267 need not support renaming files in all situations (e.g. renaming
268 across different physical devices), but the constraints must be
271 The operation may fail with:
273 \item @HardwareFault@
274 A physical I/O error has occurred.
276 \item @InvalidArgument@
277 Either operand is not a valid file name.
278 @[ENAMETOOLONG, ELOOP]@
279 \item @isDoesNotExistError@ / @NoSuchThing@
280 The original file does not exist, or there is no path to the target.
282 \item @isPermissionError@ / @PermissionDenied@
283 The process has insufficient privileges to perform the operation.
284 @[EROFS, EACCES, EPERM]@
285 \item @ResourceExhausted@
286 Insufficient resources are available to perform the operation.
287 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
288 \item @UnsatisfiedConstraints@
289 Implementation-dependent constraints are not satisfied.
291 \item @UnsupportedOperation@
292 The implementation does not support renaming in this situation.
294 \item @InappropriateType@
295 Either path refers to an existing directory.
296 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
300 renameFile :: FilePath -> FilePath -> IO ()
301 renameFile opath npath =
302 withFileStatus opath $ \st -> do
303 is_dir <- isDirectory st
305 then ioException (IOError Nothing InappropriateType "renameFile"
306 "is a directory" (Just opath))
309 withUnsafeCString opath $ \s1 ->
310 withUnsafeCString npath $ \s2 ->
311 throwErrnoIfMinus1Retry_ "renameFile" (rename s1 s2)
314 @getDirectoryContents dir@ returns a list of {\em all} entries
317 The operation may fail with:
319 \item @HardwareFault@
320 A physical I/O error has occurred.
322 \item @InvalidArgument@
323 The operand is not a valid directory name.
324 @[ENAMETOOLONG, ELOOP]@
325 \item @isDoesNotExistError@ / @NoSuchThing@
326 The directory does not exist.
328 \item @isPermissionError@ / @PermissionDenied@
329 The process has insufficient privileges to perform the operation.
331 \item @ResourceExhausted@
332 Insufficient resources are available to perform the operation.
334 \item @InappropriateType@
335 The path refers to an existing non-directory object.
340 getDirectoryContents :: FilePath -> IO [FilePath]
341 getDirectoryContents path = do
342 p <- withUnsafeCString path $ \s ->
343 throwErrnoIfNullRetry "getDirectoryContents" (opendir s)
346 loop :: Ptr CDir -> IO [String]
352 #ifdef mingw32_TARGET_OS
353 entryp <- (#peek struct dirent,d_name) p
354 entry <- peekCString entryp -- on mingwin it's a char *, not a char []
356 entry <- peekCString ((#ptr struct dirent,d_name) p)
359 return (entry:entries)
360 else do errno <- getErrno
361 if (errno == eINTR) then loop dir else do
362 throwErrnoIfMinus1_ "getDirectoryContents" $ closedir dir
363 #ifdef mingw32_TARGET_OS
364 if (errno == eNOENT) -- mingwin (20001111) cunningly sets errno to ENOENT when it runs out of files
369 else throwErrno "getDirectoryContents"
372 If the operating system has a notion of current directories,
373 @getCurrentDirectory@ returns an absolute path to the
374 current directory of the calling process.
376 The operation may fail with:
378 \item @HardwareFault@
379 A physical I/O error has occurred.
381 \item @isDoesNotExistError@ / @NoSuchThing@
382 There is no path referring to the current directory.
383 @[EPERM, ENOENT, ESTALE...]@
384 \item @isPermissionError@ / @PermissionDenied@
385 The process has insufficient privileges to perform the operation.
387 \item @ResourceExhausted@
388 Insufficient resources are available to perform the operation.
389 \item @UnsupportedOperation@
390 The operating system has no notion of current directory.
394 getCurrentDirectory :: IO FilePath
395 getCurrentDirectory = do
396 p <- mallocBytes (#const PATH_MAX)
397 go p (#const PATH_MAX)
398 where go p bytes = do
399 p' <- getcwd p (fromIntegral bytes)
401 then do s <- peekCString p'
404 else do errno <- getErrno
406 then do let bytes' = bytes * 2
407 p' <- reallocBytes p bytes'
409 else throwErrno "getCurrentDirectory"
412 If the operating system has a notion of current directories,
413 @setCurrentDirectory dir@ changes the current
414 directory of the calling process to {\em dir}.
416 The operation may fail with:
418 \item @HardwareFault@
419 A physical I/O error has occurred.
421 \item @InvalidArgument@
422 The operand is not a valid directory name.
423 @[ENAMETOOLONG, ELOOP]@
424 \item @isDoesNotExistError@ / @NoSuchThing@
425 The directory does not exist.
427 \item @isPermissionError@ / @PermissionDenied@
428 The process has insufficient privileges to perform the operation.
430 \item @UnsupportedOperation@
431 The operating system has no notion of current directory, or the
432 current directory cannot be dynamically changed.
433 \item @InappropriateType@
434 The path refers to an existing non-directory object.
439 setCurrentDirectory :: FilePath -> IO ()
440 setCurrentDirectory path = do
441 withUnsafeCString path $ \s ->
442 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (chdir s)
443 -- ToDo: add path to error
446 To clarify, @doesDirectoryExist@ returns True if a file system object
447 exist, and it's a directory. @doesFileExist@ returns True if the file
448 system object exist, but it's not a directory (i.e., for every other
449 file system object that is not a directory.)
452 doesDirectoryExist :: FilePath -> IO Bool
453 doesDirectoryExist name =
455 (withFileStatus name $ \st -> isDirectory st)
456 (\ _ -> return False)
458 doesFileExist :: FilePath -> IO Bool
459 doesFileExist name = do
461 (withFileStatus name $ \st -> do b <- isDirectory st; return (not b))
462 (\ _ -> return False)
464 getModificationTime :: FilePath -> IO ClockTime
465 getModificationTime name =
466 withFileStatus name $ \ st ->
469 getPermissions :: FilePath -> IO Permissions
470 getPermissions name = do
471 withUnsafeCString name $ \s -> do
472 read <- access s (#const R_OK)
473 write <- access s (#const W_OK)
474 exec <- access s (#const X_OK)
475 withFileStatus name $ \st -> do
476 is_dir <- isDirectory st
477 is_reg <- isRegularFile st
480 readable = read == 0,
481 writable = write == 0,
482 executable = not is_dir && exec == 0,
483 searchable = not is_reg && exec == 0
487 setPermissions :: FilePath -> Permissions -> IO ()
488 setPermissions name (Permissions r w e s) = do
490 read = if r then (#const S_IRUSR) else emptyCMode
491 write = if w then (#const S_IWUSR) else emptyCMode
492 exec = if e || s then (#const S_IXUSR) else emptyCMode
494 mode = read `unionCMode` (write `unionCMode` exec)
496 withUnsafeCString name $ \s ->
497 throwErrnoIfMinus1_ "setPermissions" $ chmod s mode
499 withFileStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
500 withFileStatus name f = do
501 allocaBytes (#const sizeof(struct stat)) $ \p ->
502 withUnsafeCString name $ \s -> do
503 throwErrnoIfMinus1Retry_ "withFileStatus" (stat s p)
506 modificationTime :: Ptr CStat -> IO ClockTime
507 modificationTime stat = do
508 mtime <- (#peek struct stat, st_mtime) stat
509 return (TOD (toInteger (mtime :: CTime)) 0)
511 isDirectory :: Ptr CStat -> IO Bool
512 isDirectory stat = do
513 mode <- (#peek struct stat, st_mode) stat
514 return (s_ISDIR mode /= 0)
516 isRegularFile :: Ptr CStat -> IO Bool
517 isRegularFile stat = do
518 mode <- (#peek struct stat, st_mode) stat
519 return (s_ISREG mode /= 0)
521 foreign import ccall unsafe s_ISDIR :: CMode -> Int
522 #def inline HsInt s_ISDIR(m) {return S_ISDIR(m);}
524 foreign import ccall unsafe s_ISREG :: CMode -> Int
525 #def inline HsInt s_ISREG(m) {return S_ISREG(m);}
530 unionCMode :: CMode -> CMode -> CMode
533 type UCString = UnsafeCString
535 #if defined(mingw32_TARGET_OS)
536 foreign import ccall unsafe mkdir :: UCString -> IO CInt
538 foreign import ccall unsafe mkdir :: UCString -> CInt -> IO CInt
541 foreign import ccall unsafe chmod :: UCString -> CMode -> IO CInt
542 foreign import ccall unsafe access :: UCString -> CMode -> IO CInt
543 foreign import ccall unsafe rmdir :: UCString -> IO CInt
544 foreign import ccall unsafe chdir :: UCString -> IO CInt
545 foreign import ccall unsafe getcwd :: Ptr CChar -> CInt -> IO (Ptr CChar)
546 foreign import ccall unsafe unlink :: UCString -> IO CInt
547 foreign import ccall unsafe rename :: UCString -> UCString -> IO CInt
549 foreign import ccall unsafe opendir :: UCString -> IO (Ptr CDir)
550 foreign import ccall unsafe readdir :: Ptr CDir -> IO (Ptr CDirent)
551 foreign import ccall unsafe closedir :: Ptr CDir -> IO CInt
553 foreign import ccall unsafe stat :: UCString -> Ptr CStat -> IO CInt