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.Environment ( getEnv )
70 import System.FilePath
71 import System.IO.Error
72 import Control.Monad ( when, unless )
85 {-# CFILES cbits/directory.c #-}
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) $ mkParents file
277 (_, False, _) -> createDirectory file
278 where mkParents = scanl1 (</>) . splitDirectories . normalise
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 </> 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)
560 = IOError h iot "copyFile" str mb_fp
562 copyContents hFrom hTo buffer = do
563 count <- hGetBuf hFrom buffer bufferSize
564 when (count > 0) $ do
565 hPutBuf hTo buffer count
566 copyContents hFrom hTo buffer
569 -- | Given path referring to a file or directory, returns a
570 -- canonicalized path, with the intent that two paths referring
571 -- to the same file\/directory will map to the same canonicalized
572 -- path. Note that it is impossible to guarantee that the
573 -- implication (same file\/dir \<=\> same canonicalizedPath) holds
574 -- in either direction: this function can make only a best-effort
576 canonicalizePath :: FilePath -> IO FilePath
577 canonicalizePath fpath =
578 withCString fpath $ \pInPath ->
579 allocaBytes long_path_size $ \pOutPath ->
580 #if defined(mingw32_HOST_OS)
581 alloca $ \ppFilePart ->
582 do c_GetFullPathName pInPath (fromIntegral long_path_size) pOutPath ppFilePart
584 do c_realpath pInPath pOutPath
588 #if defined(mingw32_HOST_OS)
589 foreign import stdcall unsafe "GetFullPathNameA"
590 c_GetFullPathName :: CString
596 foreign import ccall unsafe "realpath"
597 c_realpath :: CString
602 -- | 'makeRelative' the current directory.
603 makeRelativeToCurrentDirectory :: FilePath -> IO FilePath
604 makeRelativeToCurrentDirectory x = do
605 cur <- getCurrentDirectory
606 return $ makeRelative cur x
608 -- | Given an executable file name, searches for such file
609 -- in the directories listed in system PATH. The returned value
610 -- is the path to the found executable or Nothing if there isn't
611 -- such executable. For example (findExecutable \"ghc\")
612 -- gives you the path to GHC.
613 findExecutable :: String -> IO (Maybe FilePath)
614 findExecutable binary =
615 #if defined(mingw32_HOST_OS)
616 withCString binary $ \c_binary ->
617 withCString ('.':exeExtension) $ \c_ext ->
618 allocaBytes long_path_size $ \pOutPath ->
619 alloca $ \ppFilePart -> do
620 res <- c_SearchPath nullPtr c_binary c_ext (fromIntegral long_path_size) pOutPath ppFilePart
621 if res > 0 && res < fromIntegral long_path_size
622 then do fpath <- peekCString pOutPath
626 foreign import stdcall unsafe "SearchPathA"
627 c_SearchPath :: CString
636 path <- getEnv "PATH"
637 search (splitSearchPath path)
639 fileName = binary <.> exeExtension
641 search :: [FilePath] -> IO (Maybe FilePath)
642 search [] = return Nothing
644 let path = d </> fileName
645 b <- doesFileExist path
646 if b then return (Just path)
651 #ifdef __GLASGOW_HASKELL__
652 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
655 The operation may fail with:
658 A physical I\/O error has occurred.
662 The operand is not a valid directory name.
663 @[ENAMETOOLONG, ELOOP]@
665 * 'isDoesNotExistError' \/ 'NoSuchThing'
666 The directory does not exist.
669 * 'isPermissionError' \/ 'PermissionDenied'
670 The process has insufficient privileges to perform the operation.
673 * 'ResourceExhausted'
674 Insufficient resources are available to perform the operation.
677 * 'InappropriateType'
678 The path refers to an existing non-directory object.
683 getDirectoryContents :: FilePath -> IO [FilePath]
684 getDirectoryContents path = do
685 modifyIOError (`ioeSetFileName` path) $
686 alloca $ \ ptr_dEnt ->
688 (withCString path $ \s ->
689 throwErrnoIfNullRetry desc (c_opendir s))
690 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
691 (\p -> loop ptr_dEnt p)
693 desc = "getDirectoryContents"
695 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
696 loop ptr_dEnt dir = do
698 r <- readdir dir ptr_dEnt
701 dEnt <- peek ptr_dEnt
705 entry <- (d_name dEnt >>= peekCString)
707 entries <- loop ptr_dEnt dir
708 return (entry:entries)
709 else do errno <- getErrno
710 if (errno == eINTR) then loop ptr_dEnt dir else do
711 let (Errno eo) = errno
712 if (eo == end_of_dir)
718 {- |If the operating system has a notion of current directories,
719 'getCurrentDirectory' returns an absolute path to the
720 current directory of the calling process.
722 The operation may fail with:
725 A physical I\/O error has occurred.
728 * 'isDoesNotExistError' \/ 'NoSuchThing'
729 There is no path referring to the current directory.
730 @[EPERM, ENOENT, ESTALE...]@
732 * 'isPermissionError' \/ 'PermissionDenied'
733 The process has insufficient privileges to perform the operation.
736 * 'ResourceExhausted'
737 Insufficient resources are available to perform the operation.
739 * 'UnsupportedOperation'
740 The operating system has no notion of current directory.
744 getCurrentDirectory :: IO FilePath
745 getCurrentDirectory = do
746 p <- mallocBytes long_path_size
748 where go p bytes = do
749 p' <- c_getcwd p (fromIntegral bytes)
751 then do s <- peekCString p'
754 else do errno <- getErrno
756 then do let bytes' = bytes * 2
757 p' <- reallocBytes p bytes'
759 else throwErrno "getCurrentDirectory"
761 {- |If the operating system has a notion of current directories,
762 @'setCurrentDirectory' dir@ changes the current
763 directory of the calling process to /dir/.
765 The operation may fail with:
768 A physical I\/O error has occurred.
772 The operand is not a valid directory name.
773 @[ENAMETOOLONG, ELOOP]@
775 * 'isDoesNotExistError' \/ 'NoSuchThing'
776 The directory does not exist.
779 * 'isPermissionError' \/ 'PermissionDenied'
780 The process has insufficient privileges to perform the operation.
783 * 'UnsupportedOperation'
784 The operating system has no notion of current directory, or the
785 current directory cannot be dynamically changed.
787 * 'InappropriateType'
788 The path refers to an existing non-directory object.
793 setCurrentDirectory :: FilePath -> IO ()
794 setCurrentDirectory path = do
795 modifyIOError (`ioeSetFileName` path) $
796 withCString path $ \s ->
797 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
798 -- ToDo: add path to error
800 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
801 exists and is a directory, and 'False' otherwise.
804 doesDirectoryExist :: FilePath -> IO Bool
805 doesDirectoryExist name =
807 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
808 (\ _ -> return False)
810 {- |The operation 'doesFileExist' returns 'True'
811 if the argument file exists and is not a directory, and 'False' otherwise.
814 doesFileExist :: FilePath -> IO Bool
815 doesFileExist name = do
817 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
818 (\ _ -> return False)
820 {- |The 'getModificationTime' operation returns the
821 clock time at which the file or directory was last modified.
823 The operation may fail with:
825 * 'isPermissionError' if the user is not permitted to access
826 the modification time; or
828 * 'isDoesNotExistError' if the file or directory does not exist.
832 getModificationTime :: FilePath -> IO ClockTime
833 getModificationTime name =
834 withFileStatus "getModificationTime" name $ \ st ->
837 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
838 withFileStatus loc name f = do
839 modifyIOError (`ioeSetFileName` name) $
840 allocaBytes sizeof_stat $ \p ->
841 withCString (fileNameEndClean name) $ \s -> do
842 throwErrnoIfMinus1Retry_ loc (c_stat s p)
845 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
846 withFileOrSymlinkStatus loc name f = do
847 modifyIOError (`ioeSetFileName` name) $
848 allocaBytes sizeof_stat $ \p ->
849 withCString name $ \s -> do
850 throwErrnoIfMinus1Retry_ loc (lstat s p)
853 modificationTime :: Ptr CStat -> IO ClockTime
854 modificationTime stat = do
855 mtime <- st_mtime stat
856 let realToInteger = round . realToFrac :: Real a => a -> Integer
857 return (TOD (realToInteger (mtime :: CTime)) 0)
859 isDirectory :: Ptr CStat -> IO Bool
860 isDirectory stat = do
862 return (s_isdir mode)
864 fileNameEndClean :: String -> String
865 fileNameEndClean name = if isDrive name then addTrailingPathSeparator name
866 else dropTrailingPathSeparator name
868 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CInt
869 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CInt
870 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CInt
872 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
873 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
874 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode
876 foreign import ccall unsafe "__hscore_long_path_size"
877 long_path_size :: Int
880 long_path_size :: Int
881 long_path_size = 2048 -- // guess?
883 #endif /* __GLASGOW_HASKELL__ */
885 {- | Returns the current user's home directory.
887 The directory returned is expected to be writable by the current user,
888 but note that it isn't generally considered good practice to store
889 application-specific data here; use 'getAppUserDataDirectory'
892 On Unix, 'getHomeDirectory' returns the value of the @HOME@
893 environment variable. On Windows, the system is queried for a
894 suitable path; a typical path might be
895 @C:/Documents And Settings/user@.
897 The operation may fail with:
899 * 'UnsupportedOperation'
900 The operating system has no notion of home directory.
902 * 'isDoesNotExistError'
903 The home directory for the current user does not exist, or
906 getHomeDirectory :: IO FilePath
908 #if defined(mingw32_HOST_OS)
909 allocaBytes long_path_size $ \pPath -> do
910 r <- c_SHGetFolderPath nullPtr csidl_PROFILE nullPtr 0 pPath
913 r <- c_SHGetFolderPath nullPtr csidl_WINDOWS nullPtr 0 pPath
914 when (r < 0) (raiseUnsupported "System.Directory.getHomeDirectory")
921 {- | Returns the pathname of a directory in which application-specific
922 data for the current user can be stored. The result of
923 'getAppUserDataDirectory' for a given application is specific to
926 The argument should be the name of the application, which will be used
927 to construct the pathname (so avoid using unusual characters that
928 might result in an invalid pathname).
930 Note: the directory may not actually exist, and may need to be created
931 first. It is expected that the parent directory exists and is
934 On Unix, this function returns @$HOME\/.appName@. On Windows, a
935 typical path might be
937 > C:/Documents And Settings/user/Application Data/appName
939 The operation may fail with:
941 * 'UnsupportedOperation'
942 The operating system has no notion of application-specific data directory.
944 * 'isDoesNotExistError'
945 The home directory for the current user does not exist, or
948 getAppUserDataDirectory :: String -> IO FilePath
949 getAppUserDataDirectory appName = do
950 #if defined(mingw32_HOST_OS)
951 allocaBytes long_path_size $ \pPath -> do
952 r <- c_SHGetFolderPath nullPtr csidl_APPDATA nullPtr 0 pPath
953 when (r<0) (raiseUnsupported "System.Directory.getAppUserDataDirectory")
954 s <- peekCString pPath
955 return (s++'\\':appName)
957 path <- getEnv "HOME"
958 return (path++'/':'.':appName)
961 {- | Returns the current user's document directory.
963 The directory returned is expected to be writable by the current user,
964 but note that it isn't generally considered good practice to store
965 application-specific data here; use 'getAppUserDataDirectory'
968 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
969 environment variable. On Windows, the system is queried for a
970 suitable path; a typical path might be
971 @C:\/Documents and Settings\/user\/My Documents@.
973 The operation may fail with:
975 * 'UnsupportedOperation'
976 The operating system has no notion of document directory.
978 * 'isDoesNotExistError'
979 The document directory for the current user does not exist, or
982 getUserDocumentsDirectory :: IO FilePath
983 getUserDocumentsDirectory = do
984 #if defined(mingw32_HOST_OS)
985 allocaBytes long_path_size $ \pPath -> do
986 r <- c_SHGetFolderPath nullPtr csidl_PERSONAL nullPtr 0 pPath
987 when (r<0) (raiseUnsupported "System.Directory.getUserDocumentsDirectory")
993 {- | Returns the current directory for temporary files.
995 On Unix, 'getTemporaryDirectory' returns the value of the @TMPDIR@
996 environment variable or \"\/tmp\" if the variable isn\'t defined.
997 On Windows, the function checks for the existence of environment variables in
998 the following order and uses the first path found:
1001 TMP environment variable.
1004 TEMP environment variable.
1007 USERPROFILE environment variable.
1010 The Windows directory
1012 The operation may fail with:
1014 * 'UnsupportedOperation'
1015 The operating system has no notion of temporary directory.
1017 The function doesn\'t verify whether the path exists.
1019 getTemporaryDirectory :: IO FilePath
1020 getTemporaryDirectory = do
1021 #if defined(mingw32_HOST_OS)
1022 allocaBytes long_path_size $ \pPath -> do
1023 r <- c_GetTempPath (fromIntegral long_path_size) pPath
1026 catch (getEnv "TMPDIR") (\ex -> return "/tmp")
1029 #if defined(mingw32_HOST_OS)
1030 foreign import ccall unsafe "__hscore_getFolderPath"
1031 c_SHGetFolderPath :: Ptr ()
1037 foreign import ccall unsafe "__hscore_CSIDL_PROFILE" csidl_PROFILE :: CInt
1038 foreign import ccall unsafe "__hscore_CSIDL_APPDATA" csidl_APPDATA :: CInt
1039 foreign import ccall unsafe "__hscore_CSIDL_WINDOWS" csidl_WINDOWS :: CInt
1040 foreign import ccall unsafe "__hscore_CSIDL_PERSONAL" csidl_PERSONAL :: CInt
1042 foreign import stdcall unsafe "GetTempPathA" c_GetTempPath :: CInt -> CString -> IO CInt
1044 raiseUnsupported loc =
1045 ioException (IOError Nothing UnsupportedOperation loc "unsupported operation" Nothing)
1049 -- ToDo: This should be determined via autoconf (AC_EXEEXT)
1050 -- | Extension for executable files
1051 -- (typically @\"\"@ on Unix and @\"exe\"@ on Windows or OS\/2)
1052 exeExtension :: String
1053 #ifdef mingw32_HOST_OS
1054 exeExtension = "exe"