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