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 getHomeDirectory :: IO FilePath
64 getHomeDirectory = getEnv "HOME"
65 getAppUserDataDirectory :: String -> IO FilePath
66 getAppUserDataDirectory appName = do path <- getEnv "HOME"
67 return (path++'/':'.':appName)
68 #elif defined(__HUGS__)
74 import Control.Exception ( bracket )
75 import Control.Monad ( when )
76 import System.Posix.Types
77 import System.Posix.Internals
78 import System.Time ( ClockTime(..) )
80 import System.IO.Error
84 #ifdef __GLASGOW_HASKELL__
85 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
88 #ifndef mingw32_TARGET_OS
89 import System.Environment
93 A directory contains a series of entries, each of which is a named
94 reference to a file system object (file, directory etc.). Some
95 entries may be hidden, inaccessible, or have some administrative
96 function (e.g. `.' or `..' under POSIX
97 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
98 this standard all such entries are considered to form part of the
99 directory contents. Entries in sub-directories are not, however,
100 considered to form part of the directory contents.
102 Each file system object is referenced by a /path/. There is
103 normally at least one absolute path to each file system object. In
104 some operating systems, it may also be possible to have paths which
105 are relative to the current directory.
108 -----------------------------------------------------------------------------
113 The 'Permissions' type is used to record whether certain operations are
114 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
115 get and set these permissions, respectively. Permissions apply both to
116 files and directories. For directories, the executable field will be
117 'False', and for files the searchable field will be 'False'. Note that
118 directories may be searchable without being readable, if permission has
119 been given to use them as part of a path, but not to examine the
122 Note that to change some, but not all permissions, a construct on the following lines must be used.
124 > makeReadable f = do
125 > p <- getPermissions f
126 > setPermissions f (p {readable = True})
133 executable, searchable :: Bool
134 } deriving (Eq, Ord, Read, Show)
136 {- |The 'getPermissions' operation returns the
137 permissions for the file or directory.
139 The operation may fail with:
141 * 'isPermissionError' if the user is not permitted to access
144 * 'isDoesNotExistError' if the file or directory does not exist.
148 getPermissions :: FilePath -> IO Permissions
149 getPermissions name = do
150 withCString name $ \s -> do
151 read <- c_access s r_OK
152 write <- c_access s w_OK
153 exec <- c_access s x_OK
154 withFileStatus "getPermissions" name $ \st -> do
155 is_dir <- isDirectory st
158 readable = read == 0,
159 writable = write == 0,
160 executable = not is_dir && exec == 0,
161 searchable = is_dir && exec == 0
165 {- |The 'setPermissions' operation sets the
166 permissions for the file or directory.
168 The operation may fail with:
170 * 'isPermissionError' if the user is not permitted to set
173 * 'isDoesNotExistError' if the file or directory does not exist.
177 setPermissions :: FilePath -> Permissions -> IO ()
178 setPermissions name (Permissions r w e s) = do
179 allocaBytes sizeof_stat $ \ p_stat -> do
180 withCString name $ \p_name -> do
181 throwErrnoIfMinus1_ "setPermissions" $ do
183 mode <- st_mode p_stat
184 let mode1 = modifyBit r mode s_IRUSR
185 let mode2 = modifyBit w mode1 s_IWUSR
186 let mode3 = modifyBit (e || s) mode2 s_IXUSR
190 modifyBit :: Bool -> CMode -> CMode -> CMode
191 modifyBit False m b = m .&. (complement b)
192 modifyBit True m b = m .|. b
194 -----------------------------------------------------------------------------
197 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
198 initially empty, or as near to empty as the operating system
201 The operation may fail with:
203 * 'isPermissionError' \/ 'PermissionDenied'
204 The process has insufficient privileges to perform the operation.
207 * 'isAlreadyExistsError' \/ 'AlreadyExists'
208 The operand refers to a directory that already exists.
212 A physical I\/O error has occurred.
216 The operand is not a valid directory name.
217 @[ENAMETOOLONG, ELOOP]@
220 There is no path to the directory.
223 * 'ResourceExhausted'
224 Insufficient resources (virtual memory, process file descriptors,
225 physical disk space, etc.) are available to perform the operation.
226 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
228 * 'InappropriateType'
229 The path refers to an existing non-directory object.
234 createDirectory :: FilePath -> IO ()
235 createDirectory path = do
236 withCString path $ \s -> do
237 throwErrnoIfMinus1Retry_ "createDirectory" $
240 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
241 implementation may specify additional constraints which must be
242 satisfied before a directory can be removed (e.g. the directory has to
243 be empty, or may not be in use by other processes). It is not legal
244 for an implementation to partially remove a directory unless the
245 entire directory is removed. A conformant implementation need not
246 support directory removal in all situations (e.g. removal of the root
249 The operation may fail with:
252 A physical I\/O error has occurred.
256 The operand is not a valid directory name.
257 [ENAMETOOLONG, ELOOP]
259 * 'isDoesNotExistError' \/ 'NoSuchThing'
260 The directory does not exist.
263 * 'isPermissionError' \/ 'PermissionDenied'
264 The process has insufficient privileges to perform the operation.
265 @[EROFS, EACCES, EPERM]@
267 * 'UnsatisfiedConstraints'
268 Implementation-dependent constraints are not satisfied.
269 @[EBUSY, ENOTEMPTY, EEXIST]@
271 * 'UnsupportedOperation'
272 The implementation does not support removal in this situation.
275 * 'InappropriateType'
276 The operand refers to an existing non-directory object.
281 removeDirectory :: FilePath -> IO ()
282 removeDirectory path = do
283 modifyIOError (`ioeSetFileName` path) $
284 withCString path $ \s ->
285 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
287 {- |'removeFile' /file/ removes the directory entry for an existing file
288 /file/, where /file/ is not itself a directory. The
289 implementation may specify additional constraints which must be
290 satisfied before a file can be removed (e.g. the file may not be in
291 use by other processes).
293 The operation may fail with:
296 A physical I\/O error has occurred.
300 The operand is not a valid file name.
301 @[ENAMETOOLONG, ELOOP]@
303 * 'isDoesNotExistError' \/ 'NoSuchThing'
304 The file does not exist.
307 * 'isPermissionError' \/ 'PermissionDenied'
308 The process has insufficient privileges to perform the operation.
309 @[EROFS, EACCES, EPERM]@
311 * 'UnsatisfiedConstraints'
312 Implementation-dependent constraints are not satisfied.
315 * 'InappropriateType'
316 The operand refers to an existing directory.
321 removeFile :: FilePath -> IO ()
323 modifyIOError (`ioeSetFileName` path) $
324 withCString path $ \s ->
325 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
327 {- |@'renameDirectory' old new@ changes the name of an existing
328 directory from /old/ to /new/. If the /new/ directory
329 already exists, it is atomically replaced by the /old/ directory.
330 If the /new/ directory is neither the /old/ directory nor an
331 alias of the /old/ directory, it is removed as if by
332 'removeDirectory'. A conformant implementation need not support
333 renaming directories in all situations (e.g. renaming to an existing
334 directory, or across different physical devices), but the constraints
337 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
340 The operation may fail with:
343 A physical I\/O error has occurred.
347 Either operand is not a valid directory name.
348 @[ENAMETOOLONG, ELOOP]@
350 * 'isDoesNotExistError' \/ 'NoSuchThing'
351 The original directory does not exist, or there is no path to the target.
354 * 'isPermissionError' \/ 'PermissionDenied'
355 The process has insufficient privileges to perform the operation.
356 @[EROFS, EACCES, EPERM]@
358 * 'ResourceExhausted'
359 Insufficient resources are available to perform the operation.
360 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
362 * 'UnsatisfiedConstraints'
363 Implementation-dependent constraints are not satisfied.
364 @[EBUSY, ENOTEMPTY, EEXIST]@
366 * 'UnsupportedOperation'
367 The implementation does not support renaming in this situation.
370 * 'InappropriateType'
371 Either path refers to an existing non-directory object.
376 renameDirectory :: FilePath -> FilePath -> IO ()
377 renameDirectory opath npath =
378 withFileStatus "renameDirectory" opath $ \st -> do
379 is_dir <- isDirectory st
381 then ioException (IOError Nothing InappropriateType "renameDirectory"
382 ("not a directory") (Just opath))
385 withCString opath $ \s1 ->
386 withCString npath $ \s2 ->
387 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
389 {- |@'renameFile' old new@ changes the name of an existing file system
390 object from /old/ to /new/. If the /new/ object already
391 exists, it is atomically replaced by the /old/ object. Neither
392 path may refer to an existing directory. A conformant implementation
393 need not support renaming files in all situations (e.g. renaming
394 across different physical devices), but the constraints must be
397 The operation may fail with:
400 A physical I\/O error has occurred.
404 Either operand is not a valid file name.
405 @[ENAMETOOLONG, ELOOP]@
407 * 'isDoesNotExistError' \/ 'NoSuchThing'
408 The original file does not exist, or there is no path to the target.
411 * 'isPermissionError' \/ 'PermissionDenied'
412 The process has insufficient privileges to perform the operation.
413 @[EROFS, EACCES, EPERM]@
415 * 'ResourceExhausted'
416 Insufficient resources are available to perform the operation.
417 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
419 * 'UnsatisfiedConstraints'
420 Implementation-dependent constraints are not satisfied.
423 * 'UnsupportedOperation'
424 The implementation does not support renaming in this situation.
427 * 'InappropriateType'
428 Either path refers to an existing directory.
429 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
433 renameFile :: FilePath -> FilePath -> IO ()
434 renameFile opath npath =
435 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
436 is_dir <- isDirectory st
438 then ioException (IOError Nothing InappropriateType "renameFile"
439 "is a directory" (Just opath))
442 withCString opath $ \s1 ->
443 withCString npath $ \s2 ->
444 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
446 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
447 If the /new/ file already exists, it is atomically replaced by the /old/ file.
448 Neither path may refer to an existing directory.
450 copyFile :: FilePath -> FilePath -> IO ()
451 copyFile fromFPath toFPath =
452 (bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
453 bracket (openBinaryFile toFPath WriteMode) hClose $ \hTo ->
454 allocaBytes bufferSize $ \buffer ->
455 copyContents hFrom hTo buffer) `catch` (ioError . changeFunName)
459 changeFunName (IOError h iot fun str mb_fp) = IOError h iot "copyFile" str mb_fp
461 copyContents hFrom hTo buffer = do
462 count <- hGetBuf hFrom buffer bufferSize
463 when (count > 0) $ do
464 hPutBuf hTo buffer count
465 copyContents hFrom hTo buffer
468 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
471 The operation may fail with:
474 A physical I\/O error has occurred.
478 The operand is not a valid directory name.
479 @[ENAMETOOLONG, ELOOP]@
481 * 'isDoesNotExistError' \/ 'NoSuchThing'
482 The directory does not exist.
485 * 'isPermissionError' \/ 'PermissionDenied'
486 The process has insufficient privileges to perform the operation.
489 * 'ResourceExhausted'
490 Insufficient resources are available to perform the operation.
493 * 'InappropriateType'
494 The path refers to an existing non-directory object.
499 getDirectoryContents :: FilePath -> IO [FilePath]
500 getDirectoryContents path = do
501 modifyIOError (`ioeSetFileName` path) $
502 alloca $ \ ptr_dEnt ->
504 (withCString path $ \s ->
505 throwErrnoIfNullRetry desc (c_opendir s))
506 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
507 (\p -> loop ptr_dEnt p)
509 desc = "getDirectoryContents"
511 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
512 loop ptr_dEnt dir = do
514 r <- readdir dir ptr_dEnt
517 dEnt <- peek ptr_dEnt
521 entry <- (d_name dEnt >>= peekCString)
523 entries <- loop ptr_dEnt dir
524 return (entry:entries)
525 else do errno <- getErrno
526 if (errno == eINTR) then loop ptr_dEnt dir else do
527 let (Errno eo) = errno
528 if (eo == end_of_dir)
534 {- |If the operating system has a notion of current directories,
535 'getCurrentDirectory' returns an absolute path to the
536 current directory of the calling process.
538 The operation may fail with:
541 A physical I\/O error has occurred.
544 * 'isDoesNotExistError' \/ 'NoSuchThing'
545 There is no path referring to the current directory.
546 @[EPERM, ENOENT, ESTALE...]@
548 * 'isPermissionError' \/ 'PermissionDenied'
549 The process has insufficient privileges to perform the operation.
552 * 'ResourceExhausted'
553 Insufficient resources are available to perform the operation.
555 * 'UnsupportedOperation'
556 The operating system has no notion of current directory.
560 getCurrentDirectory :: IO FilePath
561 getCurrentDirectory = do
562 p <- mallocBytes long_path_size
564 where go p bytes = do
565 p' <- c_getcwd p (fromIntegral bytes)
567 then do s <- peekCString p'
570 else do errno <- getErrno
572 then do let bytes' = bytes * 2
573 p' <- reallocBytes p bytes'
575 else throwErrno "getCurrentDirectory"
577 {- |If the operating system has a notion of current directories,
578 @'setCurrentDirectory' dir@ changes the current
579 directory of the calling process to /dir/.
581 The operation may fail with:
584 A physical I\/O error has occurred.
588 The operand is not a valid directory name.
589 @[ENAMETOOLONG, ELOOP]@
591 * 'isDoesNotExistError' \/ 'NoSuchThing'
592 The directory does not exist.
595 * 'isPermissionError' \/ 'PermissionDenied'
596 The process has insufficient privileges to perform the operation.
599 * 'UnsupportedOperation'
600 The operating system has no notion of current directory, or the
601 current directory cannot be dynamically changed.
603 * 'InappropriateType'
604 The path refers to an existing non-directory object.
609 setCurrentDirectory :: FilePath -> IO ()
610 setCurrentDirectory path = do
611 modifyIOError (`ioeSetFileName` path) $
612 withCString path $ \s ->
613 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
614 -- ToDo: add path to error
616 {- | Returns the current user's home directory.
618 The directory returned is expected to be writable by the current user,
619 but note that it isn't generally considered good practice to store
620 application-specific data here; use 'getAppUserDataDirectory'
623 On Unix, 'getHomeDirectory' returns the value of the @HOME@
624 environment variable. On Windows, the system is queried for a
625 suitable path; a typical path might be
626 @C:/Documents And Settings/user@.
628 The operation may fail with:
630 * 'UnsupportedOperation'
631 The operating system has no notion of home directory.
633 * 'isDoesNotExistError'
634 The home directory for the current user does not exist, or
637 getHomeDirectory :: IO FilePath
639 #ifdef mingw32_TARGET_OS
640 allocaBytes long_path_size $ \pPath -> do
641 r <- c_SHGetFolderPath nullPtr csidl_PROFILE nullPtr 0 pPath
643 then c_SHGetFolderPath nullPtr csidl_WINDOWS nullPtr 0 pPath
650 {- | Returns the pathname of a directory in which application-specific
651 data for the current user can be stored. The result of
652 'getAppUserDataDirectory' for a given application is specific to
655 The argument should be the name of the application, which will be used
656 to construct the pathname (so avoid using unusual characters that
657 might result in an invalid pathname).
659 Note: the directory may not actually exist, and may need to be created
660 first. It is expected that the parent directory exists and is
663 On Unix, this function returns @$HOME\/.appName@. On Windows, a
664 typical path might be
666 > C:/Documents And Settings/user/Application Data/appName
668 The operation may fail with:
670 * 'UnsupportedOperation'
671 The operating system has no notion of application-specific data directory.
673 * 'isDoesNotExistError'
674 The home directory for the current user does not exist, or
677 getAppUserDataDirectory :: String -> IO FilePath
678 getAppUserDataDirectory appName = do
679 #ifdef mingw32_TARGET_OS
680 allocaBytes long_path_size $ \pPath -> do
681 r <- c_SHGetFolderPath nullPtr csidl_APPDATA nullPtr 0 pPath
682 s <- peekCString pPath
683 return (s++'\\':appName)
685 path <- getEnv "HOME"
686 return (path++'/':'.':appName)
689 #ifdef mingw32_TARGET_OS
690 foreign import stdcall unsafe "SHGetFolderPath"
691 c_SHGetFolderPath :: Ptr ()
697 foreign import ccall unsafe "__hscore_CSIDL_PROFILE" csidl_PROFILE :: Int
698 foreign import ccall unsafe "__hscore_CSIDL_APPDATA" csidl_APPDATA :: Int
699 foreign import ccall unsafe "__hscore_CSIDL_WINDOWS" csidl_WINDOWS :: Int
702 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
703 exists and is a directory, and 'False' otherwise.
706 doesDirectoryExist :: FilePath -> IO Bool
707 doesDirectoryExist name =
709 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
710 (\ _ -> return False)
712 {- |The operation 'doesFileExist' returns 'True'
713 if the argument file exists and is not a directory, and 'False' otherwise.
716 doesFileExist :: FilePath -> IO Bool
717 doesFileExist name = do
719 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
720 (\ _ -> return False)
722 {- |The 'getModificationTime' operation returns the
723 clock time at which the file or directory was last modified.
725 The operation may fail with:
727 * 'isPermissionError' if the user is not permitted to access
728 the modification time; or
730 * 'isDoesNotExistError' if the file or directory does not exist.
734 getModificationTime :: FilePath -> IO ClockTime
735 getModificationTime name =
736 withFileStatus "getModificationTime" name $ \ st ->
739 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
740 withFileStatus loc name f = do
741 modifyIOError (`ioeSetFileName` name) $
742 allocaBytes sizeof_stat $ \p ->
743 withCString (fileNameEndClean name) $ \s -> do
744 throwErrnoIfMinus1Retry_ loc (c_stat s p)
747 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
748 withFileOrSymlinkStatus loc name f = do
749 modifyIOError (`ioeSetFileName` name) $
750 allocaBytes sizeof_stat $ \p ->
751 withCString name $ \s -> do
752 throwErrnoIfMinus1Retry_ loc (lstat s p)
755 modificationTime :: Ptr CStat -> IO ClockTime
756 modificationTime stat = do
757 mtime <- st_mtime stat
758 let realToInteger = round . realToFrac :: Real a => a -> Integer
759 return (TOD (realToInteger (mtime :: CTime)) 0)
761 isDirectory :: Ptr CStat -> IO Bool
762 isDirectory stat = do
764 return (s_isdir mode)
766 fileNameEndClean :: String -> String
767 fileNameEndClean name =
768 if i > 0 && (ec == '\\' || ec == '/') then
769 fileNameEndClean (take i name)
773 i = (length name) - 1
776 foreign import ccall unsafe "__hscore_long_path_size"
777 long_path_size :: Int
779 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
780 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
781 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
783 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
784 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
785 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode