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

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