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 (ioeSetErrorString
495 (mkIOError InappropriateType "renameDirectory" Nothing (Just opath))
498 #ifdef mingw32_HOST_OS
499 System.Win32.moveFileEx opath npath System.Win32.mOVEFILE_REPLACE_EXISTING
501 System.Posix.rename opath npath
504 {- |@'renameFile' old new@ changes the name of an existing file system
505 object from /old/ to /new/. If the /new/ object already
506 exists, it is atomically replaced by the /old/ object. Neither
507 path may refer to an existing directory. A conformant implementation
508 need not support renaming files in all situations (e.g. renaming
509 across different physical devices), but the constraints must be
512 The operation may fail with:
515 A physical I\/O error has occurred.
519 Either operand is not a valid file name.
520 @[ENAMETOOLONG, ELOOP]@
522 * 'isDoesNotExistError' \/ 'NoSuchThing'
523 The original file does not exist, or there is no path to the target.
526 * 'isPermissionError' \/ 'PermissionDenied'
527 The process has insufficient privileges to perform the operation.
528 @[EROFS, EACCES, EPERM]@
530 * 'ResourceExhausted'
531 Insufficient resources are available to perform the operation.
532 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
534 * 'UnsatisfiedConstraints'
535 Implementation-dependent constraints are not satisfied.
538 * 'UnsupportedOperation'
539 The implementation does not support renaming in this situation.
542 * 'InappropriateType'
543 Either path refers to an existing directory.
544 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
548 renameFile :: FilePath -> FilePath -> IO ()
549 renameFile opath npath =
550 -- XXX this test isn't performed atomically with the following rename
551 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
552 is_dir <- isDirectory st
554 then ioException (ioeSetErrorString
555 (mkIOError InappropriateType "renameFile" Nothing (Just opath))
558 #ifdef mingw32_HOST_OS
559 System.Win32.moveFileEx opath npath System.Win32.mOVEFILE_REPLACE_EXISTING
561 System.Posix.rename opath npath
564 #endif /* __GLASGOW_HASKELL__ */
566 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
567 If the /new/ file already exists, it is atomically replaced by the /old/ file.
568 Neither path may refer to an existing directory. The permissions of /old/ are
569 copied to /new/, if possible.
572 copyFile :: FilePath -> FilePath -> IO ()
574 copyFile fromFPath toFPath =
575 do readFile fromFPath >>= writeFile toFPath
576 Prelude.catch (copyPermissions fromFPath toFPath)
579 copyFile fromFPath toFPath =
580 copy `Prelude.catch` (\exc -> throw $ ioeSetLocation exc "copyFile")
581 where copy = bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
582 bracketOnError openTmp cleanTmp $ \(tmpFPath, hTmp) ->
583 do allocaBytes bufferSize $ copyContents hFrom hTmp
585 ignoreIOExceptions $ copyPermissions fromFPath tmpFPath
586 renameFile tmpFPath toFPath
587 openTmp = openBinaryTempFile (takeDirectory toFPath) ".copyFile.tmp"
588 cleanTmp (tmpFPath, hTmp)
589 = do ignoreIOExceptions $ hClose hTmp
590 ignoreIOExceptions $ removeFile tmpFPath
593 copyContents hFrom hTo buffer = do
594 count <- hGetBuf hFrom buffer bufferSize
595 when (count > 0) $ do
596 hPutBuf hTo buffer count
597 copyContents hFrom hTo buffer
599 ignoreIOExceptions io = io `catch` ioExceptionIgnorer
600 ioExceptionIgnorer :: IOException -> IO ()
601 ioExceptionIgnorer _ = return ()
604 -- | Given path referring to a file or directory, returns a
605 -- canonicalized path, with the intent that two paths referring
606 -- to the same file\/directory will map to the same canonicalized
607 -- path. Note that it is impossible to guarantee that the
608 -- implication (same file\/dir \<=\> same canonicalizedPath) holds
609 -- in either direction: this function can make only a best-effort
611 canonicalizePath :: FilePath -> IO FilePath
612 canonicalizePath fpath =
613 withCString fpath $ \pInPath ->
614 allocaBytes long_path_size $ \pOutPath ->
615 #if defined(mingw32_HOST_OS)
616 alloca $ \ppFilePart ->
617 do c_GetFullPathName pInPath (fromIntegral long_path_size) pOutPath ppFilePart
619 do c_realpath pInPath pOutPath
621 path <- peekCString pOutPath
622 return (normalise path)
623 -- normalise does more stuff, like upper-casing the drive letter
625 #if defined(mingw32_HOST_OS)
626 foreign import stdcall unsafe "GetFullPathNameA"
627 c_GetFullPathName :: CString
633 foreign import ccall unsafe "realpath"
634 c_realpath :: CString
639 -- | 'makeRelative' the current directory.
640 makeRelativeToCurrentDirectory :: FilePath -> IO FilePath
641 makeRelativeToCurrentDirectory x = do
642 cur <- getCurrentDirectory
643 return $ makeRelative cur x
645 -- | Given an executable file name, searches for such file
646 -- in the directories listed in system PATH. The returned value
647 -- is the path to the found executable or Nothing if there isn't
648 -- such executable. For example (findExecutable \"ghc\")
649 -- gives you the path to GHC.
650 findExecutable :: String -> IO (Maybe FilePath)
651 findExecutable binary =
652 #if defined(mingw32_HOST_OS)
653 withCString binary $ \c_binary ->
654 withCString ('.':exeExtension) $ \c_ext ->
655 allocaBytes long_path_size $ \pOutPath ->
656 alloca $ \ppFilePart -> do
657 res <- c_SearchPath nullPtr c_binary c_ext (fromIntegral long_path_size) pOutPath ppFilePart
658 if res > 0 && res < fromIntegral long_path_size
659 then do fpath <- peekCString pOutPath
663 foreign import stdcall unsafe "SearchPathA"
664 c_SearchPath :: CString
673 path <- getEnv "PATH"
674 search (splitSearchPath path)
676 fileName = binary <.> exeExtension
678 search :: [FilePath] -> IO (Maybe FilePath)
679 search [] = return Nothing
681 let path = d </> fileName
682 b <- doesFileExist path
683 if b then return (Just path)
688 #ifdef __GLASGOW_HASKELL__
689 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
692 The operation may fail with:
695 A physical I\/O error has occurred.
699 The operand is not a valid directory name.
700 @[ENAMETOOLONG, ELOOP]@
702 * 'isDoesNotExistError' \/ 'NoSuchThing'
703 The directory does not exist.
706 * 'isPermissionError' \/ 'PermissionDenied'
707 The process has insufficient privileges to perform the operation.
710 * 'ResourceExhausted'
711 Insufficient resources are available to perform the operation.
714 * 'InappropriateType'
715 The path refers to an existing non-directory object.
720 getDirectoryContents :: FilePath -> IO [FilePath]
721 getDirectoryContents path = do
722 modifyIOError (`ioeSetFileName` path) $
723 alloca $ \ ptr_dEnt ->
725 (withCString path $ \s ->
726 throwErrnoIfNullRetry desc (c_opendir s))
727 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
728 (\p -> loop ptr_dEnt p)
730 desc = "getDirectoryContents"
732 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
733 loop ptr_dEnt dir = do
735 r <- readdir dir ptr_dEnt
738 dEnt <- peek ptr_dEnt
742 entry <- (d_name dEnt >>= peekCString)
744 entries <- loop ptr_dEnt dir
745 return (entry:entries)
746 else do errno <- getErrno
747 if (errno == eINTR) then loop ptr_dEnt dir else do
748 let (Errno eo) = errno
749 if (eo == end_of_dir)
755 {- |If the operating system has a notion of current directories,
756 'getCurrentDirectory' returns an absolute path to the
757 current directory of the calling process.
759 The operation may fail with:
762 A physical I\/O error has occurred.
765 * 'isDoesNotExistError' \/ 'NoSuchThing'
766 There is no path referring to the current directory.
767 @[EPERM, ENOENT, ESTALE...]@
769 * 'isPermissionError' \/ 'PermissionDenied'
770 The process has insufficient privileges to perform the operation.
773 * 'ResourceExhausted'
774 Insufficient resources are available to perform the operation.
776 * 'UnsupportedOperation'
777 The operating system has no notion of current directory.
781 getCurrentDirectory :: IO FilePath
782 getCurrentDirectory = do
783 #ifdef mingw32_HOST_OS
784 -- XXX: should use something from Win32
785 p <- mallocBytes long_path_size
787 where go p bytes = do
788 p' <- c_getcwd p (fromIntegral bytes)
790 then do s <- peekCString p'
793 else do errno <- getErrno
795 then do let bytes' = bytes * 2
796 p'' <- reallocBytes p bytes'
798 else throwErrno "getCurrentDirectory"
800 System.Posix.getWorkingDirectory
803 #ifdef mingw32_HOST_OS
804 foreign import ccall unsafe "getcwd"
805 c_getcwd :: Ptr CChar -> CSize -> IO (Ptr CChar)
808 {- |If the operating system has a notion of current directories,
809 @'setCurrentDirectory' dir@ changes the current
810 directory of the calling process to /dir/.
812 The operation may fail with:
815 A physical I\/O error has occurred.
819 The operand is not a valid directory name.
820 @[ENAMETOOLONG, ELOOP]@
822 * 'isDoesNotExistError' \/ 'NoSuchThing'
823 The directory does not exist.
826 * 'isPermissionError' \/ 'PermissionDenied'
827 The process has insufficient privileges to perform the operation.
830 * 'UnsupportedOperation'
831 The operating system has no notion of current directory, or the
832 current directory cannot be dynamically changed.
834 * 'InappropriateType'
835 The path refers to an existing non-directory object.
840 setCurrentDirectory :: FilePath -> IO ()
841 setCurrentDirectory path =
842 #ifdef mingw32_HOST_OS
843 System.Win32.setCurrentDirectory path
845 System.Posix.changeWorkingDirectory path
848 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
849 exists and is a directory, and 'False' otherwise.
852 doesDirectoryExist :: FilePath -> IO Bool
853 doesDirectoryExist name =
854 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
855 `catch` ((\ _ -> return False) :: IOException -> IO Bool)
857 {- |The operation 'doesFileExist' returns 'True'
858 if the argument file exists and is not a directory, and 'False' otherwise.
861 doesFileExist :: FilePath -> IO Bool
863 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
864 `catch` ((\ _ -> return False) :: IOException -> IO Bool)
866 {- |The 'getModificationTime' operation returns the
867 clock time at which the file or directory was last modified.
869 The operation may fail with:
871 * 'isPermissionError' if the user is not permitted to access
872 the modification time; or
874 * 'isDoesNotExistError' if the file or directory does not exist.
878 getModificationTime :: FilePath -> IO ClockTime
879 getModificationTime name =
880 withFileStatus "getModificationTime" name $ \ st ->
883 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
884 withFileStatus loc name f = do
885 modifyIOError (`ioeSetFileName` name) $
886 allocaBytes sizeof_stat $ \p ->
887 withCString (fileNameEndClean name) $ \s -> do
888 throwErrnoIfMinus1Retry_ loc (c_stat s p)
891 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
892 withFileOrSymlinkStatus loc name f = do
893 modifyIOError (`ioeSetFileName` name) $
894 allocaBytes sizeof_stat $ \p ->
895 withCString name $ \s -> do
896 throwErrnoIfMinus1Retry_ loc (lstat s p)
899 modificationTime :: Ptr CStat -> IO ClockTime
900 modificationTime stat = do
901 mtime <- st_mtime stat
902 let realToInteger = round . realToFrac :: Real a => a -> Integer
903 return (TOD (realToInteger (mtime :: CTime)) 0)
905 isDirectory :: Ptr CStat -> IO Bool
906 isDirectory stat = do
908 return (s_isdir mode)
910 fileNameEndClean :: String -> String
911 fileNameEndClean name = if isDrive name then addTrailingPathSeparator name
912 else dropTrailingPathSeparator name
914 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CInt
915 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CInt
916 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CInt
918 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
919 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
920 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode
921 #ifdef mingw32_HOST_OS
922 foreign import ccall unsafe "__hscore_S_IFDIR" s_IFDIR :: CMode
925 foreign import ccall unsafe "__hscore_long_path_size"
926 long_path_size :: Int
929 long_path_size :: Int
930 long_path_size = 2048 -- // guess?
932 #endif /* __GLASGOW_HASKELL__ */
934 {- | Returns the current user's home directory.
936 The directory returned is expected to be writable by the current user,
937 but note that it isn't generally considered good practice to store
938 application-specific data here; use 'getAppUserDataDirectory'
941 On Unix, 'getHomeDirectory' returns the value of the @HOME@
942 environment variable. On Windows, the system is queried for a
943 suitable path; a typical path might be
944 @C:/Documents And Settings/user@.
946 The operation may fail with:
948 * 'UnsupportedOperation'
949 The operating system has no notion of home directory.
951 * 'isDoesNotExistError'
952 The home directory for the current user does not exist, or
955 getHomeDirectory :: IO FilePath
957 #if defined(mingw32_HOST_OS)
958 allocaBytes long_path_size $ \pPath -> do
959 r0 <- c_SHGetFolderPath nullPtr csidl_PROFILE nullPtr 0 pPath
962 r1 <- c_SHGetFolderPath nullPtr csidl_WINDOWS nullPtr 0 pPath
963 when (r1 < 0) (raiseUnsupported "System.Directory.getHomeDirectory")
970 {- | Returns the pathname of a directory in which application-specific
971 data for the current user can be stored. The result of
972 'getAppUserDataDirectory' for a given application is specific to
975 The argument should be the name of the application, which will be used
976 to construct the pathname (so avoid using unusual characters that
977 might result in an invalid pathname).
979 Note: the directory may not actually exist, and may need to be created
980 first. It is expected that the parent directory exists and is
983 On Unix, this function returns @$HOME\/.appName@. On Windows, a
984 typical path might be
986 > C:/Documents And Settings/user/Application Data/appName
988 The operation may fail with:
990 * 'UnsupportedOperation'
991 The operating system has no notion of application-specific data directory.
993 * 'isDoesNotExistError'
994 The home directory for the current user does not exist, or
997 getAppUserDataDirectory :: String -> IO FilePath
998 getAppUserDataDirectory appName = do
999 #if defined(mingw32_HOST_OS)
1000 allocaBytes long_path_size $ \pPath -> do
1001 r <- c_SHGetFolderPath nullPtr csidl_APPDATA nullPtr 0 pPath
1002 when (r<0) (raiseUnsupported "System.Directory.getAppUserDataDirectory")
1003 s <- peekCString pPath
1004 return (s++'\\':appName)
1006 path <- getEnv "HOME"
1007 return (path++'/':'.':appName)
1010 {- | Returns the current user's document directory.
1012 The directory returned is expected to be writable by the current user,
1013 but note that it isn't generally considered good practice to store
1014 application-specific data here; use 'getAppUserDataDirectory'
1017 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
1018 environment variable. On Windows, the system is queried for a
1019 suitable path; a typical path might be
1020 @C:\/Documents and Settings\/user\/My Documents@.
1022 The operation may fail with:
1024 * 'UnsupportedOperation'
1025 The operating system has no notion of document directory.
1027 * 'isDoesNotExistError'
1028 The document directory for the current user does not exist, or
1031 getUserDocumentsDirectory :: IO FilePath
1032 getUserDocumentsDirectory = do
1033 #if defined(mingw32_HOST_OS)
1034 allocaBytes long_path_size $ \pPath -> do
1035 r <- c_SHGetFolderPath nullPtr csidl_PERSONAL nullPtr 0 pPath
1036 when (r<0) (raiseUnsupported "System.Directory.getUserDocumentsDirectory")
1042 {- | Returns the current directory for temporary files.
1044 On Unix, 'getTemporaryDirectory' returns the value of the @TMPDIR@
1045 environment variable or \"\/tmp\" if the variable isn\'t defined.
1046 On Windows, the function checks for the existence of environment variables in
1047 the following order and uses the first path found:
1050 TMP environment variable.
1053 TEMP environment variable.
1056 USERPROFILE environment variable.
1059 The Windows directory
1061 The operation may fail with:
1063 * 'UnsupportedOperation'
1064 The operating system has no notion of temporary directory.
1066 The function doesn\'t verify whether the path exists.
1068 getTemporaryDirectory :: IO FilePath
1069 getTemporaryDirectory = do
1070 #if defined(mingw32_HOST_OS)
1071 allocaBytes long_path_size $ \pPath -> do
1072 _r <- c_GetTempPath (fromIntegral long_path_size) pPath
1077 `Prelude.catch` \e -> if isDoesNotExistError e then return "/tmp"
1080 `Prelude.catch` (\ex -> return "/tmp")
1084 #if defined(mingw32_HOST_OS)
1085 foreign import ccall unsafe "__hscore_getFolderPath"
1086 c_SHGetFolderPath :: Ptr ()
1092 foreign import ccall unsafe "__hscore_CSIDL_PROFILE" csidl_PROFILE :: CInt
1093 foreign import ccall unsafe "__hscore_CSIDL_APPDATA" csidl_APPDATA :: CInt
1094 foreign import ccall unsafe "__hscore_CSIDL_WINDOWS" csidl_WINDOWS :: CInt
1095 foreign import ccall unsafe "__hscore_CSIDL_PERSONAL" csidl_PERSONAL :: CInt
1097 foreign import stdcall unsafe "GetTempPathA" c_GetTempPath :: CInt -> CString -> IO CInt
1099 raiseUnsupported :: String -> IO ()
1100 raiseUnsupported loc =
1101 ioException (ioeSetErrorString (mkIOError UnsupportedOperation loc Nothing Nothing) "unsupported operation")
1105 -- ToDo: This should be determined via autoconf (AC_EXEEXT)
1106 -- | Extension for executable files
1107 -- (typically @\"\"@ on Unix and @\"exe\"@ on Windows or OS\/2)
1108 exeExtension :: String
1109 #ifdef mingw32_HOST_OS
1110 exeExtension = "exe"