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