lexer: Reimplement for better testability and internationalization.
[pspp-builds.git] / src / ui / terminal / terminal-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011 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 "ui/terminal/terminal-reader.h"
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdbool.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26
27 #include "data/file-name.h"
28 #include "data/settings.h"
29 #include "language/command.h"
30 #include "language/lexer/lexer.h"
31 #include "libpspp/assertion.h"
32 #include "libpspp/cast.h"
33 #include "libpspp/message.h"
34 #include "libpspp/prompt.h"
35 #include "libpspp/str.h"
36 #include "libpspp/version.h"
37 #include "output/driver.h"
38 #include "output/journal.h"
39 #include "ui/terminal/terminal.h"
40
41 #include "gl/minmax.h"
42 #include "gl/xalloc.h"
43
44 #include "gettext.h"
45 #define _(msgid) gettext (msgid)
46
47 struct terminal_reader
48   {
49     struct lex_reader reader;
50     struct substring s;
51     size_t offset;
52     bool eof;
53   };
54
55 static int n_terminal_readers;
56
57 static void readline_init (void);
58 static void readline_done (void);
59 static struct substring readline_read (enum prompt_style);
60
61 /* Display a welcoming message. */
62 static void
63 welcome (void)
64 {
65   static bool welcomed = false;
66   if (welcomed)
67     return;
68   welcomed = true;
69   fputs ("PSPP is free software and you are welcome to distribute copies of "
70          "it\nunder certain conditions; type \"show copying.\" to see the "
71          "conditions.\nThere is ABSOLUTELY NO WARRANTY for PSPP; type \"show "
72          "warranty.\" for details.\n", stdout);
73   puts (stat_version);
74   journal_enable ();
75 }
76
77 static struct terminal_reader *
78 terminal_reader_cast (struct lex_reader *r)
79 {
80   return UP_CAST (r, struct terminal_reader, reader);
81 }
82
83 static size_t
84 terminal_reader_read (struct lex_reader *r_, char *buf, size_t n,
85                       enum prompt_style prompt_style)
86 {
87   struct terminal_reader *r = terminal_reader_cast (r_);
88   size_t chunk;
89
90   if (r->offset >= r->s.length && !r->eof)
91     {
92       welcome ();
93       msg_ui_reset_counts ();
94       output_flush ();
95
96       ss_dealloc (&r->s);
97       r->s = readline_read (prompt_style);
98       r->offset = 0;
99       r->eof = ss_is_empty (r->s);
100
101       /* Check whether the size of the window has changed, so that
102          the output drivers can adjust their settings as needed.  We
103          only do this for the first line of a command, as it's
104          possible that the output drivers are actually in use
105          afterward, and we don't want to confuse them in the middle
106          of output. */
107       if (prompt_style == PROMPT_FIRST)
108         terminal_check_size ();
109     }
110
111   chunk = MIN (n, r->s.length - r->offset);
112   memcpy (buf, r->s.string + r->offset, chunk);
113   r->offset += chunk;
114   return chunk;
115 }
116
117 static void
118 terminal_reader_close (struct lex_reader *r_)
119 {
120   struct terminal_reader *r = terminal_reader_cast (r_);
121
122   ss_dealloc (&r->s);
123   free (r->reader.file_name);
124   free (r);
125
126   if (!--n_terminal_readers)
127     readline_done ();
128 }
129
130 static struct lex_reader_class terminal_reader_class =
131   {
132     terminal_reader_read,
133     terminal_reader_close
134   };
135
136 /* Creates a source which uses readln to get its line */
137 struct lex_reader *
138 terminal_reader_create (void)
139 {
140   struct terminal_reader *r;
141
142   if (!n_terminal_readers++)
143     readline_init ();
144
145   r = xzalloc (sizeof *r);
146   r->reader.class = &terminal_reader_class;
147   r->reader.syntax = LEX_SYNTAX_INTERACTIVE;
148   r->reader.error = LEX_ERROR_INTERACTIVE;
149   r->reader.file_name = NULL;
150   r->s = ss_empty ();
151   r->offset = 0;
152   r->eof = false;
153   return &r->reader;
154 }
155 \f
156 #if HAVE_READLINE
157 #include <readline/readline.h>
158 #include <readline/history.h>
159
160 static char *history_file;
161
162 static char **complete_command_name (const char *, int, int);
163 static char **dont_complete (const char *, int, int);
164 static char *command_generator (const char *text, int state);
165
166 static void
167 readline_init (void)
168 {
169   rl_basic_word_break_characters = "\n";
170   using_history ();
171   stifle_history (500);
172   if (history_file == NULL)
173     {
174       const char *home_dir = getenv ("HOME");
175       if (home_dir != NULL)
176         {
177           history_file = xasprintf ("%s/.pspp_history", home_dir);
178           read_history (history_file);
179         }
180     }
181 }
182
183 static void
184 readline_done (void)
185 {
186   if (history_file != NULL && false == settings_get_testing_mode () )
187     write_history (history_file);
188   clear_history ();
189   free (history_file);
190 }
191
192 static const char *
193 readline_prompt (enum prompt_style style)
194 {
195   switch (style)
196     {
197     case PROMPT_FIRST:
198       return "PSPP> ";
199
200     case PROMPT_LATER:
201       return "    > ";
202
203     case PROMPT_DATA:
204       return "data> ";
205
206     case PROMPT_COMMENT:
207       return "comment> ";
208
209     case PROMPT_DOCUMENT:
210       return "document> ";
211
212     case PROMPT_DO_REPEAT:
213       return "DO REPEAT> ";
214     }
215
216   NOT_REACHED ();
217 }
218
219 static struct substring
220 readline_read (enum prompt_style style)
221 {
222   char *string;
223
224   rl_attempted_completion_function = (style == PROMPT_FIRST
225                                       ? complete_command_name
226                                       : dont_complete);
227   string = readline (readline_prompt (style));
228   if (string != NULL)
229     {
230       char *end;
231
232       if (string[0])
233         add_history (string);
234
235       end = strchr (string, '\0');
236       *end = '\n';
237       return ss_buffer (string, end - string + 1);
238     }
239   else
240     return ss_empty ();
241 }
242
243 /* Returns a set of command name completions for TEXT.
244    This is of the proper form for assigning to
245    rl_attempted_completion_function. */
246 static char **
247 complete_command_name (const char *text, int start, int end UNUSED)
248 {
249   if (start == 0)
250     {
251       /* Complete command name at start of line. */
252       return rl_completion_matches (text, command_generator);
253     }
254   else
255     {
256       /* Otherwise don't do any completion. */
257       rl_attempted_completion_over = 1;
258       return NULL;
259     }
260 }
261
262 /* Do not do any completion for TEXT. */
263 static char **
264 dont_complete (const char *text UNUSED, int start UNUSED, int end UNUSED)
265 {
266   rl_attempted_completion_over = 1;
267   return NULL;
268 }
269
270 /* If STATE is 0, returns the first command name matching TEXT.
271    Otherwise, returns the next command name matching TEXT.
272    Returns a null pointer when no matches are left. */
273 static char *
274 command_generator (const char *text, int state)
275 {
276   static const struct command *cmd;
277   const char *name;
278
279   if (state == 0)
280     cmd = NULL;
281   name = cmd_complete (text, &cmd);
282   return name ? xstrdup (name) : NULL;
283 }
284 #else  /* !HAVE_READLINE */
285 static void
286 readline_init (void)
287 {
288 }
289
290 static void
291 readline_done (void)
292 {
293 }
294
295 static struct substring
296 readline_read (enum prompt_style style)
297 {
298   const char *prompt = prompt_get (style);
299   struct string line;
300
301   fputs (prompt, stdout);
302   fflush (stdout);
303   ds_init_empty (&line);
304   ds_read_line (&line, stdin, SIZE_MAX);
305
306   return line.ss;
307 }
308 #endif /* !HAVE_READLINE */