Terminal reader: Interruptable command line interface
[pspp] / src / ui / terminal / terminal-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <signal.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <stdbool.h>
23
24 #if HAVE_READLINE
25 #include <readline/readline.h>
26 #include <readline/history.h>
27
28 static char *history_file;
29
30 static char **complete_command_name (const char *, int, int);
31 static char **dont_complete (const char *, int, int);
32 static char *command_generator (const char *text, int state);
33
34 static const bool have_readline = true;
35
36 #else
37 static const bool have_readline = false;
38 static int rl_end;
39 #endif
40
41
42 #include "ui/terminal/terminal-reader.h"
43
44 #include <assert.h>
45 #include <errno.h>
46 #include <stdint.h>
47 #include <stdlib.h>
48
49 #include "data/file-name.h"
50 #include "data/settings.h"
51 #include "language/command.h"
52 #include "language/lexer/lexer.h"
53 #include "libpspp/assertion.h"
54 #include "libpspp/cast.h"
55 #include "libpspp/message.h"
56 #include "libpspp/prompt.h"
57 #include "libpspp/str.h"
58 #include "libpspp/version.h"
59 #include "output/driver.h"
60 #include "output/journal.h"
61 #include "ui/terminal/terminal.h"
62
63 #include "gl/minmax.h"
64 #include "gl/xalloc.h"
65
66 #include "gettext.h"
67 #define _(msgid) gettext (msgid)
68
69 struct terminal_reader
70   {
71     struct lex_reader reader;
72     struct substring s;
73     size_t offset;
74     bool eof;
75   };
76
77 static int n_terminal_readers;
78
79 static void readline_init (void);
80 static void readline_done (void);
81 static bool readline_read (struct substring *, enum prompt_style);
82
83 /* Display a welcoming message. */
84 static void
85 welcome (void)
86 {
87   static bool welcomed = false;
88   if (welcomed)
89     return;
90   welcomed = true;
91   fputs ("PSPP is free software and you are welcome to distribute copies of "
92          "it\nunder certain conditions; type \"show copying.\" to see the "
93          "conditions.\nThere is ABSOLUTELY NO WARRANTY for PSPP; type \"show "
94          "warranty.\" for details.\n", stdout);
95   puts (stat_version);
96   journal_enable ();
97 }
98
99 static struct terminal_reader *
100 terminal_reader_cast (struct lex_reader *r)
101 {
102   return UP_CAST (r, struct terminal_reader, reader);
103 }
104
105 static size_t
106 terminal_reader_read (struct lex_reader *r_, char *buf, size_t n,
107                       enum prompt_style prompt_style)
108 {
109   struct terminal_reader *r = terminal_reader_cast (r_);
110   size_t chunk;
111
112   if (r->offset >= r->s.length && !r->eof)
113     {
114       welcome ();
115       msg_ui_reset_counts ();
116       output_flush ();
117
118       ss_dealloc (&r->s);
119       if (! readline_read (&r->s, prompt_style))
120         {
121           *buf = '\n';
122           fprintf (rl_outstream, "\n");
123           return 1;
124         }
125       r->offset = 0;
126       r->eof = ss_is_empty (r->s);
127
128       /* Check whether the size of the window has changed, so that
129          the output drivers can adjust their settings as needed.  We
130          only do this for the first line of a command, as it's
131          possible that the output drivers are actually in use
132          afterward, and we don't want to confuse them in the middle
133          of output. */
134       if (prompt_style == PROMPT_FIRST)
135         terminal_check_size ();
136     }
137
138   chunk = MIN (n, r->s.length - r->offset);
139   memcpy (buf, r->s.string + r->offset, chunk);
140   r->offset += chunk;
141   return chunk;
142 }
143
144 static void
145 terminal_reader_close (struct lex_reader *r_)
146 {
147   struct terminal_reader *r = terminal_reader_cast (r_);
148
149   ss_dealloc (&r->s);
150   free (r->reader.file_name);
151   free (r);
152
153   if (!--n_terminal_readers)
154     readline_done ();
155 }
156
157 static struct lex_reader_class terminal_reader_class =
158   {
159     terminal_reader_read,
160     terminal_reader_close
161   };
162
163 /* Creates a source which uses readln to get its line */
164 struct lex_reader *
165 terminal_reader_create (void)
166 {
167   struct terminal_reader *r;
168
169   if (!n_terminal_readers++)
170     readline_init ();
171
172   r = xzalloc (sizeof *r);
173   r->reader.class = &terminal_reader_class;
174   r->reader.syntax = LEX_SYNTAX_INTERACTIVE;
175   r->reader.error = LEX_ERROR_INTERACTIVE;
176   r->reader.file_name = NULL;
177   r->s = ss_empty ();
178   r->offset = 0;
179   r->eof = false;
180   return &r->reader;
181 }
182 \f
183
184
185 static const char *
186 readline_prompt (enum prompt_style style)
187 {
188   switch (style)
189     {
190     case PROMPT_FIRST:
191       return "PSPP> ";
192
193     case PROMPT_LATER:
194       return "    > ";
195
196     case PROMPT_DATA:
197       return "data> ";
198
199     case PROMPT_COMMENT:
200       return "comment> ";
201
202     case PROMPT_DOCUMENT:
203       return "document> ";
204
205     case PROMPT_DO_REPEAT:
206       return "DO REPEAT> ";
207     }
208
209   NOT_REACHED ();
210 }
211
212
213
214 static int pfd[2];
215 static bool sigint_received ;
216
217 static void 
218 handler (int sig)
219 {
220   rl_end = 0;
221
222   write (pfd[1], "x", 1);
223   rl_echo_signal_char (sig);
224 }
225
226
227 /* 
228    A function similar to getc from stdio.
229    However this one may be interrupted by SIGINT.
230    If that happens it will return EOF and the global variable
231    sigint_received will be set to true.
232  */
233 static int
234 interruptible_getc (FILE *fp)
235 {
236   int c  = 0;
237   int ret = -1;
238   do
239     {
240       struct timeval timeout = {0, 50000};
241       fd_set what;
242       int max_fd = 0;
243       int fd ;
244       FD_ZERO (&what);
245       fd = fileno (fp);
246       max_fd = (max_fd > fd) ? max_fd : fd;
247       FD_SET (fd, &what);
248       fd = pfd[0];
249       max_fd = (max_fd > fd) ? max_fd : fd;
250       FD_SET (fd, &what);
251       ret = select (max_fd + 1, &what, NULL, NULL, &timeout);
252       if ( ret == -1 && errno != EINTR)
253         {
254           perror ("Select failed");
255           continue;
256         }
257
258       if (ret > 0 )
259         {
260           if (FD_ISSET (pfd[0], &what))
261             {
262               char dummy[1];
263               read (pfd[0], dummy, 1);
264               sigint_received = true;
265               return EOF;
266             }
267         }
268     }
269   while (ret <= 0);
270
271   /* This will not block! */
272   read (fileno (fp), &c, 1);
273
274   return c;
275 }
276
277
278
279 #if HAVE_READLINE
280
281 static void
282 readline_init (void)
283 {
284   if ( 0 != pipe2 (pfd, O_NONBLOCK))
285     perror ("Cannot create pipe");
286
287   if ( SIG_ERR == signal (SIGINT, handler))
288     perror ("Cannot add signal handler");
289
290   rl_catch_signals = 0;
291   rl_getc_function = interruptible_getc;
292   rl_basic_word_break_characters = "\n";
293   using_history ();
294   stifle_history (500);
295   if (history_file == NULL)
296     {
297       const char *home_dir = getenv ("HOME");
298       if (home_dir != NULL)
299         {
300           history_file = xasprintf ("%s/.pspp_history", home_dir);
301           read_history (history_file);
302         }
303     }
304 }
305
306 static void
307 readline_done (void)
308 {
309   if (history_file != NULL && false == settings_get_testing_mode () )
310     write_history (history_file);
311   clear_history ();
312   free (history_file);
313 }
314
315 /* Prompt the user for a line of input and return it in LINE. 
316    Returns true if the LINE should be considered valid, false otherwise.
317  */
318 static bool
319 readline_read (struct substring *line, enum prompt_style style)
320 {
321   char *string;
322
323   rl_attempted_completion_function = (style == PROMPT_FIRST
324                                       ? complete_command_name
325                                       : dont_complete);
326   sigint_received = false;
327   string = readline (readline_prompt (style));
328   if (sigint_received)
329     {
330       *line = ss_empty ();
331       return false;
332     }
333
334   if (string != NULL)
335     {
336       char *end;
337
338       if (string[0])
339         add_history (string);
340
341       end = strchr (string, '\0');
342       *end = '\n';
343       *line = ss_buffer (string, end - string + 1);
344     }
345   else
346     *line = ss_empty ();
347
348   return true;
349 }
350
351 /* Returns a set of command name completions for TEXT.
352    This is of the proper form for assigning to
353    rl_attempted_completion_function. */
354 static char **
355 complete_command_name (const char *text, int start, int end UNUSED)
356 {
357   if (start == 0)
358     {
359       /* Complete command name at start of line. */
360       return rl_completion_matches (text, command_generator);
361     }
362   else
363     {
364       /* Otherwise don't do any completion. */
365       rl_attempted_completion_over = 1;
366       return NULL;
367     }
368 }
369
370 /* Do not do any completion for TEXT. */
371 static char **
372 dont_complete (const char *text UNUSED, int start UNUSED, int end UNUSED)
373 {
374   rl_attempted_completion_over = 1;
375   return NULL;
376 }
377
378 /* If STATE is 0, returns the first command name matching TEXT.
379    Otherwise, returns the next command name matching TEXT.
380    Returns a null pointer when no matches are left. */
381 static char *
382 command_generator (const char *text, int state)
383 {
384   static const struct command *cmd;
385   const char *name;
386
387   if (state == 0)
388     cmd = NULL;
389   name = cmd_complete (text, &cmd);
390   return name ? xstrdup (name) : NULL;
391 }
392
393 #else  /* !HAVE_READLINE */
394
395 static void
396 readline_init (void)
397 {
398 }
399
400 static void
401 readline_done (void)
402 {
403 }
404
405 static bool
406 readline_read (struct substring *line, enum prompt_style style)
407 {
408   struct string string;
409   const char *prompt = readline_prompt (style);
410
411   fputs (prompt, stdout);
412   fflush (stdout);
413   ds_init_empty (&string);
414   ds_read_line (&string, stdin, SIZE_MAX);
415   
416   *line = string.ss;
417   
418   return false;
419 }
420 #endif /* !HAVE_READLINE */