1 /* Minibuffer facility functions
2
3 Copyright (c) 1997-2014 Free Software Foundation, Inc.
4
5 This file is part of GNU Zile.
6
7 GNU Zile is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11
12 GNU Zile is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Zile; see the file COPYING. If not, write to the
19 Free Software Foundation, Fifth Floor, 51 Franklin Street, Boston,
20 MA 02111-1301, USA. */
21
22 #include <config.h>
23
24 #include <assert.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 #include "main.h"
30 #include "extern.h"
31
32 static History files_history = NULL;
33 static char *minibuf_contents = NULL;
34
35 /*--------------------------------------------------------------------------
36 * Minibuffer wrapper functions.
37 *--------------------------------------------------------------------------*/
38
39 void
init_minibuf(void)40 init_minibuf (void)
41 {
42 files_history = history_new ();
43 }
44
45 bool
minibuf_no_error(void)46 minibuf_no_error (void)
47 {
48 return minibuf_contents == NULL;
49 }
50
51 void
minibuf_refresh(void)52 minibuf_refresh (void)
53 {
54 if (cur_wp)
55 {
56 if (minibuf_contents)
57 term_minibuf_write (minibuf_contents);
58
59 /* Redisplay (and leave the cursor in the correct position). */
60 term_redraw_cursor ();
61 term_refresh ();
62 }
63 }
64
65 _GL_ATTRIBUTE_FORMAT_PRINTF(1, 0) static void
minibuf_vwrite(const char * fmt,va_list ap)66 minibuf_vwrite (const char *fmt, va_list ap)
67 {
68 char *s = xvasprintf (fmt, ap);
69 if (minibuf_contents == NULL || strcmp (s, minibuf_contents))
70 {
71 minibuf_contents = s;
72 minibuf_refresh ();
73 }
74 }
75
76 /*
77 * Write the specified string in the minibuffer.
78 */
79 void
minibuf_write(const char * fmt,...)80 minibuf_write (const char *fmt, ...)
81 {
82 va_list ap;
83
84 va_start (ap, fmt);
85 minibuf_vwrite (fmt, ap);
86 va_end (ap);
87 }
88
89 /*
90 * Write the specified error string in the minibuffer and signal an error.
91 */
92 void
minibuf_error(const char * fmt,...)93 minibuf_error (const char *fmt, ...)
94 {
95 va_list ap;
96
97 va_start (ap, fmt);
98 minibuf_vwrite (fmt, ap);
99 va_end (ap);
100
101 ding ();
102 }
103
104 /*
105 * Read a string from the minibuffer.
106 */
107 const_astr
minibuf_read(const char * fmt,const char * value,...)108 minibuf_read (const char *fmt, const char *value, ...)
109 {
110 va_list ap;
111 char *buf;
112
113 va_start (ap, value);
114 buf = xvasprintf (fmt, ap);
115 va_end (ap);
116
117 return term_minibuf_read (buf, value ? value : "", SIZE_MAX, NULL, NULL);
118 }
119
120 /*
121 * Read a non-negative number from the minibuffer.
122 */
123 long
minibuf_read_number(const char * fmt,...)124 minibuf_read_number (const char *fmt, ...)
125 {
126 va_list ap;
127 char *buf;
128 unsigned long n;
129
130 va_start (ap, fmt);
131 buf = xvasprintf (fmt, ap);
132 va_end (ap);
133
134 do
135 {
136 const_astr as = minibuf_read ("%s", "", buf);
137 if (as == NULL || astr_cstr (as) == NULL)
138 {
139 n = LONG_MAX;
140 FUNCALL (keyboard_quit);
141 break;
142 }
143 if (strlen (astr_cstr (as)) == 0)
144 n = LONG_MAX - 1;
145 else
146 n = strtoul (astr_cstr (as), NULL, 10);
147 if (n == LONG_MAX)
148 {
149 minibuf_write ("Please enter a number.");
150 ding ();
151 }
152 }
153 while (n == LONG_MAX);
154
155 return n;
156 }
157
158 /*
159 * Read a filename from the minibuffer.
160 */
161 const_astr
minibuf_read_filename(const char * fmt,const char * value,const char * file,...)162 minibuf_read_filename (const char *fmt, const char *value,
163 const char *file, ...)
164 {
165 const_astr p = NULL;
166
167 astr as = astr_new_cstr (value);
168 if (file == NULL && astr_len (as) > 0 && astr_get (as, astr_len (as) - 1) != '/')
169 astr_cat_char (as, '/');
170
171 if (expand_path (as))
172 {
173 va_list ap;
174 va_start (ap, file);
175 char *buf = xvasprintf (fmt, ap);
176 va_end (ap);
177
178 as = compact_path (as);
179
180 Completion cp = completion_new (true);
181 size_t pos = astr_len (as);
182 if (file)
183 pos -= strlen (file);
184 p = term_minibuf_read (buf, astr_cstr (as), pos, cp, files_history);
185
186 if (p != NULL)
187 {
188 astr q = astr_cpy (astr_new (), p);
189 if (expand_path (q))
190 add_history_element (files_history, astr_cstr (q));
191 else
192 q = NULL;
193 p = q;
194 }
195 }
196
197 return p;
198 }
199
200 bool
minibuf_test_in_completions(const char * ms,gl_list_t completions)201 minibuf_test_in_completions (const char *ms, gl_list_t completions)
202 {
203 return gl_sortedlist_search (completions, completion_strcmp, ms) != NULL;
204 }
205
206 int
minibuf_read_yesno(const char * fmt,...)207 minibuf_read_yesno (const char *fmt, ...)
208 {
209 va_list ap;
210 const char *errmsg = "Please answer yes or no.";
211 Completion cp = completion_new (false);
212 int ret = -1;
213
214 gl_sortedlist_add (get_completion_completions (cp), completion_strcmp, xstrdup ("yes"));
215 gl_sortedlist_add (get_completion_completions (cp), completion_strcmp, xstrdup ("no"));
216
217 va_start (ap, fmt);
218 const_astr ms = minibuf_vread_completion (fmt, "", cp, NULL, errmsg,
219 minibuf_test_in_completions, errmsg, ap);
220 va_end (ap);
221
222 if (ms != NULL)
223 {
224 gl_list_node_t n = gl_sortedlist_search (get_completion_completions (cp),
225 completion_strcmp, astr_cstr (ms));
226 assert (n);
227 ret = STREQ ((const char *) gl_list_node_value (get_completion_completions (cp), n),
228 "yes");
229 }
230
231 return ret;
232 }
233
234 const_astr
minibuf_read_completion(const char * fmt,const char * value,Completion cp,History hp,...)235 minibuf_read_completion (const char *fmt, const char *value, Completion cp, History hp, ...)
236 {
237 va_list ap;
238 char *buf;
239
240 va_start (ap, hp);
241 buf = xvasprintf (fmt, ap);
242 va_end (ap);
243
244 return term_minibuf_read (buf, value, SIZE_MAX, cp, hp);
245 }
246
247 /*
248 * Read a string from the minibuffer using a completion.
249 */
250 const_astr
minibuf_vread_completion(const char * fmt,const char * value,Completion cp,History hp,const char * empty_err,bool (* test)(const char * s,gl_list_t completions),const char * invalid_err,va_list ap)251 minibuf_vread_completion (const char *fmt, const char *value, Completion cp,
252 History hp, const char *empty_err,
253 bool (*test) (const char *s, gl_list_t completions),
254 const char *invalid_err, va_list ap)
255 {
256 const_astr ms;
257 char *buf = xvasprintf (fmt, ap);
258
259 for (;;)
260 {
261 ms = term_minibuf_read (buf, value, SIZE_MAX, cp, hp);
262
263 if (ms == NULL) /* Cancelled. */
264 {
265 FUNCALL (keyboard_quit);
266 break;
267 }
268 else if (astr_len (ms) == 0)
269 {
270 minibuf_error ("%s", empty_err);
271 ms = NULL;
272 break;
273 }
274 else
275 {
276 astr as = astr_cpy (astr_new (), ms);
277 /* Complete partial words if possible. */
278 if (completion_try (cp, as, false) == completion_matched)
279 ms = astr_new_cstr (get_completion_match (cp));
280
281 if (test (astr_cstr (ms), get_completion_completions (cp)))
282 {
283 if (hp)
284 add_history_element (hp, astr_cstr (ms));
285 minibuf_clear ();
286 break;
287 }
288 else
289 {
290 #pragma GCC diagnostic push
291 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
292 minibuf_error (invalid_err, astr_cstr (ms));
293 #pragma GCC diagnostic pop
294 waitkey ();
295 }
296 }
297 }
298
299 return ms;
300 }
301
302 /*
303 * Clear the minibuffer.
304 */
305 void
minibuf_clear(void)306 minibuf_clear (void)
307 {
308 term_minibuf_write ("");
309 }
310