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

Last change on this file since 16880 was 16880, checked in by tbretz, 11 years ago
Redisplay has to check if this is an update due to change of terminal size. If this is the case the cursor must be moved back to the beginning of the line. For convenience we also clean the line (to remove the last charcter in the line, which (maybe fpor safty reason to avoid newlines) readline doesn't remove.
File size: 42.6 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//
351
352void Readline::Redisplay()
353{
354 static int W=-1, H=-1;
355
356 int w, h;
357 rl_get_screen_size(&h, &w);
358 if (W!=w || h!=H)
359 {
360 cout << '\r' << string(w+1, ' ') << '\r';
361
362 W=w;
363 H=h;
364 }
365
366 rl_redisplay();
367}
368
369// --------------------------------------------------------------------------
370//
371//! Default: call rl_completion_display_matches()
372//
373void Readline::CompletionDisplay(char **matches, int num, int max)
374{
375 rl_display_match_list(matches, num, max);
376}
377
378// --------------------------------------------------------------------------
379//
380//! This is a static helper for the compilation of a completion-list.
381//! It compares the two inputs (str and txt) to a maximum of the size of
382//! txt. If they match, memory is allocated with malloc and a pointer to
383//! the null-terminated version of str is returned.
384//!
385//! @param str
386//! A reference to the string which is checked (e.g. "Makefile.am")
387//!
388//! @param txt
389//! A reference to the part of the string the user has already typed,
390//! e.g. "Makef"
391//!
392//! @returns
393//! A pointer to memory allocated with malloc containing the string str
394//
395char *Readline::Compare(const string &str, const string &txt)
396{
397 /*return strncmp(str.c_str(), txt.c_str(), txt.length())==0 ? */
398 return strncasecmp(str.c_str(), txt.c_str(), txt.length())==0 ?
399 strndup(str.c_str(), str.length()) : 0;
400}
401
402char **Readline::CompletionMatches(const char *text, char *(*func)(const char*, int))
403{
404 return rl_completion_matches(text, func);
405}
406
407// --------------------------------------------------------------------------
408//
409//! The given vector should be a reference to a vector of strings
410//! containing all possible matches. The actual match-making is then
411//! done in Complete(const char *, int)
412//!
413//! The pointer fCompletion is redirected to the vector for the run time
414//! of the function, but restored afterwards. So by this you can set a
415//! default completion list in case Complete is not called or Completion
416//! not overloaded.
417//!
418//! @param v
419//! reference to a vector of strings with all possible matches
420//!
421//! @param text
422//! the text which should be matched (it is just propagated to
423//! Readline::Completion)
424//!
425char **Readline::Complete(const vector<string> &v, const char *text)
426{
427 const vector<string> *save = fCompletion;
428
429 fCompletion = &v;
430 char **rc = rl_completion_matches(const_cast<char*>(text), CompleteImp);
431 fCompletion = save;
432
433 return rc;
434}
435
436// --------------------------------------------------------------------------
437//
438//! If fCompletion==0 the default is to call readline's
439//! rl_filename_completion_function. Otherwise the contents of fCompletion
440//! are returned. To change fCompletion either initialize it via
441//! SetCompletion() (in this case you must ensure the life time of the
442//! object) or call
443//! Complete(const vector<string>&, const char*)
444//! from
445//! Completion(const char * int, int)
446//!
447//! This is the so called generator function, the readline manual says
448//! about this function:
449//!
450//! The generator function is called repeatedly from
451//! rl_completion_matches(), returning a string each time. The arguments
452//! to the generator function are text and state. text is the partial word
453//! to be completed. state is zero the first time the function is called,
454//! allowing the generator to perform any necessary initialization, and a
455//! positive non-zero integer for each subsequent call. The generator
456//! function returns (char *)NULL to inform rl_completion_matches() that
457//! there are no more possibilities left. Usually the generator function
458//! computes the list of possible completions when state is zero, and
459//! returns them one at a time on subsequent calls. Each string the
460//! generator function returns as a match must be allocated with malloc();
461//! Readline frees the strings when it has finished with them.
462//
463char *Readline::Complete(const char* text, int state)
464{
465 if (fCompletion==0)
466 return rl_filename_completion_function(text, state);
467
468 static vector<string>::const_iterator pos;
469 if (state==0)
470 pos = fCompletion->begin();
471
472 while (pos!=fCompletion->end())
473 {
474 char *rc = Compare(*pos++, text);
475 if (rc)
476 return rc;
477 }
478
479 return 0;
480}
481
482// --------------------------------------------------------------------------
483//
484//! Calls Complete()
485//
486char *Readline::CompleteImp(const char* text, int state)
487{
488 return This->Complete(text, state);
489}
490
491// --------------------------------------------------------------------------
492//
493//! The readline manual says about this function:
494//!
495//! A pointer to an alternative function to create matches. The
496//! function is called with text, start, and end. start and end are
497//! indices in rl_line_buffer saying what the boundaries of text are.
498//! If this function exists and returns NULL, or if this variable is
499//! set to NULL, then rl_complete() will call the value of
500//! rl_completion_entry_function to generate matches, otherwise the
501//! array of strings returned will be used.
502//!
503//! This function is virtual and can be overwritten. It defaults to
504//! a call to rl_completion_matches with CompleteImp as an argument
505//! which defaults to filename completion, but can also be overwritten.
506//!
507//! It is suggested that you call
508//! Complete(const vector<string>&, const char*)
509//! from here.
510//!
511//! @param text
512//! A pointer to a char array conatining the text which should be
513//! completed. The text is null-terminated.
514//!
515//! @param start
516//! The start index within readline's line buffer rl_line_buffer,
517//! at which the text starts which presumably should be completed.
518//!
519//! @param end
520//! The end index within readline's line buffer rl_line_buffer,
521//! at which the text ends which presumably should be completed.
522//!
523//! @returns
524//! An array of strings which were allocated with malloc and which
525//! will be freed by readline with the possible matches.
526//
527char **Readline::Completion(const char *text, int /*start*/, int /*end*/)
528{
529 // To do filename completion call
530 return rl_completion_matches((char*)text, CompleteImp);
531}
532
533// --------------------------------------------------------------------------
534//
535//! Adds the given string to the history buffer of readline's history by
536//! calling add_history.
537//!
538//! @param str
539//! A reference to a string which should be added to readline's
540//! history.
541//!
542//! @param skip
543//! If skip is 1 and str matches the last added entry in the history,
544//! the entry is skipped. If skip==2, all entries matching str are
545//! removed from the history before the new entry is added as last one.
546//! <skip==2 is the default>
547//
548void Readline::AddToHistory(const string &str, int skip)
549{
550 if (skip==1 && fLastLine==str)
551 return;
552
553 if (str.empty())
554 return;
555
556 int p = -1;
557 while (skip==2)
558 {
559 p = history_search_pos(str.c_str(), 0, p+1);
560 if (p<0)
561 break;
562
563 // It seems like history_search_pos works more like
564 // history_search_prefix, therefore the identity is checked again
565 const HIST_ENTRY *e = history_get(p+1);
566 if (e && str==e->line)
567 free(remove_history(p));
568 }
569
570 add_history(str.c_str());
571 fLastLine = str;
572}
573
574// --------------------------------------------------------------------------
575//
576//! @returns
577//! a string containing [{fLine}]
578//
579string Readline::GetLinePrompt() const
580{
581 ostringstream str;
582 str << '[' << fLine << ']';
583 return str.str();
584}
585
586// --------------------------------------------------------------------------
587//
588//! Calls rl_set_prompt. This can be used from readline's callback function
589//! to change the prompt while a call to the readline function is in
590//! progress.
591//!
592//! @param prompt
593//! The new prompt to be shown
594//
595void Readline::UpdatePrompt(const string &prompt) const
596{
597 rl_set_prompt(prompt.c_str());
598}
599
600// --------------------------------------------------------------------------
601//
602//! This function is used to bind a key sequence via a call to
603//! rl_bind_keyseq.
604//!
605//! Readline's manual says about this function:
606//!
607//! Bind the key sequence represented by the string keyseq to the
608//! function function, beginning in the current keymap. This makes
609//! new keymaps as necessary. The return value is non-zero if keyseq
610//! is invalid.
611//!
612//! Key sequences are escaped sequences of characters read from an input
613//! stream when a special key is pressed. This is necessary because
614//! there are usually more keys and possible combinations than ascii codes.
615//!
616//! Possible key sequences are for example:
617//! "\033OP" F1
618//! "\033[1;5A" Ctrl+up
619//! "\033[1;5B" Ctrl+down
620//! "\033[1;3A" Alt+up
621//! "\033[1;3B" Alt+down
622//! "\033[5;3~" Alt+page up
623//! "\033[6;3~" Alt+page down
624//! "\033+" Alt++
625//! "\033-" Alt+-
626//! "\033\t" Alt+tab
627//! "\033[1~" Alt+tab
628//!
629//! @param seq
630//! The key sequence to be bound
631//!
632//! @param func
633//! A function of type "int func(int, int)
634//
635void Readline::BindKeySequence(const char *seq, int (*func)(int, int))
636{
637 rl_bind_keyseq(seq, func);
638}
639
640// --------------------------------------------------------------------------
641//
642//! Calls rl_variable_dumper(1)
643//!
644//! Print the readline variable names and their current values
645//! to rl_outstream. If readable is non-zero, the list is formatted
646//! in such a way that it can be made part of an inputrc file and
647//! re-read.
648//!
649//! rl_outstream can be redirected using SetStreamOut()
650//!
651//! @returns
652//! always true
653//
654bool Readline::DumpVariables()
655{
656 rl_variable_dumper(1);
657 return true;
658}
659
660// --------------------------------------------------------------------------
661//
662//! Calls rl_function_dumper(1)
663//!
664//! Print the readline function names and the key sequences currently
665//! bound to them to rl_outstream. If readable is non-zero, the list
666//! is formatted in such a way that it can be made part of an inputrc
667//! file and re-read.
668//!
669//! rl_outstream can be redirected using SetStreamOut()
670//!
671//! @returns
672//! always true
673//
674bool Readline::DumpFunctions()
675{
676 rl_function_dumper(1);
677 return true;
678}
679
680// --------------------------------------------------------------------------
681//
682//! Calls rl_list_funmap_names()
683//!
684//! Print the names of all bindable Readline functions to rl_outstream.
685//!
686//! rl_outstream can be redirected using SetStreamOut()
687//!
688//! @returns
689//! always true
690//
691bool Readline::DumpFunmap()
692{
693 rl_list_funmap_names();
694 return true;
695}
696
697// --------------------------------------------------------------------------
698//
699//! Sets rl_outstream (the stdio stream to which Readline performs output)
700//! to the new stream.
701//!
702//! @param f
703//! The new stdio stream to which readline should perform its output
704//!
705//! @return
706//! The old stream to which readline was performing output
707//
708FILE *Readline::SetStreamOut(FILE *f)
709{
710 FILE *rc = rl_outstream;
711 rl_outstream = f;
712 return rc;
713}
714
715// --------------------------------------------------------------------------
716//
717//! Sets rl_instream (the stdio stream from which Readline reads input)
718//! to the new stream.
719//!
720//! @param f
721//! The new stdio stream from which readline should read its input
722//!
723//! @return
724//! The old stream from which readline was reading it input
725//
726FILE *Readline::SetStreamIn(FILE *f)
727{
728 FILE *rc = rl_instream;
729 rl_instream = f;
730 return rc;
731}
732
733// --------------------------------------------------------------------------
734//
735//! return rl_display_prompt (the prompt which should currently be
736//! displayed on the screen) while a readline command is in progress
737//
738string Readline::GetPrompt()
739{
740 return rl_display_prompt;
741}
742
743// --------------------------------------------------------------------------
744//
745//! return rl_line_buffer (the current input line which should currently be
746//! displayed on the screen) while a readline command is in progress
747//!
748//! The length of the current line buffer (rl_end) is available as
749//! GetLineBuffer().size()
750//!
751//! Note that after readline has returned the contents of rl_end might
752//! not reflect the correct buffer length anymore, hence, the returned buffer
753//! might be truncated.
754//
755string Readline::GetBuffer()
756{
757 return string(rl_line_buffer, rl_end);
758}
759
760// --------------------------------------------------------------------------
761//
762//! return rl_point (the current cursor position within the line buffer)
763//
764int Readline::GetCursor()
765{
766 return rl_point;
767}
768
769// --------------------------------------------------------------------------
770//
771//! return strlen(rl_display_prompt) + rl_point
772//
773int Readline::GetAbsCursor()
774{
775 return strlen(rl_display_prompt) + rl_point;
776}
777
778// --------------------------------------------------------------------------
779//
780//! return rl_end (the current total length of the line buffer)
781//! Note that after readline has returned the contents of rl_end might
782//! not reflect the correct buffer length anymore.
783//
784int Readline::GetBufferLength()
785{
786 return rl_end;
787}
788
789// --------------------------------------------------------------------------
790//
791//! return the length of the prompt plus the length of the line buffer
792//
793int Readline::GetLineLength()
794{
795 return strlen(rl_display_prompt) + rl_end;
796}
797
798// --------------------------------------------------------------------------
799//
800//! Calls: Function: void rl_resize_terminal()
801//! Update Readline's internal screen size by reading values from the kernel.
802//
803void Readline::Resize()
804{
805 rl_resize_terminal();
806}
807
808// --------------------------------------------------------------------------
809//
810//! Calls: Function: void rl_set_screen_size (int rows, int cols)
811//! Set Readline's idea of the terminal size to rows rows and cols columns.
812//!
813//! @param width
814//! Number of columns
815//!
816//! @param height
817//! Number of rows
818//
819void Readline::Resize(int width, int height)
820{
821 rl_set_screen_size(height, width);
822}
823
824// --------------------------------------------------------------------------
825//
826//! Get the number of cols readline assumes the screen size to be
827//
828int Readline::GetCols() const
829{
830 int rows, cols;
831 rl_get_screen_size(&rows, &cols);
832 return cols;
833}
834
835// --------------------------------------------------------------------------
836//
837//! Get the number of rows readline assumes the screen size to be
838//
839int Readline::GetRows() const
840{
841 int rows, cols;
842 rl_get_screen_size(&rows, &cols);
843 return rows;
844}
845
846// --------------------------------------------------------------------------
847//
848//! Return a list of pointer to the history contents
849//
850vector<const char*> Readline::GetHistory() const
851{
852 HIST_ENTRY **next = history_list();
853
854 vector<const char*> v;
855
856 for (; *next; next++)
857 v.push_back((*next)->line);
858
859 return v;
860}
861
862// --------------------------------------------------------------------------
863//
864//! Clear readline history (calls clear_history())
865//!
866//! @returns
867//! always true
868//
869bool Readline::ClearHistory()
870{
871 clear_history();
872 return true;
873}
874
875// --------------------------------------------------------------------------
876//
877//! Displays the current history on rl_outstream
878//!
879//! rl_outstream can be redirected using SetStreamOut()
880//!
881//! @returns
882//! always true
883//
884bool Readline::DumpHistory()
885{
886 HIST_ENTRY **next = history_list();
887
888 if (!next)
889 return true;
890
891 for (; *next; next++)
892 fprintf(rl_outstream, "%s\n", (*next)->line);
893
894 return true;
895}
896
897// --------------------------------------------------------------------------
898//
899//! Execute a shell command through a pipe. Write stdout to rl_outstream
900//!
901//! @param cmd
902//! Command to be executed
903//!
904//! @returns
905//! always true
906//
907bool Readline::ExecuteShellCommand(const string &cmd)
908{
909 FILE *pipe = popen(cmd.c_str(), "r");
910 if (!pipe)
911 {
912 fprintf(rl_outstream, "ERROR - Could not create pipe '%s': %m\n", cmd.c_str());
913 return true;
914 }
915
916 while (1)
917 {
918 char buf[1024];
919
920 const size_t sz = fread(buf, 1, 1024, pipe);
921
922 fwrite(buf, 1, sz, rl_outstream);
923
924 if (feof(pipe) || ferror(pipe))
925 break;
926 }
927
928 if (ferror(pipe))
929 fprintf(rl_outstream, "ERROR - Reading from pipe '%s': %m\n", cmd.c_str());
930
931 pclose(pipe);
932
933 fprintf(rl_outstream, "\n");
934
935 return true;
936}
937
938// --------------------------------------------------------------------------
939//
940//! Print the available commands. This is intended for being overwritten
941//! by deriving classes.
942//!
943//! rl_outstream can be redirected using SetStreamOut()
944//!
945//! @returns
946//! always true
947//
948//
949bool Readline::PrintCommands()
950{
951 fprintf(rl_outstream, "\n");
952 fprintf(rl_outstream, " Commands:\n");
953 fprintf(rl_outstream, " No application specific commands defined.\n");
954 fprintf(rl_outstream, "\n");
955 return true;
956}
957
958// --------------------------------------------------------------------------
959//
960//! Print a general help message. This is intended for being overwritten
961//! by deriving classes.
962//!
963//!
964//! rl_outstream can be redirected using SetStreamOut()
965//!
966//! @returns
967//! always true
968//
969//
970bool Readline::PrintGeneralHelp()
971{
972 fprintf(rl_outstream, "\n");
973 fprintf(rl_outstream, " General help:\n");
974 fprintf(rl_outstream, " h,help Print this help message\n");
975 fprintf(rl_outstream, " clear Clear history buffer\n");
976 fprintf(rl_outstream, " lh,history Dump the history buffer to the screen\n");
977 fprintf(rl_outstream, " v,variables Dump readline variables\n");
978 fprintf(rl_outstream, " f,functions Dump readline functions\n");
979 fprintf(rl_outstream, " m,funmap Dump readline funmap\n");
980 fprintf(rl_outstream, " c,commands Dump available commands\n");
981 fprintf(rl_outstream, " k,keylist Dump key bindings\n");
982 fprintf(rl_outstream, " .! command Execute a shell command\n");
983 fprintf(rl_outstream, " .w n Sleep n milliseconds\n");
984 fprintf(rl_outstream, " .x file .. Execute a script of commands (+optional argumnets)\n");
985 fprintf(rl_outstream, " .x file:N .. Execute a script of commands, start at label N\n");
986 fprintf(rl_outstream, " .j N Forward jump to label N\n");
987 fprintf(rl_outstream, " .lt f0 f1 N If float f0 lower than float f1, jump to label N\n");
988 fprintf(rl_outstream, " .gt f0 f1 N If float f0 greater than float f1, jump to label N\n");
989 fprintf(rl_outstream, " .eq i0 i1 N If int i0 equal int i1, jump to label N\n");
990 fprintf(rl_outstream, " : N Defines a label (N=number)\n");
991 fprintf(rl_outstream, " # comment Ignored\n");
992 fprintf(rl_outstream, " .q,quit Quit\n");
993 fprintf(rl_outstream, "\n");
994 fprintf(rl_outstream, " The command history is automatically loaded and saves to\n");
995 fprintf(rl_outstream, " and from %s.\n", GetName().c_str());
996 fprintf(rl_outstream, "\n");
997 return true;
998}
999
1000// --------------------------------------------------------------------------
1001//
1002//! Print a help text about key bindings. This is intended for being
1003//! overwritten by deriving classes.
1004//!
1005//!
1006//! rl_outstream can be redirected using SetStreamOut()
1007//!
1008//! @returns
1009//! always true
1010//
1011//
1012bool Readline::PrintKeyBindings()
1013{
1014 fprintf(rl_outstream, "\n");
1015 fprintf(rl_outstream, " Key bindings:\n");
1016 fprintf(rl_outstream, " Page-up Search backward in history\n");
1017 fprintf(rl_outstream, " Page-dn Search forward in history\n");
1018 fprintf(rl_outstream, " Ctrl-left One word backward\n");
1019 fprintf(rl_outstream, " Ctrl-right One word forward\n");
1020 fprintf(rl_outstream, " Ctrl-d Quit\n");
1021 fprintf(rl_outstream, " Ctrl-y Delete line\n");
1022 fprintf(rl_outstream, " Alt-end/Ctrl-k Delete until the end of the line\n");
1023 fprintf(rl_outstream, " F1 Toggle visibility of upper panel\n");
1024 fprintf(rl_outstream, "\n");
1025 fprintf(rl_outstream, " Default key-bindings are identical with your bash.\n");
1026 fprintf(rl_outstream, "\n");
1027 return true;
1028}
1029
1030// --------------------------------------------------------------------------
1031//
1032//!
1033//
1034bool Readline::PreProcess(const string &str)
1035{
1036 // ----------- Labels -------------
1037
1038 if (str[0]==':')
1039 {
1040 try
1041 {
1042 fSection = stoi(str.substr(1));
1043 SetSection(fSection);
1044
1045 if (fLabel!=fSection)
1046 return true;
1047 }
1048 catch (const logic_error &e)
1049 {
1050 fCommandLog << "# ERROR[" << fScriptDepth << "] - Inavlid label '" << str.substr(1) << "'" << endl;
1051 fLabel = -2;
1052 return true;
1053 }
1054
1055 fLabel=-1;
1056 return false;
1057 }
1058
1059 if (fLabel>=0)
1060 {
1061 fCommandLog << "# SKIP[" << fScriptDepth << "]: " << fLabel << " - " << str << endl;
1062 return true;
1063 }
1064
1065 if (str.substr(0, 3)==".j ")
1066 {
1067 fLabel = atoi(str.substr(3).c_str());
1068 return false;
1069 }
1070
1071 return Process(str);
1072
1073}
1074
1075// --------------------------------------------------------------------------
1076//
1077//!
1078//
1079bool Readline::Process(const string &str)
1080{
1081 // ----------- Common commands -------------
1082
1083 if (str.substr(0, 3)==".w ")
1084 {
1085 usleep(stoi(str.substr(3))*1000);
1086 return true;
1087 }
1088
1089 if (str.substr(0, 3)==".x ")
1090 {
1091 string opt(str.substr(3));
1092
1093 map<string,string> data = Tools::Split(opt);
1094 if (opt.size()==0)
1095 {
1096 if (data.size()==0)
1097 PrintReadlineError("Filename missing.");
1098 else
1099 PrintReadlineError("Equal sign missing in argument '"+data.begin()->first+"'");
1100
1101 return true;
1102 }
1103
1104 const string save = fScript;
1105 const int save_sec = fSection;
1106 Execute(opt, data);
1107 fScript = save;
1108 if (save_sec!=-4)
1109 {
1110 fSection = save_sec;
1111 SetSection(save_sec);
1112 }
1113
1114 return true;
1115 }
1116
1117 if (str.substr(0, 2)==".!")
1118 {
1119 ExecuteShellCommand(str.substr(2));
1120 return true;
1121 }
1122
1123 if (str.substr(0, 4)==".gt ")
1124 {
1125 istringstream in(str.substr(4));
1126
1127 float v0, v1;
1128 int label;
1129
1130 in >> v0 >> v1 >> label;
1131 if (in.fail())
1132 {
1133 PrintReadlineError("Couldn't parse '"+str+"'");
1134 fLabel = -2;
1135 return true;
1136 }
1137
1138 if (v0 > v1)
1139 fLabel = label;
1140
1141 return true;
1142 }
1143
1144 if (str.substr(0, 4)==".lt ")
1145 {
1146 istringstream in(str.substr(4));
1147
1148 float v0, v1;
1149 int label;
1150
1151 in >> v0 >> v1 >> label;
1152 if (in.fail())
1153 {
1154 PrintReadlineError("Couldn't parse '"+str+"'");
1155 fLabel = -2;
1156 return true;
1157 }
1158
1159 if (v0 < v1)
1160 fLabel = label;
1161
1162 return true;
1163 }
1164
1165 if (str.substr(0, 4)==".eq ")
1166 {
1167 istringstream in(str.substr(4));
1168
1169 int v0, v1, label;
1170
1171 in >> v0 >> v1 >> label;
1172 if (in.fail())
1173 {
1174 PrintReadlineError("Couldn't parse '"+str+"'");
1175 fLabel = -2;
1176 return true;
1177 }
1178
1179 if (v0==v1)
1180 fLabel = label;
1181
1182 return true;
1183 }
1184
1185
1186 // ----------- Readline static -------------
1187
1188 if (str=="clear")
1189 return ClearHistory();
1190
1191 if (str=="lh" || str=="history")
1192 return DumpHistory();
1193
1194 if (str=="v" || str=="variables")
1195 return DumpVariables();
1196
1197 if (str=="f" || str=="functions")
1198 return DumpFunctions();
1199
1200 if (str=="m" || str=="funmap")
1201 return DumpFunmap();
1202
1203 // ---------- Readline virtual -------------
1204
1205 if (str=="h" || str=="help")
1206 return PrintGeneralHelp();
1207
1208 if (str=="c" || str=="commands")
1209 return PrintCommands();
1210
1211 if (str=="k" || str=="keylist")
1212 return PrintKeyBindings();
1213
1214 return false;
1215}
1216
1217// --------------------------------------------------------------------------
1218//
1219//! This function is a wrapper around the call to readline. It encapsultes
1220//! the return buffer into a std::string and deletes the memory allocated
1221//! by readline. Furthermore, it removes leading and trailing whitespaces
1222//! before return the result. The result is returned in the given
1223//! argument containing the prompt. Before the function returns Shutdown()
1224//! is called (as opposed to Startup when readline starts)
1225//!
1226//! @param str
1227//! The prompt which is to be shown by the readline libarary. it is
1228//! directly given to the call to readline. The result of the
1229//! readline call is returned in this string.
1230//!
1231//! @returns
1232//! true if the call succeeded as usual, false if EOF was detected
1233//! by the readline call.
1234//
1235bool Readline::PromptEOF(string &str)
1236{
1237 char *buf = readline(str.c_str());
1238 Shutdown(buf);
1239
1240 // Happens when EOF is encountered
1241 if (!buf)
1242 return false;
1243
1244 str = Tools::Trim(buf);
1245
1246 free(buf);
1247
1248 return true;
1249}
1250
1251// --------------------------------------------------------------------------
1252//
1253//! This function is a wrapper around the call to readline. It encapsultes
1254//! the return buffer into a std::string and deletes the memory allocated
1255//! by readline. Furthermore, it removes leading and trailing whitespaces
1256//! before return the result. Before the function returns Shutdown() is
1257//! called (as opposed to Startup when readline starts)
1258//!
1259//! @param prompt
1260//! The prompt which is to be shown by the readline libarary. it is
1261//! directly given to the call to readline.
1262//!
1263//! @returns
1264//! The result of the readline call
1265//
1266string Readline::Prompt(const string &prompt)
1267{
1268 char *buf = readline(prompt.c_str());
1269
1270 Shutdown(buf ? buf : "");
1271
1272 const string str = !buf || (rl_done && rl_pending_input==4)
1273 ? ".q" : Tools::Trim(buf);
1274
1275 free(buf);
1276
1277 return str;
1278}
1279
1280// --------------------------------------------------------------------------
1281//
1282//! Writes the current history to the file defined by fName and
1283//! replaces the history by the data from the given file.
1284//!
1285//! @param fname
1286//! Name of the history file to read
1287//!
1288void Readline::StaticPushHistory(const string &fname="")
1289{
1290 fs::path his = fs::path(This->fName).parent_path();
1291 his /= fname;
1292
1293 write_history(This->fName.c_str());
1294 stifle_history(0);
1295 unstifle_history();
1296 read_history(his.string().c_str());
1297}
1298
1299// --------------------------------------------------------------------------
1300//
1301//! Writes the current history to the file with the given name
1302//! and replaces the history by the file defined by fName.
1303//!
1304//! @param fname
1305//! Name of the history file to write (it will be truncated to 1000 lines)
1306//!
1307void Readline::StaticPopHistory(const string &fname="")
1308{
1309 fs::path his = fs::path(This->fName).parent_path();
1310 his /= fname;
1311
1312 write_history(his.string().c_str());
1313 history_truncate_file(his.string().c_str(), 1000);
1314
1315 stifle_history(0);
1316 unstifle_history();
1317 read_history(This->fName.c_str());
1318}
1319
1320// --------------------------------------------------------------------------
1321//
1322//! Just calls readline and thus allows to just prompt for something.
1323//! Adds everything to the history except '.q'
1324//!
1325//! @param prompt
1326//! Prompt to be displayed
1327//!
1328//! @return
1329//! String entered by the user ('.q' is Ctrl-d is pressed)
1330//!
1331string Readline::StaticPrompt(const string &prompt)
1332{
1333 char *buf = readline(prompt.c_str());
1334 if (!buf)
1335 return ".q";
1336
1337 const string str(buf);
1338 if (Tools::Trim(str)!=".q" && !Tools::Trim(str).empty())
1339 if (history_length==0 || history_search_pos(str.c_str(), -1, history_length-1)!=history_length-1)
1340 add_history(buf);
1341
1342 free(buf);
1343
1344 return str;
1345}
1346
1347// --------------------------------------------------------------------------
1348//
1349//! Process a single line. All lines are added to the history, but only
1350//! accepted lines are written to the command log. In this case fLine is
1351//! increased by one.
1352//! Comment (starting with #) are removed from the command-line. A # can be
1353//! escaped with quotation marks "#"
1354//
1355void Readline::ProcessLine(const string &str)
1356{
1357 const string cmd = Tools::Uncomment(str);
1358
1359 if (!cmd.empty())
1360 {
1361 const bool rc = PreProcess(cmd);
1362
1363 AddToHistory(cmd);
1364
1365 if (rc)
1366 return;
1367
1368 fLine++;
1369 }
1370
1371 fCommandLog << str << endl;
1372}
1373
1374// --------------------------------------------------------------------------
1375//
1376//! This implements a loop over readline calls. A prompt to issue can be
1377//! given. If it is NULL, the prompt is retrieved from GetUpdatePrompt().
1378//! It is updated regularly by means of calls to GetUpdatePrompt() from
1379//! EventHook(). If Prompt() returns with "quit" or ".q" the loop is
1380//! exited. If ".qqq" is entered exit(-1) is called. In case of ".qqqqqq"
1381//! abort(). Both ways to exit the program are not recommended. Empty
1382//! inputs are ignored. After that Process() with the returned string
1383//! is called. If Process returns true the input is not counted and not
1384//! added to the history, otherwise the line counter is increased
1385//! and the input is added to the history.
1386//!
1387//! @param prompt
1388//! The prompt to be issued or NULL if GetUPdatePrompt should be used
1389//! instead.
1390//!
1391void Readline::Run(const char *prompt)
1392{
1393 fLine = 0;
1394 while (1)
1395 {
1396 // Before we start we have to make sure that the
1397 // screen looks like and is ordered like expected.
1398 const string str = Prompt(prompt?prompt:GetUpdatePrompt());
1399 if (str.empty())
1400 continue;
1401
1402 if (str=="quit" || str==".q")
1403 break;
1404
1405 if (str==".qqq")
1406 exit(128);
1407
1408 if (str==".qqqqqq")
1409 abort();
1410
1411 ProcessLine(str);
1412 }
1413}
1414
1415// --------------------------------------------------------------------------
1416//
1417//! Executes commands read from an ascii file as they were typed in
1418//! the console. Empty lines and lines beginning with # are ignored.
1419//!
1420//! @param fname
1421//! Filename of file to read
1422//!
1423//! @param args
1424//! Arguments to be passed to the script. A search and replace
1425//! will be done for ${arg}
1426//!
1427//! @returns
1428//! -1 if the file couldn't be read and the number of commands for which
1429//! Process() was callled otherwise
1430//!
1431int Readline::Execute(const string &fname, const map<string,string> &args)
1432{
1433 // this could do the same:
1434 // rl_instream = fopen(str.c_str(), "r");
1435
1436 if (IsStopped())
1437 return 0;
1438
1439 string name = Tools::Trim(fname);
1440 fScript = name;
1441
1442 fSection = -3;
1443 SetSection(-3);
1444 fLabel = -1;
1445
1446 const size_t p = name.find_last_of(':');
1447 if (p!=string::npos)
1448 {
1449 fLabel = atoi(name.substr(p+1).c_str());
1450 name = name.substr(0, p);
1451 }
1452
1453 ifstream fin(name.c_str());
1454 if (!fin)
1455 {
1456 fSection = -4;
1457 SetSection(-4);
1458 return -1;
1459 }
1460
1461 if (fScriptDepth++==0)
1462 fStopScript = false;
1463
1464 fCommandLog << "# " << Time() << " - " << name << " (START[" << fScriptDepth<< "]";
1465 if (fLabel>=0)
1466 fCommandLog << ':' << fLabel;
1467 fCommandLog << ")" << endl;
1468
1469 fSection = -1;
1470 SetSection(-1);
1471
1472 int rc = 0;
1473
1474 string buffer;
1475 while (getline(fin, buffer, '\n') && !fStopScript)
1476 {
1477 buffer = Tools::Trim(buffer);
1478 if (buffer.empty())
1479 continue;
1480
1481 rc++;
1482
1483 if (buffer=="quit" || buffer==".q")
1484 {
1485 Stop();
1486 break;
1487 }
1488
1489 // find and replace arguments
1490 for (auto it=args.begin(); it!=args.end(); it++)
1491 {
1492 const string find = "${"+it->first+"}";
1493 for (size_t pos=0; (pos=buffer.find(find, pos))!=string::npos; pos+=find.length())
1494 buffer.replace(pos, find.size(), it->second);
1495 }
1496
1497 // process line
1498 ProcessLine(buffer);
1499 }
1500
1501 fCommandLog << "# " << Time() << " - " << name << " (FINISHED[" << fScriptDepth<< "])" << endl;
1502
1503 if (--fScriptDepth==0)
1504 fStopScript = false;
1505
1506 fLabel = -1;
1507 fSection = -4;
1508 SetSection(-4);
1509
1510 return rc;
1511}
1512
1513// --------------------------------------------------------------------------
1514//
1515//! Stops the readline execution. It fakes the end of an editing by
1516//! setting rl_done to 1. To distinguish this from a normal edit,
1517//! rl_pending_input is set to EOT.
1518//!
1519void Readline::Stop()
1520{
1521 rl_done = 1;
1522 rl_pending_input = 4; // EOT (end of transmission, ctrl-d)
1523}
1524
1525// --------------------------------------------------------------------------
1526//
1527//! @returns
1528//! the status of rl_done and rl_pending_input. If rl_done==1 and
1529//! rl_pending_input==4 true is returned, false otherwise.
1530//! This can be used to check if Stop() was called. If readline is
1531//! not in operation.
1532//!
1533bool Readline::IsStopped() const
1534{
1535 return rl_done==1 && rl_pending_input==4;
1536};
1537
1538void Readline::PrintReadlineError(const std::string &str)
1539{
1540 fprintf(rl_outstream, "%s\n", str.c_str());
1541}
Note: See TracBrowser for help on using the repository browser.