{-# 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.moduleFilesystem(-- * Exports from System.IOIO.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#endifimportPreludehiding(FilePath,readFile,writeFile,appendFile)importqualifiedControl.ExceptionasExcimportControl.Monad(forM_,unless,when)importqualifiedData.ByteStringasBimportqualifiedData.ByteString.LazyasBLimportqualifiedData.TextasTimportqualifiedData.Text.IOasTimportForeign.Ptr(Ptr,nullPtr)importForeign.C(CInt,CString,withCAString)importqualifiedForeign.C.ErrorasCErrorimportqualifiedSystem.EnvironmentasSE#if MIN_VERSION_system_filepath(0,4,0)importFilesystem.Path(FilePath,append)importqualifiedFilesystem.PathasPathimportFilesystem.Path.CurrentOS(currentOS,encodeString,decodeString)importqualifiedFilesystem.Path.RulesasR#elseimportSystem.FilePath(FilePath,append)importqualifiedSystem.FilePathasPathimportSystem.FilePath.CurrentOS(currentOS,encodeString,decodeString)importqualifiedSystem.FilePath.RulesasR#endifimportqualifiedSystem.IOasIOimportSystem.IO.Error(IOError)#ifdef CABAL_OS_WINDOWSimportData.Bits((.|.))importData.Time(UTCTime(..),fromGregorian,secondsToDiffTime,picosecondsToDiffTime)importForeign.C(CWString,withCWString)importqualifiedSystem.Win32asWin32importSystem.IO.Error(isDoesNotExistError)importqualified"directory"System.DirectoryasSD#elseimportData.Time(UTCTime)importData.Time.Clock.POSIX(posixSecondsToUTCTime)importqualifiedSystem.PosixasPosiximportqualifiedSystem.Posix.ErrorasPosix#endif#ifdef SYSTEMFILEIO_LOCAL_OPEN_FILEimportData.Bits((.|.))importGHC.IO.Handle.FD(mkHandleFromFD)importGHC.IO.FD(mkFD)importqualifiedGHC.IO.DeviceimportqualifiedSystem.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->IOBool#ifdef CABAL_OS_WINDOWSisFilepath=SD.doesFileExist(encodeStringpath)#elseisFilepath=Exc.catch(dostat<-posixStat"isFile"pathreturn(not(Posix.isDirectorystat)))((\_->returnFalse)::IOError->IOBool)#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->IOBool#ifdef CABAL_OS_WINDOWSisDirectorypath=SD.doesDirectoryExist(encodeStringpath)#elseisDirectorypath=Exc.catch(dostat<-posixStat"isFile"pathreturn(Posix.isDirectorystat))((\_->returnFalse)::IOError->IOBool)#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()renameoldnew=#ifdef CABAL_OS_WINDOWSletold'=encodeStringoldinletnew'=encodeStringnewinWin32.moveFileExold'new'Win32.mOVEFILE_REPLACE_EXISTING#elsewithFilePathold$\old'->withFilePathnew$\new'->throwErrnoPathIfMinus1_"rename"old(c_renameold'new')foreignimportccallunsafe"rename"c_rename::CString->CString->IOCInt#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.1canonicalizePath::FilePath->IOFilePathcanonicalizePathpath=letpath'=encodeStringpathin#ifdef CABAL_OS_WINDOWSfmapdecodeString$#if MIN_VERSION_Win32(2,2,1)Win32.getFullPathNamepath'#elseWin32.withTStringpath'$\c_name->doWin32.try"getFullPathName"(\buflen->c_GetFullPathNameWc_namelenbufnullPtr)512#endif#elsewithFilePathpath$\cPath->docOut<-Posix.throwErrnoPathIfNull"canonicalizePath"path'(c_realpathcPathnullPtr)bytes<-B.packCStringcOutc_freecOutreturn(R.decodeR.posixbytes)#endif#ifdef CABAL_OS_WINDOWS#if MIN_VERSION_Win32(2,2,1)#elseforeignimportstdcallunsafe"GetFullPathNameW"c_GetFullPathNameW::Win32.LPCTSTR->Win32.DWORD->Win32.LPTSTR->PtrWin32.LPTSTR->IOWin32.DWORD#endif#endif#ifndef CABAL_OS_WINDOWSforeignimportccallunsafe"realpath"c_realpath::CString->CString->IOCString#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()createDirectorysucceedIfExistspath=#ifdef CABAL_OS_WINDOWSletpath'=encodeStringpathinifsucceedIfExiststhenSD.createDirectoryIfMissingFalsepath'elseWin32.createDirectorypath'Nothing#elsewithFilePathpath$\cPath->throwErrnoPathIfMinus1Retry_"createDirectory"path$ifsucceedIfExiststhenmkdirIfMissingpathcPath0o777elsec_mkdircPath0o777mkdirIfMissing::FilePath->CString->CInt->IOCIntmkdirIfMissingpathcPathmode=dorc<-c_mkdircPathmodeifrc==-1thendoerrno<-CError.getErrnoiferrno==CError.eEXISTthendodirExists<-isDirectorypathifdirExiststhenreturn0elsereturnrcelsereturnrcelsereturnrcforeignimportccallunsafe"mkdir"c_mkdir::CString->CInt->IOCInt#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_WINDOWScreateTreepath=SD.createDirectoryIfMissingTrue(encodeStringpath)#elsecreateTreepath=doletparent=Path.parentpathparentExists<-isDirectoryparentunlessparentExists(createTreeparent)withFilePathpath$\cPath->throwErrnoPathIfMinus1Retry_"createTree"path(mkdirIfMissingpathcPath0o777)#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_WINDOWSlistDirectoryroot=fmapcleanupcontentswherecontents=SD.getDirectoryContents(encodeStringroot)cleanup=map(appendroot).mapdecodeString.filter(`notElem`[".",".."])#elselistDirectoryroot=Exc.bracketallocfreelistwherealloc=dodirent<-c_alloc_direntdir<-openDirrootreturn(dirent,dir)free(dirent,dir)=doc_free_direntdirentcloseDirdirlist(dirent,dir)=loopwhereloop=donext<-readDirdirdirentcasenextofNothing->return[]Justbytes|ignorebytes->loopJustbytes->doletname=appendroot(R.decodeR.posixbytes)names<-loopreturn(name:names)ignore::B.ByteString->Boolignore=ignore'wheredot=B.pack[46]dotdot=B.pack[46,46]ignore'b=b==dot||b==dotdotdataDir=DirFilePath(Ptr())openDir::FilePath->IODiropenDirroot=withFilePathroot$\cRoot->dop<-throwErrnoPathIfNullRetry"listDirectory"root(c_opendircRoot)return(Dirrootp)closeDir::Dir->IO()closeDir(Dir_p)=CError.throwErrnoIfMinus1Retry_"listDirectory"(c_closedirp)readDir::Dir->Ptr()->IO(MaybeB.ByteString)readDir(Dir_p)dirent=dorc<-CError.throwErrnoIfMinus1Retry"listDirectory"(c_readdirpdirent)ifrc==0thendobytes<-c_dirent_namedirent>>=B.packCStringreturn(Justbytes)elsereturnNothingforeignimportccallunsafe"opendir"c_opendir::CString->IO(Ptr())foreignimportccallunsafe"opendir"c_closedir::Ptr()->IOCIntforeignimportccallunsafe"hssystemfileio_alloc_dirent"c_alloc_dirent::IO(Ptr())foreignimportccallunsafe"hssystemfileio_free_dirent"c_free_dirent::Ptr()->IO()foreignimportccallunsafe"hssystemfileio_readdir"c_readdir::Ptr()->Ptr()->IOCIntforeignimportccallunsafe"hssystemfileio_dirent_name"c_dirent_name::Ptr()->IOCString#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()removeFilepath=#ifdef CABAL_OS_WINDOWSWin32.deleteFile(encodeStringpath)#elsewithFilePathpath$\cPath->throwErrnoPathIfMinus1_"removeFile"path(c_unlinkcPath)foreignimportccallunsafe"unlink"c_unlink::CString->IOCInt#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()removeDirectorypath=#ifdef CABAL_OS_WINDOWSWin32.removeDirectory(encodeStringpath)#elsewithFilePathpath$\cPath->throwErrnoPathIfMinus1Retry_"removeDirectory"path(c_rmdircPath)foreignimportccallunsafe"rmdir"c_rmdir::CString->IOCInt#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_WINDOWSremoveTreeroot=SD.removeDirectoryRecursive(encodeStringroot)#elseremoveTreeroot=doitems<-listDirectoryrootforM_items$\item->Exc.catch(removeFileitem)(\exc->doisDir<-isRealDiritemifisDirthenremoveTreeitemelseExc.throwIO(exc::IOError))removeDirectoryroot-- 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->IOBoolisRealDirpath=withFilePathpath$\cPath->dorc<-throwErrnoPathIfMinus1Retry"removeTree"path(c_isrealdircPath)return(rc==1)foreignimportccallunsafe"hssystemfileio_isrealdir"c_isrealdir::CString->IOCInt#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::IOFilePathgetWorkingDirectory=do#ifdef CABAL_OS_WINDOWS#if MIN_VERSION_Win32(2,2,1)fmapdecodeStringWin32.getCurrentDirectory#elsefmapdecodeString(Win32.try"getWorkingDirectory"(flipc_GetCurrentDirectoryW)512)#endif#elsebuf<-CError.throwErrnoIfNull"getWorkingDirectory"c_getcwdbytes<-B.packCStringbufc_freebufreturn(R.decodeR.posixbytes)foreignimportccallunsafe"hssystemfileio_getcwd"c_getcwd::IOCString#endif#ifdef CABAL_OS_WINDOWS#if MIN_VERSION_Win32(2,2,1)#elseforeignimportstdcallunsafe"GetCurrentDirectoryW"c_GetCurrentDirectoryW::Win32.DWORD->Win32.LPTSTR->IOWin32.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()setWorkingDirectorypath=#ifdef CABAL_OS_WINDOWSWin32.setCurrentDirectory(encodeStringpath)#elsewithFilePathpath$\cPath->throwErrnoPathIfMinus1Retry_"setWorkingDirectory"path(c_chdircPath)foreignimportccallunsafe"chdir"c_chdir::CString->IOCInt#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::IOFilePath#ifdef CABAL_OS_WINDOWSgetHomeDirectory=fmapdecodeStringSD.getHomeDirectory#elsegetHomeDirectory=dopath<-getenv"HOME"casepathofJustp->returnpNothing->do-- use getEnv to throw the right exception typefmapdecodeString(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::IOFilePathgetDesktopDirectory=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::IOFilePathgetDocumentsDirectory=xdg"XDG_DOCUMENTS_DIR"Nothing#ifdef CABAL_OS_WINDOWS(fmapdecodeStringSD.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->IOFilePathgetAppDataDirectorylabel=xdg"XDG_DATA_HOME"(Justlabel)#ifdef CABAL_OS_WINDOWS(fmapdecodeString(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->IOFilePathgetAppCacheDirectorylabel=xdg"XDG_CACHE_HOME"(Justlabel)#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->IOFilePathgetAppConfigDirectorylabel=xdg"XDG_CONFIG_HOME"(Justlabel)#ifdef CABAL_OS_WINDOWS(homeSlash"Local Settings")#else(homeSlash".config")#endifhomeSlash::String->IOFilePathhomeSlashpath=dohome<-getHomeDirectoryreturn(appendhome(decodeStringpath))getenv::String->IO(MaybeFilePath)#ifdef CABAL_OS_WINDOWSgetenvkey=Exc.catch(fmap(Just.decodeString)(SE.getEnvkey))(\e->ifisDoesNotExistErrorethenreturnNothingelseExc.throwIOe)#elsegetenvkey=withCAStringkey$\cKey->doret<-c_getenvcKeyifret==nullPtrthenreturnNothingelsedobytes<-B.packCStringretreturn(Just(R.decodeR.posixbytes))foreignimportccallunsafe"getenv"c_getenv::CString->IOCString#endifxdg::String->MaybeT.Text->IOFilePath->IOFilePathxdgenvkeylabelfallback=doenv<-getenvenvkeydir<-caseenvofJustvar->returnvarNothing->fallbackreturn$caselabelofJusttext->appenddir(R.fromTextcurrentOStext)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.4copyFileContent::FilePath-- ^ Old location->FilePath-- ^ New location->IO()copyFileContentoldPathnewPath=withFileoldPathIO.ReadMode$\old->withFilenewPathIO.WriteMode$\new->BL.hGetContentsold>>=BL.hPutnew-- | 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.4copyPermissions::FilePath-- ^ Old location->FilePath-- ^ New location->IO()copyPermissionsoldPathnewPath=withFilePatholdPath$\cOldPath->withFilePathnewPath$\cNewPath->CError.throwErrnoIfMinus1Retry_"copyPermissions"$c_copy_permissionscOldPathcNewPath#ifdef CABAL_OS_WINDOWSforeignimportccallunsafe"hssystemfileio_copy_permissions"c_copy_permissions::CWString->CWString->IOCInt#elseforeignimportccallunsafe"hssystemfileio_copy_permissions"c_copy_permissions::CString->CString->IOCInt#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.1copyFile::FilePath-- ^ Old location->FilePath-- ^ New location->IO()copyFileoldPathnewPath=docopyFileContentoldPathnewPathExc.catch(copyPermissionsoldPathnewPath)((\_->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.2getModified::FilePath->IOUTCTimegetModifiedpath=do#ifdef CABAL_OS_WINDOWSinfo<-withHANDLEpathWin32.getFileInformationByHandleletftime=Win32.bhfiLastWriteTimeinfostime<-Win32.fileTimeToSystemTimeftimeletdate=fromGregorian(fromIntegral(Win32.wYearstime))(fromIntegral(Win32.wMonthstime))(fromIntegral(Win32.wDaystime))letseconds=secondsToDiffTime$(toInteger(Win32.wHourstime)*3600)+(toInteger(Win32.wMinutestime)*60)+(toInteger(Win32.wSecondstime))letmsecs=picosecondsToDiffTime$(toInteger(Win32.wMillisecondsstime)*1000000000)return(UTCTimedate(seconds+msecs))#elsestat<-posixStat"getModified"pathletmtime=Posix.modificationTimestatreturn(posixSecondsToUTCTime(realToFracmtime))#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.2getSize::FilePath->IOIntegergetSizepath=do#ifdef CABAL_OS_WINDOWSinfo<-withHANDLEpathWin32.getFileInformationByHandlereturn(toInteger(Win32.bhfiSizeinfo))#elsestat<-posixStat"getSize"pathreturn(toInteger(Posix.fileSizestat))#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->IOIO.Handle#ifdef SYSTEMFILEIO_LOCAL_OPEN_FILEopenFilepathmode=openFile'"openFile"pathmodeNothing#elseopenFilepath=IO.openBinaryFile(encodeStringpath)#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->IOa)->IOawithFilepathmode=Exc.bracket(openFilepathmode)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->IOB.ByteStringreadFilepath=withFilepathIO.ReadMode(\h->IO.hFileSizeh>>=B.hGeth.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()writeFilepathbytes=withFilepathIO.WriteMode(\h->B.hPuthbytes)-- | 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()appendFilepathbytes=withFilepathIO.AppendMode(\h->B.hPuthbytes)-- | 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->IOIO.Handle#ifdef SYSTEMFILEIO_LOCAL_OPEN_FILEopenTextFilepathmode=openFile'"openTextFile"pathmode(JustIO.localeEncoding)#elseopenTextFilepath=IO.openFile(encodeStringpath)#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->IOa)->IOawithTextFilepathmode=Exc.bracket(openTextFilepathmode)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->IOT.TextreadTextFilepath=openTextFilepathIO.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()writeTextFilepathtext=withTextFilepathIO.WriteMode(\h->T.hPutStrhtext)-- | 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()appendTextFilepathtext=withTextFilepathIO.AppendMode(\h->T.hPutStrhtext)#ifdef SYSTEMFILEIO_LOCAL_OPEN_FILE-- | Copied from GHC.IO.FD.openFileopenFile'::String->FilePath->IO.IOMode->(MaybeIO.TextEncoding)->IOIO.HandleopenFile'locpathmodecodec=openwheresys_c_open=System.Posix.Internals.c_opensys_c_close=System.Posix.Internals.c_closeflags=iomodeFlagsmodeopen=withFilePathpath$\cPath->doc_fd<-throwErrnoPathIfMinus1Retrylocpath(sys_c_opencPathflags0o666)(fd,fd_type)<-Exc.onException(mkFDc_fdmodeNothingFalseTrue)(sys_c_closec_fd)when(mode==IO.WriteMode&&fd_type==GHC.IO.Device.RegularFile)$doGHC.IO.Device.setSizefd0Exc.onException(mkHandleFromFDfdfd_type(encodeStringpath)modeFalsecodec)(GHC.IO.Device.closefd)iomodeFlags::IO.IOMode->CIntiomodeFlagsmode=cased.|.commonFlagswherecased=casemodeofIO.ReadMode->flagsR#ifdef mingw32_HOST_OSIO.WriteMode->flagsW.|.System.Posix.Internals.o_TRUNC#elseIO.WriteMode->flagsW#endifIO.ReadWriteMode->flagsRWIO.AppendMode->flagsAflagsR=System.Posix.Internals.o_RDONLYflagsW=outputFlags.|.System.Posix.Internals.o_WRONLYflagsRW=outputFlags.|.System.Posix.Internals.o_RDWRflagsA=flagsW.|.System.Posix.Internals.o_APPENDcommonFlags=System.Posix.Internals.o_NOCTTY.|.System.Posix.Internals.o_NONBLOCKoutputFlags=System.Posix.Internals.o_CREAT#endif#ifdef CABAL_OS_WINDOWSwithHANDLE::FilePath->(Win32.HANDLE->IOa)->IOawithHANDLEpath=Exc.bracketopenclosewhereopen=Win32.createFile(encodeStringpath)Win32.gENERIC_READ(Win32.fILE_SHARE_READ.|.Win32.fILE_SHARE_WRITE)NothingWin32.oPEN_EXISTING0Nothingclose=Win32.closeHandlewithFilePath::FilePath->(CWString->IOa)->IOawithFilePathpath=withCWString(encodeStringpath)#elsewithFilePath::FilePath->(CString->IOa)->IOawithFilePathpath=B.useAsCString(R.encodeR.posixpath)throwErrnoPathIfMinus1::String->FilePath->IOCInt->IOCIntthrowErrnoPathIfMinus1locpath=CError.throwErrnoPathIfMinus1loc(encodeStringpath)throwErrnoPathIfMinus1_::String->FilePath->IOCInt->IO()throwErrnoPathIfMinus1_locpath=CError.throwErrnoPathIfMinus1_loc(encodeStringpath)throwErrnoPathIfNullRetry::String->FilePath->IO(Ptra)->IO(Ptra)throwErrnoPathIfNullRetry=throwErrnoPathIfRetry(==nullPtr)throwErrnoPathIfMinus1Retry::String->FilePath->IOCInt->IOCIntthrowErrnoPathIfMinus1Retry=throwErrnoPathIfRetry(==-1)throwErrnoPathIfMinus1Retry_::String->FilePath->IOCInt->IO()throwErrnoPathIfMinus1Retry_=throwErrnoPathIfRetry_(==-1)throwErrnoPathIfRetry::(a->Bool)->String->FilePath->IOa->IOathrowErrnoPathIfRetryfailedlocpathio=loopwhereloop=doa<-ioiffailedathendoerrno<-CError.getErrnoiferrno==CError.eINTRthenloopelseCError.throwErrnoPathloc(encodeStringpath)elsereturnathrowErrnoPathIfRetry_::(a->Bool)->String->FilePath->IOa->IO()throwErrnoPathIfRetry_failedlocpathio=do_<-throwErrnoPathIfRetryfailedlocpathioreturn()withFd::String->FilePath->(Posix.Fd->IOa)->IOawithFdfnNamepath=Exc.bracketopenclosewhereopen=withFilePathpath$\cpath->dofd<-throwErrnoPathIfMinus1fnNamepath(c_opencpath0)return(Posix.Fdfd)close=Posix.closeFdposixStat::String->FilePath->IOPosix.FileStatusposixStatlocpath=withFdlocpathPosix.getFdStatusforeignimportccallunsafe"open"c_open::CString->CInt->IOCIntforeignimportccallunsafe"free"c_free::Ptra->IO()#endif