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

Last change on this file since 16865 was 16862, checked in by tbretz, 12 years ago
Deleteing the line does not seem necessary anymore.
File size: 42.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
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[3~", rl_named_function("delete-char"));
144 rl_bind_keyseq("\e[5~", rl_named_function("history-search-backward"));
145 rl_bind_keyseq("\e[6~", rl_named_function("history-search-forward"));
146 rl_bind_keyseq("\033[1;3F", rl_named_function("kill-line"));
147 rl_bind_keyseq("\033[1;5D", rl_named_function("backward-word"));
148 rl_bind_keyseq("\033[1;5C", rl_named_function("forward-word"));
149 rl_bind_key(25, rl_named_function("kill-whole-line"));
150
151 //for (int i=0; i<10; i++) cout << (int)getchar() << endl;
152}
153
154// --------------------------------------------------------------------------
155//
156//! Writes the current history to the file with the name stored in fName.
157//! In addition the written file is truncated to fMaxLines to keep the
158//! file of a reasonable size. The number of lines fMaxLines can be set
159//! by SetMaxLines before the destructor is called. Setting fMaxLines
160//! to 0 or a negative value switches automatic truncation off.
161//
162Readline::~Readline()
163{
164 // Write current history to file
165 if (write_history(fName.c_str()))
166 cout << "WARNING - Write " << fName.c_str() << ": " << strerror(errno) << endl;
167
168 // Truncate file
169 if (fMaxLines>0 && history_truncate_file(fName.c_str(), fMaxLines))
170 cout << "WARNING - Truncate " << fName.c_str() << ": " << strerror(errno) << endl;
171}
172
173// --------------------------------------------------------------------------
174//
175//! This wraps the given readline function such that the output can be
176//! redirected from thr rl_outstream to the given C++ ostream.
177//!
178//! @param out
179//! The stream to which the output should be redirected.
180//!
181//! @param function
182//! Takes a function of type bool(*)() as argument
183//!
184//! @returns
185//! The return value of the function
186//
187bool Readline::RedirectionWrapper(ostream &out, bool (*function)())
188{
189 FILE *save = SetStreamOut(tmpfile());
190 const bool rc = function();
191 FILE *file = SetStreamOut(save);
192
193 const bool empty = ftell(file)==0;
194
195 rewind(file);
196
197 if (empty)
198 {
199 out << " <empty>" << endl;
200 fclose(file);
201 return rc;
202 }
203
204 while (1)
205 {
206 const int c = getc(file);
207 if (feof(file))
208 break;
209 out << (char)c;
210 }
211 out << endl;
212
213 fclose(file);
214
215 return rc;
216}
217
218// --------------------------------------------------------------------------
219//
220//! Redirected from rl_getc_function, calls Getc
221//
222int Readline::rl_ncurses_getc(FILE *f)
223{
224 return This->Getc(f);
225}
226
227// --------------------------------------------------------------------------
228//
229//! Redirected from rl_startup_hook, calls Startup.
230//! A function called just before readline prints the first prompt.
231//
232int Readline::rl_ncurses_startup()
233{
234 This->Startup();
235 return 0; // What is this for?
236}
237
238// --------------------------------------------------------------------------
239//
240//! Redirected from rl_redisplay_function, calls Redisplay.
241//! Readline will call indirectly to update the display with the current
242//! contents of the editing buffer.
243//
244void Readline::rl_ncurses_redisplay()
245{
246 This->Redisplay();
247}
248
249// --------------------------------------------------------------------------
250//
251//! Redirected from rl_event_hook, calls Update().
252//! A function called periodically when readline is waiting for
253//! terminal input.
254//!
255int Readline::rl_ncurses_event_hook()
256{
257 This->EventHook();
258 return 0;
259}
260
261// --------------------------------------------------------------------------
262//
263//! Redirected from rl_completion_display_matches_hook,
264//! calls CompletionDisplayImp
265//!
266//! A function to be called when completing a word would normally display
267//! the list of possible matches. This function is called in lieu of
268//! Readline displaying the list. It takes three arguments:
269//! (char **matches, int num_matches, int max_length) where matches is
270//! the array of matching strings, num_matches is the number of strings
271//! in that array, and max_length is the length of the longest string in
272//! that array. Readline provides a convenience function,
273//! rl_display_match_list, that takes care of doing the display to
274//! Readline's output stream.
275//
276void Readline::rl_ncurses_completion_display(char **matches, int num, int max)
277{
278 This->CompletionDisplay(matches, num, max);
279}
280
281char **Readline::rl_ncurses_completion_function(const char *text, int start, int end)
282{
283 return This->Completion(text, start, end);
284}
285
286// --------------------------------------------------------------------------
287//
288//! Calls the default rl_getc function.
289//
290int Readline::Getc(FILE *f)
291{
292 return rl_getc(f);
293}
294
295// --------------------------------------------------------------------------
296//
297//! Default: Do nothing.
298//
299void Readline::Startup()
300{
301}
302
303// --------------------------------------------------------------------------
304//
305//! The default is to redisplay the prompt which is gotten from
306//! GetUpdatePrompt(). If GetUpdatePrompt() returns an empty string the
307//! prompt is kept untouched. This can be used to keep a prompt updated
308//! with some information (e.g. time) just by overwriting GetUpdatePrompt()
309//!
310void Readline::EventHook()
311{
312 const string cpy = fExternalInput;
313 fExternalInput = "";
314
315 if (!cpy.empty())
316 {
317 rl_replace_line(cpy.c_str(), 1);
318 rl_done = 1;
319 }
320
321 string p = GetUpdatePrompt();
322 if (p.empty())
323 p = rl_prompt;
324
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 fs::path his = fs::path(This->fName).parent_path();
1273 his /= fname;
1274
1275 write_history(This->fName.c_str());
1276 stifle_history(0);
1277 unstifle_history();
1278 read_history(his.string().c_str());
1279}
1280
1281// --------------------------------------------------------------------------
1282//
1283//! Writes the current history to the file with the given name
1284//! and replaces the history by the file defined by fName.
1285//!
1286//! @param fname
1287//! Name of the history file to write (it will be truncated to 1000 lines)
1288//!
1289void Readline::StaticPopHistory(const string &fname="")
1290{
1291 fs::path his = fs::path(This->fName).parent_path();
1292 his /= fname;
1293
1294 write_history(his.string().c_str());
1295 history_truncate_file(his.string().c_str(), 1000);
1296
1297 stifle_history(0);
1298 unstifle_history();
1299 read_history(This->fName.c_str());
1300}
1301
1302// --------------------------------------------------------------------------
1303//
1304//! Just calls readline and thus allows to just prompt for something.
1305//! Adds everything to the history except '.q'
1306//!
1307//! @param prompt
1308//! Prompt to be displayed
1309//!
1310//! @return
1311//! String entered by the user ('.q' is Ctrl-d is pressed)
1312//!
1313string Readline::StaticPrompt(const string &prompt)
1314{
1315 char *buf = readline(prompt.c_str());
1316 if (!buf)
1317 return ".q";
1318
1319 const string str(buf);
1320 if (Tools::Trim(str)!=".q" && !Tools::Trim(str).empty())
1321 if (history_length==0 || history_search_pos(str.c_str(), -1, history_length-1)!=history_length-1)
1322 add_history(buf);
1323
1324 free(buf);
1325
1326 return str;
1327}
1328
1329// --------------------------------------------------------------------------
1330//
1331//! Process a single line. All lines are added to the history, but only
1332//! accepted lines are written to the command log. In this case fLine is
1333//! increased by one.
1334//! Comment (starting with #) are removed from the command-line. A # can be
1335//! escaped with quotation marks "#"
1336//
1337void Readline::ProcessLine(const string &str)
1338{
1339 const string cmd = Tools::Uncomment(str);
1340
1341 if (!cmd.empty())
1342 {
1343 const bool rc = PreProcess(cmd);
1344
1345 AddToHistory(cmd);
1346
1347 if (rc)
1348 return;
1349
1350 fLine++;
1351 }
1352
1353 fCommandLog << str << endl;
1354}
1355
1356// --------------------------------------------------------------------------
1357//
1358//! This implements a loop over readline calls. A prompt to issue can be
1359//! given. If it is NULL, the prompt is retrieved from GetUpdatePrompt().
1360//! It is updated regularly by means of calls to GetUpdatePrompt() from
1361//! EventHook(). If Prompt() returns with "quit" or ".q" the loop is
1362//! exited. If ".qqq" is entered exit(-1) is called. In case of ".qqqqqq"
1363//! abort(). Both ways to exit the program are not recommended. Empty
1364//! inputs are ignored. After that Process() with the returned string
1365//! is called. If Process returns true the input is not counted and not
1366//! added to the history, otherwise the line counter is increased
1367//! and the input is added to the history.
1368//!
1369//! @param prompt
1370//! The prompt to be issued or NULL if GetUPdatePrompt should be used
1371//! instead.
1372//!
1373void Readline::Run(const char *prompt)
1374{
1375 fLine = 0;
1376 while (1)
1377 {
1378 // Before we start we have to make sure that the
1379 // screen looks like and is ordered like expected.
1380 const string str = Prompt(prompt?prompt:GetUpdatePrompt());
1381 if (str.empty())
1382 continue;
1383
1384 if (str=="quit" || str==".q")
1385 break;
1386
1387 if (str==".qqq")
1388 exit(128);
1389
1390 if (str==".qqqqqq")
1391 abort();
1392
1393 ProcessLine(str);
1394 }
1395}
1396
1397// --------------------------------------------------------------------------
1398//
1399//! Executes commands read from an ascii file as they were typed in
1400//! the console. Empty lines and lines beginning with # are ignored.
1401//!
1402//! @param fname
1403//! Filename of file to read
1404//!
1405//! @param args
1406//! Arguments to be passed to the script. A search and replace
1407//! will be done for ${arg}
1408//!
1409//! @returns
1410//! -1 if the file couldn't be read and the number of commands for which
1411//! Process() was callled otherwise
1412//!
1413int Readline::Execute(const string &fname, const map<string,string> &args)
1414{
1415 // this could do the same:
1416 // rl_instream = fopen(str.c_str(), "r");
1417
1418 if (IsStopped())
1419 return 0;
1420
1421 string name = Tools::Trim(fname);
1422 fScript = name;
1423
1424 fSection = -3;
1425 SetSection(-3);
1426 fLabel = -1;
1427
1428 const size_t p = name.find_last_of(':');
1429 if (p!=string::npos)
1430 {
1431 fLabel = atoi(name.substr(p+1).c_str());
1432 name = name.substr(0, p);
1433 }
1434
1435 ifstream fin(name.c_str());
1436 if (!fin)
1437 {
1438 fSection = -4;
1439 SetSection(-4);
1440 return -1;
1441 }
1442
1443 if (fScriptDepth++==0)
1444 fStopScript = false;
1445
1446 fCommandLog << "# " << Time() << " - " << name << " (START[" << fScriptDepth<< "]";
1447 if (fLabel>=0)
1448 fCommandLog << ':' << fLabel;
1449 fCommandLog << ")" << endl;
1450
1451 fSection = -1;
1452 SetSection(-1);
1453
1454 int rc = 0;
1455
1456 string buffer;
1457 while (getline(fin, buffer, '\n') && !fStopScript)
1458 {
1459 buffer = Tools::Trim(buffer);
1460 if (buffer.empty())
1461 continue;
1462
1463 rc++;
1464
1465 if (buffer=="quit" || buffer==".q")
1466 {
1467 Stop();
1468 break;
1469 }
1470
1471 // find and replace arguments
1472 for (auto it=args.begin(); it!=args.end(); it++)
1473 {
1474 const string find = "${"+it->first+"}";
1475 for (size_t pos=0; (pos=buffer.find(find, pos))!=string::npos; pos+=find.length())
1476 buffer.replace(pos, find.size(), it->second);
1477 }
1478
1479 // process line
1480 ProcessLine(buffer);
1481 }
1482
1483 fCommandLog << "# " << Time() << " - " << name << " (FINISHED[" << fScriptDepth<< "])" << endl;
1484
1485 if (--fScriptDepth==0)
1486 fStopScript = false;
1487
1488 fLabel = -1;
1489 fSection = -4;
1490 SetSection(-4);
1491
1492 return rc;
1493}
1494
1495// --------------------------------------------------------------------------
1496//
1497//! Stops the readline execution. It fakes the end of an editing by
1498//! setting rl_done to 1. To distinguish this from a normal edit,
1499//! rl_pending_input is set to EOT.
1500//!
1501void Readline::Stop()
1502{
1503 rl_done = 1;
1504 rl_pending_input = 4; // EOT (end of transmission, ctrl-d)
1505}
1506
1507// --------------------------------------------------------------------------
1508//
1509//! @returns
1510//! the status of rl_done and rl_pending_input. If rl_done==1 and
1511//! rl_pending_input==4 true is returned, false otherwise.
1512//! This can be used to check if Stop() was called. If readline is
1513//! not in operation.
1514//!
1515bool Readline::IsStopped() const
1516{
1517 return rl_done==1 && rl_pending_input==4;
1518};
1519
1520void Readline::PrintReadlineError(const std::string &str)
1521{
1522 fprintf(rl_outstream, "%s\n", str.c_str());
1523}
Note: See TracBrowser for help on using the repository browser.