packages feed

safer-file-handles (empty) → 0.1

raw patch · 4 files changed

+1044/−0 lines, 4 filesdep +MonadCatchIO-transformersdep +basedep +base-unicode-symbolssetup-changed

Dependencies added: MonadCatchIO-transformers, base, base-unicode-symbols, explicit-iomodes, regions, transformers

Files

+ LICENSE view
@@ -0,0 +1,31 @@+Copyright (c) 2009 Bas van Dijk++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * The name of Bas van Dijk and the names of contributors may NOT+      be used to endorse or promote products derived from this+      software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,3 @@+import Distribution.Simple++main = defaultMain
+ System/IO/SaferFileHandles.hs view
@@ -0,0 +1,942 @@+{-# LANGUAGE UnicodeSyntax #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE MultiParamTypeClasses #-}++-------------------------------------------------------------------------------+-- |+-- Module      :  System.IO.SaferFileHandles+-- Copyright   :  (c) 2009 Bas van Dijk+-- License     :  BSD3 (see the file LICENSE)+-- Maintainer  :  Bas van Dijk <v.dijk.bas@gmail.com>+--+-- This module provides the abstract 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 contribution of this module are as follows:+--+-- * First of all this module provides an instance for 'Resource' for a '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 automatically closed. The main advantage of regions+-- is that the handles to the opened resources can not be returned from the+-- region which 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+-- to the region monad.+--+-- * The final contribution of this module is that file handles are+-- parameterised with the IOMode in which the file was opened. This can be+-- either 'R', 'W', 'A' or 'RW'. 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.+--+-- 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>+--+-- /WARNING:/ Currenly the handling of the standard files ('stdin', 'stdout' and+-- 'stderr') is not to my liking. See the documentation for details.+--+-- /NOTE:/ This module also provides functions from @System.IO@ which don't+-- directly work with file handles like 'putStrLn' or 'getLine' for+-- example. These functions implicitly use the standard handles. I actually+-- provide more general versions of these that work in any 'MonadIO'. It could+-- be argued that these functions don't belong in this module because they don't+-- have anything to do with regions and explicit IOModes. However I provide them+-- as a convenience. But be warned that in the future these lifted functions may+-- move to their own package!+--+-------------------------------------------------------------------------------++module System.IO.SaferFileHandles+    ( -- * Files with explicit IO modes as scarce resources+      File++    , FilePath++      -- ** IO Modes+      -- | Types that represent the IOMode an opened file can be in.+    , R, W, A, RW++    , ReadModes, WriteModes++      -- ** Opening files in a region++      {-| Note that this module re-exports the @Control.Monad.Trans.Region@+      module from the @regions@ package which allows you to:++      * Run regions using 'runRegionT'.++      * Concurrently run regions inside another region using 'forkTopRegion'.++       * Duplicate regional file handles to a parent region using 'dup'.+      -}+    , module Control.Monad.Trans.Region++    , RegionalFileHandle++    , openFile, withFile++    , IOMode(..)++      -- ** Standard handles+      -- $stdHndls++    , stdin, stdout, stderr++      -- $TODO_cast++      -- *  Operations on regional file handles+      -- ** Determining and changing the size of a file+    , hFileSize++#ifdef __GLASGOW_HASKELL__+    , hSetFileSize+#endif++      -- ** Detecting the end of input+    , hIsEOF+    , isEOF++      -- ** Buffering operations+    , BufferMode(..)+    , hSetBuffering+    , hGetBuffering+    , hFlush++    -- ** Repositioning handles+    , hGetPosn+    , hSetPosn+    , HandlePosn++    , hSeek+    , SeekMode(..)+#if !defined(__NHC__)+    , hTell+#endif++    -- ** Handle properties+    , hIsOpen, hIsClosed+    , hIsReadable, hIsWritable+    , hIsSeekable++    -- ** Terminal operations (not portable: GHC/Hugs only)+#if !defined(__NHC__)+    , hIsTerminalDevice++    , hSetEcho+    , hGetEcho+#endif+    -- ** Showing handle state (not portable: GHC only)+#ifdef __GLASGOW_HASKELL__+    , hShow+#endif+    -- * Text input and output+    -- ** Text input+    -- | Note that the following text input operations are polymorphic in the+    -- IOMode of the given handle. However the IOModes are restricted to+    -- 'ReadModes' only which can be either 'R' or 'RW'.+    , hWaitForInput+    , hReady+    , hGetChar+    , hGetLine+    , hLookAhead+    , hGetContents++    -- ** Text ouput+    -- | Note that the following text output operations are polymorphic in the+    -- IOMode of the given handle. However the IOModes are restricted to+    -- 'WriteModes' only which can be either 'W', 'A' or 'RW'.+    , hPutChar+    , hPutStr+    , hPutStrLn+    , hPrint++    -- ** Special cases for standard input and output+    , interact+    , putChar+    , putStr+    , putStrLn+    , print+    , getChar+    , getLine+    , getContents+    , readIO+    , readLn++    -- * Binary input and output+    , withBinaryFile, openBinaryFile++    , hSetBinaryMode+    , hPutBuf+    , hGetBuf++#if !defined(__NHC__) && !defined(__HUGS__)+    , hPutBufNonBlocking+    , hGetBufNonBlocking+#endif++    -- * Temporary files+#if MIN_VERSION_base(4,2,0)+    , DefaultPermissions+#endif+    , Template++    , openTempFile+    , openBinaryTempFile++#if MIN_VERSION_base(4,2,0)+   , openTempFileWithDefaultPermissions+   , openBinaryTempFileWithDefaultPermissions+#endif++#if MIN_VERSION_base(4,2,0) && !defined(__NHC__) && !defined(__HUGS__)+    -- * Unicode encoding/decoding+    , hSetEncoding+    , hGetEncoding++    -- ** Unicode encodings+    , TextEncoding+    , latin1+    , utf8, utf8_bom+    , utf16, utf16le, utf16be+    , utf32, utf32le, utf32be+    , localeEncoding+    , mkTextEncoding++    -- * Newline conversion+    , hSetNewlineMode+    , Newline(..)+    , nativeNewline+    , NewlineMode(..)+    , noNewlineTranslation, universalNewlineMode, nativeNewlineMode+#endif+    )+    where+++-------------------------------------------------------------------------------+-- Imports+-------------------------------------------------------------------------------++-- from base:+import Prelude       ( Integer )+import Control.Monad ( fmap+                     , return, (>>=), fail+                     , liftM+                     )+import Data.Function ( ($) )+import Data.Tuple    ( uncurry )+import Data.Bool     ( Bool(False, True) )+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 System.IO ( IO+                 , FilePath+                 , BufferMode(..)+                 , HandlePosn+                 , SeekMode(..)+                 , TextEncoding+                 , latin1+                 , utf8, utf8_bom+                 , utf16, utf16le, utf16be+                 , utf32, utf32le, utf32be+                 , localeEncoding+                 , Newline(..)+                 , nativeNewline+                 , NewlineMode(..)+                 , noNewlineTranslation, universalNewlineMode, nativeNewlineMode+                 )+import qualified System.IO as SIO++-- from base-unicode-symbols:+import Data.Function.Unicode ( (∘) )++-- from MonadCatchIO-transformers:+import Control.Monad.CatchIO ( MonadCatchIO )++-- from transformers:+import Control.Monad.Trans ( MonadIO, liftIO )++-- from regions:+import Control.Monad.Trans.Region -- (re-exported entirely)+import Control.Monad.Trans.Region.Internal ( Resource+                                           , openResource+                                           , closeResource+                                           , internalHandle+                                           , ParentOf+                                           , Dup+                                           )+import qualified Control.Monad.Trans.Region.Internal as R ( Handle )++-- from explicit-iomodes+import System.IO.ExplicitIOModes ( IOMode(..)+                                 , regularIOMode+                                 , R, W, A, RW+                                 , ReadModes+                                 , WriteModes+                                 -- TODO:, CheckMode+                                 )+++-------------------------------------------------------------------------------+-- * Files with explicit IO modes as scarce resources+-------------------------------------------------------------------------------++{-| A file scarce resource.++You can open a file with one of the following functions:++* 'openFile'++* 'openBinaryFile'++* 'openTempFile'++* 'openBinaryTempFile'+#if MIN_VERSION_base(4,2,0)+* 'openTempFileWithDefaultPermissions'++* 'openBinaryTempFileWithDefaultPermissions'+#endif++* 'stdin', 'stdout', 'stderr'+-}+data File = File Binary+                 FilePath+                 SIO.IOMode+          | TempFile Binary+                     FilePath+                     Template+#if MIN_VERSION_base(4,2,0)+                     DefaultPermissions+#endif+          -- TODO: I need to review the handling of standard files:+          | Std Standard++-- | 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++-- | The standard files.+data Standard = In | Out | Err++-- | Internally used function to convert a standard file to the corresponding+-- handle.+stdHndl ∷ Standard → SIO.Handle+stdHndl In  = SIO.stdin+stdHndl Out = SIO.stdout+stdHndl Err = SIO.stderr++instance Resource File where+    data R.Handle File = FileHandle (Maybe FilePath)+                                    -- The optional file path is needed+                                    -- because opening a temporary file+                                    -- also yields the generated file+                                    -- path.+                                    SIO.Handle++    openResource (File isBinary filePath ioMode) =+        fmap (FileHandle Nothing) $+             (if isBinary then SIO.openBinaryFile else SIO.openFile)+             filePath ioMode++#if MIN_VERSION_base(4,2,0)+    openResource (TempFile isBinary filePath template defaultPerms) = do+        fmap (uncurry (FileHandle ∘ Just)) $+             (case (isBinary, defaultPerms) of+               (False, False) → SIO.openTempFile+               (True,  False) → SIO.openBinaryTempFile+               (False, True)  → SIO.openTempFileWithDefaultPermissions+               (True,  True)  → SIO.openBinaryTempFileWithDefaultPermissions+             ) filePath template+#else+    openResource (TempFile isBinary filePath template) = do+        fmap (uncurry (FileHandle ∘ Just)) $+             (if isBinary then SIO.openBinaryTempFile else SIO.openTempFile)+             filePath template+#endif+    -- TODO: I need to review the handling of standard files:+    openResource (Std std) = return $ FileHandle Nothing $ stdHndl std++    closeResource (FileHandle _ h) = SIO.hClose h+++-- ** Opening files in a region++-- | A handle to a file parameterised by the IOMode with which the file was+-- opened and the region @r@ in which it was created.+newtype RegionalFileHandle ioMode (r ∷ * → *) = RegionalFileHandle+    { unRegionalFileHandle ∷ RegionalHandle File r }++instance Dup (RegionalFileHandle ioMode) File where+    dup = liftM RegionalFileHandle ∘ dup ∘ unRegionalFileHandle++{-| Open a file yielding a regional handle to it. This provides a safer+replacement for @System.IO.@'SIO.openFile'.++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+region of the region in which the handle was created.++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.+-}+openFile ∷ MonadCatchIO pr+         ⇒ FilePath+         → IOMode ioMode+         → RegionT File s pr+                   (RegionalFileHandle ioMode (RegionT File s pr))+openFile filePath ioMode = liftM RegionalFileHandle+                         $ open $ File False filePath $ regularIOMode ioMode++{-| 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'.+-}+withFile ∷ MonadCatchIO pr+         ⇒ FilePath+         → IOMode ioMode+         →  (∀ s. RegionalFileHandle ioMode (RegionT File s pr)+            → RegionT File s pr α+            )+         → pr α+withFile filePath ioMode f = with (File False filePath (regularIOMode ioMode))+                                  (f ∘ RegionalFileHandle)+++-- ** Standard handles++{- $stdHndls++/BIG WARNING:/ I'm not satisfied with my current implementation of the standard+handles ('stdin', 'stdout' and 'stderr')! Currently the standard handles are+regional computations that return the regional file handles to the respected+standard handles. There are 4 problems with this approach:++* When the region terminates in which you call one of the standard handles the+respected handle will be closed. I think this is not the expected behaviour. I+would expect the standard handles to always remain open.++* In 'System.IO' the standard handles are pure values. My standard handles are+monadic computations which makes them harder to work with.++* There is no way to explicitly close a standard handle. Indeed, the whole+purpose of lightweight monadic regions is to automatically close+handles. However, when writing a Unix daemon for example, you need to be able to+explicitly close the standard handles.++* When reading 'man stdin' I'm confused if the standard handles are /always/+open on program startup:++quote 'man stdin':++\".../Under normal circumstances/ every Unix program has three streams opened for+it when it starts up, one for input, one for output, and one for printing+diagnostic or error messages...\"++\"...The stdin, stdout, and stderr macros conform to C89 and this standard also+stipulates that these three streams /shall be open/ at program startup....\"++So now I'm confused... are these standard file handles always open on program+startup or are there /abnormal/ situations when they are closed?++Maybe I just have to believe the documentation in @System.IO@ which specifies+that they are always initially open.++If the standard handles are closed on startup using a handle returned from one+of the standard handles will result in an exception! This would be a violation+of my safety guarantees which is unacceptable.++Does anyone have a solution?+-}++-- TODO: I need to review these:++-- | Return a regional handle to standard input. This provides a safer+-- replacement for @System.IO.@'SIO.stdin'.+stdin ∷ MonadCatchIO pr+      ⇒ RegionT File s pr (RegionalFileHandle R (RegionT File s pr))+stdin = liftM RegionalFileHandle $ open $ Std In++-- | Return a regional handle to standard output. This provides a safer+-- replacement for @System.IO.@'SIO.stdout'.+stdout ∷ MonadCatchIO pr+       ⇒ RegionT File s pr (RegionalFileHandle W (RegionT File s pr))+stdout = liftM RegionalFileHandle $ open $ Std Out++-- | Return a regional handle to standard error. This provides a safer+-- replacement for @System.IO.@'SIO.stderr'.+stderr ∷ MonadCatchIO pr+       ⇒ RegionT File s pr (RegionalFileHandle W (RegionT File s pr))+stderr = liftM RegionalFileHandle $ open $ Std Err++{- $TODO_cast++/TODO:/++The 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 should be able to 'cast' them to the expected IOMode.++The @explicit-iomodes@ package defines this @cast@ function. I should also+define it here:++@+cast :: forall anyIOMode castedIOMode+     . (pr \`ParentOf\` cr, LiftIO cr, CheckMode castedIOMode)+     => RegionalFileHandle anyIOMode pr+     -> cr (Maybe (RegionalFileHandle castedIOMode pr))+@++However I'm not sure yet how to implement it...+-}++--------------------------------------------------------------------------------+-- Utility wrapping functions+--------------------------------------------------------------------------------++regularHandle ∷ RegionalFileHandle ioMode r → SIO.Handle+regularHandle (unRegionalFileHandle → (internalHandle → FileHandle _ h)) = h++wrap ∷ MonadIO m+     ⇒ (SIO.Handle → IO α)+     → (RegionalFileHandle ioMode r → m α)+wrap f = \h → liftIO $ f (regularHandle h)++wrap2 ∷ MonadIO m+      ⇒ (SIO.Handle → β → IO α)+      → (RegionalFileHandle ioMode r → β → m α)+wrap2 f = \h y → liftIO $ f (regularHandle h) y++wrap3 ∷ MonadIO m+      ⇒ (SIO.Handle → γ → β → IO α)+      → (RegionalFileHandle ioMode r → γ → β → m α)+wrap3 f = \h z y → liftIO $ f (regularHandle h) z y+++-------------------------------------------------------------------------------+-- * Operations on regional file handles+-------------------------------------------------------------------------------++-- **  Determining and changing the size of a file++-- | Wraps @System.IO.@'SIO.hFileSize'.+hFileSize ∷ (pr `ParentOf` cr, MonadIO cr)+          ⇒ RegionalFileHandle ioMode pr → cr Integer+hFileSize = wrap SIO.hFileSize++#ifdef __GLASGOW_HASKELL__+-- | Wraps @System.IO.@'SIO.hSetFileSize'.+hSetFileSize ∷ (pr `ParentOf` cr, MonadIO cr)+             ⇒ RegionalFileHandle ioMode pr → Integer → cr ()+hSetFileSize = wrap2 SIO.hSetFileSize+#endif+++-- ** Detecting the end of input++-- | Wraps @System.IO.@'SIO.hIsEOF'.+hIsEOF ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)+       ⇒ RegionalFileHandle ioMode pr → cr Bool+hIsEOF = wrap SIO.hIsEOF++-- | Wraps @System.IO.@'SIO.isEOF'.+isEOF ∷ MonadIO m ⇒ m Bool+isEOF = liftIO $ SIO.isEOF+++-- ** Buffering operations++-- | Wraps @System.IO.@'SIO.hSetBuffering'.+hSetBuffering ∷ (pr `ParentOf` cr, MonadIO cr)+              ⇒ RegionalFileHandle ioMode pr → BufferMode → cr ()+hSetBuffering = wrap2 SIO.hSetBuffering++-- | Wraps @System.IO.@'SIO.hGetBuffering'.+hGetBuffering ∷ (pr `ParentOf` cr, MonadIO cr)+              ⇒ RegionalFileHandle ioMode pr → cr BufferMode+hGetBuffering = wrap SIO.hGetBuffering++-- | Wraps @System.IO.@'SIO.hFlush'.+hFlush ∷ (pr `ParentOf` cr, MonadIO cr)+       ⇒ RegionalFileHandle ioMode pr → cr ()+hFlush = wrap SIO.hFlush+++-- ** Repositioning handles++-- | Wraps @System.IO.@'SIO.hGetPosn'.+hGetPosn ∷ (pr `ParentOf` cr, MonadIO cr)+         ⇒ RegionalFileHandle ioMode pr → cr HandlePosn+hGetPosn = wrap SIO.hGetPosn++-- | Wraps @System.IO.@'SIO.hSetPosn'.+hSetPosn ∷ MonadIO m ⇒ HandlePosn → m ()+hSetPosn = liftIO ∘ SIO.hSetPosn++-- | Wraps @System.IO.@'SIO.hSeek'.+hSeek ∷ (pr `ParentOf` cr, MonadIO cr)+      ⇒ RegionalFileHandle ioMode pr → SeekMode → Integer → cr ()+hSeek = wrap3 SIO.hSeek++#if !defined(__NHC__)+-- | Wraps @System.IO.@'SIO.hTell'.+hTell ∷ (pr `ParentOf` cr, MonadIO cr)+      ⇒ RegionalFileHandle ioMode pr → cr Integer+hTell = wrap SIO.hTell+#endif+++-- ** Handle properties++-- | Wraps @System.IO.@'SIO.hIsOpen'.+hIsOpen ∷ (pr `ParentOf` cr, MonadIO cr)+         ⇒ RegionalFileHandle ioMode pr → cr Bool+hIsOpen = wrap SIO.hIsOpen++-- | Wraps @System.IO.@'SIO.hIsClosed'.+hIsClosed ∷ (pr `ParentOf` cr, MonadIO cr)+           ⇒ RegionalFileHandle ioMode pr → cr Bool+hIsClosed = wrap SIO.hIsClosed++-- | Wraps @System.IO.@'SIO.hIsReadable'.+hIsReadable ∷ (pr `ParentOf` cr, MonadIO cr)+            ⇒ RegionalFileHandle ioMode pr → cr Bool+hIsReadable = wrap SIO.hIsReadable++-- | Wraps @System.IO.@'SIO.hIsWritable'.+hIsWritable ∷ (pr `ParentOf` cr, MonadIO cr)+            ⇒ RegionalFileHandle ioMode pr → cr Bool+hIsWritable = wrap SIO.hIsWritable++-- | Wraps @System.IO.@'SIO.hIsSeekable'.+hIsSeekable ∷ (pr `ParentOf` cr, MonadIO cr)+            ⇒ RegionalFileHandle ioMode pr → cr Bool+hIsSeekable = wrap SIO.hIsSeekable+++-- ** Terminal operations (not portable: GHC/Hugs only)++#if !defined(__NHC__)+-- | Wraps @System.IO.@'SIO.hIsTerminalDevice'.+hIsTerminalDevice ∷ (pr `ParentOf` cr, MonadIO cr)+                  ⇒ RegionalFileHandle ioMode pr → cr Bool+hIsTerminalDevice = wrap SIO.hIsTerminalDevice++-- | Wraps @System.IO.@'SIO.hSetEcho'.+hSetEcho ∷ (pr `ParentOf` cr, MonadIO cr)+         ⇒ RegionalFileHandle ioMode pr → Bool → cr ()+hSetEcho = wrap2 SIO.hSetEcho++-- | Wraps @System.IO.@'SIO.hGetEcho'.+hGetEcho ∷ (pr `ParentOf` cr, MonadIO cr)+         ⇒ RegionalFileHandle ioMode pr → cr Bool+hGetEcho = wrap SIO.hGetEcho+#endif+++-- ** Showing handle state (not portable: GHC only)++#ifdef __GLASGOW_HASKELL__+-- | Wraps @System.IO.@'SIO.hShow'.+hShow ∷ (pr `ParentOf` cr, MonadIO cr)+      ⇒ RegionalFileHandle ioMode pr → cr String+hShow = wrap SIO.hShow+#endif+++--------------------------------------------------------------------------------+-- * Text input and output+--------------------------------------------------------------------------------++-- ** Text input++-- | Wraps @System.IO.@'SIO.hWaitForInput'.+hWaitForInput ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)+              ⇒ RegionalFileHandle ioMode pr → Int → cr Bool+hWaitForInput = wrap2 SIO.hWaitForInput++-- | Wraps @System.IO.@'SIO.hReady'.+hReady ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)+       ⇒ RegionalFileHandle ioMode pr → cr Bool+hReady = wrap SIO.hReady++-- | Wraps @System.IO.@'SIO.hGetChar'.+hGetChar ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)+         ⇒ RegionalFileHandle ioMode pr → cr Char+hGetChar = wrap SIO.hGetChar++-- | Wraps @System.IO.@'SIO.hGetLine'.+hGetLine ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)+         ⇒ RegionalFileHandle ioMode pr → cr String+hGetLine = wrap SIO.hGetLine++-- | Wraps @System.IO.@'SIO.hLookAhead'.+hLookAhead ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)+           ⇒ RegionalFileHandle ioMode pr → cr Char+hLookAhead = wrap SIO.hLookAhead++-- | Wraps @System.IO.@'SIO.hGetContents'.+hGetContents ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)+             ⇒ RegionalFileHandle ioMode pr → cr String+hGetContents = wrap SIO.hGetContents+++-- ** Text ouput++-- | Wraps @System.IO.@'SIO.hPutChar'.+hPutChar ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)+         ⇒ RegionalFileHandle ioMode pr → Char → cr ()+hPutChar = wrap2 SIO.hPutChar++-- | Wraps @System.IO.@'SIO.hPutStr'.+hPutStr ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)+        ⇒ RegionalFileHandle ioMode pr → String → cr ()+hPutStr = wrap2 SIO.hPutStr++-- | Wraps @System.IO.@'SIO.hPutStrLn'.+hPutStrLn ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)+          ⇒ RegionalFileHandle ioMode pr → String → cr ()+hPutStrLn = wrap2 SIO.hPutStrLn++-- | Wraps @System.IO.@'SIO.hPrint'.+hPrint ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode, Show α)+       ⇒ RegionalFileHandle ioMode pr → α → cr ()+hPrint = wrap2 SIO.hPrint+++-- ** Special cases for standard input and output++-- | 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'.+putChar ∷ MonadIO m ⇒ Char → m ()+putChar c = liftIO $ SIO.putChar c++-- | 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'.+putStrLn ∷ MonadIO m ⇒ String → m ()+putStrLn s = liftIO $ SIO.putStrLn s++-- | 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'.+getChar ∷ MonadIO m ⇒ m Char+getChar = liftIO SIO.getChar++-- | 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'.+getContents ∷ MonadIO m ⇒ m String+getContents = liftIO SIO.getContents++-- | 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'.+readLn ∷ (MonadIO m, Read α) ⇒ m α+readLn = liftIO SIO.readLn+++--------------------------------------------------------------------------------+-- * Binary input and output+--------------------------------------------------------------------------------++{-| 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'.+-}+withBinaryFile ∷ MonadCatchIO pr+               ⇒ FilePath+               → IOMode ioMode+               →  (∀ s. RegionalFileHandle ioMode (RegionT File s pr)+                  → RegionT File s pr α+                  )+               → pr α+withBinaryFile filePath ioMode f = with (File True filePath (regularIOMode ioMode))+                                        (f ∘ RegionalFileHandle)++-- | Open a file in binary yielding a regional handle to it. This provides a+-- safer replacement for @System.IO.@'SIO.openBinaryFile'.+openBinaryFile ∷ MonadCatchIO pr+               ⇒ FilePath+               → IOMode ioMode+               → RegionT File s pr+                         (RegionalFileHandle ioMode (RegionT File s pr))+openBinaryFile filePath ioMode = liftM RegionalFileHandle+                               $ open $ File True filePath (regularIOMode ioMode)++-- | Wraps @System.IO.@'SIO.hSetBinaryMode'.+hSetBinaryMode ∷ (pr `ParentOf` cr, MonadIO cr)+               ⇒ RegionalFileHandle ioMode pr → Bool → cr ()+hSetBinaryMode = wrap2 SIO.hSetBinaryMode++-- | Wraps @System.IO.@'SIO.hPutBuf'.+hPutBuf ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)+        ⇒ RegionalFileHandle ioMode pr → Ptr α → Int → cr ()+hPutBuf = wrap3 SIO.hPutBuf++-- | Wraps @System.IO.@'SIO.hGetBuf'.+hGetBuf ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)+        ⇒ RegionalFileHandle ioMode pr → Ptr α → Int → cr Int+hGetBuf = wrap3 SIO.hGetBuf++#if !defined(__NHC__) && !defined(__HUGS__)+-- | Wraps @System.IO.@'SIO.hPutBufNonBlocking'.+hPutBufNonBlocking ∷ (pr `ParentOf` cr, MonadIO cr, WriteModes ioMode)+                   ⇒ RegionalFileHandle ioMode pr → Ptr α → Int → cr Int+hPutBufNonBlocking = wrap3 SIO.hPutBufNonBlocking++-- | Wraps @System.IO.@'SIO.hGetBufNonBlocking'.+hGetBufNonBlocking ∷ (pr `ParentOf` cr, MonadIO cr, ReadModes ioMode)+                   ⇒ RegionalFileHandle ioMode pr → Ptr α → Int → cr Int+hGetBufNonBlocking = wrap3 SIO.hGetBufNonBlocking+#endif+++--------------------------------------------------------------------------------+-- * Temporary files+--------------------------------------------------------------------------------++genOpenTempFile ∷ MonadCatchIO pr+                ⇒ Binary+                → DefaultPermissions+                → FilePath+                → Template+                → RegionT File s pr+                          ( FilePath+                          , RegionalFileHandle RW (RegionT File s pr)+                          )+genOpenTempFile binary defaultPerms filePath template = do+  rh@(internalHandle → FileHandle (Just fp) _) ← open $ TempFile binary+                                                                 filePath+                                                                 template+                                                                 defaultPerms+  return (fp, RegionalFileHandle rh)++-- | 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'.+openTempFile ∷ MonadCatchIO pr+             ⇒ FilePath+             → Template+             → RegionT File s pr+                       ( FilePath+                       , RegionalFileHandle RW (RegionT File s pr)+                       )+openTempFile = genOpenTempFile False False++-- | 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'.+openBinaryTempFile ∷ MonadCatchIO pr+                   ⇒ FilePath+                   → Template+                   → RegionT File s pr+                             ( FilePath+                             , RegionalFileHandle RW (RegionT File s pr)+                             )+openBinaryTempFile = genOpenTempFile True False++#if MIN_VERSION_base(4,2,0)+-- | 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'.+openTempFileWithDefaultPermissions ∷+    MonadCatchIO pr+  ⇒ FilePath+  → Template+  → RegionT File s pr+            ( FilePath+            , RegionalFileHandle RW (RegionT File s pr)+            )+openTempFileWithDefaultPermissions = genOpenTempFile False True++-- | 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+-- @System.IO.@'SIO.openBinaryTempFileWithDefaultPermissions'.+openBinaryTempFileWithDefaultPermissions ∷+    MonadCatchIO pr+  ⇒ FilePath+  → Template+  → RegionT File s pr+            ( FilePath+            , RegionalFileHandle RW (RegionT File s pr)+            )+openBinaryTempFileWithDefaultPermissions = genOpenTempFile True True+#endif+++#if MIN_VERSION_base(4,2,0) && !defined(__NHC__) && !defined(__HUGS__)+--------------------------------------------------------------------------------+-- * Unicode encoding/decoding+--------------------------------------------------------------------------------++-- | Wraps @System.IO.@'SIO.hSetEncoding'.+hSetEncoding ∷ (pr `ParentOf` cr, MonadIO cr)+             ⇒ RegionalFileHandle ioMode pr → TextEncoding → cr ()+hSetEncoding = wrap2 SIO.hSetEncoding++-- | Wraps @System.IO.@'SIO.hGetEncoding'.+hGetEncoding ∷ (pr `ParentOf` cr, MonadIO cr)+             ⇒ RegionalFileHandle ioMode pr → cr (Maybe TextEncoding)+hGetEncoding = wrap SIO.hGetEncoding++-- | Generalizes @System.IO.@'SIO.mkTextEncoding' to any 'MonadIO'.+mkTextEncoding ∷ MonadIO m ⇒ String → m TextEncoding+mkTextEncoding = liftIO ∘ SIO.mkTextEncoding+++--------------------------------------------------------------------------------+-- * Newline conversion+--------------------------------------------------------------------------------++-- | Wraps @System.IO.@'SIO.hSetNewlineMode'.+hSetNewlineMode ∷ (pr `ParentOf` cr, MonadIO cr)+                ⇒ RegionalFileHandle ioMode pr → NewlineMode → cr ()+hSetNewlineMode = wrap2 SIO.hSetNewlineMode+#endif+++-- The End ---------------------------------------------------------------------+
+ safer-file-handles.cabal view
@@ -0,0 +1,68 @@+name:          safer-file-handles+version:       0.1+cabal-version: >=1.6+build-type:    Simple+license:       BSD3+license-file:  LICENSE+copyright:     2010 Bas van Dijk+author:        Bas van Dijk+maintainer:    Bas van Dijk <v.dijk.bas@gmail.com>+stability:     experimental+category:      System, Monadic Regions+synopsis:      Type-safe file handling+description:   This package adds two safety features on top of the regular+               @System.IO@ file handles and operations:++               .++               * Regional file handles. Files must be opened in a /region/. When+                 the region terminates all opened files are automatically+                 closed. The main advantage of regions is that the handles to+                 the opened files can not be returned from the region which+                 ensures no I/O with closed files is possible.++               .++               * Explicit IOModes. The regional file handles are parameterized+                 by the IOMode in which they were opened. All operations on+                 handles explicitly specify the needed IOMode. This way it is+                 impossible to read from a write-only handle or write to a+                 read-only handle for example.++               .++               The primary technique used in this package 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>++               .++               This technique is implemented in the @regions@ package which is+               re-exported from @safer-file-handles@.++               .++               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>++source-repository head+  Type:     darcs+  Location: http://code.haskell.org/~basvandijk/code/safer-file-handles++Library+  GHC-Options: -O2 -Wall+  build-depends: base                      >= 4 && < 4.3+               , base-unicode-symbols      >= 0.1.1 && < 0.2+               , regions                   == 0.1.*+               , transformers              >= 0.1.4 && < 0.2+               , MonadCatchIO-transformers == 0.0.2.*+               , explicit-iomodes          >= 0.1.5 && < 0.2+  exposed-modules: System.IO.SaferFileHandles