1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-30 20:00:19 +02:00
guile/guile-readline/readline.c
Neil Jerram 0ebbcf43c4 Remove AC_SYS_RESTARTABLE_SYSCALLS and related code
As the Autoconf documentation says, "These days portable programs
[...] should not rely on `HAVE_RESTARTABLE_SYSCALLS', since nowadays
whether a system call is restartable is a dynamic issue, not a
configuration-time issue."

In other words, if we ever rely on HAVE_RESTARTABLE_SYSCALLS, we are
at the mercy of any code that Guile happens to be linked with, because
that code could install a signal handler without the SA_RESTART flag,
and then a Guile system call could unexpectedly return EINTR.

The readline part of this goes back to this problem report:
http://sources.redhat.com/ml/guile/2000-05/msg00177.html; and is an
excellent example of the above paragraph.  It was noted during the
discussion that undefining HAVE_RESTARTABLE_SYSCALLS would fix the
problem, but that solution wasn't adopted - I guess because Guile was
still using cooperative threads then (not pthreads) and so there was a
significant concern (whether founded or not) that not using
restartable syscalls (where available) could lead to a loss of
performance.

Now Guile's default mode of operation is with pthreads, where we
already don't assume that HAVE_RESTARTABLE_SYSCALLS is reliable, so
there is no possible further performance loss.  And in any case we
really have no choice, if we want correct operation.

Thanks to Sylvain Beucler for reporting this and suggesting the fix.

* configure.in (AC_SYS_RESTARTABLE_SYSCALLS): Removed.

* doc/ref/posix.texi (Signals): Remove statement that Guile always
  sets SA_RESTART flag.

* guile-readline/configure.in (GUILE_SIGWINCH_SA_RESTART_CLEARED):
  Remove this setting, together with its test code.
  (HAVE_RL_PRE_INPUT_HOOK): Remove this setting and its code, as no
  longer needed.

* guile-readline/readline.c (sigwinch_enable_restart): Removed.
  (scm_init_readline): Remove setting of rl_pre_input_hook.

* libguile/_scm.h (SCM_SYSCALL): Remove the definition that relies on
  HAVE_RESTARTABLE_SYSCALLS.

* libguile/scmsigs.c (scm_sigaction_for_thread): Don't always set the
  SA_RESTART flag if available.  Update docstring accordingly.
  (scm_init_scmsigs): Remove code that sets SA_RESTART flag for all
  signals.

* THANKS: Add Sylvain.
2009-06-23 23:04:41 +01:00

567 lines
12 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* readline.c --- line editing support for Guile */
/* Copyright (C) 1997,1999,2000,2001, 2002, 2003, 2006, 2007, 2008 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
*
*/
/* Include private, configure generated header (i.e. config.h). */
#include "guile-readline-config.h"
#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 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_getc (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)
ret = scm_from_locale_string (s);
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;
#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_permanent_object (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:
*/