1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 #  include "config.h"
28 #endif
29 
30 #include <cstdlib>
31 #include <cstring>
32 
33 #include <string>
34 
35 #include "cmd-edit.h"
36 #include "cmd-hist.h"
37 #include "file-ops.h"
38 #include "file-stat.h"
39 #include "lo-error.h"
40 #include "lo-utils.h"
41 #include "oct-env.h"
42 #include "oct-mutex.h"
43 #include "oct-time.h"
44 #include "quit.h"
45 #include "singleton-cleanup.h"
46 #include "strdup-wrapper.h"
47 #include "unistd-wrappers.h"
48 
49 #if defined (USE_READLINE)
50 #include <cstdio>
51 
52 #include "oct-rl-edit.h"
53 #endif
54 
55 namespace octave
56 {
57   char * do_completer_word_break_hook ();
58 
59   command_editor *command_editor::s_instance = nullptr;
60 
61   std::set<command_editor::startup_hook_fcn> command_editor::startup_hook_set;
62 
63   std::set<command_editor::pre_input_hook_fcn> command_editor::pre_input_hook_set;
64 
65   std::set<command_editor::event_hook_fcn> command_editor::event_hook_set;
66 
67   static mutex event_hook_lock;
68 
69 #if defined (USE_READLINE)
70 
71   class
72   gnu_readline : public command_editor
73   {
74   public:
75 
76     typedef command_editor::startup_hook_fcn startup_hook_fcn;
77 
78     typedef command_editor::pre_input_hook_fcn pre_input_hook_fcn;
79 
80     typedef command_editor::event_hook_fcn event_hook_fcn;
81 
82     typedef command_editor::completion_fcn completion_fcn;
83 
84     gnu_readline (void);
85 
86     ~gnu_readline (void) = default;
87 
88     void do_set_name (const std::string& n);
89 
90     std::string do_readline (const std::string& prompt, bool& eof);
91 
92     void do_set_input_stream (FILE *f);
93 
94     FILE * do_get_input_stream (void);
95 
96     void do_set_output_stream (FILE *f);
97 
98     FILE * do_get_output_stream (void);
99 
100     void do_redisplay (void);
101 
102     int do_terminal_rows (void);
103 
104     int do_terminal_cols (void);
105 
106     void do_clear_screen (bool skip_redisplay);
107 
108     void do_resize_terminal (void);
109 
110     void do_set_screen_size (int ht, int wd);
111 
112     std::string newline_chars (void);
113 
114     void do_restore_terminal_state (void);
115 
116     void do_blink_matching_paren (bool flag);
117 
118     bool do_erase_empty_line (bool flag);
119 
120     void do_set_basic_word_break_characters (const std::string& s);
121 
122     void do_set_completer_word_break_characters (const std::string& s);
123 
124     void do_set_basic_quote_characters (const std::string& s);
125 
126     void do_set_filename_quote_characters (const std::string& s);
127 
128     void do_set_completer_quote_characters (const std::string& s);
129 
130     void do_set_completion_append_character (char c);
131 
132     void do_set_completion_function (completion_fcn f);
133 
134     void do_set_quoting_function (quoting_fcn f);
135 
136     void do_set_dequoting_function (dequoting_fcn f);
137 
138     void do_set_char_is_quoted_function (char_is_quoted_fcn f);
139 
140     void do_set_user_accept_line_function (user_accept_line_fcn f);
141 
142     completion_fcn do_get_completion_function (void) const;
143 
144     quoting_fcn do_get_quoting_function (void) const;
145 
146     dequoting_fcn do_get_dequoting_function (void) const;
147 
148     char_is_quoted_fcn do_get_char_is_quoted_function (void) const;
149 
150     user_accept_line_fcn do_get_user_accept_line_function (void) const;
151 
152     string_vector
153     do_generate_filename_completions (const std::string& text);
154 
155     std::string do_get_line_buffer (void) const;
156 
157     std::string do_get_current_line (void) const;
158 
159     char do_get_prev_char (int) const;
160 
161     void do_replace_line (const std::string& text, bool clear_undo);
162 
163     void do_kill_full_line (void);
164 
165     void do_insert_text (const std::string& text);
166 
167     void do_newline (void);
168 
169     void do_accept_line (void);
170 
171     bool do_undo (void);
172 
173     void do_clear_undo_list (void);
174 
175     void set_startup_hook (startup_hook_fcn f);
176 
177     void restore_startup_hook (void);
178 
179     void set_pre_input_hook (pre_input_hook_fcn f);
180 
181     void restore_pre_input_hook (void);
182 
183     void set_event_hook (event_hook_fcn f);
184 
185     void restore_event_hook (void);
186 
187     void do_restore_event_hook (void);
188 
189     void do_read_init_file (const std::string& file);
190 
191     void do_re_read_init_file (void);
192 
193     bool do_filename_completion_desired (bool);
194 
195     bool do_filename_quoting_desired (bool);
196 
197     bool do_prefer_env_winsize (bool);
198 
199     void do_interrupt (bool);
200 
201     void do_handle_interrupt_signal (void);
202 
203     static int operate_and_get_next (int, int);
204 
205     static int history_search_backward (int, int);
206 
207     static int history_search_forward (int, int);
208 
209   private:
210 
211     startup_hook_fcn previous_startup_hook;
212 
213     pre_input_hook_fcn previous_pre_input_hook;
214 
215     event_hook_fcn previous_event_hook;
216 
217     completion_fcn completion_function;
218 
219     quoting_fcn quoting_function;
220 
221     dequoting_fcn dequoting_function;
222 
223     char_is_quoted_fcn char_is_quoted_function;
224 
225     user_accept_line_fcn user_accept_line_function;
226 
227     static std::string completer_quote_characters;
228 
229     static char * command_generator (const char *text, int state);
230 
231     static char * command_quoter (char *text, int match_type, char *quote_pointer);
232     static char * command_dequoter (char *text, int match_type);
233 
234     static int command_char_is_quoted (char *text, int index);
235 
236     static int command_accept_line (int count, int key);
237 
238     static char ** command_completer (const char *text, int start, int end);
239 
240     static char * do_completer_word_break_hook ();
241   };
242 
243   std::string gnu_readline::completer_quote_characters = "";
244 
gnu_readline()245   gnu_readline::gnu_readline ()
246     : command_editor (), previous_startup_hook (nullptr),
247       previous_pre_input_hook (nullptr),
248       previous_event_hook (nullptr), completion_function (nullptr),
249       quoting_function (nullptr), dequoting_function (nullptr),
250       char_is_quoted_function (nullptr), user_accept_line_function (nullptr)
251   {
252     // FIXME: need interface to rl_add_defun, rl_initialize, and
253     // a function to set rl_terminal_name
254 
255     std::string term = sys::env::getenv ("TERM");
256 
257     octave_rl_set_terminal_name (term.c_str ());
258 
259     octave_rl_initialize ();
260 
261     do_blink_matching_paren (true);
262 
263     // Bind operate-and-get-next.
264 
265     octave_rl_add_defun ("operate-and-get-next",
266                          gnu_readline::operate_and_get_next,
267                          octave_rl_ctrl ('O'));
268   }
269 
270   void
do_set_name(const std::string & nm)271   gnu_readline::do_set_name (const std::string& nm)
272   {
273     ::octave_rl_set_name (nm.c_str ());
274   }
275 
276   std::string
do_readline(const std::string & prompt,bool & eof)277   gnu_readline::do_readline (const std::string& prompt, bool& eof)
278   {
279     std::string retval;
280 
281     eof = false;
282 
283     const char *p = prompt.c_str ();
284 
285     char *line = ::octave_rl_readline (p);
286 
287     if (line)
288       {
289         retval = line;
290 
291         free (line);
292       }
293     else
294       eof = true;
295 
296     return retval;
297   }
298 
299   void
do_set_input_stream(FILE * f)300   gnu_readline::do_set_input_stream (FILE *f)
301   {
302     ::octave_rl_set_input_stream (f);
303   }
304 
305   FILE *
do_get_input_stream(void)306   gnu_readline::do_get_input_stream (void)
307   {
308     return ::octave_rl_get_input_stream ();
309   }
310 
311   void
do_set_output_stream(FILE * f)312   gnu_readline::do_set_output_stream (FILE *f)
313   {
314     ::octave_rl_set_output_stream (f);
315   }
316 
317   FILE *
do_get_output_stream(void)318   gnu_readline::do_get_output_stream (void)
319   {
320     return ::octave_rl_get_output_stream ();
321   }
322 
323   void
do_redisplay(void)324   gnu_readline::do_redisplay (void)
325   {
326     ::octave_rl_redisplay ();
327   }
328 
329   // GNU readline handles SIGWINCH, so these values have a good chance
330   // of being correct even if the window changes size (they may be
331   // wrong if, for example, the luser changes the window size while the
332   // pager is running, and the signal is handled by the pager instead of
333   // us.
334 
335   int
do_terminal_rows(void)336   gnu_readline::do_terminal_rows (void)
337   {
338     int sh = ::octave_rl_screen_height ();
339 
340     return sh > 0 ? sh : 24;
341   }
342 
343   int
do_terminal_cols(void)344   gnu_readline::do_terminal_cols (void)
345   {
346     int sw = ::octave_rl_screen_width ();
347 
348     return sw > 0 ? sw : 80;
349   }
350 
351   void
do_clear_screen(bool skip_redisplay)352   gnu_readline::do_clear_screen (bool skip_redisplay)
353   {
354     ::octave_rl_clear_screen (skip_redisplay);
355   }
356 
357   void
do_resize_terminal(void)358   gnu_readline::do_resize_terminal (void)
359   {
360     ::octave_rl_resize_terminal ();
361   }
362 
363   void
do_set_screen_size(int ht,int wd)364   gnu_readline::do_set_screen_size (int ht, int wd)
365   {
366     ::octave_rl_set_screen_size (ht, wd);
367   }
368 
369   std::string
newline_chars(void)370   gnu_readline::newline_chars (void)
371   {
372     return "\r\n";
373   }
374 
375   void
do_restore_terminal_state(void)376   gnu_readline::do_restore_terminal_state (void)
377   {
378     ::octave_rl_restore_terminal_state ();
379   }
380 
381   void
do_blink_matching_paren(bool flag)382   gnu_readline::do_blink_matching_paren (bool flag)
383   {
384     ::octave_rl_enable_paren_matching (flag ? 1 : 0);
385   }
386 
387   bool
do_erase_empty_line(bool flag)388   gnu_readline::do_erase_empty_line (bool flag)
389   {
390     return ::octave_rl_erase_empty_line (flag ? 1 : 0);
391   }
392 
393   void
do_set_basic_word_break_characters(const std::string & s)394   gnu_readline::do_set_basic_word_break_characters (const std::string& s)
395   {
396     ::octave_rl_set_basic_word_break_characters (s.c_str ());
397   }
398 
399   void
do_set_completer_word_break_characters(const std::string & s)400   gnu_readline::do_set_completer_word_break_characters (const std::string& s)
401   {
402     ::octave_rl_set_completer_word_break_characters (s.c_str ());
403 
404     ::octave_rl_set_completion_word_break_hook
405       (gnu_readline::do_completer_word_break_hook);
406 
407   }
408 
409   void
do_set_basic_quote_characters(const std::string & s)410   gnu_readline::do_set_basic_quote_characters (const std::string& s)
411   {
412     ::octave_rl_set_basic_quote_characters (s.c_str ());
413   }
414 
415   void
do_set_filename_quote_characters(const std::string & s)416   gnu_readline::do_set_filename_quote_characters (const std::string& s)
417   {
418     ::octave_rl_set_filename_quote_characters (s.c_str ());
419   }
420 
421   void
do_set_completer_quote_characters(const std::string & s)422   gnu_readline::do_set_completer_quote_characters (const std::string& s)
423   {
424     completer_quote_characters = s;
425   }
426 
427   void
do_set_completion_append_character(char c)428   gnu_readline::do_set_completion_append_character (char c)
429   {
430     ::octave_rl_set_completion_append_character (c);
431   }
432 
433   void
do_set_completion_function(completion_fcn f)434   gnu_readline::do_set_completion_function (completion_fcn f)
435   {
436     completion_function = f;
437 
438     rl_attempted_completion_fcn_ptr fp
439       = (f ? gnu_readline::command_completer : nullptr);
440 
441     ::octave_rl_set_completion_function (fp);
442   }
443 
444   void
do_set_quoting_function(quoting_fcn f)445   gnu_readline::do_set_quoting_function (quoting_fcn f)
446   {
447     quoting_function = f;
448 
449     rl_quoting_fcn_ptr fp
450       = (f ? gnu_readline::command_quoter : nullptr);
451 
452     ::octave_rl_set_quoting_function (fp);
453   }
454 
455   void
do_set_dequoting_function(dequoting_fcn f)456   gnu_readline::do_set_dequoting_function (dequoting_fcn f)
457   {
458     dequoting_function = f;
459 
460     rl_dequoting_fcn_ptr fp
461       = (f ? gnu_readline::command_dequoter : nullptr);
462 
463     ::octave_rl_set_dequoting_function (fp);
464   }
465 
466   void
do_set_char_is_quoted_function(char_is_quoted_fcn f)467   gnu_readline::do_set_char_is_quoted_function (char_is_quoted_fcn f)
468   {
469     char_is_quoted_function = f;
470 
471     rl_char_is_quoted_fcn_ptr fp
472       = (f ? gnu_readline::command_char_is_quoted : nullptr);
473 
474     ::octave_rl_set_char_is_quoted_function (fp);
475   }
476 
477   void
do_set_user_accept_line_function(user_accept_line_fcn f)478   gnu_readline::do_set_user_accept_line_function (user_accept_line_fcn f)
479   {
480     user_accept_line_function = f;
481 
482     if (f)
483       octave_rl_add_defun ("accept-line", gnu_readline::command_accept_line,
484                            ::octave_rl_ctrl ('M'));
485     else
486       octave_rl_add_defun ("accept-line", ::octave_rl_newline,
487                            ::octave_rl_ctrl ('M'));
488   }
489 
490   gnu_readline::completion_fcn
do_get_completion_function(void) const491   gnu_readline::do_get_completion_function (void) const
492   {
493     return completion_function;
494   }
495 
496   gnu_readline::quoting_fcn
do_get_quoting_function(void) const497   gnu_readline::do_get_quoting_function (void) const
498   {
499     return quoting_function;
500   }
501 
502   gnu_readline::dequoting_fcn
do_get_dequoting_function(void) const503   gnu_readline::do_get_dequoting_function (void) const
504   {
505     return dequoting_function;
506   }
507 
508   gnu_readline::char_is_quoted_fcn
do_get_char_is_quoted_function(void) const509   gnu_readline::do_get_char_is_quoted_function (void) const
510   {
511     return char_is_quoted_function;
512   }
513 
514   gnu_readline::user_accept_line_fcn
do_get_user_accept_line_function(void) const515   gnu_readline::do_get_user_accept_line_function (void) const
516   {
517     return user_accept_line_function;
518   }
519 
520   // True if the last "word" of the string line (delimited by delim) is
521   // an existing directory.  Used by do_completer_word_break_hook.
522 
523   static bool
looks_like_filename(const char * line,char delim)524   looks_like_filename (const char *line, char delim)
525   {
526     bool retval = false;
527 
528     const char *s = strrchr (line, delim);
529 
530     if (s)
531       {
532         // Remove incomplete component.
533         const char *f = strrchr (line, sys::file_ops::dir_sep_char ());
534 
535         if (f && (s[1] == '~' || f != s))
536           {
537             // For something like "A /b", f==s; don't assume a file.
538 
539             std::string candidate_filename = s+1;
540 
541             candidate_filename = candidate_filename.substr (0, f - s);
542 
543             // Handles any complete ~<username>, but doesn't expand usernames.
544 
545             if (candidate_filename[0] == '~')
546               candidate_filename
547                 = sys::file_ops::tilde_expand (candidate_filename);
548 
549             sys::file_stat fs (candidate_filename);
550 
551             retval = fs.is_dir ();
552           }
553       }
554 
555     return retval;
556   }
557 
558   // Decide whether to interpret partial commands like "abc/def" as a
559   // filename or division.  Return the set of delimiters appropriate for
560   // the decision.
561 
562   char *
do_completer_word_break_hook()563   gnu_readline::do_completer_word_break_hook ()
564   {
565     static char *dir_sep = octave_strdup_wrapper (" '\"");
566 
567     std::string word;
568     std::string line = get_line_buffer ();
569 
570     // For now, assume space or quote delimiter for file names.
571     const char *l = line.c_str ();
572 
573     if (looks_like_filename (l, ' ') || looks_like_filename (l, '\'')
574         || looks_like_filename (l, '"'))
575       {
576         ::octave_rl_set_completer_quote_characters
577           (completer_quote_characters.c_str ());
578 
579         return dir_sep;
580       }
581     else
582       {
583         ::octave_rl_set_completer_quote_characters ("");
584 
585         return octave_rl_get_completer_word_break_characters ();
586       }
587   }
588 
589   string_vector
do_generate_filename_completions(const std::string & text)590   gnu_readline::do_generate_filename_completions (const std::string& text)
591   {
592     string_vector retval;
593 
594     int n = 0;
595     int count = 0;
596 
597     char *fn = nullptr;
598 
599     while (1)
600       {
601         fn = ::octave_rl_filename_completion_function (text.c_str (), count);
602 
603         if (fn)
604           {
605             if (count == n)
606               {
607                 // Famous last words:  Most large directories will not
608                 // have more than a few hundred files, so we should not
609                 // resize too many times even if the growth is linear...
610                 n += 100;
611                 retval.resize (n);
612               }
613 
614             retval[count++] = fn;
615 
616             free (fn);
617           }
618         else
619           break;
620       }
621 
622     retval.resize (count);
623 
624     return retval;
625   }
626 
627   std::string
do_get_line_buffer(void) const628   gnu_readline::do_get_line_buffer (void) const
629   {
630     return ::octave_rl_line_buffer ();
631   }
632 
633   std::string
do_get_current_line(void) const634   gnu_readline::do_get_current_line (void) const
635   {
636     std::string retval;
637     char *buf = ::octave_rl_copy_line ();
638     retval = buf;
639     free (buf);
640     return retval;
641   }
642 
643   // Return the character (offset+1) to the left of the cursor,
644   // or '\0' if the cursor is at the start of the line.
645   char
do_get_prev_char(int offset) const646   gnu_readline::do_get_prev_char (int offset) const
647   {
648     const char *buf = ::octave_rl_line_buffer ();
649     int p = ::octave_rl_point ();
650 
651     return p > offset ? buf[p - offset - 1] : '\0';
652   }
653 
654   void
do_replace_line(const std::string & text,bool clear_undo)655   gnu_readline::do_replace_line (const std::string& text, bool clear_undo)
656   {
657     ::octave_rl_replace_line (text.c_str (), clear_undo);
658   }
659 
660   void
do_kill_full_line(void)661   gnu_readline::do_kill_full_line (void)
662   {
663     ::octave_rl_kill_full_line ();
664   }
665 
666   void
do_insert_text(const std::string & text)667   gnu_readline::do_insert_text (const std::string& text)
668   {
669     ::octave_rl_insert_text (text.c_str ());
670   }
671 
672   void
do_newline(void)673   gnu_readline::do_newline (void)
674   {
675     ::octave_rl_newline (1, '\n');
676   }
677 
678   void
do_accept_line(void)679   gnu_readline::do_accept_line (void)
680   {
681     command_accept_line (1, '\n');
682   }
683 
684   bool
do_undo(void)685   gnu_readline::do_undo (void)
686   {
687     return ::octave_rl_do_undo ();
688   }
689 
690   void
do_clear_undo_list()691   gnu_readline::do_clear_undo_list ()
692   {
693     ::octave_rl_clear_undo_list ();
694   }
695 
696   void
set_startup_hook(startup_hook_fcn f)697   gnu_readline::set_startup_hook (startup_hook_fcn f)
698   {
699     previous_startup_hook = ::octave_rl_get_startup_hook ();
700 
701     if (f != previous_startup_hook)
702       ::octave_rl_set_startup_hook (f);
703   }
704 
705   void
restore_startup_hook(void)706   gnu_readline::restore_startup_hook (void)
707   {
708     ::octave_rl_set_startup_hook (previous_startup_hook);
709   }
710 
711   void
set_pre_input_hook(pre_input_hook_fcn f)712   gnu_readline::set_pre_input_hook (pre_input_hook_fcn f)
713   {
714     previous_pre_input_hook = ::octave_rl_get_pre_input_hook ();
715 
716     if (f != previous_pre_input_hook)
717       ::octave_rl_set_pre_input_hook (f);
718   }
719 
720   void
restore_pre_input_hook(void)721   gnu_readline::restore_pre_input_hook (void)
722   {
723     ::octave_rl_set_pre_input_hook (previous_pre_input_hook);
724   }
725 
726   void
set_event_hook(event_hook_fcn f)727   gnu_readline::set_event_hook (event_hook_fcn f)
728   {
729     previous_event_hook = octave_rl_get_event_hook ();
730 
731     ::octave_rl_set_event_hook (f);
732   }
733 
734   void
restore_event_hook(void)735   gnu_readline::restore_event_hook (void)
736   {
737     ::octave_rl_set_event_hook (previous_event_hook);
738   }
739 
740   void
do_read_init_file(const std::string & file)741   gnu_readline::do_read_init_file (const std::string& file)
742   {
743     ::octave_rl_read_init_file (file.c_str ());
744   }
745 
746   void
do_re_read_init_file(void)747   gnu_readline::do_re_read_init_file (void)
748   {
749     ::octave_rl_re_read_init_file ();
750   }
751 
752   bool
do_filename_completion_desired(bool arg)753   gnu_readline::do_filename_completion_desired (bool arg)
754   {
755     return ::octave_rl_filename_completion_desired (arg);
756   }
757 
758   bool
do_filename_quoting_desired(bool arg)759   gnu_readline::do_filename_quoting_desired (bool arg)
760   {
761     return ::octave_rl_filename_quoting_desired (arg);
762   }
763 
764   bool
do_prefer_env_winsize(bool arg)765   gnu_readline::do_prefer_env_winsize (bool arg)
766   {
767     return ::octave_rl_prefer_env_winsize (arg);
768   }
769 
770   void
do_interrupt(bool arg)771   gnu_readline::do_interrupt (bool arg)
772   {
773     ::octave_rl_done (arg);
774   }
775 
776   void
do_handle_interrupt_signal(void)777   gnu_readline::do_handle_interrupt_signal (void)
778   {
779     octave_signal_caught = 0;
780     octave_interrupt_state = 0;
781 
782     ::octave_rl_recover_from_interrupt ();
783 
784     throw interrupt_exception ();
785   }
786 
787   int
operate_and_get_next(int,int)788   gnu_readline::operate_and_get_next (int /* count */, int /* c */)
789   {
790     // Accept the current line.
791 
792     command_editor::accept_line ();
793 
794     // Find the current line, and find the next line to use.
795 
796     int x_where = command_history::where ();
797 
798     int x_length = command_history::length ();
799 
800     if ((command_history::is_stifled ()
801          && (x_length >= command_history::max_input_history ()))
802         || (x_where >= x_length - 1))
803       command_history::set_mark (x_where);
804     else
805       command_history::set_mark (x_where + 1);
806 
807     command_editor::add_startup_hook (command_history::goto_mark);
808 
809     return 0;
810   }
811 
812   int
history_search_backward(int count,int c)813   gnu_readline::history_search_backward (int count, int c)
814   {
815     return octave_rl_history_search_backward (count, c);
816   }
817 
818   int
history_search_forward(int count,int c)819   gnu_readline::history_search_forward (int count, int c)
820   {
821     return octave_rl_history_search_forward (count, c);
822   }
823 
824   char *
command_generator(const char * text,int state)825   gnu_readline::command_generator (const char *text, int state)
826   {
827     char *retval = nullptr;
828 
829     completion_fcn f = command_editor::get_completion_function ();
830 
831     std::string tmp = f (text, state);
832 
833     std::size_t len = tmp.length ();
834 
835     if (len > 0)
836       {
837         retval = static_cast<char *> (std::malloc (len+1));
838 
839         if (retval)
840           strcpy (retval, tmp.c_str ());
841       }
842 
843     return retval;
844   }
845 
846   char *
command_quoter(char * text,int matches,char * qcp)847   gnu_readline::command_quoter (char *text, int matches, char *qcp)
848   {
849     char *retval = nullptr;
850 
851     quoting_fcn f = command_editor::get_quoting_function ();
852 
853     std::string tmp = f (text, matches, *qcp);
854 
855     std::size_t len = tmp.length ();
856 
857     if (len > 0)
858       {
859         retval = static_cast<char *> (std::malloc (len+1));
860 
861         if (retval)
862           strcpy (retval, tmp.c_str ());
863       }
864 
865     return retval;
866   }
867 
868   char *
command_dequoter(char * text,int quote)869   gnu_readline::command_dequoter (char *text, int quote)
870   {
871     char *retval = nullptr;
872 
873     dequoting_fcn f = command_editor::get_dequoting_function ();
874 
875     std::string tmp = f (text, quote);
876 
877     std::size_t len = tmp.length ();
878 
879     if (len > 0)
880       {
881         retval = static_cast<char *> (std::malloc (len+1));
882 
883         if (retval)
884           strcpy (retval, tmp.c_str ());
885       }
886 
887     return retval;
888   }
889 
890   int
command_char_is_quoted(char * text,int quote)891   gnu_readline::command_char_is_quoted (char *text, int quote)
892   {
893     char_is_quoted_fcn f = command_editor::get_char_is_quoted_function ();
894 
895     return f (text, quote);
896   }
897 
898   int
command_accept_line(int count,int key)899   gnu_readline::command_accept_line (int count, int key)
900   {
901     user_accept_line_fcn f = command_editor::get_user_accept_line_function ();
902 
903     if (f)
904       f (::octave_rl_line_buffer ());
905 
906     ::octave_rl_redisplay ();
907 
908     return ::octave_rl_newline (count, key);
909   }
910 
911   char **
command_completer(const char * text,int,int)912   gnu_readline::command_completer (const char *text, int, int)
913   {
914     char **matches
915       = ::octave_rl_completion_matches (text, gnu_readline::command_generator);
916 
917     return matches;
918   }
919 
920 #endif
921 
922   class
923   default_command_editor : public command_editor
924   {
925   public:
926 
default_command_editor(void)927     default_command_editor (void)
928       : command_editor (), input_stream (stdin), output_stream (stdout) { }
929 
930     // No copying!
931 
932     default_command_editor (const default_command_editor&) = delete;
933 
934     default_command_editor& operator = (const default_command_editor&) = delete;
935 
936     ~default_command_editor (void) = default;
937 
938     std::string do_readline (const std::string& prompt, bool& eof);
939 
940     void do_set_input_stream (FILE *f);
941 
942     FILE * do_get_input_stream (void);
943 
944     void do_set_output_stream (FILE *f);
945 
946     FILE * do_get_output_stream (void);
947 
948     string_vector do_generate_filename_completions (const std::string& text);
949 
950     std::string do_get_line_buffer (void) const;
951 
952     std::string do_get_current_line (void) const;
953 
954     char do_get_prev_char (int) const;
955 
956     void do_replace_line (const std::string& text, bool clear_undo);
957 
958     void do_kill_full_line (void);
959 
960     void do_insert_text (const std::string& text);
961 
962     void do_newline (void);
963 
964     void do_accept_line (void);
965 
966   private:
967 
968     FILE *input_stream;
969 
970     FILE *output_stream;
971   };
972 
973   std::string
do_readline(const std::string & prompt,bool & eof)974   default_command_editor::do_readline (const std::string& prompt, bool& eof)
975   {
976     std::fputs (prompt.c_str (), output_stream);
977     std::fflush (output_stream);
978 
979     return octave_fgetl (input_stream, eof);
980   }
981 
982   void
do_set_input_stream(FILE * f)983   default_command_editor::do_set_input_stream (FILE *f)
984   {
985     input_stream = f;
986   }
987 
988   FILE *
do_get_input_stream(void)989   default_command_editor::do_get_input_stream (void)
990   {
991     return input_stream;
992   }
993 
994   void
do_set_output_stream(FILE * f)995   default_command_editor::do_set_output_stream (FILE *f)
996   {
997     output_stream = f;
998   }
999 
1000   FILE *
do_get_output_stream(void)1001   default_command_editor::do_get_output_stream (void)
1002   {
1003     return output_stream;
1004   }
1005 
1006   string_vector
do_generate_filename_completions(const std::string &)1007   default_command_editor::do_generate_filename_completions (const std::string&)
1008   {
1009     // FIXME
1010     return string_vector ();
1011   }
1012 
1013   std::string
do_get_line_buffer(void) const1014   default_command_editor::do_get_line_buffer (void) const
1015   {
1016     return "";
1017   }
1018 
1019   std::string
do_get_current_line(void) const1020   default_command_editor::do_get_current_line (void) const
1021   {
1022     // FIXME
1023     return "";
1024   }
1025 
1026   char
do_get_prev_char(int) const1027   default_command_editor::do_get_prev_char (int) const
1028   {
1029     return '\0';
1030   }
1031 
1032   void
do_replace_line(const std::string &,bool)1033   default_command_editor::do_replace_line (const std::string&, bool)
1034   {
1035     // FIXME
1036   }
1037 
1038   void
do_kill_full_line(void)1039   default_command_editor::do_kill_full_line (void)
1040   {
1041     // FIXME
1042   }
1043 
1044   void
do_insert_text(const std::string &)1045   default_command_editor::do_insert_text (const std::string&)
1046   {
1047     // FIXME
1048   }
1049 
1050   void
do_newline(void)1051   default_command_editor::do_newline (void)
1052   {
1053     // FIXME
1054   }
1055 
1056   void
do_accept_line(void)1057   default_command_editor::do_accept_line (void)
1058   {
1059     // FIXME
1060   }
1061 
1062   bool
instance_ok(void)1063   command_editor::instance_ok (void)
1064   {
1065     bool retval = true;
1066 
1067     if (! s_instance)
1068       {
1069         make_command_editor ();
1070 
1071         if (s_instance)
1072           {
1073             s_instance->set_event_hook (event_handler);
1074 
1075             singleton_cleanup_list::add (cleanup_instance);
1076           }
1077       }
1078 
1079     if (! s_instance)
1080       (*current_liboctave_error_handler)
1081         ("unable to create command history object!");
1082 
1083     return retval;
1084   }
1085 
1086   void
make_command_editor(void)1087   command_editor::make_command_editor (void)
1088   {
1089 #if defined (USE_READLINE)
1090     s_instance = new gnu_readline ();
1091 #else
1092     s_instance = new default_command_editor ();
1093 #endif
1094   }
1095 
1096   void
force_default_editor(void)1097   command_editor::force_default_editor (void)
1098   {
1099     delete s_instance;
1100     s_instance = new default_command_editor ();
1101   }
1102 
1103   void
set_initial_input(const std::string & text)1104   command_editor::set_initial_input (const std::string& text)
1105   {
1106     if (instance_ok ())
1107       s_instance->m_initial_input = text;
1108   }
1109 
1110   int
insert_initial_input(void)1111   command_editor::insert_initial_input (void)
1112   {
1113     return instance_ok () ? s_instance->do_insert_initial_input () : 0;
1114   }
1115 
1116   int
startup_handler(void)1117   command_editor::startup_handler (void)
1118   {
1119     // Iterate over a copy of the set to avoid problems if a hook
1120     // function attempts to remove itself from the startup_hook_set.
1121 
1122     std::set<startup_hook_fcn> hook_set = startup_hook_set;
1123 
1124     for (startup_hook_fcn f : hook_set)
1125       {
1126         if (f)
1127           f ();
1128       }
1129 
1130     return 0;
1131   }
1132 
1133   int
pre_input_handler(void)1134   command_editor::pre_input_handler (void)
1135   {
1136     // Iterate over copy of the set to avoid problems if a hook function
1137     // attempts to remove itself from the pre_input_hook_set.
1138 
1139     std::set<pre_input_hook_fcn> hook_set = pre_input_hook_set;
1140 
1141     for (pre_input_hook_fcn f : hook_set)
1142       {
1143         if (f)
1144           f ();
1145       }
1146 
1147     return 0;
1148   }
1149 
1150   int
event_handler(void)1151   command_editor::event_handler (void)
1152   {
1153     if (octave_interrupt_state)
1154       handle_interrupt_signal ();
1155 
1156     event_hook_lock.lock ();
1157 
1158     std::set<event_hook_fcn> hook_set (event_hook_set);
1159 
1160     event_hook_lock.unlock ();
1161 
1162     for (event_hook_fcn f : hook_set)
1163       {
1164         if (f)
1165           f ();
1166       }
1167 
1168     return 0;
1169   }
1170 
1171   void
set_name(const std::string & n)1172   command_editor::set_name (const std::string& n)
1173   {
1174     if (instance_ok ())
1175       s_instance->do_set_name (n);
1176   }
1177 
1178   std::string
readline(const std::string & prompt)1179   command_editor::readline (const std::string& prompt)
1180   {
1181     bool eof;
1182 
1183     return readline (prompt, eof);
1184   }
1185 
1186   std::string
readline(const std::string & prompt,bool & eof)1187   command_editor::readline (const std::string& prompt, bool& eof)
1188   {
1189     std::string retval;
1190 
1191     if (instance_ok ())
1192       {
1193         if (! s_instance->m_initial_input.empty ())
1194           add_pre_input_hook (command_editor::insert_initial_input);
1195 
1196         retval = s_instance->do_readline (prompt, eof);
1197       }
1198 
1199     return retval;
1200   }
1201 
1202   void
set_input_stream(FILE * f)1203   command_editor::set_input_stream (FILE *f)
1204   {
1205     if (instance_ok ())
1206       s_instance->do_set_input_stream (f);
1207   }
1208 
1209   FILE *
get_input_stream(void)1210   command_editor::get_input_stream (void)
1211   {
1212     return instance_ok () ? s_instance->do_get_input_stream () : nullptr;
1213   }
1214 
1215   void
set_output_stream(FILE * f)1216   command_editor::set_output_stream (FILE *f)
1217   {
1218     if (instance_ok ())
1219       s_instance->do_set_output_stream (f);
1220   }
1221 
1222   FILE *
get_output_stream(void)1223   command_editor::get_output_stream (void)
1224   {
1225     return instance_ok () ? s_instance->do_get_output_stream () : nullptr;
1226   }
1227 
1228   void
redisplay(void)1229   command_editor::redisplay (void)
1230   {
1231     if (instance_ok ())
1232       s_instance->do_redisplay ();
1233   }
1234 
1235   int
terminal_rows(void)1236   command_editor::terminal_rows (void)
1237   {
1238     return instance_ok () ? s_instance->do_terminal_rows () : -1;
1239   }
1240 
1241   int
terminal_cols(void)1242   command_editor::terminal_cols (void)
1243   {
1244     return instance_ok () ? s_instance->do_terminal_cols () : -1;
1245   }
1246 
1247   void
clear_screen(bool skip_redisplay)1248   command_editor::clear_screen (bool skip_redisplay)
1249   {
1250     if (instance_ok ())
1251       s_instance->do_clear_screen (skip_redisplay);
1252   }
1253 
1254   void
resize_terminal(void)1255   command_editor::resize_terminal (void)
1256   {
1257     if (instance_ok ())
1258       s_instance->do_resize_terminal ();
1259   }
1260 
1261   void
set_screen_size(int ht,int wd)1262   command_editor::set_screen_size (int ht, int wd)
1263   {
1264     if (instance_ok ())
1265       s_instance->do_set_screen_size (ht, wd);
1266   }
1267 
1268   std::string
decode_prompt_string(const std::string & s)1269   command_editor::decode_prompt_string (const std::string& s)
1270   {
1271     return instance_ok () ? s_instance->do_decode_prompt_string (s) : "";
1272   }
1273 
1274   int
current_command_number(void)1275   command_editor::current_command_number (void)
1276   {
1277     return instance_ok () ? s_instance->m_command_number : 0;
1278   }
1279 
1280   void
reset_current_command_number(int n)1281   command_editor::reset_current_command_number (int n)
1282   {
1283     if (instance_ok ())
1284       s_instance->m_command_number = n;
1285   }
1286 
1287   void
increment_current_command_number(void)1288   command_editor::increment_current_command_number (void)
1289   {
1290     if (instance_ok ())
1291       s_instance->m_command_number++;
1292   }
1293 
1294   void
restore_terminal_state(void)1295   command_editor::restore_terminal_state (void)
1296   {
1297     if (instance_ok ())
1298       s_instance->do_restore_terminal_state ();
1299   }
1300 
1301   void
blink_matching_paren(bool flag)1302   command_editor::blink_matching_paren (bool flag)
1303   {
1304     if (instance_ok ())
1305       s_instance->do_blink_matching_paren (flag);
1306   }
1307 
1308   bool
erase_empty_line(bool flag)1309   command_editor::erase_empty_line (bool flag)
1310   {
1311     return instance_ok () ? s_instance->do_erase_empty_line (flag) : false;
1312   }
1313 
1314   void
set_basic_word_break_characters(const std::string & s)1315   command_editor::set_basic_word_break_characters (const std::string& s)
1316   {
1317     if (instance_ok ())
1318       s_instance->do_set_basic_word_break_characters (s);
1319   }
1320 
1321   void
set_completer_word_break_characters(const std::string & s)1322   command_editor::set_completer_word_break_characters (const std::string& s)
1323   {
1324     if (instance_ok ())
1325       s_instance->do_set_completer_word_break_characters (s);
1326   }
1327 
1328   void
set_basic_quote_characters(const std::string & s)1329   command_editor::set_basic_quote_characters (const std::string& s)
1330   {
1331     if (instance_ok ())
1332       s_instance->do_set_basic_quote_characters (s);
1333   }
1334 
1335   void
set_filename_quote_characters(const std::string & s)1336   command_editor::set_filename_quote_characters (const std::string& s)
1337   {
1338     if (instance_ok ())
1339       s_instance->do_set_filename_quote_characters (s);
1340   }
1341 
1342   void
set_completer_quote_characters(const std::string & s)1343   command_editor::set_completer_quote_characters (const std::string& s)
1344   {
1345     if (instance_ok ())
1346       s_instance->do_set_completer_quote_characters (s);
1347   }
1348 
1349   void
set_completion_append_character(char c)1350   command_editor::set_completion_append_character (char c)
1351   {
1352     if (instance_ok ())
1353       s_instance->do_set_completion_append_character (c);
1354   }
1355 
1356   void
set_completion_function(completion_fcn f)1357   command_editor::set_completion_function (completion_fcn f)
1358   {
1359     if (instance_ok ())
1360       s_instance->do_set_completion_function (f);
1361   }
1362 
1363   void
set_quoting_function(quoting_fcn f)1364   command_editor::set_quoting_function (quoting_fcn f)
1365   {
1366     if (instance_ok ())
1367       s_instance->do_set_quoting_function (f);
1368   }
1369 
1370   void
set_dequoting_function(dequoting_fcn f)1371   command_editor::set_dequoting_function (dequoting_fcn f)
1372   {
1373     if (instance_ok ())
1374       s_instance->do_set_dequoting_function (f);
1375   }
1376 
1377   void
set_char_is_quoted_function(char_is_quoted_fcn f)1378   command_editor::set_char_is_quoted_function (char_is_quoted_fcn f)
1379   {
1380     if (instance_ok ())
1381       s_instance->do_set_char_is_quoted_function (f);
1382   }
1383 
1384   void
set_user_accept_line_function(user_accept_line_fcn f)1385   command_editor::set_user_accept_line_function (user_accept_line_fcn f)
1386   {
1387     if (instance_ok ())
1388       s_instance->do_set_user_accept_line_function (f);
1389   }
1390 
1391   command_editor::completion_fcn
get_completion_function(void)1392   command_editor::get_completion_function (void)
1393   {
1394     return instance_ok () ? s_instance->do_get_completion_function () : nullptr;
1395   }
1396 
1397   command_editor::quoting_fcn
get_quoting_function(void)1398   command_editor::get_quoting_function (void)
1399   {
1400     return instance_ok () ? s_instance->do_get_quoting_function () : nullptr;
1401   }
1402 
1403   command_editor::dequoting_fcn
get_dequoting_function(void)1404   command_editor::get_dequoting_function (void)
1405   {
1406     return instance_ok () ? s_instance->do_get_dequoting_function () : nullptr;
1407   }
1408 
1409   command_editor::char_is_quoted_fcn
get_char_is_quoted_function(void)1410   command_editor::get_char_is_quoted_function (void)
1411   {
1412     return (instance_ok ()
1413             ? s_instance->do_get_char_is_quoted_function () : nullptr);
1414   }
1415 
1416   command_editor::user_accept_line_fcn
get_user_accept_line_function(void)1417   command_editor::get_user_accept_line_function (void)
1418   {
1419     return (instance_ok ()
1420             ? s_instance->do_get_user_accept_line_function () : nullptr);
1421   }
1422 
1423   string_vector
generate_filename_completions(const std::string & text)1424   command_editor::generate_filename_completions (const std::string& text)
1425   {
1426     return (instance_ok ()
1427             ? s_instance->do_generate_filename_completions (text)
1428             : string_vector ());
1429   }
1430 
1431   std::string
get_line_buffer(void)1432   command_editor::get_line_buffer (void)
1433   {
1434     return instance_ok () ? s_instance->do_get_line_buffer () : "";
1435   }
1436 
1437   std::string
get_current_line(void)1438   command_editor::get_current_line (void)
1439   {
1440     return instance_ok () ? s_instance->do_get_current_line () : "";
1441   }
1442 
1443   // Return the character (offset+1) to the left of the cursor,
1444   // or '\0' if the cursor is at the start of the line.
1445   char
get_prev_char(int offset)1446   command_editor::get_prev_char (int offset)
1447   {
1448     return instance_ok () ? s_instance->do_get_prev_char (offset) : '\0';
1449   }
1450 
1451   void
replace_line(const std::string & text,bool clear_undo)1452   command_editor::replace_line (const std::string& text, bool clear_undo)
1453   {
1454     if (instance_ok ())
1455       s_instance->do_replace_line (text, clear_undo);
1456   }
1457 
1458   void
kill_full_line(void)1459   command_editor::kill_full_line (void)
1460   {
1461     if (instance_ok ())
1462       s_instance->do_kill_full_line ();
1463   }
1464 
1465   void
insert_text(const std::string & text)1466   command_editor::insert_text (const std::string& text)
1467   {
1468     if (instance_ok ())
1469       s_instance->do_insert_text (text);
1470   }
1471 
1472   void
newline(void)1473   command_editor::newline (void)
1474   {
1475     if (instance_ok ())
1476       s_instance->do_newline ();
1477   }
1478 
1479   void
accept_line(void)1480   command_editor::accept_line (void)
1481   {
1482     if (instance_ok ())
1483       s_instance->do_accept_line ();
1484   }
1485 
1486   bool
undo(void)1487   command_editor::undo (void)
1488   {
1489     return instance_ok () ? s_instance->do_undo () : false;
1490   }
1491 
1492   void
clear_undo_list(void)1493   command_editor::clear_undo_list (void)
1494   {
1495     if (instance_ok ())
1496       s_instance->do_clear_undo_list ();
1497   }
1498 
1499   void
add_startup_hook(startup_hook_fcn f)1500   command_editor::add_startup_hook (startup_hook_fcn f)
1501   {
1502     if (instance_ok ())
1503       {
1504         startup_hook_set.insert (f);
1505 
1506         s_instance->set_startup_hook (startup_handler);
1507       }
1508   }
1509 
1510   void
remove_startup_hook(startup_hook_fcn f)1511   command_editor::remove_startup_hook (startup_hook_fcn f)
1512   {
1513     if (instance_ok ())
1514       {
1515         auto p = startup_hook_set.find (f);
1516 
1517         if (p != startup_hook_set.end ())
1518           startup_hook_set.erase (p);
1519 
1520         if (startup_hook_set.empty ())
1521           s_instance->restore_startup_hook ();
1522       }
1523   }
1524 
1525   void
add_pre_input_hook(pre_input_hook_fcn f)1526   command_editor::add_pre_input_hook (pre_input_hook_fcn f)
1527   {
1528     if (instance_ok ())
1529       {
1530         pre_input_hook_set.insert (f);
1531 
1532         s_instance->set_pre_input_hook (pre_input_handler);
1533       }
1534   }
1535 
1536   void
remove_pre_input_hook(pre_input_hook_fcn f)1537   command_editor::remove_pre_input_hook (pre_input_hook_fcn f)
1538   {
1539     if (instance_ok ())
1540       {
1541         auto p = pre_input_hook_set.find (f);
1542 
1543         if (p != pre_input_hook_set.end ())
1544           pre_input_hook_set.erase (p);
1545 
1546         if (pre_input_hook_set.empty ())
1547           s_instance->restore_pre_input_hook ();
1548       }
1549   }
1550 
1551   void
add_event_hook(event_hook_fcn f)1552   command_editor::add_event_hook (event_hook_fcn f)
1553   {
1554     autolock guard (event_hook_lock);
1555 
1556     event_hook_set.insert (f);
1557   }
1558 
1559   void
remove_event_hook(event_hook_fcn f)1560   command_editor::remove_event_hook (event_hook_fcn f)
1561   {
1562     autolock guard (event_hook_lock);
1563 
1564     auto p = event_hook_set.find (f);
1565 
1566     if (p != event_hook_set.end ())
1567       event_hook_set.erase (p);
1568 
1569   }
1570 
1571   void
run_event_hooks(void)1572   command_editor::run_event_hooks (void)
1573   {
1574     event_handler ();
1575   }
1576 
1577   void
read_init_file(const std::string & file_arg)1578   command_editor::read_init_file (const std::string& file_arg)
1579   {
1580     if (instance_ok ())
1581       {
1582         std::string file = sys::file_ops::tilde_expand (file_arg);
1583 
1584         s_instance->do_read_init_file (file);
1585       }
1586   }
1587 
1588   void
re_read_init_file(void)1589   command_editor::re_read_init_file (void)
1590   {
1591     if (instance_ok ())
1592       s_instance->do_re_read_init_file ();
1593   }
1594 
1595   bool
filename_completion_desired(bool arg)1596   command_editor::filename_completion_desired (bool arg)
1597   {
1598     return (instance_ok ()
1599             ? s_instance->do_filename_completion_desired (arg) : false);
1600   }
1601 
1602   bool
filename_quoting_desired(bool arg)1603   command_editor::filename_quoting_desired (bool arg)
1604   {
1605     return (instance_ok ())
1606            ? s_instance->do_filename_quoting_desired (arg) : false;
1607   }
1608 
1609   bool
prefer_env_winsize(bool arg)1610   command_editor::prefer_env_winsize (bool arg)
1611   {
1612     return instance_ok () ? s_instance->do_prefer_env_winsize (arg) : false;
1613   }
1614 
1615   bool
interrupt(bool arg)1616   command_editor::interrupt (bool arg)
1617   {
1618     bool retval;
1619 
1620     if (instance_ok ())
1621       {
1622         // Return the current interrupt state.
1623         retval = s_instance->m_interrupted;
1624 
1625         s_instance->do_interrupt (arg);
1626 
1627         s_instance->m_interrupted = arg;
1628       }
1629     else
1630       retval = false;
1631 
1632     return retval;
1633   }
1634 
1635   void
interrupt_event_loop(bool arg)1636   command_editor::interrupt_event_loop (bool arg)
1637   {
1638     if (instance_ok ())
1639       s_instance->do_interrupt_event_loop (arg);
1640   }
1641 
1642   bool
event_loop_interrupted(void)1643   command_editor::event_loop_interrupted (void)
1644   {
1645     return instance_ok () ? s_instance->do_event_loop_interrupted  () : false;
1646   }
1647 
1648   void
handle_interrupt_signal(void)1649   command_editor::handle_interrupt_signal (void)
1650   {
1651     if (instance_ok ())
1652       s_instance->do_handle_interrupt_signal ();
1653   }
1654 
1655   // Return a string which will be printed as a prompt.  The string may
1656   // contain special characters which are decoded as follows:
1657   //
1658   //      \a      bell (ascii 07)
1659   //      \d      the date
1660   //      \e      escape (ascii 033)
1661   //      \h      the hostname up to the first '.'
1662   //      \H      the hostname
1663   //      \n      CRLF
1664   //      \r      CR
1665   //      \s      the name of the shell (program)
1666   //      \t      the time
1667   //      \T      the time in 12-hour hh:mm:ss format
1668   //      \@      the time in 12-hour hh:mm am/pm format
1669   //      \A      the time in 24-hour hh:mm format
1670   //      \u      your username
1671   //      \w      the current working directory
1672   //      \W      the last element of PWD
1673   //      \!      the history number of this command
1674   //      \#      the command number of this command
1675   //      \$      a $ or a # if you are root
1676   //      \nnn    character code nnn in octal
1677   //      \\      a backslash
1678   //      \[      begin a sequence of non-printing chars
1679   //      \]      end a sequence of non-printing chars
1680 
1681   std::string
do_decode_prompt_string(const std::string & s)1682   command_editor::do_decode_prompt_string (const std::string& s)
1683   {
1684     std::string retval;
1685     std::string tmpstr;
1686     std::size_t i = 0;
1687     std::size_t slen = s.length ();
1688     int c;
1689 
1690     while (i < slen)
1691       {
1692         c = s[i];
1693 
1694         i++;
1695 
1696         if (c == '\\')
1697           {
1698             c = s[i];
1699 
1700             switch (c)
1701               {
1702               case '0':
1703               case '1':
1704               case '2':
1705               case '3':
1706               case '4':
1707               case '5':
1708               case '6':
1709               case '7':
1710                 // Maybe convert an octal number.
1711                 {
1712                   int n = read_octal (s.substr (i, 3));
1713 
1714                   tmpstr = '\\';
1715 
1716                   if (n != -1)
1717                     {
1718                       tmpstr[0] = n;
1719                       i += 2;   // i++ makes this += 3 later
1720                     }
1721 
1722                   break;
1723                 }
1724 
1725               case 'a':
1726                 {
1727                   tmpstr = '\a';
1728 
1729                   break;
1730                 }
1731 
1732               case 'd':
1733               case 't':
1734               case 'T':
1735               case '@':
1736               case 'A':
1737                 // Make the current time/date into a string.
1738                 {
1739                   sys::localtime now;
1740 
1741                   if (c == 'd')
1742                     tmpstr = now.strftime ("%a %b %d");
1743                   else if (c == 't')
1744                     tmpstr = now.strftime ("%H:%M:%S");
1745                   else if (c == 'T')
1746                     tmpstr = now.strftime ("%I:%M:%S");
1747                   else if (c == '@')
1748                     tmpstr = now.strftime ("%I:%M %p");
1749                   else // (c == 'A')
1750                     tmpstr = now.strftime ("%H:%M");
1751 
1752                   break;
1753                 }
1754 
1755               case 'e':
1756                 {
1757                   tmpstr = '\033';
1758 
1759                   break;
1760                 }
1761 
1762               case 'h':
1763                 {
1764                   tmpstr = sys::env::get_host_name ();
1765 
1766                   std::size_t pos = tmpstr.find ('.');
1767 
1768                   if (pos != std::string::npos)
1769                     tmpstr.resize (pos);
1770 
1771                   break;
1772                 }
1773 
1774               case 'H':
1775                 {
1776                   tmpstr = sys::env::get_host_name ();
1777 
1778                   break;
1779                 }
1780 
1781               case 'n':
1782                 {
1783                   tmpstr = newline_chars ();
1784 
1785                   break;
1786                 }
1787 
1788               case 'r':
1789                 {
1790                   tmpstr = '\r';
1791 
1792                   break;
1793                 }
1794 
1795               case 's':
1796                 {
1797                   tmpstr = sys::env::get_program_name ();
1798                   tmpstr = sys::env::base_pathname (tmpstr);
1799 
1800                   break;
1801                 }
1802 
1803               case 'u':
1804                 {
1805                   tmpstr = sys::env::get_user_name ();
1806 
1807                   break;
1808                 }
1809 
1810               case 'w':
1811               case 'W':
1812                 {
1813                   try
1814                     {
1815                       tmpstr = sys::env::get_current_directory ();
1816                     }
1817                   catch (const execution_exception&)
1818                     {
1819                       tmpstr = "";
1820                     }
1821 
1822                   std::string home_dir = sys::env::get_home_directory ();
1823 
1824                   if (c == 'W' && (home_dir.empty () || tmpstr != home_dir))
1825                     {
1826                       if (tmpstr != "/" && tmpstr != "//")
1827                         {
1828                           std::size_t pos = tmpstr.rfind ('/');
1829 
1830                           if (pos != std::string::npos && pos != 0)
1831                             tmpstr = tmpstr.substr (pos + 1);
1832                         }
1833                     }
1834                   else
1835                     tmpstr = sys::env::polite_directory_format (tmpstr);
1836 
1837                   break;
1838                 }
1839 
1840               case '!':
1841                 {
1842                   char number_buffer[32];
1843                   int num = command_history::current_number ();
1844                   if (num > 0)
1845                     sprintf (number_buffer, "%d", num);
1846                   else
1847                     strcpy (number_buffer, "!");
1848                   tmpstr = number_buffer;
1849 
1850                   break;
1851                 }
1852 
1853               case '#':
1854                 {
1855                   char number_buffer[32];
1856                   sprintf (number_buffer, "%d", m_command_number);
1857                   tmpstr = number_buffer;
1858 
1859                   break;
1860                 }
1861 
1862               case '$':
1863                 {
1864                   tmpstr = (octave_geteuid_wrapper () == 0 ? '#' : '$');
1865                   break;
1866                 }
1867 
1868 #if defined (USE_READLINE)
1869               case '[':
1870               case ']':
1871                 {
1872                   tmpstr.resize (1);
1873 
1874                   tmpstr[0] = ((c == '[')
1875                                ? ::octave_rl_prompt_start_ignore ()
1876                                : ::octave_rl_prompt_end_ignore ());
1877 
1878                   break;
1879                 }
1880 #endif
1881 
1882               case '\\':
1883                 {
1884                   tmpstr = '\\';
1885 
1886                   break;
1887                 }
1888 
1889               default:
1890                 {
1891                   tmpstr = "\\ ";
1892                   tmpstr[1] = c;
1893 
1894                   break;
1895                 }
1896               }
1897 
1898             retval.append (tmpstr);
1899             i++;   // Move past processed escape character
1900           }
1901         else
1902           retval += c;
1903       }
1904 
1905     return retval;
1906   }
1907 
1908   int
do_insert_initial_input(void)1909   command_editor::do_insert_initial_input (void)
1910   {
1911     std::string input = m_initial_input;
1912 
1913     m_initial_input = "";
1914 
1915     do_insert_text (input);
1916 
1917     // Is it really right to redisplay here?
1918     do_redisplay ();
1919 
1920     return 0;
1921   }
1922 
1923   // Return the octal number parsed from STRING, or -1 to indicate that
1924   // the string contained a bad number.
1925 
1926   int
read_octal(const std::string & s)1927   command_editor::read_octal (const std::string& s)
1928   {
1929     int result = 0;
1930     int digits = 0;
1931 
1932     std::size_t i = 0;
1933     std::size_t slen = s.length ();
1934 
1935     while (i < slen && s[i] >= '0' && s[i] < '8')
1936       {
1937         digits++;
1938         result = (result * 8) + s[i] - '0';
1939         i++;
1940       }
1941 
1942     if (! digits || result > 0777 || i < slen)
1943       result = -1;
1944 
1945     return result;
1946   }
1947 
1948   void
error(int err_num)1949   command_editor::error (int err_num)
1950   {
1951     (*current_liboctave_error_handler) ("%s", std::strerror (err_num));
1952   }
1953 
1954   void
error(const std::string & s)1955   command_editor::error (const std::string& s)
1956   {
1957     (*current_liboctave_error_handler) ("%s", s.c_str ());
1958   }
1959 }
1960