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
69 , getPermissions -- :: FilePath -> IO Permissions
70 , setPermissions -- :: FilePath -> Permissions -> IO ()
75 , getModificationTime -- :: FilePath -> IO ClockTime
78 import Prelude hiding ( catch )
79 import qualified Prelude
81 import Control.Monad (guard)
82 import System.Environment ( getEnv )
83 import System.FilePath
85 import System.IO.Error hiding ( catch, try )
86 import Control.Monad ( when, unless )
87 import Control.Exception.Base
90 import Directory -- hiding ( getDirectoryContents
91 -- , doesDirectoryExist, doesFileExist
92 -- , getModificationTime )
93 import System (system)
103 {-# CFILES cbits/directory.c #-}
105 import System.Time ( ClockTime(..) )
107 #ifdef __GLASGOW_HASKELL__
109 #if __GLASGOW_HASKELL__ >= 611
110 import GHC.IO.Exception ( IOException(..), IOErrorType(..), ioException )
112 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
115 #ifdef mingw32_HOST_OS
116 import System.Posix.Types
117 import System.Posix.Internals
118 import qualified System.Win32 as Win32
120 import qualified System.Posix as Posix
123 #endif /* __GLASGOW_HASKELL__ */
126 A directory contains a series of entries, each of which is a named
127 reference to a file system object (file, directory etc.). Some
128 entries may be hidden, inaccessible, or have some administrative
129 function (e.g. `.' or `..' under POSIX
130 <http://www.opengroup.org/onlinepubs/009695399/>), but in
131 this standard all such entries are considered to form part of the
132 directory contents. Entries in sub-directories are not, however,
133 considered to form part of the directory contents.
135 Each file system object is referenced by a /path/. There is
136 normally at least one absolute path to each file system object. In
137 some operating systems, it may also be possible to have paths which
138 are relative to the current directory.
141 -----------------------------------------------------------------------------
146 The 'Permissions' type is used to record whether certain operations are
147 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
148 get and set these permissions, respectively. Permissions apply both to
149 files and directories. For directories, the executable field will be
150 'False', and for files the searchable field will be 'False'. Note that
151 directories may be searchable without being readable, if permission has
152 been given to use them as part of a path, but not to examine the
155 Note that to change some, but not all permissions, a construct on the following lines must be used.
157 > makeReadable f = do
158 > p <- getPermissions f
159 > setPermissions f (p {readable = True})
166 executable, searchable :: Bool
167 } deriving (Eq, Ord, Read, Show)
169 emptyPermissions :: Permissions
170 emptyPermissions = Permissions {
177 setOwnerReadable :: Bool -> Permissions -> Permissions
178 setOwnerReadable b p = p { readable = b }
180 setOwnerWritable :: Bool -> Permissions -> Permissions
181 setOwnerWritable b p = p { writable = b }
183 setOwnerExecutable :: Bool -> Permissions -> Permissions
184 setOwnerExecutable b p = p { executable = b }
186 setOwnerSearchable :: Bool -> Permissions -> Permissions
187 setOwnerSearchable b p = p { searchable = b }
189 {- |The 'getPermissions' operation returns the
190 permissions for the file or directory.
192 The operation may fail with:
194 * 'isPermissionError' if the user is not permitted to access
197 * 'isDoesNotExistError' if the file or directory does not exist.
201 #ifdef __GLASGOW_HASKELL__
203 getPermissions :: FilePath -> IO Permissions
204 getPermissions name = do
205 #ifdef mingw32_HOST_OS
206 withFilePath name $ \s -> do
207 -- stat() does a better job of guessing the permissions on Windows
208 -- than access() does. e.g. for execute permission, it looks at the
209 -- filename extension :-)
211 -- I tried for a while to do this properly, using the Windows security API,
212 -- and eventually gave up. getPermissions is a flawed API anyway. -- SimonM
213 allocaBytes sizeof_stat $ \ p_stat -> do
214 throwErrnoIfMinus1_ "getPermissions" $ c_stat s p_stat
215 mode <- st_mode p_stat
216 let usr_read = mode .&. s_IRUSR
217 let usr_write = mode .&. s_IWUSR
218 let usr_exec = mode .&. s_IXUSR
219 let is_dir = mode .&. s_IFDIR
222 readable = usr_read /= 0,
223 writable = usr_write /= 0,
224 executable = is_dir == 0 && usr_exec /= 0,
225 searchable = is_dir /= 0 && usr_exec /= 0
229 read_ok <- Posix.fileAccess name True False False
230 write_ok <- Posix.fileAccess name False True False
231 exec_ok <- Posix.fileAccess name False False True
232 stat <- Posix.getFileStatus name
233 let is_dir = Posix.isDirectory stat
238 executable = not is_dir && exec_ok,
239 searchable = is_dir && exec_ok
244 {- |The 'setPermissions' operation sets the
245 permissions for the file or directory.
247 The operation may fail with:
249 * 'isPermissionError' if the user is not permitted to set
252 * 'isDoesNotExistError' if the file or directory does not exist.
256 setPermissions :: FilePath -> Permissions -> IO ()
257 setPermissions name (Permissions r w e s) = do
258 #ifdef mingw32_HOST_OS
259 allocaBytes sizeof_stat $ \ p_stat -> do
260 withFilePath name $ \p_name -> do
261 throwErrnoIfMinus1_ "setPermissions" $ do
263 mode <- st_mode p_stat
264 let mode1 = modifyBit r mode s_IRUSR
265 let mode2 = modifyBit w mode1 s_IWUSR
266 let mode3 = modifyBit (e || s) mode2 s_IXUSR
267 c_wchmod p_name mode3
269 modifyBit :: Bool -> CMode -> CMode -> CMode
270 modifyBit False m b = m .&. (complement b)
271 modifyBit True m b = m .|. b
273 stat <- Posix.getFileStatus name
274 let mode = Posix.fileMode stat
275 let mode1 = modifyBit r mode Posix.ownerReadMode
276 let mode2 = modifyBit w mode1 Posix.ownerWriteMode
277 let mode3 = modifyBit (e || s) mode2 Posix.ownerExecuteMode
278 Posix.setFileMode name mode3
280 modifyBit :: Bool -> Posix.FileMode -> Posix.FileMode -> Posix.FileMode
281 modifyBit False m b = m .&. (complement b)
282 modifyBit True m b = m .|. b
285 #ifdef mingw32_HOST_OS
286 foreign import ccall unsafe "_wchmod"
287 c_wchmod :: CWString -> CMode -> IO CInt
290 copyPermissions :: FilePath -> FilePath -> IO ()
291 copyPermissions source dest = do
292 #ifdef mingw32_HOST_OS
293 allocaBytes sizeof_stat $ \ p_stat -> do
294 withFilePath source $ \p_source -> do
295 withFilePath dest $ \p_dest -> do
296 throwErrnoIfMinus1_ "copyPermissions" $ c_stat p_source p_stat
297 mode <- st_mode p_stat
298 throwErrnoIfMinus1_ "copyPermissions" $ c_wchmod p_dest mode
300 stat <- Posix.getFileStatus source
301 let mode = Posix.fileMode stat
302 Posix.setFileMode dest mode
305 -----------------------------------------------------------------------------
308 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
309 initially empty, or as near to empty as the operating system
312 The operation may fail with:
314 * 'isPermissionError' \/ 'PermissionDenied'
315 The process has insufficient privileges to perform the operation.
318 * 'isAlreadyExistsError' \/ 'AlreadyExists'
319 The operand refers to a directory that already exists.
323 A physical I\/O error has occurred.
327 The operand is not a valid directory name.
328 @[ENAMETOOLONG, ELOOP]@
331 There is no path to the directory.
334 * 'ResourceExhausted'
335 Insufficient resources (virtual memory, process file descriptors,
336 physical disk space, etc.) are available to perform the operation.
337 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
339 * 'InappropriateType'
340 The path refers to an existing non-directory object.
345 createDirectory :: FilePath -> IO ()
346 createDirectory path = do
347 #ifdef mingw32_HOST_OS
348 Win32.createDirectory path Nothing
350 Posix.createDirectory path 0o777
353 #else /* !__GLASGOW_HASKELL__ */
355 copyPermissions :: FilePath -> FilePath -> IO ()
356 copyPermissions fromFPath toFPath
357 = getPermissions fromFPath >>= setPermissions toFPath
362 -- | @'createDirectoryIfMissing' parents dir@ creates a new directory
363 -- @dir@ if it doesn\'t exist. If the first argument is 'True'
364 -- the function will also create all parent directories if they are missing.
365 createDirectoryIfMissing :: Bool -- ^ Create its parents too?
366 -> FilePath -- ^ The path to the directory you want to make
368 createDirectoryIfMissing create_parents path0
369 | create_parents = createDirs (parents path0)
370 | otherwise = createDirs (take 1 (parents path0))
372 parents = reverse . scanl1 (</>) . splitDirectories . normalise
374 createDirs [] = return ()
375 createDirs (dir:[]) = createDir dir throw
376 createDirs (dir:dirs) =
377 createDir dir $ \_ -> do
381 createDir :: FilePath -> (IOException -> IO ()) -> IO ()
382 createDir dir notExistHandler = do
383 r <- try $ createDirectory dir
384 case (r :: Either IOException ()) of
385 Right () -> return ()
387 | isDoesNotExistError e -> notExistHandler e
388 -- createDirectory (and indeed POSIX mkdir) does not distinguish
389 -- between a dir already existing and a file already existing. So we
390 -- check for it here. Unfortunately there is a slight race condition
391 -- here, but we think it is benign. It could report an exeption in
392 -- the case that the dir did exist but another process deletes the
393 -- directory and creates a file in its place before we can check
394 -- that the directory did indeed exist.
395 | isAlreadyExistsError e -> (do
396 #ifdef mingw32_HOST_OS
397 withFileStatus "createDirectoryIfMissing" dir $ \st -> do
398 isDir <- isDirectory st
399 if isDir then return ()
402 stat <- Posix.getFileStatus dir
403 if Posix.isDirectory stat
407 ) `catch` ((\_ -> return ()) :: IOException -> IO ())
408 | otherwise -> throw e
409 #endif /* !__NHC__ */
411 #if __GLASGOW_HASKELL__
412 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
413 implementation may specify additional constraints which must be
414 satisfied before a directory can be removed (e.g. the directory has to
415 be empty, or may not be in use by other processes). It is not legal
416 for an implementation to partially remove a directory unless the
417 entire directory is removed. A conformant implementation need not
418 support directory removal in all situations (e.g. removal of the root
421 The operation may fail with:
424 A physical I\/O error has occurred.
428 The operand is not a valid directory name.
429 [ENAMETOOLONG, ELOOP]
431 * 'isDoesNotExistError' \/ 'NoSuchThing'
432 The directory does not exist.
435 * 'isPermissionError' \/ 'PermissionDenied'
436 The process has insufficient privileges to perform the operation.
437 @[EROFS, EACCES, EPERM]@
439 * 'UnsatisfiedConstraints'
440 Implementation-dependent constraints are not satisfied.
441 @[EBUSY, ENOTEMPTY, EEXIST]@
443 * 'UnsupportedOperation'
444 The implementation does not support removal in this situation.
447 * 'InappropriateType'
448 The operand refers to an existing non-directory object.
453 removeDirectory :: FilePath -> IO ()
454 removeDirectory path =
455 #ifdef mingw32_HOST_OS
456 Win32.removeDirectory path
458 Posix.removeDirectory path
463 -- | @'removeDirectoryRecursive' dir@ removes an existing directory /dir/
464 -- together with its content and all subdirectories. Be careful,
465 -- if the directory contains symlinks, the function will follow them.
466 removeDirectoryRecursive :: FilePath -> IO ()
467 removeDirectoryRecursive startLoc = do
468 cont <- getDirectoryContents startLoc
469 sequence_ [rm (startLoc </> x) | x <- cont, x /= "." && x /= ".."]
470 removeDirectory startLoc
472 rm :: FilePath -> IO ()
473 rm f = do temp <- try (removeFile f)
475 Left e -> do isDir <- doesDirectoryExist f
476 -- If f is not a directory, re-throw the error
477 unless isDir $ throw (e :: SomeException)
478 removeDirectoryRecursive f
481 #if __GLASGOW_HASKELL__
482 {- |'removeFile' /file/ removes the directory entry for an existing file
483 /file/, where /file/ is not itself a directory. The
484 implementation may specify additional constraints which must be
485 satisfied before a file can be removed (e.g. the file may not be in
486 use by other processes).
488 The operation may fail with:
491 A physical I\/O error has occurred.
495 The operand is not a valid file name.
496 @[ENAMETOOLONG, ELOOP]@
498 * 'isDoesNotExistError' \/ 'NoSuchThing'
499 The file does not exist.
502 * 'isPermissionError' \/ 'PermissionDenied'
503 The process has insufficient privileges to perform the operation.
504 @[EROFS, EACCES, EPERM]@
506 * 'UnsatisfiedConstraints'
507 Implementation-dependent constraints are not satisfied.
510 * 'InappropriateType'
511 The operand refers to an existing directory.
516 removeFile :: FilePath -> IO ()
519 Win32.deleteFile path
521 Posix.removeLink path
524 {- |@'renameDirectory' old new@ changes the name of an existing
525 directory from /old/ to /new/. If the /new/ directory
526 already exists, it is atomically replaced by the /old/ directory.
527 If the /new/ directory is neither the /old/ directory nor an
528 alias of the /old/ directory, it is removed as if by
529 'removeDirectory'. A conformant implementation need not support
530 renaming directories in all situations (e.g. renaming to an existing
531 directory, or across different physical devices), but the constraints
534 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
537 The operation may fail with:
540 A physical I\/O error has occurred.
544 Either operand is not a valid directory name.
545 @[ENAMETOOLONG, ELOOP]@
547 * 'isDoesNotExistError' \/ 'NoSuchThing'
548 The original directory does not exist, or there is no path to the target.
551 * 'isPermissionError' \/ 'PermissionDenied'
552 The process has insufficient privileges to perform the operation.
553 @[EROFS, EACCES, EPERM]@
555 * 'ResourceExhausted'
556 Insufficient resources are available to perform the operation.
557 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
559 * 'UnsatisfiedConstraints'
560 Implementation-dependent constraints are not satisfied.
561 @[EBUSY, ENOTEMPTY, EEXIST]@
563 * 'UnsupportedOperation'
564 The implementation does not support renaming in this situation.
567 * 'InappropriateType'
568 Either path refers to an existing non-directory object.
573 renameDirectory :: FilePath -> FilePath -> IO ()
574 renameDirectory opath npath = do
575 -- XXX this test isn't performed atomically with the following rename
576 #ifdef mingw32_HOST_OS
577 -- ToDo: use Win32 API
578 withFileStatus "renameDirectory" opath $ \st -> do
579 is_dir <- isDirectory st
581 stat <- Posix.getFileStatus opath
582 let is_dir = Posix.fileMode stat .&. Posix.directoryMode /= 0
585 then ioException (ioeSetErrorString
586 (mkIOError InappropriateType "renameDirectory" Nothing (Just opath))
589 #ifdef mingw32_HOST_OS
590 Win32.moveFileEx opath npath Win32.mOVEFILE_REPLACE_EXISTING
592 Posix.rename opath npath
595 {- |@'renameFile' old new@ changes the name of an existing file system
596 object from /old/ to /new/. If the /new/ object already
597 exists, it is atomically replaced by the /old/ object. Neither
598 path may refer to an existing directory. A conformant implementation
599 need not support renaming files in all situations (e.g. renaming
600 across different physical devices), but the constraints must be
603 The operation may fail with:
606 A physical I\/O error has occurred.
610 Either operand is not a valid file name.
611 @[ENAMETOOLONG, ELOOP]@
613 * 'isDoesNotExistError' \/ 'NoSuchThing'
614 The original file does not exist, or there is no path to the target.
617 * 'isPermissionError' \/ 'PermissionDenied'
618 The process has insufficient privileges to perform the operation.
619 @[EROFS, EACCES, EPERM]@
621 * 'ResourceExhausted'
622 Insufficient resources are available to perform the operation.
623 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
625 * 'UnsatisfiedConstraints'
626 Implementation-dependent constraints are not satisfied.
629 * 'UnsupportedOperation'
630 The implementation does not support renaming in this situation.
633 * 'InappropriateType'
634 Either path refers to an existing directory.
635 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
639 renameFile :: FilePath -> FilePath -> IO ()
640 renameFile opath npath = do
641 -- XXX this test isn't performed atomically with the following rename
642 #ifdef mingw32_HOST_OS
643 -- ToDo: use Win32 API
644 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
645 is_dir <- isDirectory st
647 stat <- Posix.getSymbolicLinkStatus opath
648 let is_dir = Posix.isDirectory stat
651 then ioException (ioeSetErrorString
652 (mkIOError InappropriateType "renameFile" Nothing (Just opath))
655 #ifdef mingw32_HOST_OS
656 Win32.moveFileEx opath npath Win32.mOVEFILE_REPLACE_EXISTING
658 Posix.rename opath npath
661 #endif /* __GLASGOW_HASKELL__ */
663 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
664 If the /new/ file already exists, it is atomically replaced by the /old/ file.
665 Neither path may refer to an existing directory. The permissions of /old/ are
666 copied to /new/, if possible.
669 copyFile :: FilePath -> FilePath -> IO ()
671 copyFile fromFPath toFPath =
672 do readFile fromFPath >>= writeFile toFPath
673 Prelude.catch (copyPermissions fromFPath toFPath)
676 copyFile fromFPath toFPath =
677 copy `Prelude.catch` (\exc -> throw $ ioeSetLocation exc "copyFile")
678 where copy = bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
679 bracketOnError openTmp cleanTmp $ \(tmpFPath, hTmp) ->
680 do allocaBytes bufferSize $ copyContents hFrom hTmp
682 ignoreIOExceptions $ copyPermissions fromFPath tmpFPath
683 renameFile tmpFPath toFPath
684 openTmp = openBinaryTempFile (takeDirectory toFPath) ".copyFile.tmp"
685 cleanTmp (tmpFPath, hTmp)
686 = do ignoreIOExceptions $ hClose hTmp
687 ignoreIOExceptions $ removeFile tmpFPath
690 copyContents hFrom hTo buffer = do
691 count <- hGetBuf hFrom buffer bufferSize
692 when (count > 0) $ do
693 hPutBuf hTo buffer count
694 copyContents hFrom hTo buffer
696 ignoreIOExceptions io = io `catch` ioExceptionIgnorer
697 ioExceptionIgnorer :: IOException -> IO ()
698 ioExceptionIgnorer _ = return ()
701 -- | Given path referring to a file or directory, returns a
702 -- canonicalized path, with the intent that two paths referring
703 -- to the same file\/directory will map to the same canonicalized
704 -- path. Note that it is impossible to guarantee that the
705 -- implication (same file\/dir \<=\> same canonicalizedPath) holds
706 -- in either direction: this function can make only a best-effort
708 canonicalizePath :: FilePath -> IO FilePath
709 canonicalizePath fpath =
710 #if defined(mingw32_HOST_OS)
711 do path <- Win32.getFullPathName fpath
713 withCString fpath $ \pInPath ->
714 allocaBytes long_path_size $ \pOutPath ->
715 do throwErrnoPathIfNull "canonicalizePath" fpath $ c_realpath pInPath pOutPath
716 path <- peekCString pOutPath
718 return (normalise path)
719 -- normalise does more stuff, like upper-casing the drive letter
721 #if !defined(mingw32_HOST_OS)
722 foreign import ccall unsafe "realpath"
723 c_realpath :: CString
728 -- | 'makeRelative' the current directory.
729 makeRelativeToCurrentDirectory :: FilePath -> IO FilePath
730 makeRelativeToCurrentDirectory x = do
731 cur <- getCurrentDirectory
732 return $ makeRelative cur x
734 -- | Given an executable file name, searches for such file in the
735 -- directories listed in system PATH. The returned value is the path
736 -- to the found executable or Nothing if an executable with the given
737 -- name was not found. For example (findExecutable \"ghc\") gives you
740 -- The path returned by 'findExecutable' corresponds to the
741 -- program that would be executed by 'System.Process.createProcess'
742 -- when passed the same string (as a RawCommand, not a ShellCommand).
744 -- On Windows, 'findExecutable' calls the Win32 function 'SearchPath',
745 -- which may search other places before checking the directories in
746 -- @PATH@. Where it actually searches depends on registry settings,
747 -- but notably includes the directory containing the current
749 -- <http://msdn.microsoft.com/en-us/library/aa365527.aspx> for more
752 findExecutable :: String -> IO (Maybe FilePath)
753 findExecutable binary =
754 #if defined(mingw32_HOST_OS)
755 Win32.searchPath Nothing binary ('.':exeExtension)
758 path <- getEnv "PATH"
759 search (splitSearchPath path)
761 fileName = binary <.> exeExtension
763 search :: [FilePath] -> IO (Maybe FilePath)
764 search [] = return Nothing
766 let path = d </> fileName
767 b <- doesFileExist path
768 if b then return (Just path)
773 #ifdef __GLASGOW_HASKELL__
774 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
777 The operation may fail with:
780 A physical I\/O error has occurred.
784 The operand is not a valid directory name.
785 @[ENAMETOOLONG, ELOOP]@
787 * 'isDoesNotExistError' \/ 'NoSuchThing'
788 The directory does not exist.
791 * 'isPermissionError' \/ 'PermissionDenied'
792 The process has insufficient privileges to perform the operation.
795 * 'ResourceExhausted'
796 Insufficient resources are available to perform the operation.
799 * 'InappropriateType'
800 The path refers to an existing non-directory object.
805 getDirectoryContents :: FilePath -> IO [FilePath]
806 getDirectoryContents path =
807 modifyIOError ((`ioeSetFileName` path) .
808 (`ioeSetLocation` "getDirectoryContents")) $ do
809 #ifndef mingw32_HOST_OS
811 (Posix.openDirStream path)
816 e <- Posix.readDirStream dirp
817 if null e then return [] else do
822 (Win32.findFirstFile (path </> "*"))
823 (\(h,_) -> Win32.findClose h)
824 (\(h,fdat) -> loop h fdat [])
826 -- we needn't worry about empty directories: adirectory always
827 -- has at least "." and ".." entries
828 loop :: Win32.HANDLE -> Win32.FindData -> [FilePath] -> IO [FilePath]
830 filename <- Win32.getFindDataFileName fdat
831 more <- Win32.findNextFile h fdat
833 then loop h fdat (filename:acc)
834 else return (filename:acc)
835 -- no need to reverse, ordering is undefined
838 #endif /* __GLASGOW_HASKELL__ */
841 {- |If the operating system has a notion of current directories,
842 'getCurrentDirectory' returns an absolute path to the
843 current directory of the calling process.
845 The operation may fail with:
848 A physical I\/O error has occurred.
851 * 'isDoesNotExistError' \/ 'NoSuchThing'
852 There is no path referring to the current directory.
853 @[EPERM, ENOENT, ESTALE...]@
855 * 'isPermissionError' \/ 'PermissionDenied'
856 The process has insufficient privileges to perform the operation.
859 * 'ResourceExhausted'
860 Insufficient resources are available to perform the operation.
862 * 'UnsupportedOperation'
863 The operating system has no notion of current directory.
865 Note that in a concurrent program, the current directory is global
866 state shared between all threads of the process. When using
867 filesystem operations from multiple threads, it is therefore highly
868 recommended to use absolute rather than relative `FilePath`s.
871 #ifdef __GLASGOW_HASKELL__
872 getCurrentDirectory :: IO FilePath
873 getCurrentDirectory = do
874 #ifdef mingw32_HOST_OS
875 Win32.getCurrentDirectory
877 Posix.getWorkingDirectory
880 {- |If the operating system has a notion of current directories,
881 @'setCurrentDirectory' dir@ changes the current
882 directory of the calling process to /dir/.
884 The operation may fail with:
887 A physical I\/O error has occurred.
891 The operand is not a valid directory name.
892 @[ENAMETOOLONG, ELOOP]@
894 * 'isDoesNotExistError' \/ 'NoSuchThing'
895 The directory does not exist.
898 * 'isPermissionError' \/ 'PermissionDenied'
899 The process has insufficient privileges to perform the operation.
902 * 'UnsupportedOperation'
903 The operating system has no notion of current directory, or the
904 current directory cannot be dynamically changed.
906 * 'InappropriateType'
907 The path refers to an existing non-directory object.
910 Note that in a concurrent program, the current directory is global
911 state shared between all threads of the process. When using
912 filesystem operations from multiple threads, it is therefore highly
913 recommended to use absolute rather than relative `FilePath`s.
917 setCurrentDirectory :: FilePath -> IO ()
918 setCurrentDirectory path =
919 #ifdef mingw32_HOST_OS
920 Win32.setCurrentDirectory path
922 Posix.changeWorkingDirectory path
925 #endif /* __GLASGOW_HASKELL__ */
927 #ifdef __GLASGOW_HASKELL__
928 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
929 exists and is a directory, and 'False' otherwise.
932 doesDirectoryExist :: FilePath -> IO Bool
933 doesDirectoryExist name =
934 #ifdef mingw32_HOST_OS
935 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
937 (do stat <- Posix.getFileStatus name
938 return (Posix.isDirectory stat))
940 `catch` ((\ _ -> return False) :: IOException -> IO Bool)
942 {- |The operation 'doesFileExist' returns 'True'
943 if the argument file exists and is not a directory, and 'False' otherwise.
946 doesFileExist :: FilePath -> IO Bool
948 #ifdef mingw32_HOST_OS
949 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
951 (do stat <- Posix.getFileStatus name
952 return (not (Posix.isDirectory stat)))
954 `catch` ((\ _ -> return False) :: IOException -> IO Bool)
956 {- |The 'getModificationTime' operation returns the
957 clock time at which the file or directory was last modified.
959 The operation may fail with:
961 * 'isPermissionError' if the user is not permitted to access
962 the modification time; or
964 * 'isDoesNotExistError' if the file or directory does not exist.
968 getModificationTime :: FilePath -> IO ClockTime
969 getModificationTime name = do
970 #ifdef mingw32_HOST_OS
971 -- ToDo: use Win32 API
972 withFileStatus "getModificationTime" name $ \ st -> do
975 stat <- Posix.getFileStatus name
976 let mod_time :: Posix.EpochTime
977 mod_time = Posix.modificationTime stat
979 dbl_time = realToFrac mod_time
980 return (TOD (round dbl_time) 0)
983 -- round :: (RealFrac a, Integral b => a -> b
984 -- realToFrac :: (Real a, Fractional b) => a -> b
986 #endif /* __GLASGOW_HASKELL__ */
988 #ifdef mingw32_HOST_OS
989 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
990 withFileStatus loc name f = do
991 modifyIOError (`ioeSetFileName` name) $
992 allocaBytes sizeof_stat $ \p ->
993 withFilePath (fileNameEndClean name) $ \s -> do
994 throwErrnoIfMinus1Retry_ loc (c_stat s p)
997 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
998 withFileOrSymlinkStatus loc name f = do
999 modifyIOError (`ioeSetFileName` name) $
1000 allocaBytes sizeof_stat $ \p ->
1001 withFilePath name $ \s -> do
1002 throwErrnoIfMinus1Retry_ loc (lstat s p)
1005 modificationTime :: Ptr CStat -> IO ClockTime
1006 modificationTime stat = do
1007 mtime <- st_mtime stat
1008 let dbl_time :: Double
1009 dbl_time = realToFrac (mtime :: CTime)
1010 return (TOD (round dbl_time) 0)
1012 isDirectory :: Ptr CStat -> IO Bool
1013 isDirectory stat = do
1014 mode <- st_mode stat
1015 return (s_isdir mode)
1017 fileNameEndClean :: String -> String
1018 fileNameEndClean name = if isDrive name then addTrailingPathSeparator name
1019 else dropTrailingPathSeparator name
1021 foreign import ccall unsafe "HsDirectory.h __hscore_S_IRUSR" s_IRUSR :: CMode
1022 foreign import ccall unsafe "HsDirectory.h __hscore_S_IWUSR" s_IWUSR :: CMode
1023 foreign import ccall unsafe "HsDirectory.h __hscore_S_IXUSR" s_IXUSR :: CMode
1024 foreign import ccall unsafe "__hscore_S_IFDIR" s_IFDIR :: CMode
1028 #ifdef __GLASGOW_HASKELL__
1029 foreign import ccall unsafe "__hscore_long_path_size"
1030 long_path_size :: Int
1032 long_path_size :: Int
1033 long_path_size = 2048 -- // guess?
1034 #endif /* __GLASGOW_HASKELL__ */
1036 {- | Returns the current user's home directory.
1038 The directory returned is expected to be writable by the current user,
1039 but note that it isn't generally considered good practice to store
1040 application-specific data here; use 'getAppUserDataDirectory'
1043 On Unix, 'getHomeDirectory' returns the value of the @HOME@
1044 environment variable. On Windows, the system is queried for a
1045 suitable path; a typical path might be
1046 @C:/Documents And Settings/user@.
1048 The operation may fail with:
1050 * 'UnsupportedOperation'
1051 The operating system has no notion of home directory.
1053 * 'isDoesNotExistError'
1054 The home directory for the current user does not exist, or
1057 getHomeDirectory :: IO FilePath
1059 modifyIOError ((`ioeSetLocation` "getHomeDirectory")) $ do
1060 #if defined(mingw32_HOST_OS)
1061 r <- try $ Win32.sHGetFolderPath nullPtr Win32.cSIDL_PROFILE nullPtr 0
1062 case (r :: Either IOException String) of
1065 r1 <- try $ Win32.sHGetFolderPath nullPtr Win32.cSIDL_WINDOWS nullPtr 0
1068 Left e -> ioError (e :: IOException)
1073 {- | Returns the pathname of a directory in which application-specific
1074 data for the current user can be stored. The result of
1075 'getAppUserDataDirectory' for a given application is specific to
1078 The argument should be the name of the application, which will be used
1079 to construct the pathname (so avoid using unusual characters that
1080 might result in an invalid pathname).
1082 Note: the directory may not actually exist, and may need to be created
1083 first. It is expected that the parent directory exists and is
1086 On Unix, this function returns @$HOME\/.appName@. On Windows, a
1087 typical path might be
1089 > C:/Documents And Settings/user/Application Data/appName
1091 The operation may fail with:
1093 * 'UnsupportedOperation'
1094 The operating system has no notion of application-specific data directory.
1096 * 'isDoesNotExistError'
1097 The home directory for the current user does not exist, or
1100 getAppUserDataDirectory :: String -> IO FilePath
1101 getAppUserDataDirectory appName = do
1102 modifyIOError ((`ioeSetLocation` "getAppUserDataDirectory")) $ do
1103 #if defined(mingw32_HOST_OS)
1104 s <- Win32.sHGetFolderPath nullPtr Win32.cSIDL_APPDATA nullPtr 0
1105 return (s++'\\':appName)
1107 path <- getEnv "HOME"
1108 return (path++'/':'.':appName)
1111 {- | Returns the current user's document directory.
1113 The directory returned is expected to be writable by the current user,
1114 but note that it isn't generally considered good practice to store
1115 application-specific data here; use 'getAppUserDataDirectory'
1118 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
1119 environment variable. On Windows, the system is queried for a
1120 suitable path; a typical path might be
1121 @C:\/Documents and Settings\/user\/My Documents@.
1123 The operation may fail with:
1125 * 'UnsupportedOperation'
1126 The operating system has no notion of document directory.
1128 * 'isDoesNotExistError'
1129 The document directory for the current user does not exist, or
1132 getUserDocumentsDirectory :: IO FilePath
1133 getUserDocumentsDirectory = do
1134 modifyIOError ((`ioeSetLocation` "getUserDocumentsDirectory")) $ do
1135 #if defined(mingw32_HOST_OS)
1136 Win32.sHGetFolderPath nullPtr Win32.cSIDL_PERSONAL nullPtr 0
1141 {- | Returns the current directory for temporary files.
1143 On Unix, 'getTemporaryDirectory' returns the value of the @TMPDIR@
1144 environment variable or \"\/tmp\" if the variable isn\'t defined.
1145 On Windows, the function checks for the existence of environment variables in
1146 the following order and uses the first path found:
1149 TMP environment variable.
1152 TEMP environment variable.
1155 USERPROFILE environment variable.
1158 The Windows directory
1160 The operation may fail with:
1162 * 'UnsupportedOperation'
1163 The operating system has no notion of temporary directory.
1165 The function doesn\'t verify whether the path exists.
1167 getTemporaryDirectory :: IO FilePath
1168 getTemporaryDirectory = do
1169 #if defined(mingw32_HOST_OS)
1170 Win32.getTemporaryDirectory
1174 `Prelude.catch` \e -> if isDoesNotExistError e then return "/tmp"
1177 `Prelude.catch` (\ex -> return "/tmp")
1181 -- ToDo: This should be determined via autoconf (AC_EXEEXT)
1182 -- | Extension for executable files
1183 -- (typically @\"\"@ on Unix and @\"exe\"@ on Windows or OS\/2)
1184 exeExtension :: String
1185 #ifdef mingw32_HOST_OS
1186 exeExtension = "exe"