1 {-# LANGUAGE BangPatterns, CPP, RecordWildCards #-}
    2 -- |
    3 -- Module      : Data.Text.Lazy.IO
    4 -- Copyright   : (c) 2009, 2010 Bryan O'Sullivan,
    5 --               (c) 2009 Simon Marlow
    6 -- License     : BSD-style
    7 -- Maintainer  : bos@serpentine.com
    8 -- Stability   : experimental
    9 -- Portability : GHC
   10 --
   11 -- Efficient locale-sensitive support for lazy text I\/O.
   12 
   13 module Data.Text.Lazy.IO
   14     (
   15     -- * Locale support
   16     -- $locale
   17     -- * File-at-a-time operations
   18       readFile
   19     , writeFile
   20     , appendFile
   21     -- * Operations on handles
   22     , hGetContents
   23     , hGetLine
   24     , hPutStr
   25     , hPutStrLn
   26     -- * Special cases for standard input and output
   27     , interact
   28     , getContents
   29     , getLine
   30     , putStr
   31     , putStrLn
   32     ) where
   33 
   34 import Data.Text.Lazy (Text)
   35 import Prelude hiding (appendFile, getContents, getLine, interact, putStr,
   36                        putStrLn, readFile, writeFile)
   37 import System.IO (Handle, IOMode(..), hPutChar, openFile, stdin, stdout,
   38                   withFile)
   39 import qualified Data.Text.IO as T
   40 import qualified Data.Text.Lazy as L
   41 #if __GLASGOW_HASKELL__ <= 610
   42 import Data.Text.Lazy.Encoding (decodeUtf8)
   43 import qualified Data.ByteString.Char8 as S8
   44 import qualified Data.ByteString.Lazy.Char8 as L8
   45 #else
   46 import Control.Exception (throw)
   47 import Control.Monad (when)
   48 import Data.IORef (readIORef)
   49 import Data.Text.IO.Internal (hGetLineWith, readChunk)
   50 import Data.Text.Lazy.Internal (chunk, empty)
   51 import GHC.IO.Buffer (isEmptyBuffer)
   52 import GHC.IO.Exception (IOException(..), IOErrorType(..), ioException)
   53 import GHC.IO.Handle.Internals (augmentIOError, hClose_help,
   54                                 wantReadableHandle, withHandle)
   55 import GHC.IO.Handle.Types (Handle__(..), HandleType(..))
   56 import System.IO (BufferMode(..), hGetBuffering, hSetBuffering)
   57 import System.IO.Error (isEOFError)
   58 import System.IO.Unsafe (unsafeInterleaveIO)
   59 #endif
   60 
   61 -- | Read a file and return its contents as a string.  The file is
   62 -- read lazily, as with 'getContents'.
   63 readFile :: FilePath -> IO Text
   64 -- never enteredreadFile name = openFile name ReadMode >>= hGetContents
   65 
   66 -- | Write a string to a file.  The file is truncated to zero length
   67 -- before writing begins.
   68 writeFile :: FilePath -> Text -> IO ()
   69 -- never enteredwriteFile p = withFile p WriteMode . flip hPutStr
   70 
   71 -- | Write a string the end of a file.
   72 appendFile :: FilePath -> Text -> IO ()
   73 -- never enteredappendFile p = withFile p AppendMode . flip hPutStr
   74 
   75 -- | Lazily read the remaining contents of a 'Handle'.  The 'Handle'
   76 -- will be closed after the read completes, or on error.
   77 hGetContents :: Handle -> IO Text
   78 #if __GLASGOW_HASKELL__ <= 610
   79 hGetContents = fmap decodeUtf8 . L8.hGetContents
   80 #else
   81 -- entered 100 timeshGetContents h = do
   82   chooseGoodBuffering h
   83   wantReadableHandle "hGetContents" h $ \hh -> do
   84     ts <- lazyRead h
   85     return (hh{haType=SemiClosedHandle}, ts)
   86 
   87 -- | Use a more efficient buffer size if we're reading in
   88 -- block-buffered mode with the default buffer size.
   89 chooseGoodBuffering :: Handle -> IO ()
   90 -- entered 100 timeschooseGoodBuffering h = do
   91   bufMode <- hGetBuffering h
   92   when (bufMode == BlockBuffering Nothing) $
   93     hSetBuffering h (BlockBuffering (Just 16384))
   94 
   95 lazyRead :: Handle -> IO Text
   96 -- entered 100 timeslazyRead h = unsafeInterleaveIO $
   97   withHandle "hGetContents" h $ \hh -> do
   98     case haType hh of
   99       ClosedHandle     -> return (hh, L.empty)
  100       SemiClosedHandle -> lazyReadBuffered h hh
  101       _                -> ioException 
  102                           (IOError (Just h) IllegalOperation "hGetContents"
  103                            "illegal handle type" Nothing Nothing)
  104 
  105 lazyReadBuffered :: Handle -> Handle__ -> IO (Handle__, Text)
  106 -- entered 203 timeslazyReadBuffered h hh@Handle__{..} = do
  107    buf <- readIORef haCharBuffer
  108    (do t <- readChunk hh buf
  109        ts <- lazyRead h
  110        return (hh, chunk t ts)) `catch` \e -> do
  111          (hh', _) <- hClose_help hh
  112          if isEOFError e
  113            then return $ if isEmptyBuffer buf
  114                          then (hh', empty)
  115                          else (hh', L.singleton '\r')
  116            else throw (augmentIOError e "hGetContents" h)
  117 #endif
  118 
  119 -- | Read a single line from a handle.
  120 hGetLine :: Handle -> IO Text
  121 #if __GLASGOW_HASKELL__ <= 610
  122 hGetLine = fmap (decodeUtf8 . L8.fromChunks . (:[])) . S8.hGetLine
  123 #else
  124 -- entered oncehGetLine = hGetLineWith L.fromChunks
  125 #endif
  126 
  127 -- | Write a string to a handle.
  128 hPutStr :: Handle -> Text -> IO ()
  129 -- entered 200 timeshPutStr h = mapM_ (T.hPutStr h) . L.toChunks
  130 
  131 -- | Write a string to a handle, followed by a newline.
  132 hPutStrLn :: Handle -> Text -> IO ()
  133 -- entered 100 timeshPutStrLn h t = hPutStr h t >> hPutChar h '\n'
  134 
  135 -- | The 'interact' function takes a function of type @Text -> Text@
  136 -- as its argument. The entire input from the standard input device is
  137 -- passed (lazily) to this function as its argument, and the resulting
  138 -- string is output on the standard output device.
  139 interact :: (Text -> Text) -> IO ()
  140 -- never enteredinteract f = putStr . f =<< getContents
  141 
  142 -- | Lazily read all user input on 'stdin' as a single string.
  143 getContents :: IO Text
  144 -- never enteredgetContents = hGetContents stdin
  145 
  146 -- | Read a single line of user input from 'stdin'.
  147 getLine :: IO Text
  148 -- never enteredgetLine = hGetLine stdin
  149 
  150 -- | Write a string to 'stdout'.
  151 putStr :: Text -> IO ()
  152 -- never enteredputStr = hPutStr stdout
  153 
  154 -- | Write a string to 'stdout', followed by a newline.
  155 putStrLn :: Text -> IO ()
  156 -- never enteredputStrLn = hPutStrLn stdout
  157 
  158 -- $locale
  159 --
  160 -- /Note/: The behaviour of functions in this module depends on the
  161 -- version of GHC you are using.
  162 --
  163 -- Beginning with GHC 6.12, text I\/O is performed using the system or
  164 -- handle's current locale and line ending conventions.
  165 --
  166 -- Under GHC 6.10 and earlier, the system I\/O libraries /do not
  167 -- support/ locale-sensitive I\/O or line ending conversion.  On these
  168 -- versions of GHC, functions in this library all use UTF-8.  What
  169 -- does this mean in practice?
  170 --
  171 -- * All data that is read will be decoded as UTF-8.
  172 --
  173 -- * Before data is written, it is first encoded as UTF-8.
  174 --
  175 -- * On both reading and writing, the platform's native newline
  176 --   conversion is performed.
  177 --
  178 -- If you must use a non-UTF-8 locale on an older version of GHC, you
  179 -- will have to perform the transcoding yourself, e.g. as follows:
  180 --
  181 -- > import qualified Data.ByteString.Lazy as B
  182 -- > import Data.Text.Lazy (Text)
  183 -- > import Data.Text.Lazy.Encoding (encodeUtf16)
  184 -- >
  185 -- > putStr_Utf16LE :: Text -> IO ()
  186 -- > putStr_Utf16LE t = B.putStr (encodeUtf16LE t)