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