-----------------------------------------------------------------------------
-- |
-- Module : System.Console.Editline.Readline
-- Copyright : (c) 2008, Judah Jacobson
-- Copied with permission from the readline package
-- (originally System.Console.Readline):
-- (c) 2007, Isaac Jones
-- (c) 2002, Simon Marlow
-- (c) 2001, Marcin Kowalczyk
-- License : BSD3
--
-- Maintainer : judah.jacobson@gmail.com
-- Stability : provisional
-- Portability : non-portable (requires libedit)
--
-- This module provides a subset of the functions from
-- "System.Console.Readline", which is distributed in the readline package.
-- However, because this package links against editline
-- () instead of readline, programs using
-- this module are not required to be distributed under the GPL.
--
-- An example of a typical use of the readline API with history functionality
-- is illustrated in the following read, eval, print loop:
--
-- @
-- readEvalPrintLoop :: IO ()
-- readEvalPrintLoop = do
-- maybeLine <- readline \"% \"
-- case maybeLine of
-- Nothing -> return () -- EOF \/ control-d
-- Just \"exit\" -> return ()
-- Just line -> do addHistory line
-- putStrLn $ \"The user input: \" ++ (show line)
-- readEvalPrintLoop
-- @
--
-----------------------------------------------------------------------------
#include "HsEditlineConfig.h"
#ifdef HAVE_EDITLINE_READLINE_H
#include
#else
#ifdef HAVE_READLINE_READLINE_H
#include
#else
#ifdef HAVE_EDITLINE_EDITLINE_H
#include
#endif
#endif
#endif
module System.Console.Editline.Readline (
--------------------------------------------------------------------
-- Basic Behavior.
readline, -- :: String -> IO (Maybe String)
--------------------------------------------------------------------
-- History Functionality.
addHistory, -- :: String -> IO ()
readHistory, -- :: String -> IO Bool
writeHistory, -- :: String -> IO Bool
clearHistory, -- :: IO ()
stifleHistory, -- :: Int -> IO ()
unstifleHistory, -- :: IO Int
historyIsStifled, -- :: IO Bool
historyMaxEntries, -- IO Int
--------------------------------------------------------------------
-- Readline Variables.
getLineBuffer, -- :: IO String
-- Functions involving point positions are meaningful only when string
-- conversion between Haskell and C preserves the length.
getPoint, -- :: IO Int
setPoint, -- :: Int -> IO ()
getEnd, -- :: IO Int
setEnd, -- :: Int -> IO ()
getPrompt, -- :: IO String
getLibraryVersion, -- :: IO String
getTerminalName, -- :: IO String
setReadlineName, -- :: String -> IO ()
getInStream, -- :: IO Handle
getOutStream, -- :: IO Handle
setStartupHook, -- :: Maybe (IO ()) -> IO ()
-- rl_getc_function wrapper is not provided because it uses FILE *
-- and it would be too expensive to convert FILE * to Handle
-- for each character.
setRedisplayFunction, -- :: Maybe (IO ()) -> IO ()
-- Nothing means the original: rl_redisplay.
--------------------------------------------------------------------
-- Binding Keys.
Callback, -- type Callback = Int -> Char -> IO Int
addDefun, -- :: String -> Callback -> Maybe Char -> IO ()
bindKey, -- :: Char -> Callback -> IO ()
parseAndBind, -- :: String -> IO ()
readInitFile, -- :: String -> IO ()
--------------------------------------------------------------------
-- Redisplay.
redisplay, -- :: IO ()
--------------------------------------------------------------------
-- Utility functions.
readKey, -- :: IO Char
stuffChar, -- :: Char -> IO Bool
initialize, -- :: IO ()
resetTerminal, -- :: Maybe String -> IO ()
--------------------------------------------------------------------
-- Alternate Interface.
callbackHandlerInstall, -- :: String -> (String -> IO ()) -> IO (IO ())
-- Returns the cleanup action.
callbackReadChar, -- :: IO ()
--------------------------------------------------------------------
-- Completion functions.
complete, -- :: Int -> Char -> IO Int
-- readline uses functions that are called multiple times and
-- return an entry at a time, maintaining their state at which
-- point they are. This is silly in a functional language so here
-- we work with functions String -> IO [String].
completionMatches,
-- :: String -> (String -> IO [String]) -> IO (Maybe (String, [String]))
filenameCompletionFunction, -- :: String -> IO [String]
usernameCompletionFunction, -- :: String -> IO [String]
setCompletionEntryFunction,
-- :: Maybe (String -> IO [String]) -> IO ()
setAttemptedCompletionFunction,
-- :: Maybe (String -> Int -> Int -> IO (Maybe (String, [String]))) -> IO ()
getCompletionQueryItems, -- :: IO Int
setCompletionQueryItems, -- :: Int -> IO ()
getBasicWordBreakCharacters, -- :: IO String
setBasicWordBreakCharacters, -- :: String -> IO ()
getCompleterWordBreakCharacters, -- :: IO String
setCompleterWordBreakCharacters, -- :: String -> IO ()
getCompleterQuoteCharacters, -- :: IO String
setCompleterQuoteCharacters, -- :: String -> IO ()
getSpecialPrefixes, -- :: IO String
setSpecialPrefixes, -- :: String -> IO ()
getCompletionAppendCharacter, -- :: IO (Maybe Char)
setCompletionAppendCharacter, -- :: Maybe Char -> IO ()
setInhibitCompletion, -- :: Bool -> IO ()
getInhibitCompletion, -- :: IO Bool
setAttemptedCompletionOver, -- :: Bool -> IO ()
getAttemptedCompletionOver, -- :: IO Bool
)
where
------------------------------------------------------------------------
import Control.Monad ( liftM, when, unless )
import Data.Char ( chr, ord )
import System.IO ( Handle )
import System.IO.Unsafe ( unsafePerformIO )
import Data.IORef ( newIORef, readIORef, writeIORef )
import Foreign.Ptr ( Ptr, nullPtr, FunPtr, nullFunPtr, freeHaskellFunPtr )
import Foreign.Storable ( Storable(..) )
import Foreign.Marshal.Utils ( maybePeek, maybeWith )
import Foreign.Marshal.Alloc ( free )
import Foreign.Marshal.Array ( mallocArray, peekArray0, pokeArray0 )
import Foreign.C.Types ( CInt(..), CChar, CFile )
import Foreign.C.String ( newCString, peekCString, withCString,
castCharToCChar, castCCharToChar )
import GHC.Handle ( fdToHandle )
{-# CFILES HsReadline_cbits.c #-}
------------------------------------------------------------------------
-- Basic Behavior.
-- | readline is similar to 'System.IO.getLine', but with rich edit
-- functionality and history capability. readline will read a line
-- from the terminal and return it, using /prompt/ as a prompt. If
-- prompt is the empty string, no prompt is issued. The line returned
-- has the final newline removed, so only the text of the line
-- remains. A blank line returns the empty string. If EOF is
-- encountered while reading a line, and the line is empty, Nothing is
-- returned. If an EOF is read with a non-empty line, it is treated
-- as a newline.
readline :: String-- ^prompt
-> IO (Maybe String) -- ^returns the line the user input, or Nothing if EOF is encountered.
readline prompt = do
ptr <- withCString prompt readlineC
flip maybePeek ptr $ \ptr' -> do
line <- peekCString ptr'
free ptr'
return line
foreign import ccall "readline" readlineC :: Ptr CChar -> IO (Ptr CChar)
--------------------------------------------------------------------------
-- History functionality
-- TODO: older versions of libedit don't return errors correctly...
-- |Add this command to the history. This allows users to search backward
-- through history with C-r and step through with up and down arrows, among
-- other things.
addHistory :: String -> IO ()
addHistory line = withCString line add_history
foreign import ccall unsafe add_history :: Ptr CChar -> IO ()
-- |Read in a history file. Returns 'False' on failure
-- (for example, if the file does not exist).
readHistory :: FilePath -- ^ The file to read.
-> IO Bool
readHistory fp = do
ok <- withCString fp read_history
return (histResultIsOK ok)
foreign import ccall unsafe read_history :: Ptr CChar -> IO CInt
-- |Write out a history file. Returns 'False' if there was a problem writing the file.
writeHistory :: FilePath -- ^ The file to write.
-> IO Bool
writeHistory fp = do
ok <- withCString fp write_history
return (histResultIsOK ok)
foreign import ccall unsafe write_history :: Ptr CChar -> IO CInt
histResultIsOK :: CInt -> Bool
#ifdef NEGATIVE_HIST_ERROR
-- Old way that libedit handled errors; different from readline
histResultIsOK = (>=0)
#else
histResultIsOK = (==0)
#endif
-- |Clear the history.
clearHistory :: IO ()
clearHistory = clear_history
foreign import ccall unsafe clear_history :: IO ()
-- |Stifle the history list, remembering only a certain number of entries.
stifleHistory :: Int -> IO ()
stifleHistory n = stifle_history n
foreign import ccall unsafe stifle_history :: Int -> IO ()
-- |Stop stifling the history, returning the previous amount the history was
-- stifled by.
unstifleHistory :: IO Int
unstifleHistory = unstifle_history
foreign import ccall unsafe unstifle_history :: IO Int
-- |Check whether the history is stifled or not. True if stifled, False if not.
historyIsStifled :: IO Bool
historyIsStifled = do
isStifledInt <- history_is_stifled
let isStifled = case isStifledInt of
0 -> False
1 -> True
_ -> error "historyIsStifled: history_is_stifled returned unexpected value (expected 0 or 1, received other)" -- just for completeness - there is no conceivable way readline would not return either 0 or 1 here
return isStifled
foreign import ccall unsafe history_is_stifled :: IO Int
-- |Get the maximum number of history entries, returning 0 if the history is
-- unstifled.
historyMaxEntries :: IO Int
historyMaxEntries = liftM fromIntegral (peek max_input_history)
-- Note: this variable is different than history_max_entries, but has the same
-- use.
foreign import ccall "&" max_input_history :: Ptr CInt
------------------------------------------------------------------------
-- Readline Variables.
getLineBuffer :: IO String
getLineBuffer = peek rl_line_buffer >>= peekCString
foreign import ccall "&"
rl_line_buffer :: Ptr (Ptr CChar)
-- Functions involving point positions are meaningful only when string
-- conversion between Haskell and C preserves the length.
getPoint :: IO Int
getPoint = liftM fromIntegral (peek rl_point)
setPoint :: Int -> IO ()
setPoint p = poke rl_point (fromIntegral p)
foreign import ccall "&" rl_point :: Ptr CInt
getEnd :: IO Int
getEnd = liftM fromIntegral (peek rl_end)
setEnd :: Int -> IO ()
setEnd p = poke rl_end (fromIntegral p)
foreign import ccall "&" rl_end :: Ptr CInt
getPrompt :: IO String
getPrompt = peek rl_prompt >>= peekCString
foreign import ccall "&" rl_prompt :: Ptr (Ptr CChar)
getLibraryVersion :: IO String
getLibraryVersion = peek rl_library_version >>= peekCString
foreign import ccall "&" rl_library_version :: Ptr (Ptr CChar)
getTerminalName :: IO String
getTerminalName = peek rl_terminal_name >>= peekCString
foreign import ccall "&" rl_terminal_name :: Ptr (Ptr CChar)
setReadlineName :: String -> IO ()
setReadlineName name = newCString name >>= poke rl_readline_name
-- The memory for name will never be freed. Otherwise we would
-- have to recognize the original value which is a static string
-- literal. This function is usually called only once anyway.
foreign import ccall "&" rl_readline_name :: Ptr (Ptr CChar)
getInStream :: IO Handle
getInStream = peek rl_instream >>= hs_fileno >>= fdToHandle . fromIntegral
foreign import ccall "&" rl_instream :: Ptr (Ptr CFile)
getOutStream :: IO Handle
getOutStream = peek rl_outstream >>= hs_fileno >>= fdToHandle . fromIntegral
foreign import ccall "&" rl_outstream :: Ptr (Ptr CFile)
foreign import ccall unsafe "fileno"
hs_fileno :: Ptr CFile -> IO CInt
setStartupHook :: Maybe (IO ()) -> IO ()
setStartupHook hook = setFunPtr rl_startup_hook hook exportHookInt
foreign import ccall "&" rl_startup_hook :: Ptr (FunPtr (IO CInt))
-- rl_getc_function wrapper is not provided because it uses FILE *
-- and it would be too expensive to convert FILE * to Handle
-- for each character.
setRedisplayFunction :: Maybe (IO ()) -> IO ()
-- Nothing means the original: rl_redisplay.
setRedisplayFunction fun = do
oldPtr <- peek rl_redisplay_function
when (oldPtr /= nullFunPtr && oldPtr /= rl_redisplay) $
freeHaskellFunPtr oldPtr
newPtr <- case fun of
Nothing -> return rl_redisplay
Just f -> exportHookVoid f
poke rl_redisplay_function newPtr
foreign import ccall "&" rl_redisplay_function :: Ptr (FunPtr (IO ()))
foreign import ccall "&" rl_redisplay :: FunPtr (IO ())
-- rl_redisplay_function can never be NULL.
exportHookInt :: IO () -> IO (FunPtr (IO CInt))
exportHookInt hook = exportHookIntC (hook >> return 0)
foreign import ccall "wrapper"
exportHookIntC :: IO CInt -> IO (FunPtr (IO CInt))
foreign import ccall "wrapper"
exportHookVoid :: IO () -> IO (FunPtr (IO ()))
setFunPtr_freeIf :: (FunPtr a -> Bool)
-> Ptr (FunPtr a)
-> Maybe b
-> (b -> IO (FunPtr a))
-> IO ()
setFunPtr_freeIf predicate variable newFun makeNewFun = do
oldPtr <- peek variable
when (predicate oldPtr) $ freeHaskellFunPtr oldPtr
newPtr <- case newFun of
Nothing -> return nullFunPtr
Just f -> makeNewFun f
poke variable newPtr
setFunPtr :: Ptr (FunPtr a)
-> Maybe b
-> (b -> IO (FunPtr a))
-> IO ()
setFunPtr = setFunPtr_freeIf (/= nullFunPtr)
------------------------------------------------------------------------
-- Binding Keys.
type Callback = Int -> Char -> IO Int
type CallbackC = CInt -> CInt -> IO CInt
addDefun :: String -> Callback -> Maybe Char -> IO ()
addDefun name cb key = do
namePtr <- newCString name
-- rl_add_defun does *not* make a copy of the function name.
cbPtr <- exportCallback cb
-- The memory will never be freed. But readline does not provide
-- removing defuns anyway.
rl_add_defun namePtr cbPtr (maybe (-1) (fromIntegral . ord) key)
return ()
foreign import ccall unsafe "rl_add_defun"
rl_add_defun :: Ptr CChar -> FunPtr CallbackC -> CInt -> IO CInt
bindKey :: Char -> Callback -> IO ()
bindKey key cb = do
cbPtr <- exportCallback cb
-- The memory will never be freed. We should provide a way to
-- free it, but it's complicated because of multiple keymaps.
-- It should probably be done explicitly.
rl_bind_key (fromIntegral (ord key)) cbPtr
return ()
foreign import ccall unsafe "rl_bind_key"
rl_bind_key :: CInt -> FunPtr CallbackC -> IO CInt
parseAndBind :: String -> IO ()
parseAndBind s = do
ok <- withCString s rl_parse_and_bind
unless (ok == 0) $ ioError (userError "Parse error")
foreign import ccall unsafe "rl_parse_and_bind"
rl_parse_and_bind :: Ptr CChar -> IO CInt
readInitFile :: String -> IO ()
readInitFile name = do
ok <- withCString name rl_read_init_file
unless (ok == 0) $ ioError (userError "Can't read file")
foreign import ccall unsafe "rl_read_init_file"
rl_read_init_file :: Ptr CChar -> IO CInt
------------------------------------------------------------------------
-- Associating Function Names and Bindings.
exportCallback :: Callback -> IO (FunPtr CallbackC)
exportCallback cb =
exportCallbackC $ \n key ->
liftM fromIntegral (cb (fromIntegral n) (chr (fromIntegral key)))
foreign import ccall "wrapper"
exportCallbackC :: CallbackC -> IO (FunPtr CallbackC)
------------------------------------------------------------------------
-- Redisplay.
foreign import ccall unsafe "rl_redisplay" redisplay :: IO ()
------------------------------------------------------------------------
-- Utility functions.
readKey :: IO Char
readKey = liftM (chr . fromIntegral) rl_read_key
foreign import ccall unsafe "rl_read_key"
rl_read_key :: IO CInt
stuffChar :: Char -> IO Bool
stuffChar key = liftM (/= 0) (rl_stuff_char (fromIntegral (ord key)))
foreign import ccall unsafe "rl_stuff_char"
rl_stuff_char :: CInt -> IO CInt
initialize :: IO ()
initialize = do rl_initialize; return ()
foreign import ccall unsafe "rl_initialize"
rl_initialize :: IO CInt
resetTerminal :: Maybe String -> IO ()
resetTerminal name = do
maybeWith withCString name rl_reset_terminal
return ()
foreign import ccall unsafe "rl_reset_terminal"
rl_reset_terminal :: Ptr CChar -> IO CInt
------------------------------------------------------------------------
-- Alternate Interface.
type Handler = Ptr CChar -> IO ()
callbackHandlerInstall :: String -> (String -> IO ()) -> IO (IO ())
callbackHandlerInstall prompt lhandler = do
lhandlerPtr <- exportHandler $ \linePtr -> peekCString linePtr >>= lhandler
withCString prompt $ \promptPtr -> do
rl_callback_handler_install promptPtr lhandlerPtr
return (do rl_callback_handler_remove; freeHaskellFunPtr lhandlerPtr)
foreign import ccall "wrapper"
exportHandler :: Handler -> IO (FunPtr Handler)
foreign import ccall unsafe "rl_callback_handler_install"
rl_callback_handler_install :: Ptr CChar -> FunPtr Handler -> IO ()
foreign import ccall unsafe "rl_callback_handler_remove"
rl_callback_handler_remove :: IO ()
foreign import ccall "rl_callback_read_char"
callbackReadChar :: IO ()
------------------------------------------------------------------------
-- Completion functions.
complete :: Int -> Char -> IO Int
complete n key =
liftM fromIntegral $
rl_complete (fromIntegral n) (fromIntegral (ord key))
foreign import ccall "rl_complete"
rl_complete :: CInt -> CInt -> IO CInt
type Generator = Ptr CChar -> CInt -> IO (Ptr CChar)
singleToWhole :: Generator -> String -> IO [String]
singleToWhole f text =
withCString text $ \textPtr -> let
loop n = do
ptr <- f textPtr n
if ptr == nullPtr
then return []
else do
str <- peekCString ptr
free ptr
rest <- loop (n+1)
return (str:rest)
in loop 0
wholeToSingle :: (String -> IO [String]) -> IO Generator
wholeToSingle f = do
ref <- newIORef []
return $ \textPtr state -> do
when (state == 0) $ peekCString textPtr >>= f >>= writeIORef ref
next <- readIORef ref
case next of
[] -> return nullPtr
x:xs -> do
writeIORef ref xs
newCString x
completionMatches
:: String -> (String -> IO [String]) -> IO (Maybe (String, [String]))
completionMatches text entry =
withCString text $ \textPtr -> do
entryPtr <- wholeToSingle entry >>= exportGenerator
matchesPtr <- rl_completion_matches textPtr entryPtr
freeHaskellFunPtr entryPtr
if matchesPtr == nullPtr then return Nothing else do
matchPtrs <- peekArray0 nullPtr matchesPtr
(text':matches) <- mapM peekCString matchPtrs
mapM_ free matchPtrs
free matchesPtr
return (Just (text', matches))
#ifdef HAVE_RL_COMPLETION_MATCHES
foreign import ccall "rl_completion_matches"
#else
foreign import ccall "completion_matches"
#endif
rl_completion_matches :: Ptr CChar -> FunPtr Generator -> IO (Ptr (Ptr CChar))
filenameCompletionFunction :: String -> IO [String]
filenameCompletionFunction = singleToWhole rl_filename_completion_function
foreign import ccall unsafe "filename_completion_function"
rl_filename_completion_function :: Generator
usernameCompletionFunction :: String -> IO [String]
usernameCompletionFunction = singleToWhole rl_username_completion_function
foreign import ccall unsafe "username_completion_function"
rl_username_completion_function :: Generator
setCompletionEntryFunction :: Maybe (String -> IO [String]) -> IO ()
setCompletionEntryFunction fun =
setFunPtr rl_completion_entry_function fun $ \f ->
wholeToSingle f >>= exportGenerator
foreign import ccall "&" rl_completion_entry_function :: Ptr (FunPtr Generator)
foreign import ccall "wrapper"
exportGenerator :: Generator -> IO (FunPtr Generator)
type Completer = Ptr CChar -> CInt -> CInt -> IO (Ptr (Ptr CChar))
setAttemptedCompletionFunction
:: Maybe (String -> Int -> Int -> IO (Maybe (String, [String]))) -> IO ()
setAttemptedCompletionFunction fun =
setFunPtr rl_attempted_completion_function fun $ \f ->
exportCompleter $ \textPtr start end -> do
text <- peekCString textPtr
found <- f text (fromIntegral start) (fromIntegral end)
case found of
Nothing -> return nullPtr
Just (text', matches) -> do
let matches' = if null matches then [text'] else matches
matchPtrs <- mapM newCString (text':matches')
matchesPtr <- mallocArray (length matchPtrs + 1)
pokeArray0 nullPtr matchesPtr matchPtrs
return matchesPtr
foreign import ccall "&" rl_attempted_completion_function :: Ptr (FunPtr Completer)
foreign import ccall "wrapper"
exportCompleter :: Completer -> IO (FunPtr Completer)
getCompletionQueryItems :: IO Int
getCompletionQueryItems =
liftM fromIntegral (peek rl_completion_query_items)
setCompletionQueryItems :: Int -> IO ()
setCompletionQueryItems items =
poke rl_completion_query_items (fromIntegral items)
foreign import ccall "&" rl_completion_query_items :: Ptr CInt
getBasicWordBreakCharacters :: IO String
getBasicWordBreakCharacters = getCharacters rl_basic_word_break_characters
setBasicWordBreakCharacters :: String -> IO ()
setBasicWordBreakCharacters =
setCharacters_freeIf
(/= orig_rl_basic_word_break_characters)
rl_basic_word_break_characters
foreign import ccall "&" rl_basic_word_break_characters :: Ptr (Ptr CChar)
-- Similarly to rl_quote_filename, we must be able to detect the
-- original pointer to a static char array.
{-# NOINLINE orig_rl_basic_word_break_characters #-}
orig_rl_basic_word_break_characters :: Ptr CChar
orig_rl_basic_word_break_characters = unsafePerformIO $
peek rl_basic_word_break_characters
getCompleterWordBreakCharacters :: IO String
getCompleterWordBreakCharacters = getCharacters rl_completer_word_break_characters
setCompleterWordBreakCharacters :: String -> IO ()
setCompleterWordBreakCharacters =
setCharacters_freeIf
(\oldPtr -> oldPtr /= nullPtr &&
oldPtr /= orig_rl_basic_word_break_characters)
rl_completer_word_break_characters
foreign import ccall "&" rl_completer_word_break_characters :: Ptr (Ptr CChar)
getCompleterQuoteCharacters :: IO String
getCompleterQuoteCharacters = getCharacters rl_completer_quote_characters
setCompleterQuoteCharacters :: String -> IO ()
setCompleterQuoteCharacters cs = do
oldPtr <- peek rl_completer_quote_characters
when (oldPtr /= nullPtr) $ free oldPtr
-- I think that rl_completer_quote_characters should never be empty
-- but can be NULL.
newPtr <- if null cs
then return nullPtr
else do
ptr <- mallocArray (length cs + 1)
pokeArray0 0 ptr (map castCharToCChar cs)
return ptr
poke rl_completer_quote_characters newPtr
foreign import ccall "&" rl_completer_quote_characters :: Ptr (Ptr CChar)
getSpecialPrefixes :: IO String
getSpecialPrefixes = getCharacters rl_special_prefixes
setSpecialPrefixes :: String -> IO ()
setSpecialPrefixes = setCharacters rl_special_prefixes
foreign import ccall "&" rl_special_prefixes :: Ptr (Ptr CChar)
getCompletionAppendCharacter :: IO (Maybe Char)
getCompletionAppendCharacter = do
ch <- peek rl_completion_append_character
return $ if ch == 0 then Nothing else Just (chr (fromIntegral ch))
setCompletionAppendCharacter :: Maybe Char -> IO ()
setCompletionAppendCharacter ch =
poke rl_completion_append_character (maybe 0 (fromIntegral . ord) ch)
foreign import ccall "&" rl_completion_append_character :: Ptr CInt
setInhibitCompletion :: Bool -> IO ()
setInhibitCompletion inh = poke rl_inhibit_completion (if inh then 1 else 0)
getInhibitCompletion :: IO Bool
getInhibitCompletion = liftM (/= 0) (peek rl_inhibit_completion)
foreign import ccall "&" rl_attempted_completion_over :: Ptr CInt
getAttemptedCompletionOver :: IO Bool
getAttemptedCompletionOver =
liftM (/=0) (peek rl_attempted_completion_over)
setAttemptedCompletionOver :: Bool -> IO ()
setAttemptedCompletionOver over =
poke rl_attempted_completion_over (if over then 1 else 0)
foreign import ccall "&" rl_inhibit_completion :: Ptr CInt
setCharacters_freeIf :: (Ptr CChar -> Bool) -> Ptr (Ptr CChar) -> String -> IO ()
setCharacters_freeIf predicate variable chars = do
oldPtr <- peek variable
when (predicate oldPtr) $ free oldPtr
newPtr <- mallocArray (length chars + 1)
pokeArray0 0 newPtr (map castCharToCChar chars)
poke variable newPtr
setCharacters :: Ptr (Ptr CChar) -> String -> IO ()
setCharacters = setCharacters_freeIf (/= nullPtr)
getCharacters :: Ptr (Ptr CChar) -> IO String
getCharacters variable = do
ptr <- peek variable
if ptr == nullPtr then return "" else do
cs <- peekArray0 0 ptr
return (map castCCharToChar cs)