1 -- -----------------------------------------------------------------------------
2 -- $Id: Directory.hsc,v 1.9 2001/03/23 13:00:39 rrt 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.
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
53 import Prelude -- Just to get it in the dependencies
55 import Time ( ClockTime(..) )
59 import PrelMarshalAlloc
68 -- Fix mingw stat problem
69 #include "../includes/config.h"
76 -----------------------------------------------------------------------------
79 -- The @Permissions@ type is used to record whether certain
80 -- operations are permissible on a file/directory:
81 -- [to whom? - presumably the "current user"]
86 executable, searchable :: Bool
87 } deriving (Eq, Ord, Read, Show)
89 -----------------------------------------------------------------------------
92 -- @createDirectory dir@ creates a new directory {\em dir} which is
93 -- initially empty, or as near to empty as the operating system
96 -- The operation may fail with:
100 \item @isPermissionError@ / @PermissionDenied@
101 The process has insufficient privileges to perform the operation.
103 \item @isAlreadyExistsError@ / @AlreadyExists@
104 The operand refers to a directory that already exists.
106 \item @HardwareFault@
107 A physical I/O error has occurred.
109 \item @InvalidArgument@
110 The operand is not a valid directory name.
111 @[ENAMETOOLONG, ELOOP]@
113 There is no path to the directory.
115 \item @ResourceExhausted@
116 Insufficient resources (virtual memory, process file descriptors,
117 physical disk space, etc.) are available to perform the operation.
118 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
119 \item @InappropriateType@
120 The path refers to an existing non-directory object.
125 createDirectory :: FilePath -> IO ()
126 createDirectory path = do
127 withUnsafeCString path $ \s -> do
128 throwErrnoIfMinus1Retry_ "createDirectory" $
129 #if defined(mingw32_TARGET_OS)
136 @removeDirectory dir@ removes an existing directory {\em dir}. The
137 implementation may specify additional constraints which must be
138 satisfied before a directory can be removed (e.g. the directory has to
139 be empty, or may not be in use by other processes). It is not legal
140 for an implementation to partially remove a directory unless the
141 entire directory is removed. A conformant implementation need not
142 support directory removal in all situations (e.g. removal of the root
145 The operation may fail with:
147 \item @HardwareFault@
148 A physical I/O error has occurred.
150 \item @InvalidArgument@
151 The operand is not a valid directory name.
152 @[ENAMETOOLONG, ELOOP]@
153 \item @isDoesNotExist@ / @NoSuchThing@
154 The directory does not exist.
156 \item @isPermissionError@ / @PermissionDenied@
157 The process has insufficient privileges to perform the operation.
158 @[EROFS, EACCES, EPERM]@
159 \item @UnsatisfiedConstraints@
160 Implementation-dependent constraints are not satisfied.
161 @[EBUSY, ENOTEMPTY, EEXIST]@
162 \item @UnsupportedOperation@
163 The implementation does not support removal in this situation.
165 \item @InappropriateType@
166 The operand refers to an existing non-directory object.
171 removeDirectory :: FilePath -> IO ()
172 removeDirectory path = do
173 withUnsafeCString path $ \s ->
174 throwErrnoIfMinus1Retry_ "removeDirectory" (rmdir s)
177 @Removefile file@ removes the directory entry for an existing file
178 {\em file}, where {\em file} is not itself a directory. The
179 implementation may specify additional constraints which must be
180 satisfied before a file can be removed (e.g. the file may not be in
181 use by other processes).
183 The operation may fail with:
185 \item @HardwareFault@
186 A physical I/O error has occurred.
188 \item @InvalidArgument@
189 The operand is not a valid file name.
190 @[ENAMETOOLONG, ELOOP]@
191 \item @isDoesNotExist@ / @NoSuchThing@
192 The file does not exist.
194 \item @isPermissionError@ / @PermissionDenied@
195 The process has insufficient privileges to perform the operation.
196 @[EROFS, EACCES, EPERM]@
197 \item @UnsatisfiedConstraints@
198 Implementation-dependent constraints are not satisfied.
200 \item @InappropriateType@
201 The operand refers to an existing directory.
206 removeFile :: FilePath -> IO ()
208 withUnsafeCString path $ \s ->
209 throwErrnoIfMinus1Retry_ "removeFile" (unlink s)
212 @renameDirectory@ {\em old} {\em new} changes the name of an existing
213 directory from {\em old} to {\em new}. If the {\em new} directory
214 already exists, it is atomically replaced by the {\em old} directory.
215 If the {\em new} directory is neither the {\em old} directory nor an
216 alias of the {\em old} directory, it is removed as if by
217 $removeDirectory$. A conformant implementation need not support
218 renaming directories in all situations (e.g. renaming to an existing
219 directory, or across different physical devices), but the constraints
222 The operation may fail with:
224 \item @HardwareFault@
225 A physical I/O error has occurred.
227 \item @InvalidArgument@
228 Either operand is not a valid directory name.
229 @[ENAMETOOLONG, ELOOP]@
230 \item @isDoesNotExistError@ / @NoSuchThing@
231 The original directory does not exist, or there is no path to the target.
233 \item @isPermissionError@ / @PermissionDenied@
234 The process has insufficient privileges to perform the operation.
235 @[EROFS, EACCES, EPERM]@
236 \item @ResourceExhausted@
237 Insufficient resources are available to perform the operation.
238 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
239 \item @UnsatisfiedConstraints@
240 Implementation-dependent constraints are not satisfied.
241 @[EBUSY, ENOTEMPTY, EEXIST]@
242 \item @UnsupportedOperation@
243 The implementation does not support renaming in this situation.
245 \item @InappropriateType@
246 Either path refers to an existing non-directory object.
251 renameDirectory :: FilePath -> FilePath -> IO ()
252 renameDirectory opath npath =
253 withFileStatus opath $ \st -> do
254 is_dir <- isDirectory st
256 then ioException (IOError Nothing InappropriateType "renameDirectory"
257 ("not a directory") (Just opath))
260 withUnsafeCString opath $ \s1 ->
261 withUnsafeCString npath $ \s2 ->
262 throwErrnoIfMinus1Retry_ "renameDirectory" (rename s1 s2)
265 @renameFile@ {\em old} {\em new} changes the name of an existing file system
266 object from {\em old} to {\em new}. If the {\em new} object already
267 exists, it is atomically replaced by the {\em old} object. Neither
268 path may refer to an existing directory. A conformant implementation
269 need not support renaming files in all situations (e.g. renaming
270 across different physical devices), but the constraints must be
273 The operation may fail with:
275 \item @HardwareFault@
276 A physical I/O error has occurred.
278 \item @InvalidArgument@
279 Either operand is not a valid file name.
280 @[ENAMETOOLONG, ELOOP]@
281 \item @isDoesNotExistError@ / @NoSuchThing@
282 The original file does not exist, or there is no path to the target.
284 \item @isPermissionError@ / @PermissionDenied@
285 The process has insufficient privileges to perform the operation.
286 @[EROFS, EACCES, EPERM]@
287 \item @ResourceExhausted@
288 Insufficient resources are available to perform the operation.
289 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
290 \item @UnsatisfiedConstraints@
291 Implementation-dependent constraints are not satisfied.
293 \item @UnsupportedOperation@
294 The implementation does not support renaming in this situation.
296 \item @InappropriateType@
297 Either path refers to an existing directory.
298 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
302 renameFile :: FilePath -> FilePath -> IO ()
303 renameFile opath npath =
304 withFileStatus opath $ \st -> do
305 is_dir <- isDirectory st
307 then ioException (IOError Nothing InappropriateType "renameFile"
308 "is a directory" (Just opath))
311 withUnsafeCString opath $ \s1 ->
312 withUnsafeCString npath $ \s2 ->
313 throwErrnoIfMinus1Retry_ "renameFile" (rename s1 s2)
316 @getDirectoryContents dir@ returns a list of {\em all} entries
319 The operation may fail with:
321 \item @HardwareFault@
322 A physical I/O error has occurred.
324 \item @InvalidArgument@
325 The operand is not a valid directory name.
326 @[ENAMETOOLONG, ELOOP]@
327 \item @isDoesNotExistError@ / @NoSuchThing@
328 The directory does not exist.
330 \item @isPermissionError@ / @PermissionDenied@
331 The process has insufficient privileges to perform the operation.
333 \item @ResourceExhausted@
334 Insufficient resources are available to perform the operation.
336 \item @InappropriateType@
337 The path refers to an existing non-directory object.
342 getDirectoryContents :: FilePath -> IO [FilePath]
343 getDirectoryContents path = do
344 p <- withUnsafeCString path $ \s ->
345 throwErrnoIfNullRetry "getDirectoryContents" (opendir s)
348 loop :: Ptr CDir -> IO [String]
354 #ifndef mingw32_TARGET_OS
355 entry <- peekCString ((#ptr struct dirent,d_name) p)
357 entryp <- (#peek struct dirent,d_name) p
358 entry <- peekCString entryp -- on mingwin it's a char *, not a char []
361 return (entry:entries)
362 else do errno <- getErrno
363 if (errno == eINTR) then loop dir else do
364 throwErrnoIfMinus1_ "getDirectoryContents" $ closedir dir
365 #ifndef mingw32_TARGET_OS
368 if (errno == eNOENT) -- mingwin (20001111) cunningly sets errno to ENOENT when it runs out of files
371 else throwErrno "getDirectoryContents"
374 If the operating system has a notion of current directories,
375 @getCurrentDirectory@ returns an absolute path to the
376 current directory of the calling process.
378 The operation may fail with:
380 \item @HardwareFault@
381 A physical I/O error has occurred.
383 \item @isDoesNotExistError@ / @NoSuchThing@
384 There is no path referring to the current directory.
385 @[EPERM, ENOENT, ESTALE...]@
386 \item @isPermissionError@ / @PermissionDenied@
387 The process has insufficient privileges to perform the operation.
389 \item @ResourceExhausted@
390 Insufficient resources are available to perform the operation.
391 \item @UnsupportedOperation@
392 The operating system has no notion of current directory.
396 getCurrentDirectory :: IO FilePath
397 getCurrentDirectory = do
398 p <- mallocBytes (#const PATH_MAX)
399 go p (#const PATH_MAX)
400 where go p bytes = do
401 p' <- getcwd p (fromIntegral bytes)
403 then do s <- peekCString p'
406 else do errno <- getErrno
408 then do let bytes' = bytes * 2
409 p' <- reallocBytes p bytes'
411 else throwErrno "getCurrentDirectory"
414 If the operating system has a notion of current directories,
415 @setCurrentDirectory dir@ changes the current
416 directory of the calling process to {\em dir}.
418 The operation may fail with:
420 \item @HardwareFault@
421 A physical I/O error has occurred.
423 \item @InvalidArgument@
424 The operand is not a valid directory name.
425 @[ENAMETOOLONG, ELOOP]@
426 \item @isDoesNotExistError@ / @NoSuchThing@
427 The directory does not exist.
429 \item @isPermissionError@ / @PermissionDenied@
430 The process has insufficient privileges to perform the operation.
432 \item @UnsupportedOperation@
433 The operating system has no notion of current directory, or the
434 current directory cannot be dynamically changed.
435 \item @InappropriateType@
436 The path refers to an existing non-directory object.
441 setCurrentDirectory :: FilePath -> IO ()
442 setCurrentDirectory path = do
443 withUnsafeCString path $ \s ->
444 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (chdir s)
445 -- ToDo: add path to error
448 To clarify, @doesDirectoryExist@ returns True if a file system object
449 exist, and it's a directory. @doesFileExist@ returns True if the file
450 system object exist, but it's not a directory (i.e., for every other
451 file system object that is not a directory.)
454 doesDirectoryExist :: FilePath -> IO Bool
455 doesDirectoryExist name =
457 (withFileStatus name $ \st -> isDirectory st)
458 (\ _ -> return False)
460 doesFileExist :: FilePath -> IO Bool
461 doesFileExist name = do
463 (withFileStatus name $ \st -> do b <- isDirectory st; return (not b))
464 (\ _ -> return False)
466 getModificationTime :: FilePath -> IO ClockTime
467 getModificationTime name =
468 withFileStatus name $ \ st ->
471 getPermissions :: FilePath -> IO Permissions
472 getPermissions name = do
473 withUnsafeCString name $ \s -> do
474 read <- access s (#const R_OK)
475 write <- access s (#const W_OK)
476 exec <- access s (#const X_OK)
477 withFileStatus name $ \st -> do
478 is_dir <- isDirectory st
479 is_reg <- isRegularFile st
482 readable = read == 0,
483 writable = write == 0,
484 executable = not is_dir && exec == 0,
485 searchable = not is_reg && exec == 0
489 setPermissions :: FilePath -> Permissions -> IO ()
490 setPermissions name (Permissions r w e s) = do
492 read = if r then (#const S_IRUSR) else emptyCMode
493 write = if w then (#const S_IWUSR) else emptyCMode
494 exec = if e || s then (#const S_IXUSR) else emptyCMode
496 mode = read `unionCMode` (write `unionCMode` exec)
498 withUnsafeCString name $ \s ->
499 throwErrnoIfMinus1_ "setPermissions" $ chmod s mode
501 withFileStatus :: FilePath -> (Ptr CStat -> IO a) -> IO a
502 withFileStatus name f = do
503 #ifndef mingw32_TARGET_OS
504 allocaBytes (#const sizeof(struct stat)) $ \p ->
506 allocaBytes (#const sizeof(struct _stati64)) $ \p ->
508 withUnsafeCString name $ \s -> do
509 throwErrnoIfMinus1Retry_ "withFileStatus" (stat s p)
512 modificationTime :: Ptr CStat -> IO ClockTime
513 modificationTime stat = do
514 #ifndef mingw32_TARGET_OS
515 mtime <- (#peek struct stat, st_mtime) stat
517 mtime <- (#peek struct _stati64, st_mtime) stat
519 return (TOD (toInteger (mtime :: CTime)) 0)
521 isDirectory :: Ptr CStat -> IO Bool
522 isDirectory stat = do
523 #ifndef mingw32_TARGET_OS
524 mode <- (#peek struct stat, st_mode) stat
526 mode <- (#peek struct _stati64, st_mode) stat
528 return (s_ISDIR mode /= 0)
530 isRegularFile :: Ptr CStat -> IO Bool
531 isRegularFile stat = do
532 #ifndef mingw32_TARGET_OS
533 mode <- (#peek struct stat, st_mode) stat
535 mode <- (#peek struct _stati64, st_mode) stat
537 return (s_ISREG mode /= 0)
539 foreign import ccall unsafe s_ISDIR :: CMode -> Int
540 #def inline HsInt s_ISDIR(m) {return S_ISDIR(m);}
542 foreign import ccall unsafe s_ISREG :: CMode -> Int
543 #def inline HsInt s_ISREG(m) {return S_ISREG(m);}
548 unionCMode :: CMode -> CMode -> CMode
551 type UCString = UnsafeCString
553 #if defined(mingw32_TARGET_OS)
554 foreign import ccall unsafe mkdir :: UCString -> IO CInt
556 foreign import ccall unsafe mkdir :: UCString -> CInt -> IO CInt
559 foreign import ccall unsafe chmod :: UCString -> CMode -> IO CInt
560 foreign import ccall unsafe access :: UCString -> CMode -> IO CInt
561 foreign import ccall unsafe rmdir :: UCString -> IO CInt
562 foreign import ccall unsafe chdir :: UCString -> IO CInt
563 foreign import ccall unsafe getcwd :: Ptr CChar -> CInt -> IO (Ptr CChar)
564 foreign import ccall unsafe unlink :: UCString -> IO CInt
565 foreign import ccall unsafe rename :: UCString -> UCString -> IO CInt
567 foreign import ccall unsafe opendir :: UCString -> IO (Ptr CDir)
568 foreign import ccall unsafe readdir :: Ptr CDir -> IO (Ptr CDirent)
569 foreign import ccall unsafe closedir :: Ptr CDir -> IO CInt
571 foreign import ccall unsafe stat :: UCString -> Ptr CStat -> IO CInt