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