00001
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #ifndef SREADLINE_H
00027 #define SREADLINE_H
00028
00029 #include <cstdio>
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
00168 template <typename ContainerType>
00169 void SplitTokens (const std::string& Source, ContainerType& Container) {
00170 typedef boost::tokenizer<boost::char_separator<char> > TokenizerType;
00171
00172
00173 boost::char_separator<char> Separators (" \t\n");
00174
00175 TokenizerType Tokenizer (Source, Separators);
00176
00177 Container.clear();
00178 for (TokenizerType::const_iterator k (Tokenizer.begin());
00179 k != Tokenizer.end(); ++k) {
00180
00181 std::string SingleToken (*k);
00182
00183 boost::algorithm::trim (SingleToken);
00184 Container.push_back (SingleToken);
00185 }
00186 }
00187
00188
00189 char** UserCompletion (const char* text, int start, int end) {
00190
00191 rl_attempted_completion_over = 1;
00192
00193 if (Completions.empty() == true) {
00194 return NULL;
00195 }
00196
00197
00198 std::string PreInput (rl_line_buffer, start);
00199 SplitTokens (PreInput, Tokens);
00200
00201
00202
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
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
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
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
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
00271
00272
00273
00274
00275
00276 throw std::runtime_error ("Error selecting a keymap.");
00277 }
00278
00279 (Set->second)[Key] (Count, Key);
00280 return 0;
00281 }
00282
00283
00284 int StartupHook (void) {
00285 if (KeymapWasSetup) {
00286 rl_set_keymap (Earlykeymap);
00287 }
00288 return 0;
00289 }
00290
00291 }
00292
00293
00299 namespace swift {
00300
00307 class SKeymap {
00308 private:
00309
00310 Keymap keymap;
00311
00312 public:
00319 explicit SKeymap (bool PrintableBound = false) : keymap (NULL) {
00320 if (PrintableBound == true) {
00321
00322 keymap = rl_make_keymap();
00323
00324 } else {
00325
00326 keymap = rl_make_bare_keymap();
00327 }
00328
00329 if (keymap == NULL) {
00330 throw std::runtime_error ("Cannot allocate keymap.");
00331 }
00332
00333
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
00348 Keymaps [keymap] = KeysBind();
00349 }
00350
00354 ~SKeymap() {
00355
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
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
00387
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), HistoryFileName (historyFileName),
00449 OriginalCompletion (rl_attempted_completion_function) {
00450 rl_startup_hook = StartupHook;
00451 rl_attempted_completion_function = UserCompletion;
00452 using_history();
00453 LoadHistory (HistoryFileName);
00454 }
00455
00460 ~SReadline() {
00461 rl_attempted_completion_function = OriginalCompletion;
00462 SaveHistory (HistoryFileName);
00463 }
00464
00471 std::string GetLine (const std::string& Prompt) {
00472 bool Unused;
00473 return GetLine (Prompt, Unused);
00474 }
00475
00484 template <typename Container>
00485 std::string GetLine (const std::string& Prompt, Container& ReadTokens) {
00486 bool Unused;
00487 return GetLine (Prompt, ReadTokens, Unused);
00488 }
00489
00499 template <typename Container>
00500 std::string GetLine (const std::string& Prompt, Container& ReadTokens,
00501 bool& BreakOut) {
00502 std::string Input (GetLine (Prompt, BreakOut));
00503 SplitTokens (Input, ReadTokens);
00504 return Input;
00505 }
00506
00507
00515 std::string GetLine (const std::string& Prompt, bool& BreakOut) {
00516 BreakOut = true;
00517
00518 char* ReadLine (readline (Prompt.c_str()));
00519 if (ReadLine == NULL) {
00520 return std::string();
00521 }
00522
00523
00524 BreakOut = false;
00525 std::string Input (ReadLine);
00526 free (ReadLine); ReadLine = NULL;
00527
00528 boost::algorithm::trim (Input);
00529 if (Input.empty() == false) {
00530 if (history_length == 0
00531 || Input != history_list()[ history_length - 1 ]->line) {
00532 add_history (Input.c_str());
00533
00534 if (history_length >= static_cast<int> (HistoryLimit)) {
00535 stifle_history (HistoryLimit);
00536 }
00537 }
00538 }
00539
00540 return Input;
00541 }
00542
00543
00549 template <typename ContainerType>
00550 void GetHistory (ContainerType& Container) {
00551 for (int k (0); k < history_length; ++k ) {
00552 Container.push_back (history_list()[k]->line);
00553 }
00554 }
00555
00562 bool SaveHistory (std::ostream& OS) {
00563 if (!OS) {
00564 return false;
00565 }
00566
00567 for (int k (0); k < history_length; ++k) {
00568 OS << history_list()[ k ]->line << std::endl;
00569 }
00570 return true;
00571 }
00572
00579 bool SaveHistory (const std::string& FileName) {
00580 if (FileName.empty() == true) {
00581 return false;
00582 }
00583
00584 std::ofstream OS (FileName.c_str());
00585 return SaveHistory (OS);
00586 }
00587
00592 void ClearHistory() {
00593 clear_history();
00594 }
00595
00602 bool LoadHistory (std::istream& IS) {
00603 if (!IS) {
00604 return false;
00605 }
00606
00607 ClearHistory();
00608 std::string OneLine;
00609
00610 while (!getline (IS, OneLine).eof()) {
00611 boost::algorithm::trim( OneLine );
00612 if ((history_length == 0)
00613 || OneLine != history_list()[history_length - 1]->line) {
00614 add_history (OneLine.c_str());
00615 }
00616 }
00617 stifle_history (HistoryLimit);
00618 return true;
00619 }
00620
00627 bool LoadHistory (const std::string& FileName) {
00628 if (FileName.empty() == true) {
00629 return false;
00630 }
00631
00632 std::ifstream IS (FileName.c_str());
00633 return LoadHistory (IS);
00634 }
00635
00655 template <typename ContainerType>
00656 void RegisterCompletions (const ContainerType& Container) {
00657 Completions.clear();
00658 for (typename ContainerType::const_iterator k (Container.begin());
00659 k != Container.end(); ++k) {
00660 std::vector<std::string> OneLine;
00661 const std::string& kStr = static_cast<std::string> (*k);
00662
00663 SplitTokens (kStr, OneLine);
00664 Completions.push_back (OneLine);
00665 }
00666 }
00667
00673 void SetKeymap (SKeymap& NewKeymap) {
00674 rl_set_keymap (NewKeymap.keymap);
00675 KeymapWasSetup = true;
00676 Earlykeymap = NewKeymap.keymap;
00677 }
00678
00679
00680 private:
00681
00685 const size_t HistoryLimit;
00686
00690 const std::string HistoryFileName;
00691
00695 rl_completion_func_t* OriginalCompletion;
00696 };
00697
00698 };
00699
00700 #endif
00701