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 ()
70 , getModificationTime -- :: FilePath -> IO ClockTime
73 import Prelude hiding ( catch )
74 import qualified Prelude
76 import Control.Monad (guard)
77 import System.Environment ( getEnv )
78 import System.FilePath
80 import System.IO.Error hiding ( catch, try )
81 import Control.Monad ( when, unless )
82 import Control.Exception.Base
85 import Directory hiding ( getDirectoryContents
86 , doesDirectoryExist, doesFileExist
87 , getModificationTime )
88 import System (system)
98 {-# CFILES cbits/directory.c #-}
100 import System.Time ( ClockTime(..) )
102 #ifdef __GLASGOW_HASKELL__
104 #if __GLASGOW_HASKELL__ >= 611
105 import GHC.IO.Exception ( IOException(..), IOErrorType(..), ioException )
107 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
110 #ifdef mingw32_HOST_OS
111 import System.Posix.Types
112 import System.Posix.Internals
113 import qualified System.Win32 as Win32
115 import qualified System.Posix as Posix
119 A directory contains a series of entries, each of which is a named
120 reference to a file system object (file, directory etc.). Some
121 entries may be hidden, inaccessible, or have some administrative
122 function (e.g. `.' or `..' under POSIX
123 <http://www.opengroup.org/onlinepubs/009695399/>), but in
124 this standard all such entries are considered to form part of the
125 directory contents. Entries in sub-directories are not, however,
126 considered to form part of the directory contents.
128 Each file system object is referenced by a /path/. There is
129 normally at least one absolute path to each file system object. In
130 some operating systems, it may also be possible to have paths which
131 are relative to the current directory.
134 -----------------------------------------------------------------------------
139 The 'Permissions' type is used to record whether certain operations are
140 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
141 get and set these permissions, respectively. Permissions apply both to
142 files and directories. For directories, the executable field will be
143 'False', and for files the searchable field will be 'False'. Note that
144 directories may be searchable without being readable, if permission has
145 been given to use them as part of a path, but not to examine the
148 Note that to change some, but not all permissions, a construct on the following lines must be used.
150 > makeReadable f = do
151 > p <- getPermissions f
152 > setPermissions f (p {readable = True})
159 executable, searchable :: Bool
160 } deriving (Eq, Ord, Read, Show)
162 {- |The 'getPermissions' operation returns the
163 permissions for the file or directory.
165 The operation may fail with:
167 * 'isPermissionError' if the user is not permitted to access
170 * 'isDoesNotExistError' if the file or directory does not exist.
174 getPermissions :: FilePath -> IO Permissions
175 getPermissions name = do
176 #ifdef mingw32_HOST_OS
177 withFilePath name $ \s -> do
178 -- stat() does a better job of guessing the permissions on Windows
179 -- than access() does. e.g. for execute permission, it looks at the
180 -- filename extension :-)
182 -- I tried for a while to do this properly, using the Windows security API,
183 -- and eventually gave up. getPermissions is a flawed API anyway. -- SimonM
184 allocaBytes sizeof_stat $ \ p_stat -> do
185 throwErrnoIfMinus1_ "getPermissions" $ c_stat s p_stat
186 mode <- st_mode p_stat
187 let usr_read = mode .&. s_IRUSR
188 let usr_write = mode .&. s_IWUSR
189 let usr_exec = mode .&. s_IXUSR
190 let is_dir = mode .&. s_IFDIR
193 readable = usr_read /= 0,
194 writable = usr_write /= 0,
195 executable = is_dir == 0 && usr_exec /= 0,
196 searchable = is_dir /= 0 && usr_exec /= 0
200 read_ok <- Posix.fileAccess name True False False
201 write_ok <- Posix.fileAccess name False True False
202 exec_ok <- Posix.fileAccess name False False True
203 stat <- Posix.getFileStatus name
204 let is_dir = Posix.fileMode stat .&. Posix.directoryMode /= 0
209 executable = not is_dir && exec_ok,
210 searchable = is_dir && exec_ok
215 {- |The 'setPermissions' operation sets the
216 permissions for the file or directory.
218 The operation may fail with:
220 * 'isPermissionError' if the user is not permitted to set
223 * 'isDoesNotExistError' if the file or directory does not exist.
227 setPermissions :: FilePath -> Permissions -> IO ()
228 setPermissions name (Permissions r w e s) = do
229 #ifdef mingw32_HOST_OS
230 allocaBytes sizeof_stat $ \ p_stat -> do
231 withFilePath name $ \p_name -> do
232 throwErrnoIfMinus1_ "setPermissions" $ do
234 mode <- st_mode p_stat
235 let mode1 = modifyBit r mode s_IRUSR
236 let mode2 = modifyBit w mode1 s_IWUSR
237 let mode3 = modifyBit (e || s) mode2 s_IXUSR
238 c_wchmod p_name mode3
240 modifyBit :: Bool -> CMode -> CMode -> CMode
241 modifyBit False m b = m .&. (complement b)
242 modifyBit True m b = m .|. b
244 stat <- Posix.getFileStatus name
245 let mode = Posix.fileMode stat
246 let mode1 = modifyBit r mode Posix.ownerReadMode
247 let mode2 = modifyBit w mode1 Posix.ownerWriteMode
248 let mode3 = modifyBit (e || s) mode2 Posix.ownerExecuteMode
249 Posix.setFileMode name mode3
251 modifyBit :: Bool -> Posix.FileMode -> Posix.FileMode -> Posix.FileMode
252 modifyBit False m b = m .&. (complement b)
253 modifyBit True m b = m .|. b
256 #ifdef mingw32_HOST_OS
257 foreign import ccall unsafe "_wchmod"
258 c_wchmod :: CWString -> CMode -> IO CInt
261 copyPermissions :: FilePath -> FilePath -> IO ()
262 copyPermissions source dest = do
263 #ifdef mingw32_HOST_OS
264 allocaBytes sizeof_stat $ \ p_stat -> do
265 withFilePath source $ \p_source -> do
266 withFilePath dest $ \p_dest -> do
267 throwErrnoIfMinus1_ "copyPermissions" $ c_stat p_source p_stat
268 mode <- st_mode p_stat
269 throwErrnoIfMinus1_ "copyPermissions" $ c_wchmod p_dest mode
271 stat <- Posix.getFileStatus source
272 let mode = Posix.fileMode stat
273 Posix.setFileMode dest mode
276 -----------------------------------------------------------------------------
279 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
280 initially empty, or as near to empty as the operating system
283 The operation may fail with:
285 * 'isPermissionError' \/ 'PermissionDenied'
286 The process has insufficient privileges to perform the operation.
289 * 'isAlreadyExistsError' \/ 'AlreadyExists'
290 The operand refers to a directory that already exists.
294 A physical I\/O error has occurred.
298 The operand is not a valid directory name.
299 @[ENAMETOOLONG, ELOOP]@
302 There is no path to the directory.
305 * 'ResourceExhausted'
306 Insufficient resources (virtual memory, process file descriptors,
307 physical disk space, etc.) are available to perform the operation.
308 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
310 * 'InappropriateType'
311 The path refers to an existing non-directory object.
316 createDirectory :: FilePath -> IO ()
317 createDirectory path = do
318 #ifdef mingw32_HOST_OS
319 Win32.createDirectory path Nothing
321 Posix.createDirectory path 0o777
324 #else /* !__GLASGOW_HASKELL__ */
326 copyPermissions :: FilePath -> FilePath -> IO ()
327 copyPermissions fromFPath toFPath
328 = getPermissions fromFPath >>= setPermissions toFPath
332 -- | @'createDirectoryIfMissing' parents dir@ creates a new directory
333 -- @dir@ if it doesn\'t exist. If the first argument is 'True'
334 -- the function will also create all parent directories if they are missing.
335 createDirectoryIfMissing :: Bool -- ^ Create its parents too?
336 -> FilePath -- ^ The path to the directory you want to make
338 createDirectoryIfMissing create_parents path0
339 | create_parents = createDirs (parents path0)
340 | otherwise = createDirs (take 1 (parents path0))
342 parents = reverse . scanl1 (</>) . splitDirectories . normalise
344 createDirs [] = return ()
345 createDirs (dir:[]) = createDir dir throw
346 createDirs (dir:dirs) =
347 createDir dir $ \_ -> do
351 createDir :: FilePath -> (IOException -> IO ()) -> IO ()
352 createDir dir notExistHandler = do
353 r <- try $ createDirectory dir
354 case (r :: Either IOException ()) of
355 Right () -> return ()
357 | isDoesNotExistError e -> notExistHandler e
358 -- createDirectory (and indeed POSIX mkdir) does not distinguish
359 -- between a dir already existing and a file already existing. So we
360 -- check for it here. Unfortunately there is a slight race condition
361 -- here, but we think it is benign. It could report an exeption in
362 -- the case that the dir did exist but another process deletes the
363 -- directory and creates a file in its place before we can check
364 -- that the directory did indeed exist.
365 | isAlreadyExistsError e -> (do
366 #ifdef mingw32_HOST_OS
367 withFileStatus "createDirectoryIfMissing" dir $ \st -> do
368 isDir <- isDirectory st
369 if isDir then return ()
372 stat <- Posix.getFileStatus dir
373 if Posix.fileMode stat .&. Posix.directoryMode /= 0
377 ) `catch` ((\_ -> return ()) :: IOException -> IO ())
378 | otherwise -> throw e
380 #if __GLASGOW_HASKELL__
381 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
382 implementation may specify additional constraints which must be
383 satisfied before a directory can be removed (e.g. the directory has to
384 be empty, or may not be in use by other processes). It is not legal
385 for an implementation to partially remove a directory unless the
386 entire directory is removed. A conformant implementation need not
387 support directory removal in all situations (e.g. removal of the root
390 The operation may fail with:
393 A physical I\/O error has occurred.
397 The operand is not a valid directory name.
398 [ENAMETOOLONG, ELOOP]
400 * 'isDoesNotExistError' \/ 'NoSuchThing'
401 The directory does not exist.
404 * 'isPermissionError' \/ 'PermissionDenied'
405 The process has insufficient privileges to perform the operation.
406 @[EROFS, EACCES, EPERM]@
408 * 'UnsatisfiedConstraints'
409 Implementation-dependent constraints are not satisfied.
410 @[EBUSY, ENOTEMPTY, EEXIST]@
412 * 'UnsupportedOperation'
413 The implementation does not support removal in this situation.
416 * 'InappropriateType'
417 The operand refers to an existing non-directory object.
422 removeDirectory :: FilePath -> IO ()
423 removeDirectory path =
424 #ifdef mingw32_HOST_OS
425 Win32.removeDirectory path
427 Posix.removeDirectory path
432 -- | @'removeDirectoryRecursive' dir@ removes an existing directory /dir/
433 -- together with its content and all subdirectories. Be careful,
434 -- if the directory contains symlinks, the function will follow them.
435 removeDirectoryRecursive :: FilePath -> IO ()
436 removeDirectoryRecursive startLoc = do
437 cont <- getDirectoryContents startLoc
438 sequence_ [rm (startLoc </> x) | x <- cont, x /= "." && x /= ".."]
439 removeDirectory startLoc
441 rm :: FilePath -> IO ()
442 rm f = do temp <- try (removeFile f)
444 Left e -> do isDir <- doesDirectoryExist f
445 -- If f is not a directory, re-throw the error
446 unless isDir $ throw (e :: SomeException)
447 removeDirectoryRecursive f
450 #if __GLASGOW_HASKELL__
451 {- |'removeFile' /file/ removes the directory entry for an existing file
452 /file/, where /file/ is not itself a directory. The
453 implementation may specify additional constraints which must be
454 satisfied before a file can be removed (e.g. the file may not be in
455 use by other processes).
457 The operation may fail with:
460 A physical I\/O error has occurred.
464 The operand is not a valid file name.
465 @[ENAMETOOLONG, ELOOP]@
467 * 'isDoesNotExistError' \/ 'NoSuchThing'
468 The file does not exist.
471 * 'isPermissionError' \/ 'PermissionDenied'
472 The process has insufficient privileges to perform the operation.
473 @[EROFS, EACCES, EPERM]@
475 * 'UnsatisfiedConstraints'
476 Implementation-dependent constraints are not satisfied.
479 * 'InappropriateType'
480 The operand refers to an existing directory.
485 removeFile :: FilePath -> IO ()
488 Win32.deleteFile path
490 Posix.removeLink path
493 {- |@'renameDirectory' old new@ changes the name of an existing
494 directory from /old/ to /new/. If the /new/ directory
495 already exists, it is atomically replaced by the /old/ directory.
496 If the /new/ directory is neither the /old/ directory nor an
497 alias of the /old/ directory, it is removed as if by
498 'removeDirectory'. A conformant implementation need not support
499 renaming directories in all situations (e.g. renaming to an existing
500 directory, or across different physical devices), but the constraints
503 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
506 The operation may fail with:
509 A physical I\/O error has occurred.
513 Either operand is not a valid directory name.
514 @[ENAMETOOLONG, ELOOP]@
516 * 'isDoesNotExistError' \/ 'NoSuchThing'
517 The original directory does not exist, or there is no path to the target.
520 * 'isPermissionError' \/ 'PermissionDenied'
521 The process has insufficient privileges to perform the operation.
522 @[EROFS, EACCES, EPERM]@
524 * 'ResourceExhausted'
525 Insufficient resources are available to perform the operation.
526 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
528 * 'UnsatisfiedConstraints'
529 Implementation-dependent constraints are not satisfied.
530 @[EBUSY, ENOTEMPTY, EEXIST]@
532 * 'UnsupportedOperation'
533 The implementation does not support renaming in this situation.
536 * 'InappropriateType'
537 Either path refers to an existing non-directory object.
542 renameDirectory :: FilePath -> FilePath -> IO ()
543 renameDirectory opath npath = do
544 -- XXX this test isn't performed atomically with the following rename
545 #ifdef mingw32_HOST_OS
546 -- ToDo: use Win32 API
547 withFileStatus "renameDirectory" opath $ \st -> do
548 is_dir <- isDirectory st
550 stat <- Posix.getFileStatus opath
551 let is_dir = Posix.fileMode stat .&. Posix.directoryMode /= 0
554 then ioException (ioeSetErrorString
555 (mkIOError InappropriateType "renameDirectory" Nothing (Just opath))
558 #ifdef mingw32_HOST_OS
559 Win32.moveFileEx opath npath Win32.mOVEFILE_REPLACE_EXISTING
561 Posix.rename opath npath
564 {- |@'renameFile' old new@ changes the name of an existing file system
565 object from /old/ to /new/. If the /new/ object already
566 exists, it is atomically replaced by the /old/ object. Neither
567 path may refer to an existing directory. A conformant implementation
568 need not support renaming files in all situations (e.g. renaming
569 across different physical devices), but the constraints must be
572 The operation may fail with:
575 A physical I\/O error has occurred.
579 Either operand is not a valid file name.
580 @[ENAMETOOLONG, ELOOP]@
582 * 'isDoesNotExistError' \/ 'NoSuchThing'
583 The original file does not exist, or there is no path to the target.
586 * 'isPermissionError' \/ 'PermissionDenied'
587 The process has insufficient privileges to perform the operation.
588 @[EROFS, EACCES, EPERM]@
590 * 'ResourceExhausted'
591 Insufficient resources are available to perform the operation.
592 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
594 * 'UnsatisfiedConstraints'
595 Implementation-dependent constraints are not satisfied.
598 * 'UnsupportedOperation'
599 The implementation does not support renaming in this situation.
602 * 'InappropriateType'
603 Either path refers to an existing directory.
604 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
608 renameFile :: FilePath -> FilePath -> IO ()
609 renameFile opath npath = do
610 -- XXX this test isn't performed atomically with the following rename
611 #ifdef mingw32_HOST_OS
612 -- ToDo: use Win32 API
613 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
614 is_dir <- isDirectory st
616 stat <- Posix.getSymbolicLinkStatus opath
617 let is_dir = Posix.fileMode stat .&. Posix.directoryMode /= 0
620 then ioException (ioeSetErrorString
621 (mkIOError InappropriateType "renameFile" Nothing (Just opath))
624 #ifdef mingw32_HOST_OS
625 Win32.moveFileEx opath npath Win32.mOVEFILE_REPLACE_EXISTING
627 Posix.rename opath npath
630 #endif /* __GLASGOW_HASKELL__ */
632 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
633 If the /new/ file already exists, it is atomically replaced by the /old/ file.
634 Neither path may refer to an existing directory. The permissions of /old/ are
635 copied to /new/, if possible.
638 copyFile :: FilePath -> FilePath -> IO ()
640 copyFile fromFPath toFPath =
641 do readFile fromFPath >>= writeFile toFPath
642 Prelude.catch (copyPermissions fromFPath toFPath)
645 copyFile fromFPath toFPath =
646 copy `Prelude.catch` (\exc -> throw $ ioeSetLocation exc "copyFile")
647 where copy = bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
648 bracketOnError openTmp cleanTmp $ \(tmpFPath, hTmp) ->
649 do allocaBytes bufferSize $ copyContents hFrom hTmp
651 ignoreIOExceptions $ copyPermissions fromFPath tmpFPath
652 renameFile tmpFPath toFPath
653 openTmp = openBinaryTempFile (takeDirectory toFPath) ".copyFile.tmp"
654 cleanTmp (tmpFPath, hTmp)
655 = do ignoreIOExceptions $ hClose hTmp
656 ignoreIOExceptions $ removeFile tmpFPath
659 copyContents hFrom hTo buffer = do
660 count <- hGetBuf hFrom buffer bufferSize
661 when (count > 0) $ do
662 hPutBuf hTo buffer count
663 copyContents hFrom hTo buffer
665 ignoreIOExceptions io = io `catch` ioExceptionIgnorer
666 ioExceptionIgnorer :: IOException -> IO ()
667 ioExceptionIgnorer _ = return ()
670 -- | Given path referring to a file or directory, returns a
671 -- canonicalized path, with the intent that two paths referring
672 -- to the same file\/directory will map to the same canonicalized
673 -- path. Note that it is impossible to guarantee that the
674 -- implication (same file\/dir \<=\> same canonicalizedPath) holds
675 -- in either direction: this function can make only a best-effort
677 canonicalizePath :: FilePath -> IO FilePath
678 canonicalizePath fpath =
679 #if defined(mingw32_HOST_OS)
680 do path <- Win32.getFullPathName fpath
682 withCString fpath $ \pInPath ->
683 allocaBytes long_path_size $ \pOutPath ->
684 do c_realpath pInPath pOutPath
685 path <- peekCString pOutPath
687 return (normalise path)
688 -- normalise does more stuff, like upper-casing the drive letter
690 #if !defined(mingw32_HOST_OS)
691 foreign import ccall unsafe "realpath"
692 c_realpath :: CString
697 -- | 'makeRelative' the current directory.
698 makeRelativeToCurrentDirectory :: FilePath -> IO FilePath
699 makeRelativeToCurrentDirectory x = do
700 cur <- getCurrentDirectory
701 return $ makeRelative cur x
703 -- | Given an executable file name, searches for such file in the
704 -- directories listed in system PATH. The returned value is the path
705 -- to the found executable or Nothing if an executable with the given
706 -- name was not found. For example (findExecutable \"ghc\") gives you
709 -- The path returned by 'findExecutable' corresponds to the
710 -- program that would be executed by 'System.Process.createProcess'
711 -- when passed the same string (as a RawCommand, not a ShellCommand).
713 -- On Windows, 'findExecutable' calls the Win32 function 'SearchPath',
714 -- which may search other places before checking the directories in
715 -- @PATH@. Where it actually searches depends on registry settings,
716 -- but notably includes the directory containing the current
718 -- <http://msdn.microsoft.com/en-us/library/aa365527.aspx> for more
721 findExecutable :: String -> IO (Maybe FilePath)
722 findExecutable binary =
723 #if defined(mingw32_HOST_OS)
724 Win32.searchPath Nothing binary ('.':exeExtension)
727 path <- getEnv "PATH"
728 search (splitSearchPath path)
730 fileName = binary <.> exeExtension
732 search :: [FilePath] -> IO (Maybe FilePath)
733 search [] = return Nothing
735 let path = d </> fileName
736 b <- doesFileExist path
737 if b then return (Just path)
743 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
746 The operation may fail with:
749 A physical I\/O error has occurred.
753 The operand is not a valid directory name.
754 @[ENAMETOOLONG, ELOOP]@
756 * 'isDoesNotExistError' \/ 'NoSuchThing'
757 The directory does not exist.
760 * 'isPermissionError' \/ 'PermissionDenied'
761 The process has insufficient privileges to perform the operation.
764 * 'ResourceExhausted'
765 Insufficient resources are available to perform the operation.
768 * 'InappropriateType'
769 The path refers to an existing non-directory object.
774 getDirectoryContents :: FilePath -> IO [FilePath]
775 getDirectoryContents path =
776 modifyIOError ((`ioeSetFileName` path) .
777 (`ioeSetLocation` "getDirectoryContents")) $ do
778 #ifndef mingw32_HOST_OS
780 (Posix.openDirStream path)
785 e <- Posix.readDirStream dirp
786 if null e then return [] else do
791 (Win32.findFirstFile (path </> "*"))
792 (\(h,_) -> Win32.findClose h)
793 (\(h,fdat) -> loop h fdat [])
795 -- we needn't worry about empty directories: adirectory always
796 -- has at least "." and ".." entries
797 loop :: Win32.HANDLE -> Win32.FindData -> [FilePath] -> IO [FilePath]
799 filename <- Win32.getFindDataFileName fdat
800 more <- Win32.findNextFile h fdat
802 then loop h fdat (filename:acc)
803 else return (filename:acc)
804 -- no need to reverse, ordering is undefined
807 #endif /* !__HUGS__ */
810 {- |If the operating system has a notion of current directories,
811 'getCurrentDirectory' returns an absolute path to the
812 current directory of the calling process.
814 The operation may fail with:
817 A physical I\/O error has occurred.
820 * 'isDoesNotExistError' \/ 'NoSuchThing'
821 There is no path referring to the current directory.
822 @[EPERM, ENOENT, ESTALE...]@
824 * 'isPermissionError' \/ 'PermissionDenied'
825 The process has insufficient privileges to perform the operation.
828 * 'ResourceExhausted'
829 Insufficient resources are available to perform the operation.
831 * 'UnsupportedOperation'
832 The operating system has no notion of current directory.
835 #ifdef __GLASGOW_HASKELL__
836 getCurrentDirectory :: IO FilePath
837 getCurrentDirectory = do
838 #ifdef mingw32_HOST_OS
839 Win32.getCurrentDirectory
841 Posix.getWorkingDirectory
844 {- |If the operating system has a notion of current directories,
845 @'setCurrentDirectory' dir@ changes the current
846 directory of the calling process to /dir/.
848 The operation may fail with:
851 A physical I\/O error has occurred.
855 The operand is not a valid directory name.
856 @[ENAMETOOLONG, ELOOP]@
858 * 'isDoesNotExistError' \/ 'NoSuchThing'
859 The directory does not exist.
862 * 'isPermissionError' \/ 'PermissionDenied'
863 The process has insufficient privileges to perform the operation.
866 * 'UnsupportedOperation'
867 The operating system has no notion of current directory, or the
868 current directory cannot be dynamically changed.
870 * 'InappropriateType'
871 The path refers to an existing non-directory object.
876 setCurrentDirectory :: FilePath -> IO ()
877 setCurrentDirectory path =
878 #ifdef mingw32_HOST_OS
879 Win32.setCurrentDirectory path
881 Posix.changeWorkingDirectory path
884 #endif /* __GLASGOW_HASKELL__ */
887 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
888 exists and is a directory, and 'False' otherwise.
891 doesDirectoryExist :: FilePath -> IO Bool
892 doesDirectoryExist name =
893 #ifdef mingw32_HOST_OS
894 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
896 (do stat <- Posix.getFileStatus name
897 return (Posix.fileMode stat .&. Posix.directoryMode /= 0))
899 `catch` ((\ _ -> return False) :: IOException -> IO Bool)
901 {- |The operation 'doesFileExist' returns 'True'
902 if the argument file exists and is not a directory, and 'False' otherwise.
905 doesFileExist :: FilePath -> IO Bool
907 #ifdef mingw32_HOST_OS
908 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
910 (do stat <- Posix.getFileStatus name
911 return (Posix.fileMode stat .&. Posix.directoryMode == 0))
913 `catch` ((\ _ -> return False) :: IOException -> IO Bool)
915 {- |The 'getModificationTime' operation returns the
916 clock time at which the file or directory was last modified.
918 The operation may fail with:
920 * 'isPermissionError' if the user is not permitted to access
921 the modification time; or
923 * 'isDoesNotExistError' if the file or directory does not exist.
927 getModificationTime :: FilePath -> IO ClockTime
928 getModificationTime name = do
929 #ifdef mingw32_HOST_OS
930 -- ToDo: use Win32 API
931 withFileStatus "getModificationTime" name $ \ st -> do
934 stat <- Posix.getFileStatus name
935 let realToInteger = round . realToFrac :: Real a => a -> Integer
936 return (TOD (realToInteger (Posix.modificationTime stat)) 0)
940 #endif /* !__HUGS__ */
942 #ifdef mingw32_HOST_OS
943 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
944 withFileStatus loc name f = do
945 modifyIOError (`ioeSetFileName` name) $
946 allocaBytes sizeof_stat $ \p ->
947 withFilePath (fileNameEndClean name) $ \s -> do
948 throwErrnoIfMinus1Retry_ loc (c_stat s p)
951 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
952 withFileOrSymlinkStatus loc name f = do
953 modifyIOError (`ioeSetFileName` name) $
954 allocaBytes sizeof_stat $ \p ->
955 withFilePath name $ \s -> do
956 throwErrnoIfMinus1Retry_ loc (lstat s p)
959 modificationTime :: Ptr CStat -> IO ClockTime
960 modificationTime stat = do
961 mtime <- st_mtime stat
962 let realToInteger = round . realToFrac :: Real a => a -> Integer
963 return (TOD (realToInteger (mtime :: CTime)) 0)
965 isDirectory :: Ptr CStat -> IO Bool
966 isDirectory stat = do
968 return (s_isdir mode)
970 fileNameEndClean :: String -> String
971 fileNameEndClean name = if isDrive name then addTrailingPathSeparator name
972 else dropTrailingPathSeparator name
974 foreign import ccall unsafe "HsDirectory.h __hscore_S_IRUSR" s_IRUSR :: CMode
975 foreign import ccall unsafe "HsDirectory.h __hscore_S_IWUSR" s_IWUSR :: CMode
976 foreign import ccall unsafe "HsDirectory.h __hscore_S_IXUSR" s_IXUSR :: CMode
977 foreign import ccall unsafe "__hscore_S_IFDIR" s_IFDIR :: CMode
981 #ifdef __GLASGOW_HASKELL__
982 foreign import ccall unsafe "__hscore_long_path_size"
983 long_path_size :: Int
985 long_path_size :: Int
986 long_path_size = 2048 -- // guess?
987 #endif /* __GLASGOW_HASKELL__ */
989 {- | Returns the current user's home directory.
991 The directory returned is expected to be writable by the current user,
992 but note that it isn't generally considered good practice to store
993 application-specific data here; use 'getAppUserDataDirectory'
996 On Unix, 'getHomeDirectory' returns the value of the @HOME@
997 environment variable. On Windows, the system is queried for a
998 suitable path; a typical path might be
999 @C:/Documents And Settings/user@.
1001 The operation may fail with:
1003 * 'UnsupportedOperation'
1004 The operating system has no notion of home directory.
1006 * 'isDoesNotExistError'
1007 The home directory for the current user does not exist, or
1010 getHomeDirectory :: IO FilePath
1012 modifyIOError ((`ioeSetLocation` "getHomeDirectory")) $ do
1013 #if defined(mingw32_HOST_OS)
1014 r <- try $ Win32.sHGetFolderPath nullPtr Win32.cSIDL_PROFILE nullPtr 0
1015 case (r :: Either IOException String) of
1018 r1 <- try $ Win32.sHGetFolderPath nullPtr Win32.cSIDL_WINDOWS nullPtr 0
1021 Left e -> ioError (e :: IOException)
1026 {- | Returns the pathname of a directory in which application-specific
1027 data for the current user can be stored. The result of
1028 'getAppUserDataDirectory' for a given application is specific to
1031 The argument should be the name of the application, which will be used
1032 to construct the pathname (so avoid using unusual characters that
1033 might result in an invalid pathname).
1035 Note: the directory may not actually exist, and may need to be created
1036 first. It is expected that the parent directory exists and is
1039 On Unix, this function returns @$HOME\/.appName@. On Windows, a
1040 typical path might be
1042 > C:/Documents And Settings/user/Application Data/appName
1044 The operation may fail with:
1046 * 'UnsupportedOperation'
1047 The operating system has no notion of application-specific data directory.
1049 * 'isDoesNotExistError'
1050 The home directory for the current user does not exist, or
1053 getAppUserDataDirectory :: String -> IO FilePath
1054 getAppUserDataDirectory appName = do
1055 modifyIOError ((`ioeSetLocation` "getAppUserDataDirectory")) $ do
1056 #if defined(mingw32_HOST_OS)
1057 s <- Win32.sHGetFolderPath nullPtr Win32.cSIDL_APPDATA nullPtr 0
1058 return (s++'\\':appName)
1060 path <- getEnv "HOME"
1061 return (path++'/':'.':appName)
1064 {- | Returns the current user's document directory.
1066 The directory returned is expected to be writable by the current user,
1067 but note that it isn't generally considered good practice to store
1068 application-specific data here; use 'getAppUserDataDirectory'
1071 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
1072 environment variable. On Windows, the system is queried for a
1073 suitable path; a typical path might be
1074 @C:\/Documents and Settings\/user\/My Documents@.
1076 The operation may fail with:
1078 * 'UnsupportedOperation'
1079 The operating system has no notion of document directory.
1081 * 'isDoesNotExistError'
1082 The document directory for the current user does not exist, or
1085 getUserDocumentsDirectory :: IO FilePath
1086 getUserDocumentsDirectory = do
1087 modifyIOError ((`ioeSetLocation` "getUserDocumentsDirectory")) $ do
1088 #if defined(mingw32_HOST_OS)
1089 Win32.sHGetFolderPath nullPtr Win32.cSIDL_PERSONAL nullPtr 0
1094 {- | Returns the current directory for temporary files.
1096 On Unix, 'getTemporaryDirectory' returns the value of the @TMPDIR@
1097 environment variable or \"\/tmp\" if the variable isn\'t defined.
1098 On Windows, the function checks for the existence of environment variables in
1099 the following order and uses the first path found:
1102 TMP environment variable.
1105 TEMP environment variable.
1108 USERPROFILE environment variable.
1111 The Windows directory
1113 The operation may fail with:
1115 * 'UnsupportedOperation'
1116 The operating system has no notion of temporary directory.
1118 The function doesn\'t verify whether the path exists.
1120 getTemporaryDirectory :: IO FilePath
1121 getTemporaryDirectory = do
1122 #if defined(mingw32_HOST_OS)
1123 Win32.getTemporaryDirectory
1127 `Prelude.catch` \e -> if isDoesNotExistError e then return "/tmp"
1130 `Prelude.catch` (\ex -> return "/tmp")
1134 -- ToDo: This should be determined via autoconf (AC_EXEEXT)
1135 -- | Extension for executable files
1136 -- (typically @\"\"@ on Unix and @\"exe\"@ on Windows or OS\/2)
1137 exeExtension :: String
1138 #ifdef mingw32_HOST_OS
1139 exeExtension = "exe"