{-# LINE 1 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# OPTIONS_HADDOCK hide #-}
module GHC.Internal.IO.Windows.Handle
(
NativeHandle(),
ConsoleHandle(),
IoHandle(),
HANDLE,
Io(),
convertHandle,
toHANDLE,
fromHANDLE,
handleToMode,
isAsynchronous,
optimizeFileAccess,
stdin,
stdout,
stderr,
openFile,
openFileAsTemp,
release
) where
#include "windows_cconv.h"
import GHC.Internal.Data.Bits ((.|.), (.&.), shiftL)
import GHC.Internal.Data.Functor ((<$>))
import GHC.Internal.Data.Typeable
import GHC.Internal.Base
import GHC.Internal.Enum
import GHC.Internal.Num
import GHC.Internal.Real
import GHC.Internal.List
import GHC.Internal.Word (Word8, Word16, Word64)
import GHC.Internal.IO hiding (mask)
import GHC.Internal.IO.Buffer
import GHC.Internal.IO.BufferedIO
import qualified GHC.Internal.IO.Device as GHC.IO.Device
import GHC.Internal.IO.Device (SeekMode(..), IODeviceType(..), IODevice(), devType, setSize)
import GHC.Internal.IO.Exception
import GHC.Internal.IO.IOMode
import GHC.Internal.IO.Windows.Encoding (withGhcInternalToUTF16, withUTF16ToGhcInternal)
import GHC.Internal.IO.Windows.Paths (getDevicePath)
import GHC.Internal.IO.Handle.Internals (debugIO)
import GHC.Internal.IORef
import GHC.Internal.Event.Windows (LPOVERLAPPED, withOverlappedEx, IOResult(..))
import GHC.Internal.Foreign.Ptr
import GHC.Internal.Foreign.C.Types
import GHC.Internal.Foreign.C.Error
import GHC.Internal.Foreign.C.String
import GHC.Internal.Foreign.Marshal.Array (pokeArray)
import GHC.Internal.Foreign.Marshal.Alloc (alloca, allocaBytes)
import GHC.Internal.Foreign.Marshal.Utils (with, fromBool)
import GHC.Internal.Foreign.Storable (Storable (..))
import qualified GHC.Internal.Event.Windows as Mgr
import GHC.Internal.Windows (LPVOID, LPDWORD, DWORD, HANDLE, BOOL, LPCTSTR, ULONG, WORD,
UCHAR, failIf, iNVALID_HANDLE_VALUE, failWith,
failIfFalse_, getLastError)
import GHC.Internal.Text.Show
data NativeHandle
data ConsoleHandle
data IoHandle a where
NativeHandle :: { getNativeHandle :: HANDLE
, isAsynchronous :: Bool } -> IoHandle NativeHandle
ConsoleHandle :: { getConsoleHandle :: HANDLE
, cookedHandle :: IORef Bool
} -> IoHandle ConsoleHandle
type Io a = IoHandle a
convertHandle :: Io ConsoleHandle -> Bool -> Io NativeHandle
convertHandle io async
= let !hwnd = getConsoleHandle io
in NativeHandle hwnd async
instance Show (Io NativeHandle) where
show = show . toHANDLE
instance Show (Io ConsoleHandle) where
show = show . getConsoleHandle
instance GHC.IO.Device.RawIO (Io NativeHandle) where
read = hwndRead
readNonBlocking = hwndReadNonBlocking
write = hwndWrite
writeNonBlocking = hwndWriteNonBlocking
instance GHC.IO.Device.RawIO (Io ConsoleHandle) where
read = consoleRead True
readNonBlocking = consoleReadNonBlocking
write = consoleWrite
writeNonBlocking = consoleWriteNonBlocking
class (GHC.IO.Device.RawIO a, IODevice a, BufferedIO a, Typeable a)
=> RawHandle a where
toHANDLE :: a -> HANDLE
fromHANDLE :: HANDLE -> a
isLockable :: a -> Bool
setCooked :: a -> Bool -> IO a
isCooked :: a -> IO Bool
instance RawHandle (Io NativeHandle) where
toHANDLE = getNativeHandle
fromHANDLE = flip NativeHandle True
isLockable _ = True
setCooked = const . return
isCooked _ = return False
instance RawHandle (Io ConsoleHandle) where
toHANDLE = getConsoleHandle
fromHANDLE h = unsafePerformIO $ ConsoleHandle h <$> newIORef False
isLockable _ = False
setCooked h val =
do writeIORef (cookedHandle h) val
return h
isCooked h = readIORef (cookedHandle h)
instance GHC.IO.Device.IODevice (Io NativeHandle) where
ready = handle_ready
close = handle_close
isTerminal = handle_is_console
isSeekable = handle_is_seekable
seek = handle_seek
tell = handle_tell
getSize = handle_get_size
setSize = handle_set_size
setEcho = handle_set_echo
getEcho = handle_get_echo
setRaw = handle_set_buffering
devType = handle_dev_type
dup = handle_duplicate
instance GHC.IO.Device.IODevice (Io ConsoleHandle) where
ready = handle_ready
close = handle_close . flip convertHandle False
isTerminal = handle_is_console
isSeekable = handle_is_seekable
seek = handle_console_seek
tell = handle_console_tell
getSize = handle_get_console_size
setSize = handle_set_console_size
setEcho = handle_set_echo
getEcho = handle_get_echo
setRaw = console_set_buffering
devType = handle_dev_type
dup = handle_duplicate
dEFAULT_BUFFER_SIZE :: Int
dEFAULT_BUFFER_SIZE = 8192
instance BufferedIO (Io NativeHandle) where
newBuffer _dev state = newByteBuffer dEFAULT_BUFFER_SIZE state
fillReadBuffer = readBuf'
fillReadBuffer0 = readBufNonBlocking
flushWriteBuffer = writeBuf'
flushWriteBuffer0 = writeBufNonBlocking
instance BufferedIO (Io ConsoleHandle) where
newBuffer _dev state = newByteBuffer dEFAULT_BUFFER_SIZE state
fillReadBuffer = readBuf'
fillReadBuffer0 = readBufNonBlocking
flushWriteBuffer = writeBuf'
flushWriteBuffer0 = writeBufNonBlocking
readBuf' :: RawHandle a => a -> Buffer Word8 -> IO (Int, Buffer Word8)
readBuf' hnd buf = do
debugIO ("readBuf handle=" ++ show (toHANDLE hnd) ++ " " ++
summaryBuffer buf ++ "\n")
(r,buf') <- readBuf hnd buf
debugIO ("after: " ++ summaryBuffer buf' ++ "\n")
return (r,buf')
writeBuf' :: RawHandle a => a -> Buffer Word8 -> IO (Buffer Word8)
writeBuf' hnd buf = do
debugIO ("writeBuf handle=" ++ show (toHANDLE hnd) ++ " " ++
summaryBuffer buf ++ "\n")
writeBuf hnd buf
type StdHandleId = DWORD
sTD_INPUT_HANDLE :: StdHandleId
sTD_INPUT_HANDLE = 4294967286
sTD_OUTPUT_HANDLE :: StdHandleId
sTD_OUTPUT_HANDLE = 4294967285
sTD_ERROR_HANDLE :: StdHandleId
sTD_ERROR_HANDLE = 4294967284
{-# LINE 261 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
getStdHandle :: StdHandleId -> IO HANDLE
getStdHandle hid =
failIf (== iNVALID_HANDLE_VALUE) "GetStdHandle" $ c_GetStdHandle hid
stdin, stdout, stderr :: Io ConsoleHandle
stdin = unsafePerformIO $ mkConsoleHandle =<< getStdHandle sTD_INPUT_HANDLE
stdout = unsafePerformIO $ mkConsoleHandle =<< getStdHandle sTD_OUTPUT_HANDLE
stderr = unsafePerformIO $ mkConsoleHandle =<< getStdHandle sTD_ERROR_HANDLE
mkConsoleHandle :: HANDLE -> IO (Io ConsoleHandle)
mkConsoleHandle hwnd
= do ref <- newIORef False
return $ ConsoleHandle hwnd ref
acCtrlD :: Int
acCtrlD = 0x04
acCtrlZ :: Int
acCtrlZ = 0x1A
acEotMask :: ULONG
acEotMask = (1 `shiftL` acCtrlD) .|. (1 `shiftL` acCtrlZ)
type PCONSOLE_READCONSOLE_CONTROL = Ptr CONSOLE_READCONSOLE_CONTROL
data CONSOLE_READCONSOLE_CONTROL = CONSOLE_READCONSOLE_CONTROL
{ crcNLength :: ULONG
, crcNInitialChars :: ULONG
, crcDwCtrlWakeupMask :: ULONG
, crcDwControlKeyState :: ULONG
} deriving Show
instance Storable CONSOLE_READCONSOLE_CONTROL where
sizeOf = const (16)
{-# LINE 303 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
alignment = const 4
{-# LINE 304 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
poke buf crc = do
((\hsc_ptr -> pokeByteOff hsc_ptr 0)) buf
{-# LINE 306 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
(crcNLength crc)
((\hsc_ptr -> pokeByteOff hsc_ptr 4)) buf
{-# LINE 308 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
(crcNInitialChars crc)
((\hsc_ptr -> pokeByteOff hsc_ptr 8)) buf
{-# LINE 310 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
(crcDwCtrlWakeupMask crc)
((\hsc_ptr -> pokeByteOff hsc_ptr 12)) buf
{-# LINE 312 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
(crcDwControlKeyState crc)
peek buf = do
vNLength <-
((\hsc_ptr -> peekByteOff hsc_ptr 0)) buf
{-# LINE 317 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
vNInitialChars <-
((\hsc_ptr -> peekByteOff hsc_ptr 4)) buf
{-# LINE 319 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
vDwCtrlWakeupMask <-
((\hsc_ptr -> peekByteOff hsc_ptr 8)) buf
{-# LINE 321 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
vDwControlKeyState <-
((\hsc_ptr -> peekByteOff hsc_ptr 12)) buf
{-# LINE 323 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
return $ CONSOLE_READCONSOLE_CONTROL {
crcNLength = vNLength,
crcNInitialChars = vNInitialChars,
crcDwCtrlWakeupMask = vDwCtrlWakeupMask,
crcDwControlKeyState = vDwControlKeyState
}
eotControl :: CONSOLE_READCONSOLE_CONTROL
eotControl =
CONSOLE_READCONSOLE_CONTROL
{ crcNLength = fromIntegral $
sizeOf (undefined :: CONSOLE_READCONSOLE_CONTROL)
, crcNInitialChars = 0
, crcDwCtrlWakeupMask = acEotMask
, crcDwControlKeyState = 0
}
type PINPUT_RECORD = Ptr ()
foreign import WINDOWS_CCONV safe "windows.h CreateFileW"
c_CreateFile :: LPCTSTR -> DWORD -> DWORD -> LPSECURITY_ATTRIBUTES
-> DWORD -> DWORD -> HANDLE
-> IO HANDLE
foreign import WINDOWS_CCONV safe "windows.h SetFileCompletionNotificationModes"
c_SetFileCompletionNotificationModes :: HANDLE -> UCHAR -> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h ReadFile"
c_ReadFile :: HANDLE -> LPVOID -> DWORD -> LPDWORD -> LPOVERLAPPED
-> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h WriteFile"
c_WriteFile :: HANDLE -> LPVOID -> DWORD -> LPDWORD -> LPOVERLAPPED
-> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h GetStdHandle"
c_GetStdHandle :: StdHandleId -> IO HANDLE
foreign import ccall safe "__handle_ready"
c_handle_ready :: HANDLE -> BOOL -> CInt -> IO CInt
foreign import ccall safe "__is_console"
c_is_console :: HANDLE -> IO BOOL
foreign import ccall safe "__set_console_buffering"
c_set_console_buffering :: HANDLE -> BOOL -> IO BOOL
foreign import ccall safe "__set_console_echo"
c_set_console_echo :: HANDLE -> BOOL -> IO BOOL
foreign import ccall safe "__get_console_echo"
c_get_console_echo :: HANDLE -> IO BOOL
foreign import ccall safe "__close_handle"
c_close_handle :: HANDLE -> IO Bool
foreign import ccall safe "__handle_type"
c_handle_type :: HANDLE -> IO Int
foreign import ccall safe "__set_file_pointer"
c_set_file_pointer :: HANDLE -> CLong -> DWORD -> Ptr CLong -> IO BOOL
foreign import ccall safe "__get_file_pointer"
c_get_file_pointer :: HANDLE -> IO CLong
foreign import ccall safe "__get_file_size"
c_get_file_size :: HANDLE -> IO CLong
foreign import ccall safe "__set_file_size"
c_set_file_size :: HANDLE -> CLong -> IO BOOL
foreign import ccall safe "__duplicate_handle"
c_duplicate_handle :: HANDLE -> Ptr HANDLE -> IO BOOL
foreign import ccall safe "__set_console_pointer"
c_set_console_pointer :: HANDLE -> CLong -> DWORD -> Ptr CLong -> IO BOOL
foreign import ccall safe "__get_console_pointer"
c_get_console_pointer :: HANDLE -> IO CLong
foreign import ccall safe "__get_console_buffer_size"
c_get_console_buffer_size :: HANDLE -> IO CLong
foreign import ccall safe "__set_console_buffer_size"
c_set_console_buffer_size :: HANDLE -> CLong -> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h ReadConsoleW"
c_read_console :: HANDLE -> Ptr Word16 -> DWORD -> Ptr DWORD
-> PCONSOLE_READCONSOLE_CONTROL -> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h WriteConsoleW"
c_write_console :: HANDLE -> Ptr Word16 -> DWORD -> Ptr DWORD -> Ptr ()
-> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h ReadConsoleInputW"
c_read_console_input :: HANDLE -> PINPUT_RECORD -> DWORD -> LPDWORD -> IO BOOL
foreign import WINDOWS_CCONV safe "windows.h GetNumberOfConsoleInputEvents"
c_get_num_console_inputs :: HANDLE -> LPDWORD -> IO BOOL
type LPSECURITY_ATTRIBUTES = LPVOID
hwndRead :: Io NativeHandle -> Ptr Word8 -> Word64 -> Int -> IO Int
hwndRead hwnd ptr offset bytes = do
mngr <- Mgr.getSystemManager
fmap fromIntegral $ Mgr.withException "hwndRead" $
withOverlappedEx mngr "hwndRead" (toHANDLE hwnd) (isAsynchronous hwnd)
offset (startCB ptr) completionCB
where
startCB outBuf lpOverlapped = do
debugIO ":: hwndRead"
ret <- c_ReadFile (toHANDLE hwnd) (castPtr outBuf)
(fromIntegral bytes) nullPtr lpOverlapped
return $ Mgr.CbNone ret
completionCB err dwBytes
| err == 0 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 453 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == 38 = Mgr.ioSuccess 0
{-# LINE 454 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == -1073741807 = Mgr.ioSuccess 0
{-# LINE 455 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == 109 = Mgr.ioSuccess 0
{-# LINE 456 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == -1073741493 = Mgr.ioSuccess 0
{-# LINE 457 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == 259 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 458 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == 234 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 459 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| otherwise = Mgr.ioFailed err
hwndReadNonBlocking :: Io NativeHandle -> Ptr Word8 -> Word64 -> Int
-> IO (Maybe Int)
hwndReadNonBlocking hwnd ptr offset bytes
= do mngr <- Mgr.getSystemManager
val <- withOverlappedEx mngr "hwndReadNonBlocking" (toHANDLE hwnd)
(isAsynchronous hwnd) offset (startCB ptr)
completionCB
return $ ioValue val
where
startCB inputBuf lpOverlapped = do
debugIO ":: hwndReadNonBlocking"
ret <- c_ReadFile (toHANDLE hwnd) (castPtr inputBuf)
(fromIntegral bytes) nullPtr lpOverlapped
return $ Mgr.CbNone ret
completionCB err dwBytes
| err == 0 = Mgr.ioSuccess $ Just $! fromIntegral dwBytes
{-# LINE 483 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == 38 = Mgr.ioSuccess Nothing
{-# LINE 484 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == -1073741807 = Mgr.ioSuccess Nothing
{-# LINE 485 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == 109 = Mgr.ioSuccess Nothing
{-# LINE 486 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == -1073741493 = Mgr.ioSuccess Nothing
{-# LINE 487 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == 259 = Mgr.ioSuccess Nothing
{-# LINE 488 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == 234 = Mgr.ioSuccess $ Just $! fromIntegral dwBytes
{-# LINE 489 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| otherwise = Mgr.ioFailedAny err
hwndWrite :: Io NativeHandle -> Ptr Word8 -> Word64 -> Int -> IO ()
hwndWrite hwnd ptr offset bytes
= do mngr <- Mgr.getSystemManager
_ <- Mgr.withException "hwndWrite" $
withOverlappedEx mngr "hwndWrite" (toHANDLE hwnd)
(isAsynchronous hwnd) offset (startCB ptr)
completionCB
return ()
where
startCB outBuf lpOverlapped = do
debugIO ":: hwndWrite"
ret <- c_WriteFile (toHANDLE hwnd) (castPtr outBuf)
(fromIntegral bytes) nullPtr lpOverlapped
return $ Mgr.CbNone ret
completionCB err dwBytes
| err == 0 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 509 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == 38 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 510 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| otherwise = Mgr.ioFailed err
hwndWriteNonBlocking :: Io NativeHandle -> Ptr Word8 -> Word64 -> Int -> IO Int
hwndWriteNonBlocking hwnd ptr offset bytes
= do mngr <- Mgr.getSystemManager
val <- withOverlappedEx mngr "hwndReadNonBlocking" (toHANDLE hwnd)
(isAsynchronous hwnd) offset (startCB ptr)
completionCB
return $ fromIntegral $ ioValue val
where
startCB :: Ptr a -> LPOVERLAPPED -> IO (Mgr.CbResult a1)
startCB outBuf lpOverlapped = do
debugIO ":: hwndWriteNonBlocking"
ret <- c_WriteFile (toHANDLE hwnd) (castPtr outBuf)
(fromIntegral bytes) nullPtr lpOverlapped
return $ Mgr.CbNone ret
completionCB err dwBytes
| err == 0 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 530 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| err == 38 = Mgr.ioSuccess $ fromIntegral dwBytes
{-# LINE 531 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| otherwise = Mgr.ioFailed err
consoleWrite :: Io ConsoleHandle -> Ptr Word8 -> Word64 -> Int -> IO ()
consoleWrite hwnd ptr _offset bytes
= alloca $ \res ->
do failIfFalse_ "GHC.Internal.IO.Handle.consoleWrite" $ do
debugIO ":: consoleWrite"
withGhcInternalToUTF16 ptr bytes $ \(w_ptr, w_len) -> do
success <- c_write_console (toHANDLE hwnd) w_ptr
(fromIntegral w_len) res nullPtr
if not success
then return False
else do val <- fromIntegral <$> peek res
return $ val == w_len
consoleWriteNonBlocking :: Io ConsoleHandle -> Ptr Word8 -> Word64 -> Int -> IO Int
consoleWriteNonBlocking hwnd ptr _offset bytes
= alloca $ \res ->
do failIfFalse_ "GHC.Internal.IO.Handle.consoleWriteNonBlocking" $ do
debugIO ":: consoleWriteNonBlocking"
withGhcInternalToUTF16 ptr bytes $ \(w_ptr, w_len) -> do
c_write_console (toHANDLE hwnd) w_ptr (fromIntegral w_len)
res nullPtr
val <- fromIntegral <$> peek res
return val
consoleRead :: Bool -> Io ConsoleHandle -> Ptr Word8 -> Word64 -> Int -> IO Int
consoleRead blocking hwnd ptr _offset bytes
= alloca $ \res -> do
cooked <- isCooked hwnd
case cooked || not blocking of
False -> withUTF16ToGhcInternal ptr bytes $ \reqBytes w_ptr -> do
debugIO "consoleRead :: un-cooked I/O read."
res_code <- with eotControl $ \p_eotControl ->
c_read_console (toHANDLE hwnd) w_ptr (fromIntegral reqBytes) res
p_eotControl
err <- getLastError
when (not res_code) $
case () of
_ | err == 1 -> return ()
{-# LINE 614 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| otherwise -> failWith "GHC.Internal.IO.Handle.consoleRead" err
b_read <- fromIntegral <$> peek res
if b_read /= 1
then return b_read
else do w_first <- peekElemOff w_ptr 0
case () of
_ | w_first == fromIntegral acCtrlD -> return 0
| w_first == fromIntegral acCtrlZ -> return 0
| otherwise -> return b_read
True -> do
debugIO "consoleRead :: cooked I/O read."
let entries = fromIntegral $ bytes `div` ((20))
{-# LINE 633 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
allocaBytes entries $ \p_inputs ->
maybeReadEvent p_inputs entries res ptr
where maybeReadEvent p_inputs entries res w_ptr =
case (not blocking) of
True -> do
avail <- with (0 :: DWORD) $ \num_events_ptr -> do
failIfFalse_ "GHC.Internal.IO.Handle.consoleRead [non-blocking]" $
c_get_num_console_inputs (toHANDLE hwnd) num_events_ptr
peek num_events_ptr
debugIO $ "consoleRead [avail] :: " ++ show avail
if avail > 0
then readEvent p_inputs entries res w_ptr
else return 0
False -> readEvent p_inputs entries res w_ptr
readEvent p_inputs entries res w_ptr = do
failIfFalse_ "GHC.Internal.IO.Handle.consoleRead" $
c_read_console_input (toHANDLE hwnd) p_inputs
(fromIntegral entries) res
b_read <- fromIntegral <$> peek res
read <- cobble b_read w_ptr p_inputs
debugIO $ "readEvent: =" ++ show read
if read > 0
then return $ fromIntegral read
else maybeReadEvent p_inputs entries res w_ptr
cobble :: Int -> Ptr Word8 -> PINPUT_RECORD -> IO Int
cobble 0 _ _ = do debugIO "cobble: done."
return 0
cobble n w_ptr p_inputs =
do eventType <- peekByteOff p_inputs 0 :: IO WORD
debugIO $ "cobble: Length=" ++ show n
debugIO $ "cobble: Type=" ++ show eventType
let ni_offset = (20)
{-# LINE 678 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
let event = 4
{-# LINE 679 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
let char_offset = event + 10
{-# LINE 680 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
let btnDown_offset = event + 0
{-# LINE 681 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
let repeat_offset = event + 4
{-# LINE 682 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
let n' = n - 1
let p_inputs' = p_inputs `plusPtr` ni_offset
btnDown <- peekByteOff p_inputs btnDown_offset
repeated <- fromIntegral <$> (peekByteOff p_inputs repeat_offset :: IO WORD)
debugIO $ "cobble: BtnDown=" ++ show btnDown
if eventType == 1 && btnDown
{-# LINE 689 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
then do debugIO $ "cobble: read-char."
char <- peekByteOff p_inputs char_offset
let w_ptr' = w_ptr `plusPtr` 1
debugIO $ "cobble: offset - " ++ show char_offset
debugIO $ "cobble: show > " ++ show char
debugIO $ "cobble: repeat: " ++ show repeated
pokeArray w_ptr $ replicate repeated char
(+repeated) <$> cobble n' w_ptr' p_inputs'
else do debugIO $ "cobble: skip event."
cobble n' w_ptr p_inputs'
consoleReadNonBlocking :: Io ConsoleHandle -> Ptr Word8 -> Word64 -> Int
-> IO (Maybe Int)
consoleReadNonBlocking hwnd ptr offset bytes
= Just <$> consoleRead False hwnd ptr offset bytes
handle_ready :: RawHandle a => a -> Bool -> Int -> IO Bool
handle_ready hwnd write msecs = do
r <- throwErrnoIfMinus1Retry "GHC.Internal.IO.Windows.Handle.handle_ready" $
c_handle_ready (toHANDLE hwnd) write (fromIntegral msecs)
return (toEnum (fromIntegral r))
handle_is_console :: RawHandle a => a -> IO Bool
handle_is_console = c_is_console . toHANDLE
handle_close :: RawHandle a => a -> IO ()
handle_close h = do release h
failIfFalse_ "handle_close" $ c_close_handle (toHANDLE h)
handle_dev_type :: RawHandle a => a -> IO IODeviceType
handle_dev_type hwnd = do _type <- c_handle_type $ toHANDLE hwnd
return $ case _type of
_ | _type == 3 -> Stream
| _type == 5 -> RawDevice
| otherwise -> RegularFile
handle_is_seekable :: RawHandle a => a -> IO Bool
handle_is_seekable hwnd = do
t <- handle_dev_type hwnd
return (t == RegularFile || t == RawDevice)
handle_seek :: RawHandle a => a -> SeekMode -> Integer -> IO Integer
handle_seek hwnd mode off =
with 0 $ \off_rel -> do
failIfFalse_ "GHC.Internal.IO.Handle.handle_seek" $
c_set_file_pointer (toHANDLE hwnd) (fromIntegral off) seektype off_rel
fromIntegral <$> peek off_rel
where
seektype :: DWORD
seektype = case mode of
AbsoluteSeek -> 0
{-# LINE 754 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
RelativeSeek -> 1
{-# LINE 755 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
SeekFromEnd -> 2
{-# LINE 756 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
handle_tell :: RawHandle a => a -> IO Integer
handle_tell hwnd =
fromIntegral `fmap`
(throwErrnoIfMinus1Retry "GHC.Internal.IO.Handle.handle_tell" $
c_get_file_pointer (toHANDLE hwnd))
handle_set_size :: RawHandle a => a -> Integer -> IO ()
handle_set_size hwnd size =
failIfFalse_ "GHC.Internal.IO.Handle.handle_set_size" $
c_set_file_size (toHANDLE hwnd) (fromIntegral size)
handle_get_size :: RawHandle a => a -> IO Integer
handle_get_size hwnd =
fromIntegral `fmap`
(throwErrnoIfMinus1Retry "GHC.Internal.IO.Handle.handle_set_size" $
c_get_file_size (toHANDLE hwnd))
handle_set_echo :: RawHandle a => a -> Bool -> IO ()
handle_set_echo hwnd value =
failIfFalse_ "GHC.Internal.IO.Handle.handle_set_echo" $
c_set_console_echo (toHANDLE hwnd) value
handle_get_echo :: RawHandle a => a -> IO Bool
handle_get_echo = c_get_console_echo . toHANDLE
handle_duplicate :: RawHandle a => a -> IO a
handle_duplicate hwnd = alloca $ \ptr -> do
failIfFalse_ "GHC.Internal.IO.Handle.handle_duplicate" $
c_duplicate_handle (toHANDLE hwnd) ptr
fromHANDLE <$> peek ptr
console_set_buffering :: Io ConsoleHandle -> Bool -> IO ()
console_set_buffering hwnd value = setCooked hwnd value >> return ()
handle_set_buffering :: RawHandle a => a -> Bool -> IO ()
handle_set_buffering hwnd value =
failIfFalse_ "GHC.Internal.IO.Handle.handle_set_buffering" $
c_set_console_buffering (toHANDLE hwnd) value
handle_console_seek :: RawHandle a => a -> SeekMode -> Integer -> IO Integer
handle_console_seek hwnd mode off =
with 0 $ \loc_ptr -> do
failIfFalse_ "GHC.Internal.IO.Handle.handle_console_seek" $
c_set_console_pointer (toHANDLE hwnd) (fromIntegral off) seektype loc_ptr
fromIntegral <$> peek loc_ptr
where
seektype :: DWORD
seektype = case mode of
AbsoluteSeek -> 0
{-# LINE 806 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
RelativeSeek -> 1
{-# LINE 807 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
SeekFromEnd -> 2
{-# LINE 808 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
handle_console_tell :: RawHandle a => a -> IO Integer
handle_console_tell hwnd =
fromIntegral `fmap`
(throwErrnoIfMinus1Retry "GHC.Internal.IO.Handle.handle_console_tell" $
c_get_console_pointer (toHANDLE hwnd))
handle_set_console_size :: RawHandle a => a -> Integer -> IO ()
handle_set_console_size hwnd size =
failIfFalse_ "GHC.Internal.IO.Handle.handle_set_console_size" $
c_set_console_buffer_size (toHANDLE hwnd) (fromIntegral size)
handle_get_console_size :: RawHandle a => a -> IO Integer
handle_get_console_size hwnd =
fromIntegral `fmap`
(throwErrnoIfMinus1Retry "GHC.Internal.IO.Handle.handle_get_console_size" $
c_get_console_buffer_size (toHANDLE hwnd))
data TempFileOptions = NoTemp | TempNonExcl | TempExcl deriving Eq
openFile
:: FilePath
-> IOMode
-> Bool
-> IO (Io NativeHandle, IODeviceType)
openFile filepath iomode non_blocking = openFile' filepath iomode non_blocking NoTemp
openFileAsTemp
:: FilePath
-> Bool
-> Bool
-> IO (Io NativeHandle, IODeviceType)
openFileAsTemp filepath non_blocking excl
= openFile' filepath ReadWriteMode non_blocking (if excl then TempExcl else TempNonExcl)
openFile'
:: FilePath
-> IOMode
-> Bool
-> TempFileOptions
-> IO (Io NativeHandle, IODeviceType)
openFile' filepath iomode non_blocking tmp_opts =
do devicepath <- getDevicePath filepath
h <- createFile devicepath
Mgr.associateHandle' h
let hwnd = fromHANDLE h
_type <- devType hwnd
let write_lock = iomode /= ReadMode
case _type of
RegularFile -> do
optimizeFileAccess h
(unique_dev, unique_ino) <- getUniqueFileInfo hwnd
r <- lockFile (fromIntegral $ ptrToWordPtr h) unique_dev unique_ino
(fromBool write_lock)
when (r == -1) $
ioException (IOError Nothing ResourceBusy "openFile"
"file is locked" Nothing Nothing)
_ -> return ()
when (iomode == WriteMode && _type == RegularFile) $
setSize hwnd 0
return (hwnd, _type)
where
flagIf p f2
| p = f2
| otherwise = 0
file_share_mode = 1
{-# LINE 902 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
.|. 4
{-# LINE 903 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
.|. (flagIf (tmp_opts == NoTemp)
2)
{-# LINE 906 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
file_access_mode =
case iomode of
ReadMode -> 2147483648
{-# LINE 910 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
WriteMode -> 1073741824
{-# LINE 911 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
ReadWriteMode -> 2147483648
{-# LINE 912 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
.|. 1073741824
{-# LINE 913 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
AppendMode -> 1073741824
{-# LINE 914 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
.|. 4
{-# LINE 915 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
file_open_mode =
case iomode of
ReadMode -> 3
{-# LINE 919 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
WriteMode -> 4
{-# LINE 920 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
ReadWriteMode ->
case tmp_opts of
NoTemp -> 4
{-# LINE 923 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
TempNonExcl -> 2
{-# LINE 924 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
TempExcl -> 1
{-# LINE 925 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
AppendMode -> 4
{-# LINE 926 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
file_create_flags =
if non_blocking
then 1073741824
{-# LINE 939 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
.|. 134217728
{-# LINE 948 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
.|. (flagIf (tmp_opts /= NoTemp)
256 )
{-# LINE 951 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
else 128
{-# LINE 952 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
.|. (flagIf (tmp_opts /= NoTemp)
256 )
{-# LINE 955 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
createFile devicepath =
withCWString devicepath $ \fp ->
failIf (== iNVALID_HANDLE_VALUE) "CreateFile" $
c_CreateFile fp file_access_mode
file_share_mode
nullPtr
file_open_mode
file_create_flags
nullPtr
optimizeFileAccess :: HANDLE -> IO ()
optimizeFileAccess handle =
failIfFalse_ "SetFileCompletionNotificationModes" $
c_SetFileCompletionNotificationModes handle
( 1
{-# LINE 973 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
.|. 2)
{-# LINE 974 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
handleToMode :: HANDLE -> IO IOMode
handleToMode hwnd = do
mask <- c_get_handle_access_mask hwnd
let hasFlag flag = (flag .&. mask) == flag
case () of
() | hasFlag (4) -> return AppendMode
{-# LINE 982 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| hasFlag (1073741824 .|. 2147483648) -> return ReadWriteMode
{-# LINE 983 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| hasFlag (2147483648) -> return ReadMode
{-# LINE 984 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| hasFlag (1073741824) -> return WriteMode
{-# LINE 985 "libraries\\ghc-internal\\src\\GHC\\Internal\\IO\\Windows\\Handle.hsc" #-}
| otherwise -> error "unknown access mask in handleToMode."
foreign import ccall unsafe "__get_handle_access_mask"
c_get_handle_access_mask :: HANDLE -> IO DWORD
release :: RawHandle a => a -> IO ()
release h = if isLockable h
then do let handle = fromIntegral $ ptrToWordPtr $ toHANDLE h
_ <- unlockFile handle
return ()
else return ()
foreign import ccall unsafe "lockFile"
lockFile :: CUIntPtr -> Word64 -> Word64 -> CInt -> IO CInt
foreign import ccall unsafe "unlockFile"
unlockFile :: CUIntPtr -> IO CInt
foreign import ccall unsafe "get_unique_file_info_hwnd"
c_getUniqueFileInfo :: HANDLE -> Ptr Word64 -> Ptr Word64 -> IO ()
getUniqueFileInfo :: RawHandle a => a -> IO (Word64, Word64)
getUniqueFileInfo handle = do
with 0 $ \devptr -> do
with 0 $ \inoptr -> do
c_getUniqueFileInfo (toHANDLE handle) devptr inoptr
liftM2 (,) (peek devptr) (peek inoptr)