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

Last change on this file since 14038 was 14010, checked in by tbretz, 12 years ago
Exit with 128 not -1
File size: 40.2 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), fSection(-3), 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 string save = fScript;
1077 const int save_sec = fSection;
1078 Execute(opt, data);
1079 fScript = save;
1080 if (save_sec!=-3)
1081 {
1082 fSection = save_sec;
1083 SetSection(save_sec);
1084 }
1085
1086 return true;
1087 }
1088
1089 if (str.substr(0, 2)==".!")
1090 {
1091 ExecuteShellCommand(str.substr(2));
1092 return true;
1093 }
1094
1095 if (str.substr(0, 4)==".gt ")
1096 {
1097 istringstream in(str.substr(4));
1098
1099 float v0, v1;
1100 int label;
1101
1102 in >> v0 >> v1 >> label;
1103 if (in.fail())
1104 {
1105 PrintReadlineError("Couldn't parse '"+str+"'");
1106 fLabel = -2;
1107 return true;
1108 }
1109
1110 if (v0 > v1)
1111 fLabel = label;
1112
1113 return true;
1114 }
1115
1116 if (str.substr(0, 4)==".lt ")
1117 {
1118 istringstream in(str.substr(4));
1119
1120 float v0, v1;
1121 int label;
1122
1123 in >> v0 >> v1 >> label;
1124 if (in.fail())
1125 {
1126 PrintReadlineError("Couldn't parse '"+str+"'");
1127 fLabel = -2;
1128 return true;
1129 }
1130
1131 if (v0 < v1)
1132 fLabel = label;
1133
1134 return true;
1135 }
1136
1137 if (str.substr(0, 4)==".eq ")
1138 {
1139 istringstream in(str.substr(4));
1140
1141 int v0, v1, label;
1142
1143 in >> v0 >> v1 >> label;
1144 if (in.fail())
1145 {
1146 PrintReadlineError("Couldn't parse '"+str+"'");
1147 fLabel = -2;
1148 return true;
1149 }
1150
1151 if (v0==v1)
1152 fLabel = label;
1153
1154 return true;
1155 }
1156
1157
1158 // ----------- Readline static -------------
1159
1160 if (str=="clear")
1161 return ClearHistory();
1162
1163 if (str=="lh" || str=="history")
1164 return DumpHistory();
1165
1166 if (str=="v" || str=="variables")
1167 return DumpVariables();
1168
1169 if (str=="f" || str=="functions")
1170 return DumpFunctions();
1171
1172 if (str=="m" || str=="funmap")
1173 return DumpFunmap();
1174
1175 // ---------- Readline virtual -------------
1176
1177 if (str=="h" || str=="help")
1178 return PrintGeneralHelp();
1179
1180 if (str=="c" || str=="commands")
1181 return PrintCommands();
1182
1183 if (str=="k" || str=="keylist")
1184 return PrintKeyBindings();
1185
1186 return false;
1187}
1188
1189// --------------------------------------------------------------------------
1190//
1191//! This function is a wrapper around the call to readline. It encapsultes
1192//! the return buffer into a std::string and deletes the memory allocated
1193//! by readline. Furthermore, it removes leading and trailing whitespaces
1194//! before return the result. The result is returned in the given
1195//! argument containing the prompt. Before the function returns Shutdown()
1196//! is called (as opposed to Startup when readline starts)
1197//!
1198//! @param str
1199//! The prompt which is to be shown by the readline libarary. it is
1200//! directly given to the call to readline. The result of the
1201//! readline call is returned in this string.
1202//!
1203//! @returns
1204//! true if the call succeeded as usual, false if EOF was detected
1205//! by the readline call.
1206//
1207bool Readline::PromptEOF(string &str)
1208{
1209 char *buf = readline(str.c_str());
1210 Shutdown(buf);
1211
1212 // Happens when EOF is encountered
1213 if (!buf)
1214 return false;
1215
1216 str = Tools::Trim(buf);
1217
1218 free(buf);
1219
1220 return true;
1221}
1222
1223// --------------------------------------------------------------------------
1224//
1225//! This function is a wrapper around the call to readline. It encapsultes
1226//! the return buffer into a std::string and deletes the memory allocated
1227//! by readline. Furthermore, it removes leading and trailing whitespaces
1228//! before return the result. Before the function returns Shutdown() is
1229//! called (as opposed to Startup when readline starts)
1230//!
1231//! @param prompt
1232//! The prompt which is to be shown by the readline libarary. it is
1233//! directly given to the call to readline.
1234//!
1235//! @returns
1236//! The result of the readline call
1237//
1238string Readline::Prompt(const string &prompt)
1239{
1240 char *buf = readline(prompt.c_str());
1241
1242 Shutdown(buf ? buf : "");
1243
1244 const string str = !buf || (rl_done && rl_pending_input==4)
1245 ? ".q" : Tools::Trim(buf);
1246
1247 free(buf);
1248
1249 return str;
1250}
1251
1252// --------------------------------------------------------------------------
1253//
1254//! Process a single line. All lines are added to the history, but only
1255//! accepted lines are written to the command log. In this case fLine is
1256//! increased by one.
1257//! Comment (starting with #) are removed from the command-line. A # can be
1258//! escaped with quotation marks "#"
1259//
1260void Readline::ProcessLine(const string &str)
1261{
1262 const string cmd = Tools::Uncomment(str);
1263
1264 if (!cmd.empty())
1265 {
1266 const bool rc = PreProcess(cmd);
1267
1268 AddToHistory(cmd);
1269
1270 if (rc)
1271 return;
1272
1273 fLine++;
1274 }
1275
1276 fCommandLog << str << endl;
1277}
1278
1279// --------------------------------------------------------------------------
1280//
1281//! This implements a loop over readline calls. A prompt to issue can be
1282//! given. If it is NULL, the prompt is retrieved from GetUpdatePrompt().
1283//! It is updated regularly by means of calls to GetUpdatePrompt() from
1284//! EventHook(). If Prompt() returns with "quit" or ".q" the loop is
1285//! exited. If ".qqq" is entered exit(-1) is called. In case of ".qqqqqq"
1286//! abort(). Both ways to exit the program are not recommended. Empty
1287//! inputs are ignored. After that Process() with the returned string
1288//! is called. If Process returns true the input is not counted and not
1289//! added to the history, otherwise the line counter is increased
1290//! and the input is added to the history.
1291//!
1292//! @param prompt
1293//! The prompt to be issued or NULL if GetUPdatePrompt should be used
1294//! instead.
1295//!
1296void Readline::Run(const char *prompt)
1297{
1298 fLine = 0;
1299 while (1)
1300 {
1301 // Before we start we have to make sure that the
1302 // screen looks like and is ordered like expected.
1303 const string str = Prompt(prompt?prompt:GetUpdatePrompt());
1304 if (str.empty())
1305 continue;
1306
1307 if (str=="quit" || str==".q")
1308 break;
1309
1310 if (str==".qqq")
1311 exit(128);
1312
1313 if (str==".qqqqqq")
1314 abort();
1315
1316 ProcessLine(str);
1317 }
1318}
1319
1320// --------------------------------------------------------------------------
1321//
1322//! Executes commands read from an ascii file as they were typed in
1323//! the console. Empty lines and lines beginning with # are ignored.
1324//!
1325//! @param fname
1326//! Filename of file to read
1327//!
1328//! @param args
1329//! Arguments to be passed to the script. A search and replace
1330//! will be done for ${arg}
1331//!
1332//! @returns
1333//! -1 if the file couldn't be read and the number of commands for which
1334//! Process() was callled otherwise
1335//!
1336int Readline::Execute(const string &fname, const map<string,string> &args)
1337{
1338 // this could do the same:
1339 // rl_instream = fopen(str.c_str(), "r");
1340
1341 if (IsStopped())
1342 return 0;
1343
1344 string name = Tools::Trim(fname);
1345 fScript = name;
1346
1347 fSection = -2;
1348 SetSection(-2);
1349 fLabel = -1;
1350
1351 const size_t p = name.find_last_of(':');
1352 if (p!=string::npos)
1353 {
1354 fLabel = atoi(name.substr(p+1).c_str());
1355 name = name.substr(0, p);
1356 }
1357
1358 ifstream fin(name.c_str());
1359 if (!fin)
1360 {
1361 fSection = -3;
1362 SetSection(-3);
1363 return -1;
1364 }
1365
1366 if (fScriptDepth++==0)
1367 fStopScript = false;
1368
1369 fCommandLog << "# " << Time() << " - " << name << " (START[" << fScriptDepth<< "]";
1370 if (fLabel>=0)
1371 fCommandLog << ':' << fLabel;
1372 fCommandLog << ")" << endl;
1373
1374 fSection = -1;
1375 SetSection(-1);
1376
1377 int rc = 0;
1378
1379 string buffer;
1380 while (getline(fin, buffer, '\n') && !fStopScript)
1381 {
1382 buffer = Tools::Trim(buffer);
1383 if (buffer.empty())
1384 continue;
1385
1386 rc++;
1387
1388 if (buffer=="quit" || buffer==".q")
1389 {
1390 Stop();
1391 break;
1392 }
1393
1394 // find and replace arguments
1395 for (auto it=args.begin(); it!=args.end(); it++)
1396 {
1397 const string find = "${"+it->first+"}";
1398 for (size_t pos=0; (pos=buffer.find(find, pos))!=string::npos; pos+=find.length())
1399 buffer.replace(pos, find.size(), it->second);
1400 }
1401
1402 // process line
1403 ProcessLine(buffer);
1404 }
1405
1406 fCommandLog << "# " << Time() << " - " << name << " (FINISHED[" << fScriptDepth<< "])" << endl;
1407
1408 if (--fScriptDepth==0)
1409 fStopScript = false;
1410
1411 fLabel = -1;
1412 fSection = -3;
1413 SetSection(-3);
1414
1415 return rc;
1416}
1417
1418// --------------------------------------------------------------------------
1419//
1420//! Stops the readline execution. It fakes the end of an editing by
1421//! setting rl_done to 1. To distinguish this from a normal edit,
1422//! rl_pending_input is set to EOT.
1423//!
1424void Readline::Stop()
1425{
1426 rl_done = 1;
1427 rl_pending_input = 4; // EOT (end of transmission, ctrl-d)
1428}
1429
1430// --------------------------------------------------------------------------
1431//
1432//! @returns
1433//! the status of rl_done and rl_pending_input. If rl_done==1 and
1434//! rl_pending_input==4 true is returned, false otherwise.
1435//! This can be used to check if Stop() was called. If readline is
1436//! not in operation.
1437//!
1438bool Readline::IsStopped() const
1439{
1440 return rl_done==1 && rl_pending_input==4;
1441};
1442
1443void Readline::PrintReadlineError(const std::string &str)
1444{
1445 fprintf(rl_outstream, "%s\n", str.c_str());
1446}
Note: See TracBrowser for help on using the repository browser.