1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-01 04:10:18 +02:00
guile/guile-readline/readline.c
Andy Wingo e7efe8e793 decruftify scm_sys_protects
* 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.
2009-12-05 12:38:43 +01:00

574 lines
12 KiB
C
Raw 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, 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:
*/