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