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