mintty (empty) → 0.1
raw patch · 9 files changed
+463/−0 lines, 9 filesdep +Win32dep +basedep +filepathsetup-changed
Dependencies added: Win32, base, filepath
Files
- CHANGELOG.md +2/−0
- LICENSE +30/−0
- README.md +21/−0
- Setup.hs +2/−0
- include/windows_cconv.h +12/−0
- include/winternl_compat.h +39/−0
- mintty.cabal +62/−0
- src/System/Console/MinTTY.hs +55/−0
- src/System/Console/MinTTY/Win32.hsc +240/−0
+ CHANGELOG.md view
@@ -0,0 +1,2 @@+## 0.1 [2017-01-30]+* Initial release
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2017, Ryan Scott++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Ryan Scott nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,21 @@+# `mintty`+[][Hackage: mintty]+[](http://packdeps.haskellers.com/reverse/mintty)+[][Haskell.org]+[][tl;dr Legal: BSD3]+[](https://travis-ci.org/RyanGlScott/mintty)+[](https://ci.appveyor.com/project/RyanGlScott/mintty)++[Hackage: mintty]:+ http://hackage.haskell.org/package/mintty+ "mintty package on Hackage"+[Haskell.org]:+ http://www.haskell.org+ "The Haskell Programming Language"+[tl;dr Legal: BSD3]:+ https://tldrlegal.com/license/bsd-3-clause-license-%28revised%29+ "BSD 3-Clause License (Revised)"++MinTTY is a Windows-specific terminal emulator for the widely used Cygwin and MSYS projects, which provide Unix-like environments for Windows. MinTTY consoles behave differently from native Windows consoles (such as `cmd.exe` or PowerShell) in many ways, and in some cases, these differences make it necessary to treat MinTTY consoles differently in code.++The `mintty` library provides a simple way to detect if your code in running in a MinTTY console on Windows. It exports `isMinTTY`, which does the right thing 90% of the time (by checking if standard error is attached to MinTTY), and it also exports `isMinTTYHandle` for the other 10% of the time (when you want to check is some arbitrary handle is attached to MinTTY). As you might expect, both of these functions will simply return `False` on any non-Windows operating system.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ include/windows_cconv.h view
@@ -0,0 +1,12 @@+#ifndef WINDOWS_CCONV_H+#define WINDOWS_CCONV_H++#if defined(i386_HOST_ARCH)+# define WINDOWS_CCONV stdcall+#elif defined(x86_64_HOST_ARCH)+# define WINDOWS_CCONV ccall+#else+# error Unknown mingw32 arch+#endif++#endif
+ include/winternl_compat.h view
@@ -0,0 +1,39 @@+#ifndef WINTERNL_COMPAT_H+#define WINTERNL_COMPAT_H++/*+ * winternl.h is not included in MinGW, which was shipped with the 32-bit+ * Windows version of GHC prior to the 7.10.3 release.+ */+#if defined(x86_64_HOST_ARCH) || \+ __GLASGOW_HASKELL__ >= 711 || \+ (__GLASGOW_HASKELL__ == 710 && \+ defined(__GLASGOW_HASKELL_PATCHLEVEL1__) && \+ __GLASGOW_HASKELL_PATCHLEVEL1__ >= 2)+# include <winternl.h>+#else+// Some declarations from winternl.h that we need in Win32+# include <windows.h>++typedef enum _OBJECT_INFORMATION_CLASS {+ ObjectBasicInformation,+ ObjectNameInformation,+ ObjectTypeInformation,+ ObjectAllInformation,+ ObjectDataInformation+} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;++typedef LONG NTSTATUS, *PNTSTATUS;++typedef struct _UNICODE_STRING {+ USHORT Length;+ USHORT MaximumLength;+ PWSTR Buffer;+} UNICODE_STRING, *PUNICODE_STRING;++typedef struct _OBJECT_NAME_INFORMATION {+ UNICODE_STRING Name;+} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;+#endif++#endif /* WINTERNL_COMPAT_H */
+ mintty.cabal view
@@ -0,0 +1,62 @@+name: mintty+version: 0.1+synopsis: A reliable way to detect the presence of a MinTTY console on Windows+description: MinTTY is a Windows-specific terminal emulator for the+ widely used Cygwin and MSYS projects, which provide+ Unix-like environments for Windows. MinTTY consoles behave+ differently from native Windows consoles (such as+ @cmd.exe@ or PowerShell) in many ways, and in some cases,+ these differences make it necessary to treat MinTTY+ consoles differently in code.+ .+ The @mintty@ library provides a simple way to detect if+ your code in running in a MinTTY console on Windows. It+ exports @isMinTTY@, which does the right thing 90% of the+ time (by checking if standard error is attached to+ MinTTY), and it also exports @isMinTTYHandle@ for the+ other 10% of the time (when you want to check is some+ arbitrary handle is attached to MinTTY). As you might+ expect, both of these functions will simply return @False@+ on any non-Windows operating system.+homepage: https://github.com/RyanGlScott/mintty+bug-reports: https://github.com/RyanGlScott/mintty/issues+license: BSD3+license-file: LICENSE+author: Ryan Scott+maintainer: Ryan Scott <ryan.gl.scott@gmail.com>+stability: Provisional+copyright: (C) 2017 Ryan Scott+category: System+build-type: Simple+extra-source-files: CHANGELOG.md, README.md, include/*.h+cabal-version: >=1.10++source-repository head+ type: git+ location: https://github.com/RyanGlScott/mintty++flag Win32-2-5+ description: Use Win32-2.5.0.0 or later.+ default: True++library+ exposed-modules: System.Console.MinTTY+ build-depends: base >= 4.3 && < 5++ if os(windows)+ cpp-options: "-DWINDOWS"++ if flag(Win32-2-5)+ build-depends: Win32 >= 2.5+ else+ build-depends: filepath+ , Win32 < 2.5+ build-tools: hsc2hs+ include-dirs: include+ includes: windows_cconv.h, winternl_compat.h+ other-modules: System.Console.MinTTY.Win32+ extra-libraries: ntdll++ hs-source-dirs: src+ default-language: Haskell2010+ ghc-options: -Wall
+ src/System/Console/MinTTY.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE CPP #-}++#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Safe #-}+#endif++{-|+Module: System.Console.Internal+Copyright: (C) 2017 Ryan Scott+License: BSD-style (see the file LICENSE)+Maintainer: Ryan Scott+Stability: Provisional+Portability: Portable++Exports functions that check if a process or handle is attached to a MinTTY+console on Windows, such as Cygwin or MSYS. On non-Windows operating systems,+the functions in this module will simply return 'False'.+-}+module System.Console.MinTTY (isMinTTY, isMinTTYHandle, HANDLE) where++#if defined(WINDOWS)+import System.Win32.Types (HANDLE)++# if MIN_VERSION_Win32(2,5,0)+import qualified System.Win32.MinTTY as Win32 (isMinTTY, isMinTTYHandle)+# else+-- NB: This is the backported definition local to this package, which we only+-- use if the bundled version of Win32 isn't recent enough.+import qualified System.Console.MinTTY.Win32 as Win32 (isMinTTY, isMinTTYHandle)+# endif+#else /* not Windows */+import Foreign.Ptr (Ptr)+#endif++-- | Returns 'True' if the current process's standard error is attached to a+-- MinTTY console (e.g., Cygwin or MSYS). Returns 'False' otherwise.+isMinTTY :: IO Bool+#if defined(WINDOWS)+isMinTTY = Win32.isMinTTY+#else+isMinTTY = return False+#endif++-- | Returns 'True' is the given handle is attached to a MinTTY console+-- (e.g., Cygwin or MSYS). Returns 'False' otherwise.+isMinTTYHandle :: HANDLE -> IO Bool+#if defined(WINDOWS)+isMinTTYHandle = Win32.isMinTTYHandle+#else+isMinTTYHandle _ = return False+#endif++#if !defined(WINDOWS)+type HANDLE = Ptr ()+#endif
+ src/System/Console/MinTTY/Win32.hsc view
@@ -0,0 +1,240 @@+{-+This is a direct copy of System.Win32.MinTTY from the Win32 library. We need+this for backwards compatibility with older versions of Win32 which do not ship+with this module.+-}++{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}++#if __GLASGOW_HASKELL__ >= 709+{-# LANGUAGE Safe #-}+#elif __GLASGOW_HASKELL__ >= 701+{-# LANGUAGE Trustworthy #-}+#endif+-----------------------------------------------------------------------------+-- |+-- Module : System.Win32.MinTTY+-- Copyright : (c) University of Glasgow 2006+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Esa Ilari Vuokko <ei@vuokko.info>+-- Stability : provisional+-- Portability : portable+--+-- A function to check if the current terminal uses MinTTY.+-- Much of this code was originally authored by Phil Ruffwind and the+-- git-for-windows project.+--+-----------------------------------------------------------------------------++module System.Console.MinTTY.Win32 (isMinTTY, isMinTTYHandle) where++import Graphics.Win32.Misc+import System.Win32.DLL+import System.Win32.File+import System.Win32.Types++#if MIN_VERSION_base(4,6,0)+import Control.Exception (catch)+#endif+import Data.List (isPrefixOf, isInfixOf, isSuffixOf)+import Foreign+import Foreign.C.Types+import System.FilePath (takeFileName)++#if __GLASGOW_HASKELL__ < 711+#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__)+#endif++-- The headers that are shipped with GHC's copy of MinGW-w64 assume Windows XP.+-- Since we need some structs that are only available with Vista or later,+-- we must manually set WINVER/_WIN32_WINNT accordingly.+#undef WINVER+#define WINVER 0x0600+#undef _WIN32_WINNT+#define _WIN32_WINNT 0x0600+##include "windows_cconv.h"+#include <windows.h>+#include "winternl_compat.h"++-- | Returns 'True' if the current process's standard error is attached to a+-- MinTTY console (e.g., Cygwin or MSYS). Returns 'False' otherwise.+isMinTTY :: IO Bool+isMinTTY = do+ h <- getStdHandle sTD_ERROR_HANDLE+ `catch` \(_ :: IOError) ->+ return nullHANDLE+ if h == nullHANDLE+ then return False+ else isMinTTYHandle h++-- | Returns 'True' is the given handle is attached to a MinTTY console+-- (e.g., Cygwin or MSYS). Returns 'False' otherwise.+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+ -- GetFileNameByHandleEx is only available on Vista and later (hence+ -- the name isMinTTYVista). If we're on an older version of Windows,+ -- getProcAddress will throw an IOException when it fails to find+ -- GetFileNameByHandleEx, and thus we will default to using+ -- NtQueryObject (isMinTTYCompat).++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+-- Note that GetFileInformationByHandleEx might return a filepath like:+--+-- \msys-dd50a72ab4668b33-pty1-to-master+--+-- But NtQueryObject might return something like:+--+-- \Device\NamedPipe\msys-dd50a72ab4668b33-pty1-to-master+--+-- This means we can't rely on "\cygwin-" or "\msys-" being at the very start+-- of the filepath. Therefore, we must take care to first call takeFileName+-- before checking for "cygwin" or "msys" at the start using `isPrefixOf`.++getFileNameByHandle :: HANDLE -> IO String+getFileNameByHandle h = do+ let sizeOfDWORD = sizeOf (undefined :: DWORD)+ -- note: implicitly assuming that DWORD has stronger alignment than wchar_t+ 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+ _ <- failIfNeg "NtQueryObject" $ c_NtQueryObject+ h objectNameInformation buf (fromIntegral bufSize) p_len+ oni <- peek buf+ return $ usBuffer $ oniName oni++fileNameInfo :: CInt+fileNameInfo = #const FileNameInfo++mAX_PATH :: Num a => a+mAX_PATH = #const MAX_PATH++objectNameInformation :: CInt+objectNameInformation = #const ObjectNameInformation++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 _ = #size FILE_NAME_INFO+ alignment _ = #alignment FILE_NAME_INFO+ poke buf fni = withTStringLen (fniFileName fni) $ \(str, len) -> do+ let len' = (min mAX_PATH len) * sizeOfTCHAR+ start = advancePtr (castPtr buf) (#offset FILE_NAME_INFO, FileName)+ end = advancePtr start len'+ (#poke FILE_NAME_INFO, FileNameLength) buf len'+ copyArray start (castPtr str :: Ptr Word8) len'+ poke (castPtr end) (0 :: TCHAR)+ peek buf = do+ vfniFileNameLength <- (#peek FILE_NAME_INFO, FileNameLength) buf+ let len = fromIntegral vfniFileNameLength `div` sizeOfTCHAR+ vfniFileName <- peekTStringLen (plusPtr buf (#offset FILE_NAME_INFO, FileName), len)+ return $ FILE_NAME_INFO+ { fniFileNameLength = vfniFileNameLength+ , fniFileName = vfniFileName+ }++foreign import WINDOWS_CCONV "winternl.h NtQueryObject"+ c_NtQueryObject :: HANDLE -> CInt -> Ptr OBJECT_NAME_INFORMATION+ -> ULONG -> Ptr ULONG -> IO NTSTATUS++type NTSTATUS = #type NTSTATUS+type ULONG = #type ULONG++failIfNeg :: (Num a, Ord a) => String -> IO a -> IO a+failIfNeg = failIf (< 0)++newtype OBJECT_NAME_INFORMATION = OBJECT_NAME_INFORMATION+ { oniName :: UNICODE_STRING+ } deriving Show++instance Storable OBJECT_NAME_INFORMATION where+ sizeOf _ = #size OBJECT_NAME_INFORMATION+ alignment _ = #alignment OBJECT_NAME_INFORMATION+ poke buf oni = (#poke OBJECT_NAME_INFORMATION, Name) buf (oniName oni)+ peek buf = fmap OBJECT_NAME_INFORMATION $ (#peek OBJECT_NAME_INFORMATION, Name) buf++data UNICODE_STRING = UNICODE_STRING+ { usLength :: USHORT+ , usMaximumLength :: USHORT+ , usBuffer :: String+ } deriving Show++instance Storable UNICODE_STRING where+ sizeOf _ = #size UNICODE_STRING+ alignment _ = #alignment UNICODE_STRING+ poke buf us = withTStringLen (usBuffer us) $ \(str, len) -> do+ let len' = (min mAX_PATH len) * sizeOfTCHAR+ start = advancePtr (castPtr buf) (#size UNICODE_STRING)+ end = advancePtr start len'+ (#poke UNICODE_STRING, Length) buf len'+ (#poke UNICODE_STRING, MaximumLength) buf (len' + sizeOfTCHAR)+ (#poke UNICODE_STRING, Buffer) buf start+ copyArray start (castPtr str :: Ptr Word8) len'+ poke (castPtr end) (0 :: TCHAR)+ peek buf = do+ vusLength <- (#peek UNICODE_STRING, Length) buf+ vusMaximumLength <- (#peek UNICODE_STRING, MaximumLength) buf+ vusBufferPtr <- (#peek UNICODE_STRING, Buffer) 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)