1{-# LANGUAGE ForeignFunctionInterface, NondecreasingIndentation #-}
2
3-----------------------------------------------------------------------------
4-- |
5-- Module      :  System.Directory
6-- Copyright   :  (c) The University of Glasgow 2001
7-- License     :  BSD-style (see the file libraries/base/LICENSE)
8--
9-- Maintainer  :  libraries@haskell.org
10-- Stability   :  stable
11-- Portability :  portable
12--
13-- System-independent interface to directory manipulation.
14--
15-----------------------------------------------------------------------------
16
17module System.Directory
18   (
19    -- $intro
20
21    -- * Actions on directories
22      createDirectory       -- :: FilePath -> IO ()
23    , createDirectoryIfMissing  -- :: Bool -> FilePath -> IO ()
24    , removeDirectory       -- :: FilePath -> IO ()
25    , removeDirectoryRecursive  -- :: FilePath -> IO ()
26    , renameDirectory       -- :: FilePath -> FilePath -> IO ()
27
28    , getDirectoryContents      -- :: FilePath -> IO [FilePath]
29    , getCurrentDirectory       -- :: IO FilePath
30    , setCurrentDirectory       -- :: FilePath -> IO ()
31
32    -- * Pre-defined directories
33    , getHomeDirectory
34    , getAppUserDataDirectory
35    , getUserDocumentsDirectory
36    , getTemporaryDirectory
37
38    -- * Actions on files
39    , removeFile        -- :: FilePath -> IO ()
40    , renameFile                -- :: FilePath -> FilePath -> IO ()
41    , copyFile                  -- :: FilePath -> FilePath -> IO ()
42
43    , canonicalizePath
44    , makeRelativeToCurrentDirectory
45    , findExecutable
46
47    -- * Existence tests
48    , doesFileExist     -- :: FilePath -> IO Bool
49    , doesDirectoryExist        -- :: FilePath -> IO Bool
50
51    -- * Permissions
52
53    -- $permissions
54
55    , Permissions(
56    Permissions,
57    readable,       -- :: Permissions -> Bool
58    writable,       -- :: Permissions -> Bool
59    executable,     -- :: Permissions -> Bool
60    searchable      -- :: Permissions -> Bool
61      )
62
63    , getPermissions            -- :: FilePath -> IO Permissions
64    , setPermissions            -- :: FilePath -> Permissions -> IO ()
65    , copyPermissions
66
67    -- * Timestamps
68
69    , getModificationTime       -- :: FilePath -> IO ClockTime
70   ) where
71
72import Prelude hiding ( catch )
73import qualified Prelude
74
75import Control.Monad (guard)
76import System.Environment      ( getEnv )
77import System.FilePath
78import System.IO
79import System.IO.Error hiding ( catch, try )
80import Control.Monad           ( when, unless )
81import Control.Exception.Base
82
83import Foreign
84import Foreign.C
85
86{-# CFILES cbits/directory.c #-}
87
88import System.Time             ( ClockTime(..) )
89
90import GHC.IO.Exception ( IOException(..), IOErrorType(..), ioException )
91
92import System.Posix.Types
93import System.Posix.Internals
94import qualified System.Win32 as Win32
95
96{- $intro
97A directory contains a series of entries, each of which is a named
98reference to a file system object (file, directory etc.).  Some
99entries may be hidden, inaccessible, or have some administrative
100function (e.g. `.' or `..' under POSIX
101<http://www.opengroup.org/onlinepubs/009695399/>), but in
102this standard all such entries are considered to form part of the
103directory contents. Entries in sub-directories are not, however,
104considered to form part of the directory contents.
105
106Each file system object is referenced by a /path/.  There is
107normally at least one absolute path to each file system object.  In
108some operating systems, it may also be possible to have paths which
109are relative to the current directory.
110-}
111
112-----------------------------------------------------------------------------
113-- Permissions
114
115{- $permissions
116
117 The 'Permissions' type is used to record whether certain operations are
118 permissible on a file\/directory. 'getPermissions' and 'setPermissions'
119 get and set these permissions, respectively. Permissions apply both to
120 files and directories. For directories, the executable field will be
121 'False', and for files the searchable field will be 'False'. Note that
122 directories may be searchable without being readable, if permission has
123 been given to use them as part of a path, but not to examine the
124 directory contents.
125
126Note that to change some, but not all permissions, a construct on the following lines must be used.
127
128>  makeReadable f = do
129>     p <- getPermissions f
130>     setPermissions f (p {readable = True})
131
132-}
133
134data Permissions
135 = Permissions {
136    readable,   writable,
137    executable, searchable :: Bool
138   } deriving (Eq, Ord, Read, Show)
139
140{- |The 'getPermissions' operation returns the
141permissions for the file or directory.
142
143The operation may fail with:
144
145* 'isPermissionError' if the user is not permitted to access
146  the permissions; or
147
148* 'isDoesNotExistError' if the file or directory does not exist.
149
150-}
151
152getPermissions :: FilePath -> IO Permissions
153getPermissions name = do
154  withFilePath name $ \s -> do
155  -- stat() does a better job of guessing the permissions on Windows
156  -- than access() does.  e.g. for execute permission, it looks at the
157  -- filename extension :-)
158  --
159  -- I tried for a while to do this properly, using the Windows security API,
160  -- and eventually gave up.  getPermissions is a flawed API anyway. -- SimonM
161  allocaBytes sizeof_stat $ \ p_stat -> do
162  throwErrnoIfMinus1_ "getPermissions" $ c_stat s p_stat
163  mode <- st_mode p_stat
164  let usr_read   = mode .&. s_IRUSR
165  let usr_write  = mode .&. s_IWUSR
166  let usr_exec   = mode .&. s_IXUSR
167  let is_dir = mode .&. s_IFDIR
168  return (
169    Permissions {
170      readable   = usr_read  /= 0,
171      writable   = usr_write /= 0,
172      executable = is_dir == 0 && usr_exec /= 0,
173      searchable = is_dir /= 0 && usr_exec /= 0
174    }
175   )
176
177{- |The 'setPermissions' operation sets the
178permissions for the file or directory.
179
180The operation may fail with:
181
182* 'isPermissionError' if the user is not permitted to set
183  the permissions; or
184
185* 'isDoesNotExistError' if the file or directory does not exist.
186
187-}
188
189setPermissions :: FilePath -> Permissions -> IO ()
190setPermissions name (Permissions r w e s) = do
191  allocaBytes sizeof_stat $ \ p_stat -> do
192  withFilePath name $ \p_name -> do
193    throwErrnoIfMinus1_ "setPermissions" $ do
194      c_stat p_name p_stat
195      mode <- st_mode p_stat
196      let mode1 = modifyBit r mode s_IRUSR
197      let mode2 = modifyBit w mode1 s_IWUSR
198      let mode3 = modifyBit (e || s) mode2 s_IXUSR
199      c_wchmod p_name mode3
200 where
201   modifyBit :: Bool -> CMode -> CMode -> CMode
202   modifyBit False m b = m .&. (complement b)
203   modifyBit True  m b = m .|. b
204
205foreign import ccall unsafe "_wchmod"
206   c_wchmod :: CWString -> CMode -> IO CInt
207
208copyPermissions :: FilePath -> FilePath -> IO ()
209copyPermissions source dest = do
210  allocaBytes sizeof_stat $ \ p_stat -> do
211  withFilePath source $ \p_source -> do
212  withFilePath dest $ \p_dest -> do
213    throwErrnoIfMinus1_ "copyPermissions" $ c_stat p_source p_stat
214    mode <- st_mode p_stat
215    throwErrnoIfMinus1_ "copyPermissions" $ c_wchmod p_dest mode
216
217-----------------------------------------------------------------------------
218-- Implementation
219
220{- |@'createDirectory' dir@ creates a new directory @dir@ which is
221initially empty, or as near to empty as the operating system
222allows.
223
224The operation may fail with:
225
226* 'isPermissionError' \/ 'PermissionDenied'
227The process has insufficient privileges to perform the operation.
228@[EROFS, EACCES]@
229
230* 'isAlreadyExistsError' \/ 'AlreadyExists'
231The operand refers to a directory that already exists.
232@ [EEXIST]@
233
234* 'HardwareFault'
235A physical I\/O error has occurred.
236@[EIO]@
237
238* 'InvalidArgument'
239The operand is not a valid directory name.
240@[ENAMETOOLONG, ELOOP]@
241
242* 'NoSuchThing'
243There is no path to the directory.
244@[ENOENT, ENOTDIR]@
245
246* 'ResourceExhausted'
247Insufficient resources (virtual memory, process file descriptors,
248physical disk space, etc.) are available to perform the operation.
249@[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
250
251* 'InappropriateType'
252The path refers to an existing non-directory object.
253@[EEXIST]@
254
255-}
256
257createDirectory :: FilePath -> IO ()
258createDirectory path = do
259  Win32.createDirectory path Nothing
260
261-- | @'createDirectoryIfMissing' parents dir@ creates a new directory
262-- @dir@ if it doesn\'t exist. If the first argument is 'True'
263-- the function will also create all parent directories if they are missing.
264createDirectoryIfMissing :: Bool     -- ^ Create its parents too?
265                 -> FilePath -- ^ The path to the directory you want to make
266                 -> IO ()
267createDirectoryIfMissing create_parents path0
268  | create_parents = createDirs (parents path0)
269  | otherwise      = createDirs (take 1 (parents path0))
270  where
271    parents = reverse . scanl1 (</>) . splitDirectories . normalise
272
273    createDirs []         = return ()
274    createDirs (dir:[])   = createDir dir throw
275    createDirs (dir:dirs) =
276      createDir dir $ \_ -> do
277        createDirs dirs
278        createDir dir throw
279
280    createDir :: FilePath -> (IOException -> IO ()) -> IO ()
281    createDir dir notExistHandler = do
282      r <- try $ createDirectory dir
283      case (r :: Either IOException ()) of
284        Right ()                   -> return ()
285        Left  e
286          | isDoesNotExistError  e -> notExistHandler e
287          -- createDirectory (and indeed POSIX mkdir) does not distinguish
288          -- between a dir already existing and a file already existing. So we
289          -- check for it here. Unfortunately there is a slight race condition
290          -- here, but we think it is benign. It could report an exeption in
291          -- the case that the dir did exist but another process deletes the
292          -- directory and creates a file in its place before we can check
293          -- that the directory did indeed exist.
294          | isAlreadyExistsError e -> (do
295              withFileStatus "createDirectoryIfMissing" dir $ \st -> do
296                 isDir <- isDirectory st
297                 if isDir then return ()
298                          else throw e
299              ) `catch` ((\_ -> return ()) :: IOException -> IO ())
300          | otherwise              -> throw e
301
302{- | @'removeDirectory' dir@ removes an existing directory /dir/.  The
303implementation may specify additional constraints which must be
304satisfied before a directory can be removed (e.g. the directory has to
305be empty, or may not be in use by other processes).  It is not legal
306for an implementation to partially remove a directory unless the
307entire directory is removed. A conformant implementation need not
308support directory removal in all situations (e.g. removal of the root
309directory).
310
311The operation may fail with:
312
313* 'HardwareFault'
314A physical I\/O error has occurred.
315EIO
316
317* 'InvalidArgument'
318The operand is not a valid directory name.
319[ENAMETOOLONG, ELOOP]
320
321* 'isDoesNotExistError' \/ 'NoSuchThing'
322The directory does not exist.
323@[ENOENT, ENOTDIR]@
324
325* 'isPermissionError' \/ 'PermissionDenied'
326The process has insufficient privileges to perform the operation.
327@[EROFS, EACCES, EPERM]@
328
329* 'UnsatisfiedConstraints'
330Implementation-dependent constraints are not satisfied.
331@[EBUSY, ENOTEMPTY, EEXIST]@
332
333* 'UnsupportedOperation'
334The implementation does not support removal in this situation.
335@[EINVAL]@
336
337* 'InappropriateType'
338The operand refers to an existing non-directory object.
339@[ENOTDIR]@
340
341-}
342
343removeDirectory :: FilePath -> IO ()
344removeDirectory path =
345  Win32.removeDirectory path
346
347-- | @'removeDirectoryRecursive' dir@  removes an existing directory /dir/
348-- together with its content and all subdirectories. Be careful,
349-- if the directory contains symlinks, the function will follow them.
350removeDirectoryRecursive :: FilePath -> IO ()
351removeDirectoryRecursive startLoc = do
352  cont <- getDirectoryContents startLoc
353  sequence_ [rm (startLoc </> x) | x <- cont, x /= "." && x /= ".."]
354  removeDirectory startLoc
355  where
356    rm :: FilePath -> IO ()
357    rm f = do temp <- try (removeFile f)
358              case temp of
359                Left e  -> do isDir <- doesDirectoryExist f
360                              -- If f is not a directory, re-throw the error
361                              unless isDir $ throw (e :: SomeException)
362                              removeDirectoryRecursive f
363                Right _ -> return ()
364
365{- |'removeFile' /file/ removes the directory entry for an existing file
366/file/, where /file/ is not itself a directory. The
367implementation may specify additional constraints which must be
368satisfied before a file can be removed (e.g. the file may not be in
369use by other processes).
370
371The operation may fail with:
372
373* 'HardwareFault'
374A physical I\/O error has occurred.
375@[EIO]@
376
377* 'InvalidArgument'
378The operand is not a valid file name.
379@[ENAMETOOLONG, ELOOP]@
380
381* 'isDoesNotExistError' \/ 'NoSuchThing'
382The file does not exist.
383@[ENOENT, ENOTDIR]@
384
385* 'isPermissionError' \/ 'PermissionDenied'
386The process has insufficient privileges to perform the operation.
387@[EROFS, EACCES, EPERM]@
388
389* 'UnsatisfiedConstraints'
390Implementation-dependent constraints are not satisfied.
391@[EBUSY]@
392
393* 'InappropriateType'
394The operand refers to an existing directory.
395@[EPERM, EINVAL]@
396
397-}
398
399removeFile :: FilePath -> IO ()
400removeFile path =
401  Win32.deleteFile path
402
403{- |@'renameDirectory' old new@ changes the name of an existing
404directory from /old/ to /new/.  If the /new/ directory
405already exists, it is atomically replaced by the /old/ directory.
406If the /new/ directory is neither the /old/ directory nor an
407alias of the /old/ directory, it is removed as if by
408'removeDirectory'.  A conformant implementation need not support
409renaming directories in all situations (e.g. renaming to an existing
410directory, or across different physical devices), but the constraints
411must be documented.
412
413On Win32 platforms, @renameDirectory@ fails if the /new/ directory already
414exists.
415
416The operation may fail with:
417
418* 'HardwareFault'
419A physical I\/O error has occurred.
420@[EIO]@
421
422* 'InvalidArgument'
423Either operand is not a valid directory name.
424@[ENAMETOOLONG, ELOOP]@
425
426* 'isDoesNotExistError' \/ 'NoSuchThing'
427The original directory does not exist, or there is no path to the target.
428@[ENOENT, ENOTDIR]@
429
430* 'isPermissionError' \/ 'PermissionDenied'
431The process has insufficient privileges to perform the operation.
432@[EROFS, EACCES, EPERM]@
433
434* 'ResourceExhausted'
435Insufficient resources are available to perform the operation.
436@[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
437
438* 'UnsatisfiedConstraints'
439Implementation-dependent constraints are not satisfied.
440@[EBUSY, ENOTEMPTY, EEXIST]@
441
442* 'UnsupportedOperation'
443The implementation does not support renaming in this situation.
444@[EINVAL, EXDEV]@
445
446* 'InappropriateType'
447Either path refers to an existing non-directory object.
448@[ENOTDIR, EISDIR]@
449
450-}
451
452renameDirectory :: FilePath -> FilePath -> IO ()
453renameDirectory opath npath = do
454   -- XXX this test isn't performed atomically with the following rename
455   -- ToDo: use Win32 API
456   withFileStatus "renameDirectory" opath $ \st -> do
457   is_dir <- isDirectory st
458   if (not is_dir)
459    then ioException (ioeSetErrorString
460                          (mkIOError InappropriateType "renameDirectory" Nothing (Just opath))
461                          "not a directory")
462    else do
463   Win32.moveFileEx opath npath Win32.mOVEFILE_REPLACE_EXISTING
464
465{- |@'renameFile' old new@ changes the name of an existing file system
466object from /old/ to /new/.  If the /new/ object already
467exists, it is atomically replaced by the /old/ object.  Neither
468path may refer to an existing directory.  A conformant implementation
469need not support renaming files in all situations (e.g. renaming
470across different physical devices), but the constraints must be
471documented.
472
473The operation may fail with:
474
475* 'HardwareFault'
476A physical I\/O error has occurred.
477@[EIO]@
478
479* 'InvalidArgument'
480Either operand is not a valid file name.
481@[ENAMETOOLONG, ELOOP]@
482
483* 'isDoesNotExistError' \/ 'NoSuchThing'
484The original file does not exist, or there is no path to the target.
485@[ENOENT, ENOTDIR]@
486
487* 'isPermissionError' \/ 'PermissionDenied'
488The process has insufficient privileges to perform the operation.
489@[EROFS, EACCES, EPERM]@
490
491* 'ResourceExhausted'
492Insufficient resources are available to perform the operation.
493@[EDQUOT, ENOSPC, ENOMEM, EMLINK]@
494
495* 'UnsatisfiedConstraints'
496Implementation-dependent constraints are not satisfied.
497@[EBUSY]@
498
499* 'UnsupportedOperation'
500The implementation does not support renaming in this situation.
501@[EXDEV]@
502
503* 'InappropriateType'
504Either path refers to an existing directory.
505@[ENOTDIR, EISDIR, EINVAL, EEXIST, ENOTEMPTY]@
506
507-}
508
509renameFile :: FilePath -> FilePath -> IO ()
510renameFile opath npath = do
511   -- XXX this test isn't performed atomically with the following rename
512   -- ToDo: use Win32 API
513   withFileOrSymlinkStatus "renameFile" opath $ \st -> do
514   is_dir <- isDirectory st
515   if is_dir
516    then ioException (ioeSetErrorString
517              (mkIOError InappropriateType "renameFile" Nothing (Just opath))
518              "is a directory")
519    else do
520   Win32.moveFileEx opath npath Win32.mOVEFILE_REPLACE_EXISTING
521
522{- |@'copyFile' old new@ copies the existing file from /old/ to /new/.
523If the /new/ file already exists, it is atomically replaced by the /old/ file.
524Neither path may refer to an existing directory.  The permissions of /old/ are
525copied to /new/, if possible.
526-}
527
528copyFile :: FilePath -> FilePath -> IO ()
529copyFile fromFPath toFPath =
530    copy `Prelude.catch` (\exc -> throw $ ioeSetLocation exc "copyFile")
531    where copy = bracket (openBinaryFile fromFPath ReadMode) hClose $ \hFrom ->
532                 bracketOnError openTmp cleanTmp $ \(tmpFPath, hTmp) ->
533                 do allocaBytes bufferSize $ copyContents hFrom hTmp
534                    hClose hTmp
535                    ignoreIOExceptions $ copyPermissions fromFPath tmpFPath
536                    renameFile tmpFPath toFPath
537          openTmp = openBinaryTempFile (takeDirectory toFPath) ".copyFile.tmp"
538          cleanTmp (tmpFPath, hTmp)
539              = do ignoreIOExceptions $ hClose hTmp
540                   ignoreIOExceptions $ removeFile tmpFPath
541          bufferSize = 1024
542
543          copyContents hFrom hTo buffer = do
544                  count <- hGetBuf hFrom buffer bufferSize
545                  when (count > 0) $ do
546                          hPutBuf hTo buffer count
547                          copyContents hFrom hTo buffer
548
549          ignoreIOExceptions io = io `catch` ioExceptionIgnorer
550          ioExceptionIgnorer :: IOException -> IO ()
551          ioExceptionIgnorer _ = return ()
552
553-- | Given path referring to a file or directory, returns a
554-- canonicalized path, with the intent that two paths referring
555-- to the same file\/directory will map to the same canonicalized
556-- path. Note that it is impossible to guarantee that the
557-- implication (same file\/dir \<=\> same canonicalizedPath) holds
558-- in either direction: this function can make only a best-effort
559-- attempt.
560canonicalizePath :: FilePath -> IO FilePath
561canonicalizePath fpath =
562    do path <- Win32.getFullPathName fpath
563       return (normalise path)
564        -- normalise does more stuff, like upper-casing the drive letter
565
566
567-- | 'makeRelative' the current directory.
568makeRelativeToCurrentDirectory :: FilePath -> IO FilePath
569makeRelativeToCurrentDirectory x = do
570    cur <- getCurrentDirectory
571    return $ makeRelative cur x
572
573-- | Given an executable file name, searches for such file in the
574-- directories listed in system PATH. The returned value is the path
575-- to the found executable or Nothing if an executable with the given
576-- name was not found. For example (findExecutable \"ghc\") gives you
577-- the path to GHC.
578--
579-- The path returned by 'findExecutable' corresponds to the
580-- program that would be executed by 'System.Process.createProcess'
581-- when passed the same string (as a RawCommand, not a ShellCommand).
582--
583-- On Windows, 'findExecutable' calls the Win32 function 'SearchPath',
584-- which may search other places before checking the directories in
585-- @PATH@.  Where it actually searches depends on registry settings,
586-- but notably includes the directory containing the current
587-- executable. See
588-- <http://msdn.microsoft.com/en-us/library/aa365527.aspx> for more
589-- details.
590--
591findExecutable :: String -> IO (Maybe FilePath)
592findExecutable binary =
593  Win32.searchPath Nothing binary ('.':exeExtension)
594
595
596{- |@'getDirectoryContents' dir@ returns a list of /all/ entries
597in /dir/.
598
599The operation may fail with:
600
601* 'HardwareFault'
602A physical I\/O error has occurred.
603@[EIO]@
604
605* 'InvalidArgument'
606The operand is not a valid directory name.
607@[ENAMETOOLONG, ELOOP]@
608
609* 'isDoesNotExistError' \/ 'NoSuchThing'
610The directory does not exist.
611@[ENOENT, ENOTDIR]@
612
613* 'isPermissionError' \/ 'PermissionDenied'
614The process has insufficient privileges to perform the operation.
615@[EACCES]@
616
617* 'ResourceExhausted'
618Insufficient resources are available to perform the operation.
619@[EMFILE, ENFILE]@
620
621* 'InappropriateType'
622The path refers to an existing non-directory object.
623@[ENOTDIR]@
624
625-}
626
627getDirectoryContents :: FilePath -> IO [FilePath]
628getDirectoryContents path =
629  modifyIOError ((`ioeSetFileName` path) .
630                 (`ioeSetLocation` "getDirectoryContents")) $ do
631  bracket
632     (Win32.findFirstFile (path </> "*"))
633     (\(h,_) -> Win32.findClose h)
634     (\(h,fdat) -> loop h fdat [])
635  where
636        -- we needn't worry about empty directories: adirectory always
637        -- has at least "." and ".." entries
638    loop :: Win32.HANDLE -> Win32.FindData -> [FilePath] -> IO [FilePath]
639    loop h fdat acc = do
640       filename <- Win32.getFindDataFileName fdat
641       more <- Win32.findNextFile h fdat
642       if more
643          then loop h fdat (filename:acc)
644          else return (filename:acc)
645                 -- no need to reverse, ordering is undefined
646
647{- |If the operating system has a notion of current directories,
648'getCurrentDirectory' returns an absolute path to the
649current directory of the calling process.
650
651The operation may fail with:
652
653* 'HardwareFault'
654A physical I\/O error has occurred.
655@[EIO]@
656
657* 'isDoesNotExistError' \/ 'NoSuchThing'
658There is no path referring to the current directory.
659@[EPERM, ENOENT, ESTALE...]@
660
661* 'isPermissionError' \/ 'PermissionDenied'
662The process has insufficient privileges to perform the operation.
663@[EACCES]@
664
665* 'ResourceExhausted'
666Insufficient resources are available to perform the operation.
667
668* 'UnsupportedOperation'
669The operating system has no notion of current directory.
670
671-}
672getCurrentDirectory :: IO FilePath
673getCurrentDirectory = do
674  Win32.getCurrentDirectory
675
676{- |If the operating system has a notion of current directories,
677@'setCurrentDirectory' dir@ changes the current
678directory of the calling process to /dir/.
679
680The operation may fail with:
681
682* 'HardwareFault'
683A physical I\/O error has occurred.
684@[EIO]@
685
686* 'InvalidArgument'
687The operand is not a valid directory name.
688@[ENAMETOOLONG, ELOOP]@
689
690* 'isDoesNotExistError' \/ 'NoSuchThing'
691The directory does not exist.
692@[ENOENT, ENOTDIR]@
693
694* 'isPermissionError' \/ 'PermissionDenied'
695The process has insufficient privileges to perform the operation.
696@[EACCES]@
697
698* 'UnsupportedOperation'
699The operating system has no notion of current directory, or the
700current directory cannot be dynamically changed.
701
702* 'InappropriateType'
703The path refers to an existing non-directory object.
704@[ENOTDIR]@
705
706-}
707
708setCurrentDirectory :: FilePath -> IO ()
709setCurrentDirectory path =
710  Win32.setCurrentDirectory path
711
712{- |The operation 'doesDirectoryExist' returns 'True' if the argument file
713exists and is a directory, and 'False' otherwise.
714-}
715
716doesDirectoryExist :: FilePath -> IO Bool
717doesDirectoryExist name =
718   (withFileStatus "doesDirectoryExist" name $ \st -> isDirectory st)
719   `catch` ((\ _ -> return False) :: IOException -> IO Bool)
720
721{- |The operation 'doesFileExist' returns 'True'
722if the argument file exists and is not a directory, and 'False' otherwise.
723-}
724
725doesFileExist :: FilePath -> IO Bool
726doesFileExist name =
727   (withFileStatus "doesFileExist" name $ \st -> do b <- isDirectory st; return (not b))
728   `catch` ((\ _ -> return False) :: IOException -> IO Bool)
729
730{- |The 'getModificationTime' operation returns the
731clock time at which the file or directory was last modified.
732
733The operation may fail with:
734
735* 'isPermissionError' if the user is not permitted to access
736  the modification time; or
737
738* 'isDoesNotExistError' if the file or directory does not exist.
739
740-}
741
742getModificationTime :: FilePath -> IO ClockTime
743getModificationTime name = do
744 -- ToDo: use Win32 API
745 withFileStatus "getModificationTime" name $ \ st -> do
746 modificationTime st
747
748
749withFileStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
750withFileStatus loc name f = do
751  modifyIOError (`ioeSetFileName` name) $
752    allocaBytes sizeof_stat $ \p ->
753      withFilePath (fileNameEndClean name) $ \s -> do
754        throwErrnoIfMinus1Retry_ loc (c_stat s p)
755
756withFileOrSymlinkStatus :: String -> FilePath -> (Ptr CStat -> IO a) -> IO a
757withFileOrSymlinkStatus loc name f = do
758  modifyIOError (`ioeSetFileName` name) $
759    allocaBytes sizeof_stat $ \p ->
760      withFilePath name $ \s -> do
761        throwErrnoIfMinus1Retry_ loc (lstat s p)
762
763modificationTime :: Ptr CStat -> IO ClockTime
764modificationTime stat = do
765    mtime <- st_mtime stat
766    let realToInteger = round . realToFrac :: Real a => a -> Integer
767    return (TOD (realToInteger (mtime :: CTime)) 0)
768
769isDirectory :: Ptr CStat -> IO Bool
770isDirectory stat = do
771  mode <- st_mode stat
772  return (s_isdir mode)
773
774fileNameEndClean :: String -> String
775fileNameEndClean name = if isDrive name then addTrailingPathSeparator name
776                                        else dropTrailingPathSeparator name
777
778foreign import ccall unsafe "HsDirectory.h __hscore_S_IRUSR" s_IRUSR :: CMode
779foreign import ccall unsafe "HsDirectory.h __hscore_S_IWUSR" s_IWUSR :: CMode
780foreign import ccall unsafe "HsDirectory.h __hscore_S_IXUSR" s_IXUSR :: CMode
781foreign import ccall unsafe "__hscore_S_IFDIR" s_IFDIR :: CMode
782
783
784foreign import ccall unsafe "__hscore_long_path_size"
785  long_path_size :: Int
786
787{- | Returns the current user's home directory.
788
789The directory returned is expected to be writable by the current user,
790but note that it isn't generally considered good practice to store
791application-specific data here; use 'getAppUserDataDirectory'
792instead.
793
794On Unix, 'getHomeDirectory' returns the value of the @HOME@
795environment variable.  On Windows, the system is queried for a
796suitable path; a typical path might be
797@C:/Documents And Settings/user@.
798
799The operation may fail with:
800
801* 'UnsupportedOperation'
802The operating system has no notion of home directory.
803
804* 'isDoesNotExistError'
805The home directory for the current user does not exist, or
806cannot be found.
807-}
808getHomeDirectory :: IO FilePath
809getHomeDirectory =
810  modifyIOError ((`ioeSetLocation` "getHomeDirectory")) $ do
811  r <- try $ Win32.sHGetFolderPath nullPtr Win32.cSIDL_PROFILE nullPtr 0
812  case (r :: Either IOException String) of
813    Right s -> return s
814    Left  _ -> do
815      r1 <- try $ Win32.sHGetFolderPath nullPtr Win32.cSIDL_WINDOWS nullPtr 0
816      case r1 of
817        Right s -> return s
818        Left  e -> ioError (e :: IOException)
819
820{- | Returns the pathname of a directory in which application-specific
821data for the current user can be stored.  The result of
822'getAppUserDataDirectory' for a given application is specific to
823the current user.
824
825The argument should be the name of the application, which will be used
826to construct the pathname (so avoid using unusual characters that
827might result in an invalid pathname).
828
829Note: the directory may not actually exist, and may need to be created
830first.  It is expected that the parent directory exists and is
831writable.
832
833On Unix, this function returns @$HOME\/.appName@.  On Windows, a
834typical path might be
835
836> C:/Documents And Settings/user/Application Data/appName
837
838The operation may fail with:
839
840* 'UnsupportedOperation'
841The operating system has no notion of application-specific data directory.
842
843* 'isDoesNotExistError'
844The home directory for the current user does not exist, or
845cannot be found.
846-}
847getAppUserDataDirectory :: String -> IO FilePath
848getAppUserDataDirectory appName = do
849  modifyIOError ((`ioeSetLocation` "getAppUserDataDirectory")) $ do
850  s <- Win32.sHGetFolderPath nullPtr Win32.cSIDL_APPDATA nullPtr 0
851  return (s++'\\':appName)
852
853{- | Returns the current user's document directory.
854
855The directory returned is expected to be writable by the current user,
856but note that it isn't generally considered good practice to store
857application-specific data here; use 'getAppUserDataDirectory'
858instead.
859
860On Unix, 'getUserDocumentsDirectory' returns the value of the @HOME@
861environment variable.  On Windows, the system is queried for a
862suitable path; a typical path might be
863@C:\/Documents and Settings\/user\/My Documents@.
864
865The operation may fail with:
866
867* 'UnsupportedOperation'
868The operating system has no notion of document directory.
869
870* 'isDoesNotExistError'
871The document directory for the current user does not exist, or
872cannot be found.
873-}
874getUserDocumentsDirectory :: IO FilePath
875getUserDocumentsDirectory = do
876  modifyIOError ((`ioeSetLocation` "getUserDocumentsDirectory")) $ do
877  Win32.sHGetFolderPath nullPtr Win32.cSIDL_PERSONAL nullPtr 0
878
879{- | Returns the current directory for temporary files.
880
881On Unix, 'getTemporaryDirectory' returns the value of the @TMPDIR@
882environment variable or \"\/tmp\" if the variable isn\'t defined.
883On Windows, the function checks for the existence of environment variables in
884the following order and uses the first path found:
885
886*
887TMP environment variable.
888
889*
890TEMP environment variable.
891
892*
893USERPROFILE environment variable.
894
895*
896The Windows directory
897
898The operation may fail with:
899
900* 'UnsupportedOperation'
901The operating system has no notion of temporary directory.
902
903The function doesn\'t verify whether the path exists.
904-}
905getTemporaryDirectory :: IO FilePath
906getTemporaryDirectory = do
907  Win32.getTemporaryDirectory
908
909-- ToDo: This should be determined via autoconf (AC_EXEEXT)
910-- | Extension for executable files
911-- (typically @\"\"@ on Unix and @\"exe\"@ on Windows or OS\/2)
912exeExtension :: String
913exeExtension = "exe"
914