safer-file-handles 0.5 → 0.6
raw patch · 9 files changed
+635/−686 lines, 9 filesdep −bytestringdep ~explicit-iomodesdep ~regions
Dependencies removed: bytestring
Dependency ranges changed: explicit-iomodes, regions
Files
- Data/ByteString/Char8/SaferFileHandles.hs +0/−100
- Data/ByteString/Lazy/Char8/SaferFileHandles.hs +0/−75
- Data/ByteString/Lazy/SaferFileHandles.hs +0/−86
- Data/ByteString/SaferFileHandles.hs +0/−99
- LICENSE +1/−1
- System/IO/SaferFileHandles.hs +528/−186
- System/IO/SaferFileHandles/Internal.hs +28/−128
- System/IO/SaferFileHandles/Unsafe.hs +70/−0
- safer-file-handles.cabal +8/−11
− Data/ByteString/Char8/SaferFileHandles.hs
@@ -1,100 +0,0 @@-{-# LANGUAGE UnicodeSyntax, NoImplicitPrelude #-}------------------------------------------------------------------------------------ |--- Module : Data.ByteString.SaferFileHandles--- Copyright : (c) 2010 Bas van Dijk--- License : BSD3 (see the file LICENSE)--- Maintainer : Bas van Dijk <v.dijk.bas@gmail.com>------ This module lifts the bytestring IO operations into the region monad.-------------------------------------------------------------------------------------module Data.ByteString.Char8.SaferFileHandles- ( hGetLine- , hGetContents- , hGet- , hGetNonBlocking-- , hPut- , hPutStr- , hPutStrLn- ) where------------------------------------------------------------------------------------ Imports------------------------------------------------------------------------------------ from base:-import Data.Int ( Int )---- from transformers:-import Control.Monad.IO.Class ( MonadIO )---- from bytestring:-import qualified Data.ByteString.Char8 as B---- from regions:-import Control.Monad.Trans.Region ( ParentOf )---- from explicit-iomodes:-import qualified Data.ByteString.Char8.ExplicitIOModes as E ( hGetLine- , hGetContents- , hGet- , hGetNonBlocking-- , hPut- , hPutStr- , hPutStrLn- )---- from ourselves:-import System.IO.SaferFileHandles ( RegionalFileHandle- , ReadModes, WriteModes- )-import System.IO.SaferFileHandles.Internal ( wrap, wrap2 )------------------------------------------------------------------------------------- ByteString I/O with regional file handles------------------------------------------------------------------------------------ | Wraps @Data.ByteString.@'B.hGetLine'.-hGetLine ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → cr B.ByteString-hGetLine = wrap E.hGetLine---- | Wraps @Data.ByteString.@'B.hGetContents'.-hGetContents ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → cr B.ByteString-hGetContents = wrap E.hGetContents---- | Wraps @Data.ByteString.@'B.hGet'.-hGet ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → Int → cr B.ByteString-hGet = wrap2 E.hGet---- | Wraps @Data.ByteString.@'B.hGetNonBlocking'.-hGetNonBlocking ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → Int → cr B.ByteString-hGetNonBlocking = wrap2 E.hGetNonBlocking----- | Wraps @Data.ByteString.@'B.hPut'.-hPut ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)- ⇒ RegionalFileHandle ioMode pr → B.ByteString → cr ()-hPut = wrap2 E.hPut---- | Wraps @Data.ByteString.@'B.hPutStr'.-hPutStr ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)- ⇒ RegionalFileHandle ioMode pr → B.ByteString → cr ()-hPutStr = wrap2 E.hPutStr---- | Wraps @Data.ByteString.@'B.hPutStrLn'.-hPutStrLn ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)- ⇒ RegionalFileHandle ioMode pr → B.ByteString → cr ()-hPutStrLn = wrap2 E.hPutStrLn----- The End ---------------------------------------------------------------------
− Data/ByteString/Lazy/Char8/SaferFileHandles.hs
@@ -1,75 +0,0 @@-{-# LANGUAGE UnicodeSyntax, NoImplicitPrelude #-}------------------------------------------------------------------------------------ |--- Module : Data.ByteString.SaferFileHandles--- Copyright : (c) 2010 Bas van Dijk--- License : BSD3 (see the file LICENSE)--- Maintainer : Bas van Dijk <v.dijk.bas@gmail.com>------ This module lifts the bytestring IO operations into the region monad.-------------------------------------------------------------------------------------module Data.ByteString.Lazy.Char8.SaferFileHandles- ( hGetContents- , hGet- , hGetNonBlocking-- , hPut- ) where------------------------------------------------------------------------------------ Imports------------------------------------------------------------------------------------ from base:-import Data.Int ( Int )---- from transformers:-import Control.Monad.IO.Class ( MonadIO )---- from bytestring:-import qualified Data.ByteString.Lazy.Char8 as B---- from regions:-import Control.Monad.Trans.Region ( ParentOf )---- from explicit-iomodes:-import qualified Data.ByteString.Lazy.Char8.ExplicitIOModes as E- ( hGetContents, hGet, hGetNonBlocking- , hPut- )---- from ourselves:-import System.IO.SaferFileHandles ( RegionalFileHandle , ReadModes, WriteModes )-import System.IO.SaferFileHandles.Internal ( wrap, wrap2 )------------------------------------------------------------------------------------- ByteString I/O with regional file handles------------------------------------------------------------------------------------ | Wraps @Data.ByteString.@'B.hGetContents'.-hGetContents ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → cr B.ByteString-hGetContents = wrap E.hGetContents---- | Wraps @Data.ByteString.@'B.hGet'.-hGet ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → Int → cr B.ByteString-hGet = wrap2 E.hGet---- | Wraps @Data.ByteString.@'B.hGetNonBlocking'.-hGetNonBlocking ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → Int → cr B.ByteString-hGetNonBlocking = wrap2 E.hGetNonBlocking----- | Wraps @Data.ByteString.@'B.hPut'.-hPut ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)- ⇒ RegionalFileHandle ioMode pr → B.ByteString → cr ()-hPut = wrap2 E.hPut----- The End ---------------------------------------------------------------------
− Data/ByteString/Lazy/SaferFileHandles.hs
@@ -1,86 +0,0 @@-{-# LANGUAGE UnicodeSyntax, NoImplicitPrelude #-}------------------------------------------------------------------------------------ |--- Module : Data.ByteString.SaferFileHandles--- Copyright : (c) 2010 Bas van Dijk--- License : BSD3 (see the file LICENSE)--- Maintainer : Bas van Dijk <v.dijk.bas@gmail.com>------ This module lifts the bytestring IO operations into the region monad.-------------------------------------------------------------------------------------module Data.ByteString.Lazy.SaferFileHandles- ( hGetContents- , hGet- , hGetNonBlocking-- , hPut- , hPutStr- ) where------------------------------------------------------------------------------------ Imports------------------------------------------------------------------------------------ from base:-import Data.Int ( Int )---- from transformers:-import Control.Monad.IO.Class ( MonadIO )---- from bytestring:-import qualified Data.ByteString.Lazy as B---- from regions:-import Control.Monad.Trans.Region ( ParentOf )---- from explicit-iomodes:-import qualified Data.ByteString.Lazy.ExplicitIOModes as E ( hGetContents- , hGet- , hGetNonBlocking-- , hPut- , hPutStr- )---- from ourselves:-import System.IO.SaferFileHandles ( RegionalFileHandle- , ReadModes, WriteModes- )-import System.IO.SaferFileHandles.Internal ( wrap, wrap2 )------------------------------------------------------------------------------------- ByteString I/O with regional file handles------------------------------------------------------------------------------------ | Wraps @Data.ByteString.@'B.hGetContents'.-hGetContents ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → cr B.ByteString-hGetContents = wrap E.hGetContents---- | Wraps @Data.ByteString.@'B.hGet'.-hGet ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → Int → cr B.ByteString-hGet = wrap2 E.hGet---- | Wraps @Data.ByteString.@'B.hGetNonBlocking'.-hGetNonBlocking ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → Int → cr B.ByteString-hGetNonBlocking = wrap2 E.hGetNonBlocking----- | Wraps @Data.ByteString.@'B.hPut'.-hPut ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)- ⇒ RegionalFileHandle ioMode pr → B.ByteString → cr ()-hPut = wrap2 E.hPut---- | Wraps @Data.ByteString.@'B.hPutStr'.-hPutStr ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)- ⇒ RegionalFileHandle ioMode pr → B.ByteString → cr ()-hPutStr = wrap2 E.hPutStr----- The End ---------------------------------------------------------------------
− Data/ByteString/SaferFileHandles.hs
@@ -1,99 +0,0 @@-{-# LANGUAGE UnicodeSyntax, NoImplicitPrelude #-}------------------------------------------------------------------------------------ |--- Module : Data.ByteString.SaferFileHandles--- Copyright : (c) 2010 Bas van Dijk--- License : BSD3 (see the file LICENSE)--- Maintainer : Bas van Dijk <v.dijk.bas@gmail.com>------ This module lifts the bytestring IO operations into the region monad.-------------------------------------------------------------------------------------module Data.ByteString.SaferFileHandles- ( hGetLine- , hGetContents- , hGet- , hGetNonBlocking-- , hPut- , hPutStr- , hPutStrLn- ) where------------------------------------------------------------------------------------ Imports------------------------------------------------------------------------------------ from base:-import Data.Int ( Int )---- from transformers:-import Control.Monad.IO.Class ( MonadIO )---- from bytestring:-import qualified Data.ByteString as B---- from regions:-import Control.Monad.Trans.Region ( ParentOf )---- from explicit-iomodes:-import qualified Data.ByteString.ExplicitIOModes as E ( hGetLine- , hGetContents- , hGet- , hGetNonBlocking-- , hPut- , hPutStr- , hPutStrLn- )---- from ourselves:-import System.IO.SaferFileHandles ( RegionalFileHandle- , ReadModes, WriteModes- )-import System.IO.SaferFileHandles.Internal ( wrap, wrap2 )------------------------------------------------------------------------------------- ByteString I/O with regional file handles------------------------------------------------------------------------------------ | Wraps @Data.ByteString.@'B.hGetLine'.-hGetLine ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → cr B.ByteString-hGetLine = wrap E.hGetLine---- | Wraps @Data.ByteString.@'B.hGetContents'.-hGetContents ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → cr B.ByteString-hGetContents = wrap E.hGetContents---- | Wraps @Data.ByteString.@'B.hGet'.-hGet ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → Int → cr B.ByteString-hGet = wrap2 E.hGet---- | Wraps @Data.ByteString.@'B.hGetNonBlocking'.-hGetNonBlocking ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)- ⇒ RegionalFileHandle ioMode pr → Int → cr B.ByteString-hGetNonBlocking = wrap2 E.hGetNonBlocking---- | Wraps @Data.ByteString.@'B.hPut'.-hPut ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)- ⇒ RegionalFileHandle ioMode pr → B.ByteString → cr ()-hPut = wrap2 E.hPut---- | Wraps @Data.ByteString.@'B.hPutStr'.-hPutStr ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)- ⇒ RegionalFileHandle ioMode pr → B.ByteString → cr ()-hPutStr = wrap2 E.hPutStr---- | Wraps @Data.ByteString.@'B.hPutStrLn'.-hPutStrLn ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)- ⇒ RegionalFileHandle ioMode pr → B.ByteString → cr ()-hPutStrLn = wrap2 E.hPutStrLn----- The End ---------------------------------------------------------------------
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2009 Bas van Dijk+Copyright (c) 2010 Bas van Dijk All rights reserved.
System/IO/SaferFileHandles.hs view
@@ -11,23 +11,15 @@ -- License : BSD3 (see the file LICENSE) -- Maintainer : Bas van Dijk <v.dijk.bas@gmail.com> ----- This module provides the type 'File' which represents an actual file. A file--- is a scarce resource, that is, in certain IOModes it can only be used by one--- user at a time. Because of the scarcity, a file needs to be /opened/ to grant--- temporary sole access to the file. When the file is no longer needed it--- should be /closed/ a.s.a.p to grant others access to the file.--- -- The contributions of this module are as follows: ----- * First of all this module provides an instance for 'Resource' for 'File'--- which allows it to be used with the @regions@ package. The @regions@ package--- provides the region monad transformer 'RegionT'. Scarce resources, like files--- for example, can be opened in a region. When the region terminates, all--- opened resources will be closed automatically. The main advantage of regions--- is that the handles to the opened resources can not be returned from the--- region. This ensures no I/O with closed resources is possible. The primary--- technique used in @regions@ is called \"Lightweight monadic regions\" which--- was invented by Oleg Kiselyov and Chung-chieh Shan. See:+-- * This module extends the @regions@ library with the ability to open files in+-- a 'RegionT'. When the region terminates, all opened resources will be closed+-- automatically. The main advantage of regions is that the handles to the+-- opened resources can not be returned from the region. This ensures no I/O+-- with closed resources is possible. The primary technique used in @regions@ is+-- called \"Lightweight monadic regions\" which was invented by Oleg Kiselyov+-- and Chung-chieh Shan. See: -- <http://okmij.org/ftp/Haskell/regions.html#light-weight> -- -- * Secondly this module provides all the file operations of @System.IO@ lifted@@ -57,14 +49,8 @@ ------------------------------------------------------------------------------- module System.IO.SaferFileHandles- ( -- * Files with explicit IO modes as scarce resources- File(..)- , Binary- , FilePath- , Template-#if MIN_VERSION_base(4,2,0)- , DefaultPermissions-#endif+ ( -- * Regional file handles+ RegionalFileHandle -- ** IO Modes -- | Types that represent the IOMode an opened file can be in.@@ -78,25 +64,30 @@ , ReadModes , WriteModes + -- ** Standard handles+ {-| These standard handles have concrete IOModes by default which work+ for the majority of cases. In the rare occasion that you know these+ handles have different IOModes you can 'cast' them.+ -}+ , stdin, stdout, stderr+ , cast+ -- ** Opening files in a region+ , FilePath+ , openFile, withFile + -- ** Regions {-| Note that this module re-exports the @Control.Monad.Trans.Region@ module from the @regions@ package which allows you to: - * 'open' a 'File' in a 'RegionT'.- * Run a region using 'runRegionT'. * Concurrently run a region inside another region using 'forkTopRegion'. - * Duplicate a regional file handle to a parent region using 'dup'.+ * Duplicate a 'RegionalFileHandle' to a parent region using 'dup'. -} , module Control.Monad.Trans.Region - , RegionalFileHandle-- , openFile, withFile- -- * Operations on regional file handles -- ** Determining and changing the size of a file , hFileSize@@ -176,7 +167,7 @@ , readLn -- * Binary input and output- , withBinaryFile, openBinaryFile+ , openBinaryFile, withBinaryFile , hSetBinaryMode , hPutBuf@@ -188,12 +179,14 @@ #endif -- * Temporary files+ , Template+ , openTempFile , openBinaryTempFile #if MIN_VERSION_base(4,2,0)- , openTempFileWithDefaultPermissions- , openBinaryTempFileWithDefaultPermissions+ , openTempFileWithDefaultPermissions+ , openBinaryTempFileWithDefaultPermissions #endif #if MIN_VERSION_base(4,2,0) && !defined(__NHC__) && !defined(__HUGS__)@@ -226,38 +219,41 @@ ------------------------------------------------------------------------------- -- from base:-import Prelude ( Integer )-import Control.Monad ( return, (>>=), fail )-import Data.Function ( ($) )-import Data.Bool ( Bool(False, True) )-import Data.Char ( Char, String )-import Data.Int ( Int )-import Data.Maybe ( Maybe, fromJust )-import Text.Show ( Show )-import Text.Read ( Read )-import Foreign.Ptr ( Ptr )+import Prelude ( Integer )+import Control.Monad ( return, (>>=), fail, liftM )+import Data.Bool ( Bool(..) )+import Data.Function ( ($), flip )+import Data.Functor ( fmap )+import Data.Char ( Char, String )+import Data.Int ( Int )+import Data.Maybe ( Maybe(Nothing, Just) )+import Text.Show ( Show )+import Text.Read ( Read )+import Foreign.Ptr ( Ptr ) import qualified System.IO as SIO +#ifdef __HADDOCK__+import System.IO.Error+#endif+ -- from base-unicode-symbols: import Data.Function.Unicode ( (∘) ) -- from MonadCatchIO-transformers:-import Control.Monad.CatchIO ( MonadCatchIO )+import Control.Monad.CatchIO ( MonadCatchIO, block ) -- from transformers: import Control.Monad.IO.Class ( MonadIO, liftIO ) -- from regions:-import Control.Monad.Trans.Region -- (re-exported entirely)-import Control.Monad.Trans.Region.Unsafe ( internalHandle )--#ifdef __HADDOCK__-import Control.Resource ( Resource )-#endif+import Control.Monad.Trans.Region -- ( re-exported entirely )+import Control.Monad.Trans.Region.OnExit ( onExit ) -- from explicit-iomodes-import System.IO.ExplicitIOModes ( IOMode(..)+import System.IO.ExplicitIOModes ( IO+ , hClose+ , IOMode(..) , ReadMode , WriteMode@@ -267,6 +263,8 @@ , ReadModes , WriteModes + , CheckMode+ , FilePath , BufferMode(..) , HandlePosn@@ -291,27 +289,56 @@ import qualified System.IO.ExplicitIOModes as E -- from ourselves:-import System.IO.SaferFileHandles.Internal ( RegionalFileHandle- , File(..)- , Binary- , Template-#if MIN_VERSION_base(4,2,0)- , DefaultPermissions-#endif- , mbFilePath- , wrap, wrap2, wrap3- )+import System.IO.SaferFileHandles.Internal ( RegionalFileHandle(RegionalFileHandle) )+import System.IO.SaferFileHandles.Unsafe ( wrap, wrap2, wrap3, sanitizeIOError ) ---------------------------------------------------------------------------------- * Files with explicit IO modes as scarce resources+-- ** Standard handles ------------------------------------------------------------------------------- -{-| Convenience function for opening a file which yields a regional handle to-it. This provides a safer replacement for @System.IO.@'SIO.openFile'.+-- | Wraps: @System.IO.'SIO.stdin'@.+stdin ∷ RegionalFileHandle ReadMode r+stdin = RegionalFileHandle E.stdin Nothing -Note that: @openFile filePath ioMode = 'open' $ 'File' False filePath ioMode@.+-- | Wraps: @System.IO.'SIO.stdout'@.+stdout ∷ RegionalFileHandle WriteMode r+stdout = RegionalFileHandle E.stdout Nothing +-- | Wraps: @System.IO.'SIO.stderr'@.+stderr ∷ RegionalFileHandle WriteMode r+stderr = RegionalFileHandle E.stderr Nothing++{-| Cast the IOMode of a handle if the handle supports it.++This function is primarily used in combination with the standard handles. When+you know the IOMode of a handle is different from its default IOMode you can+cast it to the right one.+-}+cast ∷ (pr `ParentOf` cr, MonadIO cr, CheckMode castedIOMode)+ ⇒ RegionalFileHandle anyIOMode pr+ → cr (Maybe (RegionalFileHandle castedIOMode pr))+cast (RegionalFileHandle h mbCloseHndl) =+ (liftM ∘ fmap) (flip RegionalFileHandle mbCloseHndl) $ liftIO $ E.cast h+++-------------------------------------------------------------------------------+-- ** Opening files in a region+-------------------------------------------------------------------------------++{-| Computation 'openFile' @filePath ioMode@ allocates and returns a new,+regional file handle to manage the file identified by @filePath@. It provides a+safer replacement for @System.IO.'SIO.openFile'@.++If the file does not exist and it is opened for output, it should be created as+a new file. If @ioMode@ is 'WriteMode' and the file already exists, then it+should be truncated to zero length. Some operating systems delete empty files,+so there is no guarantee that the file will exist following an 'openFile' with+@ioMode@ 'WriteMode' unless it is subsequently written to successfully. The+handle is positioned at the end of the file if @ioMode@ is 'AppendMode', and+otherwise at the beginning (in which case its internal position is 0). The+initial buffer mode is implementation-dependent.+ Note that the returned regional file handle is parameterized by the region in which it was created. This ensures that handles can never escape their region. And it also allows operations on handles to be executed in a child@@ -319,28 +346,52 @@ Note that if you do wish to return a handle from the region in which it was created you have to duplicate the handle by applying 'dup' to it.++Finally note that the returned regional file handle is also parameterized by the+IOMode in which you opened the file. All operations on files explicitly specify+the needed IOMode using the 'ReadModes' and 'WriteModes' type classes. This way+it is impossible to read from a write-only handle or write to a read-only handle+for example.++This operation may fail with:++ * 'isAlreadyInUseError' if the file is already open and cannot be reopened;++ * 'isDoesNotExistError' if the file does not exist; or++ * 'isPermissionError' if the user does not have permission to open the file.++Note: if you will be working with files containing binary data, you'll want to+be using 'openBinaryFile'. -} openFile ∷ MonadCatchIO pr ⇒ FilePath → IOMode ioMode → RegionT s pr- (RegionalFileHandle ioMode (RegionT s pr))-openFile filePath ioMode = open $ File False filePath ioMode+ (RegionalFileHandle ioMode (RegionT s pr))+openFile = openNormal E.openFile +openNormal ∷ MonadCatchIO pr+ ⇒ (FilePath → IOMode ioMode → IO (E.Handle ioMode))+ → FilePath+ → IOMode ioMode+ → RegionT s pr+ (RegionalFileHandle ioMode (RegionT s pr))+openNormal open filePath ioMode = block $ do+ h ← liftIO $ open filePath ioMode+ ch ← onExit $ sanitizeIOError $ hClose h+ return $ RegionalFileHandle h $ Just ch+ {-| Convenience function which opens a file, applies the given continuation function to the resulting regional file handle and runs the resulting-region. This provides a safer safer replacement for @System.IO.@'SIO.withFile'.--Note that: @withFile filePath ioMode = 'with' $ 'File' False filePath ioMode@.+region. This provides a safer safer replacement for @System.IO.'SIO.withFile'@. -} withFile ∷ MonadCatchIO pr ⇒ FilePath → IOMode ioMode- → (∀ s. RegionalFileHandle ioMode (RegionT s pr)- → RegionT s pr α- )+ → (∀ s. RegionalFileHandle ioMode (RegionT s pr) → RegionT s pr α) → pr α-withFile filePath ioMode = with $ File False filePath ioMode+withFile filePath ioMode f = runRegionT $ openFile filePath ioMode >>= f -------------------------------------------------------------------------------@@ -349,13 +400,19 @@ -- ** Determining and changing the size of a file --- | Wraps @System.IO.'SIO.hFileSize'@.+-- | For a handle @hdl@ which attached to a physical file,+-- 'hFileSize' @hdl@ returns the size of that file in 8-bit bytes.+--+-- Wraps: @System.IO.'SIO.hFileSize'@. hFileSize ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr Integer hFileSize = wrap E.hFileSize #ifdef __GLASGOW_HASKELL__--- | Wraps @System.IO.'SIO.hSetFileSize'@.+-- | 'hSetFileSize' @hdl@ @size@ truncates the physical file with handle @hdl@+-- to @size@ bytes.+--+-- Wraps: @System.IO.'SIO.hSetFileSize'@. hSetFileSize ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → Integer → cr () hSetFileSize = wrap2 E.hSetFileSize@@ -364,52 +421,118 @@ -- ** Detecting the end of input --- | Wraps @System.IO.'SIO.hIsEOF'@.+-- | For a readable handle @hdl@, 'hIsEOF' @hdl@ returns+-- 'True' if no further input can be taken from @hdl@ or for a+-- physical file, if the current I\/O position is equal to the length of+-- the file. Otherwise, it returns 'False'.+--+-- NOTE: 'hIsEOF' may block, because it is the same as calling+-- 'hLookAhead' and checking for an EOF exception.+--+-- Wraps: @System.IO.'SIO.hIsEOF'@. hIsEOF ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode) ⇒ RegionalFileHandle ioMode pr → cr Bool hIsEOF = wrap E.hIsEOF --- | Wraps @System.IO.'SIO.isEOF'@.+-- | Generalizes: @System.IO.'SIO.isEOF'@ to any 'MonadIO'. isEOF ∷ MonadIO m ⇒ m Bool isEOF = liftIO $ E.isEOF -- ** Buffering operations --- | Wraps @System.IO.'SIO.hSetBuffering'@.+-- | Computation 'hSetBuffering' @hdl mode@ sets the mode of buffering for+-- handle @hdl@ on subsequent reads and writes.+--+-- If the buffer mode is changed from 'BlockBuffering' or+-- 'LineBuffering' to 'NoBuffering', then+--+-- * if @hdl@ is writable, the buffer is flushed as for 'hFlush';+--+-- * if @hdl@ is not writable, the contents of the buffer is discarded.+--+-- This operation may fail with:+--+-- * 'isPermissionError' if the handle has already been used for reading+-- or writing and the implementation does not allow the buffering mode+-- to be changed.+--+-- Wraps: @System.IO.'SIO.hSetBuffering'@. hSetBuffering ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → BufferMode → cr () hSetBuffering = wrap2 E.hSetBuffering --- | Wraps @System.IO.'SIO.hGetBuffering'@.+-- | Computation 'hGetBuffering' @hdl@ returns the current buffering mode for+-- @hdl@.+--+-- Wraps: @System.IO.'SIO.hGetBuffering'@. hGetBuffering ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr BufferMode hGetBuffering = wrap E.hGetBuffering --- | Wraps @System.IO.'SIO.hFlush'@.-hFlush ∷ (pr `ParentOf` cr, MonadIO cr)+-- | The action 'hFlush' @hdl@ causes any items buffered for output in handle+-- @hdl@ to be sent immediately to the operating system.+--+-- This operation may fail with:+--+-- * 'isFullError' if the device is full;+--+-- * 'isPermissionError' if a system resource limit would be exceeded. It is+-- unspecified whether the characters in the buffer are discarded or retained+-- under these circumstances.+--+-- Wraps: @System.IO.'SIO.hFlush'@.+hFlush ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode) ⇒ RegionalFileHandle ioMode pr → cr () hFlush = wrap E.hFlush -- ** Repositioning handles --- | Wraps @System.IO.'SIO.hGetPosn'@.+-- | Computation 'hGetPosn' @hdl@ returns the current I\/O position of @hdl@ as+-- a value of the abstract type 'HandlePosn'.+--+-- Wraps: @System.IO.'SIO.hGetPosn'@. hGetPosn ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr HandlePosn hGetPosn = wrap E.hGetPosn --- | Wraps @System.IO.'SIO.hSetPosn'@.+-- | If a call to 'hGetPosn' @hdl@ returns a position @p@, then computation+-- 'hSetPosn' @p@ sets the position of @hdl@ to the position it held at the time+-- of the call to 'hGetPosn'.+--+-- This operation may fail with:+--+-- * 'isPermissionError' if a system resource limit would be exceeded.+--+-- Wraps: @System.IO.'SIO.hSetPosn'@. hSetPosn ∷ MonadIO m ⇒ HandlePosn → m () hSetPosn = liftIO ∘ E.hSetPosn --- | Wraps @System.IO.'SIO.hSeek'@.+-- | Computation 'hSeek' @hdl mode i@ sets the position of handle @hdl@+-- depending on @mode@. The offset @i@ is given in terms of 8-bit bytes.+--+-- If @hdl@ is block- or line-buffered, then seeking to a position which is not+-- in the current buffer will first cause any items in the output buffer to be+-- written to the device, and then cause the input buffer to be discarded. Some+-- handles may not be seekable (see 'hIsSeekable'), or only support a subset of+-- the possible positioning operations (for instance, it may only be possible to+-- seek to the end of a tape, or to a positive offset from the beginning or+-- current position).+-- It is not possible to set a negative I\/O position, or+-- for a physical file, an I\/O position beyond the current end-of-file.+--+-- This operation may fail with:+--+-- * 'isPermissionError' if a system resource limit would be exceeded.+--+-- Wraps: @System.IO.'SIO.hSeek'@. hSeek ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → SeekMode → Integer → cr () hSeek = wrap3 E.hSeek #if !defined(__NHC__)--- | Wraps @System.IO.'SIO.hTell'@.+-- | Wraps: @System.IO.'SIO.hTell'@. hTell ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr Integer hTell = wrap E.hTell@@ -418,27 +541,43 @@ -- ** Handle properties --- | Wraps @System.IO.'SIO.hIsOpen'@.+-- | Note that this operation should always return 'True' since the @regions@+-- framework ensures that handles are always open. This function is used only+-- for testing the correctness of this library.+--+-- Wraps: @System.IO.'SIO.hIsOpen'@. hIsOpen ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr Bool hIsOpen = wrap E.hIsOpen --- | Wraps @System.IO.'SIO.hIsClosed'@.+-- | Note that this operation should always return 'False' since the @regions@+-- framework ensures that handles are never closed. This function is used only+-- for testing the correctness of this library.+--+-- Wraps: @System.IO.'SIO.hIsClosed'@. hIsClosed ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr Bool hIsClosed = wrap E.hIsClosed --- | Wraps @System.IO.'SIO.hIsReadable'@.+-- | Note that this operation should always return 'True' for IOModes which have+-- an instance for 'ReadModes'. This function is used only for testing the+-- correctness of this library.+--+-- Wraps: @System.IO.'SIO.hIsReadable'@. hIsReadable ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr Bool hIsReadable = wrap E.hIsReadable --- | Wraps @System.IO.'SIO.hIsWritable'@.+-- | Note that this operation should always return 'True' for IOModes which have+-- an instance for 'WriteModes'. This function is used only for testing the+-- correctness of this library.+--+-- Wraps: @System.IO.'SIO.hIsWritable'@. hIsWritable ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr Bool hIsWritable = wrap E.hIsWritable --- | Wraps @System.IO.'SIO.hIsSeekable'@.+-- | Wraps: @System.IO.'SIO.hIsSeekable'@. hIsSeekable ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr Bool hIsSeekable = wrap E.hIsSeekable@@ -447,17 +586,23 @@ -- ** Terminal operations (not portable: GHC/Hugs only) #if !defined(__NHC__)--- | Wraps @System.IO.'SIO.hIsTerminalDevice'@.+-- | Is the handle connected to a terminal?+--+-- Wraps: @System.IO.'SIO.hIsTerminalDevice'@. hIsTerminalDevice ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr Bool hIsTerminalDevice = wrap E.hIsTerminalDevice --- | Wraps @System.IO.'SIO.hSetEcho'@.+-- | Set the echoing status of a handle connected to a terminal.+--+-- Wraps: @System.IO.'SIO.hSetEcho'@. hSetEcho ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → Bool → cr () hSetEcho = wrap2 E.hSetEcho --- | Wraps @System.IO.'SIO.hGetEcho'@.+-- | Get the echoing status of a handle connected to a terminal.+--+-- Wraps: @System.IO.'SIO.hGetEcho'@. hGetEcho ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr Bool hGetEcho = wrap E.hGetEcho@@ -467,7 +612,7 @@ -- ** Showing handle state (not portable: GHC only) #ifdef __GLASGOW_HASKELL__--- | Wraps @System.IO.'SIO.hShow'@.+-- | Wraps: @System.IO.'SIO.hShow'@. hShow ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr String hShow = wrap E.hShow@@ -480,32 +625,111 @@ -- ** Text input --- | Wraps @System.IO.'SIO.hWaitForInput'@.+-- | Computation 'hWaitForInput' @hdl t@+-- waits until input is available on handle @hdl@.+-- It returns 'True' as soon as input is available on @hdl@,+-- or 'False' if no input is available within @t@ milliseconds.+--+-- If @t@ is less than zero, then @hWaitForInput@ waits indefinitely.+--+-- This operation may fail with:+--+-- * 'isEOFError' if the end of file has been reached.+--+-- NOTE for GHC users: unless you use the @-threaded@ flag,+-- @hWaitForInput t@ where @t >= 0@ will block all other Haskell+-- threads for the duration of the call. It behaves like a+-- @safe@ foreign call in this respect.+--+-- Wraps: @System.IO.'SIO.hWaitForInput'@. hWaitForInput ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode) ⇒ RegionalFileHandle ioMode pr → Int → cr Bool hWaitForInput = wrap2 E.hWaitForInput --- | Wraps @System.IO.'SIO.hReady'@.+-- | Computation 'hReady' @hdl@ indicates whether at least one item is+-- available for input from handle @hdl@.+--+-- This operation may fail with:+--+-- * 'isEOFError' if the end of file has been reached.+--+-- Wraps: @System.IO.'SIO.hReady'@. hReady ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode) ⇒ RegionalFileHandle ioMode pr → cr Bool hReady = wrap E.hReady --- | Wraps @System.IO.'SIO.hGetChar'@.+-- | Computation 'hGetChar' @hdl@ reads a character from the file or+-- channel managed by @hdl@, blocking until a character is available.+--+-- This operation may fail with:+--+-- * 'isEOFError' if the end of file has been reached.+--+-- Wraps: @System.IO.'SIO.hGetChar'@. hGetChar ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode) ⇒ RegionalFileHandle ioMode pr → cr Char hGetChar = wrap E.hGetChar --- | Wraps @System.IO.'SIO.hGetLine'@.+-- | Computation 'hGetLine' @hdl@ reads a line from the file or+-- channel managed by @hdl@.+--+-- This operation may fail with:+--+-- * 'isEOFError' if the end of file is encountered when reading+-- the /first/ character of the line.+--+-- If 'hGetLine' encounters end-of-file at any other point while reading+-- in a line, it is treated as a line terminator and the (partial)+-- line is returned.+--+-- Wraps: @System.IO.'SIO.hGetLine'@. hGetLine ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode) ⇒ RegionalFileHandle ioMode pr → cr String hGetLine = wrap E.hGetLine --- | Wraps @System.IO.'SIO.hLookAhead'@.+-- | Computation 'hLookAhead' returns the next character from the handle+-- without removing it from the input buffer, blocking until a character+-- is available.+--+-- This operation may fail with:+--+-- * 'isEOFError' if the end of file has been reached.+--+-- Wraps: @System.IO.'SIO.hLookAhead'@. hLookAhead ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode) ⇒ RegionalFileHandle ioMode pr → cr Char hLookAhead = wrap E.hLookAhead --- | Wraps @System.IO.'SIO.hGetContents'@.+-- | Computation 'hGetContents' @hdl@ returns the list of characters+-- corresponding to the unread portion of the channel or file managed+-- by @hdl@, which is put into an intermediate state, /semi-closed/.+-- In this state, @hdl@ is effectively closed,+-- but items are read from @hdl@ on demand and accumulated in a special+-- list returned by 'hGetContents' @hdl@.+--+-- Any operation that fails because a handle is closed,+-- also fails if a handle is semi-closed.+-- A semi-closed handle becomes closed:+--+-- * if its corresponding region terminates;+--+-- * if an I\/O error occurs when reading an item from the handle;+--+-- * or once the entire contents of the handle has been read.+--+-- Once a semi-closed handle becomes closed, the contents of the+-- associated list becomes fixed. The contents of this final list is+-- only partially specified: it will contain at least all the items of+-- the stream that were evaluated prior to the handle becoming closed.+--+-- Any I\/O errors encountered while a handle is semi-closed are simply+-- discarded.+--+-- This operation may fail with:+--+-- * 'isEOFError' if the end of file has been reached.+--+-- Wraps: @System.IO.'SIO.hGetContents'@. hGetContents ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode) ⇒ RegionalFileHandle ioMode pr → cr String hGetContents = wrap E.hGetContents@@ -513,22 +737,53 @@ -- ** Text ouput --- | Wraps @System.IO.'SIO.hPutChar'@.+-- | Computation 'hPutChar' @hdl ch@ writes the character @ch@ to the+-- file or channel managed by @hdl@. Characters may be buffered if+-- buffering is enabled for @hdl@.+--+-- This operation may fail with:+--+-- * 'isFullError' if the device is full; or+--+-- * 'isPermissionError' if another system resource limit would be exceeded.+--+-- Wraps: @System.IO.'SIO.hPutChar'@. hPutChar ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode) ⇒ RegionalFileHandle ioMode pr → Char → cr () hPutChar = wrap2 E.hPutChar --- | Wraps @System.IO.'SIO.hPutStr'@.+-- | Computation 'hPutStr' @hdl s@ writes the string+-- @s@ to the file or channel managed by @hdl@.+--+-- This operation may fail with:+--+-- * 'isFullError' if the device is full; or+--+-- * 'isPermissionError' if another system resource limit would be exceeded.+--+-- Wraps: @System.IO.'SIO.hPutStr'@. hPutStr ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode) ⇒ RegionalFileHandle ioMode pr → String → cr () hPutStr = wrap2 E.hPutStr --- | Wraps @System.IO.'SIO.hPutStrLn'@.+-- | The same as 'hPutStr', but adds a newline character.+--+-- Wraps: @System.IO.'SIO.hPutStrLn'@. hPutStrLn ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode) ⇒ RegionalFileHandle ioMode pr → String → cr () hPutStrLn = wrap2 E.hPutStrLn --- | Wraps @System.IO.'SIO.hPrint'@.+-- | Computation 'hPrint' @hdl t@ writes the string representation of @t@+-- given by the 'shows' function to the file or channel managed by @hdl@+-- and appends a newline.+--+-- This operation may fail with:+--+-- * 'isFullError' if the device is full; or+--+-- * 'isPermissionError' if another system resource limit would be exceeded.+--+-- Wraps: @System.IO.'SIO.hPrint'@. hPrint ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode, Show α) ⇒ RegionalFileHandle ioMode pr → α → cr () hPrint = wrap2 E.hPrint@@ -536,43 +791,43 @@ -- ** Special cases for standard input and output --- | Generalizes @System.IO.'SIO.interact' to any 'MonadIO'@.+-- | Generalizes @System.IO.'SIO.interact'@ to any 'MonadIO'. interact ∷ MonadIO m ⇒ (String → String) → m () interact f = liftIO $ SIO.interact f --- | Generalizes @System.IO.'SIO.putChar' to any 'MonadIO'@.+-- | Generalizes @System.IO.'SIO.putChar'@ to any 'MonadIO'. putChar ∷ MonadIO m ⇒ Char → m () putChar c = liftIO $ SIO.putChar c --- | Generalizes @System.IO.'SIO.putStr' to any 'MonadIO'@.+-- | Generalizes @System.IO.'SIO.putStr'@ to any 'MonadIO'. putStr ∷ MonadIO m ⇒ String → m () putStr s = liftIO $ SIO.putStr s --- | Generalizes @System.IO.'SIO.putStrLn' to any 'MonadIO'@.+-- | Generalizes @System.IO.'SIO.putStrLn'@ to any 'MonadIO'. putStrLn ∷ MonadIO m ⇒ String → m () putStrLn s = liftIO $ SIO.putStrLn s --- | Generalizes @System.IO.'SIO.print' to any 'MonadIO'@.+-- | Generalizes @System.IO.'SIO.print'@ to any 'MonadIO'. print ∷ (MonadIO m, Show α) ⇒ α → m () print x = liftIO $ SIO.print x --- | Generalizes @System.IO.'SIO.getChar' to any 'MonadIO'@.+-- | Generalizes @System.IO.'SIO.getChar'@ to any 'MonadIO'. getChar ∷ MonadIO m ⇒ m Char getChar = liftIO SIO.getChar --- | Generalizes @System.IO.'SIO.getLine' to any 'MonadIO'@.+-- | Generalizes @System.IO.'SIO.getLine'@ to any 'MonadIO'. getLine ∷ MonadIO m ⇒ m String getLine = liftIO SIO.getLine --- | Generalizes @System.IO.'SIO.getContents' to any 'MonadIO'@.+-- | Generalizes @System.IO.'SIO.getContents'@ to any 'MonadIO'. getContents ∷ MonadIO m ⇒ m String getContents = liftIO SIO.getContents --- | Generalizes @System.IO.'SIO.readIO' to any 'MonadIO'@.+-- | Generalizes @System.IO.'SIO.readIO'@ to any 'MonadIO'. readIO ∷ (MonadIO m, Read α) ⇒ String → m α readIO s = liftIO $ SIO.readIO s --- | Generalizes @System.IO.'SIO.readLn' to any 'MonadIO'@.+-- | Generalizes @System.IO.'SIO.readLn'@ to any 'MonadIO'. readLn ∷ (MonadIO m, Read α) ⇒ m α readLn = liftIO SIO.readLn @@ -581,56 +836,116 @@ -- * Binary input and output -------------------------------------------------------------------------------- +-- | Like 'openFile', but open the file in binary mode.+--+-- On Windows, reading a file in text mode (which is the default) will translate+-- CRLF to LF, and writing will translate LF to CRLF. This is usually what you+-- want with text files. With binary files this is undesirable; also, as usual+-- under Microsoft operating systems, text mode treats control-Z as EOF. Binary+-- mode turns off all special treatment of end-of-line and end-of-file+-- characters. (See also 'hSetBinaryMode'.)+--+-- This provides a safer replacement for @System.IO.'SIO.openBinaryFile'@.+openBinaryFile ∷ MonadCatchIO pr+ ⇒ FilePath+ → IOMode ioMode+ → RegionT s pr+ (RegionalFileHandle ioMode (RegionT s pr))+openBinaryFile = openNormal E.openBinaryFile+ {-| A convenience function which opens a file in binary mode, applies the given continuation function to the resulting regional file handle and runs the resulting region. This provides a safer replacement for @System.IO.'SIO.withBinaryFile'@.--Note that: @withBinaryFile filePath ioMode = 'with' $ 'File' True filePath ioMode@. -} withBinaryFile ∷ MonadCatchIO pr ⇒ FilePath → IOMode ioMode- → (∀ s. RegionalFileHandle ioMode (RegionT s pr)- → RegionT s pr α- )+ → (∀ s. RegionalFileHandle ioMode (RegionT s pr) → RegionT s pr α) → pr α-withBinaryFile filePath ioMode = with $ File True filePath ioMode+withBinaryFile filePath ioMode f = runRegionT $ openBinaryFile filePath ioMode >>= f --- | Convenience function whichs opens a file in binary mode yielding a regional--- handle to it. This provides a safer replacement for--- @System.IO.'SIO.openBinaryFile'@.+-- | Select binary mode ('True') or text mode ('False') on a open handle.+-- (See also 'openBinaryFile'.) ----- Note that: @openBinaryFile filePath ioMode = 'open' $ 'File' True filePath ioMode@.-openBinaryFile ∷ MonadCatchIO pr- ⇒ FilePath- → IOMode ioMode- → RegionT s pr- (RegionalFileHandle ioMode (RegionT s pr))-openBinaryFile filePath ioMode = open $ File True filePath ioMode---- | Wraps @System.IO.'SIO.hSetBinaryMode'@.+-- This has the same effect as calling 'hSetEncoding' with 'latin1', together+-- with 'hSetNewlineMode' with 'noNewlineTranslation'.+--+-- Wraps: @System.IO.'SIO.hSetBinaryMode'@. hSetBinaryMode ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → Bool → cr () hSetBinaryMode = wrap2 E.hSetBinaryMode --- | Wraps @System.IO.'SIO.hPutBuf'@.+-- | 'hPutBuf' @hdl buf count@ writes @count@ 8-bit bytes from the+-- buffer @buf@ to the handle @hdl@. It returns ().+--+-- 'hPutBuf' ignores any text encoding that applies to the 'RegionalFileHandle',+-- writing the bytes directly to the underlying file or device.+--+-- 'hPutBuf' ignores the prevailing 'TextEncoding' and+-- 'NewlineMode' on the 'RegionalFileHandle', and writes bytes directly.+--+-- This operation may fail with:+--+-- * 'ResourceVanished' if the handle is a pipe or socket, and the+-- reading end is closed. (If this is a POSIX system, and the program+-- has not asked to ignore SIGPIPE, then a SIGPIPE may be delivered+-- instead, whose default action is to terminate the program).+--+-- Wraps: @System.IO.'SIO.hPutBuf'@. hPutBuf ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode) ⇒ RegionalFileHandle ioMode pr → Ptr α → Int → cr () hPutBuf = wrap3 E.hPutBuf --- | Wraps @System.IO.'SIO.hGetBuf'@.+-- | 'hGetBuf' @hdl buf count@ reads data from the handle @hdl@+-- into the buffer @buf@ until either EOF is reached or+-- @count@ 8-bit bytes have been read.+-- It returns the number of bytes actually read. This may be zero if+-- EOF was reached before any data was read (or if @count@ is zero).+--+-- 'hGetBuf' ignores whatever 'TextEncoding' the 'RegionalFileHandle' is+-- currently using, and reads bytes directly from the underlying IO device.+--+-- 'hGetBuf' never raises an EOF exception, instead it returns a value+-- smaller than @count@.+--+-- If the handle is a pipe or socket, and the writing end+-- is closed, 'hGetBuf' will behave as if EOF was reached.+--+-- 'hGetBuf' ignores the prevailing 'TextEncoding' and 'NewlineMode' on the+-- 'RegionalFileHandle', and reads bytes directly.+--+-- Wraps: @System.IO.'SIO.hGetBuf'@. hGetBuf ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode) ⇒ RegionalFileHandle ioMode pr → Ptr α → Int → cr Int hGetBuf = wrap3 E.hGetBuf #if !defined(__NHC__) && !defined(__HUGS__)--- | Wraps @System.IO.'SIO.hPutBufNonBlocking'@.+-- | Wraps: @System.IO.'SIO.hPutBufNonBlocking'@. hPutBufNonBlocking ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode) ⇒ RegionalFileHandle ioMode pr → Ptr α → Int → cr Int hPutBufNonBlocking = wrap3 E.hPutBufNonBlocking --- | Wraps @System.IO.'SIO.hGetBufNonBlocking'@.+-- | 'hGetBufNonBlocking' @hdl buf count@ reads data from the handle @hdl@+-- into the buffer @buf@ until either EOF is reached, or+-- @count@ 8-bit bytes have been read, or there is no more data available+-- to read immediately.+--+-- 'hGetBufNonBlocking' is identical to 'hGetBuf', except that it will+-- never block waiting for data to become available, instead it returns+-- only whatever data is available. To wait for data to arrive before+-- calling 'hGetBufNonBlocking', use 'hWaitForInput'.+--+-- 'hGetBufNonBlocking' ignores whatever 'TextEncoding' the 'RegionalFileHandle'+-- is currently using, and reads bytes directly from the underlying IO device.+--+-- If the handle is a pipe or socket, and the writing end+-- is closed, 'hGetBufNonBlocking' will behave as if EOF was reached.+--+-- 'hGetBufNonBlocking' ignores the prevailing 'TextEncoding' and 'NewlineMode'+-- on the 'RegionalFileHandle', and reads bytes directly.+--+-- Wraps: @System.IO.'SIO.hGetBufNonBlocking'@. hGetBufNonBlocking ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode) ⇒ RegionalFileHandle ioMode pr → Ptr α → Int → cr Int hGetBufNonBlocking = wrap3 E.hGetBufNonBlocking@@ -641,37 +956,51 @@ -- * Temporary files -------------------------------------------------------------------------------- -genOpenTempFile ∷ MonadCatchIO pr- ⇒ Binary- → FilePath- → Template- → RegionT s pr ( FilePath- , RegionalFileHandle ReadWriteMode (RegionT s pr)- )-genOpenTempFile binary filePath template = do- rh ← open $ TempFile binary filePath template-#if MIN_VERSION_base(4,2,0)- False-#endif- return (generatedFilePath rh, rh)+-- | The template of a temporary file path.+--+-- If the template is \"foo.ext\" then the created file will be \"fooXXX.ext\"+-- where XXX is some random number.+type Template = String -generatedFilePath ∷ RegionalFileHandle ioMode r → FilePath-generatedFilePath = fromJust ∘ mbFilePath ∘ internalHandle+openTemp ∷ MonadCatchIO pr+ ⇒ (FilePath → Template → IO (FilePath, E.Handle ReadWriteMode))+ → FilePath+ → Template+ → RegionT s pr ( FilePath+ , RegionalFileHandle ReadWriteMode (RegionT s pr)+ )+openTemp open filePath template = block $ do+ (fp, h) ← liftIO $ open filePath template+ ch ← onExit $ sanitizeIOError $ hClose h+ return (fp, RegionalFileHandle h $ Just ch) --- | Open a temporary file yielding a regional handle to it paired with the--- generated file path. This provides a safer replacement for--- @System.IO.'SIO.openTempFile'@.+-- | The function creates a temporary file in 'ReadWriteMode'. The created file+-- isn\'t deleted automatically, so you need to delete it manually.+--+-- The file is creates with permissions such that only the current+-- user can read\/write it.+--+-- With some exceptions (see below), the file will be created securely in the+-- sense that an attacker should not be able to cause 'openTempFile' to+-- overwrite another file on the filesystem using your credentials, by putting+-- symbolic links (on Unix) in the place where the temporary file is to be+-- created. On Unix the @O_CREAT@ and @O_EXCL@ flags are used to prevent this+-- attack, but note that @O_EXCL@ is sometimes not supported on NFS filesystems,+-- so if you rely on this behaviour it is best to use local filesystems only.+--+-- This provides a safer replacement for @System.IO.'SIO.openTempFile'@. openTempFile ∷ MonadCatchIO pr- ⇒ FilePath- → Template+ ⇒ FilePath -- ^ Directory in which to create the file.+ → Template -- ^ File name template. → RegionT s pr ( FilePath , RegionalFileHandle ReadWriteMode (RegionT s pr) )-openTempFile = genOpenTempFile False+openTempFile = openTemp E.openTempFile --- | Open a temporary file in binary mode yielding a regional handle to it--- paired with the generated file path. This provides a safer replacement for--- @System.IO.'SIO.openBinaryTempFile'@.+-- | Like 'openTempFile', but opens the file in binary mode. See+-- 'openBinaryFile' for more comments.+--+-- This provides a safer replacement for @System.IO.'SIO.openBinaryTempFile'@. openBinaryTempFile ∷ MonadCatchIO pr ⇒ FilePath@@ -679,24 +1008,13 @@ → RegionT s pr ( FilePath , RegionalFileHandle ReadWriteMode (RegionT s pr) )-openBinaryTempFile = genOpenTempFile True+openBinaryTempFile = openTemp E.openBinaryTempFile #if MIN_VERSION_base(4,2,0)-genOpenTempFileWithDefaultPermissions ∷- MonadCatchIO pr- ⇒ Binary- → FilePath- → Template- → RegionT s pr ( FilePath- , RegionalFileHandle ReadWriteMode (RegionT s pr)- )-genOpenTempFileWithDefaultPermissions binary filePath template = do- rh ← open $ TempFile binary filePath template True- return (generatedFilePath rh, rh)---- | Open a temporary file with default permissions yielding a regional handle--- to it paired with the generated file path. This provides a safer replacement--- for @System.IO.'SIO.openTempFileWithDefaultPermissions'@.+-- | Like 'openTempFile', but uses the default file permissions.+--+-- This provides a safer replacement for+-- @System.IO.'SIO.openTempFileWithDefaultPermissions'@. openTempFileWithDefaultPermissions ∷ MonadCatchIO pr ⇒ FilePath@@ -704,11 +1022,11 @@ → RegionT s pr ( FilePath , RegionalFileHandle ReadWriteMode (RegionT s pr) )-openTempFileWithDefaultPermissions = genOpenTempFileWithDefaultPermissions False+openTempFileWithDefaultPermissions = openTemp E.openTempFileWithDefaultPermissions --- | Open a temporary file in binary mode with default permissions yielding a--- regional handle to it paired with the generated file path. This provides a--- safer replacement for+-- | Like 'openBinaryTempFile', but uses the default file permissions.+--+-- This provides a safer replacement for -- @System.IO.'SIO.openBinaryTempFileWithDefaultPermissions'@. openBinaryTempFileWithDefaultPermissions ∷ MonadCatchIO pr@@ -717,7 +1035,7 @@ → RegionT s pr ( FilePath , RegionalFileHandle ReadWriteMode (RegionT s pr) )-openBinaryTempFileWithDefaultPermissions = genOpenTempFileWithDefaultPermissions True+openBinaryTempFileWithDefaultPermissions = openTemp $ E.openBinaryTempFileWithDefaultPermissions #endif @@ -726,17 +1044,38 @@ -- * Unicode encoding/decoding -------------------------------------------------------------------------------- --- | Wraps @System.IO.'SIO.hSetEncoding'@.+-- | The action 'hSetEncoding' @hdl@ @encoding@ changes the text encoding for+-- the handle @hdl@ to @encoding@. The default encoding when a+-- 'RegionalFileHandle' is created is 'localeEncoding', namely the default+-- encoding for the current locale.+--+-- To create a 'RegionalFileHandle' with no encoding at all, use+-- 'openBinaryFile'. To stop further encoding or decoding on an existing+-- 'RegionalFileHandle', use 'hSetBinaryMode'.+--+-- 'hSetEncoding' may need to flush buffered data in order to change+-- the encoding.+--+-- Wraps: @System.IO.'SIO.hSetEncoding'@. hSetEncoding ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → TextEncoding → cr () hSetEncoding = wrap2 E.hSetEncoding --- | Wraps @System.IO.'SIO.hGetEncoding'@.+-- | Return the current 'TextEncoding' for the specified 'RegionalFileHandle',+-- or 'Nothing' if the 'Handle' is in binary mode.+--+-- Note that the 'TextEncoding' remembers nothing about the state of the+-- encoder/decoder in use on this 'RegionalFileHandle'. For example, if the+-- encoding in use is UTF-16, then using 'hGetEncoding' and 'hSetEncoding' to+-- save and restore the encoding may result in an extra byte-order-mark being+-- written to the file.+--+-- Wraps: @System.IO.'SIO.hGetEncoding'@. hGetEncoding ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → cr (Maybe TextEncoding) hGetEncoding = wrap E.hGetEncoding --- | Generalizes @System.IO.'SIO.mkTextEncoding' to any 'MonadIO'@.+-- | Generalizes @System.IO.'SIO.mkTextEncoding'@ to any 'MonadIO'. mkTextEncoding ∷ MonadIO m ⇒ String → m TextEncoding mkTextEncoding = liftIO ∘ E.mkTextEncoding @@ -745,7 +1084,10 @@ -- * Newline conversion -------------------------------------------------------------------------------- --- | Wraps @System.IO.'SIO.hSetNewlineMode'@.+-- | Set the 'NewlineMode' on the specified 'RegionalFileHandle'. All buffered+-- data is flushed first.+--+-- Wraps: @System.IO.'SIO.hSetNewlineMode'@. hSetNewlineMode ∷ (pr `ParentOf` cr, MonadIO cr) ⇒ RegionalFileHandle ioMode pr → NewlineMode → cr () hSetNewlineMode = wrap2 E.hSetNewlineMode
System/IO/SaferFileHandles/Internal.hs view
@@ -1,157 +1,57 @@ {-# LANGUAGE UnicodeSyntax , NoImplicitPrelude+ , KindSignatures , CPP- , GADTs- , TypeFamilies #-} --------------------------------------------------------------------------------+-------------------------------------------------------------------------------- -- |--- Module : System.IO.Internal+-- Module : System.IO.SaferFileHandles.Internal -- Copyright : (c) 2010 Bas van Dijk -- License : BSD3 (see the file LICENSE) -- Maintainer : Bas van Dijk <v.dijk.bas@gmail.com> ----------------------------------------------------------------------------------+-------------------------------------------------------------------------------- -module System.IO.SaferFileHandles.Internal where+module System.IO.SaferFileHandles.Internal+ ( RegionalFileHandle(RegionalFileHandle) ) where --- from base:-import Data.Function ( ($) )-import Data.Functor ( (<$>) )-import Data.Tuple ( uncurry )-import Data.Bool ( Bool(False, True) )-import Data.Char ( String )-import Data.Maybe ( Maybe(Nothing, Just) )-import System.IO.Error ( modifyIOError )-import GHC.IO.Exception ( ioe_handle ) --- from base-unicode-symbols:-import Data.Function.Unicode ( (∘) )+--------------------------------------------------------------------------------+-- Imports+-------------------------------------------------------------------------------- --- from transformers:-import Control.Monad.IO.Class ( MonadIO, liftIO )+-- from base:+import Control.Monad ( return, (>>=), fail )+import Data.Function ( ($) )+import Data.Maybe ( Maybe(Nothing, Just) ) -- from regions:-import Control.Resource ( Resource, Handle, open, close )-import Control.Monad.Trans.Region ( RegionalHandle )-import Control.Monad.Trans.Region.Unsafe ( internalHandle )--#if __HADDOCK__-import qualified Control.Monad.Trans.Region as Region ( open )-#endif+import Control.Monad.Trans.Region ( Dup(dup) )+import Control.Monad.Trans.Region.OnExit ( CloseHandle ) -- from explicit-iomodes-import System.IO.ExplicitIOModes ( IOMode(..), ReadWriteMode, IO, FilePath )+import System.IO.ExplicitIOModes ( Handle ) -import qualified System.IO.ExplicitIOModes as E- ( Handle- , openFile- , openBinaryFile- , openTempFile- , openBinaryTempFile-#if MIN_VERSION_base(4,2,0)- , openTempFileWithDefaultPermissions- , openBinaryTempFileWithDefaultPermissions+#ifdef __HADDOCK__+import System.IO.ExplicitIOModes ( IOMode ) #endif- , hClose- ) ----------------------------------------------------------------------------------- * Files with explicit IO modes as scarce resources----------------------------------------------------------------------------------{-| A file scarce resource parameterized by the 'IOMode' in which you want to-open the file.--Note that this module provides an instance for 'Resource' for 'File'-@ioMode@. This allows you to 'Region.open' files in a region which are automatically-closed when the region terminates but it disallows you to return handles to-these closed files from the region so preventing I/O with closed files.--}-data File ioMode where- File ∷ Binary → FilePath → IOMode ioMode → File ioMode- TempFile ∷ Binary- → FilePath- → Template-#if MIN_VERSION_base(4,2,0)- → DefaultPermissions-#endif- → File ReadWriteMode---- | Should the file be opened in binary mode?-type Binary = Bool---- | The template of a temporary file path.-type Template = String--#if MIN_VERSION_base(4,2,0)--- | Should default permissions be used when opening a temporary file?-type DefaultPermissions = Bool-#endif--instance Resource (File ioMode) where- data Handle (File ioMode) =- FileHandle { mbFilePath ∷ Maybe FilePath- -- ^ Get the optional file path. This is needed because- -- opening a temporary file also yields the generated file- -- path.- , handle ∷ E.Handle ioMode- }-- open (File isBinary filePath ioMode) =- FileHandle Nothing <$>- (if isBinary then E.openBinaryFile else E.openFile)- filePath ioMode--#if MIN_VERSION_base(4,2,0)- open (TempFile isBinary filePath template defaultPerms) =- uncurry (FileHandle ∘ Just) <$>- (case (isBinary, defaultPerms) of- (False, False) → E.openTempFile- (True, False) → E.openBinaryTempFile- (False, True) → E.openTempFileWithDefaultPermissions- (True, True) → E.openBinaryTempFileWithDefaultPermissions- ) filePath template-#else- open (TempFile isBinary filePath template) =- uncurry (FileHandle ∘ Just) <$>- (if isBinary then E.openBinaryTempFile else E.openTempFile)- filePath template-#endif- close = sanitizeIOError ∘ E.hClose ∘ handle---- | A handy type synonym for a regional handle to an opened file parameterized--- by the 'IOMode' in which you opened the file and the region in which it was--- created.-type RegionalFileHandle ioMode r = RegionalHandle (File ioMode) r-- ----------------------------------------------------------------------------------- Utility wrapping functions+-- * Files with explicit IO modes as scarce resources -------------------------------------------------------------------------------- -regularHandle ∷ RegionalFileHandle ioMode r → E.Handle ioMode-regularHandle = handle ∘ internalHandle--wrap ∷ MonadIO m- ⇒ (E.Handle ioMode → IO α)- → (RegionalFileHandle ioMode r → m α)-wrap f = \h → liftIO $ sanitizeIOError $ f (regularHandle h)--wrap2 ∷ MonadIO m- ⇒ (E.Handle ioMode → β → IO α)- → (RegionalFileHandle ioMode r → β → m α)-wrap2 f = \h y → liftIO $ sanitizeIOError $ f (regularHandle h) y--wrap3 ∷ MonadIO m- ⇒ (E.Handle ioMode → γ → β → IO α)- → (RegionalFileHandle ioMode r → γ → β → m α)-wrap3 f = \h z y → liftIO $ sanitizeIOError $ f (regularHandle h) z y+-- | A regional handle to an opened file parameterized by the 'IOMode' in which+-- you opened the file and the region in which it was created.+data RegionalFileHandle ioMode (r ∷ * → *) =+ RegionalFileHandle (Handle ioMode) (Maybe (CloseHandle r)) -sanitizeIOError ∷ IO α → IO α-sanitizeIOError = modifyIOError $ \e → e { ioe_handle = Nothing }+instance Dup (RegionalFileHandle ioMode) where+ dup (RegionalFileHandle h Nothing) = return $ RegionalFileHandle h Nothing+ dup (RegionalFileHandle h (Just ch)) = do ch' ← dup ch+ return $ RegionalFileHandle h+ $ Just ch' -- The End ---------------------------------------------------------------------
+ System/IO/SaferFileHandles/Unsafe.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE UnicodeSyntax, NoImplicitPrelude #-}++--------------------------------------------------------------------------------+-- |+-- Module : System.IO.SaferFileHandles.Unsafe+-- Copyright : (c) 2010 Bas van Dijk+-- License : BSD3 (see the file LICENSE)+-- Maintainer : Bas van Dijk <v.dijk.bas@gmail.com>+--+-- /Unsafe/ functions for retrieving the native @Handle@ from a regional file+-- handle and for lifting operations on @Handles@ to @RegionalFileHandles@.+--+-- These operations are unsafe because they allow you to close regional file+-- handles before exiting their region. So they enable you to perform @IO@ with+-- already closed handles.+--+--------------------------------------------------------------------------------++module System.IO.SaferFileHandles.Unsafe ( unsafeHandle+ , wrap, wrap2, wrap3+ , sanitizeIOError+ ) where++--------------------------------------------------------------------------------+-- Imports+--------------------------------------------------------------------------------++-- from base:+import Data.Function ( ($) )+import Data.Maybe ( Maybe(Nothing) )+import GHC.IO.Exception ( ioe_handle )+import System.IO.Error ( modifyIOError )++-- from transformers:+import Control.Monad.IO.Class ( MonadIO, liftIO )++-- from explicit-iomodes+import System.IO.ExplicitIOModes ( Handle, IO )++-- from ourselves:+import System.IO.SaferFileHandles.Internal ( RegionalFileHandle(RegionalFileHandle) )+++--------------------------------------------------------------------------------+-- Getting the actual @Handle@+--------------------------------------------------------------------------------++unsafeHandle ∷ RegionalFileHandle ioMode r → Handle ioMode+unsafeHandle (RegionalFileHandle h _) = h++wrap ∷ MonadIO m+ ⇒ (Handle ioMode → IO α)+ → (RegionalFileHandle ioMode r → m α)+wrap f = \h → liftIO $ sanitizeIOError $ f (unsafeHandle h)++wrap2 ∷ MonadIO m+ ⇒ (Handle ioMode → β → IO α)+ → (RegionalFileHandle ioMode r → β → m α)+wrap2 f = \h y → liftIO $ sanitizeIOError $ f (unsafeHandle h) y++wrap3 ∷ MonadIO m+ ⇒ (Handle ioMode → γ → β → IO α)+ → (RegionalFileHandle ioMode r → γ → β → m α)+wrap3 f = \h z y → liftIO $ sanitizeIOError $ f (unsafeHandle h) z y++sanitizeIOError ∷ IO α → IO α+sanitizeIOError = modifyIOError $ \e → e { ioe_handle = Nothing }+++-- The End ---------------------------------------------------------------------
safer-file-handles.cabal view
@@ -1,5 +1,5 @@ name: safer-file-handles-version: 0.5+version: 0.6 cabal-version: >=1.6 build-type: Custom license: BSD3@@ -37,7 +37,10 @@ See the @safer-file-handles-examples@ package for examples how to use this package: .- darcs get <http://code.haskell.org/~basvandijk/code/safer-file-handles-examples>+ @darcs get <http://code.haskell.org/~basvandijk/code/safer-file-handles-examples>@+ .+ See the @safer-file-handles-bytestring@ package for @ByteString@+ operations on regional file handles. source-repository head Type: darcs@@ -47,16 +50,10 @@ GHC-Options: -Wall build-depends: base >= 4 && < 4.3 , base-unicode-symbols >= 0.1.1 && < 0.3- , bytestring >= 0.9 && < 0.10- , regions >= 0.5 && < 0.6+ , regions >= 0.6 && < 0.7 , transformers >= 0.2 && < 0.3 , MonadCatchIO-transformers >= 0.2 && < 0.3- , explicit-iomodes >= 0.3 && < 0.4+ , explicit-iomodes >= 0.3 && < 0.5 exposed-modules: System.IO.SaferFileHandles-- Data.ByteString.SaferFileHandles- Data.ByteString.Char8.SaferFileHandles- Data.ByteString.Lazy.SaferFileHandles- Data.ByteString.Lazy.Char8.SaferFileHandles-+ System.IO.SaferFileHandles.Unsafe other-modules: System.IO.SaferFileHandles.Internal