source: trunk/FACT++/src/Readline.cc@ 13765

Last change on this file since 13765 was 13744, checked in by tbretz, 12 years ago
Little improvements to the help text.
File size: 38.1 KB
Line 
1// **************************************************************************
2/** @class Readline
3
4@brief C++ wrapper for GNU's readline library
5
6This class is meant as a C++ wrapper around GNU's readline library.
7Note that because readline uses a global namespace only one instance
8of this class can exist at a time. Instantiating a second object after
9a first one was deleted might show unexpected results.
10
11When the object is instantiated readline's history is read from a file.
12At destruction the history in memory is copied back to that file.
13The history file will be truncated to fMaxLines.
14
15By overloading the Readline class the function used for auto-completion
16can be overwritten.
17
18Simple example:
19
20\code
21
22 Readline rl("MyProg"); // will read the history from "MyProg.his"
23 while (1)
24 {
25 string txt = rl.Prompt("prompt> ");
26 if (txt=="quit)
27 break;
28
29 // ... do something ...
30
31 rl.AddHistory(txt);
32 }
33
34 // On destruction the history will be written to the file
35
36\endcode
37
38Simpler example (you need to implement the Process() function)
39
40\code
41
42 Readline rl("MyProg"); // will read the history from "MyProg.his"
43 rl.Run("prompt> ");
44
45 // On destruction the history will be written to the file
46
47\endcode
48
49@section References
50
51 - <A HREF="http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html">GNU Readline</A>
52
53 */
54// **************************************************************************
55#include "Readline.h"
56
57#include <sstream>
58#include <fstream>
59#include <iostream>
60
61#include <readline/readline.h>
62#include <readline/history.h>
63
64#include <boost/version.hpp>
65#include <boost/filesystem.hpp>
66
67#include "tools.h"
68#include "Time.h"
69
70using namespace std;
71
72Readline *Readline::This = 0;
73int Readline::fLabel = -3;
74std::string Readline::fScript;
75
76// --------------------------------------------------------------------------
77//
78//! Construct a Readline object. The constructor reads the history from a
79//! history file. The filename is compiled by adding ".his" to the
80//! supplied argument. The name oif the history file is stored in fName.
81//!
82//! Since readline has a global namespace, the creation of only one
83//! Readline instance is allowed.
84//!
85//! The provided program name is supplied to readline by means of
86//! rl_readline_name.
87//!
88//! Readlines default callback frunction for completions is redirected
89//! to CompletionImp which in turn will call Completion, which can be
90//! overwritten by the user.
91//!
92//! Bind some default key sequences like Page-up/-down for searching forward
93//! and backward in history.
94//!
95//! @param prgname
96//! The prefix of the history filename. Usually the program name, which
97//! can be initialized by argv[0].
98//
99Readline::Readline(const char *prgname) :
100 fMaxLines(500), fLine(0), fCompletion(0)
101{
102 if (This)
103 {
104 cout << "ERROR - Readline can only be instatiated once!" << endl;
105 exit(-1);
106 }
107
108 This = this;
109
110 // Alternative completion function
111 rl_attempted_completion_function = rl_ncurses_completion_function;
112
113 // Program name
114#if BOOST_VERSION < 104600
115 static const string fname = boost::filesystem::path(prgname).filename();
116#else
117 static const string fname = boost::filesystem::path(prgname).filename().string();
118#endif
119 rl_readline_name = fname.c_str();
120
121 // Compile filename for history file
122 fName = string(prgname)+".his";
123
124 // Read history file
125 if (read_history(fName.c_str()))
126 cout << "WARNING - Reading " << fName << ": " << strerror(errno) << endl;
127
128 fCommandLog.open(string(prgname)+".evt");
129
130 // Setup the readline callback which are needed to redirect
131 // the otuput properly to our ncurses panel
132 rl_getc_function = rl_ncurses_getc;
133 rl_startup_hook = rl_ncurses_startup;
134 rl_redisplay_function = rl_ncurses_redisplay;
135 rl_event_hook = rl_ncurses_event_hook;
136 rl_completion_display_matches_hook = rl_ncurses_completion_display;
137
138 // Bind delete, page up, page down
139 rl_bind_keyseq("\e[3~", rl_named_function("delete-char"));
140 rl_bind_keyseq("\e[5~", rl_named_function("history-search-backward"));
141 rl_bind_keyseq("\e[6~", rl_named_function("history-search-forward"));
142 rl_bind_keyseq("\033[1;3F", rl_named_function("kill-line"));
143 rl_bind_keyseq("\033[1;5D", rl_named_function("backward-word"));
144 rl_bind_keyseq("\033[1;5C", rl_named_function("forward-word"));
145 rl_bind_key(25, rl_named_function("kill-whole-line"));
146
147 //for (int i=0; i<10; i++) cout << (int)getchar() << endl;
148}
149
150// --------------------------------------------------------------------------
151//
152//! Writes the current history to the file with the name stored in fName.
153//! In addition the written file is truncated to fMaxLines to keep the
154//! file of a reasonable size. The number of lines fMaxLines can be set
155//! by SetMaxLines before the destructor is called. Setting fMaxLines
156//! to 0 or a negative value switches automatic truncation off.
157//
158Readline::~Readline()
159{
160 // Write current history to file
161 if (write_history(fName.c_str()))
162 cout << "WARNING - Write " << fName.c_str() << ": " << strerror(errno) << endl;
163
164 // Truncate file
165 if (fMaxLines>0 && history_truncate_file(fName.c_str(), fMaxLines))
166 cout << "WARNING - Truncate " << fName.c_str() << ": " << strerror(errno) << endl;
167}
168
169// --------------------------------------------------------------------------
170//
171//! This wraps the given readline function such that the output can be
172//! redirected from thr rl_outstream to the given C++ ostream.
173//!
174//! @param out
175//! The stream to which the output should be redirected.
176//!
177//! @param function
178//! Takes a function of type bool(*)() as argument
179//!
180//! @returns
181//! The return value of the function
182//
183bool Readline::RedirectionWrapper(ostream &out, bool (*function)())
184{
185 FILE *save = SetStreamOut(tmpfile());
186 const bool rc = function();
187 FILE *file = SetStreamOut(save);
188
189 const bool empty = ftell(file)==0;
190
191 rewind(file);
192
193 if (empty)
194 {
195 out << " <empty>" << endl;
196 fclose(file);
197 return rc;
198 }
199
200 while (1)
201 {
202 const int c = getc(file);
203 if (feof(file))
204 break;
205 out << (char)c;
206 }
207 out << endl;
208
209 fclose(file);
210
211 return rc;
212}
213
214// --------------------------------------------------------------------------
215//
216//! Redirected from rl_getc_function, calls Getc
217//
218int Readline::rl_ncurses_getc(FILE *f)
219{
220 return This->Getc(f);
221}
222
223// --------------------------------------------------------------------------
224//
225//! Redirected from rl_startup_hook, calls Startup.
226//! A function called just before readline prints the first prompt.
227//
228int Readline::rl_ncurses_startup()
229{
230 This->Startup();
231 return 0; // What is this for?
232}
233
234// --------------------------------------------------------------------------
235//
236//! Redirected from rl_redisplay_function, calls Redisplay.
237//! Readline will call indirectly to update the display with the current
238//! contents of the editing buffer.
239//
240void Readline::rl_ncurses_redisplay()
241{
242 This->Redisplay();
243}
244
245// --------------------------------------------------------------------------
246//
247//! Redirected from rl_event_hook, calls Update().
248//! A function called periodically when readline is waiting for
249//! terminal input.
250//!
251int Readline::rl_ncurses_event_hook()
252{
253 This->EventHook();
254 return 0;
255}
256
257// --------------------------------------------------------------------------
258//
259//! Redirected from rl_completion_display_matches_hook,
260//! calls CompletionDisplayImp
261//!
262//! A function to be called when completing a word would normally display
263//! the list of possible matches. This function is called in lieu of
264//! Readline displaying the list. It takes three arguments:
265//! (char **matches, int num_matches, int max_length) where matches is
266//! the array of matching strings, num_matches is the number of strings
267//! in that array, and max_length is the length of the longest string in
268//! that array. Readline provides a convenience function,
269//! rl_display_match_list, that takes care of doing the display to
270//! Readline's output stream.
271//
272void Readline::rl_ncurses_completion_display(char **matches, int num, int max)
273{
274 This->CompletionDisplay(matches, num, max);
275}
276
277char **Readline::rl_ncurses_completion_function(const char *text, int start, int end)
278{
279 return This->Completion(text, start, end);
280}
281
282// --------------------------------------------------------------------------
283//
284//! Calls the default rl_getc function.
285//
286int Readline::Getc(FILE *f)
287{
288 return rl_getc(f);
289}
290
291// --------------------------------------------------------------------------
292//
293//! Default: Do nothing.
294//
295void Readline::Startup()
296{
297}
298
299// --------------------------------------------------------------------------
300//
301//! The default is to redisplay the prompt which is gotten from
302//! GetUpdatePrompt(). If GetUpdatePrompt() returns an empty string the
303//! prompt is kept untouched. This can be used to keep a prompt updated
304//! with some information (e.g. time) just by overwriting GetUpdatePrompt()
305//!
306void Readline::EventHook()
307{
308 const string p = GetUpdatePrompt();
309 if (p.empty())
310 return;
311
312 UpdatePrompt("");
313 Redisplay();
314 UpdatePrompt(p);
315 Redisplay();
316}
317
318// --------------------------------------------------------------------------
319//
320//! Called from Prompt and PromptEOF after readline has returned. It is
321//! meant as the opposite of Startup (called after readline finsihes)
322//! The default is to do nothing.
323//!
324//! @param buf
325//! A pointer to the buffer returned by readline
326//
327void Readline::Shutdown(const char *)
328{
329}
330
331// --------------------------------------------------------------------------
332//
333//! Default: call rl_redisplay()
334//
335void Readline::Redisplay()
336{
337 rl_redisplay();
338}
339
340// --------------------------------------------------------------------------
341//
342//! Default: call rl_completion_display_matches()
343//
344void Readline::CompletionDisplay(char **matches, int num, int max)
345{
346 rl_display_match_list(matches, num, max);
347}
348
349// --------------------------------------------------------------------------
350//
351//! This is a static helper for the compilation of a completion-list.
352//! It compares the two inputs (str and txt) to a maximum of the size of
353//! txt. If they match, memory is allocated with malloc and a pointer to
354//! the null-terminated version of str is returned.
355//!
356//! @param str
357//! A reference to the string which is checked (e.g. "Makefile.am")
358//!
359//! @param txt
360//! A reference to the part of the string the user has already typed,
361//! e.g. "Makef"
362//!
363//! @returns
364//! A pointer to memory allocated with malloc containing the string str
365//
366char *Readline::Compare(const string &str, const string &txt)
367{
368 /*return strncmp(str.c_str(), txt.c_str(), txt.length())==0 ? */
369 return strncasecmp(str.c_str(), txt.c_str(), txt.length())==0 ?
370 strndup(str.c_str(), str.length()) : 0;
371}
372
373char **Readline::CompletionMatches(const char *text, char *(*func)(const char*, int))
374{
375 return rl_completion_matches(text, func);
376}
377
378// --------------------------------------------------------------------------
379//
380//! The given vector should be a reference to a vector of strings
381//! containing all possible matches. The actual match-making is then
382//! done in Complete(const char *, int)
383//!
384//! The pointer fCompletion is redirected to the vector for the run time
385//! of the function, but restored afterwards. So by this you can set a
386//! default completion list in case Complete is not called or Completion
387//! not overloaded.
388//!
389//! @param v
390//! reference to a vector of strings with all possible matches
391//!
392//! @param text
393//! the text which should be matched (it is just propagated to
394//! Readline::Completion)
395//!
396char **Readline::Complete(const vector<string> &v, const char *text)
397{
398 const vector<string> *save = fCompletion;
399
400 fCompletion = &v;
401 char **rc = rl_completion_matches(const_cast<char*>(text), CompleteImp);
402 fCompletion = save;
403
404 return rc;
405}
406
407// --------------------------------------------------------------------------
408//
409//! If fCompletion==0 the default is to call readline's
410//! rl_filename_completion_function. Otherwise the contents of fCompletion
411//! are returned. To change fCompletion either initialize it via
412//! SetCompletion() (in this case you must ensure the life time of the
413//! object) or call
414//! Complete(const vector<string>&, const char*)
415//! from
416//! Completion(const char * int, int)
417//!
418//! This is the so called generator function, the readline manual says
419//! about this function:
420//!
421//! The generator function is called repeatedly from
422//! rl_completion_matches(), returning a string each time. The arguments
423//! to the generator function are text and state. text is the partial word
424//! to be completed. state is zero the first time the function is called,
425//! allowing the generator to perform any necessary initialization, and a
426//! positive non-zero integer for each subsequent call. The generator
427//! function returns (char *)NULL to inform rl_completion_matches() that
428//! there are no more possibilities left. Usually the generator function
429//! computes the list of possible completions when state is zero, and
430//! returns them one at a time on subsequent calls. Each string the
431//! generator function returns as a match must be allocated with malloc();
432//! Readline frees the strings when it has finished with them.
433//
434char *Readline::Complete(const char* text, int state)
435{
436 if (fCompletion==0)
437 return rl_filename_completion_function(text, state);
438
439 static vector<string>::const_iterator pos;
440 if (state==0)
441 pos = fCompletion->begin();
442
443 while (pos!=fCompletion->end())
444 {
445 char *rc = Compare(*pos++, text);
446 if (rc)
447 return rc;
448 }
449
450 return 0;
451}
452
453// --------------------------------------------------------------------------
454//
455//! Calls Complete()
456//
457char *Readline::CompleteImp(const char* text, int state)
458{
459 return This->Complete(text, state);
460}
461
462// --------------------------------------------------------------------------
463//
464//! The readline manual says about this function:
465//!
466//! A pointer to an alternative function to create matches. The
467//! function is called with text, start, and end. start and end are
468//! indices in rl_line_buffer saying what the boundaries of text are.
469//! If this function exists and returns NULL, or if this variable is
470//! set to NULL, then rl_complete() will call the value of
471//! rl_completion_entry_function to generate matches, otherwise the
472//! array of strings returned will be used.
473//!
474//! This function is virtual and can be overwritten. It defaults to
475//! a call to rl_completion_matches with CompleteImp as an argument
476//! which defaults to filename completion, but can also be overwritten.
477//!
478//! It is suggested that you call
479//! Complete(const vector<string>&, const char*)
480//! from here.
481//!
482//! @param text
483//! A pointer to a char array conatining the text which should be
484//! completed. The text is null-terminated.
485//!
486//! @param start
487//! The start index within readline's line buffer rl_line_buffer,
488//! at which the text starts which presumably should be completed.
489//!
490//! @param end
491//! The end index within readline's line buffer rl_line_buffer,
492//! at which the text ends which presumably should be completed.
493//!
494//! @returns
495//! An array of strings which were allocated with malloc and which
496//! will be freed by readline with the possible matches.
497//
498char **Readline::Completion(const char *text, int /*start*/, int /*end*/)
499{
500 // To do filename completion call
501 return rl_completion_matches((char*)text, CompleteImp);
502}
503
504// --------------------------------------------------------------------------
505//
506//! Adds the given string to the history buffer of readline's history by
507//! calling add_history.
508//!
509//! @param str
510//! A reference to a string which should be added to readline's
511//! history.
512//!
513//! @param skip
514//! If skip is 1 and str matches the last added entry in the history,
515//! the entry is skipped. If skip==2, all entries matching str are
516//! removed from the history before the new entry is added as last one.
517//! <skip==2 is the default>
518//
519void Readline::AddToHistory(const string &str, int skip)
520{
521 if (skip==1 && fLastLine==str)
522 return;
523
524 if (str.empty())
525 return;
526
527 int p = -1;
528 while (skip==2)
529 {
530 p = history_search_pos(str.c_str(), 0, p+1);
531 if (p<0)
532 break;
533
534 // It seems like history_search_pos works more like
535 // history_search_prefix, therefore the identity is checked again
536 const HIST_ENTRY *e = history_get(p+1);
537 if (e && str==e->line)
538 free(remove_history(p));
539 }
540
541 add_history(str.c_str());
542 fLastLine = str;
543}
544
545// --------------------------------------------------------------------------
546//
547//! @returns
548//! a string containing [{fLine}]
549//
550string Readline::GetLinePrompt() const
551{
552 ostringstream str;
553 str << '[' << fLine << ']';
554 return str.str();
555}
556
557// --------------------------------------------------------------------------
558//
559//! Calls rl_set_prompt. This can be used from readline's callback function
560//! to change the prompt while a call to the readline function is in
561//! progress.
562//!
563//! @param prompt
564//! The new prompt to be shown
565//
566void Readline::UpdatePrompt(const string &prompt) const
567{
568 rl_set_prompt(prompt.c_str());
569}
570
571// --------------------------------------------------------------------------
572//
573//! This function is used to bind a key sequence via a call to
574//! rl_bind_keyseq.
575//!
576//! Readline's manual says about this function:
577//!
578//! Bind the key sequence represented by the string keyseq to the
579//! function function, beginning in the current keymap. This makes
580//! new keymaps as necessary. The return value is non-zero if keyseq
581//! is invalid.
582//!
583//! Key sequences are escaped sequences of characters read from an input
584//! stream when a special key is pressed. This is necessary because
585//! there are usually more keys and possible combinations than ascii codes.
586//!
587//! Possible key sequences are for example:
588//! "\033OP" F1
589//! "\033[1;5A" Ctrl+up
590//! "\033[1;5B" Ctrl+down
591//! "\033[1;3A" Alt+up
592//! "\033[1;3B" Alt+down
593//! "\033[5;3~" Alt+page up
594//! "\033[6;3~" Alt+page down
595//! "\033+" Alt++
596//! "\033-" Alt+-
597//! "\033\t" Alt+tab
598//! "\033[1~" Alt+tab
599//!
600//! @param seq
601//! The key sequence to be bound
602//!
603//! @param func
604//! A function of type "int func(int, int)
605//
606void Readline::BindKeySequence(const char *seq, int (*func)(int, int))
607{
608 rl_bind_keyseq(seq, func);
609}
610
611// --------------------------------------------------------------------------
612//
613//! Calls rl_variable_dumper(1)
614//!
615//! Print the readline variable names and their current values
616//! to rl_outstream. If readable is non-zero, the list is formatted
617//! in such a way that it can be made part of an inputrc file and
618//! re-read.
619//!
620//! rl_outstream can be redirected using SetStreamOut()
621//!
622//! @returns
623//! always true
624//
625bool Readline::DumpVariables()
626{
627 rl_variable_dumper(1);
628 return true;
629}
630
631// --------------------------------------------------------------------------
632//
633//! Calls rl_function_dumper(1)
634//!
635//! Print the readline function names and the key sequences currently
636//! bound to them to rl_outstream. If readable is non-zero, the list
637//! is formatted in such a way that it can be made part of an inputrc
638//! file and re-read.
639//!
640//! rl_outstream can be redirected using SetStreamOut()
641//!
642//! @returns
643//! always true
644//
645bool Readline::DumpFunctions()
646{
647 rl_function_dumper(1);
648 return true;
649}
650
651// --------------------------------------------------------------------------
652//
653//! Calls rl_list_funmap_names()
654//!
655//! Print the names of all bindable Readline functions to rl_outstream.
656//!
657//! rl_outstream can be redirected using SetStreamOut()
658//!
659//! @returns
660//! always true
661//
662bool Readline::DumpFunmap()
663{
664 rl_list_funmap_names();
665 return true;
666}
667
668// --------------------------------------------------------------------------
669//
670//! Sets rl_outstream (the stdio stream to which Readline performs output)
671//! to the new stream.
672//!
673//! @param f
674//! The new stdio stream to which readline should perform its output
675//!
676//! @return
677//! The old stream to which readline was performing output
678//
679FILE *Readline::SetStreamOut(FILE *f)
680{
681 FILE *rc = rl_outstream;
682 rl_outstream = f;
683 return rc;
684}
685
686// --------------------------------------------------------------------------
687//
688//! Sets rl_instream (the stdio stream from which Readline reads input)
689//! to the new stream.
690//!
691//! @param f
692//! The new stdio stream from which readline should read its input
693//!
694//! @return
695//! The old stream from which readline was reading it input
696//
697FILE *Readline::SetStreamIn(FILE *f)
698{
699 FILE *rc = rl_instream;
700 rl_instream = f;
701 return rc;
702}
703
704// --------------------------------------------------------------------------
705//
706//! return rl_display_prompt (the prompt which should currently be
707//! displayed on the screen) while a readline command is in progress
708//
709string Readline::GetPrompt()
710{
711 return rl_display_prompt;
712}
713
714// --------------------------------------------------------------------------
715//
716//! return rl_line_buffer (the current input line which should currently be
717//! displayed on the screen) while a readline command is in progress
718//!
719//! The length of the current line buffer (rl_end) is available as
720//! GetLineBuffer().size()
721//!
722//! Note that after readline has returned the contents of rl_end might
723//! not reflect the correct buffer length anymore, hence, the returned buffer
724//! might be truncated.
725//
726string Readline::GetBuffer()
727{
728 return string(rl_line_buffer, rl_end);
729}
730
731// --------------------------------------------------------------------------
732//
733//! return rl_point (the current cursor position within the line buffer)
734//
735int Readline::GetCursor()
736{
737 return rl_point;
738}
739
740// --------------------------------------------------------------------------
741//
742//! return strlen(rl_display_prompt) + rl_point
743//
744int Readline::GetAbsCursor()
745{
746 return strlen(rl_display_prompt) + rl_point;
747}
748
749// --------------------------------------------------------------------------
750//
751//! return rl_end (the current total length of the line buffer)
752//! Note that after readline has returned the contents of rl_end might
753//! not reflect the correct buffer length anymore.
754//
755int Readline::GetBufferLength()
756{
757 return rl_end;
758}
759
760// --------------------------------------------------------------------------
761//
762//! return the length of the prompt plus the length of the line buffer
763//
764int Readline::GetLineLength()
765{
766 return strlen(rl_display_prompt) + rl_end;
767}
768
769// --------------------------------------------------------------------------
770//
771//! Calls: Function: void rl_resize_terminal()
772//! Update Readline's internal screen size by reading values from the kernel.
773//
774void Readline::Resize()
775{
776 rl_resize_terminal();
777}
778
779// --------------------------------------------------------------------------
780//
781//! Calls: Function: void rl_set_screen_size (int rows, int cols)
782//! Set Readline's idea of the terminal size to rows rows and cols columns.
783//!
784//! @param width
785//! Number of columns
786//!
787//! @param height
788//! Number of rows
789//
790void Readline::Resize(int width, int height)
791{
792 rl_set_screen_size(height, width);
793}
794
795// --------------------------------------------------------------------------
796//
797//! Get the number of cols readline assumes the screen size to be
798//
799int Readline::GetCols() const
800{
801 int rows, cols;
802 rl_get_screen_size(&rows, &cols);
803 return cols;
804}
805
806// --------------------------------------------------------------------------
807//
808//! Get the number of rows readline assumes the screen size to be
809//
810int Readline::GetRows() const
811{
812 int rows, cols;
813 rl_get_screen_size(&rows, &cols);
814 return rows;
815}
816
817// --------------------------------------------------------------------------
818//
819//! Return a list of pointer to the history contents
820//
821vector<const char*> Readline::GetHistory() const
822{
823 HIST_ENTRY **next = history_list();
824
825 vector<const char*> v;
826
827 for (; *next; next++)
828 v.push_back((*next)->line);
829
830 return v;
831}
832
833// --------------------------------------------------------------------------
834//
835//! Clear readline history (calls clear_history())
836//!
837//! @returns
838//! always true
839//
840bool Readline::ClearHistory()
841{
842 clear_history();
843 return true;
844}
845
846// --------------------------------------------------------------------------
847//
848//! Displays the current history on rl_outstream
849//!
850//! rl_outstream can be redirected using SetStreamOut()
851//!
852//! @returns
853//! always true
854//
855bool Readline::DumpHistory()
856{
857 HIST_ENTRY **next = history_list();
858
859 if (!next)
860 return true;
861
862 for (; *next; next++)
863 fprintf(rl_outstream, "%s\n", (*next)->line);
864
865 return true;
866}
867
868// --------------------------------------------------------------------------
869//
870//! Execute a shell command through a pipe. Write stdout to rl_outstream
871//!
872//! @param cmd
873//! Command to be executed
874//!
875//! @returns
876//! always true
877//
878bool Readline::ExecuteShellCommand(const string &cmd)
879{
880 FILE *pipe = popen(cmd.c_str(), "r");
881 if (!pipe)
882 {
883 fprintf(rl_outstream, "ERROR - Could not create pipe '%s': %m\n", cmd.c_str());
884 return true;
885 }
886
887 while (1)
888 {
889 char buf[1024];
890
891 const size_t sz = fread(buf, 1, 1024, pipe);
892
893 fwrite(buf, 1, sz, rl_outstream);
894
895 if (feof(pipe) || ferror(pipe))
896 break;
897 }
898
899 if (ferror(pipe))
900 fprintf(rl_outstream, "ERROR - Reading from pipe '%s': %m\n", cmd.c_str());
901
902 pclose(pipe);
903
904 fprintf(rl_outstream, "\n");
905
906 return true;
907}
908
909// --------------------------------------------------------------------------
910//
911//! Print the available commands. This is intended for being overwritten
912//! by deriving classes.
913//!
914//! rl_outstream can be redirected using SetStreamOut()
915//!
916//! @returns
917//! always true
918//
919//
920bool Readline::PrintCommands()
921{
922 fprintf(rl_outstream, "\n");
923 fprintf(rl_outstream, " Commands:\n");
924 fprintf(rl_outstream, " No application specific commands defined.\n");
925 fprintf(rl_outstream, "\n");
926 return true;
927}
928
929// --------------------------------------------------------------------------
930//
931//! Print a general help message. This is intended for being overwritten
932//! by deriving classes.
933//!
934//!
935//! rl_outstream can be redirected using SetStreamOut()
936//!
937//! @returns
938//! always true
939//
940//
941bool Readline::PrintGeneralHelp()
942{
943 fprintf(rl_outstream, "\n");
944 fprintf(rl_outstream, " General help:\n");
945 fprintf(rl_outstream, " h,help Print this help message\n");
946 fprintf(rl_outstream, " clear Clear history buffer\n");
947 fprintf(rl_outstream, " lh,history Dump the history buffer to the screen\n");
948 fprintf(rl_outstream, " v,variables Dump readline variables\n");
949 fprintf(rl_outstream, " f,functions Dump readline functions\n");
950 fprintf(rl_outstream, " m,funmap Dump readline funmap\n");
951 fprintf(rl_outstream, " c,commands Dump available commands\n");
952 fprintf(rl_outstream, " k,keylist Dump key bindings\n");
953 fprintf(rl_outstream, " .! command Execute a shell command\n");
954 fprintf(rl_outstream, " .w n Sleep n milliseconds\n");
955 fprintf(rl_outstream, " .x file .. Execute a script of commands (+optional argumnets)\n");
956 fprintf(rl_outstream, " .x file:N .. Execute a script of commands, start at label N\n");
957 fprintf(rl_outstream, " .j N Forward jump to label N\n");
958 fprintf(rl_outstream, " : N Defines a label (N=number)\n");
959 fprintf(rl_outstream, " # comment Ignored\n");
960 fprintf(rl_outstream, " .q,quit Quit\n");
961 fprintf(rl_outstream, "\n");
962 fprintf(rl_outstream, " The command history is automatically loaded and saves to\n");
963 fprintf(rl_outstream, " and from %s.\n", GetName().c_str());
964 fprintf(rl_outstream, "\n");
965 return true;
966}
967
968// --------------------------------------------------------------------------
969//
970//! Print a help text about key bindings. This is intended for being
971//! overwritten by deriving classes.
972//!
973//!
974//! rl_outstream can be redirected using SetStreamOut()
975//!
976//! @returns
977//! always true
978//
979//
980bool Readline::PrintKeyBindings()
981{
982 fprintf(rl_outstream, "\n");
983 fprintf(rl_outstream, " Key bindings:\n");
984 fprintf(rl_outstream, " Page-up Search backward in history\n");
985 fprintf(rl_outstream, " Page-dn Search forward in history\n");
986 fprintf(rl_outstream, " Ctrl-left One word backward\n");
987 fprintf(rl_outstream, " Ctrl-right One word forward\n");
988 fprintf(rl_outstream, " Ctrl-d Quit\n");
989 fprintf(rl_outstream, " Ctrl-y Delete line\n");
990 fprintf(rl_outstream, " Alt-end/Ctrl-k Delete until the end of the line\n");
991 fprintf(rl_outstream, " F1 Toggle visibility of upper panel\n");
992 fprintf(rl_outstream, "\n");
993 fprintf(rl_outstream, " Default key-bindings are identical with your bash.\n");
994 fprintf(rl_outstream, "\n");
995 return true;
996}
997
998// --------------------------------------------------------------------------
999//
1000//!
1001//
1002bool Readline::PreProcess(const string &str)
1003{
1004 // ----------- Labels -------------
1005
1006 if (str[0]==':')
1007 {
1008 try
1009 {
1010 const int section = stoi(str.substr(1));
1011 SetSection(section);
1012
1013 if (fLabel!=section)
1014 return true;
1015 }
1016 catch (const logic_error &e)
1017 {
1018 fCommandLog << "# ERROR - Inavlid label '" << str.substr(1) << "'" << endl;
1019 fLabel = -2;
1020 return true;
1021 }
1022
1023 fLabel=-1;
1024 return false;
1025 }
1026
1027 if (fLabel>=0)
1028 {
1029 fCommandLog << "# SKIP[" << fLabel << "]: " << str << endl;
1030 return true;
1031 }
1032
1033 if (str.substr(0, 3)==".j ")
1034 {
1035 fLabel = atoi(str.substr(3).c_str());
1036 return false;
1037 }
1038
1039 return Process(str);
1040
1041}
1042
1043// --------------------------------------------------------------------------
1044//
1045//!
1046//
1047bool Readline::Process(const string &str)
1048{
1049 // ----------- Common commands -------------
1050
1051 if (str.substr(0, 3)==".w ")
1052 {
1053 usleep(stoi(str.substr(3))*1000);
1054 return true;
1055 }
1056
1057 if (str.substr(0, 3)==".x ")
1058 {
1059 string opt(str.substr(3));
1060
1061 map<string,string> data = Tools::Split(opt);
1062 if (opt.size()==0)
1063 return false;
1064
1065 /*
1066 if (fData.size()==0)
1067 // File name missing ...
1068 else
1069 // "Equal sign missing in argument '"+data.begin()->first+"'"
1070 */
1071
1072 Execute(opt, data);
1073 return true;
1074 }
1075
1076 if (str.substr(0, 2)==".!")
1077 {
1078 ExecuteShellCommand(str.substr(2));
1079 return true;
1080 }
1081
1082 // ----------- Readline static -------------
1083
1084 if (str=="clear")
1085 return ClearHistory();
1086
1087 if (str=="lh" || str=="history")
1088 return DumpHistory();
1089
1090 if (str=="v" || str=="variables")
1091 return DumpVariables();
1092
1093 if (str=="f" || str=="functions")
1094 return DumpFunctions();
1095
1096 if (str=="m" || str=="funmap")
1097 return DumpFunmap();
1098
1099 // ---------- Readline virtual -------------
1100
1101 if (str=="h" || str=="help")
1102 return PrintGeneralHelp();
1103
1104 if (str=="c" || str=="commands")
1105 return PrintCommands();
1106
1107 if (str=="k" || str=="keylist")
1108 return PrintKeyBindings();
1109
1110 return false;
1111}
1112
1113// --------------------------------------------------------------------------
1114//
1115//! This function is a wrapper around the call to readline. It encapsultes
1116//! the return buffer into a std::string and deletes the memory allocated
1117//! by readline. Furthermore, it removes leading and trailing whitespaces
1118//! before return the result. The result is returned in the given
1119//! argument containing the prompt. Before the function returns Shutdown()
1120//! is called (as opposed to Startup when readline starts)
1121//!
1122//! @param str
1123//! The prompt which is to be shown by the readline libarary. it is
1124//! directly given to the call to readline. The result of the
1125//! readline call is returned in this string.
1126//!
1127//! @returns
1128//! true if the call succeeded as usual, false if EOF was detected
1129//! by the readline call.
1130//
1131bool Readline::PromptEOF(string &str)
1132{
1133 char *buf = readline(str.c_str());
1134 Shutdown(buf);
1135
1136 // Happens when EOF is encountered
1137 if (!buf)
1138 return false;
1139
1140 str = Tools::Trim(buf);
1141
1142 free(buf);
1143
1144 return true;
1145}
1146
1147// --------------------------------------------------------------------------
1148//
1149//! This function is a wrapper around the call to readline. It encapsultes
1150//! the return buffer into a std::string and deletes the memory allocated
1151//! by readline. Furthermore, it removes leading and trailing whitespaces
1152//! before return the result. Before the function returns Shutdown() is
1153//! called (as opposed to Startup when readline starts)
1154//!
1155//! @param prompt
1156//! The prompt which is to be shown by the readline libarary. it is
1157//! directly given to the call to readline.
1158//!
1159//! @returns
1160//! The result of the readline call
1161//
1162string Readline::Prompt(const string &prompt)
1163{
1164 char *buf = readline(prompt.c_str());
1165
1166 Shutdown(buf ? buf : "");
1167
1168 const string str = !buf || (rl_done && rl_pending_input==4)
1169 ? ".q" : Tools::Trim(buf);
1170
1171 free(buf);
1172
1173 return str;
1174}
1175
1176// --------------------------------------------------------------------------
1177//
1178//! Process a single line. All lines are added to the history, but only
1179//! accepted lines are written to the command log. In this case fLine is
1180//! increased by one.
1181//! Comment (starting with #) are removed from the command-line. A # can be
1182//! escaped with quotation marks "#"
1183//
1184void Readline::ProcessLine(const string &str)
1185{
1186 const string cmd = Tools::Uncomment(str);
1187
1188 if (!cmd.empty())
1189 {
1190 const bool rc = PreProcess(cmd);
1191
1192 AddToHistory(cmd);
1193
1194 if (rc)
1195 return;
1196
1197 fLine++;
1198 }
1199
1200 fCommandLog << str << endl;
1201}
1202
1203// --------------------------------------------------------------------------
1204//
1205//! This implements a loop over readline calls. A prompt to issue can be
1206//! given. If it is NULL, the prompt is retrieved from GetUpdatePrompt().
1207//! It is updated regularly by means of calls to GetUpdatePrompt() from
1208//! EventHook(). If Prompt() returns with "quit" or ".q" the loop is
1209//! exited. If ".qqq" is entered exit(-1) is called. In case of ".qqqqqq"
1210//! abort(). Both ways to exit the program are not recommended. Empty
1211//! inputs are ignored. After that Process() with the returned string
1212//! is called. If Process returns true the input is not counted and not
1213//! added to the history, otherwise the line counter is increased
1214//! and the input is added to the history.
1215//!
1216//! @param prompt
1217//! The prompt to be issued or NULL if GetUPdatePrompt should be used
1218//! instead.
1219//!
1220void Readline::Run(const char *prompt)
1221{
1222 fLine = 0;
1223 while (1)
1224 {
1225 // Before we start we have to make sure that the
1226 // screen looks like and is ordered like expected.
1227 const string str = Prompt(prompt?prompt:GetUpdatePrompt());
1228 if (str.empty())
1229 continue;
1230
1231 if (str=="quit" || str==".q")
1232 break;
1233
1234 if (str==".qqq")
1235 exit(-1);
1236
1237 if (str==".qqqqqq")
1238 abort();
1239
1240 ProcessLine(str);
1241 }
1242}
1243
1244// --------------------------------------------------------------------------
1245//
1246//! Executes commands read from an ascii file as they were typed in
1247//! the console. Empty lines and lines beginning with # are ignored.
1248//!
1249//! @param fname
1250//! Filename of file to read
1251//!
1252//! @param args
1253//! Arguments to be passed to the script. A search and replace
1254//! will be done for ${arg}
1255//!
1256//! @returns
1257//! -1 if the file couldn't be read and the number of commands for which
1258//! Process() was callled otherwise
1259//!
1260int Readline::Execute(const string &fname, const map<string,string> &args)
1261{
1262 // this could do the same:
1263 // rl_instream = fopen(str.c_str(), "r");
1264
1265 if (IsStopped())
1266 return 0;
1267
1268 string name = Tools::Trim(fname);
1269 fScript = name;
1270
1271 SetSection(-2);
1272 fLabel = -1;
1273
1274 const size_t p = name.find_last_of(':');
1275 if (p!=string::npos)
1276 {
1277 fLabel = atoi(name.substr(p+1).c_str());
1278 name = name.substr(0, p);
1279 }
1280
1281 ifstream fin(name.c_str());
1282 if (!fin)
1283 {
1284 SetSection(-3);
1285 return -1;
1286 }
1287
1288 fCommandLog << "# " << Time() << " - " << name << " (START";
1289 if (fLabel>=0)
1290 fCommandLog << ':' << fLabel;
1291 fCommandLog << ")" << endl;
1292
1293 SetSection(-1);
1294
1295 int rc = 0;
1296
1297 string buffer;
1298 while (getline(fin, buffer, '\n') && fLabel>-2)
1299 {
1300 buffer = Tools::Trim(buffer);
1301 if (buffer.empty())
1302 continue;
1303
1304 rc++;
1305
1306 if (buffer=="quit" || buffer==".q")
1307 {
1308 Stop();
1309 break;
1310 }
1311
1312 // find and replace arguments
1313 for (auto it=args.begin(); it!=args.end(); it++)
1314 {
1315 const string find = "${"+it->first+"}";
1316 for (size_t pos=0; (pos=buffer.find(find, pos))!=string::npos; pos+=find.length())
1317 buffer.replace(pos, find.size(), it->second);
1318 }
1319
1320 // process line
1321 ProcessLine(buffer);
1322 }
1323
1324 fLabel = -1;
1325 SetSection(-3);
1326
1327 fCommandLog << "# " << Time() << " - " << name << " (FINISHED)" << endl;
1328
1329 return rc;
1330}
1331
1332// --------------------------------------------------------------------------
1333//
1334//! Stops the readline execution. It fakes the end of an editing by
1335//! setting rl_done to 1. To distinguish this from a normal edit,
1336//! rl_pending_input is set to EOT.
1337//!
1338void Readline::Stop()
1339{
1340 rl_done = 1;
1341 rl_pending_input = 4; // EOT (end of transmission, ctrl-d)
1342}
1343
1344// --------------------------------------------------------------------------
1345//
1346//! @returns
1347//! the status of rl_done and rl_pending_input. If rl_done==1 and
1348//! rl_pending_input==4 true is returned, false otherwise.
1349//! This can be used to check if Stop() was called. If readline is
1350//! not in operation.
1351//!
1352bool Readline::IsStopped() const
1353{
1354 return rl_done==1 && rl_pending_input==4;
1355};
Note: See TracBrowser for help on using the repository browser.