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 , removeDirectory -- :: FilePath -> IO ()
22 , renameDirectory -- :: FilePath -> FilePath -> IO ()
24 , getDirectoryContents -- :: FilePath -> IO [FilePath]
25 , getCurrentDirectory -- :: IO FilePath
26 , setCurrentDirectory -- :: FilePath -> IO ()
28 -- * Pre-defined directories
30 , getAppUserDataDirectory
31 , getUserDocumentsDirectory
34 , removeFile -- :: FilePath -> IO ()
35 , renameFile -- :: FilePath -> FilePath -> IO ()
36 , copyFile -- :: FilePath -> FilePath -> IO ()
42 , doesFileExist -- :: FilePath -> IO Bool
43 , doesDirectoryExist -- :: FilePath -> IO Bool
51 readable, -- :: Permissions -> Bool
52 writable, -- :: Permissions -> Bool
53 executable, -- :: Permissions -> Bool
54 searchable -- :: Permissions -> Bool
57 , getPermissions -- :: FilePath -> IO Permissions
58 , setPermissions -- :: FilePath -> Permissions -> IO ()
62 , getModificationTime -- :: FilePath -> IO ClockTime
67 import System (getEnv)
68 import System.FilePath
77 #ifdef __GLASGOW_HASKELL__
80 import Control.Exception ( bracket )
81 import Control.Monad ( when )
82 import System.Posix.Types
83 import System.Posix.Internals
84 import System.Time ( ClockTime(..) )
86 import System.IO.Error
87 import System.FilePath
88 import System.Environment (getEnv)
92 import GHC.IOBase ( IOException(..), IOErrorType(..), ioException )
95 A directory contains a series of entries, each of which is a named
96 reference to a file system object (file, directory etc.). Some
97 entries may be hidden, inaccessible, or have some administrative
98 function (e.g. `.' or `..' under POSIX
99 <http://www.opengroup.org/onlinepubs/007904975/toc.htm>), but in
100 this standard all such entries are considered to form part of the
101 directory contents. Entries in sub-directories are not, however,
102 considered to form part of the directory contents.
104 Each file system object is referenced by a /path/. There is
105 normally at least one absolute path to each file system object. In
106 some operating systems, it may also be possible to have paths which
107 are relative to the current directory.
110 -----------------------------------------------------------------------------
115 The 'Permissions' type is used to record whether certain operations are
116 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
117 get and set these permissions, respectively. Permissions apply both to
118 files and directories. For directories, the executable field will be
119 'False', and for files the searchable field will be 'False'. Note that
120 directories may be searchable without being readable, if permission has
121 been given to use them as part of a path, but not to examine the
124 Note that to change some, but not all permissions, a construct on the following lines must be used.
126 > makeReadable f = do
127 > p <- getPermissions f
128 > setPermissions f (p {readable = True})
135 executable, searchable :: Bool
136 } deriving (Eq, Ord, Read, Show)
138 {- |The 'getPermissions' operation returns the
139 permissions for the file or directory.
141 The operation may fail with:
143 * 'isPermissionError' if the user is not permitted to access
146 * 'isDoesNotExistError' if the file or directory does not exist.
150 getPermissions :: FilePath -> IO Permissions
151 getPermissions name = do
152 withCString name $ \s -> do
153 read <- c_access s r_OK
154 write <- c_access s w_OK
155 exec <- c_access s x_OK
156 withFileStatus "getPermissions" name $ \st -> do
157 is_dir <- isDirectory st
160 readable = read == 0,
161 writable = write == 0,
162 executable = not is_dir && exec == 0,
163 searchable = is_dir && exec == 0
167 {- |The 'setPermissions' operation sets the
168 permissions for the file or directory.
170 The operation may fail with:
172 * 'isPermissionError' if the user is not permitted to set
175 * 'isDoesNotExistError' if the file or directory does not exist.
179 setPermissions :: FilePath -> Permissions -> IO ()
180 setPermissions name (Permissions r w e s) = do
181 allocaBytes sizeof_stat $ \ p_stat -> do
182 withCString name $ \p_name -> do
183 throwErrnoIfMinus1_ "setPermissions" $ do
185 mode <- st_mode p_stat
186 let mode1 = modifyBit r mode s_IRUSR
187 let mode2 = modifyBit w mode1 s_IWUSR
188 let mode3 = modifyBit (e || s) mode2 s_IXUSR
192 modifyBit :: Bool -> CMode -> CMode -> CMode
193 modifyBit False m b = m .&. (complement b)
194 modifyBit True m b = m .|. b
196 -----------------------------------------------------------------------------
199 {- |@'createDirectory' dir@ creates a new directory @dir@ which is
200 initially empty, or as near to empty as the operating system
203 The operation may fail with:
205 * 'isPermissionError' \/ 'PermissionDenied'
206 The process has insufficient privileges to perform the operation.
209 * 'isAlreadyExistsError' \/ 'AlreadyExists'
210 The operand refers to a directory that already exists.
214 A physical I\/O error has occurred.
218 The operand is not a valid directory name.
219 @[ENAMETOOLONG, ELOOP]@
222 There is no path to the directory.
225 * 'ResourceExhausted'
226 Insufficient resources (virtual memory, process file descriptors,
227 physical disk space, etc.) are available to perform the operation.
228 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
230 * 'InappropriateType'
231 The path refers to an existing non-directory object.
236 createDirectory :: FilePath -> IO ()
237 createDirectory path = do
238 withCString path $ \s -> do
239 throwErrnoIfMinus1Retry_ "createDirectory" $
242 {- | @'removeDirectory' dir@ removes an existing directory /dir/. The
243 implementation may specify additional constraints which must be
244 satisfied before a directory can be removed (e.g. the directory has to
245 be empty, or may not be in use by other processes). It is not legal
246 for an implementation to partially remove a directory unless the
247 entire directory is removed. A conformant implementation need not
248 support directory removal in all situations (e.g. removal of the root
251 The operation may fail with:
254 A physical I\/O error has occurred.
258 The operand is not a valid directory name.
259 [ENAMETOOLONG, ELOOP]
261 * 'isDoesNotExistError' \/ 'NoSuchThing'
262 The directory does not exist.
265 * 'isPermissionError' \/ 'PermissionDenied'
266 The process has insufficient privileges to perform the operation.
267 @[EROFS, EACCES, EPERM]@
269 * 'UnsatisfiedConstraints'
270 Implementation-dependent constraints are not satisfied.
271 @[EBUSY, ENOTEMPTY, EEXIST]@
273 * 'UnsupportedOperation'
274 The implementation does not support removal in this situation.
277 * 'InappropriateType'
278 The operand refers to an existing non-directory object.
283 removeDirectory :: FilePath -> IO ()
284 removeDirectory path = do
285 modifyIOError (`ioeSetFileName` path) $
286 withCString path $ \s ->
287 throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
289 {- |'removeFile' /file/ removes the directory entry for an existing file
290 /file/, where /file/ is not itself a directory. The
291 implementation may specify additional constraints which must be
292 satisfied before a file can be removed (e.g. the file may not be in
293 use by other processes).
295 The operation may fail with:
298 A physical I\/O error has occurred.
302 The operand is not a valid file name.
303 @[ENAMETOOLONG, ELOOP]@
305 * 'isDoesNotExistError' \/ 'NoSuchThing'
306 The file does not exist.
309 * 'isPermissionError' \/ 'PermissionDenied'
310 The process has insufficient privileges to perform the operation.
311 @[EROFS, EACCES, EPERM]@
313 * 'UnsatisfiedConstraints'
314 Implementation-dependent constraints are not satisfied.
317 * 'InappropriateType'
318 The operand refers to an existing directory.
323 removeFile :: FilePath -> IO ()
325 modifyIOError (`ioeSetFileName` path) $
326 withCString path $ \s ->
327 throwErrnoIfMinus1Retry_ "removeFile" (c_unlink s)
329 {- |@'renameDirectory' old new@ changes the name of an existing
330 directory from /old/ to /new/. If the /new/ directory
331 already exists, it is atomically replaced by the /old/ directory.
332 If the /new/ directory is neither the /old/ directory nor an
333 alias of the /old/ directory, it is removed as if by
334 'removeDirectory'. A conformant implementation need not support
335 renaming directories in all situations (e.g. renaming to an existing
336 directory, or across different physical devices), but the constraints
339 On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
342 The operation may fail with:
345 A physical I\/O error has occurred.
349 Either operand is not a valid directory name.
350 @[ENAMETOOLONG, ELOOP]@
352 * 'isDoesNotExistError' \/ 'NoSuchThing'
353 The original directory does not exist, or there is no path to the target.
356 * 'isPermissionError' \/ 'PermissionDenied'
357 The process has insufficient privileges to perform the operation.
358 @[EROFS, EACCES, EPERM]@
360 * 'ResourceExhausted'
361 Insufficient resources are available to perform the operation.
362 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
364 * 'UnsatisfiedConstraints'
365 Implementation-dependent constraints are not satisfied.
366 @[EBUSY, ENOTEMPTY, EEXIST]@
368 * 'UnsupportedOperation'
369 The implementation does not support renaming in this situation.
372 * 'InappropriateType'
373 Either path refers to an existing non-directory object.
378 renameDirectory :: FilePath -> FilePath -> IO ()
379 renameDirectory opath npath =
380 withFileStatus "renameDirectory" opath $ \st -> do
381 is_dir <- isDirectory st
383 then ioException (IOError Nothing InappropriateType "renameDirectory"
384 ("not a directory") (Just opath))
387 withCString opath $ \s1 ->
388 withCString npath $ \s2 ->
389 throwErrnoIfMinus1Retry_ "renameDirectory" (c_rename s1 s2)
391 {- |@'renameFile' old new@ changes the name of an existing file system
392 object from /old/ to /new/. If the /new/ object already
393 exists, it is atomically replaced by the /old/ object. Neither
394 path may refer to an existing directory. A conformant implementation
395 need not support renaming files in all situations (e.g. renaming
396 across different physical devices), but the constraints must be
399 The operation may fail with:
402 A physical I\/O error has occurred.
406 Either operand is not a valid file name.
407 @[ENAMETOOLONG, ELOOP]@
409 * 'isDoesNotExistError' \/ 'NoSuchThing'
410 The original file does not exist, or there is no path to the target.
413 * 'isPermissionError' \/ 'PermissionDenied'
414 The process has insufficient privileges to perform the operation.
415 @[EROFS, EACCES, EPERM]@
417 * 'ResourceExhausted'
418 Insufficient resources are available to perform the operation.
419 @[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
421 * 'UnsatisfiedConstraints'
422 Implementation-dependent constraints are not satisfied.
425 * 'UnsupportedOperation'
426 The implementation does not support renaming in this situation.
429 * 'InappropriateType'
430 Either path refers to an existing directory.
431 @[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
435 renameFile :: FilePath -> FilePath -> IO ()
436 renameFile opath npath =
437 withFileOrSymlinkStatus "renameFile" opath $ \st -> do
438 is_dir <- isDirectory st
440 then ioException (IOError Nothing InappropriateType "renameFile"
441 "is a directory" (Just opath))
444 withCString opath $ \s1 ->
445 withCString npath $ \s2 ->
446 throwErrnoIfMinus1Retry_ "renameFile" (c_rename s1 s2)
448 #endif /* __GLASGOW_HASKELL__ */
450 {- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
451 If the /new/ file already exists, it is atomically replaced by the /old/ file.
452 Neither path may refer to an existing directory.
454 copyFile :: FilePath -> FilePath -> IO ()
455 copyFile fromFPath toFPath =
456 #if (!(defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ > 600))
457 do readFile fromFPath >>= writeFile toFPath
458 try (getPermissions fromFPath >>= setPermissions toFPath)
461 (bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
462 bracket (openBinaryFile toFPath WriteMode) hClose $ \hTo ->
463 allocaBytes bufferSize $ \buffer -> do
464 copyContents hFrom hTo buffer
465 try (getPermissions fromFPath >>= setPermissions toFPath)
466 return ()) `catch` (ioError . changeFunName)
470 changeFunName (IOError h iot fun str mb_fp) = IOError h iot "copyFile" str mb_fp
472 copyContents hFrom hTo buffer = do
473 count <- hGetBuf hFrom buffer bufferSize
474 when (count > 0) $ do
475 hPutBuf hTo buffer count
476 copyContents hFrom hTo buffer
479 #ifdef __GLASGOW_HASKELL__
480 -- | Given path referring to a file or directory, returns a
481 -- canonicalized path, with the intent that two paths referring
482 -- to the same file\/directory will map to the same canonicalized
483 -- path. Note that it is impossible to guarantee that the
484 -- implication (same file\/dir <=> same canonicalizedPath) holds
485 -- in either direction: this function can make only a best-effort
487 canonicalizePath :: FilePath -> IO FilePath
488 canonicalizePath fpath =
489 withCString fpath $ \pInPath ->
490 allocaBytes long_path_size $ \pOutPath ->
491 #if defined(mingw32_TARGET_OS)
492 alloca $ \ppFilePart ->
493 do c_GetFullPathName pInPath (fromIntegral long_path_size) pOutPath ppFilePart
495 do c_realpath pInPath pOutPath
499 #if defined(mingw32_TARGET_OS)
500 foreign import stdcall unsafe "GetFullPathName"
501 c_GetFullPathName :: CString
507 foreign import ccall unsafe "realpath"
508 c_realpath :: CString
512 #else /* !__GLASGOW_HASKELL__ */
513 -- dummy implementation
514 canonicalizePath :: FilePath -> IO FilePath
515 canonicalizePath fpath = return fpath
516 #endif /* !__GLASGOW_HASKELL__ */
518 -- | Given an executable file name, searches for such file
519 -- in the directories listed in system PATH. The returned value
520 -- is the path to the found executable or Nothing if there isn't
521 -- such executable. For example (findExecutable \"ghc\")
522 -- gives you the path to GHC.
523 findExecutable :: String -> IO (Maybe FilePath)
524 findExecutable binary = do
525 path <- getEnv "PATH"
526 search (parseSearchPath path)
528 #ifdef mingw32_TARGET_OS
529 fileName = binary `joinFileExt` "exe"
534 search :: [FilePath] -> IO (Maybe FilePath)
535 search [] = return Nothing
537 let path = d `joinFileName` fileName
538 b <- doesFileExist path
539 if b then return (Just path)
542 #ifdef __GLASGOW_HASKELL__
543 {- |@'getDirectoryContents' dir@ returns a list of /all/ entries
546 The operation may fail with:
549 A physical I\/O error has occurred.
553 The operand is not a valid directory name.
554 @[ENAMETOOLONG, ELOOP]@
556 * 'isDoesNotExistError' \/ 'NoSuchThing'
557 The directory does not exist.
560 * 'isPermissionError' \/ 'PermissionDenied'
561 The process has insufficient privileges to perform the operation.
564 * 'ResourceExhausted'
565 Insufficient resources are available to perform the operation.
568 * 'InappropriateType'
569 The path refers to an existing non-directory object.
574 getDirectoryContents :: FilePath -> IO [FilePath]
575 getDirectoryContents path = do
576 modifyIOError (`ioeSetFileName` path) $
577 alloca $ \ ptr_dEnt ->
579 (withCString path $ \s ->
580 throwErrnoIfNullRetry desc (c_opendir s))
581 (\p -> throwErrnoIfMinus1_ desc (c_closedir p))
582 (\p -> loop ptr_dEnt p)
584 desc = "getDirectoryContents"
586 loop :: Ptr (Ptr CDirent) -> Ptr CDir -> IO [String]
587 loop ptr_dEnt dir = do
589 r <- readdir dir ptr_dEnt
592 dEnt <- peek ptr_dEnt
596 entry <- (d_name dEnt >>= peekCString)
598 entries <- loop ptr_dEnt dir
599 return (entry:entries)
600 else do errno <- getErrno
601 if (errno == eINTR) then loop ptr_dEnt dir else do
602 let (Errno eo) = errno
603 if (eo == end_of_dir)
609 {- |If the operating system has a notion of current directories,
610 'getCurrentDirectory' returns an absolute path to the
611 current directory of the calling process.
613 The operation may fail with:
616 A physical I\/O error has occurred.
619 * 'isDoesNotExistError' \/ 'NoSuchThing'
620 There is no path referring to the current directory.
621 @[EPERM, ENOENT, ESTALE...]@
623 * 'isPermissionError' \/ 'PermissionDenied'
624 The process has insufficient privileges to perform the operation.
627 * 'ResourceExhausted'
628 Insufficient resources are available to perform the operation.
630 * 'UnsupportedOperation'
631 The operating system has no notion of current directory.
635 getCurrentDirectory :: IO FilePath
636 getCurrentDirectory = do
637 p <- mallocBytes long_path_size
639 where go p bytes = do
640 p' <- c_getcwd p (fromIntegral bytes)
642 then do s <- peekCString p'
645 else do errno <- getErrno
647 then do let bytes' = bytes * 2
648 p' <- reallocBytes p bytes'
650 else throwErrno "getCurrentDirectory"
652 {- |If the operating system has a notion of current directories,
653 @'setCurrentDirectory' dir@ changes the current
654 directory of the calling process to /dir/.
656 The operation may fail with:
659 A physical I\/O error has occurred.
663 The operand is not a valid directory name.
664 @[ENAMETOOLONG, ELOOP]@
666 * 'isDoesNotExistError' \/ 'NoSuchThing'
667 The directory does not exist.
670 * 'isPermissionError' \/ 'PermissionDenied'
671 The process has insufficient privileges to perform the operation.
674 * 'UnsupportedOperation'
675 The operating system has no notion of current directory, or the
676 current directory cannot be dynamically changed.
678 * 'InappropriateType'
679 The path refers to an existing non-directory object.
684 setCurrentDirectory :: FilePath -> IO ()
685 setCurrentDirectory path = do
686 modifyIOError (`ioeSetFileName` path) $
687 withCString path $ \s ->
688 throwErrnoIfMinus1Retry_ "setCurrentDirectory" (c_chdir s)
689 -- ToDo: add path to error
691 {- |The operation 'doesDirectoryExist' returns 'True' if the argument file
692 exists and is a directory, and 'False' otherwise.
695 doesDirectoryExist :: FilePath -> IO Bool
696 doesDirectoryExist name =
698 (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
699 (\ _ -> return False)
701 {- |The operation 'doesFileExist' returns 'True'
702 if the argument file exists and is not a directory, and 'False' otherwise.
705 doesFileExist :: FilePath -> IO Bool
706 doesFileExist name = do
708 (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
709 (\ _ -> return False)
711 {- |The 'getModificationTime' operation returns the
712 clock time at which the file or directory was last modified.
714 The operation may fail with:
716 * 'isPermissionError' if the user is not permitted to access
717 the modification time; or
719 * 'isDoesNotExistError' if the file or directory does not exist.
723 getModificationTime :: FilePath -> IO ClockTime
724 getModificationTime name =
725 withFileStatus "getModificationTime" name $ \ st ->
728 withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
729 withFileStatus loc name f = do
730 modifyIOError (`ioeSetFileName` name) $
731 allocaBytes sizeof_stat $ \p ->
732 withCString (fileNameEndClean name) $ \s -> do
733 throwErrnoIfMinus1Retry_ loc (c_stat s p)
736 withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
737 withFileOrSymlinkStatus loc name f = do
738 modifyIOError (`ioeSetFileName` name) $
739 allocaBytes sizeof_stat $ \p ->
740 withCString name $ \s -> do
741 throwErrnoIfMinus1Retry_ loc (lstat s p)
744 modificationTime :: Ptr CStat -> IO ClockTime
745 modificationTime stat = do
746 mtime <- st_mtime stat
747 let realToInteger = round . realToFrac :: Real a => a -> Integer
748 return (TOD (realToInteger (mtime :: CTime)) 0)
750 isDirectory :: Ptr CStat -> IO Bool
751 isDirectory stat = do
753 return (s_isdir mode)
755 fileNameEndClean :: String -> String
756 fileNameEndClean name =
757 if i > 0 && (ec == '\\' || ec == '/') then
758 fileNameEndClean (take i name)
762 i = (length name) - 1
765 foreign import ccall unsafe "__hscore_long_path_size"
766 long_path_size :: Int
768 foreign import ccall unsafe "__hscore_R_OK" r_OK :: CMode
769 foreign import ccall unsafe "__hscore_W_OK" w_OK :: CMode
770 foreign import ccall unsafe "__hscore_X_OK" x_OK :: CMode
772 foreign import ccall unsafe "__hscore_S_IRUSR" s_IRUSR :: CMode
773 foreign import ccall unsafe "__hscore_S_IWUSR" s_IWUSR :: CMode
774 foreign import ccall unsafe "__hscore_S_IXUSR" s_IXUSR :: CMode
776 #endif /* __GLASGOW_HASKELL__ */
778 {- | Returns the current user's home directory.
780 The directory returned is expected to be writable by the current user,
781 but note that it isn't generally considered good practice to store
782 application-specific data here; use 'getAppUserDataDirectory'
785 On Unix, 'getHomeDirectory' returns the value of the @HOME@
786 environment variable. On Windows, the system is queried for a
787 suitable path; a typical path might be
788 @C:/Documents And Settings/user@.
790 The operation may fail with:
792 * 'UnsupportedOperation'
793 The operating system has no notion of home directory.
795 * 'isDoesNotExistError'
796 The home directory for the current user does not exist, or
799 getHomeDirectory :: IO FilePath
801 #if __GLASGOW_HASKELL__ && defined(mingw32_TARGET_OS)
802 allocaBytes long_path_size $ \pPath -> do
803 r <- c_SHGetFolderPath nullPtr csidl_PROFILE nullPtr 0 pPath
805 then c_SHGetFolderPath nullPtr csidl_WINDOWS nullPtr 0 pPath
812 {- | Returns the pathname of a directory in which application-specific
813 data for the current user can be stored. The result of
814 'getAppUserDataDirectory' for a given application is specific to
817 The argument should be the name of the application, which will be used
818 to construct the pathname (so avoid using unusual characters that
819 might result in an invalid pathname).
821 Note: the directory may not actually exist, and may need to be created
822 first. It is expected that the parent directory exists and is
825 On Unix, this function returns @$HOME\/.appName@. On Windows, a
826 typical path might be
828 > C:/Documents And Settings/user/Application Data/appName
830 The operation may fail with:
832 * 'UnsupportedOperation'
833 The operating system has no notion of application-specific data directory.
835 * 'isDoesNotExistError'
836 The home directory for the current user does not exist, or
839 getAppUserDataDirectory :: String -> IO FilePath
840 getAppUserDataDirectory appName = do
841 #if __GLASGOW_HASKELL__ && defined(mingw32_TARGET_OS)
842 allocaBytes long_path_size $ \pPath -> do
843 r <- c_SHGetFolderPath nullPtr csidl_APPDATA nullPtr 0 pPath
844 s <- peekCString pPath
845 return (s++'\\':appName)
847 path <- getEnv "HOME"
848 return (path++'/':'.':appName)
851 {- | Returns the current user's document directory.
853 The directory returned is expected to be writable by the current user,
854 but note that it isn't generally considered good practice to store
855 application-specific data here; use 'getAppUserDataDirectory'
858 On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
859 environment variable. On Windows, the system is queried for a
860 suitable path; a typical path might be
861 @C:\/Documents and Settings\/user\/My Documents@.
863 The operation may fail with:
865 * 'UnsupportedOperation'
866 The operating system has no notion of document directory.
868 * 'isDoesNotExistError'
869 The document directory for the current user does not exist, or
872 getUserDocumentsDirectory :: IO FilePath
873 getUserDocumentsDirectory = do
874 #if __GLASGOW_HASKELL__ && defined(mingw32_TARGET_OS)
875 allocaBytes long_path_size $ \pPath -> do
876 r <- c_SHGetFolderPath nullPtr csidl_PERSONAL nullPtr 0 pPath
882 #if __GLASGOW_HASKELL__ && defined(mingw32_TARGET_OS)
883 foreign import stdcall unsafe "SHGetFolderPath"
884 c_SHGetFolderPath :: Ptr ()
890 foreign import ccall unsafe "__hscore_CSIDL_PROFILE" csidl_PROFILE :: CInt
891 foreign import ccall unsafe "__hscore_CSIDL_APPDATA" csidl_APPDATA :: CInt
892 foreign import ccall unsafe "__hscore_CSIDL_WINDOWS" csidl_WINDOWS :: CInt
893 foreign import ccall unsafe "__hscore_CSIDL_PERSONAL" csidl_PERSONAL :: CInt