X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fui%2Fterminal%2Fterminal-reader.c;h=3225678e8707b28a0dba95064d93fae4abf6b6f0;hb=f5711a3a6d3f28e53513269e774bfa1e8274c1c5;hp=db561a9a2fde0b7e26452579eec8525cc32f43e9;hpb=d3bb911bf13f8fb0d10c83d42e130184e0a599ab;p=pspp diff --git a/src/ui/terminal/terminal-reader.c b/src/ui/terminal/terminal-reader.c index db561a9a2f..3225678e87 100644 --- a/src/ui/terminal/terminal-reader.c +++ b/src/ui/terminal/terminal-reader.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2013 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,15 +16,36 @@ #include +#include +#include +#include +#include +#include + +#include "libpspp/str.h" + +#if HAVE_READLINE +#include +#include +#include + +static char *history_file; + +static char **complete_command_name (const char *, int, int); +static char **dont_complete (const char *, int, int); +static char *command_generator (const char *text, int state); +#endif + + #include "ui/terminal/terminal-reader.h" +#include +#include +#include -#include #include -#include #include #include -#include "data/file-name.h" #include "data/settings.h" #include "language/command.h" #include "language/lexer/lexer.h" @@ -36,7 +57,6 @@ #include "libpspp/version.h" #include "output/driver.h" #include "output/journal.h" -#include "ui/terminal/terminal.h" #include "gl/minmax.h" #include "gl/xalloc.h" @@ -56,7 +76,7 @@ static int n_terminal_readers; static void readline_init (void); static void readline_done (void); -static struct substring readline_read (enum prompt_style); +static bool readline_read (struct substring *, enum prompt_style); /* Display a welcoming message. */ static void @@ -70,8 +90,8 @@ welcome (void) "it\nunder certain conditions; type \"show copying.\" to see the " "conditions.\nThere is ABSOLUTELY NO WARRANTY for PSPP; type \"show " "warranty.\" for details.\n", stdout); - puts (stat_version); - journal_enable (); + puts (announced_version); + journal_init (); } static struct terminal_reader * @@ -80,6 +100,42 @@ terminal_reader_cast (struct lex_reader *r) return UP_CAST (r, struct terminal_reader, reader); } + +/* Older libreadline versions do not provide rl_outstream. + However, it is almost always going to be the same as stdout. */ +#if ! HAVE_RL_OUTSTREAM +# define rl_outstream stdout +#endif + + +#if HAVE_READLINE +/* Similarly, rl_echo_signal_char is fairly recent. + We provide our own crude version if it is not present. */ +#if ! HAVE_RL_ECHO_SIGNAL_CHAR +static void +rl_echo_signal_char (int sig) +{ +#if HAVE_TERMIOS_H + struct termios t; + if (0 == tcgetattr (0, &t)) + { + cc_t c = t.c_cc[VINTR]; + + if (c >= 0 && c <= 'Z' - 'A') + fprintf (rl_outstream, "^%c", 'A' + c - 1); + else + fprintf (rl_outstream, "%c", c); + } + else +#endif + fprintf (rl_outstream, "^C"); + + fflush (rl_outstream); +} +#endif +#endif + + static size_t terminal_reader_read (struct lex_reader *r_, char *buf, size_t n, enum prompt_style prompt_style) @@ -94,18 +150,14 @@ terminal_reader_read (struct lex_reader *r_, char *buf, size_t n, output_flush (); ss_dealloc (&r->s); - r->s = readline_read (prompt_style); + if (! readline_read (&r->s, prompt_style)) + { + *buf = '\n'; + fprintf (rl_outstream, "\n"); + return 1; + } r->offset = 0; r->eof = ss_is_empty (r->s); - - /* Check whether the size of the window has changed, so that - the output drivers can adjust their settings as needed. We - only do this for the first line of a command, as it's - possible that the output drivers are actually in use - afterward, and we don't want to confuse them in the middle - of output. */ - if (prompt_style == PROMPT_FIRST) - terminal_check_size (); } chunk = MIN (n, r->s.length - r->offset); @@ -137,15 +189,13 @@ static struct lex_reader_class terminal_reader_class = struct lex_reader * terminal_reader_create (void) { - struct terminal_reader *r; - if (!n_terminal_readers++) readline_init (); - r = xzalloc (sizeof *r); + struct terminal_reader *r = XZALLOC (struct terminal_reader); r->reader.class = &terminal_reader_class; - r->reader.syntax = LEX_SYNTAX_INTERACTIVE; - r->reader.error = LEX_ERROR_INTERACTIVE; + r->reader.syntax = SEG_MODE_INTERACTIVE; + r->reader.error = LEX_ERROR_TERMINAL; r->reader.file_name = NULL; r->s = ss_empty (); r->offset = 0; @@ -177,6 +227,9 @@ readline_prompt (enum prompt_style style) case PROMPT_DO_REPEAT: return "DO REPEAT> "; + + case PROMPT_DEFINE: + return "DEFINE> "; } NOT_REACHED (); @@ -184,18 +237,81 @@ readline_prompt (enum prompt_style style) #if HAVE_READLINE -#include -#include -static char *history_file; +static int pfd[2]; +static bool sigint_received ; + +/* + A function similar to getc from stdio. + However this one may be interrupted by SIGINT. + If that happens it will return EOF and the global variable + sigint_received will be set to true. + */ +static int +interruptible_getc (FILE *fp) +{ + int c = 0; + int ret = -1; + do + { + struct timeval timeout = {0, 50000}; + fd_set what; + int max_fd = 0; + int fd ; + FD_ZERO (&what); + fd = fileno (fp); + max_fd = (max_fd > fd) ? max_fd : fd; + FD_SET (fd, &what); + fd = pfd[0]; + max_fd = (max_fd > fd) ? max_fd : fd; + FD_SET (fd, &what); + ret = select (max_fd + 1, &what, NULL, NULL, &timeout); + if (ret == -1 && errno != EINTR) + { + perror ("Select failed"); + continue; + } + + if (ret > 0) + { + if (FD_ISSET (pfd[0], &what)) + { + char dummy[1]; + read (pfd[0], dummy, 1); + sigint_received = true; + return EOF; + } + } + } + while (ret <= 0); + + /* This will not block! */ + read (fileno (fp), &c, 1); + + return c; +} + +static void +handler (int sig) +{ + rl_end = 0; + + write (pfd[1], "x", 1); + rl_echo_signal_char (sig); +} -static char **complete_command_name (const char *, int, int); -static char **dont_complete (const char *, int, int); -static char *command_generator (const char *text, int state); static void readline_init (void) { + if (0 != pipe2 (pfd, O_NONBLOCK)) + perror ("Cannot create pipe"); + + if (SIG_ERR == signal (SIGINT, handler)) + perror ("Cannot add signal handler"); + + rl_catch_signals = 0; + rl_getc_function = interruptible_getc; rl_basic_word_break_characters = "\n"; using_history (); stifle_history (500); @@ -213,21 +329,31 @@ readline_init (void) static void readline_done (void) { - if (history_file != NULL && false == settings_get_testing_mode () ) + if (history_file != NULL && false == settings_get_testing_mode ()) write_history (history_file); clear_history (); free (history_file); } -static struct substring -readline_read (enum prompt_style style) +/* Prompt the user for a line of input and return it in LINE. + Returns true if the LINE should be considered valid, false otherwise. + */ +static bool +readline_read (struct substring *line, enum prompt_style style) { char *string; rl_attempted_completion_function = (style == PROMPT_FIRST ? complete_command_name : dont_complete); + sigint_received = false; string = readline (readline_prompt (style)); + if (sigint_received) + { + *line = ss_empty (); + return false; + } + if (string != NULL) { char *end; @@ -237,10 +363,12 @@ readline_read (enum prompt_style style) end = strchr (string, '\0'); *end = '\n'; - return ss_buffer (string, end - string + 1); + *line = ss_buffer (string, end - string + 1); } else - return ss_empty (); + *line = ss_empty (); + + return true; } /* Returns a set of command name completions for TEXT. @@ -282,12 +410,26 @@ command_generator (const char *text, int state) if (state == 0) cmd = NULL; name = cmd_complete (text, &cmd); - return name ? xstrdup (name) : NULL; + return xstrdup_if_nonnull (name); } + #else /* !HAVE_READLINE */ + +static const char * the_prompt; + +static void +handler (int sig) +{ + if (the_prompt) + fputs (the_prompt, stdout); + fflush (stdout); +} + static void readline_init (void) { + if (SIG_ERR == signal (SIGINT, handler)) + perror ("Cannot add signal handler"); } static void @@ -295,17 +437,22 @@ readline_done (void) { } -static struct substring -readline_read (enum prompt_style style) +/* Prompt the user for a line of input and return it in LINE. + Returns true if the LINE should be considered valid, false otherwise. + */ +static bool +readline_read (struct substring *line, enum prompt_style style) { - const char *prompt = readline_prompt (style); - struct string line; + struct string string; + the_prompt = readline_prompt (style); - fputs (prompt, stdout); + fputs (the_prompt, stdout); fflush (stdout); - ds_init_empty (&line); - ds_read_line (&line, stdin, SIZE_MAX); + ds_init_empty (&string); + ds_read_line (&string, stdin, SIZE_MAX); + + *line = string.ss; - return line.ss; + return true; } #endif /* !HAVE_READLINE */