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

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