SReadline.h

Go to the documentation of this file.
00001 
00010 //
00011 // Date:      17 December 2005
00012 //            03 April    2006
00013 //            20 April    2006
00014 //            07 May      2006
00015 //
00016 // Copyright (c) Sergey Satskiy 2005 - 2006
00017 //               <sergesatsky@yahoo.com>
00018 //
00019 // Permission to copy, use, modify, sell and distribute this software 
00020 // is granted provided this copyright notice appears in all copies. 
00021 // This software is provided "as is" without express or implied
00022 // warranty, and with no claim as to its suitability for any purpose.
00023 //
00024 
00025 #ifndef SREADLINE_H
00026 #define SREADLINE_H
00027 
00028 #include <stdio.h>
00029 
00030 #include <readline/readline.h>
00031 #include <readline/history.h>
00032 #include <readline/keymaps.h>
00033 
00034 #include <string>
00035 #include <fstream>
00036 #include <vector>
00037 #include <stdexcept>
00038 #include <map>
00039 
00040 #include <boost/algorithm/string/trim.hpp>
00041 #include <boost/tokenizer.hpp>
00042 #include <boost/function.hpp>
00043 
00044 
00048 namespace
00049 {
00051     typedef std::vector< std::string >              TokensStorage;
00053     typedef std::vector< TokensStorage >            CompletionsStorage;
00055     typedef boost::function< int ( int, int ) >     KeyCallback;
00057     typedef std::map< int, KeyCallback >            KeysBind;
00058 
00059     
00060     const size_t                    DefaultHistoryLimit( 64 );  
00061     CompletionsStorage              Completions;                
00062     TokensStorage                   Tokens;                     
00063     std::map< Keymap, KeysBind >    Keymaps;                    
00064 
00065     bool                            KeymapWasSetup( false );    
00066 
00067     Keymap                          Earlykeymap( 0 );           
00068 
00069 
00074     char * Generator( const char *  text, int  State );
00075 
00076     
00082     char ** UserCompletion( const char * text,
00083                             int          start,
00084                             int          end );
00085 
00086 
00092     int  KeyDispatcher( int  Count, int  Key );
00093 
00094 
00097     int  StartupHook( void );
00098 
00099     
00105     template < typename  Container >
00106     bool AreTokensEqual( const Container &  Pattern, 
00107                          const Container &  Input )
00108     {
00109         if ( Input.size() > Pattern.size() ) return false;
00110 
00111         typename Container::const_iterator  k( Pattern.begin() );
00112         typename Container::const_iterator  j( Input.begin() );
00113         for ( ; j != Input.end(); ++k, ++j )
00114         {
00115             if ( *k == "%file" ) continue;
00116             if ( *k != *j )      return false;
00117         }
00118         return true;
00119     }
00120 
00121 
00122         // See description near the prototype
00123     template < typename  ContainerType >
00124     void SplitTokens( const std::string &  Source, ContainerType &  Container )
00125     {
00126         typedef boost::tokenizer< boost::char_separator< char > >       TokenizerType;
00127 
00128         boost::char_separator<char>     Separators( " \t\n" );              // Set of token separators
00129         TokenizerType                   Tokenizer( Source, Separators );    // Tokens provider
00130         std::string                     SingleToken;                        // Temporary storage for a token
00131         
00132         Container.clear();
00133         for ( TokenizerType::const_iterator  k( Tokenizer.begin() ); k != Tokenizer.end(); ++k )
00134         {
00135             SingleToken = *k;
00136             boost::algorithm::trim( SingleToken );
00137             Container.push_back( SingleToken );
00138         }
00139     }
00140 
00141     
00142         // See description near the prototype
00143     char ** UserCompletion( const char *  text, int  start, int  end )
00144     {
00145             // No default completion at all
00146         rl_attempted_completion_over = 1;
00147 
00148         if ( Completions.empty() )  return NULL;
00149 
00150             // Memorize all the previous tokens
00151         std::string     PreInput( rl_line_buffer, start );
00152         SplitTokens( PreInput, Tokens );
00153 
00154 
00155             // Detect whether we should call the standard file name completer or a custom one
00156         bool    FoundPretender( false );
00157 
00158         for ( CompletionsStorage::const_iterator  k( Completions.begin() ); k != Completions.end(); ++k )
00159         {
00160             if ( ! AreTokensEqual( *k, Tokens ) ) continue;
00161 
00162             if ( (*k).size() > Tokens.size() )
00163             {
00164                 FoundPretender = true;
00165                 if ( (*k)[ Tokens.size() ] == "%file" )
00166                 {
00167                         // Standard file name completer - called for the "%file" keyword
00168                     return rl_completion_matches( text, rl_filename_completion_function );
00169                 }
00170             }
00171         }
00172         
00173         if ( FoundPretender )
00174         {
00175             return rl_completion_matches( text, Generator );
00176         }
00177         return NULL;
00178     }
00179     
00180    
00181 
00182         // See description near the prototype
00183     char * Generator( const char *  text, int  State )
00184     {
00185         static int                                  Length;
00186         static CompletionsStorage::const_iterator   Iterator;
00187 
00188         
00189         if ( State == 0 )
00190         {
00191             Iterator = Completions.begin();
00192             Length = strlen( text );
00193         }
00194 
00195         for ( ; Iterator != Completions.end(); ++Iterator )
00196         {
00197             if ( ! AreTokensEqual( *Iterator, Tokens ) ) continue;
00198 
00199             if ( (*Iterator).size() > Tokens.size() )
00200             {
00201                 if ( (*Iterator)[ Tokens.size() ] == "%file" ) continue;
00202 
00203             
00204                 if ( strncmp( text, (*Iterator)[ Tokens.size() ].c_str(), Length ) == 0 )
00205                 {
00206                         // readline will free the allocated memory
00207                     char *  NewString( (char*)malloc( strlen( (*Iterator)[ Tokens.size() ].c_str() ) + 1 ) );
00208                     strcpy( NewString, (*Iterator)[ Tokens.size() ].c_str() );
00209                     ++Iterator;
00210                     return NewString;
00211                 }
00212             }
00213         }
00214        
00215         return NULL;
00216     }
00217 
00218     
00219         // See the description near the prototype
00220     int  KeyDispatcher( int  Count, int  Key )
00221     {
00222         std::map< Keymap, KeysBind >::iterator  Set( Keymaps.find( rl_get_keymap() ) );
00223         if ( Set == Keymaps.end() )
00224         {
00225                 // Most probably it happens bacause the header was 
00226                 // included into many compilation units and the
00227                 // keymap setting calls were made in different files.
00228                 // This is the problem of "global" data.
00229                 // The storage of all the registered keymaps is in anonymous
00230                 // namespace.
00231             throw std::runtime_error( "Error selecting a keymap." );
00232         }
00233 
00234         (Set->second)[ Key ]( Count, Key );
00235         return 0;
00236     }
00237 
00238 
00239         // See the description near the prototype
00240     int  StartupHook( void )
00241     {
00242         if ( KeymapWasSetup )
00243         {
00244             rl_set_keymap( Earlykeymap );
00245         }
00246         return 0;
00247     }
00248 
00249 }   // Anonymous namespace
00250 
00251 
00252 
00253 
00257 namespace swift 
00258 {
00259 
00264     class SKeymap
00265     {
00266         private:
00267             Keymap      keymap;     // Readline keymap
00268             
00269         public:
00274             explicit SKeymap( bool  PrintableBound = false ) : 
00275                 keymap( NULL )
00276             {
00277                 if ( PrintableBound )
00278                 {
00279                     keymap = rl_make_keymap();      // Printable characters are bound
00280                 }
00281                 else
00282                 {
00283                     keymap = rl_make_bare_keymap(); // Empty keymap
00284                 }
00285 
00286                 if ( keymap == NULL )
00287                 {
00288                     throw std::runtime_error( "Cannot allocate keymap." );
00289                 }
00290 
00291                     // Register a new keymap in the global list
00292                 Keymaps[ keymap ] = KeysBind();
00293             }
00294             
00298             explicit SKeymap( Keymap   Pattern ) :
00299                 keymap( rl_copy_keymap( Pattern ) )
00300             {
00301                 if ( keymap == NULL )
00302                 {
00303                     throw std::runtime_error( "Cannot allocate keymap." );
00304                 }
00305                 
00306                     // Register a new keymap in the global list
00307                 Keymaps[ keymap ] = KeysBind();
00308             }
00309 
00312             ~SKeymap()
00313             {
00314                     // Deregister the keymap
00315                 Keymaps.erase( keymap );
00316                 rl_discard_keymap( keymap );
00317             }
00318 
00323             void Bind( int  Key, KeyCallback  Callback )
00324             {
00325                 Keymaps[ keymap ][ Key ] = Callback;
00326                 if ( rl_bind_key_in_map( Key, KeyDispatcher, keymap ) != 0 )
00327                 {
00328                         // Remove from the map just bound key
00329                     Keymaps[ keymap ].erase( Key );
00330                     throw std::runtime_error( "Invalid key." );
00331                 }
00332             }
00333 
00334 
00338             void Unbind( int  Key )
00339             {
00340                 rl_unbind_key_in_map( Key, keymap );
00341                 Keymaps[ keymap ].erase( Key );
00342             }
00343 
00344             // void Bind( const std::string &  Sequence, boost::function< int ( int, int ) > );
00345             // void Unbind( std::string &  Sequence );
00346 
00347         public:
00351             SKeymap( const SKeymap &  rhs )
00352             {
00353                 if ( this == & rhs )
00354                 {
00355                     return;
00356                 }
00357                 keymap = rl_copy_keymap( rhs.keymap );
00358             }
00359 
00363             SKeymap &  operator=( const SKeymap &  rhs )
00364             {
00365                 if ( this == & rhs )
00366                 {
00367                     return *this;
00368                 }
00369                 keymap = rl_copy_keymap( rhs.keymap );
00370                 return *this;
00371             }
00372             
00373             friend class SReadline;
00374     };
00375     
00376 
00381     class SReadline
00382     {
00383         public:
00387             SReadline( const size_t  Limit = DefaultHistoryLimit ) : 
00388                 HistoryLimit( Limit ),
00389                 HistoryFileName( "" ), 
00390                 OriginalCompletion( rl_attempted_completion_function )
00391             {
00392                 rl_startup_hook = StartupHook;
00393                 rl_attempted_completion_function = UserCompletion;
00394                 using_history();
00395             }
00396             
00401             SReadline( const std::string &  historyFileName,
00402                        const size_t  Limit = DefaultHistoryLimit ) :
00403                 HistoryLimit( Limit ),
00404                 HistoryFileName( historyFileName ),
00405                 OriginalCompletion( rl_attempted_completion_function )
00406             {
00407                 rl_startup_hook = StartupHook;
00408                 rl_attempted_completion_function = UserCompletion;
00409                 using_history();
00410                 LoadHistory( HistoryFileName );
00411             }
00412 
00416             ~SReadline()
00417             {
00418                 rl_attempted_completion_function = OriginalCompletion;
00419                 SaveHistory( HistoryFileName );
00420             }
00421 
00426             std::string  GetLine( const std::string &  Prompt )
00427             {
00428                 bool    Unused;
00429                 return GetLine( Prompt, Unused );
00430             }
00431             
00432 
00439             template < typename  Container >
00440             std::string  GetLine( const std::string &  Prompt,
00441                                   Container &          ReadTokens )
00442             {
00443                 bool    Unused;
00444                 return GetLine( Prompt, ReadTokens, Unused );
00445             }
00446 
00447 
00455             template < typename  Container >
00456             std::string  GetLine( const std::string &  Prompt,
00457                                   Container &          ReadTokens,
00458                                   bool &               BreakOut )
00459             {
00460                 std::string     Input( GetLine( Prompt, BreakOut ) );
00461                 SplitTokens( Input, ReadTokens );
00462                 return Input;
00463             }
00464 
00465 
00471             std::string  GetLine( const std::string &  Prompt, 
00472                                   bool &               BreakOut )
00473             {
00474                 BreakOut = true;
00475 
00476                 char *  ReadLine( readline( Prompt.c_str() ) );
00477                 if ( ReadLine == NULL ) return std::string();
00478                 
00479                     // It's OK
00480                 BreakOut = false;
00481                 std::string     Input( ReadLine );
00482                 free( ReadLine );
00483 
00484                 boost::algorithm::trim( Input );
00485                 if ( !Input.empty() )
00486                 {
00487                     if ( (history_length == 0) ||
00488                          (Input != history_list()[ history_length - 1 ]->line)
00489                        )
00490                     {
00491                         add_history( Input.c_str() );
00492                         if ( history_length >= static_cast< int >( HistoryLimit ) )
00493                         {
00494                             stifle_history( HistoryLimit );
00495                         }
00496                     }
00497                 }
00498 
00499                 return Input;
00500             }
00501 
00502 
00506             template < typename ContainerType >
00507             void GetHistory( ContainerType &  Container )
00508             {
00509                 for ( int  k( 0 ); k < history_length; ++k )
00510                 {
00511                     Container.push_back( history_list()[ k ]->line );
00512                 }
00513             }
00514 
00519             bool SaveHistory( std::ostream & OS )
00520             {
00521                 if ( ! OS ) return false;
00522                 for ( int  k( 0 ); k < history_length; ++k )
00523                 {
00524                     OS << history_list()[ k ]->line << std::endl;
00525                 }
00526                 return true;
00527             }
00528 
00533             bool SaveHistory( const std::string &  FileName )
00534             {
00535                 if ( FileName.empty() ) return false;
00536 
00537                 std::ofstream  OS( FileName.c_str() );
00538                 return SaveHistory( OS );
00539             }
00540 
00544             void ClearHistory()
00545             {
00546                 clear_history();
00547             }
00548             
00549 
00554             bool LoadHistory( std::istream &  IS )
00555             {
00556                 if ( ! IS ) return false;
00557 
00558                 ClearHistory();
00559                 std::string     OneLine;
00560 
00561                 while ( ! getline( IS, OneLine ).eof() )
00562                 {
00563                     boost::algorithm::trim( OneLine );
00564                     if ( (history_length == 0) ||
00565                          (OneLine != history_list()[ history_length - 1 ]->line)
00566                        )
00567                     {
00568                         add_history( OneLine.c_str() );
00569                     }
00570                 }
00571                 stifle_history( HistoryLimit );
00572                 return true;
00573             }
00574             
00579             bool LoadHistory( const std::string &  FileName )
00580             {
00581                 if ( FileName.empty() ) return false;
00582 
00583                 std::ifstream   IS( FileName.c_str() );
00584                 return LoadHistory( IS );
00585             }
00586 
00598             template < typename  ContainerType >
00599             void RegisterCompletions( const ContainerType &  Container )
00600             {
00601                 Completions.clear();
00602                 for ( typename ContainerType::const_iterator  k( Container.begin() );
00603                       k != Container.end(); ++k )
00604                 {
00605                     std::vector< std::string >  OneLine;
00606                     SplitTokens( static_cast<std::string>(*k), OneLine );
00607                     Completions.push_back( OneLine );
00608                 }
00609             }
00610 
00611                 
00615             void  SetKeymap( SKeymap &  NewKeymap )
00616             {
00617                 rl_set_keymap( NewKeymap.keymap );
00618                 KeymapWasSetup = true;
00619                 Earlykeymap = NewKeymap.keymap;
00620             }
00621 
00622         private:
00623             const size_t            HistoryLimit;       
00624             const std::string       HistoryFileName;    
00625             rl_completion_func_t *  OriginalCompletion; 
00626     };
00627 
00628 
00629 }; // namespace swift
00630 
00631 #endif 
00632 

Generated on Mon May 8 21:58:24 2006 for SReadline - C++ Wrapper Around readline Library by  doxygen 1.4.5