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 <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
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 ),
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
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
00687 const size_t HistoryLimit;
00688
00692 const std::string HistoryFileName;
00693
00697 rl_completion_func_t* OriginalCompletion;
00698 };
00699
00700 };
00701
00702 #endif
00703