{-|
Module      : Z.IO.StdStream
Description : Standard Streams and TTY devices
Copyright   : (c) Dong Han, 2018-2020
License     : BSD
Maintainer  : winterland1989@gmail.com
Stability   : experimental
Portability : non-portable

This module provides stdin\/stderr\/stdout reading and writings. Usually you don't have to use 'stderr' or 'stderrBuf' directly, 'Z.IO.Logger' provides more logging utilities through @stderr@. While 'stdinBuf' and 'stdoutBuf' is useful when you write interactive programs, 'Z.IO.Buffered' module provide many reading and writing operations. Example:

@
import Control.Concurrent.MVar
import Z.IO.LowResTimer
import Z.IO.Buffered
import Z.IO.StdStream
import qualified Z.Data.Vector as V
import qualified Z.Data.Builder as B
main = do
    -- read by '\n'
    b1 <- readStd
    -- read whatever user input in 3s, otherwise get Nothing
    b2 <- timeoutLowRes 30 $ withMVar stdinBuf readBuffer
    ...
    putStd "hello world!"

    -- Raw mode
    setStdinTTYMode TTY_MODE_RAW
    forever $ do
        withMVar stdinBuf $ \ i -> withMVar stdoutBuf $ \ o -> do
            bs <- readBuffer i
            let Just key = V.headMaybe bs
            writeBuilder o (B.hex key)
            flushBuffer o
@

-}
module Z.IO.StdStream
  ( -- * Standard input & output streams
    StdStream
  , getStdStreamFD
  , isStdStreamTTY
  , setStdinTTYMode
  , withRawStdin
  , getStdoutWinSize
  , stdin, stdout, stderr
  , stdinBuf, stdoutBuf, stderrBuf
    -- * utils
  , readStd, printStd, putStd, printStdLn, printStdLnP, putStdLn
    -- * re-export
  , withMVar
  -- * Constant
  -- ** TTYMode
  , TTYMode
  , pattern TTY_MODE_NORMAL
  , pattern TTY_MODE_RAW
  ) where

import Control.Monad
import Control.Monad.Primitive
import Control.Concurrent.MVar
import Foreign.Ptr
import System.IO.Unsafe
import qualified Z.Data.Builder             as B
import qualified Z.Data.Parser.Base         as P
import qualified Z.Data.Text.Print          as T
import qualified Z.Data.Vector              as V
import Z.IO.UV.FFI
import Z.IO.UV.Manager
import Z.IO.UV.Errno
import Z.IO.Exception
import Z.IO.Buffered
import Z.Foreign

-- | Standard input and output streams
--
-- We support both regular file and TTY based streams, when initialized
-- 'uv_guess_handle' is called to decide which type of devices are connected
-- to standard streams.
--
-- Note 'StdStream' is not thread safe, you shouldn't use them without lock.
-- For the same reason you shouldn't use stderr directly, use `Z.IO.Logger` module instead.

data StdStream
    = StdStream Bool {-# UNPACK #-}!(Ptr UVHandle) {-# UNPACK #-}!UVSlot UVManager
    -- ^ similar to UVStream, first field is is_tty
    | StdFile {-# UNPACK #-}!FD
    -- ^ similar to UVFile

instance Show StdStream where show :: StdStream -> String
show = forall a. Print a => a -> String
T.toString

instance T.Print StdStream where
    {-# INLINE toUTF8BuilderP #-}
    toUTF8BuilderP :: Int -> StdStream -> Builder ()
toUTF8BuilderP Int
p (StdStream Bool
istty Ptr UVHandle
ptr Int
slot UVManager
uvm) = Bool -> Builder () -> Builder ()
T.parenWhen (Int
p forall a. Ord a => a -> a -> Bool
> Int
10) forall a b. (a -> b) -> a -> b
$ do
        if Bool
istty
        then Builder ()
"StdStream(TTY) "
        else Builder ()
"StdStream "
        forall a. Print a => a -> Builder ()
T.toUTF8Builder Ptr UVHandle
ptr
        Char -> Builder ()
T.char7 Char
' '
        forall a. Print a => a -> Builder ()
T.toUTF8Builder Int
slot
        Char -> Builder ()
T.char7 Char
' '
        forall a. Print a => Int -> a -> Builder ()
T.toUTF8BuilderP Int
11 UVManager
uvm
    toUTF8BuilderP Int
p (StdFile FD
fd) = Bool -> Builder () -> Builder ()
T.parenWhen (Int
p forall a. Ord a => a -> a -> Bool
> Int
10) forall a b. (a -> b) -> a -> b
$ do
        Builder ()
"StdFile "
        forall a. Print a => a -> Builder ()
T.toUTF8Builder FD
fd


-- | Is this standard stream connected to a TTY device?
isStdStreamTTY :: StdStream -> Bool
isStdStreamTTY :: StdStream -> Bool
isStdStreamTTY (StdStream Bool
istty Ptr UVHandle
_ Int
_ UVManager
_) = Bool
istty
isStdStreamTTY StdStream
_                       = Bool
False

-- | Get the standard stream's OS file descriptor.
getStdStreamFD :: StdStream -> IO FD
getStdStreamFD :: StdStream -> IO FD
getStdStreamFD (StdStream Bool
_ Ptr UVHandle
hdl Int
_ UVManager
_) = forall a. (HasCallStack, Integral a) => IO a -> IO a
throwUVIfMinus (Ptr UVHandle -> IO FD
hs_uv_fileno Ptr UVHandle
hdl)
getStdStreamFD (StdFile FD
fd) = forall (m :: * -> *) a. Monad m => a -> m a
return FD
fd

instance Input StdStream where
    {-# INLINE readInput #-}
    readInput :: StdStream -> Ptr Word8 -> Int -> IO Int
readInput (StdStream Bool
_ Ptr UVHandle
hdl Int
slot UVManager
uvm) Ptr Word8
buf Int
len = forall a. IO a -> IO a
mask_ forall a b. (a -> b) -> a -> b
$ do
        UVManager -> Int -> Ptr Word8 -> Int -> IO ()
pokeBufferTable UVManager
uvm Int
slot Ptr Word8
buf Int
len
        MVar Int
m <- UVManager -> Int -> IO (MVar Int)
getBlockMVar UVManager
uvm Int
slot
        Maybe Int
_ <- forall a. MVar a -> IO (Maybe a)
tryTakeMVar MVar Int
m
        forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ forall a b. (a -> b) -> a -> b
$ forall a. HasCallStack => UVManager -> IO a -> IO a
withUVManager' UVManager
uvm (Ptr UVHandle -> IO FD
hs_uv_read_start Ptr UVHandle
hdl)
        -- since we are inside mask, this is the only place
        -- async exceptions could possibly kick in, and we should stop reading
        Int
r <- forall a. MVar a -> IO a
takeMVar MVar Int
m forall a b. IO a -> IO b -> IO a
`onException` (do
                -- normally we call 'uv_read_stop' in C read callback
                -- but when exception raise, here's the place to stop
                -- stop a handle twice will be a libuv error, so we don't check result
                FD
_ <- forall a. HasCallStack => UVManager -> IO a -> IO a
withUVManager' UVManager
uvm (Ptr UVHandle -> IO FD
uv_read_stop Ptr UVHandle
hdl)
                forall (f :: * -> *) a. Functor f => f a -> f ()
void (forall a. MVar a -> IO (Maybe a)
tryTakeMVar MVar Int
m))
        if  | Int
r forall a. Ord a => a -> a -> Bool
> Int
0  -> forall (m :: * -> *) a. Monad m => a -> m a
return Int
r
            | Int
r forall a. Eq a => a -> a -> Bool
== forall a b. (Integral a, Num b) => a -> b
fromIntegral FD
UV_EOF -> forall (m :: * -> *) a. Monad m => a -> m a
return Int
0
            | Int
r forall a. Ord a => a -> a -> Bool
< Int
0 ->  forall a. (HasCallStack, Integral a) => IO a -> IO a
throwUVIfMinus (forall (m :: * -> *) a. Monad m => a -> m a
return Int
r)
            -- r == 0 should be impossible, since we guard this situation in c side
            | Bool
otherwise -> forall a. FD -> IOEInfo -> IO a
throwUVError FD
UV_UNKNOWN IOEInfo{
                                  ioeName :: Text
ioeName = Text
"StdStream read error"
                                , ioeDescription :: Text
ioeDescription = Text
"StdStream read should never return 0 before EOF"
                                , ioeCallStack :: CallStack
ioeCallStack = HasCallStack => CallStack
callStack
                                }
    readInput (StdFile FD
fd) Ptr Word8
buf Int
len =
        forall a. (HasCallStack, Integral a) => IO a -> IO a
throwUVIfMinus forall a b. (a -> b) -> a -> b
$ FD -> Ptr Word8 -> Int -> Int64 -> IO Int
hs_uv_fs_read FD
fd Ptr Word8
buf Int
len (-Int64
1)

instance Output StdStream where
    {-# INLINE writeOutput #-}
    writeOutput :: StdStream -> Ptr Word8 -> Int -> IO ()
writeOutput (StdStream Bool
_ Ptr UVHandle
hdl Int
_ UVManager
uvm) Ptr Word8
buf Int
len = forall a. IO a -> IO a
mask_ forall a b. (a -> b) -> a -> b
$ do
        MVar Int
m <- forall a. HasCallStack => UVManager -> IO a -> IO a
withUVManager' UVManager
uvm forall a b. (a -> b) -> a -> b
$ do
            Int
reqSlot <- HasCallStack => UVManager -> IO UVSlotUnsafe -> IO Int
getUVSlot UVManager
uvm (Ptr UVHandle -> Ptr Word8 -> Int -> IO UVSlotUnsafe
hs_uv_write Ptr UVHandle
hdl Ptr Word8
buf Int
len)
            MVar Int
m <- UVManager -> Int -> IO (MVar Int)
getBlockMVar UVManager
uvm Int
reqSlot
            Maybe Int
_ <- forall a. MVar a -> IO (Maybe a)
tryTakeMVar MVar Int
m
            forall (m :: * -> *) a. Monad m => a -> m a
return MVar Int
m
        -- we can't cancel uv_write_t with current libuv,
        -- otherwise disaster will happen if buffer got collected.
        -- so we have to turn to uninterruptibleMask_'s help.
        -- i.e. writing UVStream is an uninterruptible operation.
        -- OS will guarantee writing TTY and socket will not
        -- hang forever anyway.
        forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ (forall a. IO a -> IO a
uninterruptibleMask_ forall a b. (a -> b) -> a -> b
$ forall a. MVar a -> IO a
takeMVar MVar Int
m)
    writeOutput (StdFile FD
fd) Ptr Word8
buf Int
len = Ptr Word8 -> Int -> IO ()
go Ptr Word8
buf Int
len
      where
        go :: Ptr Word8 -> Int -> IO ()
go !Ptr Word8
b !Int
bufSiz = do
            Int
written <- forall a. (HasCallStack, Integral a) => IO a -> IO a
throwUVIfMinus
                (FD -> Ptr Word8 -> Int -> Int64 -> IO Int
hs_uv_fs_write FD
fd Ptr Word8
b Int
bufSiz (-Int64
1))
            forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
written forall a. Ord a => a -> a -> Bool
< Int
bufSiz)
                (Ptr Word8 -> Int -> IO ()
go (Ptr Word8
b forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
written) (Int
bufSizforall a. Num a => a -> a -> a
-Int
written))

-- | The global stdin stream.
stdin :: StdStream
{-# NOINLINE stdin #-}
stdin :: StdStream
stdin = forall a. IO a -> a
unsafePerformIO (HasCallStack => FD -> IO StdStream
makeStdStream FD
0)

-- | The global stdout stream.
--
-- If you want a buffered device, consider use the 'stdoutBuf' first.
-- If you want to write logs, don't use 'stdout' directly, use 'Z.IO.Logger' instead.
stdout :: StdStream
{-# NOINLINE stdout #-}
stdout :: StdStream
stdout = forall a. IO a -> a
unsafePerformIO (HasCallStack => FD -> IO StdStream
makeStdStream FD
1)

-- | The global stderr stream.
--
-- If you want a buffered device, consider use the 'stderrBuf' first.
-- | If you want to write logs, don't use 'stderr' directly, use 'Z.IO.Logger' instead.
stderr :: StdStream
{-# NOINLINE stderr #-}
stderr :: StdStream
stderr = forall a. IO a -> a
unsafePerformIO (HasCallStack => FD -> IO StdStream
makeStdStream FD
2)

-- |  A global buffered stdin stream protected by 'MVar'.
--
-- If you want a buffered device, consider use the 'stdinBuf' first.
stdinBuf :: MVar BufferedInput
{-# NOINLINE stdinBuf #-}
stdinBuf :: MVar BufferedInput
stdinBuf = forall a. IO a -> a
unsafePerformIO (forall i. Input i => i -> IO BufferedInput
newBufferedInput StdStream
stdin forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a. a -> IO (MVar a)
newMVar)

-- | A global buffered stdout stream protected by 'MVar'.
--
-- If you want to write logs, don't use 'stdoutBuf' directly, use 'Z.IO.Logger' instead.
stdoutBuf :: MVar BufferedOutput
{-# NOINLINE stdoutBuf #-}
stdoutBuf :: MVar BufferedOutput
stdoutBuf = forall a. IO a -> a
unsafePerformIO (forall o. Output o => o -> IO BufferedOutput
newBufferedOutput StdStream
stdout forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a. a -> IO (MVar a)
newMVar)

-- | A global buffered stderr stream protected by 'MVar'.
--
-- If you want to write logs, don't use 'stderrBuf' directly, use 'Z.IO.Logger' instead.
stderrBuf :: MVar BufferedOutput
{-# NOINLINE stderrBuf #-}
stderrBuf :: MVar BufferedOutput
stderrBuf = forall a. IO a -> a
unsafePerformIO (forall o. Output o => o -> IO BufferedOutput
newBufferedOutput StdStream
stderr forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a. a -> IO (MVar a)
newMVar)

makeStdStream :: HasCallStack => FD -> IO StdStream
makeStdStream :: HasCallStack => FD -> IO StdStream
makeStdStream FD
fd = do
    FD
typ <- FD -> IO FD
uv_guess_handle FD
fd
    if FD
typ forall a. Eq a => a -> a -> Bool
== FD
UV_FILE
    then forall (m :: * -> *) a. Monad m => a -> m a
return (FD -> StdStream
StdFile FD
fd)
    else forall a. IO a -> IO a
mask_ forall a b. (a -> b) -> a -> b
$ do
        UVManager
uvm <- IO UVManager
getUVManager
        forall a. HasCallStack => UVManager -> (Ptr UVLoop -> IO a) -> IO a
withUVManager UVManager
uvm forall a b. (a -> b) -> a -> b
$ \ Ptr UVLoop
loop -> do
            Ptr UVHandle
hdl <- Ptr UVLoop -> IO (Ptr UVHandle)
hs_uv_handle_alloc Ptr UVLoop
loop
            Int
slot <- HasCallStack => UVManager -> IO UVSlotUnsafe -> IO Int
getUVSlot UVManager
uvm (Ptr UVHandle -> IO UVSlotUnsafe
peekUVHandleData Ptr UVHandle
hdl)
            Maybe Int
_ <- forall a. MVar a -> IO (Maybe a)
tryTakeMVar forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< UVManager -> Int -> IO (MVar Int)
getBlockMVar UVManager
uvm Int
slot   -- clear the parking spot
            case FD
typ of
                FD
UV_TTY -> do
                    forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ (Ptr UVLoop -> Ptr UVHandle -> FD -> IO FD
uv_tty_init Ptr UVLoop
loop Ptr UVHandle
hdl (forall a b. (Integral a, Num b) => a -> b
fromIntegral FD
fd))
                    forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Ptr UVHandle -> Int -> UVManager -> StdStream
StdStream Bool
True Ptr UVHandle
hdl Int
slot UVManager
uvm)
                FD
UV_TCP -> do
                    forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ (Ptr UVLoop -> Ptr UVHandle -> IO FD
uv_tcp_init Ptr UVLoop
loop Ptr UVHandle
hdl)
                    forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ (Ptr UVHandle -> FD -> IO FD
uv_tcp_open Ptr UVHandle
hdl FD
fd)
                    forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Ptr UVHandle -> Int -> UVManager -> StdStream
StdStream Bool
False Ptr UVHandle
hdl Int
slot UVManager
uvm)
                FD
UV_UDP ->
                    forall a. FD -> IOEInfo -> IO a
throwUVError FD
UV_EXDEV IOEInfo{
                                  ioeName :: Text
ioeName = Text
"EXDEV"
                                , ioeDescription :: Text
ioeDescription = Text
"redirect to UDP is not supported"
                                , ioeCallStack :: CallStack
ioeCallStack = HasCallStack => CallStack
callStack
                                }
                -- normally this would be UV_NAMED_PIPE,
                -- but we also give UV_UNKNOWN_HANDLE a try.
                FD
_ -> do
                    forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ (Ptr UVLoop -> Ptr UVHandle -> FD -> IO FD
uv_pipe_init Ptr UVLoop
loop Ptr UVHandle
hdl FD
0)
                    forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ (Ptr UVHandle -> FD -> IO FD
uv_pipe_open Ptr UVHandle
hdl FD
fd)
                    forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Ptr UVHandle -> Int -> UVManager -> StdStream
StdStream Bool
False Ptr UVHandle
hdl Int
slot UVManager
uvm)

-- | Change terminal's mode if stdin is connected to a terminal,
-- do nothing if stdout is not connected to TTY.
--
setStdinTTYMode :: TTYMode -> IO ()
setStdinTTYMode :: FD -> IO ()
setStdinTTYMode FD
mode = case StdStream
stdin of
    StdStream Bool
True Ptr UVHandle
hdl Int
_ UVManager
uvm ->
        forall a. HasCallStack => UVManager -> IO a -> IO a
withUVManager' UVManager
uvm forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ forall a b. (a -> b) -> a -> b
$ Ptr UVHandle -> FD -> IO FD
uv_tty_set_mode Ptr UVHandle
hdl FD
mode
    StdStream
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | Set stdin to raw mode before run IO, set back to normal after.
withRawStdin :: IO a -> IO a
withRawStdin :: forall a. IO a -> IO a
withRawStdin = forall a b c. IO a -> IO b -> IO c -> IO c
bracket_ (FD -> IO ()
setStdinTTYMode FD
TTY_MODE_RAW) (FD -> IO ()
setStdinTTYMode FD
TTY_MODE_NORMAL)

-- | Get terminal's output window size in (width, height) format,
-- return (-1, -1) if stdout is not connected to TTY.
getStdoutWinSize :: HasCallStack => IO (Int, Int)
getStdoutWinSize :: HasCallStack => IO (Int, Int)
getStdoutWinSize = case StdStream
stdout of
    StdStream Bool
True Ptr UVHandle
hdl Int
_ UVManager
uvm ->
        forall a. HasCallStack => UVManager -> IO a -> IO a
withUVManager' UVManager
uvm forall a b. (a -> b) -> a -> b
$ do
            (FD
w, (FD
h, ())) <- forall a b. Prim a => (MBA# a -> IO b) -> IO (a, b)
allocPrimUnsafe @CInt forall a b. (a -> b) -> a -> b
$ \ MBA# a
w ->
                forall a b. Prim a => (MBA# a -> IO b) -> IO (a, b)
allocPrimUnsafe @CInt forall a b. (a -> b) -> a -> b
$ \ MBA# a
h -> forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ forall a b. (a -> b) -> a -> b
$ Ptr UVHandle -> MBA# a -> MBA# a -> IO FD
uv_tty_get_winsize Ptr UVHandle
hdl MBA# a
w MBA# a
h
            forall (m :: * -> *) a. Monad m => a -> m a
return (forall a b. (Integral a, Num b) => a -> b
fromIntegral FD
w, forall a b. (Integral a, Num b) => a -> b
fromIntegral FD
h)
    StdStream
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return (-Int
1, -Int
1)

--------------------------------------------------------------------------------

-- | Print a 'Print' and flush to stdout, without linefeed.
printStd :: (HasCallStack, T.Print a) => a -> IO ()
printStd :: forall a. (HasCallStack, Print a) => a -> IO ()
printStd a
s = forall a. HasCallStack => Builder a -> IO ()
putStd (forall a. Print a => a -> Builder ()
T.toUTF8Builder a
s)

-- | Print a 'Builder' and flush to stdout, without linefeed.
putStd :: HasCallStack => B.Builder a -> IO ()
putStd :: forall a. HasCallStack => Builder a -> IO ()
putStd Builder a
b = forall a b. MVar a -> (a -> IO b) -> IO b
withMVar MVar BufferedOutput
stdoutBuf forall a b. (a -> b) -> a -> b
$ \ BufferedOutput
o -> do
    forall a. HasCallStack => BufferedOutput -> Builder a -> IO ()
writeBuilder BufferedOutput
o Builder a
b
    HasCallStack => BufferedOutput -> IO ()
flushBuffer BufferedOutput
o

-- | Print a 'Print' and flush to stdout, with a linefeed.
printStdLn :: (HasCallStack, T.Print a) => a -> IO ()
printStdLn :: forall a. (HasCallStack, Print a) => a -> IO ()
printStdLn a
s = forall a. HasCallStack => Builder a -> IO ()
putStdLn (forall a. Print a => a -> Builder ()
T.toUTF8Builder a
s)

-- | Similar to 'printStdLn', 'P.Parser' debug tool.
printStdLnP :: (HasCallStack, T.Print a) => a -> P.Parser ()
printStdLnP :: forall a. (HasCallStack, Print a) => a -> Parser ()
printStdLnP a
s = forall (m :: * -> *) a. PrimMonad m => IO a -> m a
unsafeIOToPrim forall a b. (a -> b) -> a -> b
$ forall a. HasCallStack => Builder a -> IO ()
putStdLn (forall a. Print a => a -> Builder ()
T.toUTF8Builder a
s)

-- | Print a 'Builder' and flush to stdout, with a linefeed.
putStdLn :: HasCallStack => B.Builder a -> IO ()
putStdLn :: forall a. HasCallStack => Builder a -> IO ()
putStdLn Builder a
b = forall a b. MVar a -> (a -> IO b) -> IO b
withMVar MVar BufferedOutput
stdoutBuf forall a b. (a -> b) -> a -> b
$ \ BufferedOutput
o -> do
    forall a. HasCallStack => BufferedOutput -> Builder a -> IO ()
writeBuilder BufferedOutput
o (Builder a
b forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Char -> Builder ()
B.char8 Char
'\n')
    HasCallStack => BufferedOutput -> IO ()
flushBuffer BufferedOutput
o

-- | Read a line from stdin(in normal mode).
--
-- This function will throw 'ECLOSED' when meet EOF, which may cause trouble if stdin is connected
-- to a file, use 'readLine' instead.
readStd :: HasCallStack => IO V.Bytes
readStd :: HasCallStack => IO Bytes
readStd = forall a b. MVar a -> (a -> IO b) -> IO b
withMVar MVar BufferedInput
stdinBuf forall a b. (a -> b) -> a -> b
$ \ BufferedInput
s -> do
    Maybe Bytes
line <- HasCallStack => BufferedInput -> IO (Maybe Bytes)
readLine BufferedInput
s
    case Maybe Bytes
line of Just Bytes
line' -> forall (m :: * -> *) a. Monad m => a -> m a
return Bytes
line'
                 Maybe Bytes
Nothing    -> forall e a. Exception e => e -> IO a
throwIO (IOEInfo -> ResourceVanished
ResourceVanished
                    (Text -> Text -> CallStack -> IOEInfo
IOEInfo Text
"ECLOSED" Text
"stdin is closed" HasCallStack => CallStack
callStack))