module System.Win32.MinTTY (isMinTTY, isMinTTYHandle) where
import Graphics.Win32.Misc
import System.Win32.DLL
import System.Win32.File
import System.Win32.Types
import Control.Exception (catch)
import Data.List (isPrefixOf, isInfixOf, isSuffixOf)
import Foreign
import Foreign.C.Types
import System.FilePath (takeFileName)
#include "windows_cconv.h"
isMinTTY :: IO Bool
isMinTTY = do
h <- getStdHandle sTD_ERROR_HANDLE
`catch` \(_ :: IOError) ->
return nullHANDLE
if h == nullHANDLE
then return False
else isMinTTYHandle h
isMinTTYHandle :: HANDLE -> IO Bool
isMinTTYHandle h = do
fileType <- getFileType h
if fileType /= fILE_TYPE_PIPE
then return False
else isMinTTYVista h `catch` \(_ :: IOError) -> isMinTTYCompat h
isMinTTYVista :: HANDLE -> IO Bool
isMinTTYVista h = do
fn <- getFileNameByHandle h
return $ cygwinMSYSCheck fn
`catch` \(_ :: IOError) ->
return False
isMinTTYCompat :: HANDLE -> IO Bool
isMinTTYCompat h = do
fn <- ntQueryObjectNameInformation h
return $ cygwinMSYSCheck fn
`catch` \(_ :: IOError) ->
return False
cygwinMSYSCheck :: String -> Bool
cygwinMSYSCheck fn = ("cygwin-" `isPrefixOf` fn' || "msys-" `isPrefixOf` fn') &&
"-pty" `isInfixOf` fn' &&
"-master" `isSuffixOf` fn'
where
fn' = takeFileName fn
getFileNameByHandle :: HANDLE -> IO String
getFileNameByHandle h = do
let sizeOfDWORD = sizeOf (undefined :: DWORD)
bufSize = sizeOfDWORD + mAX_PATH * sizeOfTCHAR
allocaBytes bufSize $ \buf -> do
getFileInformationByHandleEx h fileNameInfo buf (fromIntegral bufSize)
fni <- peek buf
return $ fniFileName fni
getFileInformationByHandleEx
:: HANDLE -> CInt -> Ptr FILE_NAME_INFO -> DWORD -> IO ()
getFileInformationByHandleEx h cls buf bufSize = do
lib <- getModuleHandle (Just "kernel32.dll")
ptr <- getProcAddress lib "GetFileInformationByHandleEx"
let c_GetFileInformationByHandleEx =
mk_GetFileInformationByHandleEx (castPtrToFunPtr ptr)
failIfFalse_ "getFileInformationByHandleEx"
(c_GetFileInformationByHandleEx h cls buf bufSize)
ntQueryObjectNameInformation :: HANDLE -> IO String
ntQueryObjectNameInformation h = do
let sizeOfONI = sizeOf (undefined :: OBJECT_NAME_INFORMATION)
bufSize = sizeOfONI + mAX_PATH * sizeOfTCHAR
allocaBytes bufSize $ \buf ->
alloca $ \p_len -> do
hwnd <- getModuleHandle (Just "ntdll.exe")
addr <- getProcAddress hwnd "NtQueryObject"
let c_NtQueryObject = mk_NtQueryObject (castPtrToFunPtr addr)
_ <- failIfNeg "NtQueryObject" $ c_NtQueryObject
h objectNameInformation buf (fromIntegral bufSize) p_len
oni <- peek buf
return $ usBuffer $ oniName oni
fileNameInfo :: CInt
fileNameInfo = 2
mAX_PATH :: Num a => a
mAX_PATH = 260
objectNameInformation :: CInt
objectNameInformation = 1
type F_NtQueryObject = HANDLE -> CInt -> Ptr OBJECT_NAME_INFORMATION
-> ULONG -> Ptr ULONG -> IO NTSTATUS
foreign import WINDOWS_CCONV "dynamic"
mk_NtQueryObject :: FunPtr F_NtQueryObject -> F_NtQueryObject
type F_GetFileInformationByHandleEx =
HANDLE -> CInt -> Ptr FILE_NAME_INFO -> DWORD -> IO BOOL
foreign import WINDOWS_CCONV "dynamic"
mk_GetFileInformationByHandleEx
:: FunPtr F_GetFileInformationByHandleEx -> F_GetFileInformationByHandleEx
data FILE_NAME_INFO = FILE_NAME_INFO
{ fniFileNameLength :: DWORD
, fniFileName :: String
} deriving Show
instance Storable FILE_NAME_INFO where
sizeOf _ = (8)
alignment _ = 4
poke buf fni = withTStringLen (fniFileName fni) $ \(str, len) -> do
let len' = (min mAX_PATH len) * sizeOfTCHAR
start = advancePtr (castPtr buf) ((4))
end = advancePtr start len'
((\hsc_ptr -> pokeByteOff hsc_ptr 0)) buf len'
copyArray start (castPtr str :: Ptr Word8) len'
poke (castPtr end) (0 :: TCHAR)
peek buf = do
vfniFileNameLength <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) buf
let len = fromIntegral vfniFileNameLength `div` sizeOfTCHAR
vfniFileName <- peekTStringLen (plusPtr buf ((4)), len)
return $ FILE_NAME_INFO
{ fniFileNameLength = vfniFileNameLength
, fniFileName = vfniFileName
}
type NTSTATUS = Int32
newtype OBJECT_NAME_INFORMATION = OBJECT_NAME_INFORMATION
{ oniName :: UNICODE_STRING
} deriving Show
instance Storable OBJECT_NAME_INFORMATION where
sizeOf _ = (16)
alignment _ = 8
poke buf oni = ((\hsc_ptr -> pokeByteOff hsc_ptr 0)) buf (oniName oni)
peek buf = fmap OBJECT_NAME_INFORMATION $ ((\hsc_ptr -> peekByteOff hsc_ptr 0)) buf
data UNICODE_STRING = UNICODE_STRING
{ usLength :: USHORT
, usMaximumLength :: USHORT
, usBuffer :: String
} deriving Show
instance Storable UNICODE_STRING where
sizeOf _ = (16)
alignment _ = 8
poke buf us = withTStringLen (usBuffer us) $ \(str, len) -> do
let len' = (min mAX_PATH len) * sizeOfTCHAR
start = advancePtr (castPtr buf) ((16))
end = advancePtr start len'
((\hsc_ptr -> pokeByteOff hsc_ptr 0)) buf len'
((\hsc_ptr -> pokeByteOff hsc_ptr 2)) buf (len' + sizeOfTCHAR)
((\hsc_ptr -> pokeByteOff hsc_ptr 8)) buf start
copyArray start (castPtr str :: Ptr Word8) len'
poke (castPtr end) (0 :: TCHAR)
peek buf = do
vusLength <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) buf
vusMaximumLength <- ((\hsc_ptr -> peekByteOff hsc_ptr 2)) buf
vusBufferPtr <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) buf
let len = fromIntegral vusLength `div` sizeOfTCHAR
vusBuffer <- peekTStringLen (vusBufferPtr, len)
return $ UNICODE_STRING
{ usLength = vusLength
, usMaximumLength = vusMaximumLength
, usBuffer = vusBuffer
}
sizeOfTCHAR :: Int
sizeOfTCHAR = sizeOf (undefined :: TCHAR)