$treeview $search $mathjax
00001 00011 // 00012 // Date: 17 December 2005 00013 // 03 April 2006 00014 // 20 April 2006 00015 // 07 May 2006 00016 // 00017 // Copyright (c) Sergey Satskiy 2005 - 2006 00018 // <sergesatsky@yahoo.com> 00019 // 00020 // Permission to copy, use, modify, sell and distribute this software 00021 // is granted provided this copyright notice appears in all copies. 00022 // This software is provided "as is" without express or implied 00023 // warranty, and with no claim as to its suitability for any purpose. 00024 // 00025 00026 #ifndef SREADLINE_H 00027 #define SREADLINE_H 00028 00029 #include <stdio.h> 00030 00031 #include <readline/readline.h> 00032 #include <readline/history.h> 00033 #include <readline/keymaps.h> 00034 00035 #include <string> 00036 #include <fstream> 00037 #include <vector> 00038 #include <stdexcept> 00039 #include <map> 00040 00041 #include <boost/algorithm/string/trim.hpp> 00042 #include <boost/tokenizer.hpp> 00043 #include <boost/function.hpp> 00044 00045 00050 namespace { 00054 typedef std::vector<std::string> TokensStorage; 00055 00059 typedef std::vector<TokensStorage> CompletionsStorage; 00060 00064 typedef boost::function<int (int, int)> KeyCallback; 00065 00069 typedef std::map<int, KeyCallback> KeysBind; 00070 00074 const size_t DefaultHistoryLimit (64); 00075 00079 CompletionsStorage Completions; 00080 00084 TokensStorage Tokens; 00085 00089 std::map<Keymap, KeysBind> Keymaps; 00090 00094 bool KeymapWasSetup (false); 00095 00099 Keymap Earlykeymap (0); 00100 00101 00108 char* Generator (const char* text, int State); 00109 00110 00118 char** UserCompletion (const char* text, int start, int end); 00119 00120 00128 int KeyDispatcher (int Count, int Key); 00129 00130 00135 int StartupHook (void); 00136 00137 00145 template <typename Container> 00146 bool AreTokensEqual (const Container& Pattern, const Container& Input) { 00147 if (Input.size() > Pattern.size()) { 00148 return false; 00149 } 00150 00151 typename Container::const_iterator k (Pattern.begin()); 00152 typename Container::const_iterator j (Input.begin()); 00153 for ( ; j != Input.end(); ++k, ++j) { 00154 const std::string lPattern = *k; 00155 if (lPattern == "%file") { 00156 continue; 00157 } 00158 00159 const std::string lInput = *j; 00160 if (lPattern != lInput) { 00161 return false; 00162 } 00163 } 00164 return true; 00165 } 00166 00167 // See description near the prototype 00168 template <typename ContainerType> 00169 void SplitTokens (const std::string& Source, ContainerType& Container) { 00170 typedef boost::tokenizer<boost::char_separator<char> > TokenizerType; 00171 00172 // Set of token separators 00173 boost::char_separator<char> Separators (" \t\n"); 00174 // Tokens provider 00175 TokenizerType Tokenizer (Source, Separators); 00176 00177 Container.clear(); 00178 for (TokenizerType::const_iterator k (Tokenizer.begin()); 00179 k != Tokenizer.end(); ++k) { 00180 // Temporary storage for the token, in order to trim that latter 00181 std::string SingleToken (*k); 00182 00183 boost::algorithm::trim (SingleToken); 00184 Container.push_back (SingleToken); 00185 } 00186 } 00187 00188 // See description near the prototype 00189 char** UserCompletion (const char* text, int start, int end) { 00190 // No default completion at all 00191 rl_attempted_completion_over = 1; 00192 00193 if (Completions.empty() == true) { 00194 return NULL; 00195 } 00196 00197 // Memorise all the previous tokens 00198 std::string PreInput (rl_line_buffer, start); 00199 SplitTokens (PreInput, Tokens); 00200 00201 // Detect whether we should call the standard file name completer 00202 // or a custom one 00203 bool FoundPretender (false); 00204 00205 for (CompletionsStorage::const_iterator k (Completions.begin()); 00206 k != Completions.end(); ++k) { 00207 const TokensStorage& lTokenStorage = *k; 00208 if (AreTokensEqual (lTokenStorage, Tokens) == false) { 00209 continue; 00210 } 00211 00212 if (lTokenStorage.size() > Tokens.size()) { 00213 FoundPretender = true; 00214 if (lTokenStorage [Tokens.size()] == "%file") { 00215 // Standard file name completer - called for the "%file" keyword 00216 return rl_completion_matches (text, rl_filename_completion_function); 00217 } 00218 } 00219 } 00220 00221 if (FoundPretender) { 00222 return rl_completion_matches (text, Generator); 00223 } 00224 return NULL; 00225 } 00226 00227 // See description near the prototype 00228 char* Generator (const char* text, int State) { 00229 static int Length; 00230 static CompletionsStorage::const_iterator Iterator; 00231 00232 if ( State == 0 ) { 00233 Iterator = Completions.begin(); 00234 Length = strlen (text); 00235 } 00236 00237 for ( ; Iterator != Completions.end(); ++Iterator) { 00238 const TokensStorage& lCompletion = *Iterator; 00239 if (AreTokensEqual (lCompletion, Tokens) == false) { 00240 continue; 00241 } 00242 00243 if (lCompletion.size() > Tokens.size()) { 00244 if (lCompletion [Tokens.size()] == "%file") { 00245 continue; 00246 } 00247 00248 const char* lCompletionCharStr (lCompletion [Tokens.size()].c_str()); 00249 if (strncmp (text, lCompletionCharStr, Length) == 0) { 00250 // Readline will free the allocated memory 00251 const size_t lCompletionSize = strlen (lCompletionCharStr) + 1; 00252 char* NewString (static_cast<char*> (malloc (lCompletionSize))); 00253 strcpy (NewString, lCompletionCharStr); 00254 00255 ++Iterator; 00256 00257 return NewString; 00258 } 00259 } 00260 } 00261 00262 return NULL; 00263 } 00264 00265 00266 // See the description near the prototype 00267 int KeyDispatcher (int Count, int Key ) { 00268 std::map< Keymap, KeysBind >::iterator Set (Keymaps.find (rl_get_keymap())); 00269 if (Set == Keymaps.end()) { 00270 // Most probably it happens bacause the header was 00271 // included into many compilation units and the 00272 // keymap setting calls were made in different files. 00273 // This is the problem of "global" data. 00274 // The storage of all the registered keymaps is in anonymous 00275 // namespace. 00276 throw std::runtime_error ("Error selecting a keymap."); 00277 } 00278 00279 (Set->second)[Key] (Count, Key); 00280 return 0; 00281 } 00282 00283 // See the description near the prototype 00284 int StartupHook (void) { 00285 if (KeymapWasSetup) { 00286 rl_set_keymap (Earlykeymap); 00287 } 00288 return 0; 00289 } 00290 00291 } // Anonymous namespace 00292 00293 00299 namespace swift { 00300 00307 class SKeymap { 00308 private: 00309 // Readline keymap 00310 Keymap keymap; 00311 00312 public: 00319 explicit SKeymap (bool PrintableBound = false) : keymap (NULL) { 00320 if (PrintableBound == true) { 00321 // Printable characters are bound 00322 keymap = rl_make_keymap(); 00323 00324 } else { 00325 // Empty keymap 00326 keymap = rl_make_bare_keymap(); 00327 } 00328 00329 if (keymap == NULL) { 00330 throw std::runtime_error ("Cannot allocate keymap."); 00331 } 00332 00333 // Register a new keymap in the global list 00334 Keymaps [keymap] = KeysBind(); 00335 } 00336 00342 explicit SKeymap (Keymap Pattern) : keymap (rl_copy_keymap (Pattern)) { 00343 if ( keymap == NULL ) { 00344 throw std::runtime_error( "Cannot allocate keymap." ); 00345 } 00346 00347 // Register a new keymap in the global list 00348 Keymaps [keymap] = KeysBind(); 00349 } 00350 00354 ~SKeymap() { 00355 // Deregister the keymap 00356 Keymaps.erase (keymap); 00357 rl_discard_keymap (keymap); 00358 } 00359 00366 void Bind (int Key, KeyCallback Callback) { 00367 Keymaps [keymap][Key] = Callback; 00368 00369 if (rl_bind_key_in_map (Key, KeyDispatcher, keymap) != 0) { 00370 // Remove from the map just bound key 00371 Keymaps [keymap].erase (Key); 00372 throw std::runtime_error ("Invalid key."); 00373 } 00374 } 00375 00381 void Unbind (int Key) { 00382 rl_unbind_key_in_map (Key, keymap); 00383 Keymaps [keymap].erase (Key); 00384 } 00385 00386 // void Bind (const std::string& Sequence, boost::function<int (int, int)>); 00387 // void Unbind (std::string& Sequence); 00388 00389 public: 00395 SKeymap (const SKeymap& rhs) { 00396 if (this == &rhs) { 00397 return; 00398 } 00399 keymap = rl_copy_keymap (rhs.keymap); 00400 } 00401 00407 SKeymap& operator= (const SKeymap& rhs) { 00408 if (this == &rhs) { 00409 return *this; 00410 } 00411 keymap = rl_copy_keymap (rhs.keymap); 00412 return *this; 00413 } 00414 00415 friend class SReadline; 00416 }; 00417 00424 class SReadline { 00425 public: 00431 SReadline (const size_t Limit = DefaultHistoryLimit) : 00432 HistoryLimit (Limit), HistoryFileName (""), 00433 OriginalCompletion (rl_attempted_completion_function) { 00434 rl_startup_hook = StartupHook; 00435 rl_attempted_completion_function = UserCompletion; 00436 using_history(); 00437 } 00438 00446 SReadline( const std::string & historyFileName, 00447 const size_t Limit = DefaultHistoryLimit ) : 00448 HistoryLimit( Limit ), 00449 HistoryFileName( historyFileName ), 00450 OriginalCompletion( rl_attempted_completion_function ) 00451 { 00452 rl_startup_hook = StartupHook; 00453 rl_attempted_completion_function = UserCompletion; 00454 using_history(); 00455 LoadHistory( HistoryFileName ); 00456 } 00457 00462 ~SReadline() { 00463 rl_attempted_completion_function = OriginalCompletion; 00464 SaveHistory (HistoryFileName); 00465 } 00466 00473 std::string GetLine (const std::string& Prompt) { 00474 bool Unused; 00475 return GetLine (Prompt, Unused); 00476 } 00477 00486 template <typename Container> 00487 std::string GetLine (const std::string& Prompt, Container& ReadTokens) { 00488 bool Unused; 00489 return GetLine (Prompt, ReadTokens, Unused); 00490 } 00491 00501 template <typename Container> 00502 std::string GetLine (const std::string& Prompt, Container& ReadTokens, 00503 bool& BreakOut) { 00504 std::string Input (GetLine (Prompt, BreakOut)); 00505 SplitTokens (Input, ReadTokens); 00506 return Input; 00507 } 00508 00509 00517 std::string GetLine (const std::string& Prompt, bool& BreakOut) { 00518 BreakOut = true; 00519 00520 char* ReadLine (readline (Prompt.c_str())); 00521 if (ReadLine == NULL) { 00522 return std::string(); 00523 } 00524 00525 // It's OK 00526 BreakOut = false; 00527 std::string Input (ReadLine); 00528 free (ReadLine); ReadLine = NULL; 00529 00530 boost::algorithm::trim (Input); 00531 if (Input.empty() == false) { 00532 if (history_length == 0 00533 || Input != history_list()[ history_length - 1 ]->line) { 00534 add_history (Input.c_str()); 00535 00536 if (history_length >= static_cast<int> (HistoryLimit)) { 00537 stifle_history (HistoryLimit); 00538 } 00539 } 00540 } 00541 00542 return Input; 00543 } 00544 00545 00551 template <typename ContainerType> 00552 void GetHistory (ContainerType& Container) { 00553 for (int k (0); k < history_length; ++k ) { 00554 Container.push_back (history_list()[k]->line); 00555 } 00556 } 00557 00564 bool SaveHistory (std::ostream& OS) { 00565 if (!OS) { 00566 return false; 00567 } 00568 00569 for (int k (0); k < history_length; ++k) { 00570 OS << history_list()[ k ]->line << std::endl; 00571 } 00572 return true; 00573 } 00574 00581 bool SaveHistory (const std::string& FileName) { 00582 if (FileName.empty() == true) { 00583 return false; 00584 } 00585 00586 std::ofstream OS (FileName.c_str()); 00587 return SaveHistory (OS); 00588 } 00589 00594 void ClearHistory() { 00595 clear_history(); 00596 } 00597 00604 bool LoadHistory (std::istream& IS) { 00605 if (!IS) { 00606 return false; 00607 } 00608 00609 ClearHistory(); 00610 std::string OneLine; 00611 00612 while (!getline (IS, OneLine).eof()) { 00613 boost::algorithm::trim( OneLine ); 00614 if ((history_length == 0) 00615 || OneLine != history_list()[history_length - 1]->line) { 00616 add_history (OneLine.c_str()); 00617 } 00618 } 00619 stifle_history (HistoryLimit); 00620 return true; 00621 } 00622 00629 bool LoadHistory (const std::string& FileName) { 00630 if (FileName.empty() == true) { 00631 return false; 00632 } 00633 00634 std::ifstream IS (FileName.c_str()); 00635 return LoadHistory (IS); 00636 } 00637 00657 template <typename ContainerType> 00658 void RegisterCompletions (const ContainerType& Container) { 00659 Completions.clear(); 00660 for (typename ContainerType::const_iterator k (Container.begin()); 00661 k != Container.end(); ++k) { 00662 std::vector<std::string> OneLine; 00663 const std::string& kStr = static_cast<std::string> (*k); 00664 00665 SplitTokens (kStr, OneLine); 00666 Completions.push_back (OneLine); 00667 } 00668 } 00669 00675 void SetKeymap (SKeymap& NewKeymap) { 00676 rl_set_keymap (NewKeymap.keymap); 00677 KeymapWasSetup = true; 00678 Earlykeymap = NewKeymap.keymap; 00679 } 00680 00681 00682 private: 00683 // /////////////////////////// Attributes ///////////////////////// 00687 const size_t HistoryLimit; 00688 00692 const std::string HistoryFileName; 00693 00697 rl_completion_func_t* OriginalCompletion; 00698 }; 00699 00700 }; // namespace swift 00701 00702 #endif 00703