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