lexer: Add support for DEFINE...!ENDDEFINE.
[pspp] / src / ui / terminal / terminal-reader.c
index a3f33e725589a005f4b4d92a115d119e327614b4..e0f219826dfc4820f55e519dbc9c3c8c4d737929 100644 (file)
 #include <stdbool.h>
 #include <stdio.h>
 
+#include "libpspp/str.h"
 
 #if HAVE_READLINE
 #include <readline/readline.h>
 #include <readline/history.h>
+#include <termios.h>
 
 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);
-
-static const bool have_readline = true;
-
-#else
-static const bool have_readline = false;
-static int rl_end;
 #endif
 
 
@@ -46,12 +42,10 @@ static int rl_end;
 #include <sys/time.h>
 #include <sys/types.h>
 
-#include <assert.h>
 #include <errno.h>
 #include <stdint.h>
 #include <stdlib.h>
 
-#include "data/file-name.h"
 #include "data/settings.h"
 #include "language/command.h"
 #include "language/lexer/lexer.h"
@@ -63,7 +57,6 @@ static int rl_end;
 #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"
@@ -97,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 *
@@ -107,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)
@@ -129,15 +158,6 @@ terminal_reader_read (struct lex_reader *r_, char *buf, size_t n,
        }
       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);
@@ -177,7 +197,7 @@ terminal_reader_create (void)
   r = xzalloc (sizeof *r);
   r->reader.class = &terminal_reader_class;
   r->reader.syntax = LEX_SYNTAX_INTERACTIVE;
-  r->reader.error = LEX_ERROR_INTERACTIVE;
+  r->reader.error = LEX_ERROR_TERMINAL;
   r->reader.file_name = NULL;
   r->s = ss_empty ();
   r->offset = 0;
@@ -209,27 +229,21 @@ readline_prompt (enum prompt_style style)
 
     case PROMPT_DO_REPEAT:
       return "DO REPEAT> ";
+
+    case PROMPT_DEFINE:
+      return "DEFINE> ";
     }
 
   NOT_REACHED ();
 }
 
 
+#if HAVE_READLINE
 
 static int pfd[2];
 static bool sigint_received ;
 
-static void 
-handler (int sig)
-{
-  rl_end = 0;
-
-  write (pfd[1], "x", 1);
-  rl_echo_signal_char (sig);
-}
-
-
-/* 
+/*
    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
@@ -254,13 +268,13 @@ interruptible_getc (FILE *fp)
       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)
+      if (ret == -1 && errno != EINTR)
        {
          perror ("Select failed");
          continue;
        }
 
-      if (ret > 0 )
+      if (ret > 0)
        {
          if (FD_ISSET (pfd[0], &what))
            {
@@ -279,17 +293,23 @@ interruptible_getc (FILE *fp)
   return c;
 }
 
+static void
+handler (int sig)
+{
+  rl_end = 0;
 
+  write (pfd[1], "x", 1);
+  rl_echo_signal_char (sig);
+}
 
-#if HAVE_READLINE
 
 static void
 readline_init (void)
 {
-  if ( 0 != pipe2 (pfd, O_NONBLOCK))
+  if (0 != pipe2 (pfd, O_NONBLOCK))
     perror ("Cannot create pipe");
 
-  if ( SIG_ERR == signal (SIGINT, handler))
+  if (SIG_ERR == signal (SIGINT, handler))
     perror ("Cannot add signal handler");
 
   rl_catch_signals = 0;
@@ -311,13 +331,13 @@ 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);
 }
 
-/* Prompt the user for a line of input and return it in LINE. 
+/* 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
@@ -392,14 +412,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
@@ -407,19 +439,22 @@ readline_done (void)
 {
 }
 
+/* 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)
 {
   struct string string;
-  const char *prompt = readline_prompt (style);
+  the_prompt = readline_prompt (style);
 
-  fputs (prompt, stdout);
+  fputs (the_prompt, stdout);
   fflush (stdout);
   ds_init_empty (&string);
   ds_read_line (&string, stdin, SIZE_MAX);
-  
+
   *line = string.ss;
-  
-  return false;
+
+  return true;
 }
 #endif /* !HAVE_READLINE */