Comments Dialog: Remove deprecated widgets
[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/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_init ();
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 #if ! HAVE_RL_OUTSTREAM
114 # define rl_outstream stdout
115 #endif
116
117
118 #if HAVE_READLINE
119 /* Similarly, rl_echo_signal_char is fairly recent.
120    We provide our own crude version if it is not present. */
121 #if ! HAVE_RL_ECHO_SIGNAL_CHAR
122 static void
123 rl_echo_signal_char (int sig)
124 {
125 #if HAVE_TERMIOS_H
126   struct termios t;
127   if (0 == tcgetattr (0, &t))
128     {
129       cc_t c = t.c_cc[VINTR];
130   
131       if (c >= 0  && c <= 'Z' - 'A')
132         fprintf (rl_outstream, "^%c", 'A' + c - 1);
133       else
134         fprintf (rl_outstream, "%c", c);
135     }
136   else
137 #endif
138     fprintf (rl_outstream, "^C");
139
140   fflush (rl_outstream);
141 }  
142 #endif
143 #endif
144
145
146 static size_t
147 terminal_reader_read (struct lex_reader *r_, char *buf, size_t n,
148                       enum prompt_style prompt_style)
149 {
150   struct terminal_reader *r = terminal_reader_cast (r_);
151   size_t chunk;
152
153   if (r->offset >= r->s.length && !r->eof)
154     {
155       welcome ();
156       msg_ui_reset_counts ();
157       output_flush ();
158
159       ss_dealloc (&r->s);
160       if (! readline_read (&r->s, prompt_style))
161         {
162           *buf = '\n';
163           fprintf (rl_outstream, "\n");
164           return 1;
165         }
166       r->offset = 0;
167       r->eof = ss_is_empty (r->s);
168
169       /* Check whether the size of the window has changed, so that
170          the output drivers can adjust their settings as needed.  We
171          only do this for the first line of a command, as it's
172          possible that the output drivers are actually in use
173          afterward, and we don't want to confuse them in the middle
174          of output. */
175       if (prompt_style == PROMPT_FIRST)
176         terminal_check_size ();
177     }
178
179   chunk = MIN (n, r->s.length - r->offset);
180   memcpy (buf, r->s.string + r->offset, chunk);
181   r->offset += chunk;
182   return chunk;
183 }
184
185 static void
186 terminal_reader_close (struct lex_reader *r_)
187 {
188   struct terminal_reader *r = terminal_reader_cast (r_);
189
190   ss_dealloc (&r->s);
191   free (r->reader.file_name);
192   free (r);
193
194   if (!--n_terminal_readers)
195     readline_done ();
196 }
197
198 static struct lex_reader_class terminal_reader_class =
199   {
200     terminal_reader_read,
201     terminal_reader_close
202   };
203
204 /* Creates a source which uses readln to get its line */
205 struct lex_reader *
206 terminal_reader_create (void)
207 {
208   struct terminal_reader *r;
209
210   if (!n_terminal_readers++)
211     readline_init ();
212
213   r = xzalloc (sizeof *r);
214   r->reader.class = &terminal_reader_class;
215   r->reader.syntax = LEX_SYNTAX_INTERACTIVE;
216   r->reader.error = LEX_ERROR_TERMINAL;
217   r->reader.file_name = NULL;
218   r->s = ss_empty ();
219   r->offset = 0;
220   r->eof = false;
221   return &r->reader;
222 }
223 \f
224
225
226 static const char *
227 readline_prompt (enum prompt_style style)
228 {
229   switch (style)
230     {
231     case PROMPT_FIRST:
232       return "PSPP> ";
233
234     case PROMPT_LATER:
235       return "    > ";
236
237     case PROMPT_DATA:
238       return "data> ";
239
240     case PROMPT_COMMENT:
241       return "comment> ";
242
243     case PROMPT_DOCUMENT:
244       return "document> ";
245
246     case PROMPT_DO_REPEAT:
247       return "DO REPEAT> ";
248     }
249
250   NOT_REACHED ();
251 }
252
253
254
255 static int pfd[2];
256 static bool sigint_received ;
257
258
259 /* 
260    A function similar to getc from stdio.
261    However this one may be interrupted by SIGINT.
262    If that happens it will return EOF and the global variable
263    sigint_received will be set to true.
264  */
265 static int
266 interruptible_getc (FILE *fp)
267 {
268   int c  = 0;
269   int ret = -1;
270   do
271     {
272       struct timeval timeout = {0, 50000};
273       fd_set what;
274       int max_fd = 0;
275       int fd ;
276       FD_ZERO (&what);
277       fd = fileno (fp);
278       max_fd = (max_fd > fd) ? max_fd : fd;
279       FD_SET (fd, &what);
280       fd = pfd[0];
281       max_fd = (max_fd > fd) ? max_fd : fd;
282       FD_SET (fd, &what);
283       ret = select (max_fd + 1, &what, NULL, NULL, &timeout);
284       if ( ret == -1 && errno != EINTR)
285         {
286           perror ("Select failed");
287           continue;
288         }
289
290       if (ret > 0 )
291         {
292           if (FD_ISSET (pfd[0], &what))
293             {
294               char dummy[1];
295               read (pfd[0], dummy, 1);
296               sigint_received = true;
297               return EOF;
298             }
299         }
300     }
301   while (ret <= 0);
302
303   /* This will not block! */
304   read (fileno (fp), &c, 1);
305
306   return c;
307 }
308
309
310
311 #if HAVE_READLINE
312
313 static void 
314 handler (int sig)
315 {
316   rl_end = 0;
317
318   write (pfd[1], "x", 1);
319   rl_echo_signal_char (sig);
320 }
321
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 */