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 ()
45 , doesFileExist -- :: FilePath -> IO Bool
46 , doesDirectoryExist -- :: FilePath -> IO Bool
54 readable, -- :: Permissions -> Bool
55 writable, -- :: Permissions -> Bool
56 executable, -- :: Permissions -> Bool
57 searchable -- :: Permissions -> Bool
60 , getPermissions -- :: FilePath -> IO Permissions
61 , setPermissions -- :: FilePath -> Permissions -> IO ()
65 , getModificationTime -- :: FilePath -> IO ClockTime
68 import System.Directory.Internals
69 import System.Environment ( getEnv )
70 import System.IO.Error
71 import Control.Monad ( when, unless )
82 #if defined(__GLASGOW_HASKELL__) || defined(mingw32_HOST_OS)
87 #ifdef __GLASGOW_HASKELL__
90 import Control.Exception ( bracket )
91 import System.Posix.Types
92 import System.Posix.Internals
93 import System.Time ( ClockTime(..) )
96 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
99 A directory contains a series of entries, each of which is a named
100 reference to a file system object (file, directory etc.). Some
101 entries may be hidden, inaccessible, or have some administrative
102 function (e.g. `.' or `..' under POSIX
103 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
104 this standard all such entries are considered to form part of the
105 directory contents. Entries in sub-directories are not, however,
106 considered to form part of the directory contents.
108 Each file system object is referenced by a /path/. There is
109 normally at least one absolute path to each file system object. In
110 some operating systems, it may also be possible to have paths which
111 are relative to the current directory.
114 -----------------------------------------------------------------------------
119 The 'Permissions' type is used to record whether certain operations are
120 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
121 get and set these permissions, respectively. Permissions apply both to
122 files and directories. For directories, the executable field will be
123 'False', and for files the searchable field will be 'False'. Note that
124 directories may be searchable without being readable, if permission has
125 been given to use them as part of a path, but not to examine the
128 Note that to change some, but not all permissions, a construct on the following lines must be used.
130 > makeReadable f = do
131 > p <- getPermissions f
132 > setPermissions f (p {readable = True})
139 executable, searchable :: Bool
140 } deriving (Eq, Ord, Read, Show)
142 {- |The 'getPermissions' operation returns the
143 permissions for the file or directory.
145 The operation may fail with:
147 * 'isPermissionError' if the user is not permitted to access
150 * 'isDoesNotExistError' if the file or directory does not exist.
154 getPermissions :: FilePath -> IO Permissions
155 getPermissions name = do
156 withCString name $ \s -> do
157 read <- c_access s r_OK
158 write <- c_access s w_OK
159 exec <- c_access s x_OK
160 withFileStatus "getPermissions" name $ \st -> do
161 is_dir <- isDirectory st
164 readable = read == 0,
165 writable = write == 0,
166 executable = not is_dir && exec == 0,
167 searchable = is_dir && exec == 0
171 {- |The 'setPermissions' operation sets the
172 permissions for the file or directory.
174 The operation may fail with:
176 * 'isPermissionError' if the user is not permitted to set
179 * 'isDoesNotExistError' if the file or directory does not exist.
183 setPermissions :: FilePath -> Permissions -> IO ()
184 setPermissions name (Permissions r w e s) = do
185 allocaBytes sizeof_stat $ \ p_stat -> do
186 withCString name $ \p_name -> do
187 throwErrnoIfMinus1_ "setPermissions" $ do
189 mode <- st_mode p_stat
190 let mode1 = modifyBit r mode s_IRUSR
191 let mode2 = modifyBit w mode1 s_IWUSR
192 let mode3 = modifyBit (e || s) mode2 s_IXUSR
196 modifyBit :: Bool -> CMode -> CMode -> CMode
197 modifyBit False m b = m .&. (complement b)
198 modifyBit True m b = m .|. b
201 copyPermissions :: FilePath -> FilePath -> IO ()
202 copyPermissions source dest = do
203 allocaBytes sizeof_stat $ \ p_stat -> do
204 withCString source $ \p_source -> do
205 withCString dest $ \p_dest -> do
206 throwErrnoIfMinus1_ "copyPermissions" $ c_stat p_source p_stat
207 mode <- st_mode p_stat
208 throwErrnoIfMinus1_ "copyPermissions" $ c_chmod p_dest mode
210 -----------------------------------------------------------------------------
213 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
214 initially empty, or as near to empty as the operating system
217 The operation may fail with:
219 * 'isPermissionError' \/ 'PermissionDenied'
220 The process has insufficient privileges to perform the operation.
223 * 'isAlreadyExistsError' \/ 'AlreadyExists'
224 The operand refers to a directory that already exists.
228 A physical I\/O error has occurred.
232 The operand is not a valid directory name.
233 @[ENAMETOOLONG, ELOOP]@
236 There is no path to the directory.
239 * 'ResourceExhausted'
240 Insufficient resources (virtual memory, process file descriptors,
241 physical disk space, etc.) are available to perform the operation.
242 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
244 * 'InappropriateType'
245 The path refers to an existing non-directory object.
250 createDirectory :: FilePath -> IO ()
251 createDirectory path = do
252 modifyIOError (`ioeSetFileName` path) $
253 withCString path $ \s -> do
254 throwErrnoIfMinus1Retry_ "createDirectory" $
257 #else /* !__GLASGOW_HASKELL__ */
259 copyPermissions :: FilePath -> FilePath -> IO ()
260 copyPermissions fromFPath toFPath
261 = getPermissions fromFPath >>= setPermissions toFPath
265 -- | @'createDirectoryIfMissing' parents dir@ creates a new directory
266 -- @dir@ if it doesn\'t exist. If the first argument is 'True'
267 -- the function will also create all parent directories if they are missing.
268 createDirectoryIfMissing :: Bool -- ^ Create its parents too?
269 -> FilePath -- ^ The path to the directory you want to make
271 createDirectoryIfMissing parents file = do
272 b <- doesDirectoryExist file
273 case (b,parents, file) of
274 (_, _, "") -> return ()
275 (True, _, _) -> return ()
276 (_, True, _) -> mapM_ (createDirectoryIfMissing False) (tail (pathParents file))
277 (_, False, _) -> createDirectory file
279 #if __GLASGOW_HASKELL__
280 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
281 implementation may specify additional constraints which must be
282 satisfied before a directory can be removed (e.g. the directory has to
283 be empty, or may not be in use by other processes). It is not legal
284 for an implementation to partially remove a directory unless the
285 entire directory is removed. A conformant implementation need not
286 support directory removal in all situations (e.g. removal of the root
289 The operation may fail with:
292 A physical I\/O error has occurred.
296 The operand is not a valid directory name.
297 [ENAMETOOLONG, ELOOP]
299 * 'isDoesNotExistError' \/ 'NoSuchThing'
300 The directory does not exist.
303 * 'isPermissionError' \/ 'PermissionDenied'
304 The process has insufficient privileges to perform the operation.
305 @[EROFS, EACCES, EPERM]@
307 * 'UnsatisfiedConstraints'
308 Implementation-dependent constraints are not satisfied.
309 @[EBUSY, ENOTEMPTY, EEXIST]@
311 * 'UnsupportedOperation'
312 The implementation does not support removal in this situation.
315 * 'InappropriateType'
316 The operand refers to an existing non-directory object.
321 removeDirectory :: FilePath -> IO ()
322 removeDirectory path = do
323 modifyIOError (`ioeSetFileName` path) $
324 withCString path $ \s ->
325 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
328 -- | @'removeDirectoryRecursive' dir@ removes an existing directory /dir/
329 -- together with its content and all subdirectories. Be careful,
330 -- if the directory contains symlinks, the function will follow them.
331 removeDirectoryRecursive :: FilePath -> IO ()
332 removeDirectoryRecursive startLoc = do
333 cont <- getDirectoryContents startLoc
334 sequence_ [rm (startLoc `joinFileName` x) | x <- cont, x /= "." && x /= ".."]
335 removeDirectory startLoc
337 rm :: FilePath -> IO ()
338 rm f = do temp <- try (removeFile f)
340 Left e -> do isDir <- doesDirectoryExist f
341 -- If f is not a directory, re-throw the error
342 unless isDir $ ioError e
343 removeDirectoryRecursive f
346 #if __GLASGOW_HASKELL__
347 {- |'removeFile' /file/ removes the directory entry for an existing file
348 /file/, where /file/ is not itself a directory. The
349 implementation may specify additional constraints which must be
350 satisfied before a file can be removed (e.g. the file may not be in
351 use by other processes).
353 The operation may fail with:
356 A physical I\/O error has occurred.
360 The operand is not a valid file name.
361 @[ENAMETOOLONG, ELOOP]@
363 * 'isDoesNotExistError' \/ 'NoSuchThing'
364 The file does not exist.
367 * 'isPermissionError' \/ 'PermissionDenied'
368 The process has insufficient privileges to perform the operation.
369 @[EROFS, EACCES, EPERM]@
371 * 'UnsatisfiedConstraints'
372 Implementation-dependent constraints are not satisfied.
375 * 'InappropriateType'
376 The operand refers to an existing directory.
381 removeFile :: FilePath -> IO ()
383 modifyIOError (`ioeSetFileName` path) $
384 withCString path $ \s ->
385 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
387 {- |@'renameDirectory' old new@ changes the name of an existing
388 directory from /old/ to /new/. If the /new/ directory
389 already exists, it is atomically replaced by the /old/ directory.
390 If the /new/ directory is neither the /old/ directory nor an
391 alias of the /old/ directory, it is removed as if by
392 'removeDirectory'. A conformant implementation need not support
393 renaming directories in all situations (e.g. renaming to an existing
394 directory, or across different physical devices), but the constraints
397 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
400 The operation may fail with:
403 A physical I\/O error has occurred.
407 Either operand is not a valid directory name.
408 @[ENAMETOOLONG, ELOOP]@
410 * 'isDoesNotExistError' \/ 'NoSuchThing'
411 The original directory does not exist, or there is no path to the target.
414 * 'isPermissionError' \/ 'PermissionDenied'
415 The process has insufficient privileges to perform the operation.
416 @[EROFS, EACCES, EPERM]@
418 * 'ResourceExhausted'
419 Insufficient resources are available to perform the operation.
420 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
422 * 'UnsatisfiedConstraints'
423 Implementation-dependent constraints are not satisfied.
424 @[EBUSY, ENOTEMPTY, EEXIST]@
426 * 'UnsupportedOperation'
427 The implementation does not support renaming in this situation.
430 * 'InappropriateType'
431 Either path refers to an existing non-directory object.
436 renameDirectory :: FilePath -> FilePath -> IO ()
437 renameDirectory opath npath =
438 withFileStatus "renameDirectory" opath $ \st -> do
439 is_dir <- isDirectory st
441 then ioException (IOError Nothing InappropriateType "renameDirectory"
442 ("not a directory") (Just opath))
445 withCString opath $ \s1 ->
446 withCString npath $ \s2 ->
447 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
449 {- |@'renameFile' old new@ changes the name of an existing file system
450 object from /old/ to /new/. If the /new/ object already
451 exists, it is atomically replaced by the /old/ object. Neither
452 path may refer to an existing directory. A conformant implementation
453 need not support renaming files in all situations (e.g. renaming
454 across different physical devices), but the constraints must be
457 The operation may fail with:
460 A physical I\/O error has occurred.
464 Either operand is not a valid file name.
465 @[ENAMETOOLONG, ELOOP]@
467 * 'isDoesNotExistError' \/ 'NoSuchThing'
468 The original file does not exist, or there is no path to the target.
471 * 'isPermissionError' \/ 'PermissionDenied'
472 The process has insufficient privileges to perform the operation.
473 @[EROFS, EACCES, EPERM]@
475 * 'ResourceExhausted'
476 Insufficient resources are available to perform the operation.
477 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
479 * 'UnsatisfiedConstraints'
480 Implementation-dependent constraints are not satisfied.
483 * 'UnsupportedOperation'
484 The implementation does not support renaming in this situation.
487 * 'InappropriateType'
488 Either path refers to an existing directory.
489 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
493 renameFile :: FilePath -> FilePath -> IO ()
494 renameFile opath npath =
495 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
496 is_dir <- isDirectory st
498 then ioException (IOError Nothing InappropriateType "renameFile"
499 "is a directory" (Just opath))
502 withCString opath $ \s1 ->
503 withCString npath $ \s2 ->
504 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
506 #endif /* __GLASGOW_HASKELL__ */
508 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
509 If the /new/ file already exists, it is atomically replaced by the /old/ file.
510 Neither path may refer to an existing directory. The permissions of /old/ are
511 copied to /new/, if possible.
516 It's tempting to try to remove the target file before opening it for
517 writing. This could be useful: for example if the target file is an
518 executable that is in use, writing will fail, but unlinking first
521 However, it certainly isn't always what you want.
523 * if the target file is hardlinked, removing it would break
524 the hard link, but just opening would preserve it.
526 * opening and truncating will preserve permissions and
529 * If the destination file is read-only in a writable directory,
530 we might want copyFile to fail. Removing the target first
531 would succeed, however.
533 * If the destination file is special (eg. /dev/null), removing
534 it is probably not the right thing. Copying to /dev/null
535 should leave /dev/null intact, not replace it with a plain
538 * There's a small race condition between removing the target and
539 opening it for writing during which time someone might
542 copyFile :: FilePath -> FilePath -> IO ()
543 copyFile fromFPath toFPath =
544 #if (!(defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ > 600))
545 do readFile fromFPath >>= writeFile toFPath
546 try (copyPermissions fromFPath toFPath)
549 (bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
550 bracket (openBinaryFile toFPath WriteMode) hClose $ \hTo ->
551 allocaBytes bufferSize $ \buffer -> do
552 copyContents hFrom hTo buffer
553 try (copyPermissions fromFPath toFPath)
554 return ()) `catch` (ioError . changeFunName)
558 changeFunName (IOError h iot fun str mb_fp) = IOError h iot "copyFile" str mb_fp
560 copyContents hFrom hTo buffer = do
561 count <- hGetBuf hFrom buffer bufferSize
562 when (count > 0) $ do
563 hPutBuf hTo buffer count
564 copyContents hFrom hTo buffer
567 #ifdef __GLASGOW_HASKELL__
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
600 #else /* !__GLASGOW_HASKELL__ */
601 -- dummy implementation
602 canonicalizePath :: FilePath -> IO FilePath
603 canonicalizePath fpath = return fpath
604 #endif /* !__GLASGOW_HASKELL__ */
606 -- | Given an executable file name, searches for such file
607 -- in the directories listed in system PATH. The returned value
608 -- is the path to the found executable or Nothing if there isn't
609 -- such executable. For example (findExecutable \"ghc\")
610 -- gives you the path to GHC.
611 findExecutable :: String -> IO (Maybe FilePath)
612 findExecutable binary =
613 #if defined(mingw32_HOST_OS)
614 withCString binary $ \c_binary ->
615 withCString ('.':exeExtension) $ \c_ext ->
616 allocaBytes long_path_size $ \pOutPath ->
617 alloca $ \ppFilePart -> do
618 res <- c_SearchPath nullPtr c_binary c_ext (fromIntegral long_path_size) pOutPath ppFilePart
619 if res > 0 && res < fromIntegral long_path_size
620 then do fpath <- peekCString pOutPath
624 foreign import stdcall unsafe "SearchPathA"
625 c_SearchPath :: CString
632 # if !defined(__GLASGOW_HASKELL__)
633 long_path_size :: Int
634 long_path_size = 4096
638 path <- getEnv "PATH"
639 search (parseSearchPath path)
641 fileName = binary `joinFileExt` exeExtension
643 search :: [FilePath] -> IO (Maybe FilePath)
644 search [] = return Nothing
646 let path = d `joinFileName` fileName
647 b <- doesFileExist path
648 if b then return (Just path)
653 #ifdef __GLASGOW_HASKELL__
654 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
657 The operation may fail with:
660 A physical I\/O error has occurred.
664 The operand is not a valid directory name.
665 @[ENAMETOOLONG, ELOOP]@
667 * 'isDoesNotExistError' \/ 'NoSuchThing'
668 The directory does not exist.
671 * 'isPermissionError' \/ 'PermissionDenied'
672 The process has insufficient privileges to perform the operation.
675 * 'ResourceExhausted'
676 Insufficient resources are available to perform the operation.
679 * 'InappropriateType'
680 The path refers to an existing non-directory object.
685 getDirectoryContents :: FilePath -> IO [FilePath]
686 getDirectoryContents path = do
687 modifyIOError (`ioeSetFileName` path) $
688 alloca $ \ ptr_dEnt ->
690 (withCString path $ \s ->
691 throwErrnoIfNullRetry desc (c_opendir s))
692 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
693 (\p -> loop ptr_dEnt p)
695 desc = "getDirectoryContents"
697 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
698 loop ptr_dEnt dir = do
700 r <- readdir dir ptr_dEnt
703 dEnt <- peek ptr_dEnt
707 entry <- (d_name dEnt >>= peekCString)
709 entries <- loop ptr_dEnt dir
710 return (entry:entries)
711 else do errno <- getErrno
712 if (errno == eINTR) then loop ptr_dEnt dir else do
713 let (Errno eo) = errno
714 if (eo == end_of_dir)
720 {- |If the operating system has a notion of current directories,
721 'getCurrentDirectory' returns an absolute path to the
722 current directory of the calling process.
724 The operation may fail with:
727 A physical I\/O error has occurred.
730 * 'isDoesNotExistError' \/ 'NoSuchThing'
731 There is no path referring to the current directory.
732 @[EPERM, ENOENT, ESTALE...]@
734 * 'isPermissionError' \/ 'PermissionDenied'
735 The process has insufficient privileges to perform the operation.
738 * 'ResourceExhausted'
739 Insufficient resources are available to perform the operation.
741 * 'UnsupportedOperation'
742 The operating system has no notion of current directory.
746 getCurrentDirectory :: IO FilePath
747 getCurrentDirectory = do
748 p <- mallocBytes long_path_size
750 where go p bytes = do
751 p' <- c_getcwd p (fromIntegral bytes)
753 then do s <- peekCString p'
756 else do errno <- getErrno
758 then do let bytes' = bytes * 2
759 p' <- reallocBytes p bytes'
761 else throwErrno "getCurrentDirectory"
763 {- |If the operating system has a notion of current directories,
764 @'setCurrentDirectory' dir@ changes the current
765 directory of the calling process to /dir/.
767 The operation may fail with:
770 A physical I\/O error has occurred.
774 The operand is not a valid directory name.
775 @[ENAMETOOLONG, ELOOP]@
777 * 'isDoesNotExistError' \/ 'NoSuchThing'
778 The directory does not exist.
781 * 'isPermissionError' \/ 'PermissionDenied'
782 The process has insufficient privileges to perform the operation.
785 * 'UnsupportedOperation'
786 The operating system has no notion of current directory, or the
787 current directory cannot be dynamically changed.
789 * 'InappropriateType'
790 The path refers to an existing non-directory object.
795 setCurrentDirectory :: FilePath -> IO ()
796 setCurrentDirectory path = do
797 modifyIOError (`ioeSetFileName` path) $
798 withCString path $ \s ->
799 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
800 -- ToDo: add path to error
802 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
803 exists and is a directory, and 'False' otherwise.
806 doesDirectoryExist :: FilePath -> IO Bool
807 doesDirectoryExist name =
809 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
810 (\ _ -> return False)
812 {- |The operation 'doesFileExist' returns 'True'
813 if the argument file exists and is not a directory, and 'False' otherwise.
816 doesFileExist :: FilePath -> IO Bool
817 doesFileExist name = do
819 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
820 (\ _ -> return False)
822 {- |The 'getModificationTime' operation returns the
823 clock time at which the file or directory was last modified.
825 The operation may fail with:
827 * 'isPermissionError' if the user is not permitted to access
828 the modification time; or
830 * 'isDoesNotExistError' if the file or directory does not exist.
834 getModificationTime :: FilePath -> IO ClockTime
835 getModificationTime name =
836 withFileStatus "getModificationTime" name $ \ st ->
839 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
840 withFileStatus loc name f = do
841 modifyIOError (`ioeSetFileName` name) $
842 allocaBytes sizeof_stat $ \p ->
843 withCString (fileNameEndClean name) $ \s -> do
844 throwErrnoIfMinus1Retry_ loc (c_stat s p)
847 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
848 withFileOrSymlinkStatus loc name f = do
849 modifyIOError (`ioeSetFileName` name) $
850 allocaBytes sizeof_stat $ \p ->
851 withCString name $ \s -> do
852 throwErrnoIfMinus1Retry_ loc (lstat s p)
855 modificationTime :: Ptr CStat -> IO ClockTime
856 modificationTime stat = do
857 mtime <- st_mtime stat
858 let realToInteger = round . realToFrac :: Real a => a -> Integer
859 return (TOD (realToInteger (mtime :: CTime)) 0)
861 isDirectory :: Ptr CStat -> IO Bool
862 isDirectory stat = do
864 return (s_isdir mode)
866 fileNameEndClean :: String -> String
867 fileNameEndClean name =
868 if i > 0 && (ec == '\\' || ec == '/') then
869 fileNameEndClean (take i name)
873 i = (length name) - 1
876 foreign import ccall unsafe "__hscore_long_path_size"
877 long_path_size :: Int
879 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
880 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
881 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
883 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
884 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
885 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode
887 #endif /* __GLASGOW_HASKELL__ */
889 {- | Returns the current user's home directory.
891 The directory returned is expected to be writable by the current user,
892 but note that it isn't generally considered good practice to store
893 application-specific data here; use 'getAppUserDataDirectory'
896 On Unix, 'getHomeDirectory' returns the value of the @HOME@
897 environment variable. On Windows, the system is queried for a
898 suitable path; a typical path might be
899 @C:/Documents And Settings/user@.
901 The operation may fail with:
903 * 'UnsupportedOperation'
904 The operating system has no notion of home directory.
906 * 'isDoesNotExistError'
907 The home directory for the current user does not exist, or
910 getHomeDirectory :: IO FilePath
912 #if __GLASGOW_HASKELL__ && defined(mingw32_HOST_OS)
913 allocaBytes long_path_size $ \pPath -> do
914 r <- c_SHGetFolderPath nullPtr csidl_PROFILE nullPtr 0 pPath
917 r <- c_SHGetFolderPath nullPtr csidl_WINDOWS nullPtr 0 pPath
918 when (r < 0) (raiseUnsupported "System.Directory.getHomeDirectory")
925 {- | Returns the pathname of a directory in which application-specific
926 data for the current user can be stored. The result of
927 'getAppUserDataDirectory' for a given application is specific to
930 The argument should be the name of the application, which will be used
931 to construct the pathname (so avoid using unusual characters that
932 might result in an invalid pathname).
934 Note: the directory may not actually exist, and may need to be created
935 first. It is expected that the parent directory exists and is
938 On Unix, this function returns @$HOME\/.appName@. On Windows, a
939 typical path might be
941 > C:/Documents And Settings/user/Application Data/appName
943 The operation may fail with:
945 * 'UnsupportedOperation'
946 The operating system has no notion of application-specific data directory.
948 * 'isDoesNotExistError'
949 The home directory for the current user does not exist, or
952 getAppUserDataDirectory :: String -> IO FilePath
953 getAppUserDataDirectory appName = do
954 #if __GLASGOW_HASKELL__ && defined(mingw32_HOST_OS)
955 allocaBytes long_path_size $ \pPath -> do
956 r <- c_SHGetFolderPath nullPtr csidl_APPDATA nullPtr 0 pPath
957 when (r<0) (raiseUnsupported "System.Directory.getAppUserDataDirectory")
958 s <- peekCString pPath
959 return (s++'\\':appName)
961 path <- getEnv "HOME"
962 return (path++'/':'.':appName)
965 {- | Returns the current user's document directory.
967 The directory returned is expected to be writable by the current user,
968 but note that it isn't generally considered good practice to store
969 application-specific data here; use 'getAppUserDataDirectory'
972 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
973 environment variable. On Windows, the system is queried for a
974 suitable path; a typical path might be
975 @C:\/Documents and Settings\/user\/My Documents@.
977 The operation may fail with:
979 * 'UnsupportedOperation'
980 The operating system has no notion of document directory.
982 * 'isDoesNotExistError'
983 The document directory for the current user does not exist, or
986 getUserDocumentsDirectory :: IO FilePath
987 getUserDocumentsDirectory = do
988 #if __GLASGOW_HASKELL__ && defined(mingw32_HOST_OS)
989 allocaBytes long_path_size $ \pPath -> do
990 r <- c_SHGetFolderPath nullPtr csidl_PERSONAL nullPtr 0 pPath
991 when (r<0) (raiseUnsupported "System.Directory.getUserDocumentsDirectory")
997 {- | Returns the current directory for temporary files.
999 On Unix, 'getTemporaryDirectory' returns the value of the @TMPDIR@
1000 environment variable or \"\/tmp\" if the variable isn\'t defined.
1001 On Windows, the function checks for the existence of environment variables in
1002 the following order and uses the first path found:
1005 TMP environment variable.
1008 TEMP environment variable.
1011 USERPROFILE environment variable.
1014 The Windows directory
1016 The operation may fail with:
1018 * 'UnsupportedOperation'
1019 The operating system has no notion of temporary directory.
1021 The function doesn\'t verify whether the path exists.
1023 getTemporaryDirectory :: IO FilePath
1024 getTemporaryDirectory = do
1025 #if __GLASGOW_HASKELL__ && defined(mingw32_HOST_OS)
1026 allocaBytes long_path_size $ \pPath -> do
1027 r <- c_GetTempPath (fromIntegral long_path_size) pPath
1030 catch (getEnv "TMPDIR") (\ex -> return "/tmp")
1033 #if __GLASGOW_HASKELL__ && defined(mingw32_HOST_OS)
1034 foreign import ccall unsafe "__hscore_getFolderPath"
1035 c_SHGetFolderPath :: Ptr ()
1041 foreign import ccall unsafe "__hscore_CSIDL_PROFILE" csidl_PROFILE :: CInt
1042 foreign import ccall unsafe "__hscore_CSIDL_APPDATA" csidl_APPDATA :: CInt
1043 foreign import ccall unsafe "__hscore_CSIDL_WINDOWS" csidl_WINDOWS :: CInt
1044 foreign import ccall unsafe "__hscore_CSIDL_PERSONAL" csidl_PERSONAL :: CInt
1046 foreign import stdcall unsafe "GetTempPathA" c_GetTempPath :: CInt -> CString -> IO CInt
1048 raiseUnsupported loc =
1049 ioException (IOError Nothing UnsupportedOperation loc "unsupported operation" Nothing)