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 -- * Pre-defined directories
30 , getAppUserDataDirectory
31 , getUserDocumentsDirectory
34 , removeFile -- :: FilePath -> IO ()
35 , renameFile -- :: FilePath -> FilePath -> IO ()
36 #ifdef __GLASGOW_HASKELL__
37 , copyFile -- :: FilePath -> FilePath -> IO ()
41 , doesFileExist -- :: FilePath -> IO Bool
42 , doesDirectoryExist -- :: FilePath -> IO Bool
50 readable, -- :: Permissions -> Bool
51 writable, -- :: Permissions -> Bool
52 executable, -- :: Permissions -> Bool
53 searchable -- :: Permissions -> Bool
56 , getPermissions -- :: FilePath -> IO Permissions
57 , setPermissions -- :: FilePath -> Permissions -> IO ()
61 , getModificationTime -- :: FilePath -> IO ClockTime
66 getHomeDirectory :: IO FilePath
67 getHomeDirectory = getEnv "HOME"
68 getAppUserDataDirectory :: String -> IO FilePath
69 getAppUserDataDirectory appName = do path <- getEnv "HOME"
70 return (path++'/':'.':appName)
71 getUserDocumentsDirectory :: IO FilePath
72 getUserDocumentsDirectory= getEnv "HOME"
73 #elif defined(__HUGS__)
79 import Control.Exception ( bracket )
80 import Control.Monad ( when )
81 import System.Posix.Types
82 import System.Posix.Internals
83 import System.Time ( ClockTime(..) )
85 import System.IO.Error
89 #ifdef __GLASGOW_HASKELL__
90 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
93 #ifndef mingw32_TARGET_OS
94 import System.Environment
98 A directory contains a series of entries, each of which is a named
99 reference to a file system object (file, directory etc.). Some
100 entries may be hidden, inaccessible, or have some administrative
101 function (e.g. `.' or `..' under POSIX
102 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
103 this standard all such entries are considered to form part of the
104 directory contents. Entries in sub-directories are not, however,
105 considered to form part of the directory contents.
107 Each file system object is referenced by a /path/. There is
108 normally at least one absolute path to each file system object. In
109 some operating systems, it may also be possible to have paths which
110 are relative to the current directory.
113 -----------------------------------------------------------------------------
118 The 'Permissions' type is used to record whether certain operations are
119 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
120 get and set these permissions, respectively. Permissions apply both to
121 files and directories. For directories, the executable field will be
122 'False', and for files the searchable field will be 'False'. Note that
123 directories may be searchable without being readable, if permission has
124 been given to use them as part of a path, but not to examine the
127 Note that to change some, but not all permissions, a construct on the following lines must be used.
129 > makeReadable f = do
130 > p <- getPermissions f
131 > setPermissions f (p {readable = True})
138 executable, searchable :: Bool
139 } deriving (Eq, Ord, Read, Show)
141 {- |The 'getPermissions' operation returns the
142 permissions for the file or directory.
144 The operation may fail with:
146 * 'isPermissionError' if the user is not permitted to access
149 * 'isDoesNotExistError' if the file or directory does not exist.
153 getPermissions :: FilePath -> IO Permissions
154 getPermissions name = do
155 withCString name $ \s -> do
156 read <- c_access s r_OK
157 write <- c_access s w_OK
158 exec <- c_access s x_OK
159 withFileStatus "getPermissions" name $ \st -> do
160 is_dir <- isDirectory st
163 readable = read == 0,
164 writable = write == 0,
165 executable = not is_dir && exec == 0,
166 searchable = is_dir && exec == 0
170 {- |The 'setPermissions' operation sets the
171 permissions for the file or directory.
173 The operation may fail with:
175 * 'isPermissionError' if the user is not permitted to set
178 * 'isDoesNotExistError' if the file or directory does not exist.
182 setPermissions :: FilePath -> Permissions -> IO ()
183 setPermissions name (Permissions r w e s) = do
184 allocaBytes sizeof_stat $ \ p_stat -> do
185 withCString name $ \p_name -> do
186 throwErrnoIfMinus1_ "setPermissions" $ do
188 mode <- st_mode p_stat
189 let mode1 = modifyBit r mode s_IRUSR
190 let mode2 = modifyBit w mode1 s_IWUSR
191 let mode3 = modifyBit (e || s) mode2 s_IXUSR
195 modifyBit :: Bool -> CMode -> CMode -> CMode
196 modifyBit False m b = m .&. (complement b)
197 modifyBit True m b = m .|. b
199 -----------------------------------------------------------------------------
202 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
203 initially empty, or as near to empty as the operating system
206 The operation may fail with:
208 * 'isPermissionError' \/ 'PermissionDenied'
209 The process has insufficient privileges to perform the operation.
212 * 'isAlreadyExistsError' \/ 'AlreadyExists'
213 The operand refers to a directory that already exists.
217 A physical I\/O error has occurred.
221 The operand is not a valid directory name.
222 @[ENAMETOOLONG, ELOOP]@
225 There is no path to the directory.
228 * 'ResourceExhausted'
229 Insufficient resources (virtual memory, process file descriptors,
230 physical disk space, etc.) are available to perform the operation.
231 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
233 * 'InappropriateType'
234 The path refers to an existing non-directory object.
239 createDirectory :: FilePath -> IO ()
240 createDirectory path = do
241 withCString path $ \s -> do
242 throwErrnoIfMinus1Retry_ "createDirectory" $
245 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
246 implementation may specify additional constraints which must be
247 satisfied before a directory can be removed (e.g. the directory has to
248 be empty, or may not be in use by other processes). It is not legal
249 for an implementation to partially remove a directory unless the
250 entire directory is removed. A conformant implementation need not
251 support directory removal in all situations (e.g. removal of the root
254 The operation may fail with:
257 A physical I\/O error has occurred.
261 The operand is not a valid directory name.
262 [ENAMETOOLONG, ELOOP]
264 * 'isDoesNotExistError' \/ 'NoSuchThing'
265 The directory does not exist.
268 * 'isPermissionError' \/ 'PermissionDenied'
269 The process has insufficient privileges to perform the operation.
270 @[EROFS, EACCES, EPERM]@
272 * 'UnsatisfiedConstraints'
273 Implementation-dependent constraints are not satisfied.
274 @[EBUSY, ENOTEMPTY, EEXIST]@
276 * 'UnsupportedOperation'
277 The implementation does not support removal in this situation.
280 * 'InappropriateType'
281 The operand refers to an existing non-directory object.
286 removeDirectory :: FilePath -> IO ()
287 removeDirectory path = do
288 modifyIOError (`ioeSetFileName` path) $
289 withCString path $ \s ->
290 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
292 {- |'removeFile' /file/ removes the directory entry for an existing file
293 /file/, where /file/ is not itself a directory. The
294 implementation may specify additional constraints which must be
295 satisfied before a file can be removed (e.g. the file may not be in
296 use by other processes).
298 The operation may fail with:
301 A physical I\/O error has occurred.
305 The operand is not a valid file name.
306 @[ENAMETOOLONG, ELOOP]@
308 * 'isDoesNotExistError' \/ 'NoSuchThing'
309 The file does not exist.
312 * 'isPermissionError' \/ 'PermissionDenied'
313 The process has insufficient privileges to perform the operation.
314 @[EROFS, EACCES, EPERM]@
316 * 'UnsatisfiedConstraints'
317 Implementation-dependent constraints are not satisfied.
320 * 'InappropriateType'
321 The operand refers to an existing directory.
326 removeFile :: FilePath -> IO ()
328 modifyIOError (`ioeSetFileName` path) $
329 withCString path $ \s ->
330 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
332 {- |@'renameDirectory' old new@ changes the name of an existing
333 directory from /old/ to /new/. If the /new/ directory
334 already exists, it is atomically replaced by the /old/ directory.
335 If the /new/ directory is neither the /old/ directory nor an
336 alias of the /old/ directory, it is removed as if by
337 'removeDirectory'. A conformant implementation need not support
338 renaming directories in all situations (e.g. renaming to an existing
339 directory, or across different physical devices), but the constraints
342 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
345 The operation may fail with:
348 A physical I\/O error has occurred.
352 Either operand is not a valid directory name.
353 @[ENAMETOOLONG, ELOOP]@
355 * 'isDoesNotExistError' \/ 'NoSuchThing'
356 The original directory does not exist, or there is no path to the target.
359 * 'isPermissionError' \/ 'PermissionDenied'
360 The process has insufficient privileges to perform the operation.
361 @[EROFS, EACCES, EPERM]@
363 * 'ResourceExhausted'
364 Insufficient resources are available to perform the operation.
365 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
367 * 'UnsatisfiedConstraints'
368 Implementation-dependent constraints are not satisfied.
369 @[EBUSY, ENOTEMPTY, EEXIST]@
371 * 'UnsupportedOperation'
372 The implementation does not support renaming in this situation.
375 * 'InappropriateType'
376 Either path refers to an existing non-directory object.
381 renameDirectory :: FilePath -> FilePath -> IO ()
382 renameDirectory opath npath =
383 withFileStatus "renameDirectory" opath $ \st -> do
384 is_dir <- isDirectory st
386 then ioException (IOError Nothing InappropriateType "renameDirectory"
387 ("not a directory") (Just opath))
390 withCString opath $ \s1 ->
391 withCString npath $ \s2 ->
392 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
394 {- |@'renameFile' old new@ changes the name of an existing file system
395 object from /old/ to /new/. If the /new/ object already
396 exists, it is atomically replaced by the /old/ object. Neither
397 path may refer to an existing directory. A conformant implementation
398 need not support renaming files in all situations (e.g. renaming
399 across different physical devices), but the constraints must be
402 The operation may fail with:
405 A physical I\/O error has occurred.
409 Either operand is not a valid file name.
410 @[ENAMETOOLONG, ELOOP]@
412 * 'isDoesNotExistError' \/ 'NoSuchThing'
413 The original file does not exist, or there is no path to the target.
416 * 'isPermissionError' \/ 'PermissionDenied'
417 The process has insufficient privileges to perform the operation.
418 @[EROFS, EACCES, EPERM]@
420 * 'ResourceExhausted'
421 Insufficient resources are available to perform the operation.
422 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
424 * 'UnsatisfiedConstraints'
425 Implementation-dependent constraints are not satisfied.
428 * 'UnsupportedOperation'
429 The implementation does not support renaming in this situation.
432 * 'InappropriateType'
433 Either path refers to an existing directory.
434 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
438 renameFile :: FilePath -> FilePath -> IO ()
439 renameFile opath npath =
440 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
441 is_dir <- isDirectory st
443 then ioException (IOError Nothing InappropriateType "renameFile"
444 "is a directory" (Just opath))
447 withCString opath $ \s1 ->
448 withCString npath $ \s2 ->
449 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
451 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
452 If the /new/ file already exists, it is atomically replaced by the /old/ file.
453 Neither path may refer to an existing directory.
455 copyFile :: FilePath -> FilePath -> IO ()
456 copyFile fromFPath toFPath =
457 (bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
458 bracket (openBinaryFile toFPath WriteMode) hClose $ \hTo ->
459 allocaBytes bufferSize $ \buffer ->
460 copyContents hFrom hTo buffer) `catch` (ioError . changeFunName)
464 changeFunName (IOError h iot fun str mb_fp) = IOError h iot "copyFile" str mb_fp
466 copyContents hFrom hTo buffer = do
467 count <- hGetBuf hFrom buffer bufferSize
468 when (count > 0) $ do
469 hPutBuf hTo buffer count
470 copyContents hFrom hTo buffer
473 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
476 The operation may fail with:
479 A physical I\/O error has occurred.
483 The operand is not a valid directory name.
484 @[ENAMETOOLONG, ELOOP]@
486 * 'isDoesNotExistError' \/ 'NoSuchThing'
487 The directory does not exist.
490 * 'isPermissionError' \/ 'PermissionDenied'
491 The process has insufficient privileges to perform the operation.
494 * 'ResourceExhausted'
495 Insufficient resources are available to perform the operation.
498 * 'InappropriateType'
499 The path refers to an existing non-directory object.
504 getDirectoryContents :: FilePath -> IO [FilePath]
505 getDirectoryContents path = do
506 modifyIOError (`ioeSetFileName` path) $
507 alloca $ \ ptr_dEnt ->
509 (withCString path $ \s ->
510 throwErrnoIfNullRetry desc (c_opendir s))
511 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
512 (\p -> loop ptr_dEnt p)
514 desc = "getDirectoryContents"
516 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
517 loop ptr_dEnt dir = do
519 r <- readdir dir ptr_dEnt
522 dEnt <- peek ptr_dEnt
526 entry <- (d_name dEnt >>= peekCString)
528 entries <- loop ptr_dEnt dir
529 return (entry:entries)
530 else do errno <- getErrno
531 if (errno == eINTR) then loop ptr_dEnt dir else do
532 let (Errno eo) = errno
533 if (eo == end_of_dir)
539 {- |If the operating system has a notion of current directories,
540 'getCurrentDirectory' returns an absolute path to the
541 current directory of the calling process.
543 The operation may fail with:
546 A physical I\/O error has occurred.
549 * 'isDoesNotExistError' \/ 'NoSuchThing'
550 There is no path referring to the current directory.
551 @[EPERM, ENOENT, ESTALE...]@
553 * 'isPermissionError' \/ 'PermissionDenied'
554 The process has insufficient privileges to perform the operation.
557 * 'ResourceExhausted'
558 Insufficient resources are available to perform the operation.
560 * 'UnsupportedOperation'
561 The operating system has no notion of current directory.
565 getCurrentDirectory :: IO FilePath
566 getCurrentDirectory = do
567 p <- mallocBytes long_path_size
569 where go p bytes = do
570 p' <- c_getcwd p (fromIntegral bytes)
572 then do s <- peekCString p'
575 else do errno <- getErrno
577 then do let bytes' = bytes * 2
578 p' <- reallocBytes p bytes'
580 else throwErrno "getCurrentDirectory"
582 {- |If the operating system has a notion of current directories,
583 @'setCurrentDirectory' dir@ changes the current
584 directory of the calling process to /dir/.
586 The operation may fail with:
589 A physical I\/O error has occurred.
593 The operand is not a valid directory name.
594 @[ENAMETOOLONG, ELOOP]@
596 * 'isDoesNotExistError' \/ 'NoSuchThing'
597 The directory does not exist.
600 * 'isPermissionError' \/ 'PermissionDenied'
601 The process has insufficient privileges to perform the operation.
604 * 'UnsupportedOperation'
605 The operating system has no notion of current directory, or the
606 current directory cannot be dynamically changed.
608 * 'InappropriateType'
609 The path refers to an existing non-directory object.
614 setCurrentDirectory :: FilePath -> IO ()
615 setCurrentDirectory path = do
616 modifyIOError (`ioeSetFileName` path) $
617 withCString path $ \s ->
618 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
619 -- ToDo: add path to error
621 {- | Returns the current user's home directory.
623 The directory returned is expected to be writable by the current user,
624 but note that it isn't generally considered good practice to store
625 application-specific data here; use 'getAppUserDataDirectory'
628 On Unix, 'getHomeDirectory' returns the value of the @HOME@
629 environment variable. On Windows, the system is queried for a
630 suitable path; a typical path might be
631 @C:/Documents And Settings/user@.
633 The operation may fail with:
635 * 'UnsupportedOperation'
636 The operating system has no notion of home directory.
638 * 'isDoesNotExistError'
639 The home directory for the current user does not exist, or
642 getHomeDirectory :: IO FilePath
644 #ifdef mingw32_TARGET_OS
645 allocaBytes long_path_size $ \pPath -> do
646 r <- c_SHGetFolderPath nullPtr csidl_PROFILE nullPtr 0 pPath
648 then c_SHGetFolderPath nullPtr csidl_WINDOWS nullPtr 0 pPath
655 {- | Returns the pathname of a directory in which application-specific
656 data for the current user can be stored. The result of
657 'getAppUserDataDirectory' for a given application is specific to
660 The argument should be the name of the application, which will be used
661 to construct the pathname (so avoid using unusual characters that
662 might result in an invalid pathname).
664 Note: the directory may not actually exist, and may need to be created
665 first. It is expected that the parent directory exists and is
668 On Unix, this function returns @$HOME\/.appName@. On Windows, a
669 typical path might be
671 > C:/Documents And Settings/user/Application Data/appName
673 The operation may fail with:
675 * 'UnsupportedOperation'
676 The operating system has no notion of application-specific data directory.
678 * 'isDoesNotExistError'
679 The home directory for the current user does not exist, or
682 getAppUserDataDirectory :: String -> IO FilePath
683 getAppUserDataDirectory appName = do
684 #ifdef mingw32_TARGET_OS
685 allocaBytes long_path_size $ \pPath -> do
686 r <- c_SHGetFolderPath nullPtr csidl_APPDATA nullPtr 0 pPath
687 s <- peekCString pPath
688 return (s++'\\':appName)
690 path <- getEnv "HOME"
691 return (path++'/':'.':appName)
694 {- | Returns the current user's document directory.
696 The directory returned is expected to be writable by the current user,
697 but note that it isn't generally considered good practice to store
698 application-specific data here; use 'getAppUserDataDirectory'
701 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
702 environment variable. On Windows, the system is queried for a
703 suitable path; a typical path might be
704 @C:/Documents and Settings/user/My Documents@.
706 The operation may fail with:
708 * 'UnsupportedOperation'
709 The operating system has no notion of document directory.
711 * 'isDoesNotExistError'
712 The document directory for the current user does not exist, or
715 getUserDocumentsDirectory :: IO FilePath
716 getUserDocumentsDirectory = do
717 #ifdef mingw32_TARGET_OS
718 allocaBytes long_path_size $ \pPath -> do
719 r <- c_SHGetFolderPath nullPtr csidl_PERSONAL nullPtr 0 pPath
725 #ifdef mingw32_TARGET_OS
726 foreign import stdcall unsafe "SHGetFolderPath"
727 c_SHGetFolderPath :: Ptr ()
733 foreign import ccall unsafe "__hscore_CSIDL_PROFILE" csidl_PROFILE :: CInt
734 foreign import ccall unsafe "__hscore_CSIDL_APPDATA" csidl_APPDATA :: CInt
735 foreign import ccall unsafe "__hscore_CSIDL_WINDOWS" csidl_WINDOWS :: CInt
736 foreign import ccall unsafe "__hscore_CSIDL_PERSONAL" csidl_PERSONAL :: CInt
739 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
740 exists and is a directory, and 'False' otherwise.
743 doesDirectoryExist :: FilePath -> IO Bool
744 doesDirectoryExist name =
746 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
747 (\ _ -> return False)
749 {- |The operation 'doesFileExist' returns 'True'
750 if the argument file exists and is not a directory, and 'False' otherwise.
753 doesFileExist :: FilePath -> IO Bool
754 doesFileExist name = do
756 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
757 (\ _ -> return False)
759 {- |The 'getModificationTime' operation returns the
760 clock time at which the file or directory was last modified.
762 The operation may fail with:
764 * 'isPermissionError' if the user is not permitted to access
765 the modification time; or
767 * 'isDoesNotExistError' if the file or directory does not exist.
771 getModificationTime :: FilePath -> IO ClockTime
772 getModificationTime name =
773 withFileStatus "getModificationTime" name $ \ st ->
776 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
777 withFileStatus loc name f = do
778 modifyIOError (`ioeSetFileName` name) $
779 allocaBytes sizeof_stat $ \p ->
780 withCString (fileNameEndClean name) $ \s -> do
781 throwErrnoIfMinus1Retry_ loc (c_stat s p)
784 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
785 withFileOrSymlinkStatus loc name f = do
786 modifyIOError (`ioeSetFileName` name) $
787 allocaBytes sizeof_stat $ \p ->
788 withCString name $ \s -> do
789 throwErrnoIfMinus1Retry_ loc (lstat s p)
792 modificationTime :: Ptr CStat -> IO ClockTime
793 modificationTime stat = do
794 mtime <- st_mtime stat
795 let realToInteger = round . realToFrac :: Real a => a -> Integer
796 return (TOD (realToInteger (mtime :: CTime)) 0)
798 isDirectory :: Ptr CStat -> IO Bool
799 isDirectory stat = do
801 return (s_isdir mode)
803 fileNameEndClean :: String -> String
804 fileNameEndClean name =
805 if i > 0 && (ec == '\\' || ec == '/') then
806 fileNameEndClean (take i name)
810 i = (length name) - 1
813 foreign import ccall unsafe "__hscore_long_path_size"
814 long_path_size :: Int
816 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
817 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
818 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
820 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
821 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
822 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode