OpenTREP Logo  0.07.18
C++ Open Travel Request Parsing Library
Loading...
Searching...
No Matches
SReadline.hpp
Go to the documentation of this file.
1
11//
12// Date: 17 December 2005
13// 03 April 2006
14// 20 April 2006
15// 07 May 2006
16//
17// Copyright (c) Sergey Satskiy 2005 - 2006
18// <sergesatsky@yahoo.com>
19//
20// Permission to copy, use, modify, sell and distribute this software
21// is granted provided this copyright notice appears in all copies.
22// This software is provided "as is" without express or implied
23// warranty, and with no claim as to its suitability for any purpose.
24//
25
26#ifndef SREADLINE_H
27#define SREADLINE_H
28
29#include <stdio.h>
30
31#include <readline/readline.h>
32#include <readline/history.h>
33#include <readline/keymaps.h>
34
35#include <string>
36#include <fstream>
37#include <vector>
38#include <stdexcept>
39#include <map>
40
41#include <boost/algorithm/string/trim.hpp>
42#include <boost/tokenizer.hpp>
43#include <boost/function.hpp>
44
45
50namespace {
54 typedef std::vector<std::string> TokensStorage;
55
59 typedef std::vector<TokensStorage> CompletionsStorage;
60
64 typedef boost::function<int (int, int)> KeyCallback;
65
69 typedef std::map<int, KeyCallback> KeysBind;
70
74 const size_t DefaultHistoryLimit (64);
75
79 CompletionsStorage Completions;
80
84 TokensStorage Tokens;
85
89 std::map<Keymap, KeysBind> Keymaps;
90
94 bool KeymapWasSetup (false);
95
99 Keymap Earlykeymap (0);
100
101
108 char* Generator (const char* text, int State);
109
110
118 char** UserCompletion (const char* text, int start, int end);
119
120
128 int KeyDispatcher (int Count, int Key);
129
130
135 int StartupHook (void);
136
137
145 template <typename Container>
146 bool AreTokensEqual (const Container& Pattern, const Container& Input) {
147 if (Input.size() > Pattern.size()) {
148 return false;
149 }
150
151 typename Container::const_iterator k (Pattern.begin());
152 typename Container::const_iterator j (Input.begin());
153 for ( ; j != Input.end(); ++k, ++j) {
154 const std::string lPattern = *k;
155 if (lPattern == "%file") {
156 continue;
157 }
158
159 const std::string lInput = *j;
160 if (lPattern != lInput) {
161 return false;
162 }
163 }
164 return true;
165 }
166
167 // See description near the prototype
168 template <typename ContainerType>
169 void SplitTokens (const std::string& Source, ContainerType& Container) {
170 typedef boost::tokenizer<boost::char_separator<char> > TokenizerType;
171
172 // Set of token separators
173 boost::char_separator<char> Separators (" \t\n");
174 // Tokens provider
175 TokenizerType Tokenizer (Source, Separators);
176
177 Container.clear();
178 for (TokenizerType::const_iterator k (Tokenizer.begin());
179 k != Tokenizer.end(); ++k) {
180 // Temporary storage for the token, in order to trim that latter
181 std::string SingleToken (*k);
182
183 boost::algorithm::trim (SingleToken);
184 Container.push_back (SingleToken);
185 }
186 }
187
188 // See description near the prototype
189 char** UserCompletion (const char* text, int start, int end) {
190 // No default completion at all
191 rl_attempted_completion_over = 1;
192
193 if (Completions.empty() == true) {
194 return NULL;
195 }
196
197 // Memorise all the previous tokens
198 std::string PreInput (rl_line_buffer, start);
199 SplitTokens (PreInput, Tokens);
200
201 // Detect whether we should call the standard file name completer
202 // or a custom one
203 bool FoundPretender (false);
204
205 for (CompletionsStorage::const_iterator k (Completions.begin());
206 k != Completions.end(); ++k) {
207 const TokensStorage& lTokenStorage = *k;
208 if (AreTokensEqual (lTokenStorage, Tokens) == false) {
209 continue;
210 }
211
212 if (lTokenStorage.size() > Tokens.size()) {
213 FoundPretender = true;
214 if (lTokenStorage [Tokens.size()] == "%file") {
215 // Standard file name completer - called for the "%file" keyword
216 return rl_completion_matches (text, rl_filename_completion_function);
217 }
218 }
219 }
220
221 if (FoundPretender) {
222 return rl_completion_matches (text, Generator);
223 }
224 return NULL;
225 }
226
227 // See description near the prototype
228 char* Generator (const char* text, int State) {
229 static int Length;
230 static CompletionsStorage::const_iterator Iterator;
231
232 if ( State == 0 ) {
233 Iterator = Completions.begin();
234 Length = strlen (text);
235 }
236
237 for ( ; Iterator != Completions.end(); ++Iterator) {
238 const TokensStorage& lCompletion = *Iterator;
239 if (AreTokensEqual (lCompletion, Tokens) == false) {
240 continue;
241 }
242
243 if (lCompletion.size() > Tokens.size()) {
244 if (lCompletion [Tokens.size()] == "%file") {
245 continue;
246 }
247
248 const char* lCompletionCharStr (lCompletion [Tokens.size()].c_str());
249 if (strncmp (text, lCompletionCharStr, Length) == 0) {
250 // Readline will free the allocated memory
251 const size_t lCompletionSize = strlen (lCompletionCharStr) + 1;
252 char* NewString (static_cast<char*> (malloc (lCompletionSize)));
253 strcpy (NewString, lCompletionCharStr);
254
255 ++Iterator;
256
257 return NewString;
258 }
259 }
260 }
261
262 return NULL;
263 }
264
265
266 // See the description near the prototype
267 int KeyDispatcher (int Count, int Key ) {
268 std::map< Keymap, KeysBind >::iterator Set (Keymaps.find (rl_get_keymap()));
269 if (Set == Keymaps.end()) {
270 // Most probably it happens bacause the header was
271 // included into many compilation units and the
272 // keymap setting calls were made in different files.
273 // This is the problem of "global" data.
274 // The storage of all the registered keymaps is in anonymous
275 // namespace.
276 throw std::runtime_error ("Error selecting a keymap.");
277 }
278
279 (Set->second)[Key] (Count, Key);
280 return 0;
281 }
282
283 // See the description near the prototype
284 int StartupHook (void) {
285 if (KeymapWasSetup) {
286 rl_set_keymap (Earlykeymap);
287 }
288 return 0;
289 }
290
291} // Anonymous namespace
292
293
299namespace swift {
300
307 class SKeymap {
308 private:
309 // Readline keymap
310 Keymap keymap;
311
312 public:
319 explicit SKeymap (bool PrintableBound = false) : keymap (NULL) {
320 if (PrintableBound == true) {
321 // Printable characters are bound
322 keymap = rl_make_keymap();
323
324 } else {
325 // Empty keymap
326 keymap = rl_make_bare_keymap();
327 }
328
329 if (keymap == NULL) {
330 throw std::runtime_error ("Cannot allocate keymap.");
331 }
332
333 // Register a new keymap in the global list
334 Keymaps [keymap] = KeysBind();
335 }
336
342 explicit SKeymap (Keymap Pattern) : keymap (rl_copy_keymap (Pattern)) {
343 if ( keymap == NULL ) {
344 throw std::runtime_error( "Cannot allocate keymap." );
345 }
346
347 // Register a new keymap in the global list
348 Keymaps [keymap] = KeysBind();
349 }
350
355 // Deregister the keymap
356 Keymaps.erase (keymap);
357 rl_discard_keymap (keymap);
358 }
359
366 void Bind (int Key, KeyCallback Callback) {
367 Keymaps [keymap][Key] = Callback;
368
369 if (rl_bind_key_in_map (Key, KeyDispatcher, keymap) != 0) {
370 // Remove from the map just bound key
371 Keymaps [keymap].erase (Key);
372 throw std::runtime_error ("Invalid key.");
373 }
374 }
375
381 void Unbind (int Key) {
382 rl_unbind_key_in_map (Key, keymap);
383 Keymaps [keymap].erase (Key);
384 }
385
386 // void Bind (const std::string& Sequence, boost::function<int (int, int)>);
387 // void Unbind (std::string& Sequence);
388
389 public:
395 SKeymap (const SKeymap& rhs) {
396 if (this == &rhs) {
397 return;
398 }
399 keymap = rl_copy_keymap (rhs.keymap);
400 }
401
408 if (this == &rhs) {
409 return *this;
410 }
411 keymap = rl_copy_keymap (rhs.keymap);
412 return *this;
413 }
414
415 friend class SReadline;
416 };
417
424 class SReadline {
425 public:
431 SReadline (const size_t Limit = DefaultHistoryLimit) :
432 HistoryLimit (Limit), HistoryFileName (""),
433 OriginalCompletion (rl_attempted_completion_function) {
434 rl_startup_hook = StartupHook;
435 rl_attempted_completion_function = UserCompletion;
436 using_history();
437 }
438
446 SReadline( const std::string & historyFileName,
447 const size_t Limit = DefaultHistoryLimit ) :
448 HistoryLimit( Limit ),
449 HistoryFileName( historyFileName ),
450 OriginalCompletion( rl_attempted_completion_function )
451 {
452 rl_startup_hook = StartupHook;
453 rl_attempted_completion_function = UserCompletion;
454 using_history();
455 LoadHistory( HistoryFileName );
456 }
457
463 rl_attempted_completion_function = OriginalCompletion;
464 SaveHistory (HistoryFileName);
465 }
466
473 std::string GetLine (const std::string& Prompt) {
474 bool Unused;
475 return GetLine (Prompt, Unused);
476 }
477
486 template <typename Container>
487 std::string GetLine (const std::string& Prompt, Container& ReadTokens) {
488 bool Unused;
489 return GetLine (Prompt, ReadTokens, Unused);
490 }
491
501 template <typename Container>
502 std::string GetLine (const std::string& Prompt, Container& ReadTokens,
503 bool& BreakOut) {
504 std::string Input (GetLine (Prompt, BreakOut));
505 SplitTokens (Input, ReadTokens);
506 return Input;
507 }
508
509
517 std::string GetLine (const std::string& Prompt, bool& BreakOut) {
518 BreakOut = true;
519
520 char* ReadLine (readline (Prompt.c_str()));
521 if (ReadLine == NULL) {
522 return std::string();
523 }
524
525 // It's OK
526 BreakOut = false;
527 std::string Input (ReadLine);
528 free (ReadLine); ReadLine = NULL;
529
530 boost::algorithm::trim (Input);
531 if (Input.empty() == false) {
532 if (history_length == 0
533 || Input != history_list()[ history_length - 1 ]->line) {
534 add_history (Input.c_str());
535
536 if (history_length >= static_cast<int> (HistoryLimit)) {
537 stifle_history (HistoryLimit);
538 }
539 }
540 }
541
542 return Input;
543 }
544
545
551 template <typename ContainerType>
552 void GetHistory (ContainerType& Container) {
553 for (int k (0); k < history_length; ++k ) {
554 Container.push_back (history_list()[k]->line);
555 }
556 }
557
564 bool SaveHistory (std::ostream& OS) {
565 if (!OS) {
566 return false;
567 }
568
569 for (int k (0); k < history_length; ++k) {
570 OS << history_list()[ k ]->line << std::endl;
571 }
572 return true;
573 }
574
581 bool SaveHistory (const std::string& FileName) {
582 if (FileName.empty() == true) {
583 return false;
584 }
585
586 std::ofstream OS (FileName.c_str());
587 return SaveHistory (OS);
588 }
589
595 clear_history();
596 }
597
604 bool LoadHistory (std::istream& IS) {
605 if (!IS) {
606 return false;
607 }
608
609 ClearHistory();
610 std::string OneLine;
611
612 while (!getline (IS, OneLine).eof()) {
613 boost::algorithm::trim( OneLine );
614 if ((history_length == 0)
615 || OneLine != history_list()[history_length - 1]->line) {
616 add_history (OneLine.c_str());
617 }
618 }
619 stifle_history (HistoryLimit);
620 return true;
621 }
622
629 bool LoadHistory (const std::string& FileName) {
630 if (FileName.empty() == true) {
631 return false;
632 }
633
634 std::ifstream IS (FileName.c_str());
635 return LoadHistory (IS);
636 }
637
657 template <typename ContainerType>
658 void RegisterCompletions (const ContainerType& Container) {
659 Completions.clear();
660 for (typename ContainerType::const_iterator k (Container.begin());
661 k != Container.end(); ++k) {
662 std::vector<std::string> OneLine;
663 const std::string& kStr = static_cast<std::string> (*k);
664
665 SplitTokens (kStr, OneLine);
666 Completions.push_back (OneLine);
667 }
668 }
669
675 void SetKeymap (SKeymap& NewKeymap) {
676 rl_set_keymap (NewKeymap.keymap);
677 KeymapWasSetup = true;
678 Earlykeymap = NewKeymap.keymap;
679 }
680
681
682 private:
683 // /////////////////////////// Attributes /////////////////////////
687 const size_t HistoryLimit;
688
692 const std::string HistoryFileName;
693
697 rl_completion_func_t* OriginalCompletion;
698 };
699
700}; // namespace swift
701
702#endif
703
The readline keymap wrapper.
~SKeymap()
Frees the allocated keymap.
void Bind(int Key, KeyCallback Callback)
Binds the given key to a function.
SKeymap(bool PrintableBound=false)
Creates a new keymap.
friend class SReadline
SKeymap(Keymap Pattern)
Creates a new keymap which is a copy of Pattern.
SKeymap & operator=(const SKeymap &rhs)
operator=
void Unbind(int Key)
Unbinds the given key.
SKeymap(const SKeymap &rhs)
Copy constructor.
bool SaveHistory(const std::string &FileName)
Saves the history to the given file.
std::string GetLine(const std::string &Prompt, Container &ReadTokens)
Gets a single line from a user.
bool LoadHistory(const std::string &FileName)
Loads a history from the given file.
bool SaveHistory(std::ostream &OS)
Saves the history to the given file stream.
bool LoadHistory(std::istream &IS)
Loads a history from a file stream.
SReadline(const std::string &historyFileName, const size_t Limit=DefaultHistoryLimit)
Constructs the object, sets the completion function, loads history.
void SetKeymap(SKeymap &NewKeymap)
Sets the given keymap.
std::string GetLine(const std::string &Prompt, bool &BreakOut)
Gets a single line from a user.
void ClearHistory()
Clears the history. Does not affect the file where the previous session history is saved.
void GetHistory(ContainerType &Container)
Fills the given container with the current history list.
std::string GetLine(const std::string &Prompt)
Gets a single line from a user.
SReadline(const size_t Limit=DefaultHistoryLimit)
Constructs the object, sets the completion function.
void RegisterCompletions(const ContainerType &Container)
Allows to register custom completers.
~SReadline()
Saves the session history (if the file name was provided) and destroys the object.
std::string GetLine(const std::string &Prompt, Container &ReadTokens, bool &BreakOut)
Gets a single line from a user.
The wrapper namespace.