StdAir Logo  0.45.1
C++ Standard Airline IT Object Library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
SReadline.hpp
Go to the documentation of this file.
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