3 @brief C++ wrapper around libreadline.
4 Supported: editing, history, custom completers, keymaps.
5 Attention: implementation is not thread safe!
6 It is mainly because the readline library provides
7 pure C interface and has many calls for an "atomic"
11 // Date: 17 December 2005
16 // Copyright (c) Sergey Satskiy 2005 - 2006
17 // <sergesatsky@yahoo.com>
19 // Permission to copy, use, modify, sell and distribute this software
20 // is granted provided this copyright notice appears in all copies.
21 // This software is provided "as is" without express or implied
22 // warranty, and with no claim as to its suitability for any purpose.
30 #include <readline/readline.h>
31 #include <readline/history.h>
32 #include <readline/keymaps.h>
40 #include <boost/algorithm/string/trim.hpp>
41 #include <boost/tokenizer.hpp>
42 #include <boost/function.hpp>
45 /*! @brief Used to avoid linking errors in case
46 of including into many compilation units
50 //! @brief Tokens in a single variation of a user command
51 typedef std::vector< std::string > TokensStorage;
52 //! @brief Set of variations of user commands
53 typedef std::vector< TokensStorage > CompletionsStorage;
54 //! @brief Pressed key callback. Must return 0 if OK, != 0 otherwise
55 typedef boost::function< int ( int, int ) > KeyCallback;
56 //! @brief A set of keys binding
57 typedef std::map< int, KeyCallback > KeysBind;
60 const size_t DefaultHistoryLimit( 64 ); //!< Default value for the history length
61 CompletionsStorage Completions; //!< Global storage of custom completions
62 TokensStorage Tokens; //!< Tokens storage for a single completion session
63 std::map< Keymap, KeysBind > Keymaps; //!< Global storage for keymaps
65 bool KeymapWasSetup( false ); //!< Has sense if a keymap was
66 //!< setup before the first readline call
67 Keymap Earlykeymap( 0 ); //!< The keymap which was setup before the first readline call
70 /*! @brief Custom completion generator
71 @param text Pointer to a token to be completed
72 @param State 0 for a first call, non 0 for all consequent calls
74 char * Generator( const char * text, int State );
77 /*! @brief The function is called before trying to complete a token
78 @param text A token to be completed
79 @param start Index of the beginning of the token in the readline buffer
80 @param end Index of the end of the token in the readline buffer
82 char ** UserCompletion( const char * text,
87 /*! @brief The function selects the set of bindings and
88 makes the corresponding call.
89 @param Count The parameter is passed by readline
90 @param Key The pressed key
92 int KeyDispatcher( int Count, int Key );
95 /*! @brief The readline startup hook. It is required to setup the proper keymap.
97 int StartupHook( void );
100 /*! @brief Compares all the Input tokens with starts tokens in the Pattern
101 @param Pattern pattern tokens
102 @param Input user input tokens
103 @return true if first Input.size() tokens are equal to the pattern tokens
105 template < typename Container >
106 bool AreTokensEqual( const Container & Pattern,
107 const Container & Input )
109 if ( Input.size() > Pattern.size() ) return false;
111 typename Container::const_iterator k( Pattern.begin() );
112 typename Container::const_iterator j( Input.begin() );
113 for ( ; j != Input.end(); ++k, ++j )
115 if ( *k == "%file" ) continue;
116 if ( *k != *j ) return false;
122 // See description near the prototype
123 template < typename ContainerType >
124 void SplitTokens( const std::string & Source, ContainerType & Container )
126 typedef boost::tokenizer< boost::char_separator< char > > TokenizerType;
128 boost::char_separator<char> Separators( " \t\n" ); // Set of token separators
129 TokenizerType Tokenizer( Source, Separators ); // Tokens provider
130 std::string SingleToken; // Temporary storage for a token
133 for ( TokenizerType::const_iterator k( Tokenizer.begin() ); k != Tokenizer.end(); ++k )
136 boost::algorithm::trim( SingleToken );
137 Container.push_back( SingleToken );
142 // See description near the prototype
143 char ** UserCompletion( const char * text, int start, int /*end*/ )
145 // No default completion at all
146 rl_attempted_completion_over = 1;
148 if ( Completions.empty() ) return NULL;
150 // Memorize all the previous tokens
151 std::string PreInput( rl_line_buffer, start );
152 SplitTokens( PreInput, Tokens );
155 // Detect whether we should call the standard file name completer or a custom one
156 bool FoundPretender( false );
158 for ( CompletionsStorage::const_iterator k( Completions.begin() ); k != Completions.end(); ++k )
160 if ( ! AreTokensEqual( *k, Tokens ) ) continue;
162 if ( (*k).size() > Tokens.size() )
164 FoundPretender = true;
165 if ( (*k)[ Tokens.size() ] == "%file" )
167 // Standard file name completer - called for the "%file" keyword
168 return rl_completion_matches( text, rl_filename_completion_function );
173 if ( FoundPretender )
175 return rl_completion_matches( text, Generator );
182 // See description near the prototype
183 char * Generator( const char * text, int State )
186 static CompletionsStorage::const_iterator Iterator;
191 Iterator = Completions.begin();
192 Length = strlen( text );
195 for ( ; Iterator != Completions.end(); ++Iterator )
197 if ( ! AreTokensEqual( *Iterator, Tokens ) ) continue;
199 if ( (*Iterator).size() > Tokens.size() )
201 if ( (*Iterator)[ Tokens.size() ] == "%file" ) continue;
204 if ( strncmp( text, (*Iterator)[ Tokens.size() ].c_str(), Length ) == 0 )
206 // readline will free the allocated memory
207 char * NewString( (char*)malloc( strlen( (*Iterator)[ Tokens.size() ].c_str() ) + 1 ) );
208 strcpy( NewString, (*Iterator)[ Tokens.size() ].c_str() );
219 // See the description near the prototype
220 int KeyDispatcher( int Count, int Key )
222 std::map< Keymap, KeysBind >::iterator Set( Keymaps.find( rl_get_keymap() ) );
223 if ( Set == Keymaps.end() )
225 // Most probably it happens bacause the header was
226 // included into many compilation units and the
227 // keymap setting calls were made in different files.
228 // This is the problem of "global" data.
229 // The storage of all the registered keymaps is in anonymous
231 throw std::runtime_error( "Error selecting a keymap." );
234 (Set->second)[ Key ]( Count, Key );
239 // See the description near the prototype
240 int StartupHook( void )
242 if ( KeymapWasSetup )
244 rl_set_keymap( Earlykeymap );
249 } // Anonymous namespace
254 /*! @brief The wrapper namespace.
255 The namespace is also used for other library elements.
260 /*! @brief The readline keymap wrapper.
261 Attention: It is not thread safe!
262 Supports: key binding, key unbinding
267 Keymap keymap; // Readline keymap
270 /*! @brief Creates a new keymap
271 @param PrintableBound if true - the printable characters are bound
272 if false - the keymap is empty
274 explicit SKeymap( bool PrintableBound = false ) :
277 if ( PrintableBound )
279 keymap = rl_make_keymap(); // Printable characters are bound
283 keymap = rl_make_bare_keymap(); // Empty keymap
286 if ( keymap == NULL )
288 throw std::runtime_error( "Cannot allocate keymap." );
291 // Register a new keymap in the global list
292 Keymaps[ keymap ] = KeysBind();
295 /*! @brief Creates a new keymap which is a copy of Pattern
296 @param Pattern A keymap to be copied
298 explicit SKeymap( Keymap Pattern ) :
299 keymap( rl_copy_keymap( Pattern ) )
301 if ( keymap == NULL )
303 throw std::runtime_error( "Cannot allocate keymap." );
306 // Register a new keymap in the global list
307 Keymaps[ keymap ] = KeysBind();
310 /*! @brief Frees the allocated keymap
314 // Deregister the keymap
315 Keymaps.erase( keymap );
316 rl_discard_keymap( keymap );
319 /*! @brief Binds the given key to a function
320 @param Key A key to be bound
321 @param Callback A function to be called when the Key is pressed
323 void Bind( int Key, KeyCallback Callback )
325 Keymaps[ keymap ][ Key ] = Callback;
326 if ( rl_bind_key_in_map( Key, KeyDispatcher, keymap ) != 0 )
328 // Remove from the map just bound key
329 Keymaps[ keymap ].erase( Key );
330 throw std::runtime_error( "Invalid key." );
335 /*! @brief Unbinds the given key
336 @param Key A key to be unbound
338 void Unbind( int Key )
340 rl_unbind_key_in_map( Key, keymap );
341 Keymaps[ keymap ].erase( Key );
344 // void Bind( const std::string & Sequence, boost::function< int ( int, int ) > );
345 // void Unbind( std::string & Sequence );
348 /*! @brief Copy constructor
349 @param rhs Right hand side object of SKeymap
351 SKeymap( const SKeymap & rhs ) : keymap(0)
357 keymap = rl_copy_keymap( rhs.keymap );
361 @param rhs Right hand side object of SKeymap
363 SKeymap & operator=( const SKeymap & rhs )
369 keymap = rl_copy_keymap( rhs.keymap );
373 friend class SReadline;
377 /*! @brief The readline library wrapper.
378 Attention: It is not thread safe!
379 Supports: editing, history, custom completers
384 /*! @brief Constructs the object, sets the completion function
385 @param Limit History size
387 SReadline( const size_t Limit = DefaultHistoryLimit ) :
388 HistoryLimit( Limit ),
389 HistoryFileName( "" ),
390 OriginalCompletion( rl_attempted_completion_function )
392 rl_startup_hook = StartupHook;
393 rl_attempted_completion_function = UserCompletion;
397 /*! @brief Constructs the object, sets the completion function, loads history
398 @param historyFileName File name to load history from
399 @param Limit History size
401 SReadline( const std::string & historyFileName,
402 const size_t Limit = DefaultHistoryLimit ) :
403 HistoryLimit( Limit ),
404 HistoryFileName( historyFileName ),
405 OriginalCompletion( rl_attempted_completion_function )
407 rl_startup_hook = StartupHook;
408 rl_attempted_completion_function = UserCompletion;
410 LoadHistory( HistoryFileName );
413 /*! @brief Saves the session history (if the file name was provided)
414 and destroys the object
418 rl_attempted_completion_function = OriginalCompletion;
419 SaveHistory( HistoryFileName );
422 /*! @brief Gets a single line from a user
423 @param Prompt A printed prompt
424 @return A string which was actually inputed
426 std::string GetLine( const std::string & Prompt )
429 return GetLine( Prompt, Unused );
433 /*! @brief Gets a single line from a user
434 @param Prompt A printed prompt
435 @param ReadTokens A user inputed string splitted into tokens.
436 The container is cleared first
437 @return A string which was actually inputed
439 template < typename Container >
440 std::string GetLine( const std::string & Prompt,
441 Container & ReadTokens )
444 return GetLine( Prompt, ReadTokens, Unused );
448 /*! @brief Gets a single line from a user
449 @param Prompt A printed prompt
450 @param BreakOut it is set to true if the EOF found
451 @param ReadTokens A user inputed string splitted into tokens.
452 The container is cleared first
453 @return A string which was actually inputed
455 template < typename Container >
456 std::string GetLine( const std::string & Prompt,
457 Container & ReadTokens,
460 std::string Input( GetLine( Prompt, BreakOut ) );
461 SplitTokens( Input, ReadTokens );
466 /*! @brief Gets a single line from a user
467 @param Prompt A printed prompt
468 @param BreakOut it is set to true if the EOF found
469 @return A string which was actually inputed
471 std::string GetLine( const std::string & Prompt,
476 char * ReadLine( readline( Prompt.c_str() ) );
477 if ( ReadLine == NULL ) return std::string();
481 std::string Input( ReadLine );
484 boost::algorithm::trim( Input );
485 if ( !Input.empty() )
487 if ( (history_length == 0) ||
488 (Input != history_list()[ history_length - 1 ]->line)
491 add_history( Input.c_str() );
492 if ( history_length >= static_cast< int >( HistoryLimit ) )
494 stifle_history( HistoryLimit );
503 /*! @brief Fills the given container with the current history list
504 Does not clear the given container
506 template < typename ContainerType >
507 void GetHistory( ContainerType & Container )
509 for ( int k( 0 ); k < history_length; ++k )
511 Container.push_back( history_list()[ k ]->line );
515 /*! @brief Saves the history to the given file stream
516 @param OS output file stream
517 @return true if success
519 bool SaveHistory( std::ostream & OS )
521 if ( ! OS ) return false;
522 for ( int k( 0 ); k < history_length; ++k )
524 OS << history_list()[ k ]->line << std::endl;
529 /*! @brief Saves the history to the given file
530 @param FileName File name to save the history to
531 @return true if success
533 bool SaveHistory( const std::string & FileName )
535 if ( FileName.empty() ) return false;
537 std::ofstream OS( FileName.c_str() );
538 return SaveHistory( OS );
541 /*! @brief Clears the history. Does not affect the file where
542 the previous session history is saved.
550 /*! @brief Loads a history from a file stream
551 @param IS Input file stream
552 @return true if success
554 bool LoadHistory( std::istream & IS )
556 if ( ! IS ) return false;
561 while ( ! getline( IS, OneLine ).eof() )
563 boost::algorithm::trim( OneLine );
564 if ( (history_length == 0) ||
565 (OneLine != history_list()[ history_length - 1 ]->line)
568 add_history( OneLine.c_str() );
571 stifle_history( HistoryLimit );
575 /*! @brief Loads a history from the given file
576 @param FileName File name to be load from
577 @return true if success
579 bool LoadHistory( const std::string & FileName )
581 if ( FileName.empty() ) return false;
583 std::ifstream IS( FileName.c_str() );
584 return LoadHistory( IS );
587 /*! @brief Allows to register custom completers.
588 Supports a special keyword: %file. It means to use the standard file name completer
589 For example the given container elements could be as follows:
594 Each container element must describe a single possible command line
595 The container element must have a conversion to std::string operator
596 @param Container A container which has all the user possible commands
598 template < typename ContainerType >
599 void RegisterCompletions( const ContainerType & Container )
602 for ( typename ContainerType::const_iterator k( Container.begin() );
603 k != Container.end(); ++k )
605 std::vector< std::string > OneLine;
606 SplitTokens( (*k).get<0>(), OneLine );
607 Completions.push_back( OneLine );
612 /*! @brief Sets the given keymap
613 @param NewKeymap The keymap that should be used from now
615 void SetKeymap( SKeymap & NewKeymap )
617 rl_set_keymap( NewKeymap.keymap );
618 KeymapWasSetup = true;
619 Earlykeymap = NewKeymap.keymap;
623 const size_t HistoryLimit; //!< Constructor accepted or default
624 const std::string HistoryFileName; //!< Original constructor accepted
625 rl_completion_func_t * OriginalCompletion; //!< Original state will be restored