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
29 , getUserDocumentsDirectory
32 , removeFile -- :: FilePath -> IO ()
33 , renameFile -- :: FilePath -> FilePath -> IO ()
34 #ifdef __GLASGOW_HASKELL__
35 , copyFile -- :: FilePath -> FilePath -> IO ()
39 , doesFileExist -- :: FilePath -> IO Bool
40 , doesDirectoryExist -- :: FilePath -> IO Bool
48 readable, -- :: Permissions -> Bool
49 writable, -- :: Permissions -> Bool
50 executable, -- :: Permissions -> Bool
51 searchable -- :: Permissions -> Bool
54 , getPermissions -- :: FilePath -> IO Permissions
55 , setPermissions -- :: FilePath -> Permissions -> IO ()
59 , getModificationTime -- :: FilePath -> IO ClockTime
64 getHomeDirectory :: IO FilePath
65 getHomeDirectory = getEnv "HOME"
66 getAppUserDataDirectory :: String -> IO FilePath
67 getAppUserDataDirectory appName = do path <- getEnv "HOME"
68 return (path++'/':'.':appName)
69 getUserDocumentsDirectory :: IO FilePath
70 getUserDocumentsDirectory= getEnv "HOME"
71 #elif defined(__HUGS__)
77 import Control.Exception ( bracket )
78 import Control.Monad ( when )
79 import System.Posix.Types
80 import System.Posix.Internals
81 import System.Time ( ClockTime(..) )
83 import System.IO.Error
87 #ifdef __GLASGOW_HASKELL__
88 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
91 #ifndef mingw32_TARGET_OS
92 import System.Environment
96 A directory contains a series of entries, each of which is a named
97 reference to a file system object (file, directory etc.). Some
98 entries may be hidden, inaccessible, or have some administrative
99 function (e.g. `.' or `..' under POSIX
100 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
101 this standard all such entries are considered to form part of the
102 directory contents. Entries in sub-directories are not, however,
103 considered to form part of the directory contents.
105 Each file system object is referenced by a /path/. There is
106 normally at least one absolute path to each file system object. In
107 some operating systems, it may also be possible to have paths which
108 are relative to the current directory.
111 -----------------------------------------------------------------------------
116 The 'Permissions' type is used to record whether certain operations are
117 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
118 get and set these permissions, respectively. Permissions apply both to
119 files and directories. For directories, the executable field will be
120 'False', and for files the searchable field will be 'False'. Note that
121 directories may be searchable without being readable, if permission has
122 been given to use them as part of a path, but not to examine the
125 Note that to change some, but not all permissions, a construct on the following lines must be used.
127 > makeReadable f = do
128 > p <- getPermissions f
129 > setPermissions f (p {readable = True})
136 executable, searchable :: Bool
137 } deriving (Eq, Ord, Read, Show)
139 {- |The 'getPermissions' operation returns the
140 permissions for the file or directory.
142 The operation may fail with:
144 * 'isPermissionError' if the user is not permitted to access
147 * 'isDoesNotExistError' if the file or directory does not exist.
151 getPermissions :: FilePath -> IO Permissions
152 getPermissions name = do
153 withCString name $ \s -> do
154 read <- c_access s r_OK
155 write <- c_access s w_OK
156 exec <- c_access s x_OK
157 withFileStatus "getPermissions" name $ \st -> do
158 is_dir <- isDirectory st
161 readable = read == 0,
162 writable = write == 0,
163 executable = not is_dir && exec == 0,
164 searchable = is_dir && exec == 0
168 {- |The 'setPermissions' operation sets the
169 permissions for the file or directory.
171 The operation may fail with:
173 * 'isPermissionError' if the user is not permitted to set
176 * 'isDoesNotExistError' if the file or directory does not exist.
180 setPermissions :: FilePath -> Permissions -> IO ()
181 setPermissions name (Permissions r w e s) = do
182 allocaBytes sizeof_stat $ \ p_stat -> do
183 withCString name $ \p_name -> do
184 throwErrnoIfMinus1_ "setPermissions" $ do
186 mode <- st_mode p_stat
187 let mode1 = modifyBit r mode s_IRUSR
188 let mode2 = modifyBit w mode1 s_IWUSR
189 let mode3 = modifyBit (e || s) mode2 s_IXUSR
193 modifyBit :: Bool -> CMode -> CMode -> CMode
194 modifyBit False m b = m .&. (complement b)
195 modifyBit True m b = m .|. b
197 -----------------------------------------------------------------------------
200 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
201 initially empty, or as near to empty as the operating system
204 The operation may fail with:
206 * 'isPermissionError' \/ 'PermissionDenied'
207 The process has insufficient privileges to perform the operation.
210 * 'isAlreadyExistsError' \/ 'AlreadyExists'
211 The operand refers to a directory that already exists.
215 A physical I\/O error has occurred.
219 The operand is not a valid directory name.
220 @[ENAMETOOLONG, ELOOP]@
223 There is no path to the directory.
226 * 'ResourceExhausted'
227 Insufficient resources (virtual memory, process file descriptors,
228 physical disk space, etc.) are available to perform the operation.
229 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
231 * 'InappropriateType'
232 The path refers to an existing non-directory object.
237 createDirectory :: FilePath -> IO ()
238 createDirectory path = do
239 withCString path $ \s -> do
240 throwErrnoIfMinus1Retry_ "createDirectory" $
243 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
244 implementation may specify additional constraints which must be
245 satisfied before a directory can be removed (e.g. the directory has to
246 be empty, or may not be in use by other processes). It is not legal
247 for an implementation to partially remove a directory unless the
248 entire directory is removed. A conformant implementation need not
249 support directory removal in all situations (e.g. removal of the root
252 The operation may fail with:
255 A physical I\/O error has occurred.
259 The operand is not a valid directory name.
260 [ENAMETOOLONG, ELOOP]
262 * 'isDoesNotExistError' \/ 'NoSuchThing'
263 The directory does not exist.
266 * 'isPermissionError' \/ 'PermissionDenied'
267 The process has insufficient privileges to perform the operation.
268 @[EROFS, EACCES, EPERM]@
270 * 'UnsatisfiedConstraints'
271 Implementation-dependent constraints are not satisfied.
272 @[EBUSY, ENOTEMPTY, EEXIST]@
274 * 'UnsupportedOperation'
275 The implementation does not support removal in this situation.
278 * 'InappropriateType'
279 The operand refers to an existing non-directory object.
284 removeDirectory :: FilePath -> IO ()
285 removeDirectory path = do
286 modifyIOError (`ioeSetFileName` path) $
287 withCString path $ \s ->
288 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
290 {- |'removeFile' /file/ removes the directory entry for an existing file
291 /file/, where /file/ is not itself a directory. The
292 implementation may specify additional constraints which must be
293 satisfied before a file can be removed (e.g. the file may not be in
294 use by other processes).
296 The operation may fail with:
299 A physical I\/O error has occurred.
303 The operand is not a valid file name.
304 @[ENAMETOOLONG, ELOOP]@
306 * 'isDoesNotExistError' \/ 'NoSuchThing'
307 The file does not exist.
310 * 'isPermissionError' \/ 'PermissionDenied'
311 The process has insufficient privileges to perform the operation.
312 @[EROFS, EACCES, EPERM]@
314 * 'UnsatisfiedConstraints'
315 Implementation-dependent constraints are not satisfied.
318 * 'InappropriateType'
319 The operand refers to an existing directory.
324 removeFile :: FilePath -> IO ()
326 modifyIOError (`ioeSetFileName` path) $
327 withCString path $ \s ->
328 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
330 {- |@'renameDirectory' old new@ changes the name of an existing
331 directory from /old/ to /new/. If the /new/ directory
332 already exists, it is atomically replaced by the /old/ directory.
333 If the /new/ directory is neither the /old/ directory nor an
334 alias of the /old/ directory, it is removed as if by
335 'removeDirectory'. A conformant implementation need not support
336 renaming directories in all situations (e.g. renaming to an existing
337 directory, or across different physical devices), but the constraints
340 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
343 The operation may fail with:
346 A physical I\/O error has occurred.
350 Either operand is not a valid directory name.
351 @[ENAMETOOLONG, ELOOP]@
353 * 'isDoesNotExistError' \/ 'NoSuchThing'
354 The original directory does not exist, or there is no path to the target.
357 * 'isPermissionError' \/ 'PermissionDenied'
358 The process has insufficient privileges to perform the operation.
359 @[EROFS, EACCES, EPERM]@
361 * 'ResourceExhausted'
362 Insufficient resources are available to perform the operation.
363 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
365 * 'UnsatisfiedConstraints'
366 Implementation-dependent constraints are not satisfied.
367 @[EBUSY, ENOTEMPTY, EEXIST]@
369 * 'UnsupportedOperation'
370 The implementation does not support renaming in this situation.
373 * 'InappropriateType'
374 Either path refers to an existing non-directory object.
379 renameDirectory :: FilePath -> FilePath -> IO ()
380 renameDirectory opath npath =
381 withFileStatus "renameDirectory" opath $ \st -> do
382 is_dir <- isDirectory st
384 then ioException (IOError Nothing InappropriateType "renameDirectory"
385 ("not a directory") (Just opath))
388 withCString opath $ \s1 ->
389 withCString npath $ \s2 ->
390 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
392 {- |@'renameFile' old new@ changes the name of an existing file system
393 object from /old/ to /new/. If the /new/ object already
394 exists, it is atomically replaced by the /old/ object. Neither
395 path may refer to an existing directory. A conformant implementation
396 need not support renaming files in all situations (e.g. renaming
397 across different physical devices), but the constraints must be
400 The operation may fail with:
403 A physical I\/O error has occurred.
407 Either operand is not a valid file name.
408 @[ENAMETOOLONG, ELOOP]@
410 * 'isDoesNotExistError' \/ 'NoSuchThing'
411 The original file does not exist, or there is no path to the target.
414 * 'isPermissionError' \/ 'PermissionDenied'
415 The process has insufficient privileges to perform the operation.
416 @[EROFS, EACCES, EPERM]@
418 * 'ResourceExhausted'
419 Insufficient resources are available to perform the operation.
420 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
422 * 'UnsatisfiedConstraints'
423 Implementation-dependent constraints are not satisfied.
426 * 'UnsupportedOperation'
427 The implementation does not support renaming in this situation.
430 * 'InappropriateType'
431 Either path refers to an existing directory.
432 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
436 renameFile :: FilePath -> FilePath -> IO ()
437 renameFile opath npath =
438 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
439 is_dir <- isDirectory st
441 then ioException (IOError Nothing InappropriateType "renameFile"
442 "is a directory" (Just opath))
445 withCString opath $ \s1 ->
446 withCString npath $ \s2 ->
447 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
449 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
450 If the /new/ file already exists, it is atomically replaced by the /old/ file.
451 Neither path may refer to an existing directory.
453 copyFile :: FilePath -> FilePath -> IO ()
454 copyFile fromFPath toFPath =
455 (bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
456 bracket (openBinaryFile toFPath WriteMode) hClose $ \hTo ->
457 allocaBytes bufferSize $ \buffer ->
458 copyContents hFrom hTo buffer) `catch` (ioError . changeFunName)
462 changeFunName (IOError h iot fun str mb_fp) = IOError h iot "copyFile" str mb_fp
464 copyContents hFrom hTo buffer = do
465 count <- hGetBuf hFrom buffer bufferSize
466 when (count > 0) $ do
467 hPutBuf hTo buffer count
468 copyContents hFrom hTo buffer
471 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
474 The operation may fail with:
477 A physical I\/O error has occurred.
481 The operand is not a valid directory name.
482 @[ENAMETOOLONG, ELOOP]@
484 * 'isDoesNotExistError' \/ 'NoSuchThing'
485 The directory does not exist.
488 * 'isPermissionError' \/ 'PermissionDenied'
489 The process has insufficient privileges to perform the operation.
492 * 'ResourceExhausted'
493 Insufficient resources are available to perform the operation.
496 * 'InappropriateType'
497 The path refers to an existing non-directory object.
502 getDirectoryContents :: FilePath -> IO [FilePath]
503 getDirectoryContents path = do
504 modifyIOError (`ioeSetFileName` path) $
505 alloca $ \ ptr_dEnt ->
507 (withCString path $ \s ->
508 throwErrnoIfNullRetry desc (c_opendir s))
509 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
510 (\p -> loop ptr_dEnt p)
512 desc = "getDirectoryContents"
514 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
515 loop ptr_dEnt dir = do
517 r <- readdir dir ptr_dEnt
520 dEnt <- peek ptr_dEnt
524 entry <- (d_name dEnt >>= peekCString)
526 entries <- loop ptr_dEnt dir
527 return (entry:entries)
528 else do errno <- getErrno
529 if (errno == eINTR) then loop ptr_dEnt dir else do
530 let (Errno eo) = errno
531 if (eo == end_of_dir)
537 {- |If the operating system has a notion of current directories,
538 'getCurrentDirectory' returns an absolute path to the
539 current directory of the calling process.
541 The operation may fail with:
544 A physical I\/O error has occurred.
547 * 'isDoesNotExistError' \/ 'NoSuchThing'
548 There is no path referring to the current directory.
549 @[EPERM, ENOENT, ESTALE...]@
551 * 'isPermissionError' \/ 'PermissionDenied'
552 The process has insufficient privileges to perform the operation.
555 * 'ResourceExhausted'
556 Insufficient resources are available to perform the operation.
558 * 'UnsupportedOperation'
559 The operating system has no notion of current directory.
563 getCurrentDirectory :: IO FilePath
564 getCurrentDirectory = do
565 p <- mallocBytes long_path_size
567 where go p bytes = do
568 p' <- c_getcwd p (fromIntegral bytes)
570 then do s <- peekCString p'
573 else do errno <- getErrno
575 then do let bytes' = bytes * 2
576 p' <- reallocBytes p bytes'
578 else throwErrno "getCurrentDirectory"
580 {- |If the operating system has a notion of current directories,
581 @'setCurrentDirectory' dir@ changes the current
582 directory of the calling process to /dir/.
584 The operation may fail with:
587 A physical I\/O error has occurred.
591 The operand is not a valid directory name.
592 @[ENAMETOOLONG, ELOOP]@
594 * 'isDoesNotExistError' \/ 'NoSuchThing'
595 The directory does not exist.
598 * 'isPermissionError' \/ 'PermissionDenied'
599 The process has insufficient privileges to perform the operation.
602 * 'UnsupportedOperation'
603 The operating system has no notion of current directory, or the
604 current directory cannot be dynamically changed.
606 * 'InappropriateType'
607 The path refers to an existing non-directory object.
612 setCurrentDirectory :: FilePath -> IO ()
613 setCurrentDirectory path = do
614 modifyIOError (`ioeSetFileName` path) $
615 withCString path $ \s ->
616 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
617 -- ToDo: add path to error
619 {- | Returns the current user's home directory.
621 The directory returned is expected to be writable by the current user,
622 but note that it isn't generally considered good practice to store
623 application-specific data here; use 'getAppUserDataDirectory'
626 On Unix, 'getHomeDirectory' returns the value of the @HOME@
627 environment variable. On Windows, the system is queried for a
628 suitable path; a typical path might be
629 @C:/Documents And Settings/user@.
631 The operation may fail with:
633 * 'UnsupportedOperation'
634 The operating system has no notion of home directory.
636 * 'isDoesNotExistError'
637 The home directory for the current user does not exist, or
640 getHomeDirectory :: IO FilePath
642 #ifdef mingw32_TARGET_OS
643 allocaBytes long_path_size $ \pPath -> do
644 r <- c_SHGetFolderPath nullPtr csidl_PROFILE nullPtr 0 pPath
646 then c_SHGetFolderPath nullPtr csidl_WINDOWS nullPtr 0 pPath
653 {- | Returns the pathname of a directory in which application-specific
654 data for the current user can be stored. The result of
655 'getAppUserDataDirectory' for a given application is specific to
658 The argument should be the name of the application, which will be used
659 to construct the pathname (so avoid using unusual characters that
660 might result in an invalid pathname).
662 Note: the directory may not actually exist, and may need to be created
663 first. It is expected that the parent directory exists and is
666 On Unix, this function returns @$HOME\/.appName@. On Windows, a
667 typical path might be
669 > C:/Documents And Settings/user/Application Data/appName
671 The operation may fail with:
673 * 'UnsupportedOperation'
674 The operating system has no notion of application-specific data directory.
676 * 'isDoesNotExistError'
677 The home directory for the current user does not exist, or
680 getAppUserDataDirectory :: String -> IO FilePath
681 getAppUserDataDirectory appName = do
682 #ifdef mingw32_TARGET_OS
683 allocaBytes long_path_size $ \pPath -> do
684 r <- c_SHGetFolderPath nullPtr csidl_APPDATA nullPtr 0 pPath
685 s <- peekCString pPath
686 return (s++'\\':appName)
688 path <- getEnv "HOME"
689 return (path++'/':'.':appName)
692 {- | Returns the current user's document directory.
694 The directory returned is expected to be writable by the current user,
695 but note that it isn't generally considered good practice to store
696 application-specific data here; use 'getAppUserDataDirectory'
699 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
700 environment variable. On Windows, the system is queried for a
701 suitable path; a typical path might be
702 @C:/Documents and Settings/user/My Documents@.
704 The operation may fail with:
706 * 'UnsupportedOperation'
707 The operating system has no notion of document directory.
709 * 'isDoesNotExistError'
710 The document directory for the current user does not exist, or
713 getUserDocumentsDirectory :: IO FilePath
714 getUserDocumentsDirectory = do
715 #ifdef mingw32_TARGET_OS
716 allocaBytes long_path_size $ \pPath -> do
717 r <- c_SHGetFolderPath nullPtr csidl_PERSONAL nullPtr 0 pPath
723 #ifdef mingw32_TARGET_OS
724 foreign import stdcall unsafe "SHGetFolderPath"
725 c_SHGetFolderPath :: Ptr ()
731 foreign import ccall unsafe "__hscore_CSIDL_PROFILE" csidl_PROFILE :: CInt
732 foreign import ccall unsafe "__hscore_CSIDL_APPDATA" csidl_APPDATA :: CInt
733 foreign import ccall unsafe "__hscore_CSIDL_WINDOWS" csidl_WINDOWS :: CInt
734 foreign import ccall unsafe "__hscore_CSIDL_PERSONAL" csidl_PERSONAL :: CInt
737 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
738 exists and is a directory, and 'False' otherwise.
741 doesDirectoryExist :: FilePath -> IO Bool
742 doesDirectoryExist name =
744 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
745 (\ _ -> return False)
747 {- |The operation 'doesFileExist' returns 'True'
748 if the argument file exists and is not a directory, and 'False' otherwise.
751 doesFileExist :: FilePath -> IO Bool
752 doesFileExist name = do
754 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
755 (\ _ -> return False)
757 {- |The 'getModificationTime' operation returns the
758 clock time at which the file or directory was last modified.
760 The operation may fail with:
762 * 'isPermissionError' if the user is not permitted to access
763 the modification time; or
765 * 'isDoesNotExistError' if the file or directory does not exist.
769 getModificationTime :: FilePath -> IO ClockTime
770 getModificationTime name =
771 withFileStatus "getModificationTime" name $ \ st ->
774 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
775 withFileStatus loc name f = do
776 modifyIOError (`ioeSetFileName` name) $
777 allocaBytes sizeof_stat $ \p ->
778 withCString (fileNameEndClean name) $ \s -> do
779 throwErrnoIfMinus1Retry_ loc (c_stat s p)
782 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
783 withFileOrSymlinkStatus loc name f = do
784 modifyIOError (`ioeSetFileName` name) $
785 allocaBytes sizeof_stat $ \p ->
786 withCString name $ \s -> do
787 throwErrnoIfMinus1Retry_ loc (lstat s p)
790 modificationTime :: Ptr CStat -> IO ClockTime
791 modificationTime stat = do
792 mtime <- st_mtime stat
793 let realToInteger = round . realToFrac :: Real a => a -> Integer
794 return (TOD (realToInteger (mtime :: CTime)) 0)
796 isDirectory :: Ptr CStat -> IO Bool
797 isDirectory stat = do
799 return (s_isdir mode)
801 fileNameEndClean :: String -> String
802 fileNameEndClean name =
803 if i > 0 && (ec == '\\' || ec == '/') then
804 fileNameEndClean (take i name)
808 i = (length name) - 1
811 foreign import ccall unsafe "__hscore_long_path_size"
812 long_path_size :: Int
814 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
815 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
816 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
818 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
819 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
820 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode