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

Last change on this file since 13833 was 13771, checked in by tbretz, 13 years ago
Implemented the possibility to print errors form readline; added .gt, .lt and .eq commmands.
File size: 39.6 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, " .lt f0 f1 N If float f0 lower than float f1, jump to label N\n");
959 fprintf(rl_outstream, " .gt f0 f1 N If float f0 greater than float f1, jump to label N\n");
960 fprintf(rl_outstream, " .eq i0 i1 N If int i0 equal int i1, jump to label N\n");
961 fprintf(rl_outstream, " : N Defines a label (N=number)\n");
962 fprintf(rl_outstream, " # comment Ignored\n");
963 fprintf(rl_outstream, " .q,quit Quit\n");
964 fprintf(rl_outstream, "\n");
965 fprintf(rl_outstream, " The command history is automatically loaded and saves to\n");
966 fprintf(rl_outstream, " and from %s.\n", GetName().c_str());
967 fprintf(rl_outstream, "\n");
968 return true;
969}
970
971// --------------------------------------------------------------------------
972//
973//! Print a help text about key bindings. This is intended for being
974//! overwritten by deriving classes.
975//!
976//!
977//! rl_outstream can be redirected using SetStreamOut()
978//!
979//! @returns
980//! always true
981//
982//
983bool Readline::PrintKeyBindings()
984{
985 fprintf(rl_outstream, "\n");
986 fprintf(rl_outstream, " Key bindings:\n");
987 fprintf(rl_outstream, " Page-up Search backward in history\n");
988 fprintf(rl_outstream, " Page-dn Search forward in history\n");
989 fprintf(rl_outstream, " Ctrl-left One word backward\n");
990 fprintf(rl_outstream, " Ctrl-right One word forward\n");
991 fprintf(rl_outstream, " Ctrl-d Quit\n");
992 fprintf(rl_outstream, " Ctrl-y Delete line\n");
993 fprintf(rl_outstream, " Alt-end/Ctrl-k Delete until the end of the line\n");
994 fprintf(rl_outstream, " F1 Toggle visibility of upper panel\n");
995 fprintf(rl_outstream, "\n");
996 fprintf(rl_outstream, " Default key-bindings are identical with your bash.\n");
997 fprintf(rl_outstream, "\n");
998 return true;
999}
1000
1001// --------------------------------------------------------------------------
1002//
1003//!
1004//
1005bool Readline::PreProcess(const string &str)
1006{
1007 // ----------- Labels -------------
1008
1009 if (str[0]==':')
1010 {
1011 try
1012 {
1013 const int section = stoi(str.substr(1));
1014 SetSection(section);
1015
1016 if (fLabel!=section)
1017 return true;
1018 }
1019 catch (const logic_error &e)
1020 {
1021 fCommandLog << "# ERROR - Inavlid label '" << str.substr(1) << "'" << endl;
1022 fLabel = -2;
1023 return true;
1024 }
1025
1026 fLabel=-1;
1027 return false;
1028 }
1029
1030 if (fLabel>=0)
1031 {
1032 fCommandLog << "# SKIP[" << fLabel << "]: " << str << endl;
1033 return true;
1034 }
1035
1036 if (str.substr(0, 3)==".j ")
1037 {
1038 fLabel = atoi(str.substr(3).c_str());
1039 return false;
1040 }
1041
1042 return Process(str);
1043
1044}
1045
1046// --------------------------------------------------------------------------
1047//
1048//!
1049//
1050bool Readline::Process(const string &str)
1051{
1052 // ----------- Common commands -------------
1053
1054 if (str.substr(0, 3)==".w ")
1055 {
1056 usleep(stoi(str.substr(3))*1000);
1057 return true;
1058 }
1059
1060 if (str.substr(0, 3)==".x ")
1061 {
1062 string opt(str.substr(3));
1063
1064 map<string,string> data = Tools::Split(opt);
1065 if (opt.size()==0)
1066 {
1067 if (data.size()==0)
1068 PrintReadlineError("Filename missing.");
1069 else
1070 PrintReadlineError("Equal sign missing in argument '"+data.begin()->first+"'");
1071
1072 return true;
1073 }
1074
1075 Execute(opt, data);
1076 return true;
1077 }
1078
1079 if (str.substr(0, 2)==".!")
1080 {
1081 ExecuteShellCommand(str.substr(2));
1082 return true;
1083 }
1084
1085 if (str.substr(0, 4)==".gt ")
1086 {
1087 istringstream in(str.substr(4));
1088
1089 float v0, v1;
1090 int label;
1091
1092 in >> v0 >> v1 >> label;
1093 if (in.fail())
1094 {
1095 PrintReadlineError("Couldn't parse '"+str+"'");
1096 fLabel = -2;
1097 return true;
1098 }
1099
1100 if (v0 > v1)
1101 fLabel = label;
1102
1103 return true;
1104 }
1105
1106 if (str.substr(0, 4)==".lt ")
1107 {
1108 istringstream in(str.substr(4));
1109
1110 float v0, v1;
1111 int label;
1112
1113 in >> v0 >> v1 >> label;
1114 if (in.fail())
1115 {
1116 PrintReadlineError("Couldn't parse '"+str+"'");
1117 fLabel = -2;
1118 return true;
1119 }
1120
1121 if (v0 < v1)
1122 fLabel = label;
1123
1124 return true;
1125 }
1126
1127 if (str.substr(0, 4)==".eq ")
1128 {
1129 istringstream in(str.substr(4));
1130
1131 int v0, v1, label;
1132
1133 in >> v0 >> v1 >> label;
1134 if (in.fail())
1135 {
1136 PrintReadlineError("Couldn't parse '"+str+"'");
1137 fLabel = -2;
1138 return true;
1139 }
1140
1141 if (v0==v1)
1142 fLabel = label;
1143
1144 return true;
1145 }
1146
1147
1148 // ----------- Readline static -------------
1149
1150 if (str=="clear")
1151 return ClearHistory();
1152
1153 if (str=="lh" || str=="history")
1154 return DumpHistory();
1155
1156 if (str=="v" || str=="variables")
1157 return DumpVariables();
1158
1159 if (str=="f" || str=="functions")
1160 return DumpFunctions();
1161
1162 if (str=="m" || str=="funmap")
1163 return DumpFunmap();
1164
1165 // ---------- Readline virtual -------------
1166
1167 if (str=="h" || str=="help")
1168 return PrintGeneralHelp();
1169
1170 if (str=="c" || str=="commands")
1171 return PrintCommands();
1172
1173 if (str=="k" || str=="keylist")
1174 return PrintKeyBindings();
1175
1176 return false;
1177}
1178
1179// --------------------------------------------------------------------------
1180//
1181//! This function is a wrapper around the call to readline. It encapsultes
1182//! the return buffer into a std::string and deletes the memory allocated
1183//! by readline. Furthermore, it removes leading and trailing whitespaces
1184//! before return the result. The result is returned in the given
1185//! argument containing the prompt. Before the function returns Shutdown()
1186//! is called (as opposed to Startup when readline starts)
1187//!
1188//! @param str
1189//! The prompt which is to be shown by the readline libarary. it is
1190//! directly given to the call to readline. The result of the
1191//! readline call is returned in this string.
1192//!
1193//! @returns
1194//! true if the call succeeded as usual, false if EOF was detected
1195//! by the readline call.
1196//
1197bool Readline::PromptEOF(string &str)
1198{
1199 char *buf = readline(str.c_str());
1200 Shutdown(buf);
1201
1202 // Happens when EOF is encountered
1203 if (!buf)
1204 return false;
1205
1206 str = Tools::Trim(buf);
1207
1208 free(buf);
1209
1210 return true;
1211}
1212
1213// --------------------------------------------------------------------------
1214//
1215//! This function is a wrapper around the call to readline. It encapsultes
1216//! the return buffer into a std::string and deletes the memory allocated
1217//! by readline. Furthermore, it removes leading and trailing whitespaces
1218//! before return the result. Before the function returns Shutdown() is
1219//! called (as opposed to Startup when readline starts)
1220//!
1221//! @param prompt
1222//! The prompt which is to be shown by the readline libarary. it is
1223//! directly given to the call to readline.
1224//!
1225//! @returns
1226//! The result of the readline call
1227//
1228string Readline::Prompt(const string &prompt)
1229{
1230 char *buf = readline(prompt.c_str());
1231
1232 Shutdown(buf ? buf : "");
1233
1234 const string str = !buf || (rl_done && rl_pending_input==4)
1235 ? ".q" : Tools::Trim(buf);
1236
1237 free(buf);
1238
1239 return str;
1240}
1241
1242// --------------------------------------------------------------------------
1243//
1244//! Process a single line. All lines are added to the history, but only
1245//! accepted lines are written to the command log. In this case fLine is
1246//! increased by one.
1247//! Comment (starting with #) are removed from the command-line. A # can be
1248//! escaped with quotation marks "#"
1249//
1250void Readline::ProcessLine(const string &str)
1251{
1252 const string cmd = Tools::Uncomment(str);
1253
1254 if (!cmd.empty())
1255 {
1256 const bool rc = PreProcess(cmd);
1257
1258 AddToHistory(cmd);
1259
1260 if (rc)
1261 return;
1262
1263 fLine++;
1264 }
1265
1266 fCommandLog << str << endl;
1267}
1268
1269// --------------------------------------------------------------------------
1270//
1271//! This implements a loop over readline calls. A prompt to issue can be
1272//! given. If it is NULL, the prompt is retrieved from GetUpdatePrompt().
1273//! It is updated regularly by means of calls to GetUpdatePrompt() from
1274//! EventHook(). If Prompt() returns with "quit" or ".q" the loop is
1275//! exited. If ".qqq" is entered exit(-1) is called. In case of ".qqqqqq"
1276//! abort(). Both ways to exit the program are not recommended. Empty
1277//! inputs are ignored. After that Process() with the returned string
1278//! is called. If Process returns true the input is not counted and not
1279//! added to the history, otherwise the line counter is increased
1280//! and the input is added to the history.
1281//!
1282//! @param prompt
1283//! The prompt to be issued or NULL if GetUPdatePrompt should be used
1284//! instead.
1285//!
1286void Readline::Run(const char *prompt)
1287{
1288 fLine = 0;
1289 while (1)
1290 {
1291 // Before we start we have to make sure that the
1292 // screen looks like and is ordered like expected.
1293 const string str = Prompt(prompt?prompt:GetUpdatePrompt());
1294 if (str.empty())
1295 continue;
1296
1297 if (str=="quit" || str==".q")
1298 break;
1299
1300 if (str==".qqq")
1301 exit(-1);
1302
1303 if (str==".qqqqqq")
1304 abort();
1305
1306 ProcessLine(str);
1307 }
1308}
1309
1310// --------------------------------------------------------------------------
1311//
1312//! Executes commands read from an ascii file as they were typed in
1313//! the console. Empty lines and lines beginning with # are ignored.
1314//!
1315//! @param fname
1316//! Filename of file to read
1317//!
1318//! @param args
1319//! Arguments to be passed to the script. A search and replace
1320//! will be done for ${arg}
1321//!
1322//! @returns
1323//! -1 if the file couldn't be read and the number of commands for which
1324//! Process() was callled otherwise
1325//!
1326int Readline::Execute(const string &fname, const map<string,string> &args)
1327{
1328 // this could do the same:
1329 // rl_instream = fopen(str.c_str(), "r");
1330
1331 if (IsStopped())
1332 return 0;
1333
1334 string name = Tools::Trim(fname);
1335 fScript = name;
1336
1337 SetSection(-2);
1338 fLabel = -1;
1339
1340 const size_t p = name.find_last_of(':');
1341 if (p!=string::npos)
1342 {
1343 fLabel = atoi(name.substr(p+1).c_str());
1344 name = name.substr(0, p);
1345 }
1346
1347 ifstream fin(name.c_str());
1348 if (!fin)
1349 {
1350 SetSection(-3);
1351 return -1;
1352 }
1353
1354 fCommandLog << "# " << Time() << " - " << name << " (START";
1355 if (fLabel>=0)
1356 fCommandLog << ':' << fLabel;
1357 fCommandLog << ")" << endl;
1358
1359 SetSection(-1);
1360
1361 int rc = 0;
1362
1363 string buffer;
1364 while (getline(fin, buffer, '\n') && fLabel>-2)
1365 {
1366 buffer = Tools::Trim(buffer);
1367 if (buffer.empty())
1368 continue;
1369
1370 rc++;
1371
1372 if (buffer=="quit" || buffer==".q")
1373 {
1374 Stop();
1375 break;
1376 }
1377
1378 // find and replace arguments
1379 for (auto it=args.begin(); it!=args.end(); it++)
1380 {
1381 const string find = "${"+it->first+"}";
1382 for (size_t pos=0; (pos=buffer.find(find, pos))!=string::npos; pos+=find.length())
1383 buffer.replace(pos, find.size(), it->second);
1384 }
1385
1386 // process line
1387 ProcessLine(buffer);
1388 }
1389
1390 fLabel = -1;
1391 SetSection(-3);
1392
1393 fCommandLog << "# " << Time() << " - " << name << " (FINISHED)" << endl;
1394
1395 return rc;
1396}
1397
1398// --------------------------------------------------------------------------
1399//
1400//! Stops the readline execution. It fakes the end of an editing by
1401//! setting rl_done to 1. To distinguish this from a normal edit,
1402//! rl_pending_input is set to EOT.
1403//!
1404void Readline::Stop()
1405{
1406 rl_done = 1;
1407 rl_pending_input = 4; // EOT (end of transmission, ctrl-d)
1408}
1409
1410// --------------------------------------------------------------------------
1411//
1412//! @returns
1413//! the status of rl_done and rl_pending_input. If rl_done==1 and
1414//! rl_pending_input==4 true is returned, false otherwise.
1415//! This can be used to check if Stop() was called. If readline is
1416//! not in operation.
1417//!
1418bool Readline::IsStopped() const
1419{
1420 return rl_done==1 && rl_pending_input==4;
1421};
1422
1423void Readline::PrintReadlineError(const std::string &str)
1424{
1425 fprintf(rl_outstream, "%s\n", str.c_str());
1426}
Note: See TracBrowser for help on using the repository browser.