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

Last change on this file since 19438 was 18833, checked in by tbretz, 2 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.