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 import System (getEnv)
67 getHomeDirectory :: IO FilePath
68 getHomeDirectory = getEnv "HOME"
69 getAppUserDataDirectory :: String -> IO FilePath
70 getAppUserDataDirectory appName = do path <- getEnv "HOME"
71 return (path++'/':'.':appName)
72 getUserDocumentsDirectory :: IO FilePath
73 getUserDocumentsDirectory= getEnv "HOME"
74 #elif defined(__HUGS__)
80 import Control.Exception ( bracket )
81 import Control.Monad ( when )
82 import System.Posix.Types
83 import System.Posix.Internals
84 import System.Time ( ClockTime(..) )
86 import System.IO.Error
90 #ifdef __GLASGOW_HASKELL__
91 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
94 #ifndef mingw32_TARGET_OS
95 import System.Environment
99 A directory contains a series of entries, each of which is a named
100 reference to a file system object (file, directory etc.). Some
101 entries may be hidden, inaccessible, or have some administrative
102 function (e.g. `.' or `..' under POSIX
103 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
104 this standard all such entries are considered to form part of the
105 directory contents. Entries in sub-directories are not, however,
106 considered to form part of the directory contents.
108 Each file system object is referenced by a /path/. There is
109 normally at least one absolute path to each file system object. In
110 some operating systems, it may also be possible to have paths which
111 are relative to the current directory.
114 -----------------------------------------------------------------------------
119 The 'Permissions' type is used to record whether certain operations are
120 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
121 get and set these permissions, respectively. Permissions apply both to
122 files and directories. For directories, the executable field will be
123 'False', and for files the searchable field will be 'False'. Note that
124 directories may be searchable without being readable, if permission has
125 been given to use them as part of a path, but not to examine the
128 Note that to change some, but not all permissions, a construct on the following lines must be used.
130 > makeReadable f = do
131 > p <- getPermissions f
132 > setPermissions f (p {readable = True})
139 executable, searchable :: Bool
140 } deriving (Eq, Ord, Read, Show)
142 {- |The 'getPermissions' operation returns the
143 permissions for the file or directory.
145 The operation may fail with:
147 * 'isPermissionError' if the user is not permitted to access
150 * 'isDoesNotExistError' if the file or directory does not exist.
154 getPermissions :: FilePath -> IO Permissions
155 getPermissions name = do
156 withCString name $ \s -> do
157 read <- c_access s r_OK
158 write <- c_access s w_OK
159 exec <- c_access s x_OK
160 withFileStatus "getPermissions" name $ \st -> do
161 is_dir <- isDirectory st
164 readable = read == 0,
165 writable = write == 0,
166 executable = not is_dir && exec == 0,
167 searchable = is_dir && exec == 0
171 {- |The 'setPermissions' operation sets the
172 permissions for the file or directory.
174 The operation may fail with:
176 * 'isPermissionError' if the user is not permitted to set
179 * 'isDoesNotExistError' if the file or directory does not exist.
183 setPermissions :: FilePath -> Permissions -> IO ()
184 setPermissions name (Permissions r w e s) = do
185 allocaBytes sizeof_stat $ \ p_stat -> do
186 withCString name $ \p_name -> do
187 throwErrnoIfMinus1_ "setPermissions" $ do
189 mode <- st_mode p_stat
190 let mode1 = modifyBit r mode s_IRUSR
191 let mode2 = modifyBit w mode1 s_IWUSR
192 let mode3 = modifyBit (e || s) mode2 s_IXUSR
196 modifyBit :: Bool -> CMode -> CMode -> CMode
197 modifyBit False m b = m .&. (complement b)
198 modifyBit True m b = m .|. b
200 -----------------------------------------------------------------------------
203 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
204 initially empty, or as near to empty as the operating system
207 The operation may fail with:
209 * 'isPermissionError' \/ 'PermissionDenied'
210 The process has insufficient privileges to perform the operation.
213 * 'isAlreadyExistsError' \/ 'AlreadyExists'
214 The operand refers to a directory that already exists.
218 A physical I\/O error has occurred.
222 The operand is not a valid directory name.
223 @[ENAMETOOLONG, ELOOP]@
226 There is no path to the directory.
229 * 'ResourceExhausted'
230 Insufficient resources (virtual memory, process file descriptors,
231 physical disk space, etc.) are available to perform the operation.
232 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
234 * 'InappropriateType'
235 The path refers to an existing non-directory object.
240 createDirectory :: FilePath -> IO ()
241 createDirectory path = do
242 withCString path $ \s -> do
243 throwErrnoIfMinus1Retry_ "createDirectory" $
246 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
247 implementation may specify additional constraints which must be
248 satisfied before a directory can be removed (e.g. the directory has to
249 be empty, or may not be in use by other processes). It is not legal
250 for an implementation to partially remove a directory unless the
251 entire directory is removed. A conformant implementation need not
252 support directory removal in all situations (e.g. removal of the root
255 The operation may fail with:
258 A physical I\/O error has occurred.
262 The operand is not a valid directory name.
263 [ENAMETOOLONG, ELOOP]
265 * 'isDoesNotExistError' \/ 'NoSuchThing'
266 The directory does not exist.
269 * 'isPermissionError' \/ 'PermissionDenied'
270 The process has insufficient privileges to perform the operation.
271 @[EROFS, EACCES, EPERM]@
273 * 'UnsatisfiedConstraints'
274 Implementation-dependent constraints are not satisfied.
275 @[EBUSY, ENOTEMPTY, EEXIST]@
277 * 'UnsupportedOperation'
278 The implementation does not support removal in this situation.
281 * 'InappropriateType'
282 The operand refers to an existing non-directory object.
287 removeDirectory :: FilePath -> IO ()
288 removeDirectory path = do
289 modifyIOError (`ioeSetFileName` path) $
290 withCString path $ \s ->
291 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
293 {- |'removeFile' /file/ removes the directory entry for an existing file
294 /file/, where /file/ is not itself a directory. The
295 implementation may specify additional constraints which must be
296 satisfied before a file can be removed (e.g. the file may not be in
297 use by other processes).
299 The operation may fail with:
302 A physical I\/O error has occurred.
306 The operand is not a valid file name.
307 @[ENAMETOOLONG, ELOOP]@
309 * 'isDoesNotExistError' \/ 'NoSuchThing'
310 The file does not exist.
313 * 'isPermissionError' \/ 'PermissionDenied'
314 The process has insufficient privileges to perform the operation.
315 @[EROFS, EACCES, EPERM]@
317 * 'UnsatisfiedConstraints'
318 Implementation-dependent constraints are not satisfied.
321 * 'InappropriateType'
322 The operand refers to an existing directory.
327 removeFile :: FilePath -> IO ()
329 modifyIOError (`ioeSetFileName` path) $
330 withCString path $ \s ->
331 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
333 {- |@'renameDirectory' old new@ changes the name of an existing
334 directory from /old/ to /new/. If the /new/ directory
335 already exists, it is atomically replaced by the /old/ directory.
336 If the /new/ directory is neither the /old/ directory nor an
337 alias of the /old/ directory, it is removed as if by
338 'removeDirectory'. A conformant implementation need not support
339 renaming directories in all situations (e.g. renaming to an existing
340 directory, or across different physical devices), but the constraints
343 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
346 The operation may fail with:
349 A physical I\/O error has occurred.
353 Either operand is not a valid directory name.
354 @[ENAMETOOLONG, ELOOP]@
356 * 'isDoesNotExistError' \/ 'NoSuchThing'
357 The original directory does not exist, or there is no path to the target.
360 * 'isPermissionError' \/ 'PermissionDenied'
361 The process has insufficient privileges to perform the operation.
362 @[EROFS, EACCES, EPERM]@
364 * 'ResourceExhausted'
365 Insufficient resources are available to perform the operation.
366 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
368 * 'UnsatisfiedConstraints'
369 Implementation-dependent constraints are not satisfied.
370 @[EBUSY, ENOTEMPTY, EEXIST]@
372 * 'UnsupportedOperation'
373 The implementation does not support renaming in this situation.
376 * 'InappropriateType'
377 Either path refers to an existing non-directory object.
382 renameDirectory :: FilePath -> FilePath -> IO ()
383 renameDirectory opath npath =
384 withFileStatus "renameDirectory" opath $ \st -> do
385 is_dir <- isDirectory st
387 then ioException (IOError Nothing InappropriateType "renameDirectory"
388 ("not a directory") (Just opath))
391 withCString opath $ \s1 ->
392 withCString npath $ \s2 ->
393 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
395 {- |@'renameFile' old new@ changes the name of an existing file system
396 object from /old/ to /new/. If the /new/ object already
397 exists, it is atomically replaced by the /old/ object. Neither
398 path may refer to an existing directory. A conformant implementation
399 need not support renaming files in all situations (e.g. renaming
400 across different physical devices), but the constraints must be
403 The operation may fail with:
406 A physical I\/O error has occurred.
410 Either operand is not a valid file name.
411 @[ENAMETOOLONG, ELOOP]@
413 * 'isDoesNotExistError' \/ 'NoSuchThing'
414 The original file does not exist, or there is no path to the target.
417 * 'isPermissionError' \/ 'PermissionDenied'
418 The process has insufficient privileges to perform the operation.
419 @[EROFS, EACCES, EPERM]@
421 * 'ResourceExhausted'
422 Insufficient resources are available to perform the operation.
423 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
425 * 'UnsatisfiedConstraints'
426 Implementation-dependent constraints are not satisfied.
429 * 'UnsupportedOperation'
430 The implementation does not support renaming in this situation.
433 * 'InappropriateType'
434 Either path refers to an existing directory.
435 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
439 renameFile :: FilePath -> FilePath -> IO ()
440 renameFile opath npath =
441 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
442 is_dir <- isDirectory st
444 then ioException (IOError Nothing InappropriateType "renameFile"
445 "is a directory" (Just opath))
448 withCString opath $ \s1 ->
449 withCString npath $ \s2 ->
450 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
452 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
453 If the /new/ file already exists, it is atomically replaced by the /old/ file.
454 Neither path may refer to an existing directory.
456 copyFile :: FilePath -> FilePath -> IO ()
457 copyFile fromFPath toFPath =
458 (bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
459 bracket (openBinaryFile toFPath WriteMode) hClose $ \hTo ->
460 allocaBytes bufferSize $ \buffer ->
461 copyContents hFrom hTo buffer) `catch` (ioError . changeFunName)
465 changeFunName (IOError h iot fun str mb_fp) = IOError h iot "copyFile" str mb_fp
467 copyContents hFrom hTo buffer = do
468 count <- hGetBuf hFrom buffer bufferSize
469 when (count > 0) $ do
470 hPutBuf hTo buffer count
471 copyContents hFrom hTo buffer
474 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
477 The operation may fail with:
480 A physical I\/O error has occurred.
484 The operand is not a valid directory name.
485 @[ENAMETOOLONG, ELOOP]@
487 * 'isDoesNotExistError' \/ 'NoSuchThing'
488 The directory does not exist.
491 * 'isPermissionError' \/ 'PermissionDenied'
492 The process has insufficient privileges to perform the operation.
495 * 'ResourceExhausted'
496 Insufficient resources are available to perform the operation.
499 * 'InappropriateType'
500 The path refers to an existing non-directory object.
505 getDirectoryContents :: FilePath -> IO [FilePath]
506 getDirectoryContents path = do
507 modifyIOError (`ioeSetFileName` path) $
508 alloca $ \ ptr_dEnt ->
510 (withCString path $ \s ->
511 throwErrnoIfNullRetry desc (c_opendir s))
512 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
513 (\p -> loop ptr_dEnt p)
515 desc = "getDirectoryContents"
517 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
518 loop ptr_dEnt dir = do
520 r <- readdir dir ptr_dEnt
523 dEnt <- peek ptr_dEnt
527 entry <- (d_name dEnt >>= peekCString)
529 entries <- loop ptr_dEnt dir
530 return (entry:entries)
531 else do errno <- getErrno
532 if (errno == eINTR) then loop ptr_dEnt dir else do
533 let (Errno eo) = errno
534 if (eo == end_of_dir)
540 {- |If the operating system has a notion of current directories,
541 'getCurrentDirectory' returns an absolute path to the
542 current directory of the calling process.
544 The operation may fail with:
547 A physical I\/O error has occurred.
550 * 'isDoesNotExistError' \/ 'NoSuchThing'
551 There is no path referring to the current directory.
552 @[EPERM, ENOENT, ESTALE...]@
554 * 'isPermissionError' \/ 'PermissionDenied'
555 The process has insufficient privileges to perform the operation.
558 * 'ResourceExhausted'
559 Insufficient resources are available to perform the operation.
561 * 'UnsupportedOperation'
562 The operating system has no notion of current directory.
566 getCurrentDirectory :: IO FilePath
567 getCurrentDirectory = do
568 p <- mallocBytes long_path_size
570 where go p bytes = do
571 p' <- c_getcwd p (fromIntegral bytes)
573 then do s <- peekCString p'
576 else do errno <- getErrno
578 then do let bytes' = bytes * 2
579 p' <- reallocBytes p bytes'
581 else throwErrno "getCurrentDirectory"
583 {- |If the operating system has a notion of current directories,
584 @'setCurrentDirectory' dir@ changes the current
585 directory of the calling process to /dir/.
587 The operation may fail with:
590 A physical I\/O error has occurred.
594 The operand is not a valid directory name.
595 @[ENAMETOOLONG, ELOOP]@
597 * 'isDoesNotExistError' \/ 'NoSuchThing'
598 The directory does not exist.
601 * 'isPermissionError' \/ 'PermissionDenied'
602 The process has insufficient privileges to perform the operation.
605 * 'UnsupportedOperation'
606 The operating system has no notion of current directory, or the
607 current directory cannot be dynamically changed.
609 * 'InappropriateType'
610 The path refers to an existing non-directory object.
615 setCurrentDirectory :: FilePath -> IO ()
616 setCurrentDirectory path = do
617 modifyIOError (`ioeSetFileName` path) $
618 withCString path $ \s ->
619 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
620 -- ToDo: add path to error
622 {- | Returns the current user's home directory.
624 The directory returned is expected to be writable by the current user,
625 but note that it isn't generally considered good practice to store
626 application-specific data here; use 'getAppUserDataDirectory'
629 On Unix, 'getHomeDirectory' returns the value of the @HOME@
630 environment variable. On Windows, the system is queried for a
631 suitable path; a typical path might be
632 @C:/Documents And Settings/user@.
634 The operation may fail with:
636 * 'UnsupportedOperation'
637 The operating system has no notion of home directory.
639 * 'isDoesNotExistError'
640 The home directory for the current user does not exist, or
643 getHomeDirectory :: IO FilePath
645 #ifdef mingw32_TARGET_OS
646 allocaBytes long_path_size $ \pPath -> do
647 r <- c_SHGetFolderPath nullPtr csidl_PROFILE nullPtr 0 pPath
649 then c_SHGetFolderPath nullPtr csidl_WINDOWS nullPtr 0 pPath
656 {- | Returns the pathname of a directory in which application-specific
657 data for the current user can be stored. The result of
658 'getAppUserDataDirectory' for a given application is specific to
661 The argument should be the name of the application, which will be used
662 to construct the pathname (so avoid using unusual characters that
663 might result in an invalid pathname).
665 Note: the directory may not actually exist, and may need to be created
666 first. It is expected that the parent directory exists and is
669 On Unix, this function returns @$HOME\/.appName@. On Windows, a
670 typical path might be
672 > C:/Documents And Settings/user/Application Data/appName
674 The operation may fail with:
676 * 'UnsupportedOperation'
677 The operating system has no notion of application-specific data directory.
679 * 'isDoesNotExistError'
680 The home directory for the current user does not exist, or
683 getAppUserDataDirectory :: String -> IO FilePath
684 getAppUserDataDirectory appName = do
685 #ifdef mingw32_TARGET_OS
686 allocaBytes long_path_size $ \pPath -> do
687 r <- c_SHGetFolderPath nullPtr csidl_APPDATA nullPtr 0 pPath
688 s <- peekCString pPath
689 return (s++'\\':appName)
691 path <- getEnv "HOME"
692 return (path++'/':'.':appName)
695 {- | Returns the current user's document directory.
697 The directory returned is expected to be writable by the current user,
698 but note that it isn't generally considered good practice to store
699 application-specific data here; use 'getAppUserDataDirectory'
702 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
703 environment variable. On Windows, the system is queried for a
704 suitable path; a typical path might be
705 @C:\/Documents and Settings\/user\/My Documents@.
707 The operation may fail with:
709 * 'UnsupportedOperation'
710 The operating system has no notion of document directory.
712 * 'isDoesNotExistError'
713 The document directory for the current user does not exist, or
716 getUserDocumentsDirectory :: IO FilePath
717 getUserDocumentsDirectory = do
718 #ifdef mingw32_TARGET_OS
719 allocaBytes long_path_size $ \pPath -> do
720 r <- c_SHGetFolderPath nullPtr csidl_PERSONAL nullPtr 0 pPath
726 #ifdef mingw32_TARGET_OS
727 foreign import stdcall unsafe "SHGetFolderPath"
728 c_SHGetFolderPath :: Ptr ()
734 foreign import ccall unsafe "__hscore_CSIDL_PROFILE" csidl_PROFILE :: CInt
735 foreign import ccall unsafe "__hscore_CSIDL_APPDATA" csidl_APPDATA :: CInt
736 foreign import ccall unsafe "__hscore_CSIDL_WINDOWS" csidl_WINDOWS :: CInt
737 foreign import ccall unsafe "__hscore_CSIDL_PERSONAL" csidl_PERSONAL :: CInt
740 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
741 exists and is a directory, and 'False' otherwise.
744 doesDirectoryExist :: FilePath -> IO Bool
745 doesDirectoryExist name =
747 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
748 (\ _ -> return False)
750 {- |The operation 'doesFileExist' returns 'True'
751 if the argument file exists and is not a directory, and 'False' otherwise.
754 doesFileExist :: FilePath -> IO Bool
755 doesFileExist name = do
757 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
758 (\ _ -> return False)
760 {- |The 'getModificationTime' operation returns the
761 clock time at which the file or directory was last modified.
763 The operation may fail with:
765 * 'isPermissionError' if the user is not permitted to access
766 the modification time; or
768 * 'isDoesNotExistError' if the file or directory does not exist.
772 getModificationTime :: FilePath -> IO ClockTime
773 getModificationTime name =
774 withFileStatus "getModificationTime" name $ \ st ->
777 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
778 withFileStatus loc name f = do
779 modifyIOError (`ioeSetFileName` name) $
780 allocaBytes sizeof_stat $ \p ->
781 withCString (fileNameEndClean name) $ \s -> do
782 throwErrnoIfMinus1Retry_ loc (c_stat s p)
785 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
786 withFileOrSymlinkStatus loc name f = do
787 modifyIOError (`ioeSetFileName` name) $
788 allocaBytes sizeof_stat $ \p ->
789 withCString name $ \s -> do
790 throwErrnoIfMinus1Retry_ loc (lstat s p)
793 modificationTime :: Ptr CStat -> IO ClockTime
794 modificationTime stat = do
795 mtime <- st_mtime stat
796 let realToInteger = round . realToFrac :: Real a => a -> Integer
797 return (TOD (realToInteger (mtime :: CTime)) 0)
799 isDirectory :: Ptr CStat -> IO Bool
800 isDirectory stat = do
802 return (s_isdir mode)
804 fileNameEndClean :: String -> String
805 fileNameEndClean name =
806 if i > 0 && (ec == '\\' || ec == '/') then
807 fileNameEndClean (take i name)
811 i = (length name) - 1
814 foreign import ccall unsafe "__hscore_long_path_size"
815 long_path_size :: Int
817 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
818 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
819 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
821 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
822 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
823 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode