Mercurial > hg > octave-nkf
comparison liboctave/cmd-edit.cc @ 2926:66ef74ee5d9f
[project @ 1997-05-05 03:20:52 by jwe]
author | jwe |
---|---|
date | Mon, 05 May 1997 03:40:21 +0000 |
parents | |
children | b779a5b8aed4 |
comparison
equal
deleted
inserted
replaced
2925:f0665dac8e33 | 2926:66ef74ee5d9f |
---|---|
1 /* | |
2 | |
3 Copyright (C) 1996, 1997 John W. Eaton | |
4 | |
5 This file is part of Octave. | |
6 | |
7 Octave is free software; you can redistribute it and/or modify it | |
8 under the terms of the GNU General Public License as published by the | |
9 Free Software Foundation; either version 2, or (at your option) any | |
10 later version. | |
11 | |
12 Octave is distributed in the hope that it will be useful, but WITHOUT | |
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 for more details. | |
16 | |
17 You should have received a copy of the GNU General Public License | |
18 along with Octave; see the file COPYING. If not, write to the Free | |
19 Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
20 | |
21 */ | |
22 | |
23 #ifdef HAVE_CONFIG_H | |
24 #include <config.h> | |
25 #endif | |
26 | |
27 #include <cstring> | |
28 #include <ctime> | |
29 | |
30 #include <string> | |
31 | |
32 #ifdef HAVE_UNISTD_H | |
33 #ifdef HAVE_SYS_TYPES_H | |
34 #include <sys/types.h> | |
35 #endif | |
36 #include <unistd.h> | |
37 #endif | |
38 | |
39 #include "cmd-edit.h" | |
40 #include "cmd-hist.h" | |
41 #include "lo-error.h" | |
42 #include "lo-utils.h" | |
43 #include "oct-env.h" | |
44 | |
45 command_editor *command_editor::instance = 0; | |
46 | |
47 #if defined (USE_READLINE) | |
48 | |
49 #include <cstdio> | |
50 #include <cstdlib> | |
51 | |
52 #include <readline/readline.h> | |
53 | |
54 // It would be nice if readline.h declared these, I think. | |
55 | |
56 extern "C" void rl_deprep_terminal (void); | |
57 | |
58 extern int rl_blink_matching_paren; | |
59 | |
60 extern int screenheight; | |
61 | |
62 extern int screenwidth; | |
63 | |
64 class | |
65 gnu_readline : public command_editor | |
66 { | |
67 public: | |
68 | |
69 typedef command_editor::fcn fcn; | |
70 | |
71 gnu_readline (void); | |
72 | |
73 ~gnu_readline (void) { } | |
74 | |
75 void do_set_name (const string& n); | |
76 | |
77 string do_readline (const string& prompt); | |
78 | |
79 void do_set_input_stream (FILE *f); | |
80 | |
81 FILE *do_get_input_stream (void); | |
82 | |
83 void do_set_output_stream (FILE *f); | |
84 | |
85 FILE *do_get_output_stream (void); | |
86 | |
87 int do_terminal_rows (void); | |
88 | |
89 int do_terminal_cols (void); | |
90 | |
91 void do_clear_screen (void); | |
92 | |
93 string newline_chars (void); | |
94 | |
95 void do_restore_terminal_state (void); | |
96 | |
97 void do_blink_matching_paren (bool flag); | |
98 | |
99 void do_set_paren_string_delimiters (const string& s); | |
100 | |
101 void do_set_completion_append_character (char c); | |
102 | |
103 void do_set_attempted_completion_function (fcn f); | |
104 | |
105 void do_insert_text (const string& text); | |
106 | |
107 void do_newline (void); | |
108 | |
109 void do_clear_undo_list (void); | |
110 | |
111 void do_set_startup_hook (fcn f); | |
112 | |
113 void do_restore_startup_hook (void); | |
114 | |
115 static void operate_and_get_next (int, int); | |
116 | |
117 private: | |
118 | |
119 fcn previous_startup_hook; | |
120 | |
121 fcn attempted_completion_function; | |
122 }; | |
123 | |
124 gnu_readline::gnu_readline () | |
125 : command_editor (), previous_startup_hook (0), | |
126 attempted_completion_function (0) | |
127 { | |
128 rl_initialize (); | |
129 | |
130 do_blink_matching_paren (true); | |
131 | |
132 // Bind operate-and-get-next. | |
133 | |
134 rl_add_defun ("operate-and-get-next", | |
135 gnu_readline::operate_and_get_next, CTRL ('O')); | |
136 | |
137 // And the history search functions. | |
138 | |
139 rl_add_defun ("history-search-backward", | |
140 rl_history_search_backward, META ('p')); | |
141 | |
142 rl_add_defun ("history-search-forward", | |
143 rl_history_search_forward, META ('n')); | |
144 } | |
145 | |
146 void | |
147 gnu_readline::do_set_name (const string& n) | |
148 { | |
149 static char *nm = 0; | |
150 | |
151 delete [] nm; | |
152 | |
153 nm = strsave (n.c_str ()); | |
154 | |
155 rl_readline_name = nm; | |
156 | |
157 // Since we've already called rl_initialize, we need to re-read the | |
158 // init file to take advantage of the conditional parsing feature | |
159 // based on rl_readline_name; | |
160 | |
161 rl_re_read_init_file (); | |
162 } | |
163 | |
164 string | |
165 gnu_readline::do_readline (const string& prompt) | |
166 { | |
167 string retval; | |
168 | |
169 char *line = ::readline (prompt.c_str ()); | |
170 | |
171 if (line) | |
172 { | |
173 retval = line; | |
174 | |
175 free (line); | |
176 } | |
177 | |
178 return retval; | |
179 } | |
180 | |
181 void | |
182 gnu_readline::do_set_input_stream (FILE *f) | |
183 { | |
184 rl_instream = f; | |
185 } | |
186 | |
187 FILE * | |
188 gnu_readline::do_get_input_stream (void) | |
189 { | |
190 return rl_instream; | |
191 } | |
192 | |
193 void | |
194 gnu_readline::do_set_output_stream (FILE *f) | |
195 { | |
196 rl_outstream = f; | |
197 } | |
198 | |
199 FILE * | |
200 gnu_readline::do_get_output_stream (void) | |
201 { | |
202 return rl_outstream; | |
203 } | |
204 | |
205 // GNU readline handles SIGWINCH, so these values have a good chance | |
206 // of being correct even if the window changes size (they may be | |
207 // wrong if, for example, the luser changes the window size while the | |
208 // pager is running, and the signal is handled by the pager instead of | |
209 // us. | |
210 | |
211 int | |
212 gnu_readline::do_terminal_rows (void) | |
213 { | |
214 return screenheight > 0 ? screenheight : 24; | |
215 } | |
216 | |
217 int | |
218 gnu_readline::do_terminal_cols (void) | |
219 { | |
220 return screenwidth > 0 ? screenwidth : 80; | |
221 } | |
222 | |
223 void | |
224 gnu_readline::do_clear_screen (void) | |
225 { | |
226 rl_clear_screen (); | |
227 } | |
228 | |
229 string | |
230 gnu_readline::newline_chars (void) | |
231 { | |
232 return "\r\n"; | |
233 } | |
234 | |
235 void | |
236 gnu_readline::do_restore_terminal_state (void) | |
237 { | |
238 rl_deprep_terminal (); | |
239 } | |
240 | |
241 void | |
242 gnu_readline::do_blink_matching_paren (bool flag) | |
243 { | |
244 rl_blink_matching_paren = flag ? 1 : 0; | |
245 } | |
246 | |
247 void | |
248 gnu_readline::do_set_paren_string_delimiters (const string& s) | |
249 { | |
250 static char *ss = 0; | |
251 | |
252 delete [] ss; | |
253 | |
254 ss = strsave (s.c_str ()); | |
255 | |
256 rl_paren_string_delimiters = ss; | |
257 } | |
258 | |
259 void | |
260 gnu_readline::do_set_completion_append_character (char c) | |
261 { | |
262 rl_completion_append_character = c; | |
263 } | |
264 | |
265 void | |
266 gnu_readline::do_set_attempted_completion_function (fcn f) | |
267 { | |
268 attempted_completion_function = f; | |
269 } | |
270 | |
271 void | |
272 gnu_readline::do_insert_text (const string& text) | |
273 { | |
274 rl_insert_text (text.c_str ()); | |
275 } | |
276 | |
277 void | |
278 gnu_readline::do_newline (void) | |
279 { | |
280 rl_newline (); | |
281 } | |
282 | |
283 void | |
284 gnu_readline::do_clear_undo_list () | |
285 { | |
286 if (rl_undo_list) | |
287 { | |
288 free_undo_list (); | |
289 | |
290 rl_undo_list = 0; | |
291 } | |
292 } | |
293 | |
294 void | |
295 gnu_readline::do_set_startup_hook (fcn f) | |
296 { | |
297 previous_startup_hook = rl_startup_hook; | |
298 | |
299 rl_startup_hook = f; | |
300 } | |
301 | |
302 void | |
303 gnu_readline::do_restore_startup_hook (void) | |
304 { | |
305 rl_startup_hook = previous_startup_hook; | |
306 } | |
307 | |
308 void | |
309 gnu_readline::operate_and_get_next (int /* count */, int /* c */) | |
310 { | |
311 // Accept the current line. | |
312 | |
313 command_editor::newline (); | |
314 | |
315 // Find the current line, and find the next line to use. | |
316 | |
317 int x_where = command_history::where (); | |
318 | |
319 int x_length = command_history::length (); | |
320 | |
321 if ((command_history::is_stifled () | |
322 && (x_length >= command_history::max_input_history ())) | |
323 || (x_where >= x_length - 1)) | |
324 command_history::set_mark (x_where); | |
325 else | |
326 command_history::set_mark (x_where + 1); | |
327 | |
328 command_editor::set_startup_hook (command_history::goto_mark); | |
329 } | |
330 | |
331 #endif | |
332 | |
333 class | |
334 default_command_editor : public command_editor | |
335 { | |
336 public: | |
337 | |
338 default_command_editor (void) | |
339 : command_editor (), input_stream (stdin), output_stream (stdout) { } | |
340 | |
341 ~default_command_editor (void) { } | |
342 | |
343 string do_readline (const string& prompt); | |
344 | |
345 void do_set_input_stream (FILE *f); | |
346 | |
347 FILE *do_get_input_stream (void); | |
348 | |
349 void do_set_output_stream (FILE *f); | |
350 | |
351 FILE *do_get_output_stream (void); | |
352 | |
353 void do_insert_text (const string&); | |
354 | |
355 void do_newline (void); | |
356 | |
357 private: | |
358 | |
359 FILE *input_stream; | |
360 | |
361 FILE *output_stream; | |
362 }; | |
363 | |
364 string | |
365 default_command_editor::do_readline (const string& prompt) | |
366 { | |
367 fprintf (output_stream, prompt.c_str ()); | |
368 fflush (output_stream); | |
369 | |
370 return octave_fgets (input_stream); | |
371 } | |
372 | |
373 void | |
374 default_command_editor::do_set_input_stream (FILE *f) | |
375 { | |
376 input_stream = f; | |
377 } | |
378 | |
379 FILE * | |
380 default_command_editor::do_get_input_stream (void) | |
381 { | |
382 return input_stream; | |
383 } | |
384 | |
385 void | |
386 default_command_editor::do_set_output_stream (FILE *f) | |
387 { | |
388 output_stream = f; | |
389 } | |
390 | |
391 FILE * | |
392 default_command_editor::do_get_output_stream (void) | |
393 { | |
394 return output_stream; | |
395 } | |
396 | |
397 void | |
398 default_command_editor::do_insert_text (const string&) | |
399 { | |
400 // XXX FIXME XXX | |
401 } | |
402 | |
403 void | |
404 default_command_editor::do_newline (void) | |
405 { | |
406 // XXX FIXME XXX | |
407 } | |
408 | |
409 bool | |
410 command_editor::instance_ok (void) | |
411 { | |
412 bool retval = true; | |
413 | |
414 if (! instance) | |
415 make_command_editor (); | |
416 | |
417 if (! instance) | |
418 { | |
419 (*current_liboctave_error_handler) | |
420 ("unable to create command history object!"); | |
421 | |
422 retval = false; | |
423 } | |
424 | |
425 return retval; | |
426 } | |
427 | |
428 void | |
429 command_editor::make_command_editor (void) | |
430 { | |
431 #if defined (USE_READLINE) | |
432 instance = new gnu_readline (); | |
433 #else | |
434 instance = new default_command_editor (); | |
435 #endif | |
436 } | |
437 | |
438 void | |
439 command_editor::set_name (const string& n) | |
440 { | |
441 if (instance_ok ()) | |
442 instance->do_set_name (n); | |
443 } | |
444 | |
445 string | |
446 command_editor::readline (const string& prompt) | |
447 { | |
448 return (instance_ok ()) | |
449 ? instance->do_readline (prompt) : string (); | |
450 } | |
451 | |
452 void | |
453 command_editor::set_input_stream (FILE *f) | |
454 { | |
455 if (instance_ok ()) | |
456 instance->do_set_input_stream (f); | |
457 } | |
458 | |
459 FILE * | |
460 command_editor::get_input_stream (void) | |
461 { | |
462 return (instance_ok ()) | |
463 ? instance->do_get_input_stream () : 0; | |
464 } | |
465 | |
466 void | |
467 command_editor::set_output_stream (FILE *f) | |
468 { | |
469 if (instance_ok ()) | |
470 instance->do_set_output_stream (f); | |
471 } | |
472 | |
473 FILE * | |
474 command_editor::get_output_stream (void) | |
475 { | |
476 return (instance_ok ()) | |
477 ? instance->do_get_output_stream () : 0; | |
478 } | |
479 | |
480 int | |
481 command_editor::terminal_rows (void) | |
482 { | |
483 return (instance_ok ()) | |
484 ? instance->do_terminal_rows () : -1; | |
485 } | |
486 | |
487 int | |
488 command_editor::terminal_cols (void) | |
489 { | |
490 return (instance_ok ()) | |
491 ? instance->do_terminal_cols () : -1; | |
492 } | |
493 | |
494 void | |
495 command_editor::clear_screen (void) | |
496 { | |
497 if (instance_ok ()) | |
498 instance->do_clear_screen (); | |
499 } | |
500 | |
501 string | |
502 command_editor::decode_prompt_string (const string& s) | |
503 { | |
504 return (instance_ok ()) | |
505 ? instance->do_decode_prompt_string (s) : string (); | |
506 } | |
507 | |
508 int | |
509 command_editor::current_command_number (void) | |
510 { | |
511 return (instance_ok ()) | |
512 ? instance->command_number : 0; | |
513 } | |
514 | |
515 void | |
516 command_editor::reset_current_command_number (int n) | |
517 { | |
518 if (instance_ok ()) | |
519 instance->command_number = n; | |
520 } | |
521 | |
522 void | |
523 command_editor::restore_terminal_state (void) | |
524 { | |
525 if (instance_ok ()) | |
526 instance->do_restore_terminal_state (); | |
527 } | |
528 | |
529 void | |
530 command_editor::blink_matching_paren (bool flag) | |
531 { | |
532 if (instance_ok ()) | |
533 instance->do_blink_matching_paren (flag); | |
534 } | |
535 | |
536 void | |
537 command_editor::set_paren_string_delimiters (const string& s) | |
538 { | |
539 if (instance_ok ()) | |
540 instance->do_set_paren_string_delimiters (s); | |
541 } | |
542 | |
543 void | |
544 command_editor::set_completion_append_character (char c) | |
545 { | |
546 if (instance_ok ()) | |
547 instance->do_set_completion_append_character (c); | |
548 } | |
549 | |
550 void | |
551 command_editor::set_attempted_completion_function (fcn f) | |
552 { | |
553 if (instance_ok ()) | |
554 instance->do_set_attempted_completion_function (f); | |
555 } | |
556 | |
557 void | |
558 command_editor::insert_text (const string& text) | |
559 { | |
560 if (instance_ok ()) | |
561 instance->do_insert_text (text); | |
562 } | |
563 | |
564 void | |
565 command_editor::newline (void) | |
566 { | |
567 if (instance_ok ()) | |
568 instance->do_newline (); | |
569 } | |
570 | |
571 void | |
572 command_editor::clear_undo_list (void) | |
573 { | |
574 if (instance_ok ()) | |
575 instance->do_clear_undo_list (); | |
576 } | |
577 | |
578 void | |
579 command_editor::set_startup_hook (fcn f) | |
580 { | |
581 if (instance_ok ()) | |
582 instance->do_set_startup_hook (f); | |
583 } | |
584 | |
585 void | |
586 command_editor::restore_startup_hook (void) | |
587 { | |
588 if (instance_ok ()) | |
589 instance->do_restore_startup_hook (); | |
590 } | |
591 | |
592 // Return a string which will be printed as a prompt. The string may | |
593 // contain special characters which are decoded as follows: | |
594 // | |
595 // \t the time | |
596 // \d the date | |
597 // \n CRLF | |
598 // \s the name of the shell (program) | |
599 // \w the current working directory | |
600 // \W the last element of PWD | |
601 // \u your username | |
602 // \h the hostname | |
603 // \# the command number of this command | |
604 // \! the history number of this command | |
605 // \$ a $ or a # if you are root | |
606 // \<octal> character code in octal | |
607 // \\ a backslash | |
608 | |
609 string | |
610 command_editor::do_decode_prompt_string (const string& s) | |
611 { | |
612 string result; | |
613 string temp; | |
614 size_t i = 0; | |
615 size_t slen = s.length (); | |
616 int c; | |
617 | |
618 while (i < slen) | |
619 { | |
620 c = s[i]; | |
621 | |
622 i++; | |
623 | |
624 if (c == '\\') | |
625 { | |
626 c = s[i]; | |
627 | |
628 switch (c) | |
629 { | |
630 case '0': | |
631 case '1': | |
632 case '2': | |
633 case '3': | |
634 case '4': | |
635 case '5': | |
636 case '6': | |
637 case '7': | |
638 // Maybe convert an octal number. | |
639 { | |
640 int n = read_octal (s.substr (i, 3)); | |
641 | |
642 temp = "\\"; | |
643 | |
644 if (n != -1) | |
645 { | |
646 i += 3; | |
647 temp[0] = n; | |
648 } | |
649 | |
650 c = 0; | |
651 goto add_string; | |
652 } | |
653 | |
654 case 't': | |
655 case 'd': | |
656 // Make the current time/date into a string. | |
657 { | |
658 time_t now = time (0); | |
659 | |
660 temp = ctime (&now); | |
661 | |
662 if (c == 't') | |
663 { | |
664 temp = temp.substr (11); | |
665 temp.resize (8); | |
666 } | |
667 else | |
668 temp.resize (10); | |
669 | |
670 goto add_string; | |
671 } | |
672 | |
673 case 'n': | |
674 { | |
675 temp = newline_chars (); | |
676 | |
677 goto add_string; | |
678 } | |
679 | |
680 case 's': | |
681 { | |
682 temp = octave_env::get_program_name (); | |
683 temp = octave_env::base_pathname (temp); | |
684 | |
685 goto add_string; | |
686 } | |
687 | |
688 case 'w': | |
689 case 'W': | |
690 { | |
691 temp = octave_env::getcwd (); | |
692 | |
693 if (c == 'W') | |
694 { | |
695 size_t pos = temp.rfind ('/'); | |
696 | |
697 if (pos != NPOS && pos != 0) | |
698 temp = temp.substr (pos + 1); | |
699 } | |
700 else | |
701 temp = octave_env::polite_directory_format (temp); | |
702 | |
703 goto add_string; | |
704 } | |
705 | |
706 case 'u': | |
707 { | |
708 temp = octave_env::get_user_name (); | |
709 | |
710 goto add_string; | |
711 } | |
712 | |
713 case 'H': | |
714 { | |
715 temp = octave_env::get_host_name (); | |
716 | |
717 goto add_string; | |
718 } | |
719 | |
720 case 'h': | |
721 { | |
722 temp = octave_env::get_host_name (); | |
723 | |
724 size_t pos = temp.find ('.'); | |
725 | |
726 if (pos != NPOS) | |
727 temp.resize (pos); | |
728 | |
729 goto add_string; | |
730 } | |
731 | |
732 case '#': | |
733 { | |
734 char number_buffer[128]; | |
735 sprintf (number_buffer, "%d", command_number); | |
736 temp = number_buffer; | |
737 | |
738 goto add_string; | |
739 } | |
740 | |
741 case '!': | |
742 { | |
743 char number_buffer[128]; | |
744 int num = command_history::current_number (); | |
745 if (num > 0) | |
746 sprintf (number_buffer, "%d", num); | |
747 else | |
748 strcpy (number_buffer, "!"); | |
749 temp = number_buffer; | |
750 | |
751 goto add_string; | |
752 } | |
753 | |
754 case '$': | |
755 { | |
756 temp = (::geteuid () == 0 ? "#" : "$"); | |
757 | |
758 goto add_string; | |
759 } | |
760 | |
761 #if defined (USE_READLINE) | |
762 case '[': | |
763 case ']': | |
764 { | |
765 temp.resize (2); | |
766 | |
767 temp[0] = '\001'; | |
768 temp[1] = ((c == '[') | |
769 ? RL_PROMPT_START_IGNORE | |
770 : RL_PROMPT_END_IGNORE); | |
771 | |
772 goto add_string; | |
773 } | |
774 #endif | |
775 | |
776 case '\\': | |
777 { | |
778 temp = "\\"; | |
779 | |
780 goto add_string; | |
781 } | |
782 | |
783 default: | |
784 { | |
785 temp = "\\ "; | |
786 temp[1] = c; | |
787 | |
788 goto add_string; | |
789 } | |
790 | |
791 add_string: | |
792 { | |
793 if (c) | |
794 i++; | |
795 | |
796 result.append (temp); | |
797 | |
798 break; | |
799 } | |
800 } | |
801 } | |
802 else | |
803 result += c; | |
804 } | |
805 | |
806 return result; | |
807 } | |
808 | |
809 // Return the octal number parsed from STRING, or -1 to indicate that | |
810 // the string contained a bad number. | |
811 | |
812 int | |
813 command_editor::read_octal (const string& s) | |
814 { | |
815 int result = 0; | |
816 int digits = 0; | |
817 | |
818 size_t i = 0; | |
819 size_t slen = s.length (); | |
820 | |
821 while (i < slen && s[i] >= '0' && s[i] < '8') | |
822 { | |
823 digits++; | |
824 result = (result * 8) + s[i] - '0'; | |
825 i++; | |
826 } | |
827 | |
828 if (! digits || result > 0777 || i < slen) | |
829 result = -1; | |
830 | |
831 return result; | |
832 } | |
833 | |
834 void | |
835 command_editor::error (int err_num) | |
836 { | |
837 (*current_liboctave_error_handler) ("%s", strerror (err_num)); | |
838 } | |
839 | |
840 void | |
841 command_editor::error (const string& s) | |
842 { | |
843 (*current_liboctave_error_handler) ("%s", s.c_str ()); | |
844 } | |
845 | |
846 /* | |
847 ;;; Local Variables: *** | |
848 ;;; mode: C++ *** | |
849 ;;; End: *** | |
850 */ |