00001
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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
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" );
00129 TokenizerType Tokenizer( Source, Separators );
00130 std::string SingleToken;
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
00143 char ** UserCompletion( const char * text, int start, int end )
00144 {
00145
00146 rl_attempted_completion_over = 1;
00147
00148 if ( Completions.empty() ) return NULL;
00149
00150
00151 std::string PreInput( rl_line_buffer, start );
00152 SplitTokens( PreInput, Tokens );
00153
00154
00155
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
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
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
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
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
00226
00227
00228
00229
00230
00231 throw std::runtime_error( "Error selecting a keymap." );
00232 }
00233
00234 (Set->second)[ Key ]( Count, Key );
00235 return 0;
00236 }
00237
00238
00239
00240 int StartupHook( void )
00241 {
00242 if ( KeymapWasSetup )
00243 {
00244 rl_set_keymap( Earlykeymap );
00245 }
00246 return 0;
00247 }
00248
00249 }
00250
00251
00252
00253
00257 namespace swift
00258 {
00259
00264 class SKeymap
00265 {
00266 private:
00267 Keymap keymap;
00268
00269 public:
00274 explicit SKeymap( bool PrintableBound = false ) :
00275 keymap( NULL )
00276 {
00277 if ( PrintableBound )
00278 {
00279 keymap = rl_make_keymap();
00280 }
00281 else
00282 {
00283 keymap = rl_make_bare_keymap();
00284 }
00285
00286 if ( keymap == NULL )
00287 {
00288 throw std::runtime_error( "Cannot allocate keymap." );
00289 }
00290
00291
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
00307 Keymaps[ keymap ] = KeysBind();
00308 }
00309
00312 ~SKeymap()
00313 {
00314
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
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
00345
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
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 };
00630
00631 #endif
00632