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

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