1 /*
2 Copyright (C) 2005-2017,2018 John E. Davis
3
4 This file is part of the S-Lang Library.
5
6 The S-Lang Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 The S-Lang Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 USA.
20 */
21
22 /* The code in this file was adapted from jdl. The GNU readline support
23 * was provided by Mike Noble.
24 */
25
26 #include "config.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #ifdef __WIN32__
31 # include <windows.h>
32 #endif
33
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 #include <string.h>
38 #include <slang.h>
39 #include "slsh.h"
40
41 #include <signal.h>
42 #include <errno.h>
43
44 #ifndef USE_GNU_READLINE
45 # define USE_GNU_READLINE 0
46 #endif
47
48 #define USE_SLANG_READLINE (!USE_GNU_READLINE)
49
50 #ifdef REAL_UNIX_SYSTEM
51 # define SYSTEM_SUPPORTS_SIGNALS 1
52 #else
53 # define SYSTEM_SUPPORTS_SIGNALS 0
54 #endif
55
56 #if USE_GNU_READLINE
57 # include <readline/readline.h>
58 # include <readline/history.h>
59 #endif
60
61 static int Use_Readline;
62 static int Slsh_Quit = 0;
63 static SLang_Load_Type *Readline_Load_Object;
64
65 typedef struct
66 {
67 #if USE_SLANG_READLINE
68 SLrline_Type *rli;
69 #endif
70 int output_newline;
71 }
72 Slsh_Readline_Type;
73
74 static Slsh_Readline_Type *Default_Readline_Info;
75
76 static void init_tty (void);
77 static void reset_tty (void);
78
79 #if USE_GNU_READLINE
gnu_rl_sigint_handler(int sig)80 static void gnu_rl_sigint_handler (int sig)
81 {
82 (void) sig;
83 rl_delete_text (0, rl_end);
84 rl_point = rl_end = 0;
85 fprintf (stdout, "\n");
86 rl_forced_update_display ();
87 }
88
89 static void (*last_sig_sigint) (int);
init_tty(void)90 static void init_tty (void)
91 {
92 last_sig_sigint = SLsignal (SIGINT, gnu_rl_sigint_handler);
93 }
94
reset_tty(void)95 static void reset_tty (void)
96 {
97 SLsignal (SIGINT, last_sig_sigint);
98 }
99
100 #else /* ifdef USE_GNU_READLINE */
101
102 static Slsh_Readline_Type *Active_Rline_Info;
103
104 # if SYSTEM_SUPPORTS_SIGNALS
105 static void (*last_sig_sigtstp) (int);
106
107 # define USE_SIGTSTP_INTERRUPT 1
108 # if USE_SIGTSTP_INTERRUPT
109 static int Want_Suspension = 0;
110 # endif
111
suspend_slsh(void)112 static void suspend_slsh (void)
113 {
114 reset_tty ();
115 (void) SLang_run_hooks ("slsh_readline_suspend_before_hook", 0);
116 kill (0, SIGSTOP);
117 (void) SLang_run_hooks ("slsh_readline_suspend_after_hook", 0);
118 init_tty ();
119 if (Active_Rline_Info != NULL)
120 {
121 SLsig_block_signals ();
122 SLrline_set_display_width (Active_Rline_Info->rli, SLtt_Screen_Cols);
123 SLrline_redraw (Active_Rline_Info->rli);
124 SLsig_unblock_signals ();
125 }
126 }
127
sigtstp_interrupt_hook(VOID_STAR unused)128 static int sigtstp_interrupt_hook (VOID_STAR unused)
129 {
130 (void) unused;
131 if (Want_Suspension)
132 {
133 Want_Suspension = 0;
134 suspend_slsh ();
135 }
136 return 0;
137 }
138
sig_sigtstp(int sig)139 static void sig_sigtstp (int sig)
140 {
141 sig = errno;
142 # if USE_SIGTSTP_INTERRUPT
143 Want_Suspension = 1;
144 # else
145 suspend_slsh ();
146 # endif
147 (void) SLsignal (sig, sig_sigtstp);
148 errno = sig;
149 }
150
init_sigtstp(void)151 static void init_sigtstp (void)
152 {
153 (void) SLang_add_interrupt_hook (sigtstp_interrupt_hook, NULL);
154 last_sig_sigtstp = SLsignal (SIGTSTP, sig_sigtstp);
155 }
deinit_sigtstp(void)156 static void deinit_sigtstp (void)
157 {
158 SLsignal (SIGTSTP, last_sig_sigtstp);
159 (void) SLang_remove_interrupt_hook (sigtstp_interrupt_hook, NULL);
160 }
161
162 # endif /* SYSTEM_SUPPORTS_SIGNALS */
163
164 # ifdef SIGWINCH
165 static int Want_Window_Size_Change = 0;
sig_winch_handler(int sig)166 static void sig_winch_handler (int sig)
167 {
168 sig = errno;
169 Want_Window_Size_Change = 1;
170 SLsignal_intr (SIGWINCH, sig_winch_handler);
171 errno = sig;
172 }
173
screen_size_changed_hook(VOID_STAR cd_unused)174 static int screen_size_changed_hook (VOID_STAR cd_unused)
175 {
176 (void) cd_unused;
177 if (Want_Window_Size_Change)
178 {
179 Want_Window_Size_Change = 0;
180 if (Active_Rline_Info != NULL)
181 {
182 SLtt_get_screen_size ();
183 SLrline_set_display_width (Active_Rline_Info->rli, SLtt_Screen_Cols);
184 }
185 }
186 return 0;
187 }
188 # endif
189
add_sigwinch_handlers(void)190 static int add_sigwinch_handlers (void)
191 {
192 # ifdef SIGWINCH
193 (void) SLang_add_interrupt_hook (screen_size_changed_hook, NULL);
194 (void) SLsignal_intr (SIGWINCH, sig_winch_handler);
195 # endif
196 return 0;
197 }
198
199 # ifdef REAL_UNIX_SYSTEM
200 /* This hook if a signal occurs while waiting for input. */
getkey_intr_hook(void)201 static int getkey_intr_hook (void)
202 {
203 return SLang_handle_interrupt ();
204 }
205 # endif
206
207 static int TTY_Inited = 0;
init_tty(void)208 static void init_tty (void)
209 {
210 int abort_char = 3;
211
212 TTY_Inited++;
213 if (TTY_Inited > 1)
214 return;
215
216 # if SYSTEM_SUPPORTS_SIGNALS
217 SLsig_block_signals ();
218 SLang_TT_Read_FD = fileno (stdin);
219 init_sigtstp ();
220 # endif
221 # ifdef REAL_UNIX_SYSTEM
222 abort_char = -1; /* determine from tty */
223 # endif
224
225 if (-1 == SLang_init_tty (abort_char, 1, 1)) /* opost was 0 */
226 {
227 # if SYSTEM_SUPPORTS_SIGNALS
228 deinit_sigtstp ();
229 SLsig_unblock_signals ();
230 # endif
231 SLang_exit_error ("Error initializing terminal.");
232 }
233
234 # ifdef REAL_UNIX_SYSTEM
235 SLang_getkey_intr_hook = getkey_intr_hook;
236 # endif
237 (void) add_sigwinch_handlers ();
238
239 SLtt_get_screen_size ();
240
241 # if SYSTEM_SUPPORTS_SIGNALS
242 SLtty_set_suspend_state (1);
243 SLsig_unblock_signals ();
244 # endif
245 }
246
reset_tty(void)247 static void reset_tty (void)
248 {
249 if (TTY_Inited == 0)
250 return;
251
252 TTY_Inited--;
253
254 if (TTY_Inited)
255 return;
256
257 # if SYSTEM_SUPPORTS_SIGNALS
258 SLsig_block_signals ();
259 deinit_sigtstp ();
260 # endif
261 SLang_reset_tty ();
262 # if SYSTEM_SUPPORTS_SIGNALS
263 SLsig_unblock_signals ();
264 # endif
265 }
266 #endif /* ifdef USE_GNU_READLINE else */
267
close_readline(void)268 static void close_readline (void)
269 {
270 if (Default_Readline_Info != NULL)
271 {
272 #if USE_SLANG_READLINE
273 SLrline_close (Default_Readline_Info->rli);
274 #endif
275 SLfree ((char *) Default_Readline_Info);
276 Default_Readline_Info = NULL;
277 }
278 }
279
close_slsh_readline(Slsh_Readline_Type * sri)280 static void close_slsh_readline (Slsh_Readline_Type *sri)
281 {
282 if (sri == NULL)
283 return;
284 #if USE_SLANG_READLINE
285 if (sri->rli != NULL)
286 SLrline_close (sri->rli);
287 #endif
288 SLfree ((char *) sri);
289 }
290
open_slsh_readline(SLFUTURE_CONST char * name,unsigned int flags)291 static Slsh_Readline_Type *open_slsh_readline (SLFUTURE_CONST char *name, unsigned int flags)
292 {
293 Slsh_Readline_Type *sri;
294
295 if (NULL == (sri = (Slsh_Readline_Type *)SLmalloc (sizeof (Slsh_Readline_Type))))
296 return NULL;
297 memset ((char *)sri, 0, sizeof (Slsh_Readline_Type));
298
299 #if USE_SLANG_READLINE
300 if (name == NULL)
301 sri->rli = SLrline_open (SLtt_Screen_Cols, flags);
302 else
303 sri->rli = SLrline_open2 (name, SLtt_Screen_Cols, flags);
304
305 if (sri->rli == NULL)
306 {
307 SLfree ((char *)sri);
308 return NULL;
309 }
310 sri->output_newline = 1;
311 #endif
312 return sri;
313 }
314
315
open_readline(SLFUTURE_CONST char * name)316 static int open_readline (SLFUTURE_CONST char *name)
317 {
318 unsigned int flags = SL_RLINE_BLINK_MATCH|SL_RLINE_USE_MULTILINE;
319 close_readline ();
320 Default_Readline_Info = open_slsh_readline (name, flags);
321 if (Default_Readline_Info == NULL)
322 return -1;
323 return 0;
324 }
325
326 #if USE_GNU_READLINE
redisplay_dummy(void)327 static void redisplay_dummy (void)
328 {
329 }
330 #endif
331
read_with_no_readline(char * prompt,int noecho)332 static char *read_with_no_readline (char *prompt, int noecho)
333 {
334 char *line;
335 #ifdef REAL_UNIX_SYSTEM
336 int stdin_is_noecho = 0;
337 #endif
338 char buf[1024];
339 char *b;
340
341 fprintf (stdout, "%s", prompt); fflush (stdout);
342 if (noecho)
343 {
344 #ifdef REAL_UNIX_SYSTEM
345 if (isatty (fileno(stdin)))
346 {
347 (void) SLsystem ("stty -echo"); /* yuk */
348 stdin_is_noecho = 1;
349 }
350 #endif
351 }
352
353 line = buf;
354 while (NULL == fgets (buf, sizeof (buf), stdin))
355 {
356 #ifdef EINTR
357 if (errno == EINTR)
358 {
359 if (-1 == SLang_handle_interrupt ())
360 {
361 line = NULL;
362 break;
363 }
364 continue;
365 }
366 #endif
367 line = NULL;
368 break;
369 }
370 #ifdef REAL_UNIX_SYSTEM
371 if (stdin_is_noecho)
372 (void) SLsystem ("stty echo");
373 #endif
374 if (line == NULL)
375 return NULL;
376
377 /* Remove the final newline */
378 b = line;
379 while (*b && (*b != '\n'))
380 b++;
381 *b = 0;
382
383 return SLmake_string (line);
384 }
385
read_input_line(Slsh_Readline_Type * sri,char * prompt,int noecho)386 static char *read_input_line (Slsh_Readline_Type *sri, char *prompt, int noecho)
387 {
388 char *line;
389
390 if (Use_Readline == 0)
391 return read_with_no_readline (prompt, noecho);
392
393 #if SYSTEM_SUPPORTS_SIGNALS
394 init_tty ();
395 #endif
396 #if USE_GNU_READLINE
397 (void) sri;
398 if (noecho == 0)
399 rl_redisplay_function = rl_redisplay;
400 else
401 {
402 /* FIXME: What is the proper way to implement this in GNU readline? */
403 (void) fputs (prompt, stdout); (void) fflush (stdout);
404 rl_redisplay_function = redisplay_dummy;
405 }
406 line = readline (prompt);
407 rl_redisplay_function = rl_redisplay;
408 #else
409 SLtt_get_screen_size ();
410 SLrline_set_display_width (sri->rli, SLtt_Screen_Cols);
411 (void) add_sigwinch_handlers ();
412 Active_Rline_Info = sri;
413 (void) SLrline_set_echo (sri->rli, (noecho == 0));
414 line = SLrline_read_line (sri->rli, prompt, NULL);
415 Active_Rline_Info = NULL;
416 #endif
417 #if SYSTEM_SUPPORTS_SIGNALS
418 reset_tty ();
419 #endif
420 if (sri->output_newline)
421 fputs ("\r\n", stdout);
422 fflush (stdout);
423 return line;
424 }
425
save_input_line(Slsh_Readline_Type * rline,char * line)426 static int save_input_line (Slsh_Readline_Type *rline, char *line)
427 {
428 char *p;
429
430 if (Use_Readline == 0)
431 return 0;
432
433 if (line == NULL)
434 return 0;
435
436 p = line;
437 while ((*p == ' ') || (*p == '\t') || (*p == '\n'))
438 p++;
439 if (*p == 0)
440 return 0;
441
442 #if USE_GNU_READLINE
443 (void) rline;
444 add_history(line);
445 return 0;
446 #else
447 return SLrline_save_line (rline->rli);
448 #endif
449 }
450
451 static SLang_Name_Type *Prompt_Hook = NULL;
set_prompt_hook(void)452 static void set_prompt_hook (void)
453 {
454 SLang_Name_Type *h;
455
456 if (SLang_peek_at_stack () == SLANG_NULL_TYPE)
457 {
458 SLang_pop_null ();
459 h = NULL;
460 }
461 else if (NULL == (h = SLang_pop_function ()))
462 return;
463
464 if (Prompt_Hook != NULL)
465 SLang_free_function (Prompt_Hook);
466
467 Prompt_Hook = h;
468 }
469
get_prompt_hook(void)470 static void get_prompt_hook (void)
471 {
472 if (Prompt_Hook == NULL)
473 (void) SLang_push_null ();
474 else
475 (void) SLang_push_function (Prompt_Hook);
476 }
477
478 /* Returns a malloced value */
get_input_line(SLang_Load_Type * x)479 static char *get_input_line (SLang_Load_Type *x)
480 {
481 char *line;
482 int parse_level;
483 int free_prompt = 0;
484 char *prompt;
485
486 parse_level = x->parse_level;
487 if (Prompt_Hook != NULL)
488 {
489 if ((-1 == SLang_start_arg_list ())
490 || (-1 == SLang_push_int (parse_level))
491 || (-1 == SLang_end_arg_list ())
492 || (-1 == SLexecute_function (Prompt_Hook))
493 || (-1 == SLang_pop_slstring (&prompt)))
494 {
495 SLang_verror (SL_RunTime_Error, "Disabling prompt hook");
496 SLang_free_function (Prompt_Hook);
497 Prompt_Hook = NULL;
498 return NULL;
499 }
500 free_prompt = 1;
501 }
502 else if (parse_level == 0)
503 prompt = (char *) "slsh> ";
504 else
505 prompt = (char *) " ";
506
507 if (parse_level == 0)
508 {
509 if (-1 == SLang_run_hooks ("slsh_interactive_before_hook", 0))
510 {
511 if (free_prompt)
512 SLang_free_slstring (prompt);
513 return NULL;
514 }
515 }
516
517 line = read_input_line (Default_Readline_Info, prompt, 0);
518
519 if (free_prompt)
520 SLang_free_slstring (prompt);
521
522 if ((line == NULL)
523 && (parse_level == 0)
524 && (SLang_get_error() == 0))
525 {
526 Slsh_Quit = 1;
527 return NULL;
528 }
529
530 if (line == NULL)
531 {
532 return NULL;
533 }
534
535 /* This hook is used mainly for logging input */
536 (void) SLang_run_hooks ("slsh_interactive_after_hook", 1, line);
537
538 (void) save_input_line (Default_Readline_Info, line);
539
540 return line;
541 }
542
read_using_readline(SLang_Load_Type * x)543 static char *read_using_readline (SLang_Load_Type *x)
544 {
545 char *s;
546 static char *last_s;
547
548 if (last_s != NULL)
549 {
550 SLfree (last_s);
551 last_s = NULL;
552 }
553
554 if (SLang_get_error ())
555 return NULL;
556
557 SLKeyBoard_Quit = 0;
558
559 s = get_input_line (x);
560
561 if (s == NULL)
562 return NULL;
563
564 if ((x->parse_level == 0)
565 && (1 == SLang_run_hooks ("slsh_interactive_massage_hook", 1, s)))
566 {
567 SLfree (s);
568 if (-1 == SLpop_string (&s))
569 return NULL;
570 }
571
572 if (SLang_get_error ())
573 {
574 SLfree (s);
575 return NULL;
576 }
577
578 last_s = s;
579 return s;
580 }
581
enable_keyboard_interrupt(void)582 static void enable_keyboard_interrupt (void)
583 {
584 static int is_enabled = 0;
585
586 if (is_enabled == 0)
587 {
588 (void) SLang_set_abort_signal (NULL);
589 is_enabled = 1;
590 }
591 }
592
close_interactive(void)593 static void close_interactive (void)
594 {
595 close_readline ();
596
597 if (Readline_Load_Object == NULL)
598 return;
599
600 SLdeallocate_load_type (Readline_Load_Object);
601 Readline_Load_Object = NULL;
602 #if !SYSTEM_SUPPORTS_SIGNALS
603 reset_tty ();
604 fputs ("\r\n", stdout);
605 fflush (stdout);
606 #endif
607 }
608
open_interactive(void)609 static int open_interactive (void)
610 {
611 if (Use_Readline
612 && (-1 == open_readline ("slsh")))
613 return -1;
614
615 if (NULL == (Readline_Load_Object = SLallocate_load_type ("<stdin>")))
616 {
617 if (Use_Readline)
618 close_readline ();
619 return -1;
620 }
621
622 Readline_Load_Object->read = read_using_readline;
623 Readline_Load_Object->auto_declare_globals = 1;
624
625 #if !SYSTEM_SUPPORTS_SIGNALS
626 /* If the system does not support asynchronouse signals, then it may install
627 * a SLang_Interrupt hook that checks for ^C, etc. For that reason, the
628 * tty must be initialized the whole time.
629 */
630 init_tty ();
631 #endif
632 enable_keyboard_interrupt ();
633
634 return 0;
635 }
636
init_readline(char * appname)637 static int init_readline (char *appname)
638 {
639 static int inited = 0;
640
641 if (inited)
642 return 0;
643
644 #if USE_SLANG_READLINE
645 if (Use_Readline == 0)
646 {
647 inited = 1;
648 return 0;
649 }
650 if (-1 == SLrline_init (appname, NULL, NULL))
651 return -1;
652 #endif
653
654 inited = 1;
655 return 0;
656 }
657
slsh_use_readline(char * app_name,int use_readline,int is_interactive)658 int slsh_use_readline (char *app_name, int use_readline, int is_interactive)
659 {
660 Use_Readline = use_readline;
661
662 #if USE_SLANG_READLINE
663 if (is_interactive)
664 {
665 if (-1 == init_readline (app_name))
666 return -1;
667 }
668 #endif
669
670 return 0;
671 }
672
slsh_interactive(void)673 int slsh_interactive (void)
674 {
675 Slsh_Quit = 0;
676
677 (void) SLang_add_cleanup_function (close_interactive);
678
679 if (-1 == open_interactive ())
680 return -1;
681
682 (void) SLang_run_hooks ("slsh_interactive_hook", 0);
683
684 while (Slsh_Quit == 0)
685 {
686 if (SLang_get_error ())
687 {
688 SLang_restart(1);
689 /* SLang_set_error (0); */
690 }
691
692 SLKeyBoard_Quit = 0;
693 SLang_load_object (Readline_Load_Object);
694 }
695 close_interactive ();
696
697 return 0;
698 }
699
700 static Slsh_Readline_Type *Intrinsic_Rline_Info;
701
702 #if USE_SLANG_READLINE
close_intrinsic_readline(void)703 static void close_intrinsic_readline (void)
704 {
705 if (Intrinsic_Rline_Info != NULL)
706 {
707 SLrline_close (Intrinsic_Rline_Info->rli);
708 SLfree ((char *) Intrinsic_Rline_Info);
709 Intrinsic_Rline_Info = NULL;
710 }
711 }
712 #endif
713
readline_intrinsic_internal(Slsh_Readline_Type * sri,char * prompt,int noecho)714 static int readline_intrinsic_internal (Slsh_Readline_Type *sri, char *prompt, int noecho)
715 {
716 char *line;
717
718 if (sri == NULL)
719 sri = Intrinsic_Rline_Info;
720
721 #if USE_SLANG_READLINE
722 if ((sri == NULL) && Use_Readline)
723 {
724 Intrinsic_Rline_Info = open_slsh_readline (NULL, SL_RLINE_BLINK_MATCH);
725 if (Intrinsic_Rline_Info == NULL)
726 return -1;
727 (void) SLang_add_cleanup_function (close_intrinsic_readline);
728 sri = Intrinsic_Rline_Info;
729 }
730 #endif
731 enable_keyboard_interrupt ();
732
733 line = read_input_line (sri, prompt, noecho);
734 if (noecho == 0)
735 (void) save_input_line (sri, line);
736 (void) SLang_push_malloced_string (line);
737 return 0;
738 }
739
740 static int Rline_Type_Id = 0;
741
pop_sri_type(Slsh_Readline_Type ** srip)742 static SLang_MMT_Type *pop_sri_type (Slsh_Readline_Type **srip)
743 {
744 SLang_MMT_Type *mmt;
745
746 if (NULL == (mmt = SLang_pop_mmt (Rline_Type_Id)))
747 return NULL;
748 if (NULL == (*srip = (Slsh_Readline_Type *)SLang_object_from_mmt (mmt)))
749 {
750 SLang_free_mmt (mmt);
751 return NULL;
752 }
753 return mmt;
754 }
755
readline_intrinsic(char * prompt)756 static void readline_intrinsic (char *prompt)
757 {
758 Slsh_Readline_Type *sri = NULL;
759 SLang_MMT_Type *mmt = NULL;
760
761 if (SLang_Num_Function_Args == 2)
762 {
763 if (NULL == (mmt = pop_sri_type (&sri)))
764 return;
765 }
766 (void) readline_intrinsic_internal (sri, prompt, 0);
767
768 if (mmt != NULL)
769 SLang_free_mmt (mmt);
770 }
771
readline_noecho_intrinsic(char * prompt)772 static void readline_noecho_intrinsic (char *prompt)
773 {
774 Slsh_Readline_Type *sri = NULL;
775 SLang_MMT_Type *mmt = NULL;
776
777 if (SLang_Num_Function_Args == 2)
778 {
779 if (NULL == (mmt = pop_sri_type (&sri)))
780 return;
781 }
782 (void) readline_intrinsic_internal (sri, prompt, 1);
783 if (mmt != NULL)
784 SLang_free_mmt (mmt);
785 }
786
new_slrline_intrinsic(char * name)787 static void new_slrline_intrinsic (char *name)
788 {
789 Slsh_Readline_Type *sri;
790 SLang_MMT_Type *mmt;
791
792 if (NULL == (sri = open_slsh_readline (name, SL_RLINE_BLINK_MATCH)))
793 return;
794
795 if (NULL == (mmt = SLang_create_mmt (Rline_Type_Id, (VOID_STAR) sri)))
796 {
797 close_slsh_readline (sri);
798 return;
799 }
800
801 if (-1 == SLang_push_mmt (mmt))
802 SLang_free_mmt (mmt);
803 }
804
init_readline_intrinsic(char * appname)805 static void init_readline_intrinsic (char *appname)
806 {
807 (void) init_readline (appname);
808 }
809
810 /* The values returned by this function are meaningful only after readline
811 * has been used once.
812 */
get_screen_size(void)813 static void get_screen_size (void)
814 {
815 int rows, cols;
816
817 #if USE_SLANG_READLINE
818 rows = SLtt_Screen_Rows;
819 cols = SLtt_Screen_Cols;
820 #else
821 rl_get_screen_size (&rows, &cols);
822 #endif
823
824 (void) SLang_push_int (rows);
825 (void) SLang_push_int (cols);
826 }
827
828 #if USE_SLANG_READLINE
829 typedef struct
830 {
831 SLang_MMT_Type *mmt;
832 Slsh_Readline_Type *sri;
833 SLang_Name_Type *update_hook;
834 SLang_Any_Type *cd;
835 SLang_Name_Type *clear_cb;
836 SLang_Name_Type *preread_cb;
837 SLang_Name_Type *postread_cb;
838 SLang_Name_Type *width_cb;
839 }
840 Rline_CB_Type;
841
call_simple_update_cb(SLang_Name_Type * f,Rline_CB_Type * cb,int * opt)842 static int call_simple_update_cb (SLang_Name_Type *f, Rline_CB_Type *cb, int *opt)
843 {
844 if (f == NULL)
845 return 0;
846
847 if (-1 == SLang_start_arg_list ())
848 return -1;
849 if ((-1 == SLang_push_mmt (cb->mmt))
850 || ((opt != NULL) && (-1 == SLang_push_int (*opt)))
851 || ((cb->cd != NULL) && (-1 == SLang_push_anytype (cb->cd))))
852 {
853 (void) SLang_end_arg_list ();
854 return -1;
855 }
856 return SLexecute_function (f);
857 }
858
rline_update_clear_cb(SLrline_Type * rli,VOID_STAR cd)859 static void rline_update_clear_cb (SLrline_Type *rli, VOID_STAR cd)
860 {
861 Rline_CB_Type *cb = (Rline_CB_Type *)cd;
862 (void) rli;
863 (void) call_simple_update_cb (cb->clear_cb, cb, NULL);
864 }
865
rline_update_preread_cb(SLrline_Type * rli,VOID_STAR cd)866 static void rline_update_preread_cb (SLrline_Type *rli, VOID_STAR cd)
867 {
868 Rline_CB_Type *cb = (Rline_CB_Type *)cd;
869 (void) rli;
870 (void) call_simple_update_cb (cb->preread_cb, cb, NULL);
871 }
872
rline_update_postread_cb(SLrline_Type * rli,VOID_STAR cd)873 static void rline_update_postread_cb (SLrline_Type *rli, VOID_STAR cd)
874 {
875 Rline_CB_Type *cb = (Rline_CB_Type *)cd;
876 (void) rli;
877 (void) call_simple_update_cb (cb->postread_cb, cb, NULL);
878 }
879
rline_update_width_cb(SLrline_Type * rli,int w,VOID_STAR cd)880 static void rline_update_width_cb (SLrline_Type *rli, int w, VOID_STAR cd)
881 {
882 Rline_CB_Type *cb = (Rline_CB_Type *)cd;
883 (void) rli;
884 (void) call_simple_update_cb (cb->width_cb, cb, &w);
885 }
886
free_cb_info(Rline_CB_Type * cb)887 static void free_cb_info (Rline_CB_Type *cb)
888 {
889 if (cb == NULL)
890 return;
891 if (cb->mmt != NULL) SLang_free_mmt (cb->mmt);
892 if (cb->update_hook != NULL) SLang_free_function (cb->update_hook);
893 if (cb->clear_cb != NULL) SLang_free_function (cb->clear_cb);
894 if (cb->preread_cb != NULL) SLang_free_function (cb->preread_cb);
895 if (cb->postread_cb != NULL) SLang_free_function (cb->postread_cb);
896 if (cb->width_cb != NULL) SLang_free_function (cb->width_cb);
897 if (cb->cd != NULL) SLang_free_anytype (cb->cd);
898 SLfree ((char *)cb);
899 }
900
rline_call_update_hook(SLrline_Type * rli,SLFUTURE_CONST char * prompt,SLFUTURE_CONST char * buf,unsigned int len,unsigned int point,VOID_STAR cd)901 static void rline_call_update_hook (SLrline_Type *rli,
902 SLFUTURE_CONST char *prompt,
903 SLFUTURE_CONST char *buf,
904 unsigned int len,
905 unsigned int point, VOID_STAR cd)
906 {
907 Rline_CB_Type *cb;
908
909 (void) rli; (void) len;
910 cb = (Rline_CB_Type *)cd;
911
912 if (-1 == SLang_start_arg_list ())
913 return;
914
915 if ((-1 == SLang_push_mmt (cb->mmt))
916 || (-1 == SLang_push_string (prompt))
917 || (-1 == SLang_push_string (buf))
918 || (-1 == SLang_push_int ((int) point))
919 || ((cb->cd != NULL) && (-1 == SLang_push_anytype (cb->cd))))
920 {
921 (void) SLang_end_arg_list ();
922 return;
923 }
924
925 (void) SLexecute_function (cb->update_hook);
926 }
927
free_rli_update_data_cb(SLrline_Type * rli,VOID_STAR cd)928 static void free_rli_update_data_cb (SLrline_Type *rli, VOID_STAR cd)
929 {
930 (void) rli;
931 if (cd != NULL)
932 free_cb_info ((Rline_CB_Type *)cd);
933 }
934
set_rline_update_hook(void)935 static void set_rline_update_hook (void)
936 {
937 Rline_CB_Type *cb;
938 SLrline_Type *rli;
939
940 if (NULL == (cb = (Rline_CB_Type *)SLmalloc(sizeof(Rline_CB_Type))))
941 return;
942 memset ((char *)cb, 0, sizeof(Rline_CB_Type));
943
944 switch (SLang_Num_Function_Args)
945 {
946 default:
947 SLang_verror (SL_Usage_Error, "Usage: rline_set_update_hook (rli [,&hook [,clientdata]]);");
948 return;
949
950 case 3:
951 if (-1 == SLang_pop_anytype (&cb->cd))
952 return;
953 /* drop */
954 case 2:
955 if (NULL == (cb->update_hook = SLang_pop_function ()))
956 goto free_and_return;
957 /* drop */
958 case 1:
959 if (NULL == (cb->mmt = pop_sri_type (&cb->sri)))
960 goto free_and_return;
961 }
962
963 cb->sri->output_newline = 0;
964 rli = cb->sri->rli;
965
966 SLrline_set_update_clear_cb (rli, rline_update_clear_cb);
967 SLrline_set_update_preread_cb (rli, rline_update_preread_cb);
968 SLrline_set_update_postread_cb (rli, rline_update_postread_cb);
969 SLrline_set_update_width_cb (rli, rline_update_width_cb);
970
971 if (0 == SLrline_set_update_hook (rli, rline_call_update_hook, (VOID_STAR)cb))
972 {
973 SLrline_set_free_update_cb (rli, free_rli_update_data_cb);
974 return;
975 }
976
977 /* drop */
978 free_and_return:
979 free_cb_info (cb);
980 }
981
982 /* On stack: (rli, callback) */
pop_set_rline_cb_args(SLang_MMT_Type ** mmtp,Rline_CB_Type ** cbp,SLang_Name_Type ** ntp)983 static int pop_set_rline_cb_args (SLang_MMT_Type **mmtp,
984 Rline_CB_Type **cbp, SLang_Name_Type **ntp)
985 {
986 SLang_Name_Type *nt;
987 Slsh_Readline_Type *sri;
988 SLang_MMT_Type *mmt;
989
990 if (SLang_peek_at_stack () == SLANG_NULL_TYPE)
991 nt = NULL;
992 else if (NULL == (nt = SLang_pop_function ()))
993 return -1;
994
995 if (NULL == (mmt = pop_sri_type (&sri)))
996 {
997 if (nt != NULL)
998 SLang_free_function (nt);
999 return -1;
1000 }
1001 if (-1 == SLrline_get_update_client_data (sri->rli, (VOID_STAR *)cbp))
1002 goto return_error;
1003
1004 if (*cbp == NULL)
1005 {
1006 SLang_verror (SL_Application_Error, "\
1007 Attempt to define an rline update callback without first creating a readline_update_hook");
1008 goto return_error;
1009 }
1010
1011 *mmtp = mmt;
1012 *ntp = nt;
1013 return 0;
1014
1015 return_error:
1016 SLang_free_mmt (mmt);
1017 if (nt == NULL) SLang_free_function (nt);
1018 return -1;
1019 }
1020
1021 #define SET_RLINE_UPDATE_XXX_CB(fun, _xxx) \
1022 static void fun (void) \
1023 { \
1024 SLang_MMT_Type *mmt; \
1025 SLang_Name_Type *nt; \
1026 Rline_CB_Type *cb; \
1027 \
1028 if (-1 == pop_set_rline_cb_args (&mmt, &cb, &nt)) \
1029 return; \
1030 SLang_free_function (cb->_xxx); \
1031 cb->_xxx = nt; \
1032 SLang_free_mmt (mmt); \
1033 }
1034
1035 SET_RLINE_UPDATE_XXX_CB (set_rline_update_clear_cb, clear_cb)
1036 SET_RLINE_UPDATE_XXX_CB (set_rline_update_preread_cb, preread_cb)
1037 SET_RLINE_UPDATE_XXX_CB (set_rline_update_postread_cb, postread_cb)
1038 SET_RLINE_UPDATE_XXX_CB (set_rline_update_width_cb, width_cb)
1039 #endif /* USE_SLANG_READLINE */
1040
1041 static SLang_Intrin_Fun_Type Intrinsics [] =
1042 {
1043 MAKE_INTRINSIC_0("__rline_init_tty", init_tty, VOID_TYPE),
1044 MAKE_INTRINSIC_0("__rline_reset_tty", reset_tty, VOID_TYPE),
1045 MAKE_INTRINSIC_S("slsh_readline_init", init_readline_intrinsic, VOID_TYPE),
1046 MAKE_INTRINSIC_S("slsh_readline_new", new_slrline_intrinsic, VOID_TYPE),
1047 MAKE_INTRINSIC_S("slsh_readline", readline_intrinsic, VOID_TYPE),
1048 MAKE_INTRINSIC_S("slsh_readline_noecho", readline_noecho_intrinsic, VOID_TYPE),
1049 MAKE_INTRINSIC_0("slsh_set_prompt_hook", set_prompt_hook, VOID_TYPE),
1050 MAKE_INTRINSIC_0("slsh_get_prompt_hook", get_prompt_hook, VOID_TYPE),
1051 MAKE_INTRINSIC_0("slsh_get_screen_size", get_screen_size, VOID_TYPE),
1052 #if USE_SLANG_READLINE
1053 MAKE_INTRINSIC_0("slsh_set_readline_update_hook", set_rline_update_hook, VOID_TYPE),
1054 MAKE_INTRINSIC_0("slsh_set_update_clear_cb", set_rline_update_clear_cb, VOID_TYPE),
1055 MAKE_INTRINSIC_0("slsh_set_update_preread_cb", set_rline_update_preread_cb, VOID_TYPE),
1056 MAKE_INTRINSIC_0("slsh_set_update_postread_cb", set_rline_update_postread_cb, VOID_TYPE),
1057 MAKE_INTRINSIC_0("slsh_set_update_width_cb", set_rline_update_width_cb, VOID_TYPE),
1058 #endif
1059 SLANG_END_INTRIN_FUN_TABLE
1060 };
1061
destroy_rline(SLtype type,VOID_STAR f)1062 static void destroy_rline (SLtype type, VOID_STAR f)
1063 {
1064 (void) type;
1065 close_slsh_readline ((Slsh_Readline_Type *) f);
1066 }
1067
register_rline_type(void)1068 static int register_rline_type (void)
1069 {
1070 SLang_Class_Type *cl;
1071
1072 if (Rline_Type_Id != 0)
1073 return 0;
1074
1075 if (NULL == (cl = SLclass_allocate_class ("RLine_Type")))
1076 return -1;
1077
1078 if (-1 == SLclass_set_destroy_function (cl, destroy_rline))
1079 return -1;
1080
1081 /* By registering as SLANG_VOID_TYPE, slang will dynamically allocate a
1082 * type.
1083 */
1084 if (-1 == SLclass_register_class (cl, SLANG_VOID_TYPE, sizeof (Slsh_Readline_Type*), SLANG_CLASS_TYPE_MMT))
1085 return -1;
1086
1087 Rline_Type_Id = SLclass_get_class_id (cl);
1088
1089 return 0;
1090 }
1091
slsh_init_readline_intrinsics()1092 int slsh_init_readline_intrinsics ()
1093 {
1094 if (-1 == register_rline_type ())
1095 return -1;
1096
1097 if (-1 == SLadd_intrin_fun_table (Intrinsics, NULL))
1098 return -1;
1099
1100 return 0;
1101 }
1102
1103