2 -- XXX We get some warnings on Windows
4 -----------------------------------------------------------------------------
6 -- Module : System.Directory
7 -- Copyright : (c) The University of Glasgow 2001
8 -- License : BSD-style (see the file libraries/base/LICENSE)
10 -- Maintainer : libraries@haskell.org
12 -- Portability : portable
14 -- System-independent interface to directory manipulation.
16 -----------------------------------------------------------------------------
18 module System.Directory
22 -- * Actions on directories
23 createDirectory -- :: FilePath -> IO ()
24 , createDirectoryIfMissing -- :: Bool -> FilePath -> IO ()
25 , removeDirectory -- :: FilePath -> IO ()
26 , removeDirectoryRecursive -- :: FilePath -> IO ()
27 , renameDirectory -- :: FilePath -> FilePath -> IO ()
29 , getDirectoryContents -- :: FilePath -> IO [FilePath]
30 , getCurrentDirectory -- :: IO FilePath
31 , setCurrentDirectory -- :: FilePath -> IO ()
33 -- * Pre-defined directories
35 , getAppUserDataDirectory
36 , getUserDocumentsDirectory
37 , getTemporaryDirectory
40 , removeFile -- :: FilePath -> IO ()
41 , renameFile -- :: FilePath -> FilePath -> IO ()
42 , copyFile -- :: FilePath -> FilePath -> IO ()
45 , makeRelativeToCurrentDirectory
49 , doesFileExist -- :: FilePath -> IO Bool
50 , doesDirectoryExist -- :: FilePath -> IO Bool
58 readable, -- :: Permissions -> Bool
59 writable, -- :: Permissions -> Bool
60 executable, -- :: Permissions -> Bool
61 searchable -- :: Permissions -> Bool
64 , getPermissions -- :: FilePath -> IO Permissions
65 , setPermissions -- :: FilePath -> Permissions -> IO ()
69 , getModificationTime -- :: FilePath -> IO ClockTime
72 import Prelude hiding ( catch )
73 import qualified Prelude
75 import Control.Monad (guard)
76 import System.Environment ( getEnv )
77 import System.FilePath
79 import System.IO.Error hiding ( catch, try )
80 import Control.Monad ( when, unless )
81 import Control.Exception.Base
85 import System (system)
95 {-# CFILES cbits/directory.c #-}
97 #ifdef __GLASGOW_HASKELL__
98 import System.Posix.Types
99 import System.Posix.Internals
100 import System.Time ( ClockTime(..) )
102 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
104 #ifdef mingw32_HOST_OS
105 import qualified System.Win32
107 import qualified System.Posix
111 A directory contains a series of entries, each of which is a named
112 reference to a file system object (file, directory etc.). Some
113 entries may be hidden, inaccessible, or have some administrative
114 function (e.g. `.' or `..' under POSIX
115 <http://www.opengroup.org/onlinepubs/009695399/>), but in
116 this standard all such entries are considered to form part of the
117 directory contents. Entries in sub-directories are not, however,
118 considered to form part of the directory contents.
120 Each file system object is referenced by a /path/. There is
121 normally at least one absolute path to each file system object. In
122 some operating systems, it may also be possible to have paths which
123 are relative to the current directory.
126 -----------------------------------------------------------------------------
131 The 'Permissions' type is used to record whether certain operations are
132 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
133 get and set these permissions, respectively. Permissions apply both to
134 files and directories. For directories, the executable field will be
135 'False', and for files the searchable field will be 'False'. Note that
136 directories may be searchable without being readable, if permission has
137 been given to use them as part of a path, but not to examine the
140 Note that to change some, but not all permissions, a construct on the following lines must be used.
142 > makeReadable f = do
143 > p <- getPermissions f
144 > setPermissions f (p {readable = True})
151 executable, searchable :: Bool
152 } deriving (Eq, Ord, Read, Show)
154 {- |The 'getPermissions' operation returns the
155 permissions for the file or directory.
157 The operation may fail with:
159 * 'isPermissionError' if the user is not permitted to access
162 * 'isDoesNotExistError' if the file or directory does not exist.
166 getPermissions :: FilePath -> IO Permissions
167 getPermissions name = do
168 withCString name $ \s -> do
169 #ifdef mingw32_HOST_OS
170 -- stat() does a better job of guessing the permissions on Windows
171 -- than access() does. e.g. for execute permission, it looks at the
172 -- filename extension :-)
174 -- I tried for a while to do this properly, using the Windows security API,
175 -- and eventually gave up. getPermissions is a flawed API anyway. -- SimonM
176 allocaBytes sizeof_stat $ \ p_stat -> do
177 throwErrnoIfMinus1_ "getPermissions" $ c_stat s p_stat
178 mode <- st_mode p_stat
179 let usr_read = mode .&. s_IRUSR
180 let usr_write = mode .&. s_IWUSR
181 let usr_exec = mode .&. s_IXUSR
182 let is_dir = mode .&. s_IFDIR
185 readable = usr_read /= 0,
186 writable = usr_write /= 0,
187 executable = is_dir == 0 && usr_exec /= 0,
188 searchable = is_dir /= 0 && usr_exec /= 0
192 read_ok <- c_access s r_OK
193 write_ok <- c_access s w_OK
194 exec_ok <- c_access s x_OK
195 withFileStatus "getPermissions" name $ \st -> do
196 is_dir <- isDirectory st
199 readable = read_ok == 0,
200 writable = write_ok == 0,
201 executable = not is_dir && exec_ok == 0,
202 searchable = is_dir && exec_ok == 0
207 {- |The 'setPermissions' operation sets the
208 permissions for the file or directory.
210 The operation may fail with:
212 * 'isPermissionError' if the user is not permitted to set
215 * 'isDoesNotExistError' if the file or directory does not exist.
219 setPermissions :: FilePath -> Permissions -> IO ()
220 setPermissions name (Permissions r w e s) = do
221 allocaBytes sizeof_stat $ \ p_stat -> do
222 withCString name $ \p_name -> do
223 throwErrnoIfMinus1_ "setPermissions" $ do
225 mode <- st_mode p_stat
226 let mode1 = modifyBit r mode s_IRUSR
227 let mode2 = modifyBit w mode1 s_IWUSR
228 let mode3 = modifyBit (e || s) mode2 s_IXUSR
232 modifyBit :: Bool -> CMode -> CMode -> CMode
233 modifyBit False m b = m .&. (complement b)
234 modifyBit True m b = m .|. b
237 copyPermissions :: FilePath -> FilePath -> IO ()
238 copyPermissions source dest = do
239 allocaBytes sizeof_stat $ \ p_stat -> do
240 withCString source $ \p_source -> do
241 withCString dest $ \p_dest -> do
242 throwErrnoIfMinus1_ "copyPermissions" $ c_stat p_source p_stat
243 mode <- st_mode p_stat
244 throwErrnoIfMinus1_ "copyPermissions" $ c_chmod p_dest mode
246 -----------------------------------------------------------------------------
249 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
250 initially empty, or as near to empty as the operating system
253 The operation may fail with:
255 * 'isPermissionError' \/ 'PermissionDenied'
256 The process has insufficient privileges to perform the operation.
259 * 'isAlreadyExistsError' \/ 'AlreadyExists'
260 The operand refers to a directory that already exists.
264 A physical I\/O error has occurred.
268 The operand is not a valid directory name.
269 @[ENAMETOOLONG, ELOOP]@
272 There is no path to the directory.
275 * 'ResourceExhausted'
276 Insufficient resources (virtual memory, process file descriptors,
277 physical disk space, etc.) are available to perform the operation.
278 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
280 * 'InappropriateType'
281 The path refers to an existing non-directory object.
286 createDirectory :: FilePath -> IO ()
287 createDirectory path = do
288 #ifdef mingw32_HOST_OS
289 System.Win32.createDirectory path Nothing
291 System.Posix.createDirectory path 0o777
294 #else /* !__GLASGOW_HASKELL__ */
296 copyPermissions :: FilePath -> FilePath -> IO ()
297 copyPermissions fromFPath toFPath
298 = getPermissions fromFPath >>= setPermissions toFPath
302 -- | @'createDirectoryIfMissing' parents dir@ creates a new directory
303 -- @dir@ if it doesn\'t exist. If the first argument is 'True'
304 -- the function will also create all parent directories if they are missing.
305 createDirectoryIfMissing :: Bool -- ^ Create its parents too?
306 -> FilePath -- ^ The path to the directory you want to make
308 createDirectoryIfMissing create_parents "" = return ()
309 createDirectoryIfMissing create_parents path0
310 = do r <- try $ createDirectory path
311 case (r :: Either IOException ()) of
314 | isAlreadyExistsError e -> return ()
315 | isDoesNotExistError e && create_parents -> do
316 createDirectoryIfMissing True (dropFileName path)
317 createDirectoryIfMissing True path
318 | otherwise -> throw e
320 -- we want createDirectoryIfMissing "a/" to behave like
321 -- createDirectoryIfMissing "a". Also, unless we apply
322 -- dropTrailingPathSeparator first, dropFileName won't drop
323 -- anything from "a/".
324 path = dropTrailingPathSeparator path0
326 #if __GLASGOW_HASKELL__
327 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
328 implementation may specify additional constraints which must be
329 satisfied before a directory can be removed (e.g. the directory has to
330 be empty, or may not be in use by other processes). It is not legal
331 for an implementation to partially remove a directory unless the
332 entire directory is removed. A conformant implementation need not
333 support directory removal in all situations (e.g. removal of the root
336 The operation may fail with:
339 A physical I\/O error has occurred.
343 The operand is not a valid directory name.
344 [ENAMETOOLONG, ELOOP]
346 * 'isDoesNotExistError' \/ 'NoSuchThing'
347 The directory does not exist.
350 * 'isPermissionError' \/ 'PermissionDenied'
351 The process has insufficient privileges to perform the operation.
352 @[EROFS, EACCES, EPERM]@
354 * 'UnsatisfiedConstraints'
355 Implementation-dependent constraints are not satisfied.
356 @[EBUSY, ENOTEMPTY, EEXIST]@
358 * 'UnsupportedOperation'
359 The implementation does not support removal in this situation.
362 * 'InappropriateType'
363 The operand refers to an existing non-directory object.
368 removeDirectory :: FilePath -> IO ()
369 removeDirectory path =
370 #ifdef mingw32_HOST_OS
371 System.Win32.removeDirectory path
373 System.Posix.removeDirectory path
378 -- | @'removeDirectoryRecursive' dir@ removes an existing directory /dir/
379 -- together with its content and all subdirectories. Be careful,
380 -- if the directory contains symlinks, the function will follow them.
381 removeDirectoryRecursive :: FilePath -> IO ()
382 removeDirectoryRecursive startLoc = do
383 cont <- getDirectoryContents startLoc
384 sequence_ [rm (startLoc </> x) | x <- cont, x /= "." && x /= ".."]
385 removeDirectory startLoc
387 rm :: FilePath -> IO ()
388 rm f = do temp <- try (removeFile f)
390 Left e -> do isDir <- doesDirectoryExist f
391 -- If f is not a directory, re-throw the error
392 unless isDir $ throw (e :: SomeException)
393 removeDirectoryRecursive f
396 #if __GLASGOW_HASKELL__
397 {- |'removeFile' /file/ removes the directory entry for an existing file
398 /file/, where /file/ is not itself a directory. The
399 implementation may specify additional constraints which must be
400 satisfied before a file can be removed (e.g. the file may not be in
401 use by other processes).
403 The operation may fail with:
406 A physical I\/O error has occurred.
410 The operand is not a valid file name.
411 @[ENAMETOOLONG, ELOOP]@
413 * 'isDoesNotExistError' \/ 'NoSuchThing'
414 The file does not exist.
417 * 'isPermissionError' \/ 'PermissionDenied'
418 The process has insufficient privileges to perform the operation.
419 @[EROFS, EACCES, EPERM]@
421 * 'UnsatisfiedConstraints'
422 Implementation-dependent constraints are not satisfied.
425 * 'InappropriateType'
426 The operand refers to an existing directory.
431 removeFile :: FilePath -> IO ()
434 System.Win32.deleteFile path
436 System.Posix.removeLink path
439 {- |@'renameDirectory' old new@ changes the name of an existing
440 directory from /old/ to /new/. If the /new/ directory
441 already exists, it is atomically replaced by the /old/ directory.
442 If the /new/ directory is neither the /old/ directory nor an
443 alias of the /old/ directory, it is removed as if by
444 'removeDirectory'. A conformant implementation need not support
445 renaming directories in all situations (e.g. renaming to an existing
446 directory, or across different physical devices), but the constraints
449 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
452 The operation may fail with:
455 A physical I\/O error has occurred.
459 Either operand is not a valid directory name.
460 @[ENAMETOOLONG, ELOOP]@
462 * 'isDoesNotExistError' \/ 'NoSuchThing'
463 The original directory does not exist, or there is no path to the target.
466 * 'isPermissionError' \/ 'PermissionDenied'
467 The process has insufficient privileges to perform the operation.
468 @[EROFS, EACCES, EPERM]@
470 * 'ResourceExhausted'
471 Insufficient resources are available to perform the operation.
472 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
474 * 'UnsatisfiedConstraints'
475 Implementation-dependent constraints are not satisfied.
476 @[EBUSY, ENOTEMPTY, EEXIST]@
478 * 'UnsupportedOperation'
479 The implementation does not support renaming in this situation.
482 * 'InappropriateType'
483 Either path refers to an existing non-directory object.
488 renameDirectory :: FilePath -> FilePath -> IO ()
489 renameDirectory opath npath =
490 -- XXX this test isn't performed atomically with the following rename
491 withFileStatus "renameDirectory" opath $ \st -> do
492 is_dir <- isDirectory st
494 then ioException (IOError Nothing InappropriateType "renameDirectory"
495 ("not a directory") (Just opath))
497 #ifdef mingw32_HOST_OS
498 System.Win32.moveFileEx opath npath System.Win32.mOVEFILE_REPLACE_EXISTING
500 System.Posix.rename opath npath
503 {- |@'renameFile' old new@ changes the name of an existing file system
504 object from /old/ to /new/. If the /new/ object already
505 exists, it is atomically replaced by the /old/ object. Neither
506 path may refer to an existing directory. A conformant implementation
507 need not support renaming files in all situations (e.g. renaming
508 across different physical devices), but the constraints must be
511 The operation may fail with:
514 A physical I\/O error has occurred.
518 Either operand is not a valid file name.
519 @[ENAMETOOLONG, ELOOP]@
521 * 'isDoesNotExistError' \/ 'NoSuchThing'
522 The original file does not exist, or there is no path to the target.
525 * 'isPermissionError' \/ 'PermissionDenied'
526 The process has insufficient privileges to perform the operation.
527 @[EROFS, EACCES, EPERM]@
529 * 'ResourceExhausted'
530 Insufficient resources are available to perform the operation.
531 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
533 * 'UnsatisfiedConstraints'
534 Implementation-dependent constraints are not satisfied.
537 * 'UnsupportedOperation'
538 The implementation does not support renaming in this situation.
541 * 'InappropriateType'
542 Either path refers to an existing directory.
543 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
547 renameFile :: FilePath -> FilePath -> IO ()
548 renameFile opath npath =
549 -- XXX this test isn't performed atomically with the following rename
550 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
551 is_dir <- isDirectory st
553 then ioException (IOError Nothing InappropriateType "renameFile"
554 "is a directory" (Just opath))
556 #ifdef mingw32_HOST_OS
557 System.Win32.moveFileEx opath npath System.Win32.mOVEFILE_REPLACE_EXISTING
559 System.Posix.rename opath npath
562 #endif /* __GLASGOW_HASKELL__ */
564 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
565 If the /new/ file already exists, it is atomically replaced by the /old/ file.
566 Neither path may refer to an existing directory. The permissions of /old/ are
567 copied to /new/, if possible.
570 copyFile :: FilePath -> FilePath -> IO ()
572 copyFile fromFPath toFPath =
573 do readFile fromFPath >>= writeFile toFPath
574 Prelude.catch (copyPermissions fromFPath toFPath)
577 copyFile fromFPath toFPath =
578 copy `Prelude.catch` (\exc -> throw $ ioeSetLocation exc "copyFile")
579 where copy = bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
580 bracketOnError openTmp cleanTmp $ \(tmpFPath, hTmp) ->
581 do allocaBytes bufferSize $ copyContents hFrom hTmp
583 ignoreIOExceptions $ copyPermissions fromFPath tmpFPath
584 renameFile tmpFPath toFPath
585 openTmp = openBinaryTempFile (takeDirectory toFPath) ".copyFile.tmp"
586 cleanTmp (tmpFPath, hTmp)
587 = do ignoreIOExceptions $ hClose hTmp
588 ignoreIOExceptions $ removeFile tmpFPath
591 copyContents hFrom hTo buffer = do
592 count <- hGetBuf hFrom buffer bufferSize
593 when (count > 0) $ do
594 hPutBuf hTo buffer count
595 copyContents hFrom hTo buffer
597 ignoreIOExceptions io = io `catch` ioExceptionIgnorer
598 ioExceptionIgnorer :: IOException -> IO ()
599 ioExceptionIgnorer _ = return ()
602 -- | Given path referring to a file or directory, returns a
603 -- canonicalized path, with the intent that two paths referring
604 -- to the same file\/directory will map to the same canonicalized
605 -- path. Note that it is impossible to guarantee that the
606 -- implication (same file\/dir \<=\> same canonicalizedPath) holds
607 -- in either direction: this function can make only a best-effort
609 canonicalizePath :: FilePath -> IO FilePath
610 canonicalizePath fpath =
611 withCString fpath $ \pInPath ->
612 allocaBytes long_path_size $ \pOutPath ->
613 #if defined(mingw32_HOST_OS)
614 alloca $ \ppFilePart ->
615 do c_GetFullPathName pInPath (fromIntegral long_path_size) pOutPath ppFilePart
617 do c_realpath pInPath pOutPath
619 path <- peekCString pOutPath
620 return (normalise path)
621 -- normalise does more stuff, like upper-casing the drive letter
623 #if defined(mingw32_HOST_OS)
624 foreign import stdcall unsafe "GetFullPathNameA"
625 c_GetFullPathName :: CString
631 foreign import ccall unsafe "realpath"
632 c_realpath :: CString
637 -- | 'makeRelative' the current directory.
638 makeRelativeToCurrentDirectory :: FilePath -> IO FilePath
639 makeRelativeToCurrentDirectory x = do
640 cur <- getCurrentDirectory
641 return $ makeRelative cur x
643 -- | Given an executable file name, searches for such file
644 -- in the directories listed in system PATH. The returned value
645 -- is the path to the found executable or Nothing if there isn't
646 -- such executable. For example (findExecutable \"ghc\")
647 -- gives you the path to GHC.
648 findExecutable :: String -> IO (Maybe FilePath)
649 findExecutable binary =
650 #if defined(mingw32_HOST_OS)
651 withCString binary $ \c_binary ->
652 withCString ('.':exeExtension) $ \c_ext ->
653 allocaBytes long_path_size $ \pOutPath ->
654 alloca $ \ppFilePart -> do
655 res <- c_SearchPath nullPtr c_binary c_ext (fromIntegral long_path_size) pOutPath ppFilePart
656 if res > 0 && res < fromIntegral long_path_size
657 then do fpath <- peekCString pOutPath
661 foreign import stdcall unsafe "SearchPathA"
662 c_SearchPath :: CString
671 path <- getEnv "PATH"
672 search (splitSearchPath path)
674 fileName = binary <.> exeExtension
676 search :: [FilePath] -> IO (Maybe FilePath)
677 search [] = return Nothing
679 let path = d </> fileName
680 b <- doesFileExist path
681 if b then return (Just path)
686 #ifdef __GLASGOW_HASKELL__
687 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
690 The operation may fail with:
693 A physical I\/O error has occurred.
697 The operand is not a valid directory name.
698 @[ENAMETOOLONG, ELOOP]@
700 * 'isDoesNotExistError' \/ 'NoSuchThing'
701 The directory does not exist.
704 * 'isPermissionError' \/ 'PermissionDenied'
705 The process has insufficient privileges to perform the operation.
708 * 'ResourceExhausted'
709 Insufficient resources are available to perform the operation.
712 * 'InappropriateType'
713 The path refers to an existing non-directory object.
718 getDirectoryContents :: FilePath -> IO [FilePath]
719 getDirectoryContents path = do
720 modifyIOError (`ioeSetFileName` path) $
721 alloca $ \ ptr_dEnt ->
723 (withCString path $ \s ->
724 throwErrnoIfNullRetry desc (c_opendir s))
725 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
726 (\p -> loop ptr_dEnt p)
728 desc = "getDirectoryContents"
730 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
731 loop ptr_dEnt dir = do
733 r <- readdir dir ptr_dEnt
736 dEnt <- peek ptr_dEnt
740 entry <- (d_name dEnt >>= peekCString)
742 entries <- loop ptr_dEnt dir
743 return (entry:entries)
744 else do errno <- getErrno
745 if (errno == eINTR) then loop ptr_dEnt dir else do
746 let (Errno eo) = errno
747 if (eo == end_of_dir)
753 {- |If the operating system has a notion of current directories,
754 'getCurrentDirectory' returns an absolute path to the
755 current directory of the calling process.
757 The operation may fail with:
760 A physical I\/O error has occurred.
763 * 'isDoesNotExistError' \/ 'NoSuchThing'
764 There is no path referring to the current directory.
765 @[EPERM, ENOENT, ESTALE...]@
767 * 'isPermissionError' \/ 'PermissionDenied'
768 The process has insufficient privileges to perform the operation.
771 * 'ResourceExhausted'
772 Insufficient resources are available to perform the operation.
774 * 'UnsupportedOperation'
775 The operating system has no notion of current directory.
779 getCurrentDirectory :: IO FilePath
780 getCurrentDirectory = do
781 #ifdef mingw32_HOST_OS
782 -- XXX: should use something from Win32
783 p <- mallocBytes long_path_size
785 where go p bytes = do
786 p' <- c_getcwd p (fromIntegral bytes)
788 then do s <- peekCString p'
791 else do errno <- getErrno
793 then do let bytes' = bytes * 2
794 p'' <- reallocBytes p bytes'
796 else throwErrno "getCurrentDirectory"
798 System.Posix.getWorkingDirectory
801 #ifdef mingw32_HOST_OS
802 foreign import ccall unsafe "getcwd"
803 c_getcwd :: Ptr CChar -> CSize -> IO (Ptr CChar)
806 {- |If the operating system has a notion of current directories,
807 @'setCurrentDirectory' dir@ changes the current
808 directory of the calling process to /dir/.
810 The operation may fail with:
813 A physical I\/O error has occurred.
817 The operand is not a valid directory name.
818 @[ENAMETOOLONG, ELOOP]@
820 * 'isDoesNotExistError' \/ 'NoSuchThing'
821 The directory does not exist.
824 * 'isPermissionError' \/ 'PermissionDenied'
825 The process has insufficient privileges to perform the operation.
828 * 'UnsupportedOperation'
829 The operating system has no notion of current directory, or the
830 current directory cannot be dynamically changed.
832 * 'InappropriateType'
833 The path refers to an existing non-directory object.
838 setCurrentDirectory :: FilePath -> IO ()
839 setCurrentDirectory path =
840 #ifdef mingw32_HOST_OS
841 System.Win32.setCurrentDirectory path
843 System.Posix.changeWorkingDirectory path
846 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
847 exists and is a directory, and 'False' otherwise.
850 doesDirectoryExist :: FilePath -> IO Bool
851 doesDirectoryExist name =
852 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
853 `catch` ((\ _ -> return False) :: IOException -> IO Bool)
855 {- |The operation 'doesFileExist' returns 'True'
856 if the argument file exists and is not a directory, and 'False' otherwise.
859 doesFileExist :: FilePath -> IO Bool
861 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
862 `catch` ((\ _ -> return False) :: IOException -> IO Bool)
864 {- |The 'getModificationTime' operation returns the
865 clock time at which the file or directory was last modified.
867 The operation may fail with:
869 * 'isPermissionError' if the user is not permitted to access
870 the modification time; or
872 * 'isDoesNotExistError' if the file or directory does not exist.
876 getModificationTime :: FilePath -> IO ClockTime
877 getModificationTime name =
878 withFileStatus "getModificationTime" name $ \ st ->
881 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
882 withFileStatus loc name f = do
883 modifyIOError (`ioeSetFileName` name) $
884 allocaBytes sizeof_stat $ \p ->
885 withCString (fileNameEndClean name) $ \s -> do
886 throwErrnoIfMinus1Retry_ loc (c_stat s p)
889 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
890 withFileOrSymlinkStatus loc name f = do
891 modifyIOError (`ioeSetFileName` name) $
892 allocaBytes sizeof_stat $ \p ->
893 withCString name $ \s -> do
894 throwErrnoIfMinus1Retry_ loc (lstat s p)
897 modificationTime :: Ptr CStat -> IO ClockTime
898 modificationTime stat = do
899 mtime <- st_mtime stat
900 let realToInteger = round . realToFrac :: Real a => a -> Integer
901 return (TOD (realToInteger (mtime :: CTime)) 0)
903 isDirectory :: Ptr CStat -> IO Bool
904 isDirectory stat = do
906 return (s_isdir mode)
908 fileNameEndClean :: String -> String
909 fileNameEndClean name = if isDrive name then addTrailingPathSeparator name
910 else dropTrailingPathSeparator name
912 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CInt
913 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CInt
914 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CInt
916 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
917 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
918 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode
919 #ifdef mingw32_HOST_OS
920 foreign import ccall unsafe "__hscore_S_IFDIR" s_IFDIR :: CMode
923 foreign import ccall unsafe "__hscore_long_path_size"
924 long_path_size :: Int
927 long_path_size :: Int
928 long_path_size = 2048 -- // guess?
930 #endif /* __GLASGOW_HASKELL__ */
932 {- | Returns the current user's home directory.
934 The directory returned is expected to be writable by the current user,
935 but note that it isn't generally considered good practice to store
936 application-specific data here; use 'getAppUserDataDirectory'
939 On Unix, 'getHomeDirectory' returns the value of the @HOME@
940 environment variable. On Windows, the system is queried for a
941 suitable path; a typical path might be
942 @C:/Documents And Settings/user@.
944 The operation may fail with:
946 * 'UnsupportedOperation'
947 The operating system has no notion of home directory.
949 * 'isDoesNotExistError'
950 The home directory for the current user does not exist, or
953 getHomeDirectory :: IO FilePath
955 #if defined(mingw32_HOST_OS)
956 allocaBytes long_path_size $ \pPath -> do
957 r0 <- c_SHGetFolderPath nullPtr csidl_PROFILE nullPtr 0 pPath
960 r1 <- c_SHGetFolderPath nullPtr csidl_WINDOWS nullPtr 0 pPath
961 when (r1 < 0) (raiseUnsupported "System.Directory.getHomeDirectory")
968 {- | Returns the pathname of a directory in which application-specific
969 data for the current user can be stored. The result of
970 'getAppUserDataDirectory' for a given application is specific to
973 The argument should be the name of the application, which will be used
974 to construct the pathname (so avoid using unusual characters that
975 might result in an invalid pathname).
977 Note: the directory may not actually exist, and may need to be created
978 first. It is expected that the parent directory exists and is
981 On Unix, this function returns @$HOME\/.appName@. On Windows, a
982 typical path might be
984 > C:/Documents And Settings/user/Application Data/appName
986 The operation may fail with:
988 * 'UnsupportedOperation'
989 The operating system has no notion of application-specific data directory.
991 * 'isDoesNotExistError'
992 The home directory for the current user does not exist, or
995 getAppUserDataDirectory :: String -> IO FilePath
996 getAppUserDataDirectory appName = do
997 #if defined(mingw32_HOST_OS)
998 allocaBytes long_path_size $ \pPath -> do
999 r <- c_SHGetFolderPath nullPtr csidl_APPDATA nullPtr 0 pPath
1000 when (r<0) (raiseUnsupported "System.Directory.getAppUserDataDirectory")
1001 s <- peekCString pPath
1002 return (s++'\\':appName)
1004 path <- getEnv "HOME"
1005 return (path++'/':'.':appName)
1008 {- | Returns the current user's document directory.
1010 The directory returned is expected to be writable by the current user,
1011 but note that it isn't generally considered good practice to store
1012 application-specific data here; use 'getAppUserDataDirectory'
1015 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
1016 environment variable. On Windows, the system is queried for a
1017 suitable path; a typical path might be
1018 @C:\/Documents and Settings\/user\/My Documents@.
1020 The operation may fail with:
1022 * 'UnsupportedOperation'
1023 The operating system has no notion of document directory.
1025 * 'isDoesNotExistError'
1026 The document directory for the current user does not exist, or
1029 getUserDocumentsDirectory :: IO FilePath
1030 getUserDocumentsDirectory = do
1031 #if defined(mingw32_HOST_OS)
1032 allocaBytes long_path_size $ \pPath -> do
1033 r <- c_SHGetFolderPath nullPtr csidl_PERSONAL nullPtr 0 pPath
1034 when (r<0) (raiseUnsupported "System.Directory.getUserDocumentsDirectory")
1040 {- | Returns the current directory for temporary files.
1042 On Unix, 'getTemporaryDirectory' returns the value of the @TMPDIR@
1043 environment variable or \"\/tmp\" if the variable isn\'t defined.
1044 On Windows, the function checks for the existence of environment variables in
1045 the following order and uses the first path found:
1048 TMP environment variable.
1051 TEMP environment variable.
1054 USERPROFILE environment variable.
1057 The Windows directory
1059 The operation may fail with:
1061 * 'UnsupportedOperation'
1062 The operating system has no notion of temporary directory.
1064 The function doesn\'t verify whether the path exists.
1066 getTemporaryDirectory :: IO FilePath
1067 getTemporaryDirectory = do
1068 #if defined(mingw32_HOST_OS)
1069 allocaBytes long_path_size $ \pPath -> do
1070 _r <- c_GetTempPath (fromIntegral long_path_size) pPath
1075 `Prelude.catch` \e -> if isDoesNotExistError e then return "/tmp"
1078 `Prelude.catch` (\ex -> return "/tmp")
1082 #if defined(mingw32_HOST_OS)
1083 foreign import ccall unsafe "__hscore_getFolderPath"
1084 c_SHGetFolderPath :: Ptr ()
1090 foreign import ccall unsafe "__hscore_CSIDL_PROFILE" csidl_PROFILE :: CInt
1091 foreign import ccall unsafe "__hscore_CSIDL_APPDATA" csidl_APPDATA :: CInt
1092 foreign import ccall unsafe "__hscore_CSIDL_WINDOWS" csidl_WINDOWS :: CInt
1093 foreign import ccall unsafe "__hscore_CSIDL_PERSONAL" csidl_PERSONAL :: CInt
1095 foreign import stdcall unsafe "GetTempPathA" c_GetTempPath :: CInt -> CString -> IO CInt
1097 raiseUnsupported :: String -> IO ()
1098 raiseUnsupported loc =
1099 ioException (IOError Nothing UnsupportedOperation loc "unsupported operation" Nothing)
1103 -- ToDo: This should be determined via autoconf (AC_EXEEXT)
1104 -- | Extension for executable files
1105 -- (typically @\"\"@ on Unix and @\"exe\"@ on Windows or OS\/2)
1106 exeExtension :: String
1107 #ifdef mingw32_HOST_OS
1108 exeExtension = "exe"