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

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