packages feed

system-fileio 0.2.7 → 0.3

raw patch · 17 files changed

+511/−2177 lines, 17 filesdep ~Win32dep ~basedep ~system-filepath

Dependency ranges changed: Win32, base, system-filepath, text, time, unix

Files

+ Filesystem.hs view
@@ -0,0 +1,494 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE PackageImports #-}++-- |+-- Module: Filesystem+-- Copyright: 2011 John Millikin+-- License: MIT+--+-- Maintainer: jmillikin@gmail.com+-- Portability: portable+--+-- Simple 'FilePath'&#x2010;aware wrappers around standard "System.IO"+-- computations. See the linked documentation for each computation for+-- details on exceptions and operating system interaction.+module Filesystem+	( IO.Handle+	, IO.IOMode(..)+	, rename+	+	-- * Files+	, isFile+	, getModified+	, getSize+	, copyFile+	, removeFile+	+	-- ** Binary files+	, openFile+	, withFile+	, readFile+	, writeFile+	, appendFile+	+	-- ** Text files+	, openTextFile+	, withTextFile+	, readTextFile+	, writeTextFile+	, appendTextFile+	+	-- * Directories+	, isDirectory+	, canonicalizePath+	, listDirectory+	+	-- ** Creating+	, createDirectory+	, createTree+	+	-- ** Removing+	, removeDirectory+	, removeTree+	+	-- ** Current working directory+	, getWorkingDirectory+	, setWorkingDirectory+	+	-- ** Commonly used paths+	, getHomeDirectory+	, getDesktopDirectory+	, getDocumentsDirectory+	, getAppDataDirectory+	, getAppCacheDirectory+	, getAppConfigDirectory+	) where++import           Prelude hiding (FilePath, readFile, writeFile, appendFile)++import qualified Control.Exception as Exc+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as B8+import qualified Data.Text as T+import qualified Data.Text.IO as T+import           Foreign.Ptr (nullPtr)+import           Foreign.C (CString, withCString, peekCString)+import qualified System.Environment as SE+import           System.FilePath (FilePath, append)+import           System.FilePath.CurrentOS (currentOS)+import qualified System.FilePath.Rules as R+import qualified System.IO as IO+import           System.IO.Error (isDoesNotExistError)++#ifdef CABAL_OS_WINDOWS++import           Data.Bits ((.|.))+import           Data.Time ( UTCTime(..)+                           , fromGregorian+                           , secondsToDiffTime+                           , picosecondsToDiffTime)+import qualified System.Win32 as Win32++#else++import           Data.Time (UTCTime)+import           Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import qualified System.Posix as Posix+import qualified System.Posix.Error as Posix++#endif++-- hopefully temporary+import qualified "directory" System.Directory as SD++-- | Check if a file exists at the given path.+--+-- See: 'SD.doesFileExist'+isFile :: FilePath -> IO Bool+isFile path = SD.doesFileExist (encodeString path)++-- | Check if a directory exists at the given path.+--+-- See: 'SD.doesDirectoryExist'+isDirectory :: FilePath -> IO Bool+isDirectory path = SD.doesDirectoryExist (encodeString path)++-- | Rename a filesystem object. Some operating systems have restrictions+-- on what objects can be renamed; see linked documentation for details.+--+-- See: 'SD.renameFile' and 'SD.renameDirectory'+rename :: FilePath -> FilePath -> IO ()+rename old new =+	let old' = encodeString old in+	let new' = encodeString new in+#ifdef CABAL_OS_WINDOWS+	Win32.moveFileEx old' new' Win32.mOVEFILE_REPLACE_EXISTING+#else+	Posix.rename old' new'+#endif++-- Resolve symlinks and \"..\" path elements to return a canonical path.+-- It is intended that two paths referring to the same object will always+-- resolve to the same canonical path.+--+-- Note that on many operating systems, it is impossible to guarantee that+-- two paths to the same file will resolve to the same canonical path.+--+-- See: 'SD.canonicalizePath'+--+-- Since: 0.1.1+canonicalizePath :: FilePath -> IO FilePath+canonicalizePath path =+	let path' = encodeString path in+#ifdef CABAL_OS_WINDOWS+	fmap decodeString (Win32.getFullPathName path')+#else+	withCString path' $ \cPath -> do+		cOut <- Posix.throwErrnoPathIfNull "canonicalizePath" path' (c_realpath cPath nullPtr)+		out <- peekCString cOut+		return (decodeString out)+#endif++#ifndef CABAL_OS_WINDOWS+foreign import ccall unsafe "realpath"+	c_realpath :: CString -> CString -> IO CString+#endif++-- | Create a directory at a given path. The user may choose whether it is+-- an error for a directory to already exist at that path.+--+-- See: 'SD.createDirectory'.+createDirectory :: Bool -- ^ Succeed if the directory already exists+                -> FilePath -> IO ()+createDirectory False path =+	let path' = encodeString path in+#ifdef CABAL_OS_WINDOWS+	Win32.createDirectory path' Nothing+#else+	Posix.createDirectory path' 0o777+#endif+createDirectory True path = SD.createDirectoryIfMissing False (encodeString path)++-- | Create a directory at a given path, including any parents which might+-- be missing.+--+-- See: 'SD.createDirectoryIfMissing'+createTree :: FilePath -> IO ()+createTree path = SD.createDirectoryIfMissing True (encodeString path)++-- | List contents of a directory, excluding @\".\"@ and @\"..\"@. Each+-- returned 'FilePath' includes the path of the directory.+--+-- See: 'SD.getDirectoryContents'+listDirectory :: FilePath -> IO [FilePath]+listDirectory path = fmap cleanup contents where+	contents = SD.getDirectoryContents (encodeString path)+	cleanup = map (append path) . map decodeString . filter (`notElem` [".", ".."])++-- | Remove a file.+--+-- See: 'SD.removeFile'+removeFile :: FilePath -> IO ()+removeFile path =+	let path' = encodeString path in+#ifdef CABAL_OS_WINDOWS+	Win32.deleteFile path'+#else+	Posix.removeLink path'+#endif++-- | Remove an empty directory.+--+-- See: 'SD.removeDirectory'+removeDirectory :: FilePath -> IO ()+removeDirectory path =+	let path' = encodeString path in+#ifdef CABAL_OS_WINDOWS+	Win32.removeDirectory path'+#else+	Posix.removeDirectory path'+#endif++-- | Recursively remove a directory tree rooted at the given path.+--+-- See: 'SD.removeDirectoryRecursive'+removeTree :: FilePath -> IO ()+removeTree path = SD.removeDirectoryRecursive (encodeString path)++-- | Get the current working directory.+--+-- See: 'SD.getCurrentDirectory'+getWorkingDirectory :: IO FilePath+getWorkingDirectory = do+#ifdef CABAL_OS_WINDOWS+	raw <- Win32.getCurrentDirectory+#else+	raw <- Posix.getWorkingDirectory+#endif+	return (decodeString raw)++-- | Set the current working directory.+--+-- See: 'SD.setCurrentDirectory'+setWorkingDirectory :: FilePath -> IO ()+setWorkingDirectory path =+	let path' = encodeString path in+#ifdef CABAL_OS_WINDOWS+	Win32.setCurrentDirectory path'+#else+	Posix.changeWorkingDirectory path'+#endif++-- TODO: expose all known exceptions as specific types, for users to catch+-- if need be++-- | Get the user&#x2019;s home directory. This is useful for building paths+-- to more specific directories.+--+-- For directing the user to open or safe a document, use+-- 'getDocumentsDirectory'.+--+-- For data files the user does not explicitly create, such as automatic+-- saves, use 'getAppDataDirectory'.+--+-- See: 'SD.getHomeDirectory'+getHomeDirectory :: IO FilePath+getHomeDirectory = fmap decodeString SD.getHomeDirectory++-- | Get the user&#x2019;s home directory. This is a good starting point for+-- file dialogs and other user queries. For data files the user does not+-- explicitly create, such as automatic saves, use 'getAppDataDirectory'.+getDesktopDirectory :: IO FilePath+getDesktopDirectory = xdg "XDG_DESKTOP_DIR" Nothing+	(homeSlash "Desktop")++-- | Get the user&#x2019;s documents directory. This is a good place to save+-- user-created files. For data files the user does not explicitly create,+-- such as automatic saves, use 'getAppDataDirectory'.+--+-- See: 'SD.getUserDocumentsDirectory'+getDocumentsDirectory :: IO FilePath+getDocumentsDirectory = xdg "XDG_DOCUMENTS_DIR" Nothing+#ifdef CABAL_OS_WINDOWS+	(fmap decodeString SD.getUserDocumentsDirectory)+#else+	(homeSlash "Documents")+#endif++-- | Get the user&#x2019;s application data directory, given an application+-- label. This directory is where applications should store data the user did+-- not explicitly create, such as databases and automatic saves.+--+-- See: 'SD.getAppUserDataDirectory'+getAppDataDirectory :: T.Text -> IO FilePath+getAppDataDirectory label = xdg "XDG_DATA_HOME" (Just label)+#ifdef CABAL_OS_WINDOWS+	(fmap decodeString (SD.getAppUserDataDirectory ""))+#else+	(homeSlash ".local/share")+#endif++-- | Get the user&#x2019;s application cache directory, given an application+-- label. This directory is where applications should store caches, which+-- might be large and can be safely deleted.+getAppCacheDirectory :: T.Text -> IO FilePath+getAppCacheDirectory label = xdg "XDG_CACHE_HOME" (Just label)+#ifdef CABAL_OS_WINDOWS+	(homeSlash "Local Settings\\Cache")+#else+	(homeSlash ".cache")+#endif++-- | Get the user&#x2019;s application configuration directory, given an+-- application label. This directory is where applications should store their+-- configurations and settings.+getAppConfigDirectory :: T.Text -> IO FilePath+getAppConfigDirectory label = xdg "XDG_CONFIG_HOME" (Just label)+#ifdef CABAL_OS_WINDOWS+	(homeSlash "Local Settings")+#else+	(homeSlash ".config")+#endif++homeSlash :: String -> IO FilePath+homeSlash path = do+	home <- getHomeDirectory+	return (append home (decodeString path))++getenv :: String -> IO (Maybe String)+getenv key = Exc.catch+	(fmap Just (SE.getEnv key))+	(\e -> if isDoesNotExistError e+		then return Nothing+		else Exc.throwIO e)++xdg :: String -> Maybe T.Text -> IO FilePath -> IO FilePath+xdg envkey label fallback = do+	env <- getenv envkey+	dir <- case env of+		Just var -> return (decodeString var)+		Nothing -> fallback+	return $ case label of+		Just text -> append dir (R.fromText currentOS text)+		Nothing -> dir++-- | Copy a file to a new entry in the filesystem. If a file already exists+-- at the new location, it will be replaced.+--+-- See: 'SD.copyFile'+--+-- Since: 0.1.1+copyFile :: FilePath -- ^ Old location+         -> FilePath -- ^ New location+         -> IO ()+copyFile old new = SD.copyFile (encodeString old) (encodeString new)++-- | Get when the object at a given path was last modified.+--+-- Since: 0.2+getModified :: FilePath -> IO UTCTime+getModified path = do+#ifdef CABAL_OS_WINDOWS+	info <- withHANDLE path Win32.getFileInformationByHandle+	let ftime = Win32.bhfiLastWriteTime info+	stime <- Win32.fileTimeToSystemTime ftime+	+	let date = fromGregorian+		(fromIntegral (Win32.wYear stime))+		(fromIntegral (Win32.wMonth stime))+		(fromIntegral (Win32.wDay stime))+	+	let seconds = secondsToDiffTime $+		(toInteger (Win32.wHour stime) * 3600) ++		(toInteger (Win32.wMinute stime) * 60) ++		(toInteger (Win32.wSecond stime))+	+	let msecs = picosecondsToDiffTime $+		(toInteger (Win32.wMilliseconds stime) * 1000000000)+	+	return (UTCTime date (seconds + msecs))+#else+	stat <- Posix.getFileStatus (encodeString path)+	let mtime = Posix.modificationTime stat+	return (posixSecondsToUTCTime (realToFrac mtime))+#endif++-- | Get the size of an object at a given path. For special objects like+-- links or directories, the size is filesystem&#x2010; and+-- platform&#x2010;dependent.+--+-- Since: 0.2+getSize :: FilePath -> IO Integer+getSize path = do+#ifdef CABAL_OS_WINDOWS+	info <- withHANDLE path Win32.getFileInformationByHandle+	return (toInteger (Win32.bhfiSize info))+#else+	stat <- Posix.getFileStatus (encodeString path)+	return (toInteger (Posix.fileSize stat))+#endif++-- | Open a file in binary mode, and return an open 'Handle'. The 'Handle'+-- should be 'IO.hClose'd when it is no longer needed.+--+-- 'withFile' is easier to use, because it will handle the 'Handle'&#x2019;s+-- lifetime automatically.+--+-- See: 'IO.openBinaryFile'+openFile :: FilePath -> IO.IOMode -> IO IO.Handle+openFile path = IO.openBinaryFile (encodeString path)++-- | Open a file in binary mode, and pass its 'Handle' to a provided+-- computation. The 'Handle' will be automatically closed when the+-- computation returns.+--+-- See: 'IO.withBinaryFile'+withFile :: FilePath -> IO.IOMode -> (IO.Handle -> IO a) -> IO a+withFile path = IO.withBinaryFile (encodeString path)++-- | Read in the entire contents of a binary file.+--+-- See: 'B.readFile'+readFile :: FilePath -> IO B.ByteString+readFile path = B.readFile (encodeString path)++-- | Replace the entire contents of a binary file with the provided+-- 'B.ByteString'.+--+-- See: 'B.writeFile'+writeFile :: FilePath -> B.ByteString -> IO ()+writeFile path = B.writeFile (encodeString path)++-- | Append a 'B.ByteString' to a file. If the file does not exist, it will+-- be created.+--+-- See: 'B.appendFile'+appendFile :: FilePath -> B.ByteString -> IO ()+appendFile path = B.appendFile (encodeString path)++-- | Open a file in text mode, and return an open 'Handle'. The 'Handle'+-- should be 'IO.hClose'd when it is no longer needed.+--+-- 'withTextFile' is easier to use, because it will handle the+-- 'Handle'&#x2019;s lifetime automatically.+--+-- See: 'IO.openFile'+openTextFile :: FilePath -> IO.IOMode -> IO IO.Handle+openTextFile path = IO.openFile (encodeString path)++-- | Open a file in text mode, and pass its 'Handle' to a provided+-- computation. The 'Handle' will be automatically closed when the+-- computation returns.+--+-- See: 'IO.withFile'+withTextFile :: FilePath -> IO.IOMode -> (IO.Handle -> IO a) -> IO a+withTextFile path = IO.withFile (encodeString path)++-- | Read in the entire contents of a text file.+--+-- See: 'T.readFile'+readTextFile :: FilePath -> IO T.Text+readTextFile path = T.readFile (encodeString path)++-- | Replace the entire contents of a text file with the provided+-- 'T.Text'.+--+-- See: 'T.writeFile'+writeTextFile :: FilePath -> T.Text -> IO ()+writeTextFile path = T.writeFile (encodeString path)++-- | Append 'T.Text' to a file. If the file does not exist, it will+-- be created.+--+-- See: 'T.appendFile'+appendTextFile :: FilePath -> T.Text -> IO ()+appendTextFile path = T.appendFile (encodeString path)++#ifdef CABAL_OS_WINDOWS+withHANDLE :: FilePath -> (Win32.HANDLE -> IO a) -> IO a+withHANDLE path = Exc.bracket open close where+	open = Win32.createFile+		(encodeString path)+		Win32.gENERIC_READ+		(Win32.fILE_SHARE_READ .|. Win32.fILE_SHARE_WRITE)+		Nothing+		Win32.oPEN_EXISTING+		0+		Nothing+	close = Win32.closeHandle+#endif++encodeString :: FilePath -> String+#ifdef CABAL_OS_WINDOWS+encodeString = T.unpack . R.encode R.windows+#else+encodeString = B8.unpack . R.encode R.posix+#endif++decodeString :: String -> FilePath+#ifdef CABAL_OS_WINDOWS+decodeString = R.decode R.windows . T.pack+#else+decodeString = R.decode R.posix . B8.pack+#endif
− lib/Filesystem.hs
@@ -1,974 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE ForeignFunctionInterface #-}-{-# LANGUAGE PackageImports #-}---- |--- Module: Filesystem--- Copyright: 2011-2012 John Millikin <jmillikin@gmail.com>--- License: MIT------ Maintainer: John Millikin <jmillikin@gmail.com>--- Portability: portable------ Simple 'FilePath'&#8208;aware wrappers around standard "System.IO"--- computations. These wrappers are designed to work as similarly as--- possible across various versions of GHC.------ In particular, they do not require POSIX file paths to be valid strings,--- and can therefore open paths regardless of the current locale encoding.-module Filesystem-	(-	-- * Exports from System.IO-	  IO.Handle-	, IO.IOMode(..)-	-	-- * Files-	, isFile-	, getModified-	, getSize-	, copyFile-	, copyFileContent-	, copyPermissions-	, removeFile-	-	-- ** Binary files-	, openFile-	, withFile-	, readFile-	, writeFile-	, appendFile-	-	-- ** Text files-	, openTextFile-	, withTextFile-	, readTextFile-	, writeTextFile-	, appendTextFile-	-	-- * Directories-	, isDirectory-	, canonicalizePath-	, listDirectory-	-	-- ** Creating directories-	, createDirectory-	, createTree-	-	-- ** Removing directories-	, removeDirectory-	, removeTree-	-	-- ** Current working directory-	, getWorkingDirectory-	, setWorkingDirectory-	-	-- ** Commonly used paths-	, getHomeDirectory-	, getDesktopDirectory-	, getDocumentsDirectory-	, getAppDataDirectory-	, getAppCacheDirectory-	, getAppConfigDirectory-	-	-- * Other-	, rename-	) where--#ifndef CABAL_OS_WINDOWS-#if MIN_VERSION_base(4,2,0)-#define SYSTEMFILEIO_LOCAL_OPEN_FILE-#endif-#endif--import           Prelude hiding (FilePath, readFile, writeFile, appendFile)--import qualified Control.Exception as Exc-import           Control.Monad (forM_, unless, when)-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as BL-import qualified Data.Text as T-import qualified Data.Text.IO as T-import           Foreign.Ptr (Ptr, nullPtr)-import           Foreign.C (CInt, CString, withCAString)-import qualified Foreign.C.Error as CError-import qualified System.Environment as SE--#if MIN_VERSION_system_filepath(0,4,0)-import           Filesystem.Path (FilePath, append)-import qualified Filesystem.Path as Path-import           Filesystem.Path.CurrentOS (currentOS, encodeString, decodeString)-import qualified Filesystem.Path.Rules as R-#else-import           System.FilePath (FilePath, append)-import qualified System.FilePath as Path-import           System.FilePath.CurrentOS (currentOS, encodeString, decodeString)-import qualified System.FilePath.Rules as R-#endif--import qualified System.IO as IO-import           System.IO.Error (IOError)--#ifdef CABAL_OS_WINDOWS--import           Data.Bits ((.|.))-import           Data.Time ( UTCTime(..)-                           , fromGregorian-                           , secondsToDiffTime-                           , picosecondsToDiffTime)-import           Foreign.C (CWString, withCWString)-import qualified System.Win32 as Win32-import           System.IO.Error (isDoesNotExistError)-import qualified "directory" System.Directory as SD--#else--import           Data.Time (UTCTime)-import           Data.Time.Clock.POSIX (posixSecondsToUTCTime)-import qualified System.Posix as Posix-import qualified System.Posix.Error as Posix--#endif--#ifdef SYSTEMFILEIO_LOCAL_OPEN_FILE-import           Data.Bits ((.|.))-import           GHC.IO.Handle.FD (mkHandleFromFD)-import           GHC.IO.FD (mkFD)-import qualified GHC.IO.Device-import qualified System.Posix.Internals-#endif----- | Check if a file exists at the given path.------ Any non&#8208;directory object, including devices and pipes, are--- considered to be files. Symbolic links are resolved to their targets--- before checking their type.------ This computation does not throw exceptions.-isFile :: FilePath -> IO Bool-#ifdef CABAL_OS_WINDOWS-isFile path = SD.doesFileExist (encodeString path)-#else-isFile path = Exc.catch-	(do-		stat <- posixStat "isFile" path-		return (not (Posix.isDirectory stat)))-	((\_ -> return False) :: IOError -> IO Bool)-#endif---- | Check if a directory exists at the given path.------ Symbolic links are resolved to their targets before checking their type.------ This computation does not throw exceptions.-isDirectory :: FilePath -> IO Bool-#ifdef CABAL_OS_WINDOWS-isDirectory path = SD.doesDirectoryExist (encodeString path)-#else-isDirectory path = Exc.catch-	(do-		stat <- posixStat "isFile" path-		return (Posix.isDirectory stat))-	((\_ -> return False) :: IOError -> IO Bool)-#endif---- | Rename a filesystem object.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-rename :: FilePath -> FilePath -> IO ()-rename old new =-#ifdef CABAL_OS_WINDOWS-	let old' = encodeString old in-	let new' = encodeString new in-	Win32.moveFileEx old' new' Win32.mOVEFILE_REPLACE_EXISTING-#else-	withFilePath old $ \old' ->-	withFilePath new $ \new' ->-	throwErrnoPathIfMinus1_ "rename" old (c_rename old' new')--foreign import ccall unsafe "rename"-	c_rename :: CString -> CString -> IO CInt--#endif---- | Resolve symlinks and \"..\" path elements to return a canonical path.--- It is intended that two paths referring to the same object will always--- resolve to the same canonical path.------ Note that on many operating systems, it is impossible to guarantee that--- two paths to the same file will resolve to the same canonical path.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.------ Since: 0.1.1-canonicalizePath :: FilePath -> IO FilePath-canonicalizePath path =-	let path' = encodeString path in-#ifdef CABAL_OS_WINDOWS-	fmap decodeString $-#if MIN_VERSION_Win32(2,2,1)-	Win32.getFullPathName path'-#else-	Win32.withTString path' $ \c_name -> do-		Win32.try "getFullPathName" (\buf len ->-			c_GetFullPathNameW c_name len buf nullPtr) 512-#endif-#else-	withFilePath path $ \cPath -> do-		cOut <- Posix.throwErrnoPathIfNull "canonicalizePath" path' (c_realpath cPath nullPtr)-		bytes <- B.packCString cOut-		c_free cOut-		return (R.decode R.posix bytes)-#endif--#ifdef CABAL_OS_WINDOWS-#if MIN_VERSION_Win32(2,2,1)-#else-foreign import stdcall unsafe "GetFullPathNameW"-	c_GetFullPathNameW :: Win32.LPCTSTR -> Win32.DWORD -> Win32.LPTSTR -> Ptr Win32.LPTSTR -> IO Win32.DWORD-#endif-#endif--#ifndef CABAL_OS_WINDOWS-foreign import ccall unsafe "realpath"-	c_realpath :: CString -> CString -> IO CString-#endif---- | Create a directory at a given path. The user may choose whether it is--- an error for a directory to already exist at that path.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-createDirectory :: Bool -- ^ Succeed if the directory already exists-                -> FilePath -> IO ()-createDirectory succeedIfExists path =-#ifdef CABAL_OS_WINDOWS-	let path' = encodeString path in-	if succeedIfExists-		then SD.createDirectoryIfMissing False path'-		else Win32.createDirectory path' Nothing-#else-	withFilePath path $ \cPath ->-	throwErrnoPathIfMinus1Retry_ "createDirectory" path $ if succeedIfExists-		then mkdirIfMissing path cPath 0o777-		else c_mkdir cPath 0o777--mkdirIfMissing :: FilePath -> CString -> CInt -> IO CInt-mkdirIfMissing path cPath mode = do-	rc <- c_mkdir cPath mode-	if rc == -1-		then do-			errno <- CError.getErrno-			if errno == CError.eEXIST-				then do-					dirExists <- isDirectory path-					if dirExists-						then return 0-						else return rc-				else return rc-		else return rc--foreign import ccall unsafe "mkdir"-	c_mkdir :: CString -> CInt -> IO CInt-#endif---- | Create a directory at a given path, including any parents which might--- be missing.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-createTree :: FilePath -> IO ()-#ifdef CABAL_OS_WINDOWS-createTree path = SD.createDirectoryIfMissing True (encodeString path)-#else-createTree path = do-	let parent = Path.parent path-	parentExists <- isDirectory parent-	unless parentExists (createTree parent)-	withFilePath path $ \cPath ->-		throwErrnoPathIfMinus1Retry_ "createTree" path (mkdirIfMissing path cPath 0o777)-#endif---- | List objects in a directory, excluding @\".\"@ and @\"..\"@. Each--- returned 'FilePath' includes the path of the directory. Entries are not--- sorted.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-listDirectory :: FilePath -> IO [FilePath]-#ifdef CABAL_OS_WINDOWS-listDirectory root = fmap cleanup contents where-	contents = SD.getDirectoryContents (encodeString root)-	cleanup = map (append root) . map decodeString . filter (`notElem` [".", ".."])-#else-listDirectory root = Exc.bracket alloc free list where-	alloc = do-		dirent <- c_alloc_dirent-		dir <- openDir root-		return (dirent, dir)-	free (dirent, dir) = do-		c_free_dirent dirent-		closeDir dir-	list (dirent, dir) = loop where-		loop = do-			next <- readDir dir dirent-			case next of-				Nothing -> return []-				Just bytes | ignore bytes -> loop-				Just bytes -> do-					let name = append root (R.decode R.posix bytes)-					names <- loop-					return (name:names)--ignore :: B.ByteString -> Bool-ignore = ignore' where-	dot = B.pack [46]-	dotdot = B.pack [46, 46]-	ignore' b = b == dot || b == dotdot--data Dir = Dir FilePath (Ptr ())--openDir :: FilePath -> IO Dir-openDir root = withFilePath root $ \cRoot -> do-	p <- throwErrnoPathIfNullRetry "listDirectory" root (c_opendir cRoot)-	return (Dir root p)--closeDir :: Dir -> IO ()-closeDir (Dir _ p) = CError.throwErrnoIfMinus1Retry_ "listDirectory" (c_closedir p)--readDir :: Dir -> Ptr () -> IO (Maybe B.ByteString)-readDir (Dir _ p) dirent = do-	rc <- CError.throwErrnoIfMinus1Retry "listDirectory" (c_readdir p dirent)-	if rc == 0-		then do-			bytes <- c_dirent_name dirent >>= B.packCString-			return (Just bytes)-		else return Nothing--foreign import ccall unsafe "opendir"-	c_opendir :: CString -> IO (Ptr ())--foreign import ccall unsafe "closedir"-	c_closedir :: Ptr () -> IO CInt--foreign import ccall unsafe "hssystemfileio_alloc_dirent"-	c_alloc_dirent :: IO (Ptr ())--foreign import ccall unsafe "hssystemfileio_free_dirent"-	c_free_dirent :: Ptr () -> IO ()--foreign import ccall unsafe "hssystemfileio_readdir"-	c_readdir :: Ptr () -> Ptr () -> IO CInt--foreign import ccall unsafe "hssystemfileio_dirent_name"-	c_dirent_name :: Ptr () -> IO CString--#endif---- | Remove a file. This will fail if the file does not exist.------ This computation cannot remove directories. For that, use 'removeDirectory'--- or 'removeTree'.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-removeFile :: FilePath -> IO ()-removeFile path =-#ifdef CABAL_OS_WINDOWS-	Win32.deleteFile (encodeString path)-#else-	withFilePath path $ \cPath ->-	throwErrnoPathIfMinus1_ "removeFile" path (c_unlink cPath)--foreign import ccall unsafe "unlink"-	c_unlink :: CString -> IO CInt-#endif---- | Remove an empty directory.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-removeDirectory :: FilePath -> IO ()-removeDirectory path =-#ifdef CABAL_OS_WINDOWS-	Win32.removeDirectory (encodeString path)-#else-	withFilePath path $ \cPath ->-	throwErrnoPathIfMinus1Retry_ "removeDirectory" path (c_rmdir cPath)--foreign import ccall unsafe "rmdir"-	c_rmdir :: CString -> IO CInt-#endif---- | Recursively remove a directory tree rooted at the given path.------ This computation does not follow symlinks. If the tree contains symlinks,--- the links themselves will be removed, but not the objects they point to.------ If the root path is a symlink, then it will be treated as if it were a--- regular directory.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-removeTree :: FilePath -> IO ()-#ifdef CABAL_OS_WINDOWS-removeTree root = SD.removeDirectoryRecursive (encodeString root)-#else-removeTree root = do-	items <- listDirectory root-	forM_ items $ \item -> Exc.catch-		(removeFile item)-		(\exc -> do-			isDir <- isRealDir item-			if isDir-				then removeTree item-				else Exc.throwIO (exc :: IOError))-	removeDirectory root---- Check whether a path is a directory, and not just a symlink to a directory.------ This is used in 'removeTree' to prevent recursing into symlinks if the link--- itself cannot be deleted.-isRealDir :: FilePath -> IO Bool-isRealDir path = withFilePath path $ \cPath -> do-	rc <- throwErrnoPathIfMinus1Retry "removeTree" path (c_isrealdir cPath)-	return (rc == 1)--foreign import ccall unsafe "hssystemfileio_isrealdir"-	c_isrealdir :: CString -> IO CInt--#endif---- | Get the current working directory.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-getWorkingDirectory :: IO FilePath-getWorkingDirectory = do-#ifdef CABAL_OS_WINDOWS-#if MIN_VERSION_Win32(2,2,1)-	fmap decodeString Win32.getCurrentDirectory-#else-	fmap decodeString (Win32.try "getWorkingDirectory" (flip c_GetCurrentDirectoryW) 512)-#endif-#else-	buf <- CError.throwErrnoIfNull "getWorkingDirectory" c_getcwd-	bytes <- B.packCString buf-	c_free buf-	return (R.decode R.posix bytes)--foreign import ccall unsafe "hssystemfileio_getcwd"-	c_getcwd :: IO CString--#endif--#ifdef CABAL_OS_WINDOWS-#if MIN_VERSION_Win32(2,2,1)-#else-foreign import stdcall unsafe "GetCurrentDirectoryW"-	c_GetCurrentDirectoryW :: Win32.DWORD -> Win32.LPTSTR -> IO Win32.UINT-#endif-#endif---- | Set the current working directory.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-setWorkingDirectory :: FilePath -> IO ()-setWorkingDirectory path =-#ifdef CABAL_OS_WINDOWS-	Win32.setCurrentDirectory (encodeString path)-#else-	withFilePath path $ \cPath ->-	throwErrnoPathIfMinus1Retry_ "setWorkingDirectory" path (c_chdir cPath)--foreign import ccall unsafe "chdir"-	c_chdir :: CString -> IO CInt--#endif---- TODO: expose all known exceptions as specific types, for users to catch--- if need be---- | Get the user&#x2019;s home directory. This is useful for building paths--- to more specific directories.------ For directing the user to open or safe a document, use--- 'getDocumentsDirectory'.------ For data files the user does not explicitly create, such as automatic--- saves, use 'getAppDataDirectory'.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-getHomeDirectory :: IO FilePath-#ifdef CABAL_OS_WINDOWS-getHomeDirectory = fmap decodeString SD.getHomeDirectory-#else-getHomeDirectory = do-	path <- getenv "HOME"-	case path of-		Just p -> return p-		Nothing -> do-			-- use getEnv to throw the right exception type-			fmap decodeString (SE.getEnv "HOME")-#endif---- | Get the user&#x2019;s desktop directory. This is a good starting point for--- file dialogs and other user queries. For data files the user does not--- explicitly create, such as automatic saves, use 'getAppDataDirectory'.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-getDesktopDirectory :: IO FilePath-getDesktopDirectory = xdg "XDG_DESKTOP_DIR" Nothing-	(homeSlash "Desktop")---- | Get the user&#x2019;s documents directory. This is a good place to save--- user&#8208;created files. For data files the user does not explicitly--- create, such as automatic saves, use 'getAppDataDirectory'.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-getDocumentsDirectory :: IO FilePath-getDocumentsDirectory = xdg "XDG_DOCUMENTS_DIR" Nothing-#ifdef CABAL_OS_WINDOWS-	(fmap decodeString SD.getUserDocumentsDirectory)-#else-	(homeSlash "Documents")-#endif---- | Get the user&#x2019;s application data directory, given an application--- label. This directory is where applications should store data the user did--- not explicitly create, such as databases and automatic saves.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-getAppDataDirectory :: T.Text -> IO FilePath-getAppDataDirectory label = xdg "XDG_DATA_HOME" (Just label)-#ifdef CABAL_OS_WINDOWS-	(fmap decodeString (SD.getAppUserDataDirectory ""))-#else-	(homeSlash ".local/share")-#endif---- | Get the user&#x2019;s application cache directory, given an application--- label. This directory is where applications should store caches, which--- might be large and can be safely deleted.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-getAppCacheDirectory :: T.Text -> IO FilePath-getAppCacheDirectory label = xdg "XDG_CACHE_HOME" (Just label)-#ifdef CABAL_OS_WINDOWS-	(homeSlash "Local Settings\\Cache")-#else-	(homeSlash ".cache")-#endif---- | Get the user&#x2019;s application configuration directory, given an--- application label. This directory is where applications should store their--- configurations and settings.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-getAppConfigDirectory :: T.Text -> IO FilePath-getAppConfigDirectory label = xdg "XDG_CONFIG_HOME" (Just label)-#ifdef CABAL_OS_WINDOWS-	(homeSlash "Local Settings")-#else-	(homeSlash ".config")-#endif--homeSlash :: String -> IO FilePath-homeSlash path = do-	home <- getHomeDirectory-	return (append home (decodeString path))--getenv :: String -> IO (Maybe FilePath)-#ifdef CABAL_OS_WINDOWS-getenv key = Exc.catch-	(fmap (Just . decodeString) (SE.getEnv key))-	(\e -> if isDoesNotExistError e-		then return Nothing-		else Exc.throwIO e)-#else-getenv key = withCAString key $ \cKey -> do-	ret <- c_getenv cKey-	if ret == nullPtr-		then return Nothing-		else do-			bytes <- B.packCString ret-			return (Just (R.decode R.posix bytes))--foreign import ccall unsafe "getenv"-	c_getenv :: CString -> IO CString--#endif--xdg :: String -> Maybe T.Text -> IO FilePath -> IO FilePath-xdg envkey label fallback = do-	env <- getenv envkey-	dir <- case env of-		Just var -> return var-		Nothing -> fallback-	return $ case label of-		Just text -> append dir (R.fromText currentOS text)-		Nothing -> dir---- | Copy the content of a file to a new entry in the filesystem. If a--- file already exists at the new location, it will be replaced. Copying--- a file is not atomic.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.------ Since: 0.2.4-copyFileContent :: FilePath -- ^ Old location-                -> FilePath -- ^ New location-                -> IO ()-copyFileContent oldPath newPath =-	withFile oldPath IO.ReadMode $ \old ->-	withFile newPath IO.WriteMode $ \new ->-	BL.hGetContents old >>= BL.hPut new---- | Copy the permissions from one path to another. Both paths must already--- exist.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.------ Since: 0.2.4-copyPermissions :: FilePath -- ^ Old location-                -> FilePath -- ^ New location-                -> IO ()-copyPermissions oldPath newPath =-	withFilePath oldPath $ \cOldPath ->-	withFilePath newPath $ \cNewPath ->-	CError.throwErrnoIfMinus1Retry_ "copyPermissions" $-	c_copy_permissions cOldPath cNewPath--#ifdef CABAL_OS_WINDOWS--foreign import ccall unsafe "hssystemfileio_copy_permissions"-	c_copy_permissions :: CWString -> CWString -> IO CInt--#else--foreign import ccall unsafe "hssystemfileio_copy_permissions"-	c_copy_permissions :: CString -> CString -> IO CInt--#endif---- | Copy the content and permissions of a file to a new entry in the--- filesystem. If a file already exists at the new location, it will be--- replaced. Copying a file is not atomic.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.------ Since: 0.1.1-copyFile :: FilePath -- ^ Old location-         -> FilePath -- ^ New location-         -> IO ()-copyFile oldPath newPath = do-	copyFileContent oldPath newPath-	Exc.catch-		(copyPermissions oldPath newPath)-		((\_ -> return ()) :: IOError -> IO ())---- | Get when the object at a given path was last modified.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.------ Since: 0.2-getModified :: FilePath -> IO UTCTime-getModified path = do-#ifdef CABAL_OS_WINDOWS-	info <- withHANDLE path Win32.getFileInformationByHandle-	let ftime = Win32.bhfiLastWriteTime info-	stime <- Win32.fileTimeToSystemTime ftime-	-	let date = fromGregorian-		(fromIntegral (Win32.wYear stime))-		(fromIntegral (Win32.wMonth stime))-		(fromIntegral (Win32.wDay stime))-	-	let seconds = secondsToDiffTime $-		(toInteger (Win32.wHour stime) * 3600) +-		(toInteger (Win32.wMinute stime) * 60) +-		(toInteger (Win32.wSecond stime))-	-	let msecs = picosecondsToDiffTime $-		(toInteger (Win32.wMilliseconds stime) * 1000000000)-	-	return (UTCTime date (seconds + msecs))-#else-	stat <- posixStat "getModified" path-	let mtime = Posix.modificationTime stat-	return (posixSecondsToUTCTime (realToFrac mtime))-#endif---- | Get the size of an object at a given path. For special objects like--- links or directories, the size is filesystem&#8208; and--- platform&#8208;dependent.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.------ Since: 0.2-getSize :: FilePath -> IO Integer-getSize path = do-#ifdef CABAL_OS_WINDOWS-	info <- withHANDLE path Win32.getFileInformationByHandle-	return (toInteger (Win32.bhfiSize info))-#else-	stat <- posixStat "getSize" path-	return (toInteger (Posix.fileSize stat))-#endif---- | Open a file in binary mode, and return an open 'Handle'. The 'Handle'--- should be closed with 'IO.hClose' when it is no longer needed.------ 'withFile' is easier to use, because it will handle the 'Handle'&#x2019;s--- lifetime automatically.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-openFile :: FilePath -> IO.IOMode -> IO IO.Handle-#ifdef SYSTEMFILEIO_LOCAL_OPEN_FILE-openFile path mode = openFile' "openFile" path mode Nothing-#else-openFile path = IO.openBinaryFile (encodeString path)-#endif---- | Open a file in binary mode, and pass its 'Handle' to a provided--- computation. The 'Handle' will be automatically closed when the--- computation returns.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-withFile :: FilePath -> IO.IOMode -> (IO.Handle -> IO a) -> IO a-withFile path mode = Exc.bracket (openFile path mode) IO.hClose---- | Read in the entire content of a binary file.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-readFile :: FilePath -> IO B.ByteString-readFile path = withFile path IO.ReadMode-	(\h -> IO.hFileSize h >>= B.hGet h . fromIntegral)---- | Replace the entire content of a binary file with the provided--- 'B.ByteString'.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-writeFile :: FilePath -> B.ByteString -> IO ()-writeFile path bytes = withFile path IO.WriteMode-	(\h -> B.hPut h bytes)---- | Append a 'B.ByteString' to a file. If the file does not exist, it will--- be created.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-appendFile :: FilePath -> B.ByteString -> IO ()-appendFile path bytes = withFile path IO.AppendMode-	(\h -> B.hPut h bytes)---- | Open a file in text mode, and return an open 'Handle'. The 'Handle'--- should be closed with 'IO.hClose' when it is no longer needed.------ 'withTextFile' is easier to use, because it will handle the--- 'Handle'&#x2019;s lifetime automatically.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-openTextFile :: FilePath -> IO.IOMode -> IO IO.Handle-#ifdef SYSTEMFILEIO_LOCAL_OPEN_FILE-openTextFile path mode = openFile' "openTextFile" path mode (Just IO.localeEncoding)-#else-openTextFile path = IO.openFile (encodeString path)-#endif---- | Open a file in text mode, and pass its 'Handle' to a provided--- computation. The 'Handle' will be automatically closed when the--- computation returns.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-withTextFile :: FilePath -> IO.IOMode -> (IO.Handle -> IO a) -> IO a-withTextFile path mode = Exc.bracket (openTextFile path mode) IO.hClose---- | Read in the entire content of a text file.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-readTextFile :: FilePath -> IO T.Text-readTextFile path = openTextFile path IO.ReadMode >>= T.hGetContents---- | Replace the entire content of a text file with the provided--- 'T.Text'.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-writeTextFile :: FilePath -> T.Text -> IO ()-writeTextFile path text = withTextFile path IO.WriteMode-	(\h -> T.hPutStr h text)---- | Append 'T.Text' to a file. If the file does not exist, it will--- be created.------ This computation throws 'IOError' on failure. See &#8220;Classifying--- I/O errors&#8221; in the "System.IO.Error" documentation for information on--- why the failure occured.-appendTextFile :: FilePath -> T.Text -> IO ()-appendTextFile path text = withTextFile path IO.AppendMode-	(\h -> T.hPutStr h text)--#ifdef SYSTEMFILEIO_LOCAL_OPEN_FILE--- | Copied from GHC.IO.FD.openFile-openFile' :: String -> FilePath -> IO.IOMode -> (Maybe IO.TextEncoding) -> IO IO.Handle-openFile' loc path mode codec = open where-	sys_c_open = System.Posix.Internals.c_open-	sys_c_close = System.Posix.Internals.c_close-	flags = iomodeFlags mode-	open = withFilePath path $ \cPath -> do-		c_fd <- throwErrnoPathIfMinus1Retry loc path (sys_c_open cPath flags 0o666)-		(fd, fd_type) <- Exc.onException-			(mkFD c_fd mode Nothing False True)-			(sys_c_close c_fd)-		when (mode == IO.WriteMode && fd_type == GHC.IO.Device.RegularFile) $ do-			GHC.IO.Device.setSize fd 0-		Exc.onException-			(mkHandleFromFD fd fd_type (encodeString path) mode False codec)-			(GHC.IO.Device.close fd)--iomodeFlags :: IO.IOMode -> CInt-iomodeFlags mode = cased .|. commonFlags where-	cased = case mode of-		IO.ReadMode -> flagsR-#ifdef mingw32_HOST_OS-		IO.WriteMode -> flagsW .|. System.Posix.Internals.o_TRUNC-#else-		IO.WriteMode -> flagsW-#endif-		IO.ReadWriteMode -> flagsRW-		IO.AppendMode -> flagsA-	-	flagsR  = System.Posix.Internals.o_RDONLY-	flagsW  = outputFlags .|. System.Posix.Internals.o_WRONLY-	flagsRW = outputFlags .|. System.Posix.Internals.o_RDWR-	flagsA  = flagsW      .|. System.Posix.Internals.o_APPEND-	-	commonFlags = System.Posix.Internals.o_NOCTTY .|.-	              System.Posix.Internals.o_NONBLOCK-	outputFlags = System.Posix.Internals.o_CREAT--#endif--#ifdef CABAL_OS_WINDOWS--withHANDLE :: FilePath -> (Win32.HANDLE -> IO a) -> IO a-withHANDLE path = Exc.bracket open close where-	open = Win32.createFile-		(encodeString path)-		Win32.gENERIC_READ-		(Win32.fILE_SHARE_READ .|. Win32.fILE_SHARE_WRITE)-		Nothing-		Win32.oPEN_EXISTING-		0-		Nothing-	close = Win32.closeHandle--withFilePath :: FilePath -> (CWString -> IO a) -> IO a-withFilePath path = withCWString (encodeString path)--#else--withFilePath :: FilePath -> (CString -> IO a) -> IO a-withFilePath path = B.useAsCString (R.encode R.posix path)--throwErrnoPathIfMinus1 :: String -> FilePath -> IO CInt -> IO CInt-throwErrnoPathIfMinus1 loc path = CError.throwErrnoPathIfMinus1 loc (encodeString path)--throwErrnoPathIfMinus1_ :: String -> FilePath -> IO CInt -> IO ()-throwErrnoPathIfMinus1_ loc path = CError.throwErrnoPathIfMinus1_ loc (encodeString path)--throwErrnoPathIfNullRetry :: String -> FilePath -> IO (Ptr a) -> IO (Ptr a)-throwErrnoPathIfNullRetry = throwErrnoPathIfRetry (== nullPtr)--throwErrnoPathIfMinus1Retry :: String -> FilePath -> IO CInt -> IO CInt-throwErrnoPathIfMinus1Retry = throwErrnoPathIfRetry (== -1)--throwErrnoPathIfMinus1Retry_ :: String -> FilePath -> IO CInt -> IO ()-throwErrnoPathIfMinus1Retry_ = throwErrnoPathIfRetry_ (== -1)--throwErrnoPathIfRetry :: (a -> Bool) -> String -> FilePath -> IO a -> IO a-throwErrnoPathIfRetry failed loc path io = loop where-	loop = do-		a <- io-		if failed a-			then do-				errno <- CError.getErrno-				if errno == CError.eINTR-					then loop-					else CError.throwErrnoPath loc (encodeString path)-			else return a--throwErrnoPathIfRetry_ :: (a -> Bool) -> String -> FilePath -> IO a -> IO ()-throwErrnoPathIfRetry_ failed loc path io = do-	_ <- throwErrnoPathIfRetry failed loc path io-	return ()--withFd :: String -> FilePath -> (Posix.Fd -> IO a) -> IO a-withFd fnName path = Exc.bracket open close where-	open = withFilePath path $ \cpath -> do-		fd <- throwErrnoPathIfMinus1 fnName path (c_open cpath 0)-		return (Posix.Fd fd)-	close = Posix.closeFd--posixStat :: String -> FilePath -> IO Posix.FileStatus-posixStat loc path = withFd loc path Posix.getFdStatus--foreign import ccall unsafe "open"-	c_open :: CString -> CInt -> IO CInt--foreign import ccall unsafe "free"-	c_free :: Ptr a -> IO ()--#endif
− lib/System/Directory.hs
@@ -1,42 +0,0 @@--- |--- Module: System.Directory--- Copyright: 2011 John Millikin--- License: MIT------ Maintainer: jmillikin@gmail.com--- Portability: portable------ Deprecated alias for "Filesystem".-module System.Directory-	(-	-	-- * Generic operations-	  isFile-	, isDirectory-	, rename-	, canonicalizePath-	, listDirectory-	-	-- * Creating things-	, createDirectory-	, createTree-	-	-- * Removing things-	, removeFile-	, removeDirectory-	, removeTree-	-	-- * Current working directory-	, getWorkingDirectory-	, setWorkingDirectory-	-	-- * Commonly used paths-	, getHomeDirectory-	, getDesktopDirectory-	, getDocumentsDirectory-	, getAppDataDirectory-	, getAppCacheDirectory-	, getAppConfigDirectory-	) where--import           Filesystem
− lib/System/File.hs
@@ -1,36 +0,0 @@--- |--- Module: System.File--- Copyright: 2011 John Millikin--- License: MIT------ Maintainer: jmillikin@gmail.com--- Portability: portable------ Deprecated alias for "Filesystem".-module System.File-	( Filesystem.Handle-	, Filesystem.IOMode(..)-	-	-- * File operations-	, copyFile-	-	-- * File information-	, getModified-	, getSize-	-	-- * Binary files-	, openFile-	, withFile-	, Filesystem.readFile-	, Filesystem.writeFile-	, Filesystem.appendFile-	-	-- * Text files-	, openTextFile-	, withTextFile-	, readTextFile-	, writeTextFile-	, appendTextFile-	) where--import           Filesystem
− lib/hssystemfileio-unix.c
@@ -1,93 +0,0 @@-#include "hssystemfileio-unix.h"--#include <errno.h>-#include <stdlib.h>-#include <sys/stat.h>-#include <sys/types.h>-#include <unistd.h>--struct dirent *-hssystemfileio_alloc_dirent()-{-	return malloc(sizeof (struct dirent));-}--void-hssystemfileio_free_dirent(struct dirent *p)-{-	free(p);-}--int-hssystemfileio_readdir(DIR *dir, struct dirent *dirent)-{-	struct dirent *dirent_result;-	while (1)-	{-		int rc = readdir_r(dir, dirent, &dirent_result);-		if (rc != 0)-		{ return -1; }-		-		if (dirent_result == NULL)-		{ return 1; }-		-		return 0;-	}-}--char *-hssystemfileio_dirent_name(struct dirent *dirent)-{-	return dirent->d_name;-}--char *-hssystemfileio_getcwd(void)-{-#ifdef PATH_MAX-	int bufsize = PATH_MAX;-#else-	int bufsize = 4096;-#endif-	char *buf = malloc(bufsize);-	while (1)-	{-		char *ret = getcwd(buf, bufsize);-		if (ret != NULL)-		{ return ret; }-		-		free(buf);-		if (errno == ERANGE)-		{-			bufsize *= 2;-			buf = malloc(bufsize);-			continue;-		}-		return NULL;-	}-}--int-hssystemfileio_isrealdir(const char *path)-{-	struct stat st;-	int rc = lstat(path, &st);-	if (rc == -1)-	{ return rc; }-	-	if (S_ISDIR(st.st_mode))-	{ return 1; }-	-	return 0;-}--int-hssystemfileio_copy_permissions(const char *old_path, const char *new_path)-{-	struct stat st;-	int rc = stat(old_path, &st);-	if (rc == -1)-	{ return rc; }-	-	return chmod(new_path, st.st_mode);-}
− lib/hssystemfileio-unix.h
@@ -1,27 +0,0 @@-#ifndef HSSYSTEMFILEIO_UNIX_H-#define HSSYSTEMFILEIO_UNIX_H--#include <dirent.h>--struct dirent *-hssystemfileio_alloc_dirent();--void-hssystemfileio_free_dirent(struct dirent *);--int-hssystemfileio_readdir(DIR *dir, struct dirent *dirent);--char *-hssystemfileio_dirent_name(struct dirent *dirent);--char *-hssystemfileio_getcwd(void);--int-hssystemfileio_isrealdir(const char *);--int-hssystemfileio_copy_permissions(const char *old_path, const char *new_path);--#endif
− lib/hssystemfileio-win32.c
@@ -1,16 +0,0 @@-#include "hssystemfileio-win32.h"--#include <io.h>-#include <sys/types.h>-#include <sys/stat.h>--int-hssystemfileio_copy_permissions(const wchar_t *old_path, const wchar_t *new_path)-{-	struct _stat st;-	int rc = _wstat(old_path, &st);-	if (rc == -1)-	{ return rc; }-	-	return _wchmod(new_path, st.st_mode);-}
− lib/hssystemfileio-win32.h
@@ -1,9 +0,0 @@-#ifndef HSSYSTEMFILEIO_WIN32_H-#define HSSYSTEMFILEIO_WIN32_H--#include <wchar.h>--int-hssystemfileio_copy_permissions(const wchar_t *old_path, const wchar_t *new_path);--#endif
− scripts/common.bash
@@ -1,23 +0,0 @@-PATH="$PATH:$PWD/cabal-dev/bin/"--VERSION=$(awk '/^version:/{print $2}' system-fileio.cabal)--CABAL_DEV=$(which cabal-dev)-XZ=$(which xz)--require_cabal_dev()-{-	if [ -z "$CABAL_DEV" ]; then-		echo "Can't find 'cabal-dev' executable; make sure it exists on your "'$PATH'-		echo "Cowardly refusing to fuck with the global package database"-		exit 1-	fi-}--clean_dev_install()-{-	require_cabal_dev-	-	rm -rf dist-	$CABAL_DEV install || exit 1-}
− scripts/run-coverage
@@ -1,28 +0,0 @@-#!/bin/bash-if [ ! -f 'system-fileio.cabal' ]; then-	echo -n "Can't find system-fileio.cabal; please run this script as"-	echo -n " ./scripts/run-coverage from within the system-fileio source"-	echo " directory"-	exit 1-fi--. scripts/common.bash--require_cabal_dev--pushd tests-$CABAL_DEV -s ../cabal-dev install --flags="coverage" || exit 1-popd--rm -f system-fileio_tests.tix-cabal-dev/bin/system-fileio_tests $@--EXCLUDES="\---exclude=Main \---exclude=FilesystemTests.Posix \---exclude=FilesystemTests.Util \---exclude=FilesystemTests.Windows-"--hpc markup --srcdir=src --srcdir=tests/ system-fileio_tests.tix --destdir=hpc-markup $EXCLUDES > /dev/null-hpc report --srcdir=src --srcdir=tests/ system-fileio_tests.tix $EXCLUDES
− scripts/run-tests
@@ -1,17 +0,0 @@-#!/bin/bash-if [ ! -f 'system-fileio.cabal' ]; then-	echo -n "Can't find system-fileio.cabal; please run this script as"-	echo -n " ./scripts/run-tests from within the system-fileio source"-	echo " directory"-	exit 1-fi--. scripts/common.bash--require_cabal_dev--pushd tests-$CABAL_DEV -s ../cabal-dev install || exit 1-popd--cabal-dev/bin/system-fileio_tests $@
system-fileio.cabal view
@@ -1,72 +1,43 @@ name: system-fileio-version: 0.2.7+version: 0.3+synopsis: High-level filesystem interaction license: MIT license-file: license.txt author: John Millikin <jmillikin@gmail.com>-maintainer: John Millikin <jmillikin@gmail.com>+maintainer: jmillikin@gmail.com+copyright: Copyright (c) John Millikin 2011 build-type: Simple-cabal-version: >= 1.6+cabal-version: >=1.6 category: System stability: experimental-homepage: https://john-millikin.com/software/haskell-filesystem/+homepage: https://john-millikin.com/software/hs-fileio/ bug-reports: mailto:jmillikin@gmail.com+tested-with: GHC==6.12.1 -synopsis: Consistent filesystem interaction across GHC versions description:   This is a small wrapper around the \"directory\", \"unix\", and \"Win32\"-  packages, for use with \"system-filepath\". It provides a consistent API-  to the various versions of these packages distributed with different-  versions of GHC.-  .-  In particular, this library supports working with POSIX files that have-  paths which can't be decoded in the current locale encoding.--extra-source-files:-  lib/hssystemfileio-unix.h-  lib/hssystemfileio-win32.h-  ---  scripts/common.bash-  scripts/run-coverage-  scripts/run-tests-  ---  tests/system-fileio-tests.cabal-  tests/FilesystemTests.hs-  tests/FilesystemTests/Posix.hs-  tests/FilesystemTests/Util.hs-  tests/FilesystemTests/Windows.hs+  packages for use with \"system-filepath\".  source-repository head-  type: bazaar-  location: https://john-millikin.com/branches/system-fileio/0.2/--source-repository this-  type: bazaar-  location: https://john-millikin.com/branches/system-fileio/0.2/-  tag: system-fileio_0.2.7+  type: bzr+  location: https://john-millikin.com/software/hs-fileio/  library   ghc-options: -Wall -O2-  hs-source-dirs: lib    build-depends:-      base >= 4.0 && < 5.0+      base >= 3 && < 5     , bytestring >= 0.9 && < 0.10-    , system-filepath >= 0.3.1 && < 0.5-    , text >= 0.7.1 && < 0.12-    , time >= 1.0 && < 1.5+    , directory >= 1.0 && < 1.2+    , system-filepath >= 0.3 && < 0.4+    , text >= 0.1 && < 0.12+    , time >= 1.0 && < 1.3    if os(windows)     cpp-options: -DCABAL_OS_WINDOWS-    build-depends:-        Win32 >= 2.2 && < 2.3-      , directory >= 1.0 && < 1.2-    c-sources: lib/hssystemfileio-win32.c+    build-depends: Win32   else-    build-depends:-        unix >= 2.3 && < 2.6-    c-sources: lib/hssystemfileio-unix.c+    build-depends: unix    exposed-modules:     Filesystem-    System.Directory-    System.File
− tests/FilesystemTests.hs
@@ -1,30 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE ForeignFunctionInterface #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}---- Copyright (C) 2011 John Millikin <jmillikin@gmail.com>------ See license.txt for details-module Main-	( tests-	, main-	) where--import           Test.Chell--#ifdef CABAL_OS_WINDOWS-import           FilesystemTests.Windows (test_Windows)-#else-import           FilesystemTests.Posix (test_Posix)-#endif--main :: IO ()-main = Test.Chell.defaultMain tests--tests :: [Suite]-#ifdef CABAL_OS_WINDOWS-tests = [test_Windows]-#else-tests = [test_Posix]-#endif
− tests/FilesystemTests/Posix.hs
@@ -1,691 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE ForeignFunctionInterface #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}---- Copyright (C) 2011 John Millikin <jmillikin@gmail.com>------ See license.txt for details-module FilesystemTests.Posix-	( test_Posix-	) where--import           Prelude hiding (FilePath)-import           Control.Exception (bracket)-import           Control.Monad-import           Control.Monad.IO.Class (liftIO)-import qualified Data.ByteString-import           Data.ByteString (ByteString)-import qualified Data.ByteString.Char8 as Char8-import qualified Data.Text-import           Data.Text (Text)-import qualified Data.Text.IO-import           Data.Time.Clock (diffUTCTime, getCurrentTime)-import           Foreign-import           Foreign.C-import           Test.Chell--#if MIN_VERSION_base(4,2,0)-import qualified GHC.IO.Exception as GHC-#else-import qualified GHC.IOBase as GHC-#endif--import qualified System.Posix.IO as PosixIO--import           Filesystem-import           Filesystem.Path-import qualified Filesystem.Path.Rules as Rules-import qualified Filesystem.Path.CurrentOS as CurrentOS--import           FilesystemTests.Util (assertionsWithTemp, todo)--test_Posix :: Suite-test_Posix = suite "posix"-	[ suite "isFile"-		[ test_IsFile "ascii"-			(decode "test.txt")-		, test_IsFile "utf8"-			(fromText "\xA1\xA2.txt")-		, test_IsFile "iso8859"-			(decode "\xA1\xA2\xA3.txt")-		]-	, suite "isDirectory"-		[ test_IsDirectory "ascii"-			(decode "test.d")-		, test_IsDirectory "utf8"-			(fromText "\xA1\xA2.d")-		, test_IsDirectory "iso8859"-			(decode "\xA1\xA2\xA3.d")-		]-	, suite "rename"-		[ test_Rename "ascii"-			(decode "old_test.txt")-			(decode "new_test.txt")-		, test_Rename "utf8"-			(fromText "old_\xA1\xA2.txt")-			(fromText "new_\xA1\xA2.txt")-		, test_Rename "iso8859"-			(decode "old_\xA1\xA2\xA3.txt")-			(decode "new_\xA1\xA2\xA3.txt")-		]-	, suite "canonicalizePath"-		[ test_CanonicalizePath "ascii"-			(decode "test-a.txt")-			(decode "test-b.txt")-		, test_CanonicalizePath "utf8"-			(fromText "\xA1\xA2-a.txt")-			(fromText "\xA1\xA2-b.txt")-		, test_CanonicalizePath "iso8859"-			(decode "\xA1\xA2\xA3-a.txt")-#ifdef CABAL_OS_DARWIN-			(decode "%A1%A2%A3-b.txt")-#else-			(decode "\xA1\xA2\xA3-b.txt")-#endif-		]-	, suite "createDirectory"-		[ test_CreateDirectory "ascii"-			(decode "test.d")-		, test_CreateDirectory "utf8"-			(fromText "\xA1\xA2.d")-		, test_CreateDirectory "iso8859"-			(decode "\xA1\xA2\xA3.d")-		, test_CreateDirectory_FailExists-		, test_CreateDirectory_SucceedExists-		, test_CreateDirectory_FailFileExists-		]-	, suite "createTree"-		[ test_CreateTree "ascii"-			(decode "test.d")-		, test_CreateTree "ascii-slash"-			(decode "test.d/")-		, test_CreateTree "utf8"-			(fromText "\xA1\xA2.d")-		, test_CreateTree "utf8-slash"-			(fromText "\xA1\xA2.d/")-		, test_CreateTree "iso8859"-			(decode "\xA1\xA2\xA3.d")-		, test_CreateTree "iso8859-slash"-			(decode "\xA1\xA2\xA3.d/")-		]-	, test_ListDirectory-	, suite "removeFile"-		[ test_RemoveFile "ascii"-			(decode "test.txt")-		, test_RemoveFile "utf8"-			(fromText "\xA1\xA2.txt")-		, test_RemoveFile "iso8859"-			(decode "\xA1\xA2\xA3.txt")-		]-	, suite "removeDirectory"-		[ test_RemoveDirectory "ascii"-			(decode "test.d")-		, test_RemoveDirectory "utf8"-			(fromText "\xA1\xA2.d")-		, test_RemoveDirectory "iso8859"-			(decode "\xA1\xA2\xA3.d")-		]-	, suite "removeTree"-		[ test_RemoveTree "ascii"-			(decode "test.d")-		, test_RemoveTree "utf8"-			(fromText "\xA1\xA2.d")-		, test_RemoveTree "iso8859"-			(decode "\xA1\xA2\xA3.d")-		]-	, suite "getWorkingDirectory"-		[ test_GetWorkingDirectory "ascii"-			(decode "test.d")-		, test_GetWorkingDirectory "utf8"-			(fromText "\xA1\xA2.d")-		, test_GetWorkingDirectory "iso8859"-			(decode "\xA1\xA2\xA3.d")-		]-	, suite "setWorkingDirectory"-		[ test_SetWorkingDirectory "ascii"-			(decode "test.d")-		, test_SetWorkingDirectory "utf8"-			(fromText "\xA1\xA2.d")-		, test_SetWorkingDirectory "iso8859"-			(decode "\xA1\xA2\xA3.d")-		]-	, suite "getHomeDirectory"-		[ test_GetHomeDirectory "ascii"-			(decode "/home/test.d")-		, test_GetHomeDirectory "utf8"-			(decode "/home/\xA1\xA2.d")-		, test_GetHomeDirectory "iso8859"-			(decode "/home/\xA1\xA2\xA3.d")-		]-	, suite "getDesktopDirectory"-		[ test_GetDesktopDirectory "ascii"-			(decode "/desktop/test.d")-		, test_GetDesktopDirectory "utf8"-			(decode "/desktop/\xA1\xA2.d")-		, test_GetDesktopDirectory "iso8859"-			(decode "/desktop/\xA1\xA2\xA3.d")-		]-	, todo "getDocumentsDirectory"-	, todo "getAppDataDirectory"-	, todo "getAppCacheDirectory"-	, todo "getAppConfigDirectory"-	, suite "getModified"-		[ test_GetModified "ascii"-			(decode "test.txt")-		, test_GetModified "utf8"-			(fromText "\xA1\xA2.txt")-		, test_GetModified "iso8859"-			(decode "\xA1\xA2\xA3.txt")-		]-	, suite "getSize"-		[ test_GetSize "ascii"-			(decode "test.txt")-		, test_GetSize "utf8"-			(fromText "\xA1\xA2.txt")-		, test_GetSize "iso8859"-			(decode "\xA1\xA2\xA3.txt")-		]-	, suite "copyFile"-		[ test_CopyFile "ascii"-			(decode "old_test.txt")-			(decode "new_test.txt")-		, test_CopyFile "utf8"-			(fromText "old_\xA1\xA2.txt")-			(fromText "new_\xA1\xA2.txt")-		, test_CopyFile "iso8859"-			(decode "old_\xA1\xA2\xA3.txt")-			(decode "new_\xA1\xA2\xA3.txt")-		]-	, todo "openFile"-	, suite "withFile"-		[ suite "read"-			[ test_WithFile_Read "ascii"-				(decode "test.txt")-			, test_WithFile_Read "utf8"-				(fromText "\xA1\xA2.txt")-			, test_WithFile_Read "iso8859"-				(decode "\xA1\xA2\xA3.txt")-			]-		, suite "write"-			[ test_WithFile_Write "ascii"-				(decode "test.txt")-			, test_WithFile_Write "utf8"-				(fromText "\xA1\xA2.txt")-			, test_WithFile_Write "iso8859"-				(decode "\xA1\xA2\xA3.txt")-			]-		]-	, todo "readFile"-	, todo "writeFile"-	, todo "appendFile"-	, todo "openTextFile"-	, suite "withTextFile"-		[ test_WithTextFile "ascii"-			(decode "test.txt")-		, test_WithTextFile "utf8"-			(fromText "\xA1\xA2.txt")-		, test_WithTextFile "iso8859"-			(decode "\xA1\xA2\xA3.txt")-		]-	, todo "readTextFile"-	, todo "writeTextFile"-	, todo "appendTextFile"-	, suite "regression-tests"-		[ test_ListDirectoryLeaksFds-		]-	]--test_IsFile :: Text -> FilePath -> Suite-test_IsFile test_name file_name = assertionsWithTemp test_name $ \tmp -> do-	let path = tmp </> file_name-	-	before <- liftIO $ Filesystem.isFile path-	$expect (not before)-	-	touch_ffi path "contents\n"-	-	after <- liftIO $ Filesystem.isFile path-	$expect after--test_IsDirectory :: Text -> FilePath -> Suite-test_IsDirectory test_name dir_name = assertionsWithTemp test_name $ \tmp -> do-	let path = tmp </> dir_name-	-	before <- liftIO $ Filesystem.isDirectory path-	$expect (not before)-	-	mkdir_ffi path-	-	after <- liftIO $ Filesystem.isDirectory path-	$expect after--test_Rename :: Text -> FilePath -> FilePath -> Suite-test_Rename test_name old_name new_name = assertionsWithTemp test_name $ \tmp -> do-	let old_path = tmp </> old_name-	let new_path = tmp </> new_name-	-	touch_ffi old_path ""-	-	old_before <- liftIO $ Filesystem.isFile old_path-	new_before <- liftIO $ Filesystem.isFile new_path-	$expect old_before-	$expect (not new_before)-	-	liftIO $ Filesystem.rename old_path new_path-	-	old_after <- liftIO $ Filesystem.isFile old_path-	new_after <- liftIO $ Filesystem.isFile new_path-	$expect (not old_after)-	$expect new_after--test_CopyFile :: Text -> FilePath -> FilePath -> Suite-test_CopyFile test_name old_name new_name = assertionsWithTemp test_name $ \tmp -> do-	let old_path = tmp </> old_name-	let new_path = tmp </> new_name-	-	touch_ffi old_path ""-	-	old_before <- liftIO $ Filesystem.isFile old_path-	new_before <- liftIO $ Filesystem.isFile new_path-	$expect old_before-	$expect (not new_before)-	-	liftIO $ Filesystem.copyFile old_path new_path-	-	old_after <- liftIO $ Filesystem.isFile old_path-	new_after <- liftIO $ Filesystem.isFile new_path-	$expect old_after-	$expect new_after-	old_contents <- liftIO $-		Filesystem.withTextFile old_path ReadMode $-		Data.Text.IO.hGetContents-	new_contents <- liftIO $-		Filesystem.withTextFile new_path ReadMode $-		Data.Text.IO.hGetContents-	$expect (equalLines old_contents new_contents)--test_CanonicalizePath :: Text -> FilePath -> FilePath -> Suite-test_CanonicalizePath test_name src_name dst_name = assertionsWithTemp test_name $ \tmp -> do-	let src_path = tmp </> src_name-	let subdir = tmp </> "subdir"-	-	-- canonicalize the directory first, to avoid false negatives if-	-- it gets placed in a symlinked location.-	mkdir_ffi subdir-	canon_subdir <- liftIO (Filesystem.canonicalizePath subdir)-	-	let dst_path = canon_subdir </> dst_name-	-	touch_ffi dst_path ""-	symlink_ffi dst_path src_path-	-	canonicalized <- liftIO $ Filesystem.canonicalizePath src_path-	$expect $ equal canonicalized dst_path--test_CreateDirectory :: Text -> FilePath -> Suite-test_CreateDirectory test_name dir_name = assertionsWithTemp test_name $ \tmp -> do-	let dir_path = tmp </> dir_name-	-	exists_before <- liftIO $ Filesystem.isDirectory dir_path-	$assert (not exists_before)-	-	liftIO $ Filesystem.createDirectory False dir_path-	exists_after <- liftIO $ Filesystem.isDirectory dir_path-	-	$expect exists_after--test_CreateDirectory_FailExists :: Suite-test_CreateDirectory_FailExists = assertionsWithTemp "fail-if-exists" $ \tmp -> do-	let dir_path = tmp </> "subdir"-	mkdir_ffi dir_path-	-	$expect $ throwsEq-		(mkAlreadyExists "createDirectory" dir_path)-		(Filesystem.createDirectory False dir_path)--test_CreateDirectory_SucceedExists :: Suite-test_CreateDirectory_SucceedExists = assertionsWithTemp "succeed-if-exists" $ \tmp -> do-	let dir_path = tmp </> "subdir"-	mkdir_ffi dir_path-	-	liftIO $ Filesystem.createDirectory True dir_path--test_CreateDirectory_FailFileExists :: Suite-test_CreateDirectory_FailFileExists = assertionsWithTemp "fail-if-file-exists" $ \tmp -> do-	let dir_path = tmp </> "subdir"-	touch_ffi dir_path ""-	-	$expect $ throwsEq-		(mkAlreadyExists "createDirectory" dir_path)-		(Filesystem.createDirectory False dir_path)-	$expect $ throwsEq-		(mkAlreadyExists "createDirectory" dir_path)-		(Filesystem.createDirectory True dir_path)--mkAlreadyExists :: String -> FilePath -> GHC.IOError-mkAlreadyExists loc path = GHC.IOError Nothing GHC.AlreadyExists loc "File exists"-#if MIN_VERSION_base(4,2,0)-	(Just (errnoCInt eEXIST))-#endif-	(Just (CurrentOS.encodeString path))--test_CreateTree :: Text -> FilePath -> Suite-test_CreateTree test_name dir_name = assertionsWithTemp test_name $ \tmp -> do-	let dir_path = tmp </> dir_name-	let subdir = dir_path </> "subdir"-	-	dir_exists_before <- liftIO $ Filesystem.isDirectory dir_path-	subdir_exists_before <- liftIO $ Filesystem.isDirectory subdir-	$assert (not dir_exists_before)-	$assert (not subdir_exists_before)-	-	liftIO $ Filesystem.createTree subdir-	dir_exists_after <- liftIO $ Filesystem.isDirectory dir_path-	subdir_exists_after <- liftIO $ Filesystem.isDirectory subdir-	-	$expect dir_exists_after-	$expect subdir_exists_after--test_ListDirectory :: Suite-test_ListDirectory = assertionsWithTemp "listDirectory" $ \tmp -> do-	-- OSX replaces non-UTF8 filenames with http-style %XX escapes-	let paths =-#ifdef CABAL_OS_DARWIN-		[ tmp </> decode "%A1%A2%A3.txt"-		, tmp </> decode "test.txt"-		, tmp </> fromText "\xA1\xA2.txt"-		]-#else-		[ tmp </> decode "test.txt"-		, tmp </> fromText "\xA1\xA2.txt"-		, tmp </> decode "\xA1\xA2\xA3.txt"-		]-#endif-	forM_ paths (\path -> touch_ffi path "")-	-	names <- liftIO $ Filesystem.listDirectory tmp-	$expect $ sameItems paths names--test_RemoveFile :: Text -> FilePath -> Suite-test_RemoveFile test_name file_name = assertionsWithTemp test_name $ \tmp -> do-	let file_path = tmp </> file_name-	-	touch_ffi file_path "contents\n"-	-	before <- liftIO $ Filesystem.isFile file_path-	$assert before-	-	liftIO $ Filesystem.removeFile file_path-	-	after <- liftIO $ Filesystem.isFile file_path-	$expect (not after)--test_RemoveDirectory :: Text -> FilePath -> Suite-test_RemoveDirectory test_name dir_name = assertionsWithTemp test_name $ \tmp -> do-	let dir_path = tmp </> dir_name-	-	mkdir_ffi dir_path-	-	before <- liftIO $ Filesystem.isDirectory dir_path-	$assert before-	-	liftIO $ Filesystem.removeDirectory dir_path-	-	after <- liftIO $ Filesystem.isDirectory dir_path-	$expect (not after)--test_RemoveTree :: Text -> FilePath -> Suite-test_RemoveTree test_name dir_name = assertionsWithTemp test_name $ \tmp -> do-	let dir_path = tmp </> dir_name-	let subdir = dir_path </> "subdir"-	-	mkdir_ffi dir_path-	mkdir_ffi subdir-	-	dir_before <- liftIO $ Filesystem.isDirectory dir_path-	subdir_before <- liftIO $ Filesystem.isDirectory subdir-	$assert dir_before-	$assert subdir_before-	-	liftIO $ Filesystem.removeTree dir_path-	-	dir_after <- liftIO $ Filesystem.isDirectory dir_path-	subdir_after <- liftIO $ Filesystem.isDirectory subdir-	$expect (not dir_after)-	$expect (not subdir_after)--test_GetWorkingDirectory :: Text -> FilePath -> Suite-test_GetWorkingDirectory test_name dir_name = assertionsWithTemp test_name $ \tmp -> do-	-- canonicalize to avoid issues with symlinked temp dirs-	canon_tmp <- liftIO (Filesystem.canonicalizePath tmp)-	let dir_path = canon_tmp </> dir_name-	-	mkdir_ffi dir_path-	chdir_ffi dir_path-	-	cwd <- liftIO $ Filesystem.getWorkingDirectory-	$expect (equal cwd dir_path)--test_SetWorkingDirectory :: Text -> FilePath -> Suite-test_SetWorkingDirectory test_name dir_name = assertionsWithTemp test_name $ \tmp -> do-	-- canonicalize to avoid issues with symlinked temp dirs-	canon_tmp <- liftIO (Filesystem.canonicalizePath tmp)-	let dir_path = canon_tmp </> dir_name-	-	mkdir_ffi dir_path-	liftIO $ Filesystem.setWorkingDirectory dir_path-	-	cwd <- getcwd_ffi-	$expect (equal cwd dir_path)--test_GetHomeDirectory :: Text -> FilePath -> Suite-test_GetHomeDirectory test_name dir_name = assertions test_name $ do-	path <- liftIO $ withEnv "HOME" (Just dir_name) Filesystem.getHomeDirectory-	$expect (equal path dir_name)--test_GetDesktopDirectory :: Text -> FilePath -> Suite-test_GetDesktopDirectory test_name dir_name = assertions test_name $ do-	path <- liftIO $-		withEnv "XDG_DESKTOP_DIR" (Just dir_name) $-		Filesystem.getDesktopDirectory-	$expect (equal path dir_name)-	-	fallback <- liftIO $-		withEnv "XDG_DESKTOP_DIR" Nothing $-		withEnv "HOME" (Just dir_name) $-		Filesystem.getDesktopDirectory-	$expect (equal fallback (dir_name </> "Desktop"))--test_GetModified :: Text -> FilePath -> Suite-test_GetModified test_name file_name = assertionsWithTemp test_name $ \tmp -> do-	let file_path = tmp </> file_name-	-	touch_ffi file_path ""-	now <- liftIO getCurrentTime-	-	mtime <- liftIO $ Filesystem.getModified file_path-	$expect (equalWithin (diffUTCTime mtime now) 0 2)--test_GetSize :: Text -> FilePath -> Suite-test_GetSize test_name file_name = assertionsWithTemp test_name $ \tmp -> do-	let file_path = tmp </> file_name-	let contents = "contents\n"-	-	touch_ffi file_path contents-	-	size <- liftIO $ Filesystem.getSize file_path-	$expect (equal size (toInteger (Data.ByteString.length contents)))--test_WithFile_Read :: Text -> FilePath -> Suite-test_WithFile_Read test_name file_name = assertionsWithTemp test_name $ \tmp -> do-	let file_path = tmp </> file_name-	let contents = "contents\n"-	-	touch_ffi file_path contents-	-	read_contents <- liftIO $-		Filesystem.withFile file_path ReadMode $-		Data.ByteString.hGetContents-	$expect (equalLines contents read_contents)--test_WithFile_Write :: Text -> FilePath -> Suite-test_WithFile_Write test_name file_name = assertionsWithTemp test_name $ \tmp -> do-	let file_path = tmp </> file_name-	let contents = "contents\n"-	-	liftIO $-		Filesystem.withFile file_path WriteMode $-		(\h -> Data.ByteString.hPut h contents)-	-	read_contents <- liftIO $-		Filesystem.withFile file_path ReadMode $-		Data.ByteString.hGetContents-	$expect (equalLines contents read_contents)--test_WithTextFile :: Text -> FilePath -> Suite-test_WithTextFile test_name file_name = assertionsWithTemp test_name $ \tmp -> do-	let file_path = tmp </> file_name-	let contents = "contents\n"-	-	touch_ffi file_path (Char8.pack contents)-	-	read_contents <- liftIO $-		Filesystem.withTextFile file_path ReadMode $-		Data.Text.IO.hGetContents-	$expect (equalLines (Data.Text.pack contents) read_contents)--test_ListDirectoryLeaksFds :: Suite-test_ListDirectoryLeaksFds = assertionsWithTemp "listDirectory-leaks-fds" $ \tmp -> do-	-- Test that listDirectory doesn't leak file descriptors.-	let dir_path = tmp </> "subdir"-	mkdir_ffi dir_path-	-	nullfd1 <- liftIO $ PosixIO.openFd "/dev/null" PosixIO.ReadOnly Nothing PosixIO.defaultFileFlags-	liftIO $ PosixIO.closeFd nullfd1-	-	subdirContents <- liftIO $ listDirectory dir_path-	-	nullfd2 <- liftIO $ PosixIO.openFd "/dev/null" PosixIO.ReadOnly Nothing PosixIO.defaultFileFlags-	liftIO $ PosixIO.closeFd nullfd2-	-	$assert (equal nullfd1 nullfd2)--withPathCString :: FilePath -> (CString -> IO a) -> IO a-withPathCString p = Data.ByteString.useAsCString (encode p)--decode :: ByteString -> FilePath-decode = Rules.decode Rules.posix--encode :: FilePath -> ByteString-encode = Rules.encode Rules.posix--fromText :: Text -> FilePath-fromText = Rules.fromText Rules.posix---- | Create a file using the raw POSIX API, via FFI-touch_ffi :: FilePath -> Data.ByteString.ByteString -> Assertions ()-touch_ffi path contents = do-	fp <- liftIO $ withPathCString path $ \path_cstr ->-		Foreign.C.withCString "wb" $ \mode_cstr ->-		c_fopen path_cstr mode_cstr-	-	$assert (fp /= nullPtr)-	-	_ <- liftIO $ Data.ByteString.useAsCStringLen contents $ \(buf, len) ->-		c_fwrite buf 1 (fromIntegral len) fp-	-	_ <- liftIO $ c_fclose fp-	return ()---- | Create a directory using the raw POSIX API, via FFI-mkdir_ffi :: FilePath -> Assertions ()-mkdir_ffi path = do-	ret <- liftIO $ withPathCString path $ \path_cstr ->-		c_mkdir path_cstr 0o700-	-	$assert (ret == 0)---- | Create a symlink using the raw POSIX API, via FFI-symlink_ffi :: FilePath -> FilePath -> Assertions ()-symlink_ffi dst src  = do-	ret <- liftIO $-		withPathCString dst $ \dst_p ->-		withPathCString src $ \src_p ->-		c_symlink dst_p src_p-	-	$assert (ret == 0)--getcwd_ffi :: Assertions FilePath-getcwd_ffi = do-	buf <- liftIO $ c_getcwd nullPtr 0-	$assert (buf /= nullPtr)-	bytes <- liftIO $ Data.ByteString.packCString buf-	liftIO $ c_free buf-	return (decode bytes)--chdir_ffi :: FilePath -> Assertions ()-chdir_ffi path = do-	ret <- liftIO $-		withPathCString path $ \path_p ->-		c_chdir path_p-	$assert (ret == 0)--errnoCInt :: Errno -> CInt-errnoCInt (Errno x) = x--withEnv :: ByteString -> Maybe FilePath -> IO a -> IO a-withEnv name val io = bracket set unset (\_ -> io) where-	set = do-		old <- getEnv name-		setEnv name (fmap encode val)-		return old-	unset = setEnv name--getEnv :: ByteString -> IO (Maybe ByteString)-getEnv name = Data.ByteString.useAsCString name $ \cName -> do-	ret <- liftIO (c_getenv cName)-	if ret == nullPtr-		then return Nothing-		else fmap Just (Data.ByteString.packCString ret)--setEnv :: ByteString -> Maybe ByteString -> IO ()-setEnv name Nothing = throwErrnoIfMinus1_ "setEnv" $-	Data.ByteString.useAsCString name c_unsetenv-setEnv name (Just val) = throwErrnoIfMinus1_ "setEnv" $-	Data.ByteString.useAsCString name $ \cName ->-	Data.ByteString.useAsCString val $ \cVal ->-	c_setenv cName cVal 1--foreign import ccall unsafe "fopen"-	c_fopen :: CString -> CString -> IO (Ptr ())--foreign import ccall unsafe "fclose"-	c_fclose :: Ptr () -> IO CInt--foreign import ccall unsafe "fwrite"-	c_fwrite :: CString -> CSize -> CSize -> Ptr () -> IO CSize--foreign import ccall unsafe "mkdir"-	c_mkdir :: CString -> CInt -> IO CInt--foreign import ccall unsafe "symlink"-	c_symlink :: CString -> CString -> IO CInt--foreign import ccall unsafe "getcwd"-	c_getcwd :: CString -> CSize -> IO CString--foreign import ccall unsafe "chdir"-	c_chdir :: CString -> IO CInt--foreign import ccall unsafe "free"-	c_free :: Ptr a -> IO ()--foreign import ccall unsafe "getenv"-	c_getenv :: CString -> IO CString--foreign import ccall unsafe "setenv"-	c_setenv :: CString -> CString -> CInt -> IO CInt--foreign import ccall unsafe "unsetenv"-	c_unsetenv :: CString -> IO CInt
− tests/FilesystemTests/Util.hs
@@ -1,37 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}---- Copyright (C) 2011 John Millikin <jmillikin@gmail.com>------ See license.txt for details-module FilesystemTests.Util-	( assertionsWithTemp-	, todo-	) where--import           Prelude hiding (FilePath)--import           Control.Exception (finally)-import           Data.Text (Text, unpack)-import           System.IO.Temp (withSystemTempDirectory)-import           Test.Chell--import           Filesystem (removeTree)-import           Filesystem.Path.CurrentOS (FilePath, decodeString)--assertionsWithTemp :: Text -> (FilePath -> Assertions a) -> Suite-assertionsWithTemp name io = test (Test name impl) where-	impl options = withTempDir name $ \dir -> do-		case suiteTests (assertions name (io dir)) of-			[(Test _ io')] -> io' options-			_ -> error "assertionsWithTemp: use in place of 'assertions' only."--withTempDir :: Text -> (FilePath -> IO a) -> IO a-withTempDir name io = withSystemTempDirectory-	("tests." ++ unpack name ++ ".")-	(\dir ->-		let dir' = decodeString dir in-		finally (io dir') (removeTree dir'))--todo :: Text -> Suite-todo name = skipIf True (assertions name (return ()))
− tests/FilesystemTests/Windows.hs
@@ -1,66 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}---- Copyright (C) 2011 John Millikin <jmillikin@gmail.com>------ See license.txt for details-module FilesystemTests.Windows-	( test_Windows-	) where--import           Control.Monad-import           Control.Monad.IO.Class (liftIO)-import           Test.Chell--import           Filesystem-import           Filesystem.Path.CurrentOS--import           FilesystemTests.Util (assertionsWithTemp, todo)--test_Windows :: Suite-test_Windows = suite "windows"-	[ todo "isFile"-	, todo "isDirectory"-	, todo "rename"-	, todo "canonicalizePath"-	, todo "createDirectory"-	, todo "createTree"-	, test_ListDirectory-	, todo "removeFile"-	, todo "removeDirectory"-	, todo "removeTree"-	, todo "getWorkingDirectory"-	, todo "setWorkingDirectory"-	, todo "getHomeDirectory"-	, todo "getDesktopDirectory"-	, todo "getDocumentsDirectory"-	, todo "getAppDataDirectory"-	, todo "getAppCacheDirectory"-	, todo "getAppConfigDirectory"-	, todo "copyFile"-	, todo "getModified"-	, todo "getSize"-	, todo "openFile"-	, todo "withFile"-	, todo "readFile"-	, todo "writeFile"-	, todo "appendFile"-	, todo "openTextFile"-	, todo "withTextFile"-	, todo "readTextFile"-	, todo "writeTextFile"-	, todo "appendTextFile"-	]--test_ListDirectory :: Suite-test_ListDirectory = assertionsWithTemp "listDirectory" $ \dir -> do-	let paths =-		[ dir </> decode "test.txt"-		, dir </> decode "\12354\946\1076\119070.txt"-		, dir </> decode "\xA1\xA2\xA3.txt"-		]-	-	liftIO $ forM_ paths (\path -> writeTextFile path "")-	-	names <- liftIO $ Filesystem.listDirectory dir-	$expect $ sameItems paths names
− tests/system-fileio-tests.cabal
@@ -1,42 +0,0 @@-name: system-fileio-tests-version: 0-build-type: Simple-cabal-version: >= 1.6--flag coverage-  default: False-  manual: True--executable system-fileio_tests-  main-is: FilesystemTests.hs-  ghc-options: -Wall-  hs-source-dirs: ../lib,.--  if flag(coverage)-    ghc-options: -fhpc--  build-depends:-      base >= 4.0 && < 5.0-    , bytestring >= 0.9 && < 0.10-    , chell >= 0.2 && < 0.3-    , system-filepath >= 0.3 && < 0.5-    , temporary >= 1.1 && < 2.0-    , text >= 0.1 && < 0.12-    , time >= 1.0 && < 1.5-    , transformers >= 0.2 && < 0.3--  if os(windows)-    cpp-options: -DCABAL_OS_WINDOWS--  if os(darwin)-    cpp-options: -DCABAL_OS_DARWIN--  if os(windows)-    build-depends:-        Win32 >= 2.2 && < 2.3-      , directory >= 1.0 && < 1.2-    c-sources: ../lib/hssystemfileio-win32.c-  else-    build-depends:-        unix >= 2.3 && < 2.6-    c-sources: ../lib/hssystemfileio-unix.c