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