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