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

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