0fa145c98ac502edb7f29f25e471a2ca6ee885b7
[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   msg_ui_reset_counts ();
155
156   welcome ();
157
158   output_flush ();
159
160 #if HAVE_READLINE
161   rl_attempted_completion_function = (style == PROMPT_FIRST
162                                       ? complete_command_name
163                                       : dont_complete);
164   string = readline (prompt);
165   if (string == NULL)
166     eof = true;
167   else
168     {
169       if (string[0])
170         add_history (string);
171       ds_assign_cstr (line, string);
172       free (string);
173       eof = false;
174     }
175 #else
176   fputs (prompt, stdout);
177   fflush (stdout);
178   if (ds_read_line (line, stdin, SIZE_MAX))
179     {
180       ds_chomp (line, '\n');
181       eof = false;
182     }
183   else
184     eof = true;
185 #endif
186
187   /* Check whether the size of the window has changed, so that
188      the output drivers can adjust their settings as needed.  We
189      only do this for the first line of a command, as it's
190      possible that the output drivers are actually in use
191      afterward, and we don't want to confuse them in the middle
192      of output. */
193   if (style == PROMPT_FIRST)
194     terminal_check_size ();
195
196   return !eof;
197 }
198
199 static void
200 readln_close (struct getl_interface *i)
201 {
202   free (i);
203 }
204
205 /* Creates a source which uses readln to get its line */
206 struct getl_interface *
207 create_readln_source (void)
208 {
209   struct readln_source *rlns  = xzalloc (sizeof (*rlns));
210
211   rlns->interactive_func = readln_read;
212
213   rlns->parent.interactive = always_true;
214   rlns->parent.read = read_interactive;
215   rlns->parent.close = readln_close;
216
217   return &rlns->parent;
218 }
219
220
221 #if HAVE_READLINE
222 static char *command_generator (const char *text, int state);
223
224 /* Returns a set of command name completions for TEXT.
225    This is of the proper form for assigning to
226    rl_attempted_completion_function. */
227 static char **
228 complete_command_name (const char *text, int start, int end UNUSED)
229 {
230   if (start == 0)
231     {
232       /* Complete command name at start of line. */
233       return rl_completion_matches (text, command_generator);
234     }
235   else
236     {
237       /* Otherwise don't do any completion. */
238       rl_attempted_completion_over = 1;
239       return NULL;
240     }
241 }
242
243 /* Do not do any completion for TEXT. */
244 static char **
245 dont_complete (const char *text UNUSED, int start UNUSED, int end UNUSED)
246 {
247   rl_attempted_completion_over = 1;
248   return NULL;
249 }
250
251 /* If STATE is 0, returns the first command name matching TEXT.
252    Otherwise, returns the next command name matching TEXT.
253    Returns a null pointer when no matches are left. */
254 static char *
255 command_generator (const char *text, int state)
256 {
257   static const struct command *cmd;
258   const char *name;
259
260   if (state == 0)
261     cmd = NULL;
262   name = cmd_complete (text, &cmd);
263   return name ? xstrdup (name) : NULL;
264 }
265 #endif /* HAVE_READLINE */