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