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

Last change on this file since 18833 was 18833, checked in by tbretz, 7 years ago
Secured Tools::Split against exceptions from boost::tokenize
File size: 43.1 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//
249void Readline::rl_ncurses_redisplay()
250{
251 This->Redisplay();
252}
253
254// --------------------------------------------------------------------------
255//
256//! Redirected from rl_event_hook, calls Update().
257//! A function called periodically when readline is waiting for
258//! terminal input.
259//!
260int Readline::rl_ncurses_event_hook()
261{
262 This->EventHook();
263 return 0;
264}
265
266// --------------------------------------------------------------------------
267//
268//! Redirected from rl_completion_display_matches_hook,
269//! calls CompletionDisplayImp
270//!
271//! A function to be called when completing a word would normally display
272//! the list of possible matches. This function is called in lieu of
273//! Readline displaying the list. It takes three arguments:
274//! (char **matches, int num_matches, int max_length) where matches is
275//! the array of matching strings, num_matches is the number of strings
276//! in that array, and max_length is the length of the longest string in
277//! that array. Readline provides a convenience function,
278//! rl_display_match_list, that takes care of doing the display to
279//! Readline's output stream.
280//
281void Readline::rl_ncurses_completion_display(char **matches, int num, int max)
282{
283 This->CompletionDisplay(matches, num, max);
284}
285
286char **Readline::rl_ncurses_completion_function(const char *text, int start, int end)
287{
288 return This->Completion(text, start, end);
289}
290
291// --------------------------------------------------------------------------
292//
293//! Calls the default rl_getc function.
294//
295int Readline::Getc(FILE *f)
296{
297 return rl_getc(f);
298}
299
300// --------------------------------------------------------------------------
301//
302//! Default: Do nothing.
303//
304void Readline::Startup()
305{
306}
307
308// --------------------------------------------------------------------------
309//
310//! The default is to redisplay the prompt which is gotten from
311//! GetUpdatePrompt(). If GetUpdatePrompt() returns an empty string the
312//! prompt is kept untouched. This can be used to keep a prompt updated
313//! with some information (e.g. time) just by overwriting GetUpdatePrompt()
314//!
315void Readline::EventHook(bool newline)
316{
317 const string cpy = fExternalInput;
318 fExternalInput = "";
319
320 if (!cpy.empty())
321 {
322 rl_replace_line(cpy.c_str(), 1);
323 rl_done = 1;
324 }
325
326 string p = GetUpdatePrompt();
327 if (p.empty())
328 p = rl_prompt;
329
330 if (newline)
331 rl_on_new_line();
332
333 if (rl_prompt==p && !newline)
334 return;
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 rl_forced_update_display();
390}
391
392// --------------------------------------------------------------------------
393//
394//! This is a static helper for the compilation of a completion-list.
395//! It compares the two inputs (str and txt) to a maximum of the size of
396//! txt. If they match, memory is allocated with malloc and a pointer to
397//! the null-terminated version of str is returned.
398//!
399//! @param str
400//! A reference to the string which is checked (e.g. "Makefile.am")
401//!
402//! @param txt
403//! A reference to the part of the string the user has already typed,
404//! e.g. "Makef"
405//!
406//! @returns
407//! A pointer to memory allocated with malloc containing the string str
408//
409char *Readline::Compare(const string &str, const string &txt)
410{
411 /*return strncmp(str.c_str(), txt.c_str(), txt.length())==0 ? */
412 return strncasecmp(str.c_str(), txt.c_str(), txt.length())==0 ?
413 strndup(str.c_str(), str.length()) : 0;
414}
415
416char **Readline::CompletionMatches(const char *text, char *(*func)(const char*, int))
417{
418 return rl_completion_matches(text, func);
419}
420
421// --------------------------------------------------------------------------
422//
423//! The given vector should be a reference to a vector of strings
424//! containing all possible matches. The actual match-making is then
425//! done in Complete(const char *, int)
426//!
427//! The pointer fCompletion is redirected to the vector for the run time
428//! of the function, but restored afterwards. So by this you can set a
429//! default completion list in case Complete is not called or Completion
430//! not overloaded.
431//!
432//! @param v
433//! reference to a vector of strings with all possible matches
434//!
435//! @param text
436//! the text which should be matched (it is just propagated to
437//! Readline::Completion)
438//!
439char **Readline::Complete(const vector<string> &v, const char *text)
440{
441 const vector<string> *save = fCompletion;
442
443 fCompletion = &v;
444 char **rc = rl_completion_matches(const_cast<char*>(text), CompleteImp);
445 fCompletion = save;
446
447 return rc;
448}
449
450// --------------------------------------------------------------------------
451//
452//! If fCompletion==0 the default is to call readline's
453//! rl_filename_completion_function. Otherwise the contents of fCompletion
454//! are returned. To change fCompletion either initialize it via
455//! SetCompletion() (in this case you must ensure the life time of the
456//! object) or call
457//! Complete(const vector<string>&, const char*)
458//! from
459//! Completion(const char * int, int)
460//!
461//! This is the so called generator function, the readline manual says
462//! about this function:
463//!
464//! The generator function is called repeatedly from
465//! rl_completion_matches(), returning a string each time. The arguments
466//! to the generator function are text and state. text is the partial word
467//! to be completed. state is zero the first time the function is called,
468//! allowing the generator to perform any necessary initialization, and a
469//! positive non-zero integer for each subsequent call. The generator
470//! function returns (char *)NULL to inform rl_completion_matches() that
471//! there are no more possibilities left. Usually the generator function
472//! computes the list of possible completions when state is zero, and
473//! returns them one at a time on subsequent calls. Each string the
474//! generator function returns as a match must be allocated with malloc();
475//! Readline frees the strings when it has finished with them.
476//
477char *Readline::Complete(const char* text, int state)
478{
479 if (fCompletion==0)
480 return rl_filename_completion_function(text, state);
481
482 static vector<string>::const_iterator pos;
483 if (state==0)
484 pos = fCompletion->begin();
485
486 while (pos!=fCompletion->end())
487 {
488 char *rc = Compare(*pos++, text);
489 if (rc)
490 return rc;
491 }
492
493 return 0;
494}
495
496// --------------------------------------------------------------------------
497//
498//! Calls Complete()
499//
500char *Readline::CompleteImp(const char* text, int state)
501{
502 return This->Complete(text, state);
503}
504
505// --------------------------------------------------------------------------
506//
507//! The readline manual says about this function:
508//!
509//! A pointer to an alternative function to create matches. The
510//! function is called with text, start, and end. start and end are
511//! indices in rl_line_buffer saying what the boundaries of text are.
512//! If this function exists and returns NULL, or if this variable is
513//! set to NULL, then rl_complete() will call the value of
514//! rl_completion_entry_function to generate matches, otherwise the
515//! array of strings returned will be used.
516//!
517//! This function is virtual and can be overwritten. It defaults to
518//! a call to rl_completion_matches with CompleteImp as an argument
519//! which defaults to filename completion, but can also be overwritten.
520//!
521//! It is suggested that you call
522//! Complete(const vector<string>&, const char*)
523//! from here.
524//!
525//! @param text
526//! A pointer to a char array conatining the text which should be
527//! completed. The text is null-terminated.
528//!
529//! @param start
530//! The start index within readline's line buffer rl_line_buffer,
531//! at which the text starts which presumably should be completed.
532//!
533//! @param end
534//! The end index within readline's line buffer rl_line_buffer,
535//! at which the text ends which presumably should be completed.
536//!
537//! @returns
538//! An array of strings which were allocated with malloc and which
539//! will be freed by readline with the possible matches.
540//
541char **Readline::Completion(const char *text, int /*start*/, int /*end*/)
542{
543 // To do filename completion call
544 return rl_completion_matches((char*)text, CompleteImp);
545}
546
547// --------------------------------------------------------------------------
548//
549//! Adds the given string to the history buffer of readline's history by
550//! calling add_history.
551//!
552//! @param str
553//! A reference to a string which should be added to readline's
554//! history.
555//!
556//! @param skip
557//! If skip is 1 and str matches the last added entry in the history,
558//! the entry is skipped. If skip==2, all entries matching str are
559//! removed from the history before the new entry is added as last one.
560//! <skip==2 is the default>
561//
562void Readline::AddToHistory(const string &str, int skip)
563{
564 if (skip==1 && fLastLine==str)
565 return;
566
567 if (str.empty())
568 return;
569
570 int p = -1;
571 while (skip==2)
572 {
573 p = history_search_pos(str.c_str(), 0, p+1);
574 if (p<0)
575 break;
576
577 HIST_ENTRY *e = remove_history(p--);
578
579 free(e->line);
580 free(e);
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, " Home Beginning of line\n");
1034 fprintf(rl_outstream, " End End of line\n");
1035 fprintf(rl_outstream, " Ctrl-d Quit\n");
1036 fprintf(rl_outstream, " Ctrl-y Delete line\n");
1037 fprintf(rl_outstream, " Alt-end/Ctrl-k Delete until the end of the line\n");
1038 fprintf(rl_outstream, " F1 Toggle visibility of upper panel\n");
1039 fprintf(rl_outstream, "\n");
1040 fprintf(rl_outstream, " Default key-bindings are identical with your bash.\n");
1041 fprintf(rl_outstream, "\n");
1042 return true;
1043}
1044
1045// --------------------------------------------------------------------------
1046//
1047//!
1048//
1049bool Readline::PreProcess(const string &str)
1050{
1051 // ----------- Labels -------------
1052
1053 if (str[0]==':')
1054 {
1055 try
1056 {
1057 fSection = stoi(str.substr(1));
1058 SetSection(fSection);
1059
1060 if (fLabel!=fSection)
1061 return true;
1062 }
1063 catch (const logic_error &e)
1064 {
1065 fCommandLog << "# ERROR[" << fScriptDepth << "] - Inavlid label '" << str.substr(1) << "'" << endl;
1066 fLabel = -2;
1067 return true;
1068 }
1069
1070 fLabel=-1;
1071 return false;
1072 }
1073
1074 if (fLabel>=0)
1075 {
1076 fCommandLog << "# SKIP[" << fScriptDepth << "]: " << fLabel << " - " << str << endl;
1077 return true;
1078 }
1079
1080 if (str.substr(0, 3)==".j ")
1081 {
1082 fLabel = atoi(str.substr(3).c_str());
1083 return false;
1084 }
1085
1086 return Process(str);
1087
1088}
1089
1090// --------------------------------------------------------------------------
1091//
1092//!
1093//
1094bool Readline::Process(const string &str)
1095{
1096 // ----------- Common commands -------------
1097
1098 if (str.substr(0, 3)==".w ")
1099 {
1100 usleep(stoi(str.substr(3))*1000);
1101 return true;
1102 }
1103
1104 if (str.substr(0, 3)==".x ")
1105 {
1106 string opt(str.substr(3));
1107
1108 map<string,string> data = Tools::Split(opt);
1109 if (opt.size()==0)
1110 {
1111 if (data.size()==0)
1112 PrintReadlineError("Filename missing.");
1113 else
1114 PrintReadlineError("Equal sign missing in argument '"+data.begin()->first+"'");
1115
1116 return true;
1117 }
1118
1119 const string save = fScript;
1120 const int save_sec = fSection;
1121 Execute(opt, data);
1122 fScript = save;
1123 if (save_sec!=-4)
1124 {
1125 fSection = save_sec;
1126 SetSection(save_sec);
1127 }
1128
1129 return true;
1130 }
1131
1132 if (str.substr(0, 2)==".!")
1133 {
1134 ExecuteShellCommand(str.substr(2));
1135 return true;
1136 }
1137
1138 if (str.substr(0, 4)==".gt ")
1139 {
1140 istringstream in(str.substr(4));
1141
1142 float v0, v1;
1143 int label;
1144
1145 in >> v0 >> v1 >> label;
1146 if (in.fail())
1147 {
1148 PrintReadlineError("Couldn't parse '"+str+"'");
1149 fLabel = -2;
1150 return true;
1151 }
1152
1153 if (v0 > v1)
1154 fLabel = label;
1155
1156 return true;
1157 }
1158
1159 if (str.substr(0, 4)==".lt ")
1160 {
1161 istringstream in(str.substr(4));
1162
1163 float v0, v1;
1164 int label;
1165
1166 in >> v0 >> v1 >> label;
1167 if (in.fail())
1168 {
1169 PrintReadlineError("Couldn't parse '"+str+"'");
1170 fLabel = -2;
1171 return true;
1172 }
1173
1174 if (v0 < v1)
1175 fLabel = label;
1176
1177 return true;
1178 }
1179
1180 if (str.substr(0, 4)==".eq ")
1181 {
1182 istringstream in(str.substr(4));
1183
1184 int v0, v1, label;
1185
1186 in >> v0 >> v1 >> label;
1187 if (in.fail())
1188 {
1189 PrintReadlineError("Couldn't parse '"+str+"'");
1190 fLabel = -2;
1191 return true;
1192 }
1193
1194 if (v0==v1)
1195 fLabel = label;
1196
1197 return true;
1198 }
1199
1200
1201 // ----------- Readline static -------------
1202
1203 if (str=="clear")
1204 return ClearHistory();
1205
1206 if (str=="lh" || str=="history")
1207 return DumpHistory();
1208
1209 if (str=="v" || str=="variables")
1210 return DumpVariables();
1211
1212 if (str=="f" || str=="functions")
1213 return DumpFunctions();
1214
1215 if (str=="m" || str=="funmap")
1216 return DumpFunmap();
1217
1218 // ---------- Readline virtual -------------
1219
1220 if (str=="h" || str=="help")
1221 return PrintGeneralHelp();
1222
1223 if (str=="c" || str=="commands")
1224 return PrintCommands();
1225
1226 if (str=="k" || str=="keylist")
1227 return PrintKeyBindings();
1228
1229 return false;
1230}
1231
1232// --------------------------------------------------------------------------
1233//
1234//! This function is a wrapper around the call to readline. It encapsultes
1235//! the return buffer into a std::string and deletes the memory allocated
1236//! by readline. Furthermore, it removes leading and trailing whitespaces
1237//! before return the result. The result is returned in the given
1238//! argument containing the prompt. Before the function returns Shutdown()
1239//! is called (as opposed to Startup when readline starts)
1240//!
1241//! @param str
1242//! The prompt which is to be shown by the readline libarary. it is
1243//! directly given to the call to readline. The result of the
1244//! readline call is returned in this string.
1245//!
1246//! @returns
1247//! true if the call succeeded as usual, false if EOF was detected
1248//! by the readline call.
1249//
1250bool Readline::PromptEOF(string &str)
1251{
1252 char *buf = readline(str.c_str());
1253 Shutdown(buf);
1254
1255 // Happens when EOF is encountered
1256 if (!buf)
1257 return false;
1258
1259 str = Tools::Trim(buf);
1260
1261 free(buf);
1262
1263 return true;
1264}
1265
1266// --------------------------------------------------------------------------
1267//
1268//! This function is a wrapper around the call to readline. It encapsultes
1269//! the return buffer into a std::string and deletes the memory allocated
1270//! by readline. Furthermore, it removes leading and trailing whitespaces
1271//! before return the result. Before the function returns Shutdown() is
1272//! called (as opposed to Startup when readline starts)
1273//!
1274//! @param prompt
1275//! The prompt which is to be shown by the readline libarary. it is
1276//! directly given to the call to readline.
1277//!
1278//! @returns
1279//! The result of the readline call
1280//
1281string Readline::Prompt(const string &prompt)
1282{
1283 char *buf = readline(prompt.c_str());
1284
1285 Shutdown(buf ? buf : "");
1286
1287 const string str = !buf || (rl_done && rl_pending_input==4)
1288 ? ".q" : Tools::Trim(buf);
1289
1290 free(buf);
1291
1292 return str;
1293}
1294
1295// --------------------------------------------------------------------------
1296//
1297//! Writes the current history to the file defined by fName and
1298//! replaces the history by the data from the given file.
1299//!
1300//! @param fname
1301//! Name of the history file to read
1302//!
1303void Readline::StaticPushHistory(const string &fname="")
1304{
1305 fs::path his = fs::path(This->fName).parent_path();
1306 his /= fname;
1307
1308 write_history(This->fName.c_str());
1309 stifle_history(0);
1310 unstifle_history();
1311 read_history(his.string().c_str());
1312}
1313
1314// --------------------------------------------------------------------------
1315//
1316//! Writes the current history to the file with the given name
1317//! and replaces the history by the file defined by fName.
1318//!
1319//! @param fname
1320//! Name of the history file to write (it will be truncated to 1000 lines)
1321//!
1322void Readline::StaticPopHistory(const string &fname="")
1323{
1324 fs::path his = fs::path(This->fName).parent_path();
1325 his /= fname;
1326
1327 write_history(his.string().c_str());
1328 history_truncate_file(his.string().c_str(), 1000);
1329
1330 stifle_history(0);
1331 unstifle_history();
1332 read_history(This->fName.c_str());
1333}
1334
1335// --------------------------------------------------------------------------
1336//
1337//! Just calls readline and thus allows to just prompt for something.
1338//! Adds everything to the history except '.q'
1339//!
1340//! @param prompt
1341//! Prompt to be displayed
1342//!
1343//! @return
1344//! String entered by the user ('.q' is Ctrl-d is pressed)
1345//!
1346string Readline::StaticPrompt(const string &prompt)
1347{
1348 char *buf = readline(prompt.c_str());
1349 if (!buf)
1350 return ".q";
1351
1352 const string str(buf);
1353 if (Tools::Trim(str)!=".q" && !Tools::Trim(str).empty())
1354 if (history_length==0 || history_search_pos(str.c_str(), -1, history_length-1)!=history_length-1)
1355 add_history(buf);
1356
1357 free(buf);
1358
1359 return str;
1360}
1361
1362// --------------------------------------------------------------------------
1363//
1364//! Process a single line. All lines are added to the history, but only
1365//! accepted lines are written to the command log. In this case fLine is
1366//! increased by one.
1367//! Comment (starting with #) are removed from the command-line. A # can be
1368//! escaped with quotation marks "#"
1369//
1370void Readline::ProcessLine(const string &str)
1371{
1372 try
1373 {
1374 const string cmd = Tools::Uncomment(str);
1375
1376 if (!cmd.empty())
1377 {
1378 const bool rc = PreProcess(cmd);
1379
1380 AddToHistory(cmd);
1381
1382 if (rc)
1383 return;
1384
1385 fLine++;
1386 }
1387
1388 fCommandLog << str << endl;
1389 }
1390 catch (const exception &e)
1391 {
1392 PrintReadlineError("Couldn't parse: "+str+" ["+string(e.what())+"]");
1393 }
1394}
1395
1396// --------------------------------------------------------------------------
1397//
1398//! This implements a loop over readline calls. A prompt to issue can be
1399//! given. If it is NULL, the prompt is retrieved from GetUpdatePrompt().
1400//! It is updated regularly by means of calls to GetUpdatePrompt() from
1401//! EventHook(). If Prompt() returns with "quit" or ".q" the loop is
1402//! exited. If ".qqq" is entered exit(-1) is called. In case of ".qqqqqq"
1403//! abort(). Both ways to exit the program are not recommended. Empty
1404//! inputs are ignored. After that Process() with the returned string
1405//! is called. If Process returns true the input is not counted and not
1406//! added to the history, otherwise the line counter is increased
1407//! and the input is added to the history.
1408//!
1409//! @param prompt
1410//! The prompt to be issued or NULL if GetUPdatePrompt should be used
1411//! instead.
1412//!
1413void Readline::Run(const char *prompt)
1414{
1415 fLine = 0;
1416 while (1)
1417 {
1418 // Before we start we have to make sure that the
1419 // screen looks like and is ordered like expected.
1420 const string str = Prompt(prompt?prompt:GetUpdatePrompt());
1421 if (str.empty())
1422 continue;
1423
1424 if (str=="quit" || str==".q")
1425 break;
1426
1427 if (str==".qqq")
1428 exit(128);
1429
1430 if (str==".qqqqqq")
1431 abort();
1432
1433 ProcessLine(str);
1434 }
1435}
1436
1437// --------------------------------------------------------------------------
1438//
1439//! Executes commands read from an ascii file as they were typed in
1440//! the console. Empty lines and lines beginning with # are ignored.
1441//!
1442//! @param fname
1443//! Filename of file to read
1444//!
1445//! @param args
1446//! Arguments to be passed to the script. A search and replace
1447//! will be done for ${arg}
1448//!
1449//! @returns
1450//! -1 if the file couldn't be read and the number of commands for which
1451//! Process() was callled otherwise
1452//!
1453int Readline::Execute(const string &fname, const map<string,string> &args)
1454{
1455 // this could do the same:
1456 // rl_instream = fopen(str.c_str(), "r");
1457
1458 if (IsStopped())
1459 return 0;
1460
1461 string name = Tools::Trim(fname);
1462 fScript = name;
1463
1464 fSection = -3;
1465 SetSection(-3);
1466 fLabel = -1;
1467
1468 const size_t p = name.find_last_of(':');
1469 if (p!=string::npos)
1470 {
1471 fLabel = atoi(name.substr(p+1).c_str());
1472 name = name.substr(0, p);
1473 }
1474
1475 ifstream fin(name.c_str());
1476 if (!fin)
1477 {
1478 fSection = -4;
1479 SetSection(-4);
1480 return -1;
1481 }
1482
1483 if (fScriptDepth++==0)
1484 fStopScript = false;
1485
1486 fCommandLog << "# " << Time() << " - " << name << " (START[" << fScriptDepth<< "]";
1487 if (fLabel>=0)
1488 fCommandLog << ':' << fLabel;
1489 fCommandLog << ")" << endl;
1490
1491 fSection = -1;
1492 SetSection(-1);
1493
1494 int rc = 0;
1495
1496 string buffer;
1497 while (getline(fin, buffer, '\n') && !fStopScript)
1498 {
1499 buffer = Tools::Trim(buffer);
1500 if (buffer.empty())
1501 continue;
1502
1503 rc++;
1504
1505 if (buffer=="quit" || buffer==".q")
1506 {
1507 Stop();
1508 break;
1509 }
1510
1511 // find and replace arguments
1512 for (auto it=args.begin(); it!=args.end(); it++)
1513 {
1514 const string find = "${"+it->first+"}";
1515 for (size_t pos=0; (pos=buffer.find(find, pos))!=string::npos; pos+=find.length())
1516 buffer.replace(pos, find.size(), it->second);
1517 }
1518
1519 // process line
1520 ProcessLine(buffer);
1521 }
1522
1523 fCommandLog << "# " << Time() << " - " << name << " (FINISHED[" << fScriptDepth<< "])" << endl;
1524
1525 if (--fScriptDepth==0)
1526 fStopScript = false;
1527
1528 fLabel = -1;
1529 fSection = -4;
1530 SetSection(-4);
1531
1532 return rc;
1533}
1534
1535// --------------------------------------------------------------------------
1536//
1537//! Stops the readline execution. It fakes the end of an editing by
1538//! setting rl_done to 1. To distinguish this from a normal edit,
1539//! rl_pending_input is set to EOT.
1540//!
1541void Readline::Stop()
1542{
1543 rl_done = 1;
1544 rl_pending_input = 4; // EOT (end of transmission, ctrl-d)
1545}
1546
1547// --------------------------------------------------------------------------
1548//
1549//! @returns
1550//! the status of rl_done and rl_pending_input. If rl_done==1 and
1551//! rl_pending_input==4 true is returned, false otherwise.
1552//! This can be used to check if Stop() was called. If readline is
1553//! not in operation.
1554//!
1555bool Readline::IsStopped() const
1556{
1557 return rl_done==1 && rl_pending_input==4;
1558};
1559
1560void Readline::PrintReadlineError(const std::string &str)
1561{
1562 fprintf(rl_outstream, "%s\n", str.c_str());
1563}
Note: See TracBrowser for help on using the repository browser.