haskeline 0.7.3.0 → 0.7.3.1
raw patch · 10 files changed
+223/−33 lines, 10 filesdep +processPVP ok
version bump matches the API change (PVP)
Dependencies added: process
API changes (from Hackage documentation)
Files
- Changelog +3/−0
- System/Console/Haskeline.hs +5/−3
- System/Console/Haskeline/Backend/Posix.hsc +9/−6
- System/Console/Haskeline/Backend/Win32.hsc +3/−9
- System/Console/Haskeline/Backend/Win32/Echo.hs +171/−0
- System/Console/Haskeline/Directory.hsc +2/−9
- System/Console/Haskeline/Term.hs +2/−1
- haskeline.cabal +7/−5
- includes/h_iconv.h +9/−0
- includes/windows_cconv.h +12/−0
Changelog view
@@ -1,3 +1,6 @@+Changed in version 0.7.3.1:+ * Properly disable echoing in getPassword when running in MinTTY.+ * Use `cast` from Data.Typeable instead of Data.Dynamic. Changed in version 0.7.3.0: * Require ghc version of at least 7.4.1, and clean up obsolete code * Add thread-safe (in terminal-style interaction) external print function
System/Console/Haskeline.hs view
@@ -235,6 +235,10 @@ When using file-style interaction, this function turns off echoing while reading the line of input.++Note that if Haskeline is built against a version of the @Win32@ library+earlier than 2.5, 'getPassword' will incorrectly echo back input on MinTTY+consoles (such as Cygwin or MSYS). -} getPassword :: MonadException m => Maybe Char -- ^ A masking character; e.g., @Just \'*\'@@@ -243,9 +247,7 @@ (\tops prefix -> runInputCmdT tops $ runCommandLoop tops prefix loop $ Password [] x)- (\fops -> let h_in = inputHandle fops- in bracketSet (hGetEcho h_in) (hSetEcho h_in) False- $ runMaybeT $ getLocaleLine fops)+ (\fops -> withoutInputEcho fops $ runMaybeT $ getLocaleLine fops) where loop = choiceCmd [ simpleChar '\n' +> finish , simpleKey Backspace +> change deletePasswordChar
System/Console/Haskeline/Backend/Posix.hsc view
@@ -34,7 +34,7 @@ import System.Console.Haskeline.Backend.Posix.Encoder import GHC.IO.FD (fdFD)-import Data.Dynamic (cast)+import Data.Typeable (cast) import System.IO.Error import GHC.IO.Exception import GHC.IO.Handle.Types hiding (getState)@@ -320,12 +320,15 @@ hFlush (ehOut hs) , closeTerm = closeHandles hs , wrapInterrupt = withSigIntHandler- , termOps = Right FileOps- { inputHandle = ehIn hs+ , termOps = let h_in = ehIn hs+ in Right FileOps+ { withoutInputEcho = bracketSet (hGetEcho h_in)+ (hSetEcho h_in)+ False , wrapFileInput = withCodingMode (hIn hs)- , getLocaleChar = guardedEOF hGetChar (ehIn hs)- , maybeReadNewline = hMaybeReadNewline (ehIn hs)- , getLocaleLine = guardedEOF hGetLine (ehIn hs)+ , getLocaleChar = guardedEOF hGetChar h_in+ , maybeReadNewline = hMaybeReadNewline h_in+ , getLocaleLine = guardedEOF hGetLine h_in } }
System/Console/Haskeline/Backend/Win32.hsc view
@@ -22,19 +22,13 @@ import System.Console.Haskeline.LineState import System.Console.Haskeline.Term import System.Console.Haskeline.Backend.WCWidth+import System.Console.Haskeline.Backend.Win32.Echo (hWithoutInputEcho) import Data.ByteString.Internal (createAndTrim) import qualified Data.ByteString as B -##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- #include "win_console.h"+##include "windows_cconv.h" foreign import WINDOWS_CCONV "windows.h ReadConsoleInputW" c_ReadConsoleInput :: HANDLE -> Ptr () -> DWORD -> Ptr DWORD -> IO Bool@@ -409,7 +403,7 @@ putStrOut = putter, wrapInterrupt = withCtrlCHandler, termOps = Right FileOps- { inputHandle = h_in+ { withoutInputEcho = hWithoutInputEcho h_in , wrapFileInput = hWithBinaryMode h_in , getLocaleChar = getMultiByteChar cp h_in , maybeReadNewline = hMaybeReadNewline h_in
+ System/Console/Haskeline/Backend/Win32/Echo.hs view
@@ -0,0 +1,171 @@+{-# LANGUAGE CPP #-}++module System.Console.Haskeline.Backend.Win32.Echo (hWithoutInputEcho) where++import Control.Exception (throw)+import Control.Monad (void)+import Control.Monad.IO.Class (liftIO)++import System.Console.Haskeline.MonadException (MonadException, bracket)+import System.Exit (ExitCode(..))+import System.IO (Handle, hGetContents, hGetEcho, hSetEcho)+import System.Process (StdStream(..), createProcess, shell,+ std_in, std_out, waitForProcess)++#if MIN_VERSION_Win32(2,5,0)+import Control.Concurrent.MVar (readMVar)++import Data.Typeable (cast)++import Foreign.C.Types+import Foreign.StablePtr (StablePtr, freeStablePtr, newStablePtr)++import GHC.IO.FD (FD(..))+import GHC.IO.Handle.Types (Handle(..), Handle__(..))++import System.Win32.Types (HANDLE)+import System.Win32.MinTTY (isMinTTYHandle)+#endif++-- | Return the handle's current input 'EchoState'.+hGetInputEchoState :: Handle -> IO EchoState+hGetInputEchoState input = do+ min_tty <- minTTY input+ if min_tty+ then fmap MinTTY (hGetInputEchoSTTY input)+ else fmap DefaultTTY $ hGetEcho input++-- | Return all of @stty@'s current settings in a non-human-readable format.+--+-- This function is not very useful on its own. Its greater purpose is to+-- provide a compact 'STTYSettings' that can be fed back into+-- 'hSetInputEchoState'.+hGetInputEchoSTTY :: Handle -> IO STTYSettings+hGetInputEchoSTTY input = hSttyRaw input "-g"++-- | Set the handle's input 'EchoState'.+hSetInputEchoState :: Handle -> EchoState -> IO ()+hSetInputEchoState input (MinTTY settings) = hSetInputEchoSTTY input settings+hSetInputEchoState input (DefaultTTY echo) = hSetEcho input echo++-- | Create an @stty@ process and wait for it to complete. This is useful for+-- changing @stty@'s settings, after which @stty@ does not output anything.+--+-- @+-- hSetInputEchoSTTY input = 'void' . 'hSttyRaw' input+-- @+hSetInputEchoSTTY :: Handle -> STTYSettings -> IO ()+hSetInputEchoSTTY input = void . hSttyRaw input++-- | Save the handle's current input 'EchoState', perform a computation,+-- restore the saved 'EchoState', and then return the result of the+-- computation.+--+-- @+-- bracketInputEcho input action =+-- 'bracket' ('liftIO' $ 'hGetInputEchoState' input)+-- ('liftIO' . 'hSetInputEchoState' input)+-- (const action)+-- @+hBracketInputEcho :: MonadException m => Handle -> m a -> m a+hBracketInputEcho input action =+ bracket (liftIO $ hGetInputEchoState input)+ (liftIO . hSetInputEchoState input)+ (const action)++-- | Perform a computation with the handle's input echoing disabled. Before+-- running the computation, the handle's input 'EchoState' is saved, and the+-- saved 'EchoState' is restored after the computation finishes.+hWithoutInputEcho :: MonadException m => Handle -> m a -> m a+hWithoutInputEcho input action = do+ echo_off <- liftIO $ hEchoOff input+ hBracketInputEcho input+ (liftIO (hSetInputEchoState input echo_off) >> action)++-- | Create an @stty@ process, wait for it to complete, and return its output.+hSttyRaw :: Handle -> String -> IO STTYSettings+hSttyRaw input arg = do+ let stty = (shell $ "stty " ++ arg) {+ std_in = UseHandle input+ , std_out = CreatePipe+ }+ (_, mbStdout, _, rStty) <- createProcess stty+ exStty <- waitForProcess rStty+ case exStty of+ e@ExitFailure{} -> throw e+ ExitSuccess -> maybe (return "") hGetContents mbStdout++-- | A representation of the handle input's current echoing state.+-- See, for instance, 'hEchoOff'.+data EchoState+ = MinTTY STTYSettings+ -- ^ The argument to (or value returned from) an invocation of the @stty@+ -- command-line utility. Most POSIX-like shells have @stty@, including+ -- MinTTY on Windows. Since neither 'hGetEcho' nor 'hSetEcho' work on+ -- MinTTY, when 'getInputEchoState' runs on MinTTY, it returns a value+ -- built with this constructor.+ --+ -- However, native Windows consoles like @cmd.exe@ or PowerShell do not+ -- have @stty@, so if you construct an 'EchoState' with this constructor+ -- manually, take care not to use it with a native Windows console.+ | DefaultTTY Bool+ -- ^ A simple on ('True') or off ('False') toggle. This is returned by+ -- 'hGetEcho' and given as an argument to 'hSetEcho', which work on most+ -- consoles, with the notable exception of MinTTY on Windows. If you+ -- construct an 'EchoState' with this constructor manually, take care not+ -- to use it with MinTTY.+ deriving (Eq, Ord, Show)++-- | Indicates that the handle's input echoing is (or should be) off.+hEchoOff :: Handle -> IO EchoState+hEchoOff input = do+ min_tty <- minTTY input+ return $ if min_tty+ then MinTTY "-echo"+ else DefaultTTY False++-- | Settings used to configure the @stty@ command-line utility.+type STTYSettings = String++-- | Is the current process attached to a MinTTY console (e.g., Cygwin or MSYS)?+minTTY :: Handle -> IO Bool+#if MIN_VERSION_Win32(2,5,0)+minTTY input = withHandleToHANDLE input isMinTTYHandle+#else+-- On older versions of Win32, we simply punt.+minTTY _ = return False+#endif++#if MIN_VERSION_Win32(2,5,0)+foreign import ccall unsafe "_get_osfhandle"+ c_get_osfhandle :: CInt -> IO HANDLE++-- | Extract a Windows 'HANDLE' from a Haskell 'Handle' and perform+-- an action on it.++-- Originally authored by Max Bolingbroke in the ansi-terminal library+withHandleToHANDLE :: Handle -> (HANDLE -> IO a) -> IO a+withHandleToHANDLE haskell_handle action =+ -- Create a stable pointer to the Handle. This prevents the garbage collector+ -- getting to it while we are doing horrible manipulations with it, and hence+ -- stops it being finalized (and closed).+ withStablePtr haskell_handle $ const $ do+ -- Grab the write handle variable from the Handle+ let write_handle_mvar = case haskell_handle of+ FileHandle _ handle_mvar -> handle_mvar+ DuplexHandle _ _ handle_mvar -> handle_mvar+ -- This is "write" MVar, we could also take the "read" one++ -- Get the FD from the algebraic data type+ Just fd <- fmap (\(Handle__ { haDevice = dev }) -> fmap fdFD (cast dev))+ $ readMVar write_handle_mvar++ -- Finally, turn that (C-land) FD into a HANDLE using msvcrt+ windows_handle <- c_get_osfhandle fd++ -- Do what the user originally wanted+ action windows_handle++withStablePtr :: a -> (StablePtr a -> IO b) -> IO b+withStablePtr value = bracket (newStablePtr value) freeStablePtr+#endif
System/Console/Haskeline/Directory.hsc view
@@ -18,14 +18,7 @@ #include <windows.h> #include <shlobj.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+##include "windows_cconv.h" foreign import WINDOWS_CCONV "FindFirstFileW" c_FindFirstFile :: LPCTSTR -> Ptr () -> IO HANDLE@@ -63,7 +56,7 @@ getHomeDirectory :: IO FilePath getHomeDirectory = System.Directory.getHomeDirectory -#else +#else import System.Directory
System/Console/Haskeline/Term.hs view
@@ -67,7 +67,8 @@ -- Backends can assume that getLocaleLine, getLocaleChar and maybeReadNewline -- are "wrapped" by wrapFileInput. data FileOps = FileOps {- inputHandle :: Handle, -- ^ e.g. for turning off echoing.+ withoutInputEcho :: forall m a . MonadException m => m a -> m a,+ -- ^ Perform an action without echoing input. wrapFileInput :: forall a . IO a -> IO a, getLocaleLine :: MaybeT IO String, getLocaleChar :: MaybeT IO Char,
haskeline.cabal view
@@ -1,6 +1,6 @@ Name: haskeline Cabal-Version: >=1.10-Version: 0.7.3.0+Version: 0.7.3.1 Category: User Interfaces License: BSD3 License-File: LICENSE@@ -20,7 +20,7 @@ Bug-Reports: https://github.com/judah/haskeline/issues Stability: Stable Build-Type: Custom-extra-source-files: examples/Test.hs Changelog+extra-source-files: examples/Test.hs Changelog includes/*.h source-repository head type: git@@ -44,7 +44,8 @@ -- wasn't fixed until 7.4.1 Build-depends: base >=4.5 && < 4.11, containers>=0.4 && < 0.6, directory>=1.1 && < 1.4, bytestring>=0.9 && < 0.11,- filepath >= 1.2 && < 1.5, transformers >= 0.2 && < 0.6+ filepath >= 1.2 && < 1.5, transformers >= 0.2 && < 0.6,+ process >= 1.0 && < 1.5 Default-Language: Haskell98 Default-Extensions: ForeignFunctionInterface, Rank2Types, FlexibleInstances,@@ -86,13 +87,14 @@ if os(windows) { Build-depends: Win32>=2.0 Other-modules: System.Console.Haskeline.Backend.Win32+ System.Console.Haskeline.Backend.Win32.Echo c-sources: cbits/win_console.c- includes: win_console.h+ includes: win_console.h, windows_cconv.h install-includes: win_console.h cpp-options: -DMINGW } else { Build-depends: unix>=2.0 && < 2.8- Other-modules: + Other-modules: System.Console.Haskeline.Backend.Posix System.Console.Haskeline.Backend.Posix.Encoder System.Console.Haskeline.Backend.DumbTerm
+ includes/h_iconv.h view
@@ -0,0 +1,9 @@+#include <iconv.h>++iconv_t haskeline_iconv_open(const char *tocode, const char *fromcode);++void haskeline_iconv_close(iconv_t cd);++size_t haskeline_iconv(iconv_t cd, char **inbuf, size_t *inbytesleft,+ char **outbuf, size_t *outbytesleft);+
+ includes/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