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