mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-01 04:10:18 +02:00
* libguile/root.h * libguile/root.c (scm_sys_protects): It used to be that for some reason we'd define a special array of "protected" values. This was a little silly, always, but with the BDW GC it's completely unnecessary. Also many of these variables were unused, and none of them were good API. So remove this array, and either eliminate, make static, or make internal the various values. * libguile/snarf.h: No need to generate calls to scm_permanent_object. * guile-readline/readline.c (scm_init_readline): No need to call scm_permanent_object. * libguile/array-map.c (ramap, rafe): Remove the dubious nullvect optimizations. * libguile/async.c (scm_init_async): No need to init scm_asyncs, it is no more. * libguile/eval.c (scm_init_eval): No need to init scm_listofnull, it is no more. * libguile/gc.c: Make scm_protects a static var. (scm_storage_prehistory): Change the sanity check to use the address of protects. (scm_init_gc_protect_object): No need to clear the scm_sys_protects, as it is no more. * libguile/keywords.c: Make the keyword obarray a static var. * libguile/numbers.c: Make flo0 a static var. * libguile/objprop.c: Make object_whash a static var. * libguile/properties.c: Make properties_whash a static var. * libguile/srcprop.h: * libguile/srcprop.c: Make scm_source_whash a global with internal linkage. * libguile/strings.h: * libguile/strings.c: Make scm_nullstr a global with internal linkage. * libguile/vectors.c (scm_init_vectors): No need to init scm_nullvect, it's unused.
574 lines
12 KiB
C
574 lines
12 KiB
C
/* readline.c --- line editing support for Guile */
|
||
|
||
/* Copyright (C) 1997,1999,2000,2001, 2002, 2003, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
||
*
|
||
* This program is free software; you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation; either version 3, or (at your option)
|
||
* any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this software; see the file COPYING. If not, write to
|
||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
* Boston, MA 02110-1301 USA
|
||
*
|
||
*/
|
||
|
||
|
||
|
||
#ifdef HAVE_CONFIG_H
|
||
# include <config.h>
|
||
#endif
|
||
|
||
#ifdef HAVE_RL_GETC_FUNCTION
|
||
#include "libguile.h"
|
||
#include "libguile/iselect.h"
|
||
|
||
#include <stdio.h>
|
||
#ifdef HAVE_UNISTD_H
|
||
#include <unistd.h>
|
||
#endif
|
||
#include <readline/readline.h>
|
||
#include <readline/history.h>
|
||
#ifndef __MINGW32__
|
||
#include <sys/time.h>
|
||
#else
|
||
#include <io.h>
|
||
#endif
|
||
#include <signal.h>
|
||
|
||
#include "libguile/validate.h"
|
||
#include "guile-readline/readline.h"
|
||
|
||
scm_t_option scm_readline_opts[] = {
|
||
{ SCM_OPTION_BOOLEAN, "history-file", 1,
|
||
"Use history file." },
|
||
{ SCM_OPTION_INTEGER, "history-length", 200,
|
||
"History length." },
|
||
{ SCM_OPTION_INTEGER, "bounce-parens", 500,
|
||
"Time (ms) to show matching opening parenthesis (0 = off)."},
|
||
{ 0 }
|
||
};
|
||
|
||
extern void stifle_history (int max);
|
||
|
||
SCM_DEFINE (scm_readline_options, "readline-options-interface", 0, 1, 0,
|
||
(SCM setting),
|
||
"")
|
||
#define FUNC_NAME s_scm_readline_options
|
||
{
|
||
SCM ans = scm_options (setting,
|
||
scm_readline_opts,
|
||
FUNC_NAME);
|
||
stifle_history (SCM_HISTORY_LENGTH);
|
||
return ans;
|
||
}
|
||
#undef FUNC_NAME
|
||
|
||
#ifndef HAVE_STRDUP
|
||
static char *
|
||
strdup (char *s)
|
||
{
|
||
size_t len = strlen (s);
|
||
char *new = malloc (len + 1);
|
||
strcpy (new, s);
|
||
return new;
|
||
}
|
||
#endif /* HAVE_STRDUP */
|
||
|
||
#ifndef HAVE_RL_CLEANUP_AFTER_SIGNAL
|
||
|
||
/* These are readline functions added in release 2.3. They will work
|
||
* together with readline-2.1 and 2.2. (The readline interface is
|
||
* disabled for earlier releases.)
|
||
* They are declared static; if we want to use them elsewhere, then
|
||
* we need external declarations for them, but at the moment, I don't
|
||
* think anything else in Guile ought to use these.
|
||
*/
|
||
|
||
extern void _rl_clean_up_for_exit ();
|
||
extern void _rl_kill_kbd_macro ();
|
||
extern int _rl_init_argument ();
|
||
|
||
void
|
||
rl_cleanup_after_signal ()
|
||
{
|
||
#ifdef HAVE_RL_CLEAR_SIGNALS
|
||
_rl_clean_up_for_exit ();
|
||
#endif
|
||
(*rl_deprep_term_function) ();
|
||
#ifdef HAVE_RL_CLEAR_SIGNALS
|
||
rl_clear_signals ();
|
||
#endif
|
||
rl_pending_input = 0;
|
||
}
|
||
|
||
void
|
||
rl_free_line_state ()
|
||
{
|
||
register HIST_ENTRY *entry;
|
||
|
||
free_undo_list ();
|
||
|
||
entry = current_history ();
|
||
if (entry)
|
||
entry->data = (char *)NULL;
|
||
|
||
_rl_kill_kbd_macro ();
|
||
rl_clear_message ();
|
||
_rl_init_argument ();
|
||
}
|
||
|
||
#endif /* !HAVE_RL_CLEANUP_AFTER_SIGNAL */
|
||
|
||
static int promptp;
|
||
static SCM input_port;
|
||
static SCM output_port;
|
||
static SCM before_read;
|
||
|
||
static int
|
||
current_input_getc (FILE *in SCM_UNUSED)
|
||
{
|
||
if (promptp && scm_is_true (before_read))
|
||
{
|
||
scm_apply (before_read, SCM_EOL, SCM_EOL);
|
||
promptp = 0;
|
||
}
|
||
return scm_get_byte_or_eof (input_port);
|
||
}
|
||
|
||
static int in_readline = 0;
|
||
static SCM reentry_barrier_mutex;
|
||
|
||
static SCM internal_readline (SCM text);
|
||
static SCM handle_error (void *data, SCM tag, SCM args);
|
||
static void reentry_barrier (void);
|
||
|
||
|
||
SCM_DEFINE (scm_readline, "%readline", 0, 4, 0,
|
||
(SCM text, SCM inp, SCM outp, SCM read_hook),
|
||
"")
|
||
#define FUNC_NAME s_scm_readline
|
||
{
|
||
SCM ans;
|
||
|
||
reentry_barrier ();
|
||
|
||
before_read = SCM_BOOL_F;
|
||
|
||
if (!SCM_UNBNDP (text))
|
||
{
|
||
if (!scm_is_string (text))
|
||
{
|
||
--in_readline;
|
||
scm_wrong_type_arg (s_scm_readline, SCM_ARG1, text);
|
||
}
|
||
}
|
||
|
||
if (!((SCM_UNBNDP (inp) && SCM_OPINFPORTP (scm_current_input_port ()))
|
||
|| SCM_OPINFPORTP (inp)))
|
||
{
|
||
--in_readline;
|
||
scm_misc_error (s_scm_readline,
|
||
"Input port is not open or not a file port",
|
||
SCM_EOL);
|
||
}
|
||
|
||
if (!((SCM_UNBNDP (outp) && SCM_OPOUTFPORTP (scm_current_output_port ()))
|
||
|| SCM_OPOUTFPORTP (outp)))
|
||
{
|
||
--in_readline;
|
||
scm_misc_error (s_scm_readline,
|
||
"Output port is not open or not a file port",
|
||
SCM_EOL);
|
||
}
|
||
|
||
if (!(SCM_UNBNDP (read_hook) || scm_is_false (read_hook)))
|
||
{
|
||
if (scm_is_false (scm_thunk_p (read_hook)))
|
||
{
|
||
--in_readline;
|
||
scm_wrong_type_arg (s_scm_readline, SCM_ARG4, read_hook);
|
||
}
|
||
before_read = read_hook;
|
||
}
|
||
|
||
scm_readline_init_ports (inp, outp);
|
||
|
||
ans = scm_internal_catch (SCM_BOOL_T,
|
||
(scm_t_catch_body) internal_readline,
|
||
(void *) SCM_UNPACK (text),
|
||
handle_error, 0);
|
||
|
||
#ifndef __MINGW32__
|
||
fclose (rl_instream);
|
||
fclose (rl_outstream);
|
||
#endif
|
||
|
||
--in_readline;
|
||
return ans;
|
||
}
|
||
#undef FUNC_NAME
|
||
|
||
|
||
static void
|
||
reentry_barrier ()
|
||
{
|
||
int reentryp = 0;
|
||
/* We should rather use scm_try_mutex when it becomes available */
|
||
scm_lock_mutex (reentry_barrier_mutex);
|
||
if (in_readline)
|
||
reentryp = 1;
|
||
else
|
||
++in_readline;
|
||
scm_unlock_mutex (reentry_barrier_mutex);
|
||
if (reentryp)
|
||
scm_misc_error (s_scm_readline, "readline is not reentrant", SCM_EOL);
|
||
}
|
||
|
||
static SCM
|
||
handle_error (void *data, SCM tag, SCM args)
|
||
{
|
||
rl_free_line_state ();
|
||
rl_cleanup_after_signal ();
|
||
fputc ('\n', rl_outstream); /* We don't want next output on this line */
|
||
#ifndef __MINGW32__
|
||
fclose (rl_instream);
|
||
fclose (rl_outstream);
|
||
#endif
|
||
--in_readline;
|
||
scm_handle_by_throw (data, tag, args);
|
||
return SCM_UNSPECIFIED; /* never reached */
|
||
}
|
||
|
||
static SCM
|
||
internal_readline (SCM text)
|
||
{
|
||
SCM ret;
|
||
char *s;
|
||
char *prompt = SCM_UNBNDP (text) ? "" : scm_to_locale_string (text);
|
||
|
||
promptp = 1;
|
||
s = readline (prompt);
|
||
if (s)
|
||
{
|
||
scm_t_port *pt = SCM_PTAB_ENTRY (output_port);
|
||
|
||
ret = scm_from_stringn (s, strlen (s), pt->encoding,
|
||
SCM_FAILED_CONVERSION_ESCAPE_SEQUENCE);
|
||
}
|
||
else
|
||
ret = SCM_EOF_VAL;
|
||
|
||
if (!SCM_UNBNDP (text))
|
||
free (prompt);
|
||
free (s);
|
||
|
||
return ret;
|
||
}
|
||
|
||
static FILE *
|
||
stream_from_fport (SCM port, char *mode, const char *subr)
|
||
{
|
||
int fd;
|
||
FILE *f;
|
||
|
||
fd = dup (((struct scm_t_fport *) SCM_STREAM (port))->fdes);
|
||
if (fd == -1)
|
||
{
|
||
--in_readline;
|
||
scm_syserror (subr);
|
||
}
|
||
|
||
f = fdopen (fd, mode);
|
||
if (f == NULL)
|
||
{
|
||
--in_readline;
|
||
scm_syserror (subr);
|
||
}
|
||
|
||
return f;
|
||
}
|
||
|
||
void
|
||
scm_readline_init_ports (SCM inp, SCM outp)
|
||
{
|
||
if (SCM_UNBNDP (inp))
|
||
inp = scm_current_input_port ();
|
||
|
||
if (SCM_UNBNDP (outp))
|
||
outp = scm_current_output_port ();
|
||
|
||
if (!SCM_OPINFPORTP (inp)) {
|
||
scm_misc_error (0,
|
||
"Input port is not open or not a file port",
|
||
SCM_EOL);
|
||
}
|
||
|
||
if (!SCM_OPOUTFPORTP (outp)) {
|
||
scm_misc_error (0,
|
||
"Output port is not open or not a file port",
|
||
SCM_EOL);
|
||
}
|
||
|
||
input_port = inp;
|
||
output_port = outp;
|
||
#ifndef __MINGW32__
|
||
rl_instream = stream_from_fport (inp, "r", s_scm_readline);
|
||
rl_outstream = stream_from_fport (outp, "w", s_scm_readline);
|
||
#endif
|
||
}
|
||
|
||
|
||
|
||
SCM_DEFINE (scm_add_history, "add-history", 1, 0, 0,
|
||
(SCM text),
|
||
"")
|
||
#define FUNC_NAME s_scm_add_history
|
||
{
|
||
char* s;
|
||
|
||
s = scm_to_locale_string (text);
|
||
add_history (s);
|
||
free (s);
|
||
|
||
return SCM_UNSPECIFIED;
|
||
}
|
||
#undef FUNC_NAME
|
||
|
||
|
||
SCM_DEFINE (scm_read_history, "read-history", 1, 0, 0,
|
||
(SCM file),
|
||
"")
|
||
#define FUNC_NAME s_scm_read_history
|
||
{
|
||
char *filename;
|
||
SCM ret;
|
||
|
||
filename = scm_to_locale_string (file);
|
||
ret = scm_from_bool (!read_history (filename));
|
||
free (filename);
|
||
return ret;
|
||
}
|
||
#undef FUNC_NAME
|
||
|
||
|
||
SCM_DEFINE (scm_write_history, "write-history", 1, 0, 0,
|
||
(SCM file),
|
||
"")
|
||
#define FUNC_NAME s_scm_write_history
|
||
{
|
||
char *filename;
|
||
SCM ret;
|
||
|
||
filename = scm_to_locale_string (file);
|
||
ret = scm_from_bool (!write_history (filename));
|
||
free (filename);
|
||
return ret;
|
||
}
|
||
#undef FUNC_NAME
|
||
|
||
SCM_DEFINE (scm_clear_history, "clear-history", 0, 0, 0,
|
||
(),
|
||
"Clear the history buffer of the readline machinery.")
|
||
#define FUNC_NAME s_scm_clear_history
|
||
{
|
||
clear_history();
|
||
return SCM_UNSPECIFIED;
|
||
}
|
||
#undef FUNC_NAME
|
||
|
||
|
||
SCM_DEFINE (scm_filename_completion_function, "filename-completion-function", 2, 0, 0,
|
||
(SCM text, SCM continuep),
|
||
"")
|
||
#define FUNC_NAME s_scm_filename_completion_function
|
||
{
|
||
char *s;
|
||
SCM ans;
|
||
char *c_text = scm_to_locale_string (text);
|
||
#ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION
|
||
s = rl_filename_completion_function (c_text, scm_is_true (continuep));
|
||
#else
|
||
s = filename_completion_function (c_text, scm_is_true (continuep));
|
||
#endif
|
||
ans = scm_take_locale_string (s);
|
||
free (c_text);
|
||
return ans;
|
||
}
|
||
#undef FUNC_NAME
|
||
|
||
/*
|
||
* The following has been modified from code contributed by
|
||
* Andrew Archibald <aarchiba@undergrad.math.uwaterloo.ca>
|
||
*/
|
||
|
||
SCM scm_readline_completion_function_var;
|
||
|
||
static char *
|
||
completion_function (char *text, int continuep)
|
||
{
|
||
SCM compfunc = SCM_VARIABLE_REF (scm_readline_completion_function_var);
|
||
SCM res;
|
||
|
||
if (scm_is_false (compfunc))
|
||
return NULL; /* #f => completion disabled */
|
||
else
|
||
{
|
||
SCM t = scm_from_locale_string (text);
|
||
SCM c = scm_from_bool (continuep);
|
||
res = scm_apply (compfunc, scm_list_2 (t, c), SCM_EOL);
|
||
|
||
if (scm_is_false (res))
|
||
return NULL;
|
||
|
||
return scm_to_locale_string (res);
|
||
}
|
||
}
|
||
|
||
#if HAVE_RL_GET_KEYMAP
|
||
/*Bouncing parenthesis (reimplemented by GH, 11/23/98, since readline is strict gpl)*/
|
||
|
||
static int match_paren (int x, int k);
|
||
static int find_matching_paren (int k);
|
||
static void init_bouncing_parens ();
|
||
|
||
static void
|
||
init_bouncing_parens ()
|
||
{
|
||
if (strncmp (rl_get_keymap_name (rl_get_keymap ()), "vi", 2))
|
||
{
|
||
rl_bind_key (')', match_paren);
|
||
rl_bind_key (']', match_paren);
|
||
rl_bind_key ('}', match_paren);
|
||
}
|
||
}
|
||
|
||
static int
|
||
find_matching_paren(int k)
|
||
{
|
||
register int i;
|
||
register char c = 0;
|
||
int end_parens_found = 0;
|
||
|
||
/* Choose the corresponding opening bracket. */
|
||
if (k == ')') c = '(';
|
||
else if (k == ']') c = '[';
|
||
else if (k == '}') c = '{';
|
||
|
||
for (i=rl_point-2; i>=0; i--)
|
||
{
|
||
/* Is the current character part of a character literal? */
|
||
if (i - 2 >= 0
|
||
&& rl_line_buffer[i - 1] == '\\'
|
||
&& rl_line_buffer[i - 2] == '#')
|
||
;
|
||
else if (rl_line_buffer[i] == k)
|
||
end_parens_found++;
|
||
else if (rl_line_buffer[i] == '"')
|
||
{
|
||
/* Skip over a string literal. */
|
||
for (i--; i >= 0; i--)
|
||
if (rl_line_buffer[i] == '"'
|
||
&& ! (i - 1 >= 0
|
||
&& rl_line_buffer[i - 1] == '\\'))
|
||
break;
|
||
}
|
||
else if (rl_line_buffer[i] == c)
|
||
{
|
||
if (end_parens_found==0)
|
||
return i;
|
||
else --end_parens_found;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
static int
|
||
match_paren (int x, int k)
|
||
{
|
||
int tmp;
|
||
#ifndef __MINGW32__
|
||
int fno;
|
||
SELECT_TYPE readset;
|
||
struct timeval timeout;
|
||
#endif
|
||
|
||
rl_insert (x, k);
|
||
if (!SCM_READLINE_BOUNCE_PARENS)
|
||
return 0;
|
||
|
||
/* Did we just insert a quoted paren? If so, then don't bounce. */
|
||
if (rl_point - 1 >= 1
|
||
&& rl_line_buffer[rl_point - 2] == '\\')
|
||
return 0;
|
||
|
||
#ifndef __MINGW32__
|
||
tmp = 1000 * SCM_READLINE_BOUNCE_PARENS;
|
||
timeout.tv_sec = tmp / 1000000;
|
||
timeout.tv_usec = tmp % 1000000;
|
||
FD_ZERO (&readset);
|
||
fno = fileno (rl_instream);
|
||
FD_SET (fno, &readset);
|
||
#endif
|
||
|
||
if (rl_point > 1)
|
||
{
|
||
tmp = rl_point;
|
||
rl_point = find_matching_paren (k);
|
||
if (rl_point > -1)
|
||
{
|
||
rl_redisplay ();
|
||
#ifndef __MINGW32__
|
||
scm_std_select (fno + 1, &readset, NULL, NULL, &timeout);
|
||
#else
|
||
WaitForSingleObject (GetStdHandle(STD_INPUT_HANDLE),
|
||
SCM_READLINE_BOUNCE_PARENS);
|
||
#endif
|
||
}
|
||
rl_point = tmp;
|
||
}
|
||
return 0;
|
||
}
|
||
#endif /* HAVE_RL_GET_KEYMAP */
|
||
|
||
#endif /* HAVE_RL_GETC_FUNCTION */
|
||
|
||
void
|
||
scm_init_readline ()
|
||
{
|
||
#ifdef HAVE_RL_GETC_FUNCTION
|
||
#include "guile-readline/readline.x"
|
||
scm_readline_completion_function_var
|
||
= scm_c_define ("*readline-completion-function*", SCM_BOOL_F);
|
||
#ifndef __MINGW32__
|
||
rl_getc_function = current_input_getc;
|
||
#endif
|
||
#if defined (_RL_FUNCTION_TYPEDEF)
|
||
rl_completion_entry_function = (rl_compentry_func_t*) completion_function;
|
||
#else
|
||
rl_completion_entry_function = (Function*) completion_function;
|
||
#endif
|
||
rl_basic_word_break_characters = "\t\n\"'`;()";
|
||
rl_readline_name = "Guile";
|
||
|
||
reentry_barrier_mutex = scm_make_mutex ();
|
||
scm_init_opts (scm_readline_options,
|
||
scm_readline_opts);
|
||
#if HAVE_RL_GET_KEYMAP
|
||
init_bouncing_parens();
|
||
#endif
|
||
scm_add_feature ("readline");
|
||
#endif /* HAVE_RL_GETC_FUNCTION */
|
||
}
|
||
|
||
/*
|
||
Local Variables:
|
||
c-file-style: "gnu"
|
||
End:
|
||
*/
|