Fix regression in command name completion reported by John Darrington.
[pspp-builds.git] / src / ui / terminal / read-line.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21
22 #include "read-line.h"
23
24 #include <stdlib.h>
25 #include <stdbool.h>
26 #include <assert.h>
27 #include <errno.h>
28
29 #include "msg-ui.h"
30
31 #include <data/file-name.h>
32 #include <data/file-name.h>
33 #include <data/settings.h>
34 #include <language/command.h>
35 #include <libpspp/message.h>
36 #include <libpspp/str.h>
37 #include <libpspp/version.h>
38 #include <output/table.h>
39
40 #include "xalloc.h"
41
42 #include "gettext.h"
43 #define _(msgid) gettext (msgid)
44
45 #if HAVE_READLINE
46 #include <readline/readline.h>
47 #include <readline/history.h>
48
49 static char *history_file;
50
51 static char **complete_command_name (const char *, int, int);
52 static char **dont_complete (const char *, int, int);
53 #endif /* HAVE_READLINE */
54
55 static bool initialised = false;
56
57 /* Initialize getl. */
58 void
59 readln_initialize (void)
60 {
61   initialised = true;
62
63 #if HAVE_READLINE
64   rl_basic_word_break_characters = "\n";
65 #ifdef unix
66   if (history_file == NULL)
67     {
68       history_file = tilde_expand ("~/.pspp_history");
69       using_history ();
70       read_history (history_file);
71       stifle_history (500);
72     }
73 #endif
74 #endif
75 }
76
77 /* Close getl. */
78 void
79 readln_uninitialize (void)
80 {
81   initialised = false;
82
83 #if HAVE_READLINE && unix
84   if (history_file != NULL)
85     write_history (history_file);
86 #endif
87 }
88
89 /* Display a welcoming message. */
90 static void
91 welcome (void)
92 {
93   static bool welcomed = false;
94   if (welcomed)
95     return;
96   welcomed = true;
97   fputs ("PSPP is free software and you are welcome to distribute copies of "
98          "it\nunder certain conditions; type \"show copying.\" to see the "
99          "conditions.\nThere is ABSOLUTELY NO WARRANTY for PSPP; type \"show "
100          "warranty.\" for details.\n", stdout);
101   puts (stat_version);
102
103 #if HAVE_READLINE && unix
104   if (history_file == NULL)
105     {
106       history_file = tilde_expand ("~/.pspp_history");
107       using_history ();
108       read_history (history_file);
109       stifle_history (500);
110     }
111 #endif
112 }
113
114 /* Gets a line from the user and stores it into LINE.
115    Prompts the user with PROMPT.
116    Returns true if successful, false at end of file.
117    Suitable for passing to getl_append_interactive(). */
118 bool
119 readln_read (struct string *line, enum getl_prompt_style style)
120 {
121   const char *prompt = getl_get_prompt (style);
122 #if HAVE_READLINE
123   char *string;
124 #endif
125   
126   assert (initialised);
127
128   reset_msg_count ();
129
130   welcome ();
131
132 #if HAVE_READLINE
133   rl_attempted_completion_function = (style == GETL_PROMPT_FIRST
134                                       ? complete_command_name
135                                       : dont_complete);
136   string = readline (prompt);
137   if (string == NULL)
138     return false;
139   else 
140     {
141       if (string[0])
142         add_history (string);
143       ds_assign_cstr (line, string);
144       free (string);
145       return true; 
146     }
147 #else
148   fputs (prompt, stdout);
149   fflush (stdout);
150   if (ds_read_line (line, stdin)) 
151     {
152       ds_chomp (line, '\n');
153       return true;
154     }
155   else
156     return false;
157 #endif
158 }
159
160 #ifdef HAVE_READLINE
161 static char *command_generator (const char *text, int state);
162
163 /* Returns a set of command name completions for TEXT.
164    This is of the proper form for assigning to
165    rl_attempted_completion_function. */
166 static char **
167 complete_command_name (const char *text, int start, int end UNUSED)
168 {
169   if (start == 0) 
170     {
171       /* Complete command name at start of line. */
172       return rl_completion_matches (text, command_generator); 
173     }
174   else 
175     {
176       /* Otherwise don't do any completion. */
177       rl_attempted_completion_over = 1;
178       return NULL; 
179     }
180 }
181
182 /* Do not do any completion for TEXT. */
183 static char **
184 dont_complete (const char *text UNUSED, int start UNUSED, int end UNUSED)
185 {
186   rl_attempted_completion_over = 1;
187   return NULL; 
188 }
189
190 /* If STATE is 0, returns the first command name matching TEXT.
191    Otherwise, returns the next command name matching TEXT.
192    Returns a null pointer when no matches are left. */
193 static char *
194 command_generator (const char *text, int state) 
195
196   static const struct command *cmd;
197   const char *name;
198
199   if (state == 0)
200     cmd = NULL;
201   name = cmd_complete (text, &cmd);
202   return name ? xstrdup (name) : NULL;
203 }
204 #endif /* HAVE_READLINE */