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

Last change on this file since 17288 was 16937, checked in by tbretz, 11 years ago
Some fixes for the display of command completion results; fixed the removal of double entries from the history; added Home and End to key bindings output
File size: 42.9 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 if (read_history(fName.c_str()))
132 cout << "WARNING - Reading " << fName << ": " << strerror(errno) << endl;
133
134 fCommandLog.open(string(prgname)+".evt");
135
136 // Setup the readline callback which are needed to redirect
137 // the otuput properly to our ncurses panel
138 rl_getc_function = rl_ncurses_getc;
139 rl_startup_hook = rl_ncurses_startup;
140 rl_redisplay_function = rl_ncurses_redisplay;
141 rl_event_hook = rl_ncurses_event_hook;
142 rl_completion_display_matches_hook = rl_ncurses_completion_display;
143
144 // Bind delete, page up, page down
145 rl_bind_keyseq("\e[1~", rl_named_function("beginning-of-line"));
146 rl_bind_keyseq("\e[3~", rl_named_function("delete-char"));
147 rl_bind_keyseq("\e[4~", rl_named_function("end-of-line"));
148 rl_bind_keyseq("\e[5~", rl_named_function("history-search-backward"));
149 rl_bind_keyseq("\e[6~", rl_named_function("history-search-forward"));
150 rl_bind_keyseq("\033[1;3F", rl_named_function("kill-line"));
151 rl_bind_keyseq("\033[1;5D", rl_named_function("backward-word"));
152 rl_bind_keyseq("\033[1;5C", rl_named_function("forward-word"));
153 rl_bind_key(25, rl_named_function("kill-whole-line"));
154
155 //for (int i=0; i<10; i++) cout << (int)getchar() << endl;
156}
157
158// --------------------------------------------------------------------------
159//
160//! Writes the current history to the file with the name stored in fName.
161//! In addition the written file is truncated to fMaxLines to keep the
162//! file of a reasonable size. The number of lines fMaxLines can be set
163//! by SetMaxLines before the destructor is called. Setting fMaxLines
164//! to 0 or a negative value switches automatic truncation off.
165//
166Readline::~Readline()
167{
168 // Write current history to file
169 if (write_history(fName.c_str()))
170 cout << "WARNING - Write " << fName.c_str() << ": " << strerror(errno) << endl;
171
172 // Truncate file
173 if (fMaxLines>0 && history_truncate_file(fName.c_str(), fMaxLines))
174 cout << "WARNING - Truncate " << fName.c_str() << ": " << strerror(errno) << endl;
175}
176
177// --------------------------------------------------------------------------
178//
179//! This wraps the given readline function such that the output can be
180//! redirected from thr rl_outstream to the given C++ ostream.
181//!
182//! @param out
183//! The stream to which the output should be redirected.
184//!
185//! @param function
186//! Takes a function of type bool(*)() as argument
187//!
188//! @returns
189//! The return value of the function
190//
191bool Readline::RedirectionWrapper(ostream &out, bool (*function)())
192{
193 FILE *save = SetStreamOut(tmpfile());
194 const bool rc = function();
195 FILE *file = SetStreamOut(save);
196
197 const bool empty = ftell(file)==0;
198
199 rewind(file);
200
201 if (empty)
202 {
203 out << " <empty>" << endl;
204 fclose(file);
205 return rc;
206 }
207
208 while (1)
209 {
210 const int c = getc(file);
211 if (feof(file))
212 break;
213 out << (char)c;
214 }
215 out << endl;
216
217 fclose(file);
218
219 return rc;
220}
221
222// --------------------------------------------------------------------------
223//
224//! Redirected from rl_getc_function, calls Getc
225//
226int Readline::rl_ncurses_getc(FILE *f)
227{
228 return This->Getc(f);
229}
230
231// --------------------------------------------------------------------------
232//
233//! Redirected from rl_startup_hook, calls Startup.
234//! A function called just before readline prints the first prompt.
235//
236int Readline::rl_ncurses_startup()
237{
238 This->Startup();
239 return 0; // What is this for?
240}
241
242// --------------------------------------------------------------------------
243//
244//! Redirected from rl_redisplay_function, calls Redisplay.
245//! Readline will call indirectly to update the display with the current
246//! contents of the editing buffer.
247//
248void Readline::rl_ncurses_redisplay()
249{
250 This->Redisplay();
251}
252
253// --------------------------------------------------------------------------
254//
255//! Redirected from rl_event_hook, calls Update().
256//! A function called periodically when readline is waiting for
257//! terminal input.
258//!
259int Readline::rl_ncurses_event_hook()
260{
261 This->EventHook();
262 return 0;
263}
264
265// --------------------------------------------------------------------------
266//
267//! Redirected from rl_completion_display_matches_hook,
268//! calls CompletionDisplayImp
269//!
270//! A function to be called when completing a word would normally display
271//! the list of possible matches. This function is called in lieu of
272//! Readline displaying the list. It takes three arguments:
273//! (char **matches, int num_matches, int max_length) where matches is
274//! the array of matching strings, num_matches is the number of strings
275//! in that array, and max_length is the length of the longest string in
276//! that array. Readline provides a convenience function,
277//! rl_display_match_list, that takes care of doing the display to
278//! Readline's output stream.
279//
280void Readline::rl_ncurses_completion_display(char **matches, int num, int max)
281{
282 This->CompletionDisplay(matches, num, max);
283}
284
285char **Readline::rl_ncurses_completion_function(const char *text, int start, int end)
286{
287 return This->Completion(text, start, end);
288}
289
290// --------------------------------------------------------------------------
291//
292//! Calls the default rl_getc function.
293//
294int Readline::Getc(FILE *f)
295{
296 return rl_getc(f);
297}
298
299// --------------------------------------------------------------------------
300//
301//! Default: Do nothing.
302//
303void Readline::Startup()
304{
305}
306
307// --------------------------------------------------------------------------
308//
309//! The default is to redisplay the prompt which is gotten from
310//! GetUpdatePrompt(). If GetUpdatePrompt() returns an empty string the
311//! prompt is kept untouched. This can be used to keep a prompt updated
312//! with some information (e.g. time) just by overwriting GetUpdatePrompt()
313//!
314void Readline::EventHook(bool newline)
315{
316 const string cpy = fExternalInput;
317 fExternalInput = "";
318
319 if (!cpy.empty())
320 {
321 rl_replace_line(cpy.c_str(), 1);
322 rl_done = 1;
323 }
324
325 string p = GetUpdatePrompt();
326 if (p.empty())
327 p = rl_prompt;
328
329 if (newline)
330 rl_on_new_line();
331
332 if (rl_prompt==p && !newline)
333 return;
334
335 UpdatePrompt(p);
336
337 int w, h;
338 rl_get_screen_size(&h, &w);
339 cout << '\r' << string(w+1, ' ') << '\r';
340 rl_forced_update_display();
341}
342
343// --------------------------------------------------------------------------
344//
345//! Called from Prompt and PromptEOF after readline has returned. It is
346//! meant as the opposite of Startup (called after readline finsihes)
347//! The default is to do nothing.
348//!
349//! @param buf
350//! A pointer to the buffer returned by readline
351//
352void Readline::Shutdown(const char *)
353{
354}
355
356// --------------------------------------------------------------------------
357//
358//! Default: call rl_redisplay()
359//
360
361void Readline::Redisplay()
362{
363 static int W=-1, H=-1;
364
365 int w, h;
366 rl_get_screen_size(&h, &w);
367 if (W==w && h==H)
368 {
369 rl_redisplay();
370 return;
371 }
372
373 cout << '\r' << string(w+1, ' ') << '\r';
374
375 W=w;
376 H=h;
377
378 rl_forced_update_display();
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 strlen(rl_display_prompt) + rl_point
784//
785int Readline::GetAbsCursor()
786{
787 return strlen(rl_display_prompt) + rl_point;
788}
789
790// --------------------------------------------------------------------------
791//
792//! return rl_end (the current total length of the line buffer)
793//! Note that after readline has returned the contents of rl_end might
794//! not reflect the correct buffer length anymore.
795//
796int Readline::GetBufferLength()
797{
798 return rl_end;
799}
800
801// --------------------------------------------------------------------------
802//
803//! return the length of the prompt plus the length of the line buffer
804//
805int Readline::GetLineLength()
806{
807 return strlen(rl_display_prompt) + rl_end;
808}
809
810// --------------------------------------------------------------------------
811//
812//! Calls: Function: void rl_resize_terminal()
813//! Update Readline's internal screen size by reading values from the kernel.
814//
815void Readline::Resize()
816{
817 rl_resize_terminal();
818}
819
820// --------------------------------------------------------------------------
821//
822//! Calls: Function: void rl_set_screen_size (int rows, int cols)
823//! Set Readline's idea of the terminal size to rows rows and cols columns.
824//!
825//! @param width
826//! Number of columns
827//!
828//! @param height
829//! Number of rows
830//
831void Readline::Resize(int width, int height)
832{
833 rl_set_screen_size(height, width);
834}
835
836// --------------------------------------------------------------------------
837//
838//! Get the number of cols readline assumes the screen size to be
839//
840int Readline::GetCols() const
841{
842 int rows, cols;
843 rl_get_screen_size(&rows, &cols);
844 return cols;
845}
846
847// --------------------------------------------------------------------------
848//
849//! Get the number of rows readline assumes the screen size to be
850//
851int Readline::GetRows() const
852{
853 int rows, cols;
854 rl_get_screen_size(&rows, &cols);
855 return rows;
856}
857
858// --------------------------------------------------------------------------
859//
860//! Return a list of pointer to the history contents
861//
862vector<const char*> Readline::GetHistory() const
863{
864 HIST_ENTRY **next = history_list();
865
866 vector<const char*> v;
867
868 for (; *next; next++)
869 v.push_back((*next)->line);
870
871 return v;
872}
873
874// --------------------------------------------------------------------------
875//
876//! Clear readline history (calls clear_history())
877//!
878//! @returns
879//! always true
880//
881bool Readline::ClearHistory()
882{
883 clear_history();
884 return true;
885}
886
887// --------------------------------------------------------------------------
888//
889//! Displays the current history on rl_outstream
890//!
891//! rl_outstream can be redirected using SetStreamOut()
892//!
893//! @returns
894//! always true
895//
896bool Readline::DumpHistory()
897{
898 HIST_ENTRY **next = history_list();
899
900 if (!next)
901 return true;
902
903 for (; *next; next++)
904 fprintf(rl_outstream, "%s\n", (*next)->line);
905
906 return true;
907}
908
909// --------------------------------------------------------------------------
910//
911//! Execute a shell command through a pipe. Write stdout to rl_outstream
912//!
913//! @param cmd
914//! Command to be executed
915//!
916//! @returns
917//! always true
918//
919bool Readline::ExecuteShellCommand(const string &cmd)
920{
921 FILE *pipe = popen(cmd.c_str(), "r");
922 if (!pipe)
923 {
924 fprintf(rl_outstream, "ERROR - Could not create pipe '%s': %m\n", cmd.c_str());
925 return true;
926 }
927
928 while (1)
929 {
930 char buf[1024];
931
932 const size_t sz = fread(buf, 1, 1024, pipe);
933
934 fwrite(buf, 1, sz, rl_outstream);
935
936 if (feof(pipe) || ferror(pipe))
937 break;
938 }
939
940 if (ferror(pipe))
941 fprintf(rl_outstream, "ERROR - Reading from pipe '%s': %m\n", cmd.c_str());
942
943 pclose(pipe);
944
945 fprintf(rl_outstream, "\n");
946
947 return true;
948}
949
950// --------------------------------------------------------------------------
951//
952//! Print the available commands. This is intended for being overwritten
953//! by deriving classes.
954//!
955//! rl_outstream can be redirected using SetStreamOut()
956//!
957//! @returns
958//! always true
959//
960//
961bool Readline::PrintCommands()
962{
963 fprintf(rl_outstream, "\n");
964 fprintf(rl_outstream, " Commands:\n");
965 fprintf(rl_outstream, " No application specific commands defined.\n");
966 fprintf(rl_outstream, "\n");
967 return true;
968}
969
970// --------------------------------------------------------------------------
971//
972//! Print a general help message. This is intended for being overwritten
973//! by deriving classes.
974//!
975//!
976//! rl_outstream can be redirected using SetStreamOut()
977//!
978//! @returns
979//! always true
980//
981//
982bool Readline::PrintGeneralHelp()
983{
984 fprintf(rl_outstream, "\n");
985 fprintf(rl_outstream, " General help:\n");
986 fprintf(rl_outstream, " h,help Print this help message\n");
987 fprintf(rl_outstream, " clear Clear history buffer\n");
988 fprintf(rl_outstream, " lh,history Dump the history buffer to the screen\n");
989 fprintf(rl_outstream, " v,variables Dump readline variables\n");
990 fprintf(rl_outstream, " f,functions Dump readline functions\n");
991 fprintf(rl_outstream, " m,funmap Dump readline funmap\n");
992 fprintf(rl_outstream, " c,commands Dump available commands\n");
993 fprintf(rl_outstream, " k,keylist Dump key bindings\n");
994 fprintf(rl_outstream, " .! command Execute a shell command\n");
995 fprintf(rl_outstream, " .w n Sleep n milliseconds\n");
996 fprintf(rl_outstream, " .x file .. Execute a script of commands (+optional argumnets)\n");
997 fprintf(rl_outstream, " .x file:N .. Execute a script of commands, start at label N\n");
998 fprintf(rl_outstream, " .j N Forward jump to label N\n");
999 fprintf(rl_outstream, " .lt f0 f1 N If float f0 lower than float f1, jump to label N\n");
1000 fprintf(rl_outstream, " .gt f0 f1 N If float f0 greater than float f1, jump to label N\n");
1001 fprintf(rl_outstream, " .eq i0 i1 N If int i0 equal int i1, jump to label N\n");
1002 fprintf(rl_outstream, " : N Defines a label (N=number)\n");
1003 fprintf(rl_outstream, " # comment Ignored\n");
1004 fprintf(rl_outstream, " .q,quit Quit\n");
1005 fprintf(rl_outstream, "\n");
1006 fprintf(rl_outstream, " The command history is automatically loaded and saves to\n");
1007 fprintf(rl_outstream, " and from %s.\n", GetName().c_str());
1008 fprintf(rl_outstream, "\n");
1009 return true;
1010}
1011
1012// --------------------------------------------------------------------------
1013//
1014//! Print a help text about key bindings. This is intended for being
1015//! overwritten by deriving classes.
1016//!
1017//!
1018//! rl_outstream can be redirected using SetStreamOut()
1019//!
1020//! @returns
1021//! always true
1022//
1023//
1024bool Readline::PrintKeyBindings()
1025{
1026 fprintf(rl_outstream, "\n");
1027 fprintf(rl_outstream, " Key bindings:\n");
1028 fprintf(rl_outstream, " Page-up Search backward in history\n");
1029 fprintf(rl_outstream, " Page-dn Search forward in history\n");
1030 fprintf(rl_outstream, " Ctrl-left One word backward\n");
1031 fprintf(rl_outstream, " Ctrl-right One word forward\n");
1032 fprintf(rl_outstream, " Home Beginning of line\n");
1033 fprintf(rl_outstream, " End End of line\n");
1034 fprintf(rl_outstream, " Ctrl-d Quit\n");
1035 fprintf(rl_outstream, " Ctrl-y Delete line\n");
1036 fprintf(rl_outstream, " Alt-end/Ctrl-k Delete until the end of the line\n");
1037 fprintf(rl_outstream, " F1 Toggle visibility of upper panel\n");
1038 fprintf(rl_outstream, "\n");
1039 fprintf(rl_outstream, " Default key-bindings are identical with your bash.\n");
1040 fprintf(rl_outstream, "\n");
1041 return true;
1042}
1043
1044// --------------------------------------------------------------------------
1045//
1046//!
1047//
1048bool Readline::PreProcess(const string &str)
1049{
1050 // ----------- Labels -------------
1051
1052 if (str[0]==':')
1053 {
1054 try
1055 {
1056 fSection = stoi(str.substr(1));
1057 SetSection(fSection);
1058
1059 if (fLabel!=fSection)
1060 return true;
1061 }
1062 catch (const logic_error &e)
1063 {
1064 fCommandLog << "# ERROR[" << fScriptDepth << "] - Inavlid label '" << str.substr(1) << "'" << endl;
1065 fLabel = -2;
1066 return true;
1067 }
1068
1069 fLabel=-1;
1070 return false;
1071 }
1072
1073 if (fLabel>=0)
1074 {
1075 fCommandLog << "# SKIP[" << fScriptDepth << "]: " << fLabel << " - " << str << endl;
1076 return true;
1077 }
1078
1079 if (str.substr(0, 3)==".j ")
1080 {
1081 fLabel = atoi(str.substr(3).c_str());
1082 return false;
1083 }
1084
1085 return Process(str);
1086
1087}
1088
1089// --------------------------------------------------------------------------
1090//
1091//!
1092//
1093bool Readline::Process(const string &str)
1094{
1095 // ----------- Common commands -------------
1096
1097 if (str.substr(0, 3)==".w ")
1098 {
1099 usleep(stoi(str.substr(3))*1000);
1100 return true;
1101 }
1102
1103 if (str.substr(0, 3)==".x ")
1104 {
1105 string opt(str.substr(3));
1106
1107 map<string,string> data = Tools::Split(opt);
1108 if (opt.size()==0)
1109 {
1110 if (data.size()==0)
1111 PrintReadlineError("Filename missing.");
1112 else
1113 PrintReadlineError("Equal sign missing in argument '"+data.begin()->first+"'");
1114
1115 return true;
1116 }
1117
1118 const string save = fScript;
1119 const int save_sec = fSection;
1120 Execute(opt, data);
1121 fScript = save;
1122 if (save_sec!=-4)
1123 {
1124 fSection = save_sec;
1125 SetSection(save_sec);
1126 }
1127
1128 return true;
1129 }
1130
1131 if (str.substr(0, 2)==".!")
1132 {
1133 ExecuteShellCommand(str.substr(2));
1134 return true;
1135 }
1136
1137 if (str.substr(0, 4)==".gt ")
1138 {
1139 istringstream in(str.substr(4));
1140
1141 float v0, v1;
1142 int label;
1143
1144 in >> v0 >> v1 >> label;
1145 if (in.fail())
1146 {
1147 PrintReadlineError("Couldn't parse '"+str+"'");
1148 fLabel = -2;
1149 return true;
1150 }
1151
1152 if (v0 > v1)
1153 fLabel = label;
1154
1155 return true;
1156 }
1157
1158 if (str.substr(0, 4)==".lt ")
1159 {
1160 istringstream in(str.substr(4));
1161
1162 float v0, v1;
1163 int label;
1164
1165 in >> v0 >> v1 >> label;
1166 if (in.fail())
1167 {
1168 PrintReadlineError("Couldn't parse '"+str+"'");
1169 fLabel = -2;
1170 return true;
1171 }
1172
1173 if (v0 < v1)
1174 fLabel = label;
1175
1176 return true;
1177 }
1178
1179 if (str.substr(0, 4)==".eq ")
1180 {
1181 istringstream in(str.substr(4));
1182
1183 int v0, v1, label;
1184
1185 in >> v0 >> v1 >> label;
1186 if (in.fail())
1187 {
1188 PrintReadlineError("Couldn't parse '"+str+"'");
1189 fLabel = -2;
1190 return true;
1191 }
1192
1193 if (v0==v1)
1194 fLabel = label;
1195
1196 return true;
1197 }
1198
1199
1200 // ----------- Readline static -------------
1201
1202 if (str=="clear")
1203 return ClearHistory();
1204
1205 if (str=="lh" || str=="history")
1206 return DumpHistory();
1207
1208 if (str=="v" || str=="variables")
1209 return DumpVariables();
1210
1211 if (str=="f" || str=="functions")
1212 return DumpFunctions();
1213
1214 if (str=="m" || str=="funmap")
1215 return DumpFunmap();
1216
1217 // ---------- Readline virtual -------------
1218
1219 if (str=="h" || str=="help")
1220 return PrintGeneralHelp();
1221
1222 if (str=="c" || str=="commands")
1223 return PrintCommands();
1224
1225 if (str=="k" || str=="keylist")
1226 return PrintKeyBindings();
1227
1228 return false;
1229}
1230
1231// --------------------------------------------------------------------------
1232//
1233//! This function is a wrapper around the call to readline. It encapsultes
1234//! the return buffer into a std::string and deletes the memory allocated
1235//! by readline. Furthermore, it removes leading and trailing whitespaces
1236//! before return the result. The result is returned in the given
1237//! argument containing the prompt. Before the function returns Shutdown()
1238//! is called (as opposed to Startup when readline starts)
1239//!
1240//! @param str
1241//! The prompt which is to be shown by the readline libarary. it is
1242//! directly given to the call to readline. The result of the
1243//! readline call is returned in this string.
1244//!
1245//! @returns
1246//! true if the call succeeded as usual, false if EOF was detected
1247//! by the readline call.
1248//
1249bool Readline::PromptEOF(string &str)
1250{
1251 char *buf = readline(str.c_str());
1252 Shutdown(buf);
1253
1254 // Happens when EOF is encountered
1255 if (!buf)
1256 return false;
1257
1258 str = Tools::Trim(buf);
1259
1260 free(buf);
1261
1262 return true;
1263}
1264
1265// --------------------------------------------------------------------------
1266//
1267//! This function is a wrapper around the call to readline. It encapsultes
1268//! the return buffer into a std::string and deletes the memory allocated
1269//! by readline. Furthermore, it removes leading and trailing whitespaces
1270//! before return the result. Before the function returns Shutdown() is
1271//! called (as opposed to Startup when readline starts)
1272//!
1273//! @param prompt
1274//! The prompt which is to be shown by the readline libarary. it is
1275//! directly given to the call to readline.
1276//!
1277//! @returns
1278//! The result of the readline call
1279//
1280string Readline::Prompt(const string &prompt)
1281{
1282 char *buf = readline(prompt.c_str());
1283
1284 Shutdown(buf ? buf : "");
1285
1286 const string str = !buf || (rl_done && rl_pending_input==4)
1287 ? ".q" : Tools::Trim(buf);
1288
1289 free(buf);
1290
1291 return str;
1292}
1293
1294// --------------------------------------------------------------------------
1295//
1296//! Writes the current history to the file defined by fName and
1297//! replaces the history by the data from the given file.
1298//!
1299//! @param fname
1300//! Name of the history file to read
1301//!
1302void Readline::StaticPushHistory(const string &fname="")
1303{
1304 fs::path his = fs::path(This->fName).parent_path();
1305 his /= fname;
1306
1307 write_history(This->fName.c_str());
1308 stifle_history(0);
1309 unstifle_history();
1310 read_history(his.string().c_str());
1311}
1312
1313// --------------------------------------------------------------------------
1314//
1315//! Writes the current history to the file with the given name
1316//! and replaces the history by the file defined by fName.
1317//!
1318//! @param fname
1319//! Name of the history file to write (it will be truncated to 1000 lines)
1320//!
1321void Readline::StaticPopHistory(const string &fname="")
1322{
1323 fs::path his = fs::path(This->fName).parent_path();
1324 his /= fname;
1325
1326 write_history(his.string().c_str());
1327 history_truncate_file(his.string().c_str(), 1000);
1328
1329 stifle_history(0);
1330 unstifle_history();
1331 read_history(This->fName.c_str());
1332}
1333
1334// --------------------------------------------------------------------------
1335//
1336//! Just calls readline and thus allows to just prompt for something.
1337//! Adds everything to the history except '.q'
1338//!
1339//! @param prompt
1340//! Prompt to be displayed
1341//!
1342//! @return
1343//! String entered by the user ('.q' is Ctrl-d is pressed)
1344//!
1345string Readline::StaticPrompt(const string &prompt)
1346{
1347 char *buf = readline(prompt.c_str());
1348 if (!buf)
1349 return ".q";
1350
1351 const string str(buf);
1352 if (Tools::Trim(str)!=".q" && !Tools::Trim(str).empty())
1353 if (history_length==0 || history_search_pos(str.c_str(), -1, history_length-1)!=history_length-1)
1354 add_history(buf);
1355
1356 free(buf);
1357
1358 return str;
1359}
1360
1361// --------------------------------------------------------------------------
1362//
1363//! Process a single line. All lines are added to the history, but only
1364//! accepted lines are written to the command log. In this case fLine is
1365//! increased by one.
1366//! Comment (starting with #) are removed from the command-line. A # can be
1367//! escaped with quotation marks "#"
1368//
1369void Readline::ProcessLine(const string &str)
1370{
1371 const string cmd = Tools::Uncomment(str);
1372
1373 if (!cmd.empty())
1374 {
1375 const bool rc = PreProcess(cmd);
1376
1377 AddToHistory(cmd);
1378
1379 if (rc)
1380 return;
1381
1382 fLine++;
1383 }
1384
1385 fCommandLog << str << endl;
1386}
1387
1388// --------------------------------------------------------------------------
1389//
1390//! This implements a loop over readline calls. A prompt to issue can be
1391//! given. If it is NULL, the prompt is retrieved from GetUpdatePrompt().
1392//! It is updated regularly by means of calls to GetUpdatePrompt() from
1393//! EventHook(). If Prompt() returns with "quit" or ".q" the loop is
1394//! exited. If ".qqq" is entered exit(-1) is called. In case of ".qqqqqq"
1395//! abort(). Both ways to exit the program are not recommended. Empty
1396//! inputs are ignored. After that Process() with the returned string
1397//! is called. If Process returns true the input is not counted and not
1398//! added to the history, otherwise the line counter is increased
1399//! and the input is added to the history.
1400//!
1401//! @param prompt
1402//! The prompt to be issued or NULL if GetUPdatePrompt should be used
1403//! instead.
1404//!
1405void Readline::Run(const char *prompt)
1406{
1407 fLine = 0;
1408 while (1)
1409 {
1410 // Before we start we have to make sure that the
1411 // screen looks like and is ordered like expected.
1412 const string str = Prompt(prompt?prompt:GetUpdatePrompt());
1413 if (str.empty())
1414 continue;
1415
1416 if (str=="quit" || str==".q")
1417 break;
1418
1419 if (str==".qqq")
1420 exit(128);
1421
1422 if (str==".qqqqqq")
1423 abort();
1424
1425 ProcessLine(str);
1426 }
1427}
1428
1429// --------------------------------------------------------------------------
1430//
1431//! Executes commands read from an ascii file as they were typed in
1432//! the console. Empty lines and lines beginning with # are ignored.
1433//!
1434//! @param fname
1435//! Filename of file to read
1436//!
1437//! @param args
1438//! Arguments to be passed to the script. A search and replace
1439//! will be done for ${arg}
1440//!
1441//! @returns
1442//! -1 if the file couldn't be read and the number of commands for which
1443//! Process() was callled otherwise
1444//!
1445int Readline::Execute(const string &fname, const map<string,string> &args)
1446{
1447 // this could do the same:
1448 // rl_instream = fopen(str.c_str(), "r");
1449
1450 if (IsStopped())
1451 return 0;
1452
1453 string name = Tools::Trim(fname);
1454 fScript = name;
1455
1456 fSection = -3;
1457 SetSection(-3);
1458 fLabel = -1;
1459
1460 const size_t p = name.find_last_of(':');
1461 if (p!=string::npos)
1462 {
1463 fLabel = atoi(name.substr(p+1).c_str());
1464 name = name.substr(0, p);
1465 }
1466
1467 ifstream fin(name.c_str());
1468 if (!fin)
1469 {
1470 fSection = -4;
1471 SetSection(-4);
1472 return -1;
1473 }
1474
1475 if (fScriptDepth++==0)
1476 fStopScript = false;
1477
1478 fCommandLog << "# " << Time() << " - " << name << " (START[" << fScriptDepth<< "]";
1479 if (fLabel>=0)
1480 fCommandLog << ':' << fLabel;
1481 fCommandLog << ")" << endl;
1482
1483 fSection = -1;
1484 SetSection(-1);
1485
1486 int rc = 0;
1487
1488 string buffer;
1489 while (getline(fin, buffer, '\n') && !fStopScript)
1490 {
1491 buffer = Tools::Trim(buffer);
1492 if (buffer.empty())
1493 continue;
1494
1495 rc++;
1496
1497 if (buffer=="quit" || buffer==".q")
1498 {
1499 Stop();
1500 break;
1501 }
1502
1503 // find and replace arguments
1504 for (auto it=args.begin(); it!=args.end(); it++)
1505 {
1506 const string find = "${"+it->first+"}";
1507 for (size_t pos=0; (pos=buffer.find(find, pos))!=string::npos; pos+=find.length())
1508 buffer.replace(pos, find.size(), it->second);
1509 }
1510
1511 // process line
1512 ProcessLine(buffer);
1513 }
1514
1515 fCommandLog << "# " << Time() << " - " << name << " (FINISHED[" << fScriptDepth<< "])" << endl;
1516
1517 if (--fScriptDepth==0)
1518 fStopScript = false;
1519
1520 fLabel = -1;
1521 fSection = -4;
1522 SetSection(-4);
1523
1524 return rc;
1525}
1526
1527// --------------------------------------------------------------------------
1528//
1529//! Stops the readline execution. It fakes the end of an editing by
1530//! setting rl_done to 1. To distinguish this from a normal edit,
1531//! rl_pending_input is set to EOT.
1532//!
1533void Readline::Stop()
1534{
1535 rl_done = 1;
1536 rl_pending_input = 4; // EOT (end of transmission, ctrl-d)
1537}
1538
1539// --------------------------------------------------------------------------
1540//
1541//! @returns
1542//! the status of rl_done and rl_pending_input. If rl_done==1 and
1543//! rl_pending_input==4 true is returned, false otherwise.
1544//! This can be used to check if Stop() was called. If readline is
1545//! not in operation.
1546//!
1547bool Readline::IsStopped() const
1548{
1549 return rl_done==1 && rl_pending_input==4;
1550};
1551
1552void Readline::PrintReadlineError(const std::string &str)
1553{
1554 fprintf(rl_outstream, "%s\n", str.c_str());
1555}
Note: See TracBrowser for help on using the repository browser.