*** empty log message ***
[bpt/guile.git] / guile-readline / readline.c
CommitLineData
c374ab69
MV
1/* readline.c --- line editing support for Guile */
2
3/* Copyright (C) 1997,1999 Free Software Foundation, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this software; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 */
20\f
21
22#if defined (HAVE_RL_GETC_FUNCTION)
23#include <libguile.h>
24#include <readline.h>
25#include <guile/gh.h>
26#include <readline/readline.h>
27#include <readline/history.h>
28
29#include <sys/time.h>
30#include <libguile/iselect.h>
31
32
33scm_option scm_readline_opts[] = {
34 { SCM_OPTION_BOOLEAN, "history-file", 1,
35 "Use history file." },
36 { SCM_OPTION_INTEGER, "history-length", 200,
37 "History length." },
38 { SCM_OPTION_INTEGER, "bounce-parens", 500,
39 "Time (ms) to show matching opening parenthesis (0 = off)."}
40};
41
42extern void stifle_history (int max);
43
44SCM_PROC (s_readline_options, "readline-options-interface", 0, 1, 0, scm_readline_options);
45
46SCM
47scm_readline_options (setting)
48 SCM setting;
49{
50 SCM ans = scm_options (setting,
51 scm_readline_opts,
52 SCM_N_READLINE_OPTIONS,
53 s_readline_options);
54 stifle_history (SCM_HISTORY_LENGTH);
55 return ans;
56}
57
58#ifndef HAVE_STRDUP
59static char *
60strdup (char *s)
61{
62 int len = strlen (s);
63 char *new = malloc (len + 1);
64 strcpy (new, s);
65 return new;
66}
67#endif /* HAVE_STRDUP */
68
69#ifndef HAVE_RL_CLEANUP_AFTER_SIGNAL
70
71/* These are readline functions added in release 2.3. They will work
72 * together with readline-2.1 and 2.2. (The readline interface is
73 * disabled for earlier releases.)
74 * They are declared static; if we want to use them elsewhere, then
75 * we need external declarations for them, but at the moment, I don't
76 * think anything else in Guile ought to use these.
77 */
78
79extern void _rl_clean_up_for_exit ();
80extern void _rl_kill_kbd_macro ();
81extern int _rl_init_argument ();
82
83static void
84rl_cleanup_after_signal ()
85{
86#ifdef HAVE_RL_CLEAR_SIGNALS
87 _rl_clean_up_for_exit ();
88#endif
89 (*rl_deprep_term_function) ();
90#ifdef HAVE_RL_CLEAR_SIGNALS
91 rl_clear_signals ();
92#endif
93 rl_pending_input = 0;
94}
95
96static void
97rl_free_line_state ()
98{
99 register HIST_ENTRY *entry;
100
101 free_undo_list ();
102
103 entry = current_history ();
104 if (entry)
105 entry->data = (char *)NULL;
106
107 _rl_kill_kbd_macro ();
108 rl_clear_message ();
109 _rl_init_argument ();
110}
111
112#endif /* !HAVE_RL_CLEANUP_AFTER_SIGNAL */
113
114static int promptp;
115static SCM input_port;
116static SCM before_read;
117
118static int
119current_input_getc (FILE *in)
120{
121 SCM ans;
122 if (promptp && SCM_NIMP (before_read))
123 {
124 scm_apply (before_read, SCM_EOL, SCM_EOL);
125 promptp = 0;
126 }
127 ans = scm_getc (input_port);
128 return ans;
129}
130
131static void
132redisplay ()
133{
134 rl_redisplay ();
135 /* promptp = 1; */
136}
137
138SCM_PROC (s_readline, "readline", 0, 4, 0, scm_readline);
139
140static int in_readline = 0;
141#ifdef USE_THREADS
142static scm_mutex_t reentry_barrier_mutex;
143#endif
144
145static void
146reentry_barrier ()
147{
148 int reentryp = 0;
149#ifdef USE_THREADS
150 /* We should rather use scm_mutex_try_lock when it becomes available */
151 scm_mutex_lock (&reentry_barrier_mutex);
152#endif
153 if (in_readline)
154 reentryp = 1;
155 else
156 ++in_readline;
157#ifdef USE_THREADS
158 scm_mutex_unlock (&reentry_barrier_mutex);
159#endif
160 if (reentryp)
161 scm_misc_error (s_readline, "readline is not reentrant", SCM_EOL);
162}
163
164static SCM
165handle_error (void *data, SCM tag, SCM args)
166{
167 rl_free_line_state ();
168 rl_cleanup_after_signal ();
169 fclose (rl_instream);
170 fclose (rl_outstream);
171 --in_readline;
172 scm_handle_by_throw (data, tag, args);
173 return SCM_UNSPECIFIED; /* never reached */
174}
175
176static SCM
177internal_readline (SCM text)
178{
179 SCM ret;
180 char *s;
181 char *prompt = SCM_UNBNDP (text) ? "" : SCM_CHARS (text);
182
183 promptp = 1;
184 s = readline (prompt);
185 if (s)
186 ret = scm_makfrom0str (s);
187 else
188 ret = SCM_EOF_VAL;
189
190 free (s);
191
192 return ret;
193}
194
195static FILE *
196stream_from_fport (SCM port, char *mode, const char *subr)
197{
198 int fd;
199 FILE *f;
200
201 fd = dup (((struct scm_fport *) SCM_STREAM (port))->fdes);
202 if (fd == -1)
203 {
204 --in_readline;
205 scm_syserror (subr);
206 }
207
208 f = fdopen (fd, mode);
209 if (f == NULL)
210 {
211 --in_readline;
212 scm_syserror (subr);
213 }
214
215 return f;
216}
217
218SCM
219scm_readline (SCM text, SCM inp, SCM outp, SCM read_hook)
220{
221 SCM ans;
222
223 reentry_barrier ();
224
225 before_read = SCM_BOOL_F;
226
227 if (!SCM_UNBNDP (text))
228 {
229 if (!(SCM_NIMP (text) && SCM_STRINGP (text)))
230 {
231 --in_readline;
232 scm_wrong_type_arg (s_readline, SCM_ARG1, text);
233 }
234 SCM_COERCE_SUBSTR (text);
235 }
236
237 if (SCM_UNBNDP (inp))
238 inp = scm_cur_inp;
239
240 if (SCM_UNBNDP (outp))
241 outp = scm_cur_outp;
242
243 if (!(SCM_UNBNDP (read_hook) || SCM_FALSEP (read_hook)))
244 {
245 if (!(SCM_NFALSEP (scm_thunk_p (read_hook))))
246 {
247 --in_readline;
248 scm_wrong_type_arg (s_readline, SCM_ARG4, read_hook);
249 }
250 before_read = read_hook;
251 }
252
253 if (!(SCM_NIMP (inp) && SCM_OPINFPORTP (inp)))
254 {
255 --in_readline;
256 scm_misc_error (s_readline,
257 "Input port is not open or not a file port",
258 SCM_EOL);
259 }
260 if (!(SCM_NIMP (outp) && SCM_OPOUTFPORTP (outp)))
261 {
262 --in_readline;
263 scm_misc_error (s_readline,
264 "Output port is not open or not a file port",
265 SCM_EOL);
266 }
267
268 input_port = inp;
269 rl_instream = stream_from_fport (inp, "r", s_readline);
270 rl_outstream = stream_from_fport (outp, "w", s_readline);
271
272 ans = scm_internal_catch (SCM_BOOL_T,
273 (scm_catch_body_t) internal_readline,
274 (void *) text,
275 handle_error, 0);
276
277 fclose (rl_instream);
278 fclose (rl_outstream);
279
280 --in_readline;
281 return ans;
282}
283
284SCM_PROC (s_add_history, "add-history", 1, 0, 0, scm_add_history);
285
286SCM
287scm_add_history (SCM text)
288{
289 char* s;
290 SCM_ASSERT ((SCM_NIMP(text) && SCM_STRINGP(text)), text, SCM_ARG1,
291 s_add_history);
292 SCM_COERCE_SUBSTR (text);
293
294 s = SCM_CHARS (text);
295 add_history (strdup (s));
296
297 return SCM_UNSPECIFIED;
298}
299
300
301SCM_PROC (s_read_history, "read-history", 1, 0, 0, scm_read_history);
302
303SCM
304scm_read_history (SCM file)
305{
306 SCM_ASSERT (SCM_NIMP (file) && SCM_STRINGP (file),
307 file, SCM_ARG1, s_read_history);
308 return read_history (SCM_ROCHARS (file)) ? SCM_BOOL_F : SCM_BOOL_T;
309}
310
311
312SCM_PROC (s_write_history, "write-history", 1, 0, 0, scm_write_history);
313
314SCM
315scm_write_history (SCM file)
316{
317 SCM_ASSERT (SCM_NIMP (file) && SCM_STRINGP (file),
318 file, SCM_ARG1, s_write_history);
319 return write_history (SCM_ROCHARS (file)) ? SCM_BOOL_F : SCM_BOOL_T;
320}
321
322
323SCM_PROC (s_filename_completion_function, "filename-completion-function", 2, 0, 0, scm_filename_completion_function);
324
325SCM
326scm_filename_completion_function (SCM text, SCM continuep)
327{
328 char *s;
329 SCM ans;
330 SCM_ASSERT (SCM_NIMP (text) && SCM_STRINGP (text),
331 text,
332 SCM_ARG1,
333 s_filename_completion_function);
334 SCM_COERCE_SUBSTR (text);
335 s = filename_completion_function (SCM_CHARS (text), SCM_NFALSEP (continuep));
336 ans = scm_makfrom0str (s);
337 free (s);
338 return ans;
339}
340
341/*
342 * The following has been modified from code contributed by
343 * Andrew Archibald <aarchiba@undergrad.math.uwaterloo.ca>
344 */
345
346SCM scm_readline_completion_function_var;
347
348static char *
349completion_function (char *text, int continuep)
350{
351 SCM compfunc = SCM_CDR (scm_readline_completion_function_var);
352 SCM res;
353
354 if (SCM_FALSEP (compfunc))
355 return NULL; /* #f => completion disabled */
356 else
357 {
358 SCM t = scm_makfrom0str (text);
359 SCM c = continuep ? SCM_BOOL_T : SCM_BOOL_F;
360 res = scm_apply (compfunc, SCM_LIST2 (t, c), SCM_EOL);
361
362 if (SCM_FALSEP (res))
363 return NULL;
364
365 if (!(SCM_NIMP (res) && SCM_STRINGP (res)))
366 scm_misc_error (s_readline,
367 "Completion function returned bogus value: %S",
368 SCM_LIST1 (res));
369 SCM_COERCE_SUBSTR (res);
370 return strdup (SCM_CHARS (res));
371 }
372}
373
374/*Bouncing parenthesis (reimplemented by GH, 11/23/98, since readline is strict gpl)*/
375
376static void match_paren(int x, int k);
377static int find_matching_paren(int k);
378static void init_bouncing_parens();
379
380static void
381init_bouncing_parens()
382{
383 if(strncmp(rl_get_keymap_name(rl_get_keymap()), "vi", 2)) {
384 rl_bind_key(')', match_paren);
385 rl_bind_key(']', match_paren);
386 rl_bind_key('}', match_paren);
387 }
388}
389
390static int
391find_matching_paren(int k)
392{
393 register int i;
394 register char c = 0;
395 int end_parens_found = 0;
396
397 /* Choose the corresponding opening bracket. */
398 if (k == ')') c = '(';
399 else if (k == ']') c = '[';
400 else if (k == '}') c = '{';
401
402 for (i=rl_point-2; i>=0; i--)
403 {
404 /* Is the current character part of a character literal? */
405 if (i - 2 >= 0
406 && rl_line_buffer[i - 1] == '\\'
407 && rl_line_buffer[i - 2] == '#')
408 ;
409 else if (rl_line_buffer[i] == k)
410 end_parens_found++;
411 else if (rl_line_buffer[i] == '"')
412 {
413 /* Skip over a string literal. */
414 for (i--; i >= 0; i--)
415 if (rl_line_buffer[i] == '"'
416 && ! (i - 1 >= 0
417 && rl_line_buffer[i - 1] == '\\'))
418 break;
419 }
420 else if (rl_line_buffer[i] == c)
421 {
422 if (end_parens_found==0) return i;
423 else --end_parens_found;
424 }
425 }
426 return -1;
427}
428
429static void
430match_paren(int x, int k)
431{
432 int tmp;
433 fd_set readset;
434 struct timeval timeout;
435
436 rl_insert(x, k);
437 if (!SCM_READLINE_BOUNCE_PARENS)
438 return;
439
440 /* Did we just insert a quoted paren? If so, then don't bounce. */
441 if (rl_point - 1 >= 1
442 && rl_line_buffer[rl_point - 2] == '\\')
443 return;
444
445 tmp = 1000 * SCM_READLINE_BOUNCE_PARENS;
446 timeout.tv_sec = tmp / 1000000;
447 timeout.tv_usec = tmp % 1000000;
448 FD_ZERO(&readset);
449 FD_SET(fileno(rl_instream), &readset);
450
451 if(rl_point > 1) {
452 tmp = rl_point;
453 rl_point = find_matching_paren(k);
454 if(rl_point > -1) {
455 rl_redisplay();
456 scm_internal_select(1, &readset, NULL, NULL, &timeout);
457 }
458 rl_point = tmp;
459 }
460}
461
462
463void
464scm_init_readline ()
465{
466#include "readline.x"
467 scm_readline_completion_function_var
468 = scm_sysintern ("*readline-completion-function*", SCM_BOOL_F);
469 rl_getc_function = current_input_getc;
470 rl_redisplay_function = redisplay;
471 rl_completion_entry_function = (Function*) completion_function;
472 rl_basic_word_break_characters = "\t\n\"'`;()";
473#ifdef USE_THREADS
474 scm_mutex_init (&reentry_barrier_mutex);
475#endif
476 scm_init_opts (scm_readline_options,
477 scm_readline_opts,
478 SCM_N_READLINE_OPTIONS);
479 init_bouncing_parens();
480 scm_add_feature ("readline");
481}
482
483#endif