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 ()
25 , createDirectoryIfMissing -- :: Bool -> FilePath -> IO ()
27 , removeDirectory -- :: FilePath -> IO ()
28 , removeDirectoryRecursive -- :: FilePath -> IO ()
29 , renameDirectory -- :: FilePath -> FilePath -> IO ()
31 , getDirectoryContents -- :: FilePath -> IO [FilePath]
32 , getCurrentDirectory -- :: IO FilePath
33 , setCurrentDirectory -- :: FilePath -> IO ()
35 -- * Pre-defined directories
37 , getAppUserDataDirectory
38 , getUserDocumentsDirectory
39 , getTemporaryDirectory
42 , removeFile -- :: FilePath -> IO ()
43 , renameFile -- :: FilePath -> FilePath -> IO ()
44 , copyFile -- :: FilePath -> FilePath -> IO ()
47 , makeRelativeToCurrentDirectory
51 , doesFileExist -- :: FilePath -> IO Bool
52 , doesDirectoryExist -- :: FilePath -> IO Bool
60 readable, -- :: Permissions -> Bool
61 writable, -- :: Permissions -> Bool
62 executable, -- :: Permissions -> Bool
63 searchable -- :: Permissions -> Bool
66 , getPermissions -- :: FilePath -> IO Permissions
67 , setPermissions -- :: FilePath -> Permissions -> IO ()
72 , getModificationTime -- :: FilePath -> IO ClockTime
75 import Prelude hiding ( catch )
76 import qualified Prelude
78 import Control.Monad (guard)
79 import System.Environment ( getEnv )
80 import System.FilePath
82 import System.IO.Error hiding ( catch, try )
83 import Control.Monad ( when, unless )
84 import Control.Exception.Base
87 import Directory -- hiding ( getDirectoryContents
88 -- , doesDirectoryExist, doesFileExist
89 -- , getModificationTime )
90 import System (system)
100 {-# CFILES cbits/directory.c #-}
102 import System.Time ( ClockTime(..) )
104 #ifdef __GLASGOW_HASKELL__
106 #if __GLASGOW_HASKELL__ >= 611
107 import GHC.IO.Exception ( IOException(..), IOErrorType(..), ioException )
109 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
112 #ifdef mingw32_HOST_OS
113 import System.Posix.Types
114 import System.Posix.Internals
115 import qualified System.Win32 as Win32
117 import qualified System.Posix as Posix
121 A directory contains a series of entries, each of which is a named
122 reference to a file system object (file, directory etc.). Some
123 entries may be hidden, inaccessible, or have some administrative
124 function (e.g. `.' or `..' under POSIX
125 <http://www.opengroup.org/onlinepubs/009695399/>), but in
126 this standard all such entries are considered to form part of the
127 directory contents. Entries in sub-directories are not, however,
128 considered to form part of the directory contents.
130 Each file system object is referenced by a /path/. There is
131 normally at least one absolute path to each file system object. In
132 some operating systems, it may also be possible to have paths which
133 are relative to the current directory.
136 -----------------------------------------------------------------------------
141 The 'Permissions' type is used to record whether certain operations are
142 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
143 get and set these permissions, respectively. Permissions apply both to
144 files and directories. For directories, the executable field will be
145 'False', and for files the searchable field will be 'False'. Note that
146 directories may be searchable without being readable, if permission has
147 been given to use them as part of a path, but not to examine the
150 Note that to change some, but not all permissions, a construct on the following lines must be used.
152 > makeReadable f = do
153 > p <- getPermissions f
154 > setPermissions f (p {readable = True})
161 executable, searchable :: Bool
162 } deriving (Eq, Ord, Read, Show)
164 {- |The 'getPermissions' operation returns the
165 permissions for the file or directory.
167 The operation may fail with:
169 * 'isPermissionError' if the user is not permitted to access
172 * 'isDoesNotExistError' if the file or directory does not exist.
176 getPermissions :: FilePath -> IO Permissions
177 getPermissions name = do
178 #ifdef mingw32_HOST_OS
179 withFilePath name $ \s -> do
180 -- stat() does a better job of guessing the permissions on Windows
181 -- than access() does. e.g. for execute permission, it looks at the
182 -- filename extension :-)
184 -- I tried for a while to do this properly, using the Windows security API,
185 -- and eventually gave up. getPermissions is a flawed API anyway. -- SimonM
186 allocaBytes sizeof_stat $ \ p_stat -> do
187 throwErrnoIfMinus1_ "getPermissions" $ c_stat s p_stat
188 mode <- st_mode p_stat
189 let usr_read = mode .&. s_IRUSR
190 let usr_write = mode .&. s_IWUSR
191 let usr_exec = mode .&. s_IXUSR
192 let is_dir = mode .&. s_IFDIR
195 readable = usr_read /= 0,
196 writable = usr_write /= 0,
197 executable = is_dir == 0 && usr_exec /= 0,
198 searchable = is_dir /= 0 && usr_exec /= 0
202 read_ok <- Posix.fileAccess name True False False
203 write_ok <- Posix.fileAccess name False True False
204 exec_ok <- Posix.fileAccess name False False True
205 stat <- Posix.getFileStatus name
206 let is_dir = Posix.isDirectory stat
211 executable = not is_dir && exec_ok,
212 searchable = is_dir && exec_ok
217 {- |The 'setPermissions' operation sets the
218 permissions for the file or directory.
220 The operation may fail with:
222 * 'isPermissionError' if the user is not permitted to set
225 * 'isDoesNotExistError' if the file or directory does not exist.
229 setPermissions :: FilePath -> Permissions -> IO ()
230 setPermissions name (Permissions r w e s) = do
231 #ifdef mingw32_HOST_OS
232 allocaBytes sizeof_stat $ \ p_stat -> do
233 withFilePath name $ \p_name -> do
234 throwErrnoIfMinus1_ "setPermissions" $ do
236 mode <- st_mode p_stat
237 let mode1 = modifyBit r mode s_IRUSR
238 let mode2 = modifyBit w mode1 s_IWUSR
239 let mode3 = modifyBit (e || s) mode2 s_IXUSR
240 c_wchmod p_name mode3
242 modifyBit :: Bool -> CMode -> CMode -> CMode
243 modifyBit False m b = m .&. (complement b)
244 modifyBit True m b = m .|. b
246 stat <- Posix.getFileStatus name
247 let mode = Posix.fileMode stat
248 let mode1 = modifyBit r mode Posix.ownerReadMode
249 let mode2 = modifyBit w mode1 Posix.ownerWriteMode
250 let mode3 = modifyBit (e || s) mode2 Posix.ownerExecuteMode
251 Posix.setFileMode name mode3
253 modifyBit :: Bool -> Posix.FileMode -> Posix.FileMode -> Posix.FileMode
254 modifyBit False m b = m .&. (complement b)
255 modifyBit True m b = m .|. b
258 #ifdef mingw32_HOST_OS
259 foreign import ccall unsafe "_wchmod"
260 c_wchmod :: CWString -> CMode -> IO CInt
263 copyPermissions :: FilePath -> FilePath -> IO ()
264 copyPermissions source dest = do
265 #ifdef mingw32_HOST_OS
266 allocaBytes sizeof_stat $ \ p_stat -> do
267 withFilePath source $ \p_source -> do
268 withFilePath dest $ \p_dest -> do
269 throwErrnoIfMinus1_ "copyPermissions" $ c_stat p_source p_stat
270 mode <- st_mode p_stat
271 throwErrnoIfMinus1_ "copyPermissions" $ c_wchmod p_dest mode
273 stat <- Posix.getFileStatus source
274 let mode = Posix.fileMode stat
275 Posix.setFileMode dest mode
278 -----------------------------------------------------------------------------
281 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
282 initially empty, or as near to empty as the operating system
285 The operation may fail with:
287 * 'isPermissionError' \/ 'PermissionDenied'
288 The process has insufficient privileges to perform the operation.
291 * 'isAlreadyExistsError' \/ 'AlreadyExists'
292 The operand refers to a directory that already exists.
296 A physical I\/O error has occurred.
300 The operand is not a valid directory name.
301 @[ENAMETOOLONG, ELOOP]@
304 There is no path to the directory.
307 * 'ResourceExhausted'
308 Insufficient resources (virtual memory, process file descriptors,
309 physical disk space, etc.) are available to perform the operation.
310 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
312 * 'InappropriateType'
313 The path refers to an existing non-directory object.
318 createDirectory :: FilePath -> IO ()
319 createDirectory path = do
320 #ifdef mingw32_HOST_OS
321 Win32.createDirectory path Nothing
323 Posix.createDirectory path 0o777
326 #else /* !__GLASGOW_HASKELL__ */
328 copyPermissions :: FilePath -> FilePath -> IO ()
329 copyPermissions fromFPath toFPath
330 = getPermissions fromFPath >>= setPermissions toFPath
335 -- | @'createDirectoryIfMissing' parents dir@ creates a new directory
336 -- @dir@ if it doesn\'t exist. If the first argument is 'True'
337 -- the function will also create all parent directories if they are missing.
338 createDirectoryIfMissing :: Bool -- ^ Create its parents too?
339 -> FilePath -- ^ The path to the directory you want to make
341 createDirectoryIfMissing create_parents path0
342 | create_parents = createDirs (parents path0)
343 | otherwise = createDirs (take 1 (parents path0))
345 parents = reverse . scanl1 (</>) . splitDirectories . normalise
347 createDirs [] = return ()
348 createDirs (dir:[]) = createDir dir throw
349 createDirs (dir:dirs) =
350 createDir dir $ \_ -> do
354 createDir :: FilePath -> (IOException -> IO ()) -> IO ()
355 createDir dir notExistHandler = do
356 r <- try $ createDirectory dir
357 case (r :: Either IOException ()) of
358 Right () -> return ()
360 | isDoesNotExistError e -> notExistHandler e
361 -- createDirectory (and indeed POSIX mkdir) does not distinguish
362 -- between a dir already existing and a file already existing. So we
363 -- check for it here. Unfortunately there is a slight race condition
364 -- here, but we think it is benign. It could report an exeption in
365 -- the case that the dir did exist but another process deletes the
366 -- directory and creates a file in its place before we can check
367 -- that the directory did indeed exist.
368 | isAlreadyExistsError e -> (do
369 #ifdef mingw32_HOST_OS
370 withFileStatus "createDirectoryIfMissing" dir $ \st -> do
371 isDir <- isDirectory st
372 if isDir then return ()
375 stat <- Posix.getFileStatus dir
376 if Posix.isDirectory stat
380 ) `catch` ((\_ -> return ()) :: IOException -> IO ())
381 | otherwise -> throw e
382 #endif /* !__NHC__ */
384 #if __GLASGOW_HASKELL__
385 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
386 implementation may specify additional constraints which must be
387 satisfied before a directory can be removed (e.g. the directory has to
388 be empty, or may not be in use by other processes). It is not legal
389 for an implementation to partially remove a directory unless the
390 entire directory is removed. A conformant implementation need not
391 support directory removal in all situations (e.g. removal of the root
394 The operation may fail with:
397 A physical I\/O error has occurred.
401 The operand is not a valid directory name.
402 [ENAMETOOLONG, ELOOP]
404 * 'isDoesNotExistError' \/ 'NoSuchThing'
405 The directory does not exist.
408 * 'isPermissionError' \/ 'PermissionDenied'
409 The process has insufficient privileges to perform the operation.
410 @[EROFS, EACCES, EPERM]@
412 * 'UnsatisfiedConstraints'
413 Implementation-dependent constraints are not satisfied.
414 @[EBUSY, ENOTEMPTY, EEXIST]@
416 * 'UnsupportedOperation'
417 The implementation does not support removal in this situation.
420 * 'InappropriateType'
421 The operand refers to an existing non-directory object.
426 removeDirectory :: FilePath -> IO ()
427 removeDirectory path =
428 #ifdef mingw32_HOST_OS
429 Win32.removeDirectory path
431 Posix.removeDirectory path
436 -- | @'removeDirectoryRecursive' dir@ removes an existing directory /dir/
437 -- together with its content and all subdirectories. Be careful,
438 -- if the directory contains symlinks, the function will follow them.
439 removeDirectoryRecursive :: FilePath -> IO ()
440 removeDirectoryRecursive startLoc = do
441 cont <- getDirectoryContents startLoc
442 sequence_ [rm (startLoc </> x) | x <- cont, x /= "." && x /= ".."]
443 removeDirectory startLoc
445 rm :: FilePath -> IO ()
446 rm f = do temp <- try (removeFile f)
448 Left e -> do isDir <- doesDirectoryExist f
449 -- If f is not a directory, re-throw the error
450 unless isDir $ throw (e :: SomeException)
451 removeDirectoryRecursive f
454 #if __GLASGOW_HASKELL__
455 {- |'removeFile' /file/ removes the directory entry for an existing file
456 /file/, where /file/ is not itself a directory. The
457 implementation may specify additional constraints which must be
458 satisfied before a file can be removed (e.g. the file may not be in
459 use by other processes).
461 The operation may fail with:
464 A physical I\/O error has occurred.
468 The operand is not a valid file name.
469 @[ENAMETOOLONG, ELOOP]@
471 * 'isDoesNotExistError' \/ 'NoSuchThing'
472 The file does not exist.
475 * 'isPermissionError' \/ 'PermissionDenied'
476 The process has insufficient privileges to perform the operation.
477 @[EROFS, EACCES, EPERM]@
479 * 'UnsatisfiedConstraints'
480 Implementation-dependent constraints are not satisfied.
483 * 'InappropriateType'
484 The operand refers to an existing directory.
489 removeFile :: FilePath -> IO ()
492 Win32.deleteFile path
494 Posix.removeLink path
497 {- |@'renameDirectory' old new@ changes the name of an existing
498 directory from /old/ to /new/. If the /new/ directory
499 already exists, it is atomically replaced by the /old/ directory.
500 If the /new/ directory is neither the /old/ directory nor an
501 alias of the /old/ directory, it is removed as if by
502 'removeDirectory'. A conformant implementation need not support
503 renaming directories in all situations (e.g. renaming to an existing
504 directory, or across different physical devices), but the constraints
507 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
510 The operation may fail with:
513 A physical I\/O error has occurred.
517 Either operand is not a valid directory name.
518 @[ENAMETOOLONG, ELOOP]@
520 * 'isDoesNotExistError' \/ 'NoSuchThing'
521 The original directory does not exist, or there is no path to the target.
524 * 'isPermissionError' \/ 'PermissionDenied'
525 The process has insufficient privileges to perform the operation.
526 @[EROFS, EACCES, EPERM]@
528 * 'ResourceExhausted'
529 Insufficient resources are available to perform the operation.
530 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
532 * 'UnsatisfiedConstraints'
533 Implementation-dependent constraints are not satisfied.
534 @[EBUSY, ENOTEMPTY, EEXIST]@
536 * 'UnsupportedOperation'
537 The implementation does not support renaming in this situation.
540 * 'InappropriateType'
541 Either path refers to an existing non-directory object.
546 renameDirectory :: FilePath -> FilePath -> IO ()
547 renameDirectory opath npath = do
548 -- XXX this test isn't performed atomically with the following rename
549 #ifdef mingw32_HOST_OS
550 -- ToDo: use Win32 API
551 withFileStatus "renameDirectory" opath $ \st -> do
552 is_dir <- isDirectory st
554 stat <- Posix.getFileStatus opath
555 let is_dir = Posix.fileMode stat .&. Posix.directoryMode /= 0
558 then ioException (ioeSetErrorString
559 (mkIOError InappropriateType "renameDirectory" Nothing (Just opath))
562 #ifdef mingw32_HOST_OS
563 Win32.moveFileEx opath npath Win32.mOVEFILE_REPLACE_EXISTING
565 Posix.rename opath npath
568 {- |@'renameFile' old new@ changes the name of an existing file system
569 object from /old/ to /new/. If the /new/ object already
570 exists, it is atomically replaced by the /old/ object. Neither
571 path may refer to an existing directory. A conformant implementation
572 need not support renaming files in all situations (e.g. renaming
573 across different physical devices), but the constraints must be
576 The operation may fail with:
579 A physical I\/O error has occurred.
583 Either operand is not a valid file name.
584 @[ENAMETOOLONG, ELOOP]@
586 * 'isDoesNotExistError' \/ 'NoSuchThing'
587 The original file does not exist, or there is no path to the target.
590 * 'isPermissionError' \/ 'PermissionDenied'
591 The process has insufficient privileges to perform the operation.
592 @[EROFS, EACCES, EPERM]@
594 * 'ResourceExhausted'
595 Insufficient resources are available to perform the operation.
596 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
598 * 'UnsatisfiedConstraints'
599 Implementation-dependent constraints are not satisfied.
602 * 'UnsupportedOperation'
603 The implementation does not support renaming in this situation.
606 * 'InappropriateType'
607 Either path refers to an existing directory.
608 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
612 renameFile :: FilePath -> FilePath -> IO ()
613 renameFile opath npath = do
614 -- XXX this test isn't performed atomically with the following rename
615 #ifdef mingw32_HOST_OS
616 -- ToDo: use Win32 API
617 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
618 is_dir <- isDirectory st
620 stat <- Posix.getSymbolicLinkStatus opath
621 let is_dir = Posix.isDirectory stat
624 then ioException (ioeSetErrorString
625 (mkIOError InappropriateType "renameFile" Nothing (Just opath))
628 #ifdef mingw32_HOST_OS
629 Win32.moveFileEx opath npath Win32.mOVEFILE_REPLACE_EXISTING
631 Posix.rename opath npath
634 #endif /* __GLASGOW_HASKELL__ */
636 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
637 If the /new/ file already exists, it is atomically replaced by the /old/ file.
638 Neither path may refer to an existing directory. The permissions of /old/ are
639 copied to /new/, if possible.
642 copyFile :: FilePath -> FilePath -> IO ()
644 copyFile fromFPath toFPath =
645 do readFile fromFPath >>= writeFile toFPath
646 Prelude.catch (copyPermissions fromFPath toFPath)
649 copyFile fromFPath toFPath =
650 copy `Prelude.catch` (\exc -> throw $ ioeSetLocation exc "copyFile")
651 where copy = bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
652 bracketOnError openTmp cleanTmp $ \(tmpFPath, hTmp) ->
653 do allocaBytes bufferSize $ copyContents hFrom hTmp
655 ignoreIOExceptions $ copyPermissions fromFPath tmpFPath
656 renameFile tmpFPath toFPath
657 openTmp = openBinaryTempFile (takeDirectory toFPath) ".copyFile.tmp"
658 cleanTmp (tmpFPath, hTmp)
659 = do ignoreIOExceptions $ hClose hTmp
660 ignoreIOExceptions $ removeFile tmpFPath
663 copyContents hFrom hTo buffer = do
664 count <- hGetBuf hFrom buffer bufferSize
665 when (count > 0) $ do
666 hPutBuf hTo buffer count
667 copyContents hFrom hTo buffer
669 ignoreIOExceptions io = io `catch` ioExceptionIgnorer
670 ioExceptionIgnorer :: IOException -> IO ()
671 ioExceptionIgnorer _ = return ()
674 -- | Given path referring to a file or directory, returns a
675 -- canonicalized path, with the intent that two paths referring
676 -- to the same file\/directory will map to the same canonicalized
677 -- path. Note that it is impossible to guarantee that the
678 -- implication (same file\/dir \<=\> same canonicalizedPath) holds
679 -- in either direction: this function can make only a best-effort
681 canonicalizePath :: FilePath -> IO FilePath
682 canonicalizePath fpath =
683 #if defined(mingw32_HOST_OS)
684 do path <- Win32.getFullPathName fpath
686 withCString fpath $ \pInPath ->
687 allocaBytes long_path_size $ \pOutPath ->
688 do throwErrnoPathIfNull "canonicalizePath" fpath $ c_realpath pInPath pOutPath
689 path <- peekCString pOutPath
691 return (normalise path)
692 -- normalise does more stuff, like upper-casing the drive letter
694 #if !defined(mingw32_HOST_OS)
695 foreign import ccall unsafe "realpath"
696 c_realpath :: CString
701 -- | 'makeRelative' the current directory.
702 makeRelativeToCurrentDirectory :: FilePath -> IO FilePath
703 makeRelativeToCurrentDirectory x = do
704 cur <- getCurrentDirectory
705 return $ makeRelative cur x
707 -- | Given an executable file name, searches for such file in the
708 -- directories listed in system PATH. The returned value is the path
709 -- to the found executable or Nothing if an executable with the given
710 -- name was not found. For example (findExecutable \"ghc\") gives you
713 -- The path returned by 'findExecutable' corresponds to the
714 -- program that would be executed by 'System.Process.createProcess'
715 -- when passed the same string (as a RawCommand, not a ShellCommand).
717 -- On Windows, 'findExecutable' calls the Win32 function 'SearchPath',
718 -- which may search other places before checking the directories in
719 -- @PATH@. Where it actually searches depends on registry settings,
720 -- but notably includes the directory containing the current
722 -- <http://msdn.microsoft.com/en-us/library/aa365527.aspx> for more
725 findExecutable :: String -> IO (Maybe FilePath)
726 findExecutable binary =
727 #if defined(mingw32_HOST_OS)
728 Win32.searchPath Nothing binary ('.':exeExtension)
731 path <- getEnv "PATH"
732 search (splitSearchPath path)
734 fileName = binary <.> exeExtension
736 search :: [FilePath] -> IO (Maybe FilePath)
737 search [] = return Nothing
739 let path = d </> fileName
740 b <- doesFileExist path
741 if b then return (Just path)
746 #ifdef __GLASGOW_HASKELL__
747 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
750 The operation may fail with:
753 A physical I\/O error has occurred.
757 The operand is not a valid directory name.
758 @[ENAMETOOLONG, ELOOP]@
760 * 'isDoesNotExistError' \/ 'NoSuchThing'
761 The directory does not exist.
764 * 'isPermissionError' \/ 'PermissionDenied'
765 The process has insufficient privileges to perform the operation.
768 * 'ResourceExhausted'
769 Insufficient resources are available to perform the operation.
772 * 'InappropriateType'
773 The path refers to an existing non-directory object.
778 getDirectoryContents :: FilePath -> IO [FilePath]
779 getDirectoryContents path =
780 modifyIOError ((`ioeSetFileName` path) .
781 (`ioeSetLocation` "getDirectoryContents")) $ do
782 #ifndef mingw32_HOST_OS
784 (Posix.openDirStream path)
789 e <- Posix.readDirStream dirp
790 if null e then return [] else do
795 (Win32.findFirstFile (path </> "*"))
796 (\(h,_) -> Win32.findClose h)
797 (\(h,fdat) -> loop h fdat [])
799 -- we needn't worry about empty directories: adirectory always
800 -- has at least "." and ".." entries
801 loop :: Win32.HANDLE -> Win32.FindData -> [FilePath] -> IO [FilePath]
803 filename <- Win32.getFindDataFileName fdat
804 more <- Win32.findNextFile h fdat
806 then loop h fdat (filename:acc)
807 else return (filename:acc)
808 -- no need to reverse, ordering is undefined
811 #endif /* __GLASGOW_HASKELL__ */
814 {- |If the operating system has a notion of current directories,
815 'getCurrentDirectory' returns an absolute path to the
816 current directory of the calling process.
818 The operation may fail with:
821 A physical I\/O error has occurred.
824 * 'isDoesNotExistError' \/ 'NoSuchThing'
825 There is no path referring to the current directory.
826 @[EPERM, ENOENT, ESTALE...]@
828 * 'isPermissionError' \/ 'PermissionDenied'
829 The process has insufficient privileges to perform the operation.
832 * 'ResourceExhausted'
833 Insufficient resources are available to perform the operation.
835 * 'UnsupportedOperation'
836 The operating system has no notion of current directory.
839 #ifdef __GLASGOW_HASKELL__
840 getCurrentDirectory :: IO FilePath
841 getCurrentDirectory = do
842 #ifdef mingw32_HOST_OS
843 Win32.getCurrentDirectory
845 Posix.getWorkingDirectory
848 {- |If the operating system has a notion of current directories,
849 @'setCurrentDirectory' dir@ changes the current
850 directory of the calling process to /dir/.
852 The operation may fail with:
855 A physical I\/O error has occurred.
859 The operand is not a valid directory name.
860 @[ENAMETOOLONG, ELOOP]@
862 * 'isDoesNotExistError' \/ 'NoSuchThing'
863 The directory does not exist.
866 * 'isPermissionError' \/ 'PermissionDenied'
867 The process has insufficient privileges to perform the operation.
870 * 'UnsupportedOperation'
871 The operating system has no notion of current directory, or the
872 current directory cannot be dynamically changed.
874 * 'InappropriateType'
875 The path refers to an existing non-directory object.
880 setCurrentDirectory :: FilePath -> IO ()
881 setCurrentDirectory path =
882 #ifdef mingw32_HOST_OS
883 Win32.setCurrentDirectory path
885 Posix.changeWorkingDirectory path
888 #endif /* __GLASGOW_HASKELL__ */
890 #ifdef __GLASGOW_HASKELL__
891 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
892 exists and is a directory, and 'False' otherwise.
895 doesDirectoryExist :: FilePath -> IO Bool
896 doesDirectoryExist name =
897 #ifdef mingw32_HOST_OS
898 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
900 (do stat <- Posix.getFileStatus name
901 return (Posix.isDirectory stat))
903 `catch` ((\ _ -> return False) :: IOException -> IO Bool)
905 {- |The operation 'doesFileExist' returns 'True'
906 if the argument file exists and is not a directory, and 'False' otherwise.
909 doesFileExist :: FilePath -> IO Bool
911 #ifdef mingw32_HOST_OS
912 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
914 (do stat <- Posix.getFileStatus name
915 return (not (Posix.isDirectory stat)))
917 `catch` ((\ _ -> return False) :: IOException -> IO Bool)
919 {- |The 'getModificationTime' operation returns the
920 clock time at which the file or directory was last modified.
922 The operation may fail with:
924 * 'isPermissionError' if the user is not permitted to access
925 the modification time; or
927 * 'isDoesNotExistError' if the file or directory does not exist.
931 getModificationTime :: FilePath -> IO ClockTime
932 getModificationTime name = do
933 #ifdef mingw32_HOST_OS
934 -- ToDo: use Win32 API
935 withFileStatus "getModificationTime" name $ \ st -> do
938 stat <- Posix.getFileStatus name
939 let mod_time :: Posix.EpochTime
940 mod_time = Posix.modificationTime stat
942 dbl_time = realToFrac mod_time
943 return (TOD (round dbl_time) 0)
946 -- round :: (RealFrac a, Integral b => a -> b
947 -- realToFrac :: (Real a, Fractional b) => a -> b
949 #endif /* __GLASGOW_HASKELL__ */
951 #ifdef mingw32_HOST_OS
952 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
953 withFileStatus loc name f = do
954 modifyIOError (`ioeSetFileName` name) $
955 allocaBytes sizeof_stat $ \p ->
956 withFilePath (fileNameEndClean name) $ \s -> do
957 throwErrnoIfMinus1Retry_ loc (c_stat s p)
960 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
961 withFileOrSymlinkStatus loc name f = do
962 modifyIOError (`ioeSetFileName` name) $
963 allocaBytes sizeof_stat $ \p ->
964 withFilePath name $ \s -> do
965 throwErrnoIfMinus1Retry_ loc (lstat s p)
968 modificationTime :: Ptr CStat -> IO ClockTime
969 modificationTime stat = do
970 mtime <- st_mtime stat
971 let dbl_time :: Double
972 dbl_time = realToFrac (mtime :: CTime)
973 return (TOD (round dbl_time) 0)
975 isDirectory :: Ptr CStat -> IO Bool
976 isDirectory stat = do
978 return (s_isdir mode)
980 fileNameEndClean :: String -> String
981 fileNameEndClean name = if isDrive name then addTrailingPathSeparator name
982 else dropTrailingPathSeparator name
984 foreign import ccall unsafe "HsDirectory.h __hscore_S_IRUSR" s_IRUSR :: CMode
985 foreign import ccall unsafe "HsDirectory.h __hscore_S_IWUSR" s_IWUSR :: CMode
986 foreign import ccall unsafe "HsDirectory.h __hscore_S_IXUSR" s_IXUSR :: CMode
987 foreign import ccall unsafe "__hscore_S_IFDIR" s_IFDIR :: CMode
991 #ifdef __GLASGOW_HASKELL__
992 foreign import ccall unsafe "__hscore_long_path_size"
993 long_path_size :: Int
995 long_path_size :: Int
996 long_path_size = 2048 -- // guess?
997 #endif /* __GLASGOW_HASKELL__ */
999 {- | Returns the current user's home directory.
1001 The directory returned is expected to be writable by the current user,
1002 but note that it isn't generally considered good practice to store
1003 application-specific data here; use 'getAppUserDataDirectory'
1006 On Unix, 'getHomeDirectory' returns the value of the @HOME@
1007 environment variable. On Windows, the system is queried for a
1008 suitable path; a typical path might be
1009 @C:/Documents And Settings/user@.
1011 The operation may fail with:
1013 * 'UnsupportedOperation'
1014 The operating system has no notion of home directory.
1016 * 'isDoesNotExistError'
1017 The home directory for the current user does not exist, or
1020 getHomeDirectory :: IO FilePath
1022 modifyIOError ((`ioeSetLocation` "getHomeDirectory")) $ do
1023 #if defined(mingw32_HOST_OS)
1024 r <- try $ Win32.sHGetFolderPath nullPtr Win32.cSIDL_PROFILE nullPtr 0
1025 case (r :: Either IOException String) of
1028 r1 <- try $ Win32.sHGetFolderPath nullPtr Win32.cSIDL_WINDOWS nullPtr 0
1031 Left e -> ioError (e :: IOException)
1036 {- | Returns the pathname of a directory in which application-specific
1037 data for the current user can be stored. The result of
1038 'getAppUserDataDirectory' for a given application is specific to
1041 The argument should be the name of the application, which will be used
1042 to construct the pathname (so avoid using unusual characters that
1043 might result in an invalid pathname).
1045 Note: the directory may not actually exist, and may need to be created
1046 first. It is expected that the parent directory exists and is
1049 On Unix, this function returns @$HOME\/.appName@. On Windows, a
1050 typical path might be
1052 > C:/Documents And Settings/user/Application Data/appName
1054 The operation may fail with:
1056 * 'UnsupportedOperation'
1057 The operating system has no notion of application-specific data directory.
1059 * 'isDoesNotExistError'
1060 The home directory for the current user does not exist, or
1063 getAppUserDataDirectory :: String -> IO FilePath
1064 getAppUserDataDirectory appName = do
1065 modifyIOError ((`ioeSetLocation` "getAppUserDataDirectory")) $ do
1066 #if defined(mingw32_HOST_OS)
1067 s <- Win32.sHGetFolderPath nullPtr Win32.cSIDL_APPDATA nullPtr 0
1068 return (s++'\\':appName)
1070 path <- getEnv "HOME"
1071 return (path++'/':'.':appName)
1074 {- | Returns the current user's document directory.
1076 The directory returned is expected to be writable by the current user,
1077 but note that it isn't generally considered good practice to store
1078 application-specific data here; use 'getAppUserDataDirectory'
1081 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
1082 environment variable. On Windows, the system is queried for a
1083 suitable path; a typical path might be
1084 @C:\/Documents and Settings\/user\/My Documents@.
1086 The operation may fail with:
1088 * 'UnsupportedOperation'
1089 The operating system has no notion of document directory.
1091 * 'isDoesNotExistError'
1092 The document directory for the current user does not exist, or
1095 getUserDocumentsDirectory :: IO FilePath
1096 getUserDocumentsDirectory = do
1097 modifyIOError ((`ioeSetLocation` "getUserDocumentsDirectory")) $ do
1098 #if defined(mingw32_HOST_OS)
1099 Win32.sHGetFolderPath nullPtr Win32.cSIDL_PERSONAL nullPtr 0
1104 {- | Returns the current directory for temporary files.
1106 On Unix, 'getTemporaryDirectory' returns the value of the @TMPDIR@
1107 environment variable or \"\/tmp\" if the variable isn\'t defined.
1108 On Windows, the function checks for the existence of environment variables in
1109 the following order and uses the first path found:
1112 TMP environment variable.
1115 TEMP environment variable.
1118 USERPROFILE environment variable.
1121 The Windows directory
1123 The operation may fail with:
1125 * 'UnsupportedOperation'
1126 The operating system has no notion of temporary directory.
1128 The function doesn\'t verify whether the path exists.
1130 getTemporaryDirectory :: IO FilePath
1131 getTemporaryDirectory = do
1132 #if defined(mingw32_HOST_OS)
1133 Win32.getTemporaryDirectory
1137 `Prelude.catch` \e -> if isDoesNotExistError e then return "/tmp"
1140 `Prelude.catch` (\ex -> return "/tmp")
1144 -- ToDo: This should be determined via autoconf (AC_EXEEXT)
1145 -- | Extension for executable files
1146 -- (typically @\"\"@ on Unix and @\"exe\"@ on Windows or OS\/2)
1147 exeExtension :: String
1148 #ifdef mingw32_HOST_OS
1149 exeExtension = "exe"