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