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

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