Main Page | Class List | Directories | File List | Class Members | File Members

readline.c

Go to the documentation of this file.
00001 /*
00002 Copyright (C) 2005, 2006 John E. Davis
00003 
00004 This file is part of the S-Lang Library.
00005 
00006 The S-Lang Library is free software; you can redistribute it and/or
00007 modify it under the terms of the GNU General Public License as
00008 published by the Free Software Foundation; either version 2 of the
00009 License, or (at your option) any later version.
00010 
00011 The S-Lang Library is distributed in the hope that it will be useful,
00012 but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014 General Public License for more details.
00015 
00016 You should have received a copy of the GNU General Public License
00017 along with this library; if not, write to the Free Software
00018 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
00019 USA.
00020 */
00021 
00022 /* The code in this file was adapted from jdl.  The GNU readline support
00023  * was provided by Mike Noble.
00024  */
00025 
00026 #include "config.h"
00027 
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #ifdef __WIN32__
00031 # include <windows.h>
00032 #endif
00033 
00034 #ifdef HAVE_UNISTD_H
00035 # include <unistd.h>
00036 #endif
00037 #include <string.h>
00038 #include <slang.h>
00039 #include "slsh.h"
00040 
00041 #include <signal.h>
00042 #include <errno.h>
00043 
00044 #ifndef USE_GNU_READLINE
00045 # define USE_GNU_READLINE 0
00046 #endif
00047 
00048 #define USE_SLANG_READLINE (!USE_GNU_READLINE)
00049 
00050 #ifdef REAL_UNIX_SYSTEM
00051 # define SYSTEM_SUPPORTS_SIGNALS 1
00052 #else
00053 # define SYSTEM_SUPPORTS_SIGNALS 0
00054 #endif
00055 
00056 #if USE_GNU_READLINE
00057 # include <readline/readline.h>
00058 # include <readline/history.h>
00059 #endif
00060 
00061 
00062 static int Use_Readline;
00063 static char *Prompt;
00064 static int Slsh_Quit = 0;
00065 static SLang_Load_Type *Readline_Load_Object;
00066 static SLang_RLine_Info_Type *Rline_Info;
00067 
00068 static int open_readline (void);
00069 static void close_readline (void);
00070 
00071 static void init_tty (void);
00072 static void reset_tty (void);
00073 
00074 #if USE_GNU_READLINE
00075 static void gnu_rl_sigint_handler (int sig)
00076 {
00077    (void) sig;
00078    rl_delete_text (0, rl_end);
00079    rl_point = rl_end = 0;
00080    fprintf (stdout, "\n");
00081    rl_forced_update_display ();
00082 }
00083 
00084 static void (*last_sig_sigint) (int);
00085 static void init_tty (void)
00086 {
00087    last_sig_sigint = SLsignal (SIGINT, gnu_rl_sigint_handler);
00088 }
00089 
00090 static void reset_tty (void)
00091 {
00092    SLsignal (SIGINT, last_sig_sigint);
00093 }
00094 
00095 #else
00096 
00097 static SLang_RLine_Info_Type *Active_Rline_Info;
00098 
00099 # if SYSTEM_SUPPORTS_SIGNALS
00100 static void (*last_sig_sigtstp) (int);
00101 
00102 static void sig_sigtstp (int sig)
00103 {
00104    (void) sig;
00105    SLsig_block_signals ();
00106    reset_tty ();
00107    kill(getpid(),SIGSTOP);
00108    init_tty ();
00109    if (Active_Rline_Info != NULL) 
00110      SLrline_redraw (Active_Rline_Info);
00111    SLsig_unblock_signals ();
00112 }
00113 # endif
00114 
00115 # ifdef REAL_UNIX_SYSTEM
00116 /* This hook if a signal occurs while waiting for input. */
00117 static int getkey_intr_hook (void)
00118 {
00119    return SLang_handle_interrupt ();
00120 }
00121 # endif
00122 
00123 static int TTY_Inited = 0;
00124 static void init_tty (void)
00125 {
00126    int abort_char = 3;
00127    if (TTY_Inited)
00128      return;
00129    TTY_Inited++;
00130 # if SYSTEM_SUPPORTS_SIGNALS
00131    SLsig_block_signals ();
00132    SLang_TT_Read_FD = fileno (stdin);
00133    last_sig_sigtstp = SLsignal (SIGTSTP, sig_sigtstp);
00134 # endif
00135 # ifdef REAL_UNIX_SYSTEM
00136    abort_char = -1;                    /* determine from tty */
00137 # endif
00138 
00139    if (-1 == SLang_init_tty (abort_char, 1, 0))
00140      {
00141 # if SYSTEM_SUPPORTS_SIGNALS
00142         SLsignal (SIGTSTP, last_sig_sigtstp);
00143         SLsig_unblock_signals ();
00144 # endif
00145         SLang_exit_error ("Error initializing terminal.");
00146      }
00147 
00148 # ifdef REAL_UNIX_SYSTEM
00149    SLang_getkey_intr_hook = getkey_intr_hook;
00150 # endif
00151 
00152 # if SYSTEM_SUPPORTS_SIGNALS
00153    SLtty_set_suspend_state (1);
00154    SLsig_unblock_signals ();
00155 # endif
00156 }
00157 
00158 static void reset_tty (void)
00159 {
00160    if (TTY_Inited == 0)
00161      return;
00162    TTY_Inited = 0;
00163 # if SYSTEM_SUPPORTS_SIGNALS
00164    SLsig_block_signals ();
00165    SLsignal (SIGTSTP, last_sig_sigtstp);
00166 # endif
00167    SLang_reset_tty ();
00168 # if SYSTEM_SUPPORTS_SIGNALS
00169    SLsig_unblock_signals ();
00170 # endif
00171    /* Add a \r here to work around what I believe is a solaris kernel bug.
00172     * The terminal is being reset by SLang_reset_tty which uses TCSADRAIN
00173     * option.  However, that is not supposed to affect output after that
00174     * call is made (like the output below), but it does.
00175     */
00176    fputs ("\r\n", stdout);
00177    fflush (stdout);
00178 }
00179 #endif
00180 
00181 static void close_readline ()
00182 {
00183 #if USE_SLANG_READLINE
00184    if (Rline_Info != NULL)
00185      {
00186         SLrline_close (Rline_Info);
00187         Rline_Info = NULL;
00188      }
00189 #endif
00190 }
00191 
00192 static int open_readline ()
00193 {
00194 #if USE_GNU_READLINE
00195    return 0;
00196 #else
00197    close_readline ();
00198    if (NULL == (Rline_Info = SLrline_open (SLtt_Screen_Cols, SL_RLINE_BLINK_MATCH)))
00199      return -1;
00200    return 0;
00201 #endif
00202 }
00203 
00204 #if USE_GNU_READLINE
00205 static void redisplay_dummy (void)
00206 {
00207 }
00208 #endif
00209 static char *read_input_line (SLang_RLine_Info_Type *rline, char *prompt, int noecho)
00210 {
00211    char *line;
00212 #ifdef REAL_UNIX_SYSTEM
00213    int stdin_is_noecho = 0;
00214 #endif
00215 
00216    if (Use_Readline == 0)
00217      {
00218         char buf[1024];
00219         char *b;
00220 
00221         fprintf (stdout, "%s", prompt); fflush (stdout);
00222         if (noecho)
00223           {
00224 #ifdef REAL_UNIX_SYSTEM
00225              if (isatty (fileno(stdin)))
00226                {
00227                   (void) SLsystem ("stty -echo");   /* yuk */
00228                   stdin_is_noecho = 1;
00229                }
00230 #endif
00231           }
00232         
00233         line = buf;
00234         while (1)
00235           {
00236              while (NULL == fgets (buf, sizeof (buf), stdin))
00237                {
00238 #ifdef EINTR
00239                   if (errno == EINTR)
00240                     {
00241                        if (-1 == SLang_handle_interrupt ())
00242                          {
00243                             line = NULL;
00244                             break;
00245                          }
00246                        continue;
00247                     }
00248 #endif
00249                   line = NULL;
00250                   break;
00251                }
00252              break;
00253           }
00254 #ifdef REAL_UNIX_SYSTEM
00255         if (stdin_is_noecho)
00256           (void) SLsystem ("stty echo");
00257 #endif
00258         if (line == NULL)
00259           return NULL;
00260 
00261         /* Remove the final newline */
00262         b = line;
00263         while (*b && (*b != '\n'))
00264           b++;
00265         *b = 0;
00266 
00267         return SLmake_string (line);
00268      }
00269 #if SYSTEM_SUPPORTS_SIGNALS
00270    init_tty ();
00271 #endif
00272 #if USE_GNU_READLINE
00273    (void) rline;
00274    if (noecho == 0)
00275      rl_redisplay_function = rl_redisplay;
00276    else
00277      {
00278         /* FIXME: What is the proper way to implement this in GNU readline? */
00279         (void) fputs (prompt, stdout); (void) fflush (stdout);
00280         rl_redisplay_function = redisplay_dummy;
00281      }
00282    line = readline (prompt);
00283    rl_redisplay_function = rl_redisplay;
00284 #else
00285    SLtt_get_screen_size ();
00286    SLrline_set_display_width (rline, SLtt_Screen_Cols);
00287    Active_Rline_Info = rline;
00288    (void) SLrline_set_echo (rline, (noecho == 0));
00289    line = SLrline_read_line (rline, prompt, NULL);
00290    Active_Rline_Info = NULL;
00291 #endif
00292 #if SYSTEM_SUPPORTS_SIGNALS
00293    reset_tty ();
00294 #else
00295    fputs ("\r\n", stdout);
00296    fflush (stdout);
00297 #endif
00298    return line;
00299 }
00300 
00301 
00302 static int save_input_line (SLang_RLine_Info_Type *rline, char *line)
00303 {
00304    char *p;
00305 
00306    if (line == NULL)
00307      return 0;
00308    
00309    p = line;
00310    while ((*p == ' ') || (*p == '\t') || (*p == '\n'))
00311      p++;
00312    if (*p == 0)
00313      return 0;
00314 
00315 #if USE_GNU_READLINE
00316    (void) rline;
00317    add_history(line);
00318    return 0;
00319 #else
00320    return SLrline_save_line (rline);
00321 #endif
00322 }
00323 
00324 
00325 /* Returns a malloced value */
00326 static char *get_input_line (SLang_Load_Type *x)
00327 {
00328    char *prompt;
00329    char *line;
00330    int parse_level;
00331    
00332    parse_level = x->parse_level;
00333    if (parse_level == 0) 
00334      {
00335         if (-1 == SLang_run_hooks ("slsh_interactive_before_hook", 0))
00336           return NULL;
00337 
00338         prompt = Prompt;
00339         if (prompt == NULL)
00340           prompt = "slsh> ";
00341      }
00342    else prompt = "       ";
00343 
00344    line = read_input_line (Rline_Info, prompt, 0);
00345 
00346    if ((line == NULL) 
00347        && (parse_level == 0)
00348        && (SLang_get_error() == 0))
00349      {
00350         Slsh_Quit = 1;
00351         return NULL;
00352      }
00353 
00354    if (line == NULL)
00355      return NULL;
00356 
00357    /* This hook is used mainly for logging input */
00358    (void) SLang_run_hooks ("slsh_interactive_after_hook", 1, line);
00359 
00360    (void) save_input_line (Rline_Info, line);
00361    
00362    return line;
00363 }
00364 
00365 static char *read_using_readline (SLang_Load_Type *x)
00366 {
00367    char *s;
00368    static char *last_s;
00369 
00370    if (last_s != NULL)
00371      {
00372         SLfree (last_s);
00373         last_s = NULL;
00374      }
00375 
00376    if (SLang_get_error ())
00377      return NULL;
00378 
00379    SLKeyBoard_Quit = 0;
00380 
00381    s = get_input_line (x);
00382 
00383    if (s == NULL)
00384      return NULL;
00385 
00386    if ((x->parse_level == 0)
00387        && (1 == SLang_run_hooks ("slsh_interactive_massage_hook", 1, s)))
00388      {
00389         SLfree (s);
00390         if (-1 == SLpop_string (&s))
00391           return NULL;
00392      }
00393 
00394    if (SLang_get_error ())
00395      {
00396         SLfree (s);
00397         return NULL;
00398      }
00399 
00400    last_s = s;
00401    return s;
00402 }
00403 
00404 static void enable_keyboard_interrupt (void)
00405 {
00406    static int is_enabled = 0;
00407    
00408    if (is_enabled == 0)
00409      {
00410         (void) SLang_set_abort_signal (NULL);
00411         is_enabled = 1;
00412      }
00413 }
00414 
00415 static void close_interactive (void)
00416 {
00417    close_readline ();
00418 
00419    if (Readline_Load_Object == NULL)
00420      return;
00421      
00422    SLdeallocate_load_type (Readline_Load_Object);
00423    Readline_Load_Object = NULL;
00424 #if !SYSTEM_SUPPORTS_SIGNALS
00425    reset_tty ();
00426 #endif
00427 }
00428 
00429 static int open_interactive (void)
00430 {
00431    if (Use_Readline
00432        && (-1 == open_readline ()))
00433      return -1;
00434 
00435    if (NULL == (Readline_Load_Object = SLallocate_load_type ("<stdin>")))
00436      {
00437         if (Use_Readline)
00438           close_readline ();
00439         return -1;
00440      }
00441 
00442    Readline_Load_Object->read = read_using_readline;
00443    Readline_Load_Object->auto_declare_globals = 1;
00444 
00445 #if !SYSTEM_SUPPORTS_SIGNALS
00446    /* If the system does not support asynchronouse signals, then it may install
00447     * a SLang_Interrupt hook that checks for ^C, etc.  For that reason, the
00448     * tty must be initialized the whole time.
00449     */
00450    init_tty ();
00451 #endif
00452    enable_keyboard_interrupt ();
00453 
00454    return 0;
00455 }
00456 
00457 
00458 int slsh_use_readline (int use_readline)
00459 {
00460    Use_Readline = use_readline;
00461    return 0;
00462 }
00463 
00464 int slsh_interactive (void)
00465 {
00466    Slsh_Quit = 0;
00467 
00468    (void) SLang_add_cleanup_function (close_interactive);
00469 
00470    if (-1 == open_interactive ())
00471      return -1;
00472    
00473    while (Slsh_Quit == 0)
00474      {
00475         if (SLang_get_error ())
00476           {
00477              SLang_restart(1);
00478              /* SLang_set_error (0); */
00479           }
00480 
00481         SLKeyBoard_Quit = 0;
00482         SLang_load_object (Readline_Load_Object);
00483      }
00484    close_interactive ();
00485 
00486    return 0;
00487 }
00488 
00489 
00490 static SLang_RLine_Info_Type *Intrinsic_Rline_Info;
00491 #if USE_SLANG_READLINE
00492 static void close_intrinsic_readline (void)
00493 {
00494    if (Intrinsic_Rline_Info != NULL)
00495      {
00496         SLrline_close (Intrinsic_Rline_Info);
00497         Intrinsic_Rline_Info = NULL;
00498      }
00499 }
00500 #endif
00501         
00502 static int readline_intrinsic_internal (char *prompt, int noecho)
00503 {
00504    char *line;
00505 
00506 #if USE_SLANG_READLINE
00507    if ((Intrinsic_Rline_Info == NULL) 
00508        && Use_Readline)
00509      {
00510         Intrinsic_Rline_Info = SLrline_open (SLtt_Screen_Cols, SL_RLINE_BLINK_MATCH);
00511         if (Intrinsic_Rline_Info == NULL)
00512           return -1;
00513         (void) SLang_add_cleanup_function (close_intrinsic_readline);
00514      }
00515 #endif
00516    enable_keyboard_interrupt ();
00517 
00518    line = read_input_line (Intrinsic_Rline_Info, prompt, noecho);
00519    if (noecho == 0)
00520      (void) save_input_line (Intrinsic_Rline_Info, line);
00521    (void) SLang_push_malloced_string (line);
00522    return 0;
00523 }
00524 
00525 void slsh_readline_intrinsic (char *prompt)
00526 {
00527    (void) readline_intrinsic_internal (prompt, 0);
00528 }
00529 
00530 void slsh_readline_noecho_intrinsic (char *prompt)
00531 {
00532    (void) readline_intrinsic_internal (prompt, 1);
00533 }

© sourcejam.com 2005-2008