Fix building when readline library is not present
[pspp] / 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
157
158 static const char *
159 readline_prompt (enum prompt_style style)
160 {
161   switch (style)
162     {
163     case PROMPT_FIRST:
164       return "PSPP> ";
165
166     case PROMPT_LATER:
167       return "    > ";
168
169     case PROMPT_DATA:
170       return "data> ";
171
172     case PROMPT_COMMENT:
173       return "comment> ";
174
175     case PROMPT_DOCUMENT:
176       return "document> ";
177
178     case PROMPT_DO_REPEAT:
179       return "DO REPEAT> ";
180     }
181
182   NOT_REACHED ();
183 }
184
185
186 #if HAVE_READLINE
187 #include <readline/readline.h>
188 #include <readline/history.h>
189
190 static char *history_file;
191
192 static char **complete_command_name (const char *, int, int);
193 static char **dont_complete (const char *, int, int);
194 static char *command_generator (const char *text, int state);
195
196 static void
197 readline_init (void)
198 {
199   rl_basic_word_break_characters = "\n";
200   using_history ();
201   stifle_history (500);
202   if (history_file == NULL)
203     {
204       const char *home_dir = getenv ("HOME");
205       if (home_dir != NULL)
206         {
207           history_file = xasprintf ("%s/.pspp_history", home_dir);
208           read_history (history_file);
209         }
210     }
211 }
212
213 static void
214 readline_done (void)
215 {
216   if (history_file != NULL && false == settings_get_testing_mode () )
217     write_history (history_file);
218   clear_history ();
219   free (history_file);
220 }
221
222 static struct substring
223 readline_read (enum prompt_style style)
224 {
225   char *string;
226
227   rl_attempted_completion_function = (style == PROMPT_FIRST
228                                       ? complete_command_name
229                                       : dont_complete);
230   string = readline (readline_prompt (style));
231   if (string != NULL)
232     {
233       char *end;
234
235       if (string[0])
236         add_history (string);
237
238       end = strchr (string, '\0');
239       *end = '\n';
240       return ss_buffer (string, end - string + 1);
241     }
242   else
243     return ss_empty ();
244 }
245
246 /* Returns a set of command name completions for TEXT.
247    This is of the proper form for assigning to
248    rl_attempted_completion_function. */
249 static char **
250 complete_command_name (const char *text, int start, int end UNUSED)
251 {
252   if (start == 0)
253     {
254       /* Complete command name at start of line. */
255       return rl_completion_matches (text, command_generator);
256     }
257   else
258     {
259       /* Otherwise don't do any completion. */
260       rl_attempted_completion_over = 1;
261       return NULL;
262     }
263 }
264
265 /* Do not do any completion for TEXT. */
266 static char **
267 dont_complete (const char *text UNUSED, int start UNUSED, int end UNUSED)
268 {
269   rl_attempted_completion_over = 1;
270   return NULL;
271 }
272
273 /* If STATE is 0, returns the first command name matching TEXT.
274    Otherwise, returns the next command name matching TEXT.
275    Returns a null pointer when no matches are left. */
276 static char *
277 command_generator (const char *text, int state)
278 {
279   static const struct command *cmd;
280   const char *name;
281
282   if (state == 0)
283     cmd = NULL;
284   name = cmd_complete (text, &cmd);
285   return name ? xstrdup (name) : NULL;
286 }
287 #else  /* !HAVE_READLINE */
288 static void
289 readline_init (void)
290 {
291 }
292
293 static void
294 readline_done (void)
295 {
296 }
297
298 static struct substring
299 readline_read (enum prompt_style style)
300 {
301   const char *prompt = readline_prompt (style);
302   struct string line;
303
304   fputs (prompt, stdout);
305   fflush (stdout);
306   ds_init_empty (&line);
307   ds_read_line (&line, stdin, SIZE_MAX);
308
309   return line.ss;
310 }
311 #endif /* !HAVE_READLINE */