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