|
System.IO.SaferFileHandles | Maintainer | Bas van Dijk <v.dijk.bas@gmail.com> |
|
|
|
|
|
Description |
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!
|
|
Synopsis |
|
data File | | type FilePath = String | | data R | | data W | | data A | | data RW | | class ReadModes ioMode | | class WriteModes ioMode | | module Control.Monad.Trans.Region | | data RegionalFileHandle ioMode r | | openFile :: MonadCatchIO pr => FilePath -> IOMode ioMode -> RegionT File s pr (RegionalFileHandle ioMode (RegionT File s pr)) | | withFile :: MonadCatchIO pr => FilePath -> IOMode ioMode -> (forall s. RegionalFileHandle ioMode (RegionT File s pr) -> RegionT File s pr α) -> pr α | | | | stdin :: MonadCatchIO pr => RegionT File s pr (RegionalFileHandle R (RegionT File s pr)) | | stdout :: MonadCatchIO pr => RegionT File s pr (RegionalFileHandle W (RegionT File s pr)) | | stderr :: MonadCatchIO pr => RegionT File s pr (RegionalFileHandle W (RegionT File s pr)) | | hFileSize :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr Integer | | hSetFileSize :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> Integer -> cr () | | hIsEOF :: (ParentOf pr cr, MonadIO cr, ReadModes ioMode) => RegionalFileHandle ioMode pr -> cr Bool | | isEOF :: MonadIO m => m Bool | | | | hSetBuffering :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> BufferMode -> cr () | | hGetBuffering :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr BufferMode | | hFlush :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr () | | hGetPosn :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr HandlePosn | | hSetPosn :: MonadIO m => HandlePosn -> m () | | data HandlePosn | | hSeek :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> SeekMode -> Integer -> cr () | | | | hTell :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr Integer | | hIsOpen :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr Bool | | hIsClosed :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr Bool | | hIsReadable :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr Bool | | hIsWritable :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr Bool | | hIsSeekable :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr Bool | | hIsTerminalDevice :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr Bool | | hSetEcho :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> Bool -> cr () | | hGetEcho :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr Bool | | hShow :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr String | | hWaitForInput :: (ParentOf pr cr, MonadIO cr, ReadModes ioMode) => RegionalFileHandle ioMode pr -> Int -> cr Bool | | hReady :: (ParentOf pr cr, MonadIO cr, ReadModes ioMode) => RegionalFileHandle ioMode pr -> cr Bool | | hGetChar :: (ParentOf pr cr, MonadIO cr, ReadModes ioMode) => RegionalFileHandle ioMode pr -> cr Char | | hGetLine :: (ParentOf pr cr, MonadIO cr, ReadModes ioMode) => RegionalFileHandle ioMode pr -> cr String | | hLookAhead :: (ParentOf pr cr, MonadIO cr, ReadModes ioMode) => RegionalFileHandle ioMode pr -> cr Char | | hGetContents :: (ParentOf pr cr, MonadIO cr, ReadModes ioMode) => RegionalFileHandle ioMode pr -> cr String | | hPutChar :: (ParentOf pr cr, MonadIO cr, WriteModes ioMode) => RegionalFileHandle ioMode pr -> Char -> cr () | | hPutStr :: (ParentOf pr cr, MonadIO cr, WriteModes ioMode) => RegionalFileHandle ioMode pr -> String -> cr () | | hPutStrLn :: (ParentOf pr cr, MonadIO cr, WriteModes ioMode) => RegionalFileHandle ioMode pr -> String -> cr () | | hPrint :: (ParentOf pr cr, MonadIO cr, WriteModes ioMode, Show α) => RegionalFileHandle ioMode pr -> α -> cr () | | interact :: MonadIO m => (String -> String) -> m () | | putChar :: MonadIO m => Char -> m () | | putStr :: MonadIO m => String -> m () | | putStrLn :: MonadIO m => String -> m () | | print :: (MonadIO m, Show α) => α -> m () | | getChar :: MonadIO m => m Char | | getLine :: MonadIO m => m String | | getContents :: MonadIO m => m String | | readIO :: (MonadIO m, Read α) => String -> m α | | readLn :: (MonadIO m, Read α) => m α | | withBinaryFile :: MonadCatchIO pr => FilePath -> IOMode ioMode -> (forall s. RegionalFileHandle ioMode (RegionT File s pr) -> RegionT File s pr α) -> pr α | | openBinaryFile :: MonadCatchIO pr => FilePath -> IOMode ioMode -> RegionT File s pr (RegionalFileHandle ioMode (RegionT File s pr)) | | hSetBinaryMode :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> Bool -> cr () | | hPutBuf :: (ParentOf pr cr, MonadIO cr, WriteModes ioMode) => RegionalFileHandle ioMode pr -> Ptr α -> Int -> cr () | | hGetBuf :: (ParentOf pr cr, MonadIO cr, ReadModes ioMode) => RegionalFileHandle ioMode pr -> Ptr α -> Int -> cr Int | | hPutBufNonBlocking :: (ParentOf pr cr, MonadIO cr, WriteModes ioMode) => RegionalFileHandle ioMode pr -> Ptr α -> Int -> cr Int | | hGetBufNonBlocking :: (ParentOf pr cr, MonadIO cr, ReadModes ioMode) => RegionalFileHandle ioMode pr -> Ptr α -> Int -> cr Int | | type DefaultPermissions = Bool | | type Template = String | | openTempFile :: MonadCatchIO pr => FilePath -> Template -> RegionT File s pr (FilePath, RegionalFileHandle RW (RegionT File s pr)) | | openBinaryTempFile :: MonadCatchIO pr => FilePath -> Template -> RegionT File s pr (FilePath, RegionalFileHandle RW (RegionT File s pr)) | | openTempFileWithDefaultPermissions :: MonadCatchIO pr => FilePath -> Template -> RegionT File s pr (FilePath, RegionalFileHandle RW (RegionT File s pr)) | | openBinaryTempFileWithDefaultPermissions :: MonadCatchIO pr => FilePath -> Template -> RegionT File s pr (FilePath, RegionalFileHandle RW (RegionT File s pr)) | | hSetEncoding :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> TextEncoding -> cr () | | hGetEncoding :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr (Maybe TextEncoding) | | data TextEncoding | | latin1 :: TextEncoding | | utf8 :: TextEncoding | | utf8_bom :: TextEncoding | | utf16 :: TextEncoding | | utf16le :: TextEncoding | | utf16be :: TextEncoding | | utf32 :: TextEncoding | | utf32le :: TextEncoding | | utf32be :: TextEncoding | | localeEncoding :: TextEncoding | | mkTextEncoding :: MonadIO m => String -> m TextEncoding | | hSetNewlineMode :: (ParentOf pr cr, MonadIO cr) => RegionalFileHandle ioMode pr -> NewlineMode -> cr () | | | | nativeNewline :: Newline | | data NewlineMode = NewlineMode {} | | noNewlineTranslation :: NewlineMode | | universalNewlineMode :: NewlineMode | | nativeNewlineMode :: NewlineMode |
|
|
|
Files with explicit IO modes as scarce resources
|
|
|
A file scarce resource.
You can open a file with one of the following functions:
| Instances | |
|
|
|
File and directory names are values of type String, whose precise
meaning is operating system dependent. Files can be opened, yielding a
handle which can then be used to operate on the contents of that file.
|
|
IO Modes
|
|
Types that represent the IOMode an opened file can be in.
|
|
|
Read only.
| Instances | |
|
|
|
Write only.
| Instances | |
|
|
|
Write only by appending.
| Instances | |
|
|
|
Both read and write.
| Instances | |
|
|
|
Class of readable IO mode types.
| | Instances | |
|
|
class WriteModes ioMode | Source |
|
Class of writable IO mode types.
| | Instances | |
|
|
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 |
|
data RegionalFileHandle ioMode r | Source |
|
A handle to a file parameterised by the IOMode with which the file was
opened and the region r in which it was created.
| Instances | |
|
|
|
Open a file yielding a regional handle to it. This provides a safer
replacement for System.IO.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.
|
|
|
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.withFile.
|
|
data IOMode ioMode where | Source |
|
The IOMode GADT which for each constructor specifies the associated IOMode
type.
Also see: System.IO.IOMode.
| Constructors | | Instances | |
|
|
Standard handles
|
|
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?
|
|
|
Return a regional handle to standard input. This provides a safer
replacement for System.IO.stdin.
|
|
|
Return a regional handle to standard output. This provides a safer
replacement for System.IO.stdout.
|
|
|
Return a regional handle to standard error. This provides a safer
replacement for System.IO.stderr.
|
|
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...
|
|
Operations on regional file handles
|
|
Determining and changing the size of a file
|
|
|
Wraps System.IO.hFileSize.
|
|
|
Wraps System.IO.hSetFileSize.
|
|
Detecting the end of input
|
|
|
Wraps System.IO.hIsEOF.
|
|
|
Wraps System.IO.isEOF.
|
|
Buffering operations
|
|
|
Three kinds of buffering are supported: line-buffering,
block-buffering or no-buffering. These modes have the following
effects. For output, items are written out, or flushed,
from the internal buffer according to the buffer mode:
- line-buffering: the entire output buffer is flushed
whenever a newline is output, the buffer overflows,
a System.IO.hFlush is issued, or the handle is closed.
- block-buffering: the entire buffer is written out whenever it
overflows, a System.IO.hFlush is issued, or the handle is closed.
- no-buffering: output is written immediately, and never stored
in the buffer.
An implementation is free to flush the buffer more frequently,
but not less frequently, than specified above.
The output buffer is emptied as soon as it has been written out.
Similarly, input occurs according to the buffer mode for the handle:
- line-buffering: when the buffer for the handle is not empty,
the next item is obtained from the buffer; otherwise, when the
buffer is empty, characters up to and including the next newline
character are read into the buffer. No characters are available
until the newline character is available or the buffer is full.
- block-buffering: when the buffer for the handle becomes empty,
the next block of data is read into the buffer.
- no-buffering: the next input item is read and returned.
The System.IO.hLookAhead operation implies that even a no-buffered
handle may require a one-character buffer.
The default buffering mode when a handle is opened is
implementation-dependent and may depend on the file system object
which is attached to that handle.
For most implementations, physical files will normally be block-buffered
and terminals will normally be line-buffered.
| Constructors | NoBuffering | buffering is disabled if possible.
| LineBuffering | line-buffering should be enabled if possible.
| BlockBuffering (Maybe Int) | block-buffering should be enabled if possible.
The size of the buffer is n items if the argument
is Just n and is otherwise implementation-dependent.
|
| Instances | |
|
|
|
Wraps System.IO.hSetBuffering.
|
|
|
Wraps System.IO.hGetBuffering.
|
|
|
Wraps System.IO.hFlush.
|
|
Repositioning handles
|
|
|
Wraps System.IO.hGetPosn.
|
|
|
Wraps System.IO.hSetPosn.
|
|
|
Instances | |
|
|
|
Wraps System.IO.hSeek.
|
|
|
A mode that determines the effect of hSeek hdl mode i, as follows:
| Constructors | AbsoluteSeek | the position of hdl is set to i.
| RelativeSeek | the position of hdl is set to offset i
from the current position.
| SeekFromEnd | the position of hdl is set to offset i
from the end of the file.
|
| Instances | |
|
|
|
Wraps System.IO.hTell.
|
|
Handle properties
|
|
|
Wraps System.IO.hIsOpen.
|
|
|
Wraps System.IO.hIsClosed.
|
|
|
Wraps System.IO.hIsReadable.
|
|
|
Wraps System.IO.hIsWritable.
|
|
|
Wraps System.IO.hIsSeekable.
|
|
Terminal operations (not portable: GHC/Hugs only)
|
|
|
Wraps System.IO.hIsTerminalDevice.
|
|
|
Wraps System.IO.hSetEcho.
|
|
|
Wraps System.IO.hGetEcho.
|
|
Showing handle state (not portable: GHC only)
|
|
|
Wraps System.IO.hShow.
|
|
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.
|
|
|
Wraps System.IO.hWaitForInput.
|
|
|
Wraps System.IO.hReady.
|
|
|
Wraps System.IO.hGetChar.
|
|
|
Wraps System.IO.hGetLine.
|
|
|
Wraps System.IO.hLookAhead.
|
|
|
Wraps System.IO.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.
|
|
|
Wraps System.IO.hPutChar.
|
|
|
Wraps System.IO.hPutStr.
|
|
|
Wraps System.IO.hPutStrLn.
|
|
|
Wraps System.IO.hPrint.
|
|
Special cases for standard input and output
|
|
|
Generalizes System.IO.interact to any MonadIO.
|
|
|
Generalizes System.IO.putChar to any MonadIO.
|
|
|
Generalizes System.IO.putStr to any MonadIO.
|
|
|
Generalizes System.IO.putStrLn to any MonadIO.
|
|
|
Generalizes System.IO.print to any MonadIO.
|
|
|
Generalizes System.IO.getChar to any MonadIO.
|
|
|
Generalizes System.IO.getLine to any MonadIO.
|
|
|
Generalizes System.IO.getContents to any MonadIO.
|
|
|
Generalizes System.IO.readIO to any MonadIO.
|
|
|
Generalizes System.IO.readLn to any MonadIO.
|
|
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.withBinaryFile.
|
|
|
Open a file in binary yielding a regional handle to it. This provides a
safer replacement for System.IO.openBinaryFile.
|
|
|
Wraps System.IO.hSetBinaryMode.
|
|
|
Wraps System.IO.hPutBuf.
|
|
|
Wraps System.IO.hGetBuf.
|
|
|
Wraps System.IO.hPutBufNonBlocking.
|
|
|
Wraps System.IO.hGetBufNonBlocking.
|
|
Temporary files
|
|
|
Should default permissions be used when opening a temporary file?
|
|
|
The template of a temporary file path.
|
|
|
Open a temporary file yielding a regional handle to it paired with the
generated file path. This provides a safer replacement for
System.IO.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.openBinaryTempFile.
|
|
|
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.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
System.IO.openBinaryTempFileWithDefaultPermissions.
|
|
Unicode encoding/decoding
|
|
|
Wraps System.IO.hSetEncoding.
|
|
|
Wraps System.IO.hGetEncoding.
|
|
Unicode encodings
|
|
|
A TextEncoding is a specification of a conversion scheme
between sequences of bytes and sequences of Unicode characters.
For example, UTF-8 is an encoding of Unicode characters into a sequence
of bytes. The TextEncoding for UTF-8 is utf8.
|
|
|
|
The Latin1 (ISO8859-1) encoding. This encoding maps bytes
directly to the first 256 Unicode code points, and is thus not a
complete Unicode encoding. An attempt to write a character greater than
'\255' to a Handle using the latin1 encoding will result in an error.
|
|
|
The UTF-8 Unicode encoding
|
|
|
The UTF-8 Unicode encoding, with a byte-order-mark (BOM; the byte
sequence 0xEF 0xBB 0xBF). This encoding behaves like utf8,
except that on input, the BOM sequence is ignored at the beginning
of the stream, and on output, the BOM sequence is prepended.
The byte-order-mark is strictly unnecessary in UTF-8, but is
sometimes used to identify the encoding of a file.
|
|
|
The UTF-16 Unicode encoding (a byte-order-mark should be used to
indicate endianness).
|
|
|
The UTF-16 Unicode encoding (litte-endian)
|
|
|
The UTF-16 Unicode encoding (big-endian)
|
|
|
The UTF-32 Unicode encoding (a byte-order-mark should be used to
indicate endianness).
|
|
|
The UTF-32 Unicode encoding (litte-endian)
|
|
|
The UTF-32 Unicode encoding (big-endian)
|
|
|
The Unicode encoding of the current locale
|
|
|
Generalizes System.IO.mkTextEncoding to any MonadIO.
|
|
Newline conversion
|
|
|
Wraps System.IO.hSetNewlineMode.
|
|
|
The representation of a newline in the external file or stream.
| Constructors | | Instances | |
|
|
|
The native newline representation for the current platform: LF
on Unix systems, CRLF on Windows.
|
|
|
Specifies the translation, if any, of newline characters between
internal Strings and the external file or stream. Haskell Strings
are assumed to represent newlines with the '\n' character; the
newline mode specifies how to translate '\n' on output, and what to
translate into '\n' on input.
| Constructors | NewlineMode | | inputNL :: Newline | the representation of newlines on input
| outputNL :: Newline | the representation of newlines on output
|
|
| Instances | |
|
|
|
Do no newline translation at all.
noNewlineTranslation = NewlineMode { inputNL = LF, outputNL = LF }
|
|
|
Map '\r\n' into '\n' on input, and '\n' to the native newline
represetnation on output. This mode can be used on any platform, and
works with text files using any newline convention. The downside is
that readFile >>= writeFile might yield a different file.
universalNewlineMode = NewlineMode { inputNL = CRLF,
outputNL = nativeNewline }
|
|
|
Use the native newline representation on both input and output
nativeNewlineMode = NewlineMode { inputNL = nativeNewline
outputNL = nativeNewline }
|
|
Produced by Haddock version 2.6.0 |