1 -----------------------------------------------------------------------------
3 -- Module : System.Directory
4 -- Copyright : (c) The University of Glasgow 2001
5 -- License : BSD-style (see the file libraries/base/LICENSE)
7 -- Maintainer : libraries@haskell.org
9 -- Portability : portable
11 -- System-independent interface to directory manipulation.
13 -----------------------------------------------------------------------------
15 module System.Directory
19 -- * Actions on directories
20 createDirectory -- :: FilePath -> IO ()
21 , createDirectoryIfMissing -- :: Bool -> FilePath -> IO ()
22 , removeDirectory -- :: FilePath -> IO ()
23 , removeDirectoryRecursive -- :: FilePath -> IO ()
24 , renameDirectory -- :: FilePath -> FilePath -> IO ()
26 , getDirectoryContents -- :: FilePath -> IO [FilePath]
27 , getCurrentDirectory -- :: IO FilePath
28 , setCurrentDirectory -- :: FilePath -> IO ()
30 -- * Pre-defined directories
32 , getAppUserDataDirectory
33 , getUserDocumentsDirectory
34 , getTemporaryDirectory
37 , removeFile -- :: FilePath -> IO ()
38 , renameFile -- :: FilePath -> FilePath -> IO ()
39 , copyFile -- :: FilePath -> FilePath -> IO ()
42 , makeRelativeToCurrentDirectory
46 , doesFileExist -- :: FilePath -> IO Bool
47 , doesDirectoryExist -- :: FilePath -> IO Bool
55 readable, -- :: Permissions -> Bool
56 writable, -- :: Permissions -> Bool
57 executable, -- :: Permissions -> Bool
58 searchable -- :: Permissions -> Bool
61 , getPermissions -- :: FilePath -> IO Permissions
62 , setPermissions -- :: FilePath -> Permissions -> IO ()
66 , getModificationTime -- :: FilePath -> IO ClockTime
69 import System.Directory.Internals
70 import System.Environment ( getEnv )
71 import System.FilePath
72 import System.IO.Error
73 import Control.Monad ( when, unless )
86 {-# CFILES cbits/directory.c #-}
88 #ifdef __GLASGOW_HASKELL__
91 import Control.Exception ( bracket )
92 import System.Posix.Types
93 import System.Posix.Internals
94 import System.Time ( ClockTime(..) )
97 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
100 A directory contains a series of entries, each of which is a named
101 reference to a file system object (file, directory etc.). Some
102 entries may be hidden, inaccessible, or have some administrative
103 function (e.g. `.' or `..' under POSIX
104 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
105 this standard all such entries are considered to form part of the
106 directory contents. Entries in sub-directories are not, however,
107 considered to form part of the directory contents.
109 Each file system object is referenced by a /path/. There is
110 normally at least one absolute path to each file system object. In
111 some operating systems, it may also be possible to have paths which
112 are relative to the current directory.
115 -----------------------------------------------------------------------------
120 The 'Permissions' type is used to record whether certain operations are
121 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
122 get and set these permissions, respectively. Permissions apply both to
123 files and directories. For directories, the executable field will be
124 'False', and for files the searchable field will be 'False'. Note that
125 directories may be searchable without being readable, if permission has
126 been given to use them as part of a path, but not to examine the
129 Note that to change some, but not all permissions, a construct on the following lines must be used.
131 > makeReadable f = do
132 > p <- getPermissions f
133 > setPermissions f (p {readable = True})
140 executable, searchable :: Bool
141 } deriving (Eq, Ord, Read, Show)
143 {- |The 'getPermissions' operation returns the
144 permissions for the file or directory.
146 The operation may fail with:
148 * 'isPermissionError' if the user is not permitted to access
151 * 'isDoesNotExistError' if the file or directory does not exist.
155 getPermissions :: FilePath -> IO Permissions
156 getPermissions name = do
157 withCString name $ \s -> do
158 read <- c_access s r_OK
159 write <- c_access s w_OK
160 exec <- c_access s x_OK
161 withFileStatus "getPermissions" name $ \st -> do
162 is_dir <- isDirectory st
165 readable = read == 0,
166 writable = write == 0,
167 executable = not is_dir && exec == 0,
168 searchable = is_dir && exec == 0
172 {- |The 'setPermissions' operation sets the
173 permissions for the file or directory.
175 The operation may fail with:
177 * 'isPermissionError' if the user is not permitted to set
180 * 'isDoesNotExistError' if the file or directory does not exist.
184 setPermissions :: FilePath -> Permissions -> IO ()
185 setPermissions name (Permissions r w e s) = do
186 allocaBytes sizeof_stat $ \ p_stat -> do
187 withCString name $ \p_name -> do
188 throwErrnoIfMinus1_ "setPermissions" $ do
190 mode <- st_mode p_stat
191 let mode1 = modifyBit r mode s_IRUSR
192 let mode2 = modifyBit w mode1 s_IWUSR
193 let mode3 = modifyBit (e || s) mode2 s_IXUSR
197 modifyBit :: Bool -> CMode -> CMode -> CMode
198 modifyBit False m b = m .&. (complement b)
199 modifyBit True m b = m .|. b
202 copyPermissions :: FilePath -> FilePath -> IO ()
203 copyPermissions source dest = do
204 allocaBytes sizeof_stat $ \ p_stat -> do
205 withCString source $ \p_source -> do
206 withCString dest $ \p_dest -> do
207 throwErrnoIfMinus1_ "copyPermissions" $ c_stat p_source p_stat
208 mode <- st_mode p_stat
209 throwErrnoIfMinus1_ "copyPermissions" $ c_chmod p_dest mode
211 -----------------------------------------------------------------------------
214 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
215 initially empty, or as near to empty as the operating system
218 The operation may fail with:
220 * 'isPermissionError' \/ 'PermissionDenied'
221 The process has insufficient privileges to perform the operation.
224 * 'isAlreadyExistsError' \/ 'AlreadyExists'
225 The operand refers to a directory that already exists.
229 A physical I\/O error has occurred.
233 The operand is not a valid directory name.
234 @[ENAMETOOLONG, ELOOP]@
237 There is no path to the directory.
240 * 'ResourceExhausted'
241 Insufficient resources (virtual memory, process file descriptors,
242 physical disk space, etc.) are available to perform the operation.
243 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
245 * 'InappropriateType'
246 The path refers to an existing non-directory object.
251 createDirectory :: FilePath -> IO ()
252 createDirectory path = do
253 modifyIOError (`ioeSetFileName` path) $
254 withCString path $ \s -> do
255 throwErrnoIfMinus1Retry_ "createDirectory" $
258 #else /* !__GLASGOW_HASKELL__ */
260 copyPermissions :: FilePath -> FilePath -> IO ()
261 copyPermissions fromFPath toFPath
262 = getPermissions fromFPath >>= setPermissions toFPath
266 -- | @'createDirectoryIfMissing' parents dir@ creates a new directory
267 -- @dir@ if it doesn\'t exist. If the first argument is 'True'
268 -- the function will also create all parent directories if they are missing.
269 createDirectoryIfMissing :: Bool -- ^ Create its parents too?
270 -> FilePath -- ^ The path to the directory you want to make
272 createDirectoryIfMissing parents file = do
273 b <- doesDirectoryExist file
274 case (b,parents, file) of
275 (_, _, "") -> return ()
276 (True, _, _) -> return ()
277 (_, True, _) -> mapM_ (createDirectoryIfMissing False) (tail (pathParents file))
278 (_, False, _) -> createDirectory file
280 #if __GLASGOW_HASKELL__
281 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
282 implementation may specify additional constraints which must be
283 satisfied before a directory can be removed (e.g. the directory has to
284 be empty, or may not be in use by other processes). It is not legal
285 for an implementation to partially remove a directory unless the
286 entire directory is removed. A conformant implementation need not
287 support directory removal in all situations (e.g. removal of the root
290 The operation may fail with:
293 A physical I\/O error has occurred.
297 The operand is not a valid directory name.
298 [ENAMETOOLONG, ELOOP]
300 * 'isDoesNotExistError' \/ 'NoSuchThing'
301 The directory does not exist.
304 * 'isPermissionError' \/ 'PermissionDenied'
305 The process has insufficient privileges to perform the operation.
306 @[EROFS, EACCES, EPERM]@
308 * 'UnsatisfiedConstraints'
309 Implementation-dependent constraints are not satisfied.
310 @[EBUSY, ENOTEMPTY, EEXIST]@
312 * 'UnsupportedOperation'
313 The implementation does not support removal in this situation.
316 * 'InappropriateType'
317 The operand refers to an existing non-directory object.
322 removeDirectory :: FilePath -> IO ()
323 removeDirectory path = do
324 modifyIOError (`ioeSetFileName` path) $
325 withCString path $ \s ->
326 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
329 -- | @'removeDirectoryRecursive' dir@ removes an existing directory /dir/
330 -- together with its content and all subdirectories. Be careful,
331 -- if the directory contains symlinks, the function will follow them.
332 removeDirectoryRecursive :: FilePath -> IO ()
333 removeDirectoryRecursive startLoc = do
334 cont <- getDirectoryContents startLoc
335 sequence_ [rm (startLoc `joinFileName` x) | x <- cont, x /= "." && x /= ".."]
336 removeDirectory startLoc
338 rm :: FilePath -> IO ()
339 rm f = do temp <- try (removeFile f)
341 Left e -> do isDir <- doesDirectoryExist f
342 -- If f is not a directory, re-throw the error
343 unless isDir $ ioError e
344 removeDirectoryRecursive f
347 #if __GLASGOW_HASKELL__
348 {- |'removeFile' /file/ removes the directory entry for an existing file
349 /file/, where /file/ is not itself a directory. The
350 implementation may specify additional constraints which must be
351 satisfied before a file can be removed (e.g. the file may not be in
352 use by other processes).
354 The operation may fail with:
357 A physical I\/O error has occurred.
361 The operand is not a valid file name.
362 @[ENAMETOOLONG, ELOOP]@
364 * 'isDoesNotExistError' \/ 'NoSuchThing'
365 The file does not exist.
368 * 'isPermissionError' \/ 'PermissionDenied'
369 The process has insufficient privileges to perform the operation.
370 @[EROFS, EACCES, EPERM]@
372 * 'UnsatisfiedConstraints'
373 Implementation-dependent constraints are not satisfied.
376 * 'InappropriateType'
377 The operand refers to an existing directory.
382 removeFile :: FilePath -> IO ()
384 modifyIOError (`ioeSetFileName` path) $
385 withCString path $ \s ->
386 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
388 {- |@'renameDirectory' old new@ changes the name of an existing
389 directory from /old/ to /new/. If the /new/ directory
390 already exists, it is atomically replaced by the /old/ directory.
391 If the /new/ directory is neither the /old/ directory nor an
392 alias of the /old/ directory, it is removed as if by
393 'removeDirectory'. A conformant implementation need not support
394 renaming directories in all situations (e.g. renaming to an existing
395 directory, or across different physical devices), but the constraints
398 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
401 The operation may fail with:
404 A physical I\/O error has occurred.
408 Either operand is not a valid directory name.
409 @[ENAMETOOLONG, ELOOP]@
411 * 'isDoesNotExistError' \/ 'NoSuchThing'
412 The original directory does not exist, or there is no path to the target.
415 * 'isPermissionError' \/ 'PermissionDenied'
416 The process has insufficient privileges to perform the operation.
417 @[EROFS, EACCES, EPERM]@
419 * 'ResourceExhausted'
420 Insufficient resources are available to perform the operation.
421 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
423 * 'UnsatisfiedConstraints'
424 Implementation-dependent constraints are not satisfied.
425 @[EBUSY, ENOTEMPTY, EEXIST]@
427 * 'UnsupportedOperation'
428 The implementation does not support renaming in this situation.
431 * 'InappropriateType'
432 Either path refers to an existing non-directory object.
437 renameDirectory :: FilePath -> FilePath -> IO ()
438 renameDirectory opath npath =
439 withFileStatus "renameDirectory" opath $ \st -> do
440 is_dir <- isDirectory st
442 then ioException (IOError Nothing InappropriateType "renameDirectory"
443 ("not a directory") (Just opath))
446 withCString opath $ \s1 ->
447 withCString npath $ \s2 ->
448 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
450 {- |@'renameFile' old new@ changes the name of an existing file system
451 object from /old/ to /new/. If the /new/ object already
452 exists, it is atomically replaced by the /old/ object. Neither
453 path may refer to an existing directory. A conformant implementation
454 need not support renaming files in all situations (e.g. renaming
455 across different physical devices), but the constraints must be
458 The operation may fail with:
461 A physical I\/O error has occurred.
465 Either operand is not a valid file name.
466 @[ENAMETOOLONG, ELOOP]@
468 * 'isDoesNotExistError' \/ 'NoSuchThing'
469 The original file does not exist, or there is no path to the target.
472 * 'isPermissionError' \/ 'PermissionDenied'
473 The process has insufficient privileges to perform the operation.
474 @[EROFS, EACCES, EPERM]@
476 * 'ResourceExhausted'
477 Insufficient resources are available to perform the operation.
478 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
480 * 'UnsatisfiedConstraints'
481 Implementation-dependent constraints are not satisfied.
484 * 'UnsupportedOperation'
485 The implementation does not support renaming in this situation.
488 * 'InappropriateType'
489 Either path refers to an existing directory.
490 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
494 renameFile :: FilePath -> FilePath -> IO ()
495 renameFile opath npath =
496 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
497 is_dir <- isDirectory st
499 then ioException (IOError Nothing InappropriateType "renameFile"
500 "is a directory" (Just opath))
503 withCString opath $ \s1 ->
504 withCString npath $ \s2 ->
505 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
507 #endif /* __GLASGOW_HASKELL__ */
509 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
510 If the /new/ file already exists, it is atomically replaced by the /old/ file.
511 Neither path may refer to an existing directory. The permissions of /old/ are
512 copied to /new/, if possible.
517 It's tempting to try to remove the target file before opening it for
518 writing. This could be useful: for example if the target file is an
519 executable that is in use, writing will fail, but unlinking first
522 However, it certainly isn't always what you want.
524 * if the target file is hardlinked, removing it would break
525 the hard link, but just opening would preserve it.
527 * opening and truncating will preserve permissions and
530 * If the destination file is read-only in a writable directory,
531 we might want copyFile to fail. Removing the target first
532 would succeed, however.
534 * If the destination file is special (eg. /dev/null), removing
535 it is probably not the right thing. Copying to /dev/null
536 should leave /dev/null intact, not replace it with a plain
539 * There's a small race condition between removing the target and
540 opening it for writing during which time someone might
543 copyFile :: FilePath -> FilePath -> IO ()
544 copyFile fromFPath toFPath =
545 #if (!(defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ > 600))
546 do readFile fromFPath >>= writeFile toFPath
547 try (copyPermissions fromFPath toFPath)
550 (bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
551 bracket (openBinaryFile toFPath WriteMode) hClose $ \hTo ->
552 allocaBytes bufferSize $ \buffer -> do
553 copyContents hFrom hTo buffer
554 try (copyPermissions fromFPath toFPath)
555 return ()) `catch` (ioError . changeFunName)
559 changeFunName (IOError h iot fun str mb_fp) = IOError h iot "copyFile" str mb_fp
561 copyContents hFrom hTo buffer = do
562 count <- hGetBuf hFrom buffer bufferSize
563 when (count > 0) $ do
564 hPutBuf hTo buffer count
565 copyContents hFrom hTo buffer
568 -- | Given path referring to a file or directory, returns a
569 -- canonicalized path, with the intent that two paths referring
570 -- to the same file\/directory will map to the same canonicalized
571 -- path. Note that it is impossible to guarantee that the
572 -- implication (same file\/dir \<=\> same canonicalizedPath) holds
573 -- in either direction: this function can make only a best-effort
575 canonicalizePath :: FilePath -> IO FilePath
576 canonicalizePath fpath =
577 withCString fpath $ \pInPath ->
578 allocaBytes long_path_size $ \pOutPath ->
579 #if defined(mingw32_HOST_OS)
580 alloca $ \ppFilePart ->
581 do c_GetFullPathName pInPath (fromIntegral long_path_size) pOutPath ppFilePart
583 do c_realpath pInPath pOutPath
587 #if defined(mingw32_HOST_OS)
588 foreign import stdcall unsafe "GetFullPathNameA"
589 c_GetFullPathName :: CString
595 foreign import ccall unsafe "realpath"
596 c_realpath :: CString
601 -- | 'makeRelative' the current directory.
602 makeRelativeToCurrentDirectory :: FilePath -> IO FilePath
603 makeRelativeToCurrentDirectory x = do
604 cur <- getCurrentDirectory
605 return $ makeRelative cur x
607 -- | Given an executable file name, searches for such file
608 -- in the directories listed in system PATH. The returned value
609 -- is the path to the found executable or Nothing if there isn't
610 -- such executable. For example (findExecutable \"ghc\")
611 -- gives you the path to GHC.
612 findExecutable :: String -> IO (Maybe FilePath)
613 findExecutable binary =
614 #if defined(mingw32_HOST_OS)
615 withCString binary $ \c_binary ->
616 withCString ('.':exeExtension) $ \c_ext ->
617 allocaBytes long_path_size $ \pOutPath ->
618 alloca $ \ppFilePart -> do
619 res <- c_SearchPath nullPtr c_binary c_ext (fromIntegral long_path_size) pOutPath ppFilePart
620 if res > 0 && res < fromIntegral long_path_size
621 then do fpath <- peekCString pOutPath
625 foreign import stdcall unsafe "SearchPathA"
626 c_SearchPath :: CString
635 path <- getEnv "PATH"
636 search (parseSearchPath path)
638 fileName = binary `joinFileExt` exeExtension
640 search :: [FilePath] -> IO (Maybe FilePath)
641 search [] = return Nothing
643 let path = d `joinFileName` fileName
644 b <- doesFileExist path
645 if b then return (Just path)
650 #ifdef __GLASGOW_HASKELL__
651 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
654 The operation may fail with:
657 A physical I\/O error has occurred.
661 The operand is not a valid directory name.
662 @[ENAMETOOLONG, ELOOP]@
664 * 'isDoesNotExistError' \/ 'NoSuchThing'
665 The directory does not exist.
668 * 'isPermissionError' \/ 'PermissionDenied'
669 The process has insufficient privileges to perform the operation.
672 * 'ResourceExhausted'
673 Insufficient resources are available to perform the operation.
676 * 'InappropriateType'
677 The path refers to an existing non-directory object.
682 getDirectoryContents :: FilePath -> IO [FilePath]
683 getDirectoryContents path = do
684 modifyIOError (`ioeSetFileName` path) $
685 alloca $ \ ptr_dEnt ->
687 (withCString path $ \s ->
688 throwErrnoIfNullRetry desc (c_opendir s))
689 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
690 (\p -> loop ptr_dEnt p)
692 desc = "getDirectoryContents"
694 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
695 loop ptr_dEnt dir = do
697 r <- readdir dir ptr_dEnt
700 dEnt <- peek ptr_dEnt
704 entry <- (d_name dEnt >>= peekCString)
706 entries <- loop ptr_dEnt dir
707 return (entry:entries)
708 else do errno <- getErrno
709 if (errno == eINTR) then loop ptr_dEnt dir else do
710 let (Errno eo) = errno
711 if (eo == end_of_dir)
717 {- |If the operating system has a notion of current directories,
718 'getCurrentDirectory' returns an absolute path to the
719 current directory of the calling process.
721 The operation may fail with:
724 A physical I\/O error has occurred.
727 * 'isDoesNotExistError' \/ 'NoSuchThing'
728 There is no path referring to the current directory.
729 @[EPERM, ENOENT, ESTALE...]@
731 * 'isPermissionError' \/ 'PermissionDenied'
732 The process has insufficient privileges to perform the operation.
735 * 'ResourceExhausted'
736 Insufficient resources are available to perform the operation.
738 * 'UnsupportedOperation'
739 The operating system has no notion of current directory.
743 getCurrentDirectory :: IO FilePath
744 getCurrentDirectory = do
745 p <- mallocBytes long_path_size
747 where go p bytes = do
748 p' <- c_getcwd p (fromIntegral bytes)
750 then do s <- peekCString p'
753 else do errno <- getErrno
755 then do let bytes' = bytes * 2
756 p' <- reallocBytes p bytes'
758 else throwErrno "getCurrentDirectory"
760 {- |If the operating system has a notion of current directories,
761 @'setCurrentDirectory' dir@ changes the current
762 directory of the calling process to /dir/.
764 The operation may fail with:
767 A physical I\/O error has occurred.
771 The operand is not a valid directory name.
772 @[ENAMETOOLONG, ELOOP]@
774 * 'isDoesNotExistError' \/ 'NoSuchThing'
775 The directory does not exist.
778 * 'isPermissionError' \/ 'PermissionDenied'
779 The process has insufficient privileges to perform the operation.
782 * 'UnsupportedOperation'
783 The operating system has no notion of current directory, or the
784 current directory cannot be dynamically changed.
786 * 'InappropriateType'
787 The path refers to an existing non-directory object.
792 setCurrentDirectory :: FilePath -> IO ()
793 setCurrentDirectory path = do
794 modifyIOError (`ioeSetFileName` path) $
795 withCString path $ \s ->
796 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
797 -- ToDo: add path to error
799 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
800 exists and is a directory, and 'False' otherwise.
803 doesDirectoryExist :: FilePath -> IO Bool
804 doesDirectoryExist name =
806 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
807 (\ _ -> return False)
809 {- |The operation 'doesFileExist' returns 'True'
810 if the argument file exists and is not a directory, and 'False' otherwise.
813 doesFileExist :: FilePath -> IO Bool
814 doesFileExist name = do
816 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
817 (\ _ -> return False)
819 {- |The 'getModificationTime' operation returns the
820 clock time at which the file or directory was last modified.
822 The operation may fail with:
824 * 'isPermissionError' if the user is not permitted to access
825 the modification time; or
827 * 'isDoesNotExistError' if the file or directory does not exist.
831 getModificationTime :: FilePath -> IO ClockTime
832 getModificationTime name =
833 withFileStatus "getModificationTime" name $ \ st ->
836 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
837 withFileStatus loc name f = do
838 modifyIOError (`ioeSetFileName` name) $
839 allocaBytes sizeof_stat $ \p ->
840 withCString (fileNameEndClean name) $ \s -> do
841 throwErrnoIfMinus1Retry_ loc (c_stat s p)
844 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
845 withFileOrSymlinkStatus loc name f = do
846 modifyIOError (`ioeSetFileName` name) $
847 allocaBytes sizeof_stat $ \p ->
848 withCString name $ \s -> do
849 throwErrnoIfMinus1Retry_ loc (lstat s p)
852 modificationTime :: Ptr CStat -> IO ClockTime
853 modificationTime stat = do
854 mtime <- st_mtime stat
855 let realToInteger = round . realToFrac :: Real a => a -> Integer
856 return (TOD (realToInteger (mtime :: CTime)) 0)
858 isDirectory :: Ptr CStat -> IO Bool
859 isDirectory stat = do
861 return (s_isdir mode)
863 fileNameEndClean :: String -> String
864 fileNameEndClean name =
865 if i > 0 && (ec == '\\' || ec == '/') then
866 fileNameEndClean (take i name)
870 i = (length name) - 1
873 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CInt
874 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CInt
875 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CInt
877 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
878 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
879 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode
881 foreign import ccall unsafe "__hscore_long_path_size"
882 long_path_size :: Int
885 long_path_size :: Int
886 long_path_size = 2048 -- // guess?
888 #endif /* __GLASGOW_HASKELL__ */
890 {- | Returns the current user's home directory.
892 The directory returned is expected to be writable by the current user,
893 but note that it isn't generally considered good practice to store
894 application-specific data here; use 'getAppUserDataDirectory'
897 On Unix, 'getHomeDirectory' returns the value of the @HOME@
898 environment variable. On Windows, the system is queried for a
899 suitable path; a typical path might be
900 @C:/Documents And Settings/user@.
902 The operation may fail with:
904 * 'UnsupportedOperation'
905 The operating system has no notion of home directory.
907 * 'isDoesNotExistError'
908 The home directory for the current user does not exist, or
911 getHomeDirectory :: IO FilePath
913 #if defined(mingw32_HOST_OS)
914 allocaBytes long_path_size $ \pPath -> do
915 r <- c_SHGetFolderPath nullPtr csidl_PROFILE nullPtr 0 pPath
918 r <- c_SHGetFolderPath nullPtr csidl_WINDOWS nullPtr 0 pPath
919 when (r < 0) (raiseUnsupported "System.Directory.getHomeDirectory")
926 {- | Returns the pathname of a directory in which application-specific
927 data for the current user can be stored. The result of
928 'getAppUserDataDirectory' for a given application is specific to
931 The argument should be the name of the application, which will be used
932 to construct the pathname (so avoid using unusual characters that
933 might result in an invalid pathname).
935 Note: the directory may not actually exist, and may need to be created
936 first. It is expected that the parent directory exists and is
939 On Unix, this function returns @$HOME\/.appName@. On Windows, a
940 typical path might be
942 > C:/Documents And Settings/user/Application Data/appName
944 The operation may fail with:
946 * 'UnsupportedOperation'
947 The operating system has no notion of application-specific data directory.
949 * 'isDoesNotExistError'
950 The home directory for the current user does not exist, or
953 getAppUserDataDirectory :: String -> IO FilePath
954 getAppUserDataDirectory appName = do
955 #if defined(mingw32_HOST_OS)
956 allocaBytes long_path_size $ \pPath -> do
957 r <- c_SHGetFolderPath nullPtr csidl_APPDATA nullPtr 0 pPath
958 when (r<0) (raiseUnsupported "System.Directory.getAppUserDataDirectory")
959 s <- peekCString pPath
960 return (s++'\\':appName)
962 path <- getEnv "HOME"
963 return (path++'/':'.':appName)
966 {- | Returns the current user's document directory.
968 The directory returned is expected to be writable by the current user,
969 but note that it isn't generally considered good practice to store
970 application-specific data here; use 'getAppUserDataDirectory'
973 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
974 environment variable. On Windows, the system is queried for a
975 suitable path; a typical path might be
976 @C:\/Documents and Settings\/user\/My Documents@.
978 The operation may fail with:
980 * 'UnsupportedOperation'
981 The operating system has no notion of document directory.
983 * 'isDoesNotExistError'
984 The document directory for the current user does not exist, or
987 getUserDocumentsDirectory :: IO FilePath
988 getUserDocumentsDirectory = do
989 #if defined(mingw32_HOST_OS)
990 allocaBytes long_path_size $ \pPath -> do
991 r <- c_SHGetFolderPath nullPtr csidl_PERSONAL nullPtr 0 pPath
992 when (r<0) (raiseUnsupported "System.Directory.getUserDocumentsDirectory")
998 {- | Returns the current directory for temporary files.
1000 On Unix, 'getTemporaryDirectory' returns the value of the @TMPDIR@
1001 environment variable or \"\/tmp\" if the variable isn\'t defined.
1002 On Windows, the function checks for the existence of environment variables in
1003 the following order and uses the first path found:
1006 TMP environment variable.
1009 TEMP environment variable.
1012 USERPROFILE environment variable.
1015 The Windows directory
1017 The operation may fail with:
1019 * 'UnsupportedOperation'
1020 The operating system has no notion of temporary directory.
1022 The function doesn\'t verify whether the path exists.
1024 getTemporaryDirectory :: IO FilePath
1025 getTemporaryDirectory = do
1026 #if defined(mingw32_HOST_OS)
1027 allocaBytes long_path_size $ \pPath -> do
1028 r <- c_GetTempPath (fromIntegral long_path_size) pPath
1031 catch (getEnv "TMPDIR") (\ex -> return "/tmp")
1034 #if defined(mingw32_HOST_OS)
1035 foreign import ccall unsafe "__hscore_getFolderPath"
1036 c_SHGetFolderPath :: Ptr ()
1042 foreign import ccall unsafe "__hscore_CSIDL_PROFILE" csidl_PROFILE :: CInt
1043 foreign import ccall unsafe "__hscore_CSIDL_APPDATA" csidl_APPDATA :: CInt
1044 foreign import ccall unsafe "__hscore_CSIDL_WINDOWS" csidl_WINDOWS :: CInt
1045 foreign import ccall unsafe "__hscore_CSIDL_PERSONAL" csidl_PERSONAL :: CInt
1047 foreign import stdcall unsafe "GetTempPathA" c_GetTempPath :: CInt -> CString -> IO CInt
1049 raiseUnsupported loc =
1050 ioException (IOError Nothing UnsupportedOperation loc "unsupported operation" Nothing)