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 path0
309 | create_parents = createDirs (parents path0)
310 | otherwise = createDirs (take 1 (parents path0))
312 parents = reverse . scanl1 (</>) . splitDirectories . normalise
314 createDirs [] = return ()
315 createDirs (dir:[]) = createDir dir throw
316 createDirs (dir:dirs) =
317 createDir dir $ \_ -> do
321 createDir :: FilePath -> (IOException -> IO ()) -> IO ()
322 createDir dir notExistHandler = do
323 r <- try $ createDirectory dir
324 case (r :: Either IOException ()) of
325 Right () -> return ()
327 | isDoesNotExistError e -> notExistHandler e
328 -- createDirectory (and indeed POSIX mkdir) does not distinguish
329 -- between a dir already existing and a file already existing. So we
330 -- check for it here. Unfortunately there is a slight race condition
331 -- here, but we think it is benign. It could report an exeption in
332 -- the case that the dir did exist but another process deletes the
333 -- directory and creates a file in its place before we can check
334 -- that the directory did indeed exist.
335 | isAlreadyExistsError e ->
336 (withFileStatus "createDirectoryIfMissing" dir $ \st -> do
337 isDir <- isDirectory st
338 if isDir then return ()
340 ) `catch` ((\_ -> return ()) :: IOException -> IO ())
341 | otherwise -> throw e
343 #if __GLASGOW_HASKELL__
344 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
345 implementation may specify additional constraints which must be
346 satisfied before a directory can be removed (e.g. the directory has to
347 be empty, or may not be in use by other processes). It is not legal
348 for an implementation to partially remove a directory unless the
349 entire directory is removed. A conformant implementation need not
350 support directory removal in all situations (e.g. removal of the root
353 The operation may fail with:
356 A physical I\/O error has occurred.
360 The operand is not a valid directory name.
361 [ENAMETOOLONG, ELOOP]
363 * 'isDoesNotExistError' \/ 'NoSuchThing'
364 The directory does not exist.
367 * 'isPermissionError' \/ 'PermissionDenied'
368 The process has insufficient privileges to perform the operation.
369 @[EROFS, EACCES, EPERM]@
371 * 'UnsatisfiedConstraints'
372 Implementation-dependent constraints are not satisfied.
373 @[EBUSY, ENOTEMPTY, EEXIST]@
375 * 'UnsupportedOperation'
376 The implementation does not support removal in this situation.
379 * 'InappropriateType'
380 The operand refers to an existing non-directory object.
385 removeDirectory :: FilePath -> IO ()
386 removeDirectory path =
387 #ifdef mingw32_HOST_OS
388 System.Win32.removeDirectory path
390 System.Posix.removeDirectory path
395 -- | @'removeDirectoryRecursive' dir@ removes an existing directory /dir/
396 -- together with its content and all subdirectories. Be careful,
397 -- if the directory contains symlinks, the function will follow them.
398 removeDirectoryRecursive :: FilePath -> IO ()
399 removeDirectoryRecursive startLoc = do
400 cont <- getDirectoryContents startLoc
401 sequence_ [rm (startLoc </> x) | x <- cont, x /= "." && x /= ".."]
402 removeDirectory startLoc
404 rm :: FilePath -> IO ()
405 rm f = do temp <- try (removeFile f)
407 Left e -> do isDir <- doesDirectoryExist f
408 -- If f is not a directory, re-throw the error
409 unless isDir $ throw (e :: SomeException)
410 removeDirectoryRecursive f
413 #if __GLASGOW_HASKELL__
414 {- |'removeFile' /file/ removes the directory entry for an existing file
415 /file/, where /file/ is not itself a directory. The
416 implementation may specify additional constraints which must be
417 satisfied before a file can be removed (e.g. the file may not be in
418 use by other processes).
420 The operation may fail with:
423 A physical I\/O error has occurred.
427 The operand is not a valid file name.
428 @[ENAMETOOLONG, ELOOP]@
430 * 'isDoesNotExistError' \/ 'NoSuchThing'
431 The file does not exist.
434 * 'isPermissionError' \/ 'PermissionDenied'
435 The process has insufficient privileges to perform the operation.
436 @[EROFS, EACCES, EPERM]@
438 * 'UnsatisfiedConstraints'
439 Implementation-dependent constraints are not satisfied.
442 * 'InappropriateType'
443 The operand refers to an existing directory.
448 removeFile :: FilePath -> IO ()
451 System.Win32.deleteFile path
453 System.Posix.removeLink path
456 {- |@'renameDirectory' old new@ changes the name of an existing
457 directory from /old/ to /new/. If the /new/ directory
458 already exists, it is atomically replaced by the /old/ directory.
459 If the /new/ directory is neither the /old/ directory nor an
460 alias of the /old/ directory, it is removed as if by
461 'removeDirectory'. A conformant implementation need not support
462 renaming directories in all situations (e.g. renaming to an existing
463 directory, or across different physical devices), but the constraints
466 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
469 The operation may fail with:
472 A physical I\/O error has occurred.
476 Either operand is not a valid directory name.
477 @[ENAMETOOLONG, ELOOP]@
479 * 'isDoesNotExistError' \/ 'NoSuchThing'
480 The original directory does not exist, or there is no path to the target.
483 * 'isPermissionError' \/ 'PermissionDenied'
484 The process has insufficient privileges to perform the operation.
485 @[EROFS, EACCES, EPERM]@
487 * 'ResourceExhausted'
488 Insufficient resources are available to perform the operation.
489 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
491 * 'UnsatisfiedConstraints'
492 Implementation-dependent constraints are not satisfied.
493 @[EBUSY, ENOTEMPTY, EEXIST]@
495 * 'UnsupportedOperation'
496 The implementation does not support renaming in this situation.
499 * 'InappropriateType'
500 Either path refers to an existing non-directory object.
505 renameDirectory :: FilePath -> FilePath -> IO ()
506 renameDirectory opath npath =
507 -- XXX this test isn't performed atomically with the following rename
508 withFileStatus "renameDirectory" opath $ \st -> do
509 is_dir <- isDirectory st
511 then ioException (ioeSetErrorString
512 (mkIOError InappropriateType "renameDirectory" Nothing (Just opath))
515 #ifdef mingw32_HOST_OS
516 System.Win32.moveFileEx opath npath System.Win32.mOVEFILE_REPLACE_EXISTING
518 System.Posix.rename opath npath
521 {- |@'renameFile' old new@ changes the name of an existing file system
522 object from /old/ to /new/. If the /new/ object already
523 exists, it is atomically replaced by the /old/ object. Neither
524 path may refer to an existing directory. A conformant implementation
525 need not support renaming files in all situations (e.g. renaming
526 across different physical devices), but the constraints must be
529 The operation may fail with:
532 A physical I\/O error has occurred.
536 Either operand is not a valid file name.
537 @[ENAMETOOLONG, ELOOP]@
539 * 'isDoesNotExistError' \/ 'NoSuchThing'
540 The original file does not exist, or there is no path to the target.
543 * 'isPermissionError' \/ 'PermissionDenied'
544 The process has insufficient privileges to perform the operation.
545 @[EROFS, EACCES, EPERM]@
547 * 'ResourceExhausted'
548 Insufficient resources are available to perform the operation.
549 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
551 * 'UnsatisfiedConstraints'
552 Implementation-dependent constraints are not satisfied.
555 * 'UnsupportedOperation'
556 The implementation does not support renaming in this situation.
559 * 'InappropriateType'
560 Either path refers to an existing directory.
561 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
565 renameFile :: FilePath -> FilePath -> IO ()
566 renameFile opath npath =
567 -- XXX this test isn't performed atomically with the following rename
568 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
569 is_dir <- isDirectory st
571 then ioException (ioeSetErrorString
572 (mkIOError InappropriateType "renameFile" Nothing (Just opath))
575 #ifdef mingw32_HOST_OS
576 System.Win32.moveFileEx opath npath System.Win32.mOVEFILE_REPLACE_EXISTING
578 System.Posix.rename opath npath
581 #endif /* __GLASGOW_HASKELL__ */
583 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
584 If the /new/ file already exists, it is atomically replaced by the /old/ file.
585 Neither path may refer to an existing directory. The permissions of /old/ are
586 copied to /new/, if possible.
589 copyFile :: FilePath -> FilePath -> IO ()
591 copyFile fromFPath toFPath =
592 do readFile fromFPath >>= writeFile toFPath
593 Prelude.catch (copyPermissions fromFPath toFPath)
596 copyFile fromFPath toFPath =
597 copy `Prelude.catch` (\exc -> throw $ ioeSetLocation exc "copyFile")
598 where copy = bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
599 bracketOnError openTmp cleanTmp $ \(tmpFPath, hTmp) ->
600 do allocaBytes bufferSize $ copyContents hFrom hTmp
602 ignoreIOExceptions $ copyPermissions fromFPath tmpFPath
603 renameFile tmpFPath toFPath
604 openTmp = openBinaryTempFile (takeDirectory toFPath) ".copyFile.tmp"
605 cleanTmp (tmpFPath, hTmp)
606 = do ignoreIOExceptions $ hClose hTmp
607 ignoreIOExceptions $ removeFile tmpFPath
610 copyContents hFrom hTo buffer = do
611 count <- hGetBuf hFrom buffer bufferSize
612 when (count > 0) $ do
613 hPutBuf hTo buffer count
614 copyContents hFrom hTo buffer
616 ignoreIOExceptions io = io `catch` ioExceptionIgnorer
617 ioExceptionIgnorer :: IOException -> IO ()
618 ioExceptionIgnorer _ = return ()
621 -- | Given path referring to a file or directory, returns a
622 -- canonicalized path, with the intent that two paths referring
623 -- to the same file\/directory will map to the same canonicalized
624 -- path. Note that it is impossible to guarantee that the
625 -- implication (same file\/dir \<=\> same canonicalizedPath) holds
626 -- in either direction: this function can make only a best-effort
628 canonicalizePath :: FilePath -> IO FilePath
629 canonicalizePath fpath =
630 withCString fpath $ \pInPath ->
631 allocaBytes long_path_size $ \pOutPath ->
632 #if defined(mingw32_HOST_OS)
633 alloca $ \ppFilePart ->
634 do c_GetFullPathName pInPath (fromIntegral long_path_size) pOutPath ppFilePart
636 do c_realpath pInPath pOutPath
638 path <- peekCString pOutPath
639 return (normalise path)
640 -- normalise does more stuff, like upper-casing the drive letter
642 #if defined(mingw32_HOST_OS)
643 foreign import stdcall unsafe "GetFullPathNameA"
644 c_GetFullPathName :: CString
650 foreign import ccall unsafe "realpath"
651 c_realpath :: CString
656 -- | 'makeRelative' the current directory.
657 makeRelativeToCurrentDirectory :: FilePath -> IO FilePath
658 makeRelativeToCurrentDirectory x = do
659 cur <- getCurrentDirectory
660 return $ makeRelative cur x
662 -- | Given an executable file name, searches for such file
663 -- in the directories listed in system PATH. The returned value
664 -- is the path to the found executable or Nothing if there isn't
665 -- such executable. For example (findExecutable \"ghc\")
666 -- gives you the path to GHC.
667 findExecutable :: String -> IO (Maybe FilePath)
668 findExecutable binary =
669 #if defined(mingw32_HOST_OS)
670 withCString binary $ \c_binary ->
671 withCString ('.':exeExtension) $ \c_ext ->
672 allocaBytes long_path_size $ \pOutPath ->
673 alloca $ \ppFilePart -> do
674 res <- c_SearchPath nullPtr c_binary c_ext (fromIntegral long_path_size) pOutPath ppFilePart
675 if res > 0 && res < fromIntegral long_path_size
676 then do fpath <- peekCString pOutPath
680 foreign import stdcall unsafe "SearchPathA"
681 c_SearchPath :: CString
690 path <- getEnv "PATH"
691 search (splitSearchPath path)
693 fileName = binary <.> exeExtension
695 search :: [FilePath] -> IO (Maybe FilePath)
696 search [] = return Nothing
698 let path = d </> fileName
699 b <- doesFileExist path
700 if b then return (Just path)
705 #ifdef __GLASGOW_HASKELL__
706 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
709 The operation may fail with:
712 A physical I\/O error has occurred.
716 The operand is not a valid directory name.
717 @[ENAMETOOLONG, ELOOP]@
719 * 'isDoesNotExistError' \/ 'NoSuchThing'
720 The directory does not exist.
723 * 'isPermissionError' \/ 'PermissionDenied'
724 The process has insufficient privileges to perform the operation.
727 * 'ResourceExhausted'
728 Insufficient resources are available to perform the operation.
731 * 'InappropriateType'
732 The path refers to an existing non-directory object.
737 getDirectoryContents :: FilePath -> IO [FilePath]
738 getDirectoryContents path = do
739 modifyIOError (`ioeSetFileName` path) $
740 alloca $ \ ptr_dEnt ->
742 (withCString path $ \s ->
743 throwErrnoIfNullRetry desc (c_opendir s))
744 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
745 (\p -> loop ptr_dEnt p)
747 desc = "getDirectoryContents"
749 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
750 loop ptr_dEnt dir = do
752 r <- readdir dir ptr_dEnt
755 dEnt <- peek ptr_dEnt
759 entry <- (d_name dEnt >>= peekCString)
761 entries <- loop ptr_dEnt dir
762 return (entry:entries)
763 else do errno <- getErrno
764 if (errno == eINTR) then loop ptr_dEnt dir else do
765 let (Errno eo) = errno
766 if (eo == end_of_dir)
772 {- |If the operating system has a notion of current directories,
773 'getCurrentDirectory' returns an absolute path to the
774 current directory of the calling process.
776 The operation may fail with:
779 A physical I\/O error has occurred.
782 * 'isDoesNotExistError' \/ 'NoSuchThing'
783 There is no path referring to the current directory.
784 @[EPERM, ENOENT, ESTALE...]@
786 * 'isPermissionError' \/ 'PermissionDenied'
787 The process has insufficient privileges to perform the operation.
790 * 'ResourceExhausted'
791 Insufficient resources are available to perform the operation.
793 * 'UnsupportedOperation'
794 The operating system has no notion of current directory.
798 getCurrentDirectory :: IO FilePath
799 getCurrentDirectory = do
800 #ifdef mingw32_HOST_OS
801 -- XXX: should use something from Win32
802 p <- mallocBytes long_path_size
804 where go p bytes = do
805 p' <- c_getcwd p (fromIntegral bytes)
807 then do s <- peekCString p'
810 else do errno <- getErrno
812 then do let bytes' = bytes * 2
813 p'' <- reallocBytes p bytes'
815 else throwErrno "getCurrentDirectory"
817 System.Posix.getWorkingDirectory
820 #ifdef mingw32_HOST_OS
821 foreign import ccall unsafe "getcwd"
822 c_getcwd :: Ptr CChar -> CSize -> IO (Ptr CChar)
825 {- |If the operating system has a notion of current directories,
826 @'setCurrentDirectory' dir@ changes the current
827 directory of the calling process to /dir/.
829 The operation may fail with:
832 A physical I\/O error has occurred.
836 The operand is not a valid directory name.
837 @[ENAMETOOLONG, ELOOP]@
839 * 'isDoesNotExistError' \/ 'NoSuchThing'
840 The directory does not exist.
843 * 'isPermissionError' \/ 'PermissionDenied'
844 The process has insufficient privileges to perform the operation.
847 * 'UnsupportedOperation'
848 The operating system has no notion of current directory, or the
849 current directory cannot be dynamically changed.
851 * 'InappropriateType'
852 The path refers to an existing non-directory object.
857 setCurrentDirectory :: FilePath -> IO ()
858 setCurrentDirectory path =
859 #ifdef mingw32_HOST_OS
860 System.Win32.setCurrentDirectory path
862 System.Posix.changeWorkingDirectory path
865 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
866 exists and is a directory, and 'False' otherwise.
869 doesDirectoryExist :: FilePath -> IO Bool
870 doesDirectoryExist name =
871 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
872 `catch` ((\ _ -> return False) :: IOException -> IO Bool)
874 {- |The operation 'doesFileExist' returns 'True'
875 if the argument file exists and is not a directory, and 'False' otherwise.
878 doesFileExist :: FilePath -> IO Bool
880 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
881 `catch` ((\ _ -> return False) :: IOException -> IO Bool)
883 {- |The 'getModificationTime' operation returns the
884 clock time at which the file or directory was last modified.
886 The operation may fail with:
888 * 'isPermissionError' if the user is not permitted to access
889 the modification time; or
891 * 'isDoesNotExistError' if the file or directory does not exist.
895 getModificationTime :: FilePath -> IO ClockTime
896 getModificationTime name =
897 withFileStatus "getModificationTime" name $ \ st ->
900 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
901 withFileStatus loc name f = do
902 modifyIOError (`ioeSetFileName` name) $
903 allocaBytes sizeof_stat $ \p ->
904 withCString (fileNameEndClean name) $ \s -> do
905 throwErrnoIfMinus1Retry_ loc (c_stat s p)
908 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
909 withFileOrSymlinkStatus loc name f = do
910 modifyIOError (`ioeSetFileName` name) $
911 allocaBytes sizeof_stat $ \p ->
912 withCString name $ \s -> do
913 throwErrnoIfMinus1Retry_ loc (lstat s p)
916 modificationTime :: Ptr CStat -> IO ClockTime
917 modificationTime stat = do
918 mtime <- st_mtime stat
919 let realToInteger = round . realToFrac :: Real a => a -> Integer
920 return (TOD (realToInteger (mtime :: CTime)) 0)
922 isDirectory :: Ptr CStat -> IO Bool
923 isDirectory stat = do
925 return (s_isdir mode)
927 fileNameEndClean :: String -> String
928 fileNameEndClean name = if isDrive name then addTrailingPathSeparator name
929 else dropTrailingPathSeparator name
931 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CInt
932 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CInt
933 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CInt
935 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
936 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
937 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode
938 #ifdef mingw32_HOST_OS
939 foreign import ccall unsafe "__hscore_S_IFDIR" s_IFDIR :: CMode
942 foreign import ccall unsafe "__hscore_long_path_size"
943 long_path_size :: Int
946 long_path_size :: Int
947 long_path_size = 2048 -- // guess?
949 #endif /* __GLASGOW_HASKELL__ */
951 {- | Returns the current user's home directory.
953 The directory returned is expected to be writable by the current user,
954 but note that it isn't generally considered good practice to store
955 application-specific data here; use 'getAppUserDataDirectory'
958 On Unix, 'getHomeDirectory' returns the value of the @HOME@
959 environment variable. On Windows, the system is queried for a
960 suitable path; a typical path might be
961 @C:/Documents And Settings/user@.
963 The operation may fail with:
965 * 'UnsupportedOperation'
966 The operating system has no notion of home directory.
968 * 'isDoesNotExistError'
969 The home directory for the current user does not exist, or
972 getHomeDirectory :: IO FilePath
974 #if defined(mingw32_HOST_OS)
975 allocaBytes long_path_size $ \pPath -> do
976 r0 <- c_SHGetFolderPath nullPtr csidl_PROFILE nullPtr 0 pPath
979 r1 <- c_SHGetFolderPath nullPtr csidl_WINDOWS nullPtr 0 pPath
980 when (r1 < 0) (raiseUnsupported "System.Directory.getHomeDirectory")
987 {- | Returns the pathname of a directory in which application-specific
988 data for the current user can be stored. The result of
989 'getAppUserDataDirectory' for a given application is specific to
992 The argument should be the name of the application, which will be used
993 to construct the pathname (so avoid using unusual characters that
994 might result in an invalid pathname).
996 Note: the directory may not actually exist, and may need to be created
997 first. It is expected that the parent directory exists and is
1000 On Unix, this function returns @$HOME\/.appName@. On Windows, a
1001 typical path might be
1003 > C:/Documents And Settings/user/Application Data/appName
1005 The operation may fail with:
1007 * 'UnsupportedOperation'
1008 The operating system has no notion of application-specific data directory.
1010 * 'isDoesNotExistError'
1011 The home directory for the current user does not exist, or
1014 getAppUserDataDirectory :: String -> IO FilePath
1015 getAppUserDataDirectory appName = do
1016 #if defined(mingw32_HOST_OS)
1017 allocaBytes long_path_size $ \pPath -> do
1018 r <- c_SHGetFolderPath nullPtr csidl_APPDATA nullPtr 0 pPath
1019 when (r<0) (raiseUnsupported "System.Directory.getAppUserDataDirectory")
1020 s <- peekCString pPath
1021 return (s++'\\':appName)
1023 path <- getEnv "HOME"
1024 return (path++'/':'.':appName)
1027 {- | Returns the current user's document directory.
1029 The directory returned is expected to be writable by the current user,
1030 but note that it isn't generally considered good practice to store
1031 application-specific data here; use 'getAppUserDataDirectory'
1034 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
1035 environment variable. On Windows, the system is queried for a
1036 suitable path; a typical path might be
1037 @C:\/Documents and Settings\/user\/My Documents@.
1039 The operation may fail with:
1041 * 'UnsupportedOperation'
1042 The operating system has no notion of document directory.
1044 * 'isDoesNotExistError'
1045 The document directory for the current user does not exist, or
1048 getUserDocumentsDirectory :: IO FilePath
1049 getUserDocumentsDirectory = do
1050 #if defined(mingw32_HOST_OS)
1051 allocaBytes long_path_size $ \pPath -> do
1052 r <- c_SHGetFolderPath nullPtr csidl_PERSONAL nullPtr 0 pPath
1053 when (r<0) (raiseUnsupported "System.Directory.getUserDocumentsDirectory")
1059 {- | Returns the current directory for temporary files.
1061 On Unix, 'getTemporaryDirectory' returns the value of the @TMPDIR@
1062 environment variable or \"\/tmp\" if the variable isn\'t defined.
1063 On Windows, the function checks for the existence of environment variables in
1064 the following order and uses the first path found:
1067 TMP environment variable.
1070 TEMP environment variable.
1073 USERPROFILE environment variable.
1076 The Windows directory
1078 The operation may fail with:
1080 * 'UnsupportedOperation'
1081 The operating system has no notion of temporary directory.
1083 The function doesn\'t verify whether the path exists.
1085 getTemporaryDirectory :: IO FilePath
1086 getTemporaryDirectory = do
1087 #if defined(mingw32_HOST_OS)
1088 allocaBytes long_path_size $ \pPath -> do
1089 _r <- c_GetTempPath (fromIntegral long_path_size) pPath
1094 `Prelude.catch` \e -> if isDoesNotExistError e then return "/tmp"
1097 `Prelude.catch` (\ex -> return "/tmp")
1101 #if defined(mingw32_HOST_OS)
1102 foreign import ccall unsafe "__hscore_getFolderPath"
1103 c_SHGetFolderPath :: Ptr ()
1109 foreign import ccall unsafe "__hscore_CSIDL_PROFILE" csidl_PROFILE :: CInt
1110 foreign import ccall unsafe "__hscore_CSIDL_APPDATA" csidl_APPDATA :: CInt
1111 foreign import ccall unsafe "__hscore_CSIDL_WINDOWS" csidl_WINDOWS :: CInt
1112 foreign import ccall unsafe "__hscore_CSIDL_PERSONAL" csidl_PERSONAL :: CInt
1114 foreign import stdcall unsafe "GetTempPathA" c_GetTempPath :: CInt -> CString -> IO CInt
1116 raiseUnsupported :: String -> IO ()
1117 raiseUnsupported loc =
1118 ioException (ioeSetErrorString (mkIOError UnsupportedOperation loc Nothing Nothing) "unsupported operation")
1122 -- ToDo: This should be determined via autoconf (AC_EXEEXT)
1123 -- | Extension for executable files
1124 -- (typically @\"\"@ on Unix and @\"exe\"@ on Windows or OS\/2)
1125 exeExtension :: String
1126 #ifdef mingw32_HOST_OS
1127 exeExtension = "exe"