Rewrite PSPP output engine.
[pspp-builds.git] / src / ui / terminal / read-line.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2007, 2009 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 "read-line.h"
20
21 #include <stdlib.h>
22 #include <stdbool.h>
23 #include <assert.h>
24 #include <errno.h>
25 #if ! HAVE_READLINE
26 #include <stdint.h>
27 #endif
28
29 #include "msg-ui.h"
30
31 #include <data/file-name.h>
32 #include <data/settings.h>
33 #include <language/command.h>
34 #include <libpspp/cast.h>
35 #include <libpspp/message.h>
36 #include <libpspp/str.h>
37 #include <libpspp/version.h>
38 #include <language/prompt.h>
39 #include <output/journal.h>
40 #include <output/driver.h>
41 #include <ui/terminal/terminal.h>
42
43 #include "xalloc.h"
44
45 #include "gettext.h"
46 #define _(msgid) gettext (msgid)
47
48 #if HAVE_READLINE
49 #include <readline/readline.h>
50 #include <readline/history.h>
51
52 static char *history_file;
53
54 static char **complete_command_name (const char *, int, int);
55 static char **dont_complete (const char *, int, int);
56 #endif /* HAVE_READLINE */
57
58
59 struct readln_source
60 {
61   struct getl_interface parent ;
62
63   bool (*interactive_func) (struct string *line,
64                             enum prompt_style) ;
65 };
66
67
68 static bool initialised = false;
69
70 /* Initialize getl. */
71 void
72 readln_initialize (void)
73 {
74   initialised = true;
75
76 #if HAVE_READLINE
77   rl_basic_word_break_characters = "\n";
78   using_history ();
79   stifle_history (500);
80   if (history_file == NULL)
81     {
82       const char *home_dir = getenv ("HOME");
83       if (home_dir != NULL)
84         {
85           history_file = xasprintf ("%s/.pspp_history", home_dir);
86           read_history (history_file);
87         }
88     }
89 #endif
90 }
91
92 /* Close getl. */
93 void
94 readln_uninitialize (void)
95 {
96   initialised = false;
97
98 #if HAVE_READLINE
99   if (history_file != NULL && false == settings_get_testing_mode () )
100     write_history (history_file);
101   clear_history ();
102   free (history_file);
103 #endif
104 }
105
106
107 static bool
108 read_interactive (struct getl_interface *s,
109                   struct string *line)
110 {
111   struct readln_source *is  = UP_CAST (s, struct readln_source, parent);
112
113   return is->interactive_func (line, prompt_get_style ());
114 }
115
116 static bool
117 always_true (const struct getl_interface *s UNUSED)
118 {
119   return true;
120 }
121
122 /* Display a welcoming message. */
123 static void
124 welcome (void)
125 {
126   static bool welcomed = false;
127   if (welcomed)
128     return;
129   welcomed = true;
130   fputs ("PSPP is free software and you are welcome to distribute copies of "
131          "it\nunder certain conditions; type \"show copying.\" to see the "
132          "conditions.\nThere is ABSOLUTELY NO WARRANTY for PSPP; type \"show "
133          "warranty.\" for details.\n", stdout);
134   puts (stat_version);
135   readln_initialize ();
136   journal_enable ();
137 }
138
139 /* Gets a line from the user and stores it into LINE.
140    Prompts the user with PROMPT.
141    Returns true if successful, false at end of file.
142    */
143 static bool
144 readln_read (struct string *line, enum prompt_style style)
145 {
146   const char *prompt = prompt_get (style);
147 #if HAVE_READLINE
148   char *string;
149 #endif
150   bool eof;
151
152   assert (initialised);
153
154   reset_msg_count ();
155
156   welcome ();
157
158   if (style == PROMPT_FIRST)
159     output_flush ();
160
161 #if HAVE_READLINE
162   rl_attempted_completion_function = (style == PROMPT_FIRST
163                                       ? complete_command_name
164                                       : dont_complete);
165   string = readline (prompt);
166   if (string == NULL)
167     eof = true;
168   else
169     {
170       if (string[0])
171         add_history (string);
172       ds_assign_cstr (line, string);
173       free (string);
174       eof = false;
175     }
176 #else
177   fputs (prompt, stdout);
178   fflush (stdout);
179   if (ds_read_line (line, stdin, SIZE_MAX))
180     {
181       ds_chomp (line, '\n');
182       eof = false;
183     }
184   else
185     eof = true;
186 #endif
187
188   /* Check whether the size of the window has changed, so that
189      the output drivers can adjust their settings as needed.  We
190      only do this for the first line of a command, as it's
191      possible that the output drivers are actually in use
192      afterward, and we don't want to confuse them in the middle
193      of output. */
194   if (style == PROMPT_FIRST)
195     terminal_check_size ();
196
197   return !eof;
198 }
199
200 static void
201 readln_close (struct getl_interface *i)
202 {
203   free (i);
204 }
205
206 /* Creates a source which uses readln to get its line */
207 struct getl_interface *
208 create_readln_source (void)
209 {
210   struct readln_source *rlns  = xzalloc (sizeof (*rlns));
211
212   rlns->interactive_func = readln_read;
213
214   rlns->parent.interactive = always_true;
215   rlns->parent.read = read_interactive;
216   rlns->parent.close = readln_close;
217
218   return &rlns->parent;
219 }
220
221
222 #if HAVE_READLINE
223 static char *command_generator (const char *text, int state);
224
225 /* Returns a set of command name completions for TEXT.
226    This is of the proper form for assigning to
227    rl_attempted_completion_function. */
228 static char **
229 complete_command_name (const char *text, int start, int end UNUSED)
230 {
231   if (start == 0)
232     {
233       /* Complete command name at start of line. */
234       return rl_completion_matches (text, command_generator);
235     }
236   else
237     {
238       /* Otherwise don't do any completion. */
239       rl_attempted_completion_over = 1;
240       return NULL;
241     }
242 }
243
244 /* Do not do any completion for TEXT. */
245 static char **
246 dont_complete (const char *text UNUSED, int start UNUSED, int end UNUSED)
247 {
248   rl_attempted_completion_over = 1;
249   return NULL;
250 }
251
252 /* If STATE is 0, returns the first command name matching TEXT.
253    Otherwise, returns the next command name matching TEXT.
254    Returns a null pointer when no matches are left. */
255 static char *
256 command_generator (const char *text, int state)
257 {
258   static const struct command *cmd;
259   const char *name;
260
261   if (state == 0)
262     cmd = NULL;
263   name = cmd_complete (text, &cmd);
264   return name ? xstrdup (name) : NULL;
265 }
266 #endif /* HAVE_READLINE */