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
9 -- Portability : portable
11 -- System-independent interface to directory manipulation.
13 -----------------------------------------------------------------------------
15 module System.Directory
19 -- * Actions on directories
20 createDirectory -- :: FilePath -> IO ()
21 , removeDirectory -- :: FilePath -> IO ()
22 , renameDirectory -- :: FilePath -> FilePath -> IO ()
24 , getDirectoryContents -- :: FilePath -> IO [FilePath]
25 , getCurrentDirectory -- :: IO FilePath
26 , setCurrentDirectory -- :: FilePath -> IO ()
28 , getAppUserDataDirectory
31 , removeFile -- :: FilePath -> IO ()
32 , renameFile -- :: FilePath -> FilePath -> IO ()
33 #ifdef __GLASGOW_HASKELL__
34 , copyFile -- :: FilePath -> FilePath -> IO ()
38 , doesFileExist -- :: FilePath -> IO Bool
39 , doesDirectoryExist -- :: FilePath -> IO Bool
47 readable, -- :: Permissions -> Bool
48 writable, -- :: Permissions -> Bool
49 executable, -- :: Permissions -> Bool
50 searchable -- :: Permissions -> Bool
53 , getPermissions -- :: FilePath -> IO Permissions
54 , setPermissions -- :: FilePath -> Permissions -> IO ()
58 , getModificationTime -- :: FilePath -> IO ClockTime
63 #elif defined(__HUGS__)
69 import Control.Exception ( bracket )
70 import Control.Monad ( when )
71 import System.Posix.Types
72 import System.Posix.Internals
73 import System.Time ( ClockTime(..) )
75 import System.IO.Error
79 #ifdef __GLASGOW_HASKELL__
80 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
83 #ifndef mingw32_TARGET_OS
84 import System.Environment
88 A directory contains a series of entries, each of which is a named
89 reference to a file system object (file, directory etc.). Some
90 entries may be hidden, inaccessible, or have some administrative
91 function (e.g. `.' or `..' under POSIX
92 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
93 this standard all such entries are considered to form part of the
94 directory contents. Entries in sub-directories are not, however,
95 considered to form part of the directory contents.
97 Each file system object is referenced by a /path/. There is
98 normally at least one absolute path to each file system object. In
99 some operating systems, it may also be possible to have paths which
100 are relative to the current directory.
103 -----------------------------------------------------------------------------
108 The 'Permissions' type is used to record whether certain operations are
109 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
110 get and set these permissions, respectively. Permissions apply both to
111 files and directories. For directories, the executable field will be
112 'False', and for files the searchable field will be 'False'. Note that
113 directories may be searchable without being readable, if permission has
114 been given to use them as part of a path, but not to examine the
117 Note that to change some, but not all permissions, a construct on the following lines must be used.
119 > makeReadable f = do
120 > p <- getPermissions f
121 > setPermissions f (p {readable = True})
128 executable, searchable :: Bool
129 } deriving (Eq, Ord, Read, Show)
131 {- |The 'getPermissions' operation returns the
132 permissions for the file or directory.
134 The operation may fail with:
136 * 'isPermissionError' if the user is not permitted to access
139 * 'isDoesNotExistError' if the file or directory does not exist.
143 getPermissions :: FilePath -> IO Permissions
144 getPermissions name = do
145 withCString name $ \s -> do
146 read <- c_access s r_OK
147 write <- c_access s w_OK
148 exec <- c_access s x_OK
149 withFileStatus "getPermissions" name $ \st -> do
150 is_dir <- isDirectory st
153 readable = read == 0,
154 writable = write == 0,
155 executable = not is_dir && exec == 0,
156 searchable = is_dir && exec == 0
160 {- |The 'setPermissions' operation sets the
161 permissions for the file or directory.
163 The operation may fail with:
165 * 'isPermissionError' if the user is not permitted to set
168 * 'isDoesNotExistError' if the file or directory does not exist.
172 setPermissions :: FilePath -> Permissions -> IO ()
173 setPermissions name (Permissions r w e s) = do
174 allocaBytes sizeof_stat $ \ p_stat -> do
175 withCString name $ \p_name -> do
176 throwErrnoIfMinus1_ "setPermissions" $ do
178 mode <- st_mode p_stat
179 let mode1 = modifyBit r mode s_IRUSR
180 let mode2 = modifyBit w mode1 s_IWUSR
181 let mode3 = modifyBit (e || s) mode2 s_IXUSR
185 modifyBit :: Bool -> CMode -> CMode -> CMode
186 modifyBit False m b = m .&. (complement b)
187 modifyBit True m b = m .|. b
189 -----------------------------------------------------------------------------
192 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
193 initially empty, or as near to empty as the operating system
196 The operation may fail with:
198 * 'isPermissionError' \/ 'PermissionDenied'
199 The process has insufficient privileges to perform the operation.
202 * 'isAlreadyExistsError' \/ 'AlreadyExists'
203 The operand refers to a directory that already exists.
207 A physical I\/O error has occurred.
211 The operand is not a valid directory name.
212 @[ENAMETOOLONG, ELOOP]@
215 There is no path to the directory.
218 * 'ResourceExhausted'
219 Insufficient resources (virtual memory, process file descriptors,
220 physical disk space, etc.) are available to perform the operation.
221 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
223 * 'InappropriateType'
224 The path refers to an existing non-directory object.
229 createDirectory :: FilePath -> IO ()
230 createDirectory path = do
231 withCString path $ \s -> do
232 throwErrnoIfMinus1Retry_ "createDirectory" $
235 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
236 implementation may specify additional constraints which must be
237 satisfied before a directory can be removed (e.g. the directory has to
238 be empty, or may not be in use by other processes). It is not legal
239 for an implementation to partially remove a directory unless the
240 entire directory is removed. A conformant implementation need not
241 support directory removal in all situations (e.g. removal of the root
244 The operation may fail with:
247 A physical I\/O error has occurred.
251 The operand is not a valid directory name.
252 [ENAMETOOLONG, ELOOP]
254 * 'isDoesNotExistError' \/ 'NoSuchThing'
255 The directory does not exist.
258 * 'isPermissionError' \/ 'PermissionDenied'
259 The process has insufficient privileges to perform the operation.
260 @[EROFS, EACCES, EPERM]@
262 * 'UnsatisfiedConstraints'
263 Implementation-dependent constraints are not satisfied.
264 @[EBUSY, ENOTEMPTY, EEXIST]@
266 * 'UnsupportedOperation'
267 The implementation does not support removal in this situation.
270 * 'InappropriateType'
271 The operand refers to an existing non-directory object.
276 removeDirectory :: FilePath -> IO ()
277 removeDirectory path = do
278 modifyIOError (`ioeSetFileName` path) $
279 withCString path $ \s ->
280 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
282 {- |'removeFile' /file/ removes the directory entry for an existing file
283 /file/, where /file/ is not itself a directory. The
284 implementation may specify additional constraints which must be
285 satisfied before a file can be removed (e.g. the file may not be in
286 use by other processes).
288 The operation may fail with:
291 A physical I\/O error has occurred.
295 The operand is not a valid file name.
296 @[ENAMETOOLONG, ELOOP]@
298 * 'isDoesNotExistError' \/ 'NoSuchThing'
299 The file does not exist.
302 * 'isPermissionError' \/ 'PermissionDenied'
303 The process has insufficient privileges to perform the operation.
304 @[EROFS, EACCES, EPERM]@
306 * 'UnsatisfiedConstraints'
307 Implementation-dependent constraints are not satisfied.
310 * 'InappropriateType'
311 The operand refers to an existing directory.
316 removeFile :: FilePath -> IO ()
318 modifyIOError (`ioeSetFileName` path) $
319 withCString path $ \s ->
320 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
322 {- |@'renameDirectory' old new@ changes the name of an existing
323 directory from /old/ to /new/. If the /new/ directory
324 already exists, it is atomically replaced by the /old/ directory.
325 If the /new/ directory is neither the /old/ directory nor an
326 alias of the /old/ directory, it is removed as if by
327 'removeDirectory'. A conformant implementation need not support
328 renaming directories in all situations (e.g. renaming to an existing
329 directory, or across different physical devices), but the constraints
332 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
335 The operation may fail with:
338 A physical I\/O error has occurred.
342 Either operand is not a valid directory name.
343 @[ENAMETOOLONG, ELOOP]@
345 * 'isDoesNotExistError' \/ 'NoSuchThing'
346 The original directory does not exist, or there is no path to the target.
349 * 'isPermissionError' \/ 'PermissionDenied'
350 The process has insufficient privileges to perform the operation.
351 @[EROFS, EACCES, EPERM]@
353 * 'ResourceExhausted'
354 Insufficient resources are available to perform the operation.
355 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
357 * 'UnsatisfiedConstraints'
358 Implementation-dependent constraints are not satisfied.
359 @[EBUSY, ENOTEMPTY, EEXIST]@
361 * 'UnsupportedOperation'
362 The implementation does not support renaming in this situation.
365 * 'InappropriateType'
366 Either path refers to an existing non-directory object.
371 renameDirectory :: FilePath -> FilePath -> IO ()
372 renameDirectory opath npath =
373 withFileStatus "renameDirectory" opath $ \st -> do
374 is_dir <- isDirectory st
376 then ioException (IOError Nothing InappropriateType "renameDirectory"
377 ("not a directory") (Just opath))
380 withCString opath $ \s1 ->
381 withCString npath $ \s2 ->
382 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
384 {- |@'renameFile' old new@ changes the name of an existing file system
385 object from /old/ to /new/. If the /new/ object already
386 exists, it is atomically replaced by the /old/ object. Neither
387 path may refer to an existing directory. A conformant implementation
388 need not support renaming files in all situations (e.g. renaming
389 across different physical devices), but the constraints must be
392 The operation may fail with:
395 A physical I\/O error has occurred.
399 Either operand is not a valid file name.
400 @[ENAMETOOLONG, ELOOP]@
402 * 'isDoesNotExistError' \/ 'NoSuchThing'
403 The original file does not exist, or there is no path to the target.
406 * 'isPermissionError' \/ 'PermissionDenied'
407 The process has insufficient privileges to perform the operation.
408 @[EROFS, EACCES, EPERM]@
410 * 'ResourceExhausted'
411 Insufficient resources are available to perform the operation.
412 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
414 * 'UnsatisfiedConstraints'
415 Implementation-dependent constraints are not satisfied.
418 * 'UnsupportedOperation'
419 The implementation does not support renaming in this situation.
422 * 'InappropriateType'
423 Either path refers to an existing directory.
424 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
428 renameFile :: FilePath -> FilePath -> IO ()
429 renameFile opath npath =
430 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
431 is_dir <- isDirectory st
433 then ioException (IOError Nothing InappropriateType "renameFile"
434 "is a directory" (Just opath))
437 withCString opath $ \s1 ->
438 withCString npath $ \s2 ->
439 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
441 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
442 If the /new/ file already exists, it is atomically replaced by the /old/ file.
443 Neither path may refer to an existing directory.
445 copyFile :: FilePath -> FilePath -> IO ()
446 copyFile fromFPath toFPath =
447 (bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
448 bracket (openBinaryFile toFPath WriteMode) hClose $ \hTo ->
449 allocaBytes bufferSize $ \buffer ->
450 copyContents hFrom hTo buffer) `catch` (ioError . changeFunName)
454 changeFunName (IOError h iot fun str mb_fp) = IOError h iot "copyFile" str mb_fp
456 copyContents hFrom hTo buffer = do
457 count <- hGetBuf hFrom buffer bufferSize
458 when (count > 0) $ do
459 hPutBuf hTo buffer count
460 copyContents hFrom hTo buffer
463 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
466 The operation may fail with:
469 A physical I\/O error has occurred.
473 The operand is not a valid directory name.
474 @[ENAMETOOLONG, ELOOP]@
476 * 'isDoesNotExistError' \/ 'NoSuchThing'
477 The directory does not exist.
480 * 'isPermissionError' \/ 'PermissionDenied'
481 The process has insufficient privileges to perform the operation.
484 * 'ResourceExhausted'
485 Insufficient resources are available to perform the operation.
488 * 'InappropriateType'
489 The path refers to an existing non-directory object.
494 getDirectoryContents :: FilePath -> IO [FilePath]
495 getDirectoryContents path = do
496 modifyIOError (`ioeSetFileName` path) $
497 alloca $ \ ptr_dEnt ->
499 (withCString path $ \s ->
500 throwErrnoIfNullRetry desc (c_opendir s))
501 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
502 (\p -> loop ptr_dEnt p)
504 desc = "getDirectoryContents"
506 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
507 loop ptr_dEnt dir = do
509 r <- readdir dir ptr_dEnt
512 dEnt <- peek ptr_dEnt
516 entry <- (d_name dEnt >>= peekCString)
518 entries <- loop ptr_dEnt dir
519 return (entry:entries)
520 else do errno <- getErrno
521 if (errno == eINTR) then loop ptr_dEnt dir else do
522 let (Errno eo) = errno
523 if (eo == end_of_dir)
529 {- |If the operating system has a notion of current directories,
530 'getCurrentDirectory' returns an absolute path to the
531 current directory of the calling process.
533 The operation may fail with:
536 A physical I\/O error has occurred.
539 * 'isDoesNotExistError' \/ 'NoSuchThing'
540 There is no path referring to the current directory.
541 @[EPERM, ENOENT, ESTALE...]@
543 * 'isPermissionError' \/ 'PermissionDenied'
544 The process has insufficient privileges to perform the operation.
547 * 'ResourceExhausted'
548 Insufficient resources are available to perform the operation.
550 * 'UnsupportedOperation'
551 The operating system has no notion of current directory.
555 getCurrentDirectory :: IO FilePath
556 getCurrentDirectory = do
557 p <- mallocBytes long_path_size
559 where go p bytes = do
560 p' <- c_getcwd p (fromIntegral bytes)
562 then do s <- peekCString p'
565 else do errno <- getErrno
567 then do let bytes' = bytes * 2
568 p' <- reallocBytes p bytes'
570 else throwErrno "getCurrentDirectory"
572 {- |If the operating system has a notion of current directories,
573 @'setCurrentDirectory' dir@ changes the current
574 directory of the calling process to /dir/.
576 The operation may fail with:
579 A physical I\/O error has occurred.
583 The operand is not a valid directory name.
584 @[ENAMETOOLONG, ELOOP]@
586 * 'isDoesNotExistError' \/ 'NoSuchThing'
587 The directory does not exist.
590 * 'isPermissionError' \/ 'PermissionDenied'
591 The process has insufficient privileges to perform the operation.
594 * 'UnsupportedOperation'
595 The operating system has no notion of current directory, or the
596 current directory cannot be dynamically changed.
598 * 'InappropriateType'
599 The path refers to an existing non-directory object.
604 setCurrentDirectory :: FilePath -> IO ()
605 setCurrentDirectory path = do
606 modifyIOError (`ioeSetFileName` path) $
607 withCString path $ \s ->
608 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
609 -- ToDo: add path to error
611 getHomeDirectory :: IO FilePath
613 #ifdef mingw32_TARGET_OS
614 allocaBytes long_path_size $ \pPath -> do
615 r <- c_SHGetFolderPath nullPtr csidl_PROFILE nullPtr 0 pPath
617 then c_SHGetFolderPath nullPtr csidl_WINDOWS nullPtr 0 pPath
624 getAppUserDataDirectory :: String -> IO FilePath
625 getAppUserDataDirectory appName = do
626 #ifdef mingw32_TARGET_OS
627 allocaBytes long_path_size $ \pPath -> do
628 r <- c_SHGetFolderPath nullPtr csidl_APPDATA nullPtr 0 pPath
629 s <- peekCString pPath
630 return (s++'\\':appName)
632 path <- getEnv "HOME"
633 return (path++'/':'.':appName)
636 #ifdef mingw32_TARGET_OS
637 foreign import stdcall unsafe "SHGetFolderPath"
638 c_SHGetFolderPath :: Ptr ()
644 foreign import ccall unsafe "__hscore_CSIDL_PROFILE" csidl_PROFILE :: Int
645 foreign import ccall unsafe "__hscore_CSIDL_APPDATA" csidl_APPDATA :: Int
646 foreign import ccall unsafe "__hscore_CSIDL_WINDOWS" csidl_WINDOWS :: Int
649 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
650 exists and is a directory, and 'False' otherwise.
653 doesDirectoryExist :: FilePath -> IO Bool
654 doesDirectoryExist name =
656 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
657 (\ _ -> return False)
659 {- |The operation 'doesFileExist' returns 'True'
660 if the argument file exists and is not a directory, and 'False' otherwise.
663 doesFileExist :: FilePath -> IO Bool
664 doesFileExist name = do
666 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
667 (\ _ -> return False)
669 {- |The 'getModificationTime' operation returns the
670 clock time at which the file or directory was last modified.
672 The operation may fail with:
674 * 'isPermissionError' if the user is not permitted to access
675 the modification time; or
677 * 'isDoesNotExistError' if the file or directory does not exist.
681 getModificationTime :: FilePath -> IO ClockTime
682 getModificationTime name =
683 withFileStatus "getModificationTime" name $ \ st ->
686 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
687 withFileStatus loc name f = do
688 modifyIOError (`ioeSetFileName` name) $
689 allocaBytes sizeof_stat $ \p ->
690 withCString (fileNameEndClean name) $ \s -> do
691 throwErrnoIfMinus1Retry_ loc (c_stat s p)
694 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
695 withFileOrSymlinkStatus loc name f = do
696 modifyIOError (`ioeSetFileName` name) $
697 allocaBytes sizeof_stat $ \p ->
698 withCString name $ \s -> do
699 throwErrnoIfMinus1Retry_ loc (lstat s p)
702 modificationTime :: Ptr CStat -> IO ClockTime
703 modificationTime stat = do
704 mtime <- st_mtime stat
705 let realToInteger = round . realToFrac :: Real a => a -> Integer
706 return (TOD (realToInteger (mtime :: CTime)) 0)
708 isDirectory :: Ptr CStat -> IO Bool
709 isDirectory stat = do
711 return (s_isdir mode)
713 fileNameEndClean :: String -> String
714 fileNameEndClean name =
715 if i > 0 && (ec == '\\' || ec == '/') then
716 fileNameEndClean (take i name)
720 i = (length name) - 1
723 foreign import ccall unsafe "__hscore_long_path_size"
724 long_path_size :: Int
726 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
727 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
728 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
730 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
731 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
732 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode