{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE NoImplicitPrelude, BangPatterns #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  GHC.IO.Device
-- Copyright   :  (c) The University of Glasgow, 1994-2008
-- License     :  see libraries/base/LICENSE
--
-- Maintainer  :  [email protected]
-- Stability   :  internal
-- Portability :  non-portable
--
-- Type classes for I/O providers.
--
-----------------------------------------------------------------------------

module GHC.IO.Device (
        RawIO(..),
        IODevice(..),
        IODeviceType(..),
        SeekMode(..)
    ) where

import GHC.Base
import GHC.Word
import GHC.Arr
import GHC.Enum
import GHC.Read
import GHC.Show
import GHC.Ptr
import GHC.Num
import GHC.IO
import {-# SOURCE #-} GHC.IO.Exception ( unsupportedOperation )

-- | A low-level I/O provider where the data is bytes in memory.
class RawIO a where
  -- | Read up to the specified number of bytes, returning the number
  -- of bytes actually read.  This function should only block if there
  -- is no data available.  If there is not enough data available,
  -- then the function should just return the available data. A return
  -- value of zero indicates that the end of the data stream (e.g. end
  -- of file) has been reached.
  read                :: a -> Ptr Word8 -> Int -> IO Int

  -- | Read up to the specified number of bytes, returning the number
  -- of bytes actually read, or 'Nothing' if the end of the stream has
  -- been reached.
  readNonBlocking     :: a -> Ptr Word8 -> Int -> IO (Maybe Int)

  -- | Write the specified number of bytes.
  write               :: a -> Ptr Word8 -> Int -> IO ()

  -- | Write up to the specified number of bytes without blocking.  Returns
  -- the actual number of bytes written.
  writeNonBlocking    :: a -> Ptr Word8 -> Int -> IO Int


-- | I/O operations required for implementing a 'Handle'.
class IODevice a where
  -- | @ready dev write [email protected] returns 'True' if the device has data
  -- to read (if @[email protected] is 'False') or space to write new data (if
  -- @[email protected] is 'True').  @[email protected] specifies how long to wait, in
  -- milliseconds.
  --
  ready :: a -> Bool -> Int -> IO Bool

  -- | closes the device.  Further operations on the device should
  -- produce exceptions.
  close :: a -> IO ()

  -- | returns 'True' if the device is a terminal or console.
  isTerminal :: a -> IO Bool
  isTerminal _ = return False

  -- | returns 'True' if the device supports 'seek' operations.
  isSeekable :: a -> IO Bool
  isSeekable _ = return False

  -- | seek to the specified position in the data.
  seek :: a -> SeekMode -> Integer -> IO ()
  seek _ _ _ = ioe_unsupportedOperation

  -- | return the current position in the data.
  tell :: a -> IO Integer
  tell _ = ioe_unsupportedOperation

  -- | return the size of the data.
  getSize :: a -> IO Integer
  getSize _ = ioe_unsupportedOperation

  -- | change the size of the data.
  setSize :: a -> Integer -> IO ()
  setSize _ _ = ioe_unsupportedOperation

  -- | for terminal devices, changes whether characters are echoed on
  -- the device.
  setEcho :: a -> Bool -> IO ()
  setEcho _ _ = ioe_unsupportedOperation

  -- | returns the current echoing status.
  getEcho :: a -> IO Bool
  getEcho _ = ioe_unsupportedOperation

  -- | some devices (e.g. terminals) support a "raw" mode where
  -- characters entered are immediately made available to the program.
  -- If available, this operations enables raw mode.
  setRaw :: a -> Bool -> IO ()
  setRaw _ _ = ioe_unsupportedOperation

  -- | returns the 'IODeviceType' corresponding to this device.
  devType :: a -> IO IODeviceType

  -- | duplicates the device, if possible.  The new device is expected
  -- to share a file pointer with the original device (like Unix @[email protected]).
  dup :: a -> IO a
  dup _ = ioe_unsupportedOperation

  -- | @dup2 source [email protected] replaces the target device with the source
  -- device.  The target device is closed first, if necessary, and then
  -- it is made into a duplicate of the first device (like Unix @[email protected]).
  dup2 :: a -> a -> IO a
  dup2 _ _ = ioe_unsupportedOperation

ioe_unsupportedOperation :: IO a
ioe_unsupportedOperation = throwIO unsupportedOperation

-- | Type of a device that can be used to back a
-- 'GHC.IO.Handle.Handle' (see also 'GHC.IO.Handle.mkFileHandle'). The
-- standard libraries provide creation of 'GHC.IO.Handle.Handle's via
-- Posix file operations with file descriptors (see
-- 'GHC.IO.Handle.FD.mkHandleFromFD') with FD being the underlying
-- 'GHC.IO.Device.IODevice' instance.
--
-- Users may provide custom instances of 'GHC.IO.Device.IODevice'
-- which are expected to conform the following rules:

data IODeviceType
  = Directory -- ^ The standard libraries do not have direct support
              -- for this device type, but a user implementation is
              -- expected to provide a list of file names in
              -- the directory, in any order, separated by @'\0'@
              -- characters, excluding the @"."@ and @".."@ names. See
              -- also 'System.Directory.getDirectoryContents'.  Seek
              -- operations are not supported on directories (other
              -- than to the zero position).
  | Stream    -- ^ A duplex communications channel (results in
              -- creation of a duplex 'GHC.IO.Handle.Handle'). The
              -- standard libraries use this device type when
              -- creating 'GHC.IO.Handle.Handle's for open sockets.
  | RegularFile -- ^ A file that may be read or written, and also
                -- may be seekable.
  | RawDevice -- ^ A "raw" (disk) device which supports block binary
              -- read and write operations and may be seekable only
              -- to positions of certain granularity (block-
              -- aligned).
  deriving (Eq)

-- -----------------------------------------------------------------------------
-- SeekMode type

-- | A mode that determines the effect of 'hSeek' @hdl mode [email protected]
data SeekMode
  = AbsoluteSeek        -- ^ the position of @[email protected] is set to @[email protected]
  | RelativeSeek        -- ^ the position of @[email protected] is set to offset @[email protected]
                        -- from the current position.
  | SeekFromEnd         -- ^ the position of @[email protected] is set to offset @[email protected]
                        -- from the end of the file.
    deriving (Eq, Ord, Ix, Enum, Read, Show)