Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1511 lines (1394 sloc) 39.86 kb
/************************************************
readline.c - GNU Readline module
$Author: akr $
created at: Wed Jan 20 13:59:32 JST 1999
Copyright (C) 1997-2008 Shugo Maeda
Copyright (C) 2008-2009 TAKAO Kouji
$Id: readline.c 25189 2009-10-02 12:04:37Z akr $
Contact:
- TAKAO Kouji <kouji at takao7 dot net> (current maintainer)
************************************************/
#ifdef RUBY_EXTCONF_H
#include RUBY_EXTCONF_H
#endif
#include "ruby/config.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_READLINE_READLINE_H
#include <readline/readline.h>
#endif
#ifdef HAVE_READLINE_HISTORY_H
#include <readline/history.h>
#endif
#ifdef HAVE_EDITLINE_READLINE_H
#include <editline/readline.h>
#endif
#include "macruby_internal.h"
#include "ruby/io.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifndef GetOpenFile
#define GetOpenFile(obj, var) var = RFILE(rb_io_taint_check(obj))->fptr
#endif
#if WITH_OBJC
/* We cannot use the GC memory functions here because the underlying libedit
* function will call free() on the memory, resulting in a leak.
*/
# undef ALLOC_N
# define ALLOC_N(type,n) ((type *)malloc(sizeof(type) * (n)))
# undef REALLOC_N
# define REALLOC_N(var,type,n) \
(var)=(type*)realloc((char*)(var),(n) * sizeof(type))
#endif
static VALUE mReadline;
#define EDIT_LINE_LIBRARY_VERSION "EditLine wrapper"
#define COMPLETION_PROC "completion_proc"
#define COMPLETION_CASE_FOLD "completion_case_fold"
static ID completion_proc, completion_case_fold;
#ifndef HAVE_RL_FILENAME_COMPLETION_FUNCTION
# define rl_filename_completion_function filename_completion_function
#endif
#ifndef HAVE_RL_USERNAME_COMPLETION_FUNCTION
# define rl_username_completion_function username_completion_function
#endif
#ifndef HAVE_RL_COMPLETION_MATCHES
# define rl_completion_matches completion_matches
#endif
static int (*history_get_offset_func)(int);
static char **readline_attempted_completion_function(const char *text,
int start, int end);
#if WITH_OBJC
#define OutputStringValue(str) SafeStringValue(str)
#else
#define OutputStringValue(str) do {\
SafeStringValue(str);\
str = rb_str_conv_enc(str, rb_enc_get(str), rb_locale_encoding());\
} while (0)\
#endif
#if WITH_OBJC
#define rb_locale_str_new_cstr(str) rb_tainted_str_new2(str)
#define rb_locale_str_new(str, len) rb_tainted_str_new(str, len)
#define rb_str_new_cstr(str) rb_tainted_str_new2(str)
#endif
/*
* Document-class: Readline
*
* The Readline module provides interface for GNU Readline.
* This module defines a number of methods to facilitate completion
* and accesses input history from the Ruby interpreter.
* This module supported Edit Line(libedit) too.
* libedit is compatible with GNU Readline.
*
* GNU Readline:: http://www.gnu.org/directory/readline.html
* libedit:: http://www.thrysoee.dk/editline/
*
* Reads one inputted line with line edit by Readline.readline method.
* At this time, the facilitatation completion and the key
* bind like Emacs can be operated like GNU Readline.
*
* require "readline"
* while buf = Readline.readline("> ", true)
* p buf
* end
*
* The content that the user input can be recorded to the history.
* The history can be accessed by Readline::HISTORY constant.
*
* require "readline"
* while buf = Readline.readline("> ", true)
* p Readline::HISTORY.to_a
* print("-> ", buf, "\n")
* end
*
* Most of methods raise SecurityError exception if $SAFE is 4.
*
* Documented by TAKAO Kouji <kouji at takao7 dot net>.
*/
#if defined HAVE_RL_GETC_FUNCTION
static VALUE readline_instream;
static ID id_getbyte;
int rl_getc(FILE *);
static int readline_getc(FILE *);
static int
readline_getc(FILE *input)
{
rb_io_t *ifp = 0;
VALUE c;
if (!readline_instream) return rl_getc(input);
GetOpenFile(readline_instream, ifp);
#if WITH_OBJC
if (fileno(rl_instream) != ifp->fd) return rl_getc(input);
#else
if (rl_instream != ifp->stdio_file) return rl_getc(input);
#endif
c = rb_funcall(readline_instream, id_getbyte, 0, 0);
if (NIL_P(c)) return EOF;
return NUM2CHR(c);
}
#elif defined HAVE_RL_EVENT_HOOK
#define BUSY_WAIT 0
static int readline_event(void);
static int
readline_event(void)
{
#if BUSY_WAIT
rb_thread_schedule();
#else
fd_set rset;
FD_ZERO(&rset);
FD_SET(fileno(rl_instream), &rset);
rb_thread_select(fileno(rl_instream) + 1, &rset, NULL, NULL, NULL);
return 0;
#endif
}
#endif
static VALUE
readline_get(VALUE prompt)
{
return (VALUE)readline((char *)prompt);
}
/*
* call-seq:
* Readline.readline(prompt = "", add_hist = false) -> string or nil
*
* Shows the +prompt+ and reads the inputted line with line editing.
* The inputted line is added to the history if +add_hist+ is true.
*
* Returns nil when the inputted line is empty and user inputs EOF
* (Presses ^D on UNIX).
*
* Raises IOError exception if below conditions are satisfied.
* 1. stdin is not tty.
* 2. stdin was closed. (errno is EBADF after called isatty(2).)
*
* This method supports thread. Switchs the thread context when waits
* inputting line.
*
* Supports line edit when inputs line. Provides VI and Emacs editing mode.
* Default is Emacs editing mode.
*
* NOTE: Terminates ruby interpreter and does not return the terminal
* status after user pressed '^C' when wait inputting line.
* Give 3 examples that avoid it.
*
* * Catches the Interrupt exception by pressed ^C after returns
* terminal status:
*
* require "readline"
*
* stty_save = `stty -g`.chomp
* begin
* while buf = Readline.readline
* p buf
* end
* rescue Interrupt
* system("stty", stty_save)
* exit
* end
* end
* end
*
* * Catches the INT signal by pressed ^C after returns terminal
* status:
*
* require "readline"
*
* stty_save = `stty -g`.chomp
* trap("INT") { system "stty", stty_save; exit }
*
* while buf = Readline.readline
* p buf
* end
*
* * Ignores pressing ^C:
*
* require "readline"
*
* trap("INT", "SIG_IGN")
*
* while buf = Readline.readline
* p buf
* end
*
* Can make as follows with Readline::HISTORY constant.
* It does not record to the history if the inputted line is empty or
* the same it as last one.
*
* require "readline"
*
* while buf = Readline.readline("> ", true)
* # p Readline::HISTORY.to_a
* Readline::HISTORY.pop if /^\s*$/ =~ buf
*
* begin
* if Readline::HISTORY[Readline::HISTORY.length-2] == buf
* Readline::HISTORY.pop
* end
* rescue IndexError
* end
*
* # p Readline::HISTORY.to_a
* print "-> ", buf, "\n"
* end
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_readline(VALUE self, SEL sel, int argc, VALUE *argv)
{
VALUE tmp, add_hist, result;
const char *prompt = NULL;
char *buff;
int status;
rb_secure(4);
if (rb_scan_args(argc, argv, "02", &tmp, &add_hist) > 0) {
OutputStringValue(tmp);
prompt = RSTRING_PTR(tmp);
}
if (!isatty(0) && errno == EBADF) rb_raise(rb_eIOError, "closed stdin");
#ifdef _WIN32
rl_prep_terminal(1);
#endif
buff = (char*)rb_protect((VALUE(*)_((VALUE)))readline_get, (VALUE)prompt,
&status);
if (status) {
#if defined HAVE_RL_CLEANUP_AFTER_SIGNAL
/* restore terminal mode and signal handler*/
rl_free_line_state();
rl_cleanup_after_signal();
#elif defined HAVE_RL_DEPREP_TERM_FUNCTION
/* restore terminal mode */
if (rl_deprep_term_function != NULL) /* NULL in libedit. [ruby-dev:29116] */
(*rl_deprep_term_function)();
else
#else
rl_deprep_terminal();
#endif
rb_jump_tag(status);
}
if (RTEST(add_hist) && buff) {
add_history(buff);
}
if (buff) {
result = rb_locale_str_new_cstr(buff);
}
else
result = Qnil;
if (buff) free(buff);
return result;
}
/*
* call-seq:
* Readline.input = input
*
* Specifies a File object +input+ that is input stream for
* Readline.readline method.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_set_input(VALUE self, SEL sel, VALUE input)
{
rb_io_t *ifp;
rb_secure(4);
Check_Type(input, T_FILE);
GetOpenFile(input, ifp);
#if WITH_OBJC
rl_instream = fdopen(ifp->fd, "r+");
#else
rl_instream = rb_io_stdio_file(ifp);
#endif
#ifdef HAVE_RL_GETC_FUNCTION
readline_instream = input;
#endif
return input;
}
/*
* call-seq:
* Readline.output = output
*
* Specifies a File object +output+ that is output stream for
* Readline.readline method.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_set_output(VALUE self, SEL sel, VALUE output)
{
rb_io_t *ofp;
rb_secure(4);
Check_Type(output, T_FILE);
GetOpenFile(output, ofp);
#if WITH_OBJC
rl_outstream = fdopen(ofp->fd, "w+");
#else
rl_outstream = rb_io_stdio_file(ofp);
#endif
return output;
}
/*
* call-seq:
* Readline.completion_proc = proc
*
* Specifies a Proc object +proc+ to determine completion behavior. It
* should take input-string, and return an array of completion
* candidates.
*
* Set default if +proc+ is nil.
*
* Raises ArgumentError exception if +proc+ does not respond to call method.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_set_completion_proc(VALUE self, SEL sel, VALUE proc)
{
rb_secure(4);
if (!NIL_P(proc) && !rb_respond_to(proc, rb_intern("call")))
rb_raise(rb_eArgError, "argument must respond to `call'");
return rb_ivar_set(mReadline, completion_proc, proc);
}
/*
* call-seq:
* Readline.completion_proc -> proc
*
* Returns the completion Proc object.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_get_completion_proc(VALUE self, SEL sel)
{
rb_secure(4);
return rb_attr_get(mReadline, completion_proc);
}
/*
* call-seq:
* Readline.completion_case_fold = bool
*
* Sets whether or not to ignore case on completion.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_set_completion_case_fold(VALUE self, SEL sel, VALUE val)
{
rb_secure(4);
return rb_ivar_set(mReadline, completion_case_fold, val);
}
/*
* call-seq:
* Readline.completion_case_fold -> bool
*
* Returns true if completion ignores case. If no, returns false.
*
* NOTE: Returns the same object that is specified by
* Readline.completion_case_fold= method.
*
* require "readline"
*
* Readline.completion_case_fold = "This is a String."
* p Readline.completion_case_fold # => "This is a String."
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_get_completion_case_fold(VALUE self, SEL sel)
{
rb_secure(4);
return rb_attr_get(mReadline, completion_case_fold);
}
#ifdef HAVE_RL_LINE_BUFFER
/*
* call-seq:
* Readline.line_buffer -> string
*
* Returns the full line that is being edited. This is useful from
* within the complete_proc for determining the context of the
* completion request.
*
* The length of +Readline.line_buffer+ and GNU Readline's rl_end are
* same.
*/
static VALUE
readline_s_get_line_buffer(VALUE self, SEL sel)
{
rb_secure(4);
if (rl_line_buffer == NULL)
return Qnil;
return rb_tainted_str_new2(rl_line_buffer);
}
#else
#define readline_s_get_line_buffer rb_f_notimplement
#endif
#ifdef HAVE_RL_POINT
/*
* call-seq:
* Readline.point -> int
*
* Returns the index of the current cursor position in
* +Readline.line_buffer+.
*
* The index in +Readline.line_buffer+ which matches the start of
* input-string passed to completion_proc is computed by subtracting
* the length of input-string from +Readline.point+.
*
* start = (the length of input-string) - Readline.point
*/
static VALUE
readline_s_get_point(VALUE self, SEL sel)
{
rb_secure(4);
return INT2NUM(rl_point);
}
#else
#define readline_s_get_point rb_f_notimplement
#endif
static char **
readline_attempted_completion_function(const char *text, int start, int end)
{
VALUE proc, ary, temp;
char **result;
int case_fold;
long i, matches;
proc = rb_attr_get(mReadline, completion_proc);
if (NIL_P(proc))
return NULL;
#ifdef HAVE_RL_ATTEMPTED_COMPLETION_OVER
rl_attempted_completion_over = 1;
#endif
case_fold = RTEST(rb_attr_get(mReadline, completion_case_fold));
ary = rb_funcall(proc, rb_intern("call"), 1, rb_locale_str_new_cstr(text));
if (TYPE(ary) != T_ARRAY)
ary = rb_Array(ary);
matches = RARRAY_LEN(ary);
if (matches == 0)
return NULL;
result = ALLOC_N(char *, matches + 2);
for (i = 0; i < matches; i++) {
temp = rb_obj_as_string(RARRAY_PTR(ary)[i]);
result[i + 1] = ALLOC_N(char, RSTRING_LEN(temp) + 1);
strcpy(result[i + 1], RSTRING_PTR(temp));
}
result[matches + 1] = NULL;
if (matches == 1) {
result[0] = strdup(result[1]);
}
else {
register int i = 1;
int low = 100000;
while (i < matches) {
register int c1, c2, si;
if (case_fold) {
for (si = 0;
(c1 = TOLOWER(result[i][si])) &&
(c2 = TOLOWER(result[i + 1][si]));
si++)
if (c1 != c2) break;
} else {
for (si = 0;
(c1 = result[i][si]) &&
(c2 = result[i + 1][si]);
si++)
if (c1 != c2) break;
}
if (low > si) low = si;
i++;
}
result[0] = ALLOC_N(char, low + 1);
strncpy(result[0], result[1], low);
result[0][low] = '\0';
}
return result;
}
#ifdef HAVE_RL_SET_SCREEN_SIZE
/*
* call-seq:
* Readline.set_screen_size(rows, columns) -> self
*
* Set terminal size to +rows+ and +columns+.
*
* See GNU Readline's rl_set_screen_size function.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_set_screen_size(VALUE self, SEL sel, VALUE rows, VALUE columns)
{
rb_secure(4);
rl_set_screen_size(NUM2INT(rows), NUM2INT(columns));
return self;
}
#else
#define readline_s_set_screen_size rb_f_notimplement
#endif
#ifdef HAVE_RL_GET_SCREEN_SIZE
/*
* call-seq:
* Readline.get_screen_size -> [rows, columns]
*
* Returns the terminal's rows and columns.
*
* See GNU Readline's rl_get_screen_size function.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_get_screen_size(VALUE self, SEL sel)
{
int rows, columns;
VALUE res;
rb_secure(4);
rl_get_screen_size(&rows, &columns);
res = rb_ary_new();
rb_ary_push(res, INT2NUM(rows));
rb_ary_push(res, INT2NUM(columns));
return res;
}
#else
#define readline_s_get_screen_size rb_f_notimplement
#endif
#ifdef HAVE_RL_VI_EDITING_MODE
/*
* call-seq:
* Readline.vi_editing_mode -> nil
*
* Specifies VI editing mode. See the manual of GNU Readline for
* details of VI editing mode.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_vi_editing_mode(VALUE self, SEL sel)
{
rb_secure(4);
rl_vi_editing_mode(1,0);
return Qnil;
}
#else
#define readline_s_vi_editing_mode rb_f_notimplement
#endif
#ifdef HAVE_RL_EDITING_MODE
/*
* call-seq:
* Readline.vi_editing_mode? -> bool
*
* Returns true if vi mode is active. Returns false if not.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_vi_editing_mode_p(VALUE self, SEL sel)
{
rb_secure(4);
return rl_editing_mode == 0 ? Qtrue : Qfalse;
}
#else
#define readline_s_vi_editing_mode_p rb_f_notimplement
#endif
#ifdef HAVE_RL_EMACS_EDITING_MODE
/*
* call-seq:
* Readline.emacs_editing_mode -> nil
*
* Specifies Emacs editing mode. The default is this mode. See the
* manual of GNU Readline for details of Emacs editing mode.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_emacs_editing_mode(VALUE self, SEL sel)
{
rb_secure(4);
rl_emacs_editing_mode(1,0);
return Qnil;
}
#else
#define readline_s_emacs_editing_mode rb_f_notimplement
#endif
#ifdef HAVE_RL_EDITING_MODE
/*
* call-seq:
* Readline.emacs_editing_mode? -> bool
*
* Returns true if emacs mode is active. Returns false if not.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_emacs_editing_mode_p(VALUE self, SEL sel)
{
rb_secure(4);
return rl_editing_mode == 1 ? Qtrue : Qfalse;
}
#else
#define readline_s_emacs_editing_mode_p rb_f_notimplement
#endif
#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
/*
* call-seq:
* Readline.completion_append_character = char
*
* Specifies a character to be appended on completion.
* Nothing will be appended if an empty string ("") or nil is
* specified.
*
* For example:
* require "readline"
*
* Readline.readline("> ", true)
* Readline.completion_append_character = " "
*
* Result:
* >
* Input "/var/li".
*
* > /var/li
* Press TAB key.
*
* > /var/lib
* Completes "b" and appends " ". So, you can continuously input "/usr".
*
* > /var/lib /usr
*
* NOTE: Only one character can be specified. When "string" is
* specified, sets only "s" that is the first.
*
* require "readline"
*
* Readline.completion_append_character = "string"
* p Readline.completion_append_character # => "s"
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_set_completion_append_character(VALUE self, SEL sel, VALUE str)
{
rb_secure(4);
if (NIL_P(str)) {
rl_completion_append_character = '\0';
}
else {
OutputStringValue(str);
if (RSTRING_LEN(str) == 0) {
rl_completion_append_character = '\0';
} else {
rl_completion_append_character = RSTRING_PTR(str)[0];
}
}
return self;
}
#else
#define readline_s_set_completion_append_character rb_f_notimplement
#endif
#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
/*
* call-seq:
* Readline.completion_append_character -> char
*
* Returns a string containing a character to be appended on
* completion. The default is a space (" ").
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_get_completion_append_character(VALUE self, SEL sel)
{
char buf[1];
rb_secure(4);
if (rl_completion_append_character == '\0')
return Qnil;
buf[0] = (char) rl_completion_append_character;
return rb_locale_str_new(buf, 1);
}
#else
#define readline_s_get_completion_append_character rb_f_notimplement
#endif
#ifdef HAVE_RL_BASIC_WORD_BREAK_CHARACTERS
/*
* call-seq:
* Readline.basic_word_break_characters = string
*
* Sets the basic list of characters that signal a break between words
* for the completer routine. The default is the characters which
* break words for completion in Bash: "\t\n\"\\'`@$><=;|&{(".
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_set_basic_word_break_characters(VALUE self, SEL sel, VALUE str)
{
static char *basic_word_break_characters = NULL;
rb_secure(4);
OutputStringValue(str);
if (basic_word_break_characters == NULL) {
basic_word_break_characters =
ALLOC_N(char, RSTRING_LEN(str) + 1);
}
else {
REALLOC_N(basic_word_break_characters, char, RSTRING_LEN(str) + 1);
}
strncpy(basic_word_break_characters,
RSTRING_PTR(str), RSTRING_LEN(str));
basic_word_break_characters[RSTRING_LEN(str)] = '\0';
rl_basic_word_break_characters = basic_word_break_characters;
return self;
}
#else
#define readline_s_set_basic_word_break_characters rb_f_notimplement
#endif
#ifdef HAVE_RL_BASIC_WORD_BREAK_CHARACTERS
/*
* call-seq:
* Readline.basic_word_break_characters -> string
*
* Gets the basic list of characters that signal a break between words
* for the completer routine.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_get_basic_word_break_characters(VALUE self, SEL sel, VALUE str)
{
rb_secure(4);
if (rl_basic_word_break_characters == NULL)
return Qnil;
return rb_locale_str_new_cstr(rl_basic_word_break_characters);
}
#else
#define readline_s_get_basic_word_break_characters rb_f_notimplement
#endif
#ifdef HAVE_RL_COMPLETER_WORD_BREAK_CHARACTERS
/*
* call-seq:
* Readline.completer_word_break_characters = string
*
* Sets the basic list of characters that signal a break between words
* for rl_complete_internal(). The default is the value of
* Readline.basic_word_break_characters.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_set_completer_word_break_characters(VALUE self, SEL sel, VALUE str)
{
static char *completer_word_break_characters = NULL;
rb_secure(4);
OutputStringValue(str);
if (completer_word_break_characters == NULL) {
completer_word_break_characters =
ALLOC_N(char, RSTRING_LEN(str) + 1);
}
else {
REALLOC_N(completer_word_break_characters, char, RSTRING_LEN(str) + 1);
}
strncpy(completer_word_break_characters,
RSTRING_PTR(str), RSTRING_LEN(str));
completer_word_break_characters[RSTRING_LEN(str)] = '\0';
rl_completer_word_break_characters = completer_word_break_characters;
return self;
}
#else
#define readline_s_set_completer_word_break_characters rb_f_notimplement
#endif
#ifdef HAVE_RL_COMPLETER_WORD_BREAK_CHARACTERS
/*
* call-seq:
* Readline.completer_word_break_characters -> string
*
* Gets the basic list of characters that signal a break between words
* for rl_complete_internal().
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_get_completer_word_break_characters(VALUE self, SEL sel, VALUE str)
{
rb_secure(4);
if (rl_completer_word_break_characters == NULL)
return Qnil;
return rb_locale_str_new_cstr(rl_completer_word_break_characters);
}
#else
#define readline_s_get_completer_word_break_characters rb_f_notimplement
#endif
#ifdef HAVE_RL_BASIC_QUOTE_CHARACTERS
/*
* call-seq:
* Readline.basic_quote_characters = string
*
* Sets a list of quote characters which can cause a word break.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_set_basic_quote_characters(VALUE self, SEL sel, VALUE str)
{
static char *basic_quote_characters = NULL;
rb_secure(4);
OutputStringValue(str);
if (basic_quote_characters == NULL) {
basic_quote_characters =
ALLOC_N(char, RSTRING_LEN(str) + 1);
}
else {
REALLOC_N(basic_quote_characters, char, RSTRING_LEN(str) + 1);
}
strncpy(basic_quote_characters,
RSTRING_PTR(str), RSTRING_LEN(str));
basic_quote_characters[RSTRING_LEN(str)] = '\0';
rl_basic_quote_characters = basic_quote_characters;
return self;
}
#else
#define readline_s_set_basic_quote_characters rb_f_notimplement
#endif
#ifdef HAVE_RL_BASIC_QUOTE_CHARACTERS
/*
* call-seq:
* Readline.basic_quote_characters -> string
*
* Gets a list of quote characters which can cause a word break.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_get_basic_quote_characters(VALUE self, SEL sel, VALUE str)
{
rb_secure(4);
if (rl_basic_quote_characters == NULL)
return Qnil;
return rb_locale_str_new_cstr(rl_basic_quote_characters);
}
#else
#define readline_s_get_basic_quote_characters rb_f_notimplement
#endif
#ifdef HAVE_RL_COMPLETER_QUOTE_CHARACTERS
/*
* call-seq:
* Readline.completer_quote_characters = string
*
* Sets a list of characters which can be used to quote a substring of
* the line. Completion occurs on the entire substring, and within
* the substring Readline.completer_word_break_characters are treated
* as any other character, unless they also appear within this list.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_set_completer_quote_characters(VALUE self, SEL sel, VALUE str)
{
static char *completer_quote_characters = NULL;
rb_secure(4);
OutputStringValue(str);
if (completer_quote_characters == NULL) {
completer_quote_characters =
ALLOC_N(char, RSTRING_LEN(str) + 1);
}
else {
REALLOC_N(completer_quote_characters, char, RSTRING_LEN(str) + 1);
}
strncpy(completer_quote_characters, RSTRING_PTR(str), RSTRING_LEN(str));
completer_quote_characters[RSTRING_LEN(str)] = '\0';
rl_completer_quote_characters = completer_quote_characters;
return self;
}
#else
#define readline_s_set_completer_quote_characters rb_f_notimplement
#endif
#ifdef HAVE_RL_COMPLETER_QUOTE_CHARACTERS
/*
* call-seq:
* Readline.completer_quote_characters -> string
*
* Gets a list of characters which can be used to quote a substring of
* the line.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_get_completer_quote_characters(VALUE self, SEL sel, VALUE str)
{
rb_secure(4);
if (rl_completer_quote_characters == NULL)
return Qnil;
return rb_locale_str_new_cstr(rl_completer_quote_characters);
}
#else
#define readline_s_get_completer_quote_characters rb_f_notimplement
#endif
#ifdef HAVE_RL_FILENAME_QUOTE_CHARACTERS
/*
* call-seq:
* Readline.filename_quote_characters = string
*
* Sets a list of characters that cause a filename to be quoted by the completer
* when they appear in a completed filename. The default is nil.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_set_filename_quote_characters(VALUE self, SEL sel, VALUE str)
{
static char *filename_quote_characters = NULL;
rb_secure(4);
OutputStringValue(str);
if (filename_quote_characters == NULL) {
filename_quote_characters =
ALLOC_N(char, RSTRING_LEN(str) + 1);
}
else {
REALLOC_N(filename_quote_characters, char, RSTRING_LEN(str) + 1);
}
strncpy(filename_quote_characters, RSTRING_PTR(str), RSTRING_LEN(str));
filename_quote_characters[RSTRING_LEN(str)] = '\0';
rl_filename_quote_characters = filename_quote_characters;
return self;
}
#else
#define readline_s_set_filename_quote_characters rb_f_notimplement
#endif
#ifdef HAVE_RL_FILENAME_QUOTE_CHARACTERS
/*
* call-seq:
* Readline.filename_quote_characters -> string
*
* Gets a list of characters that cause a filename to be quoted by the completer
* when they appear in a completed filename.
*
* Raises NotImplementedError if the using readline library does not support.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_get_filename_quote_characters(VALUE self, SEL sel, VALUE str)
{
rb_secure(4);
if (rl_filename_quote_characters == NULL)
return Qnil;
return rb_locale_str_new_cstr(rl_filename_quote_characters);
}
#else
#define readline_s_get_filename_quote_characters rb_f_notimplement
#endif
#ifdef HAVE_RL_REFRESH_LINE
/*
* call-seq:
* Readline.refresh_line -> nil
*
* Clear the current input line.
*
* Raises SecurityError exception if $SAFE is 4.
*/
static VALUE
readline_s_refresh_line(VALUE self, SEL sel)
{
rb_secure(4);
rl_refresh_line(0, 0);
return Qnil;
}
#else
#define readline_s_refresh_line rb_f_notimplement
#endif
static VALUE
hist_to_s(VALUE self, SEL sel)
{
return rb_str_new_cstr("HISTORY");
}
static int
history_get_offset_history_base(int offset)
{
return history_base + offset;
}
static int
history_get_offset_0(int offset)
{
return offset;
}
static VALUE
hist_get(VALUE self, SEL sel, VALUE index)
{
HIST_ENTRY *entry = NULL;
int i;
rb_secure(4);
i = NUM2INT(index);
if (i < 0) {
i += history_length;
}
if (i >= 0) {
entry = history_get(history_get_offset_func(i));
}
if (entry == NULL) {
rb_raise(rb_eIndexError, "invalid index");
}
return rb_locale_str_new_cstr(entry->line);
}
#ifdef HAVE_REPLACE_HISTORY_ENTRY
static VALUE
hist_set(VALUE self, SEL sel, VALUE index, VALUE str)
{
HIST_ENTRY *entry = NULL;
int i;
rb_secure(4);
i = NUM2INT(index);
OutputStringValue(str);
if (i < 0) {
i += history_length;
}
if (i >= 0) {
entry = replace_history_entry(i, RSTRING_PTR(str), NULL);
}
if (entry == NULL) {
rb_raise(rb_eIndexError, "invalid index");
}
return str;
}
#else
#define hist_set rb_f_notimplement
#endif
static VALUE
hist_push(VALUE self, SEL sel, VALUE str)
{
rb_secure(4);
OutputStringValue(str);
add_history(RSTRING_PTR(str));
return self;
}
static VALUE
hist_push_method(VALUE self, SEL sel, int argc, VALUE *argv)
{
VALUE str;
rb_secure(4);
while (argc--) {
str = *argv++;
OutputStringValue(str);
add_history(RSTRING_PTR(str));
}
return self;
}
static VALUE
rb_remove_history(int index)
{
#ifdef HAVE_REMOVE_HISTORY
HIST_ENTRY *entry;
VALUE val;
rb_secure(4);
entry = remove_history(index);
if (entry) {
val = rb_locale_str_new_cstr(entry->line);
free((void *) entry->line);
free(entry);
return val;
}
return Qnil;
#else
rb_notimplement();
return Qnil; /* not reached */
#endif
}
static VALUE
hist_pop(VALUE self, SEL sel)
{
rb_secure(4);
if (history_length > 0) {
return rb_remove_history(history_length - 1);
} else {
return Qnil;
}
}
static VALUE
hist_shift(VALUE self, SEL sel)
{
rb_secure(4);
if (history_length > 0) {
return rb_remove_history(0);
} else {
return Qnil;
}
}
static VALUE
hist_each(VALUE self, SEL sel)
{
HIST_ENTRY *entry;
int i;
RETURN_ENUMERATOR(self, 0, 0);
rb_secure(4);
for (i = 0; i < history_length; i++) {
entry = history_get(history_get_offset_func(i));
if (entry == NULL)
break;
rb_yield(rb_locale_str_new_cstr(entry->line));
}
return self;
}
static VALUE
hist_length(VALUE self, SEL sel)
{
rb_secure(4);
return INT2NUM(history_length);
}
static VALUE
hist_empty_p(VALUE self, SEL sel)
{
rb_secure(4);
return history_length == 0 ? Qtrue : Qfalse;
}
static VALUE
hist_delete_at(VALUE self, SEL sel, VALUE index)
{
int i;
rb_secure(4);
i = NUM2INT(index);
if (i < 0)
i += history_length;
if (i < 0 || i > history_length - 1) {
rb_raise(rb_eIndexError, "invalid index");
}
return rb_remove_history(i);
}
#ifdef HAVE_CLEAR_HISTORY
static VALUE
hist_clear(VALUE self, SEL sel)
{
rb_secure(4);
clear_history();
return self;
}
#else
#define hist_clear rb_f_notimplement
#endif
static VALUE
filename_completion_proc_call(VALUE self, SEL sel, VALUE str)
{
VALUE result;
char **matches;
int i;
matches = rl_completion_matches(StringValuePtr(str),
rl_filename_completion_function);
if (matches) {
result = rb_ary_new();
for (i = 0; matches[i]; i++) {
rb_ary_push(result, rb_locale_str_new_cstr(matches[i]));
free(matches[i]);
}
free(matches);
if (RARRAY_LEN(result) >= 2)
rb_ary_shift(result);
}
else {
result = Qnil;
}
return result;
}
static VALUE
username_completion_proc_call(VALUE self, SEL sel, VALUE str)
{
VALUE result;
char **matches;
int i;
matches = rl_completion_matches(StringValuePtr(str),
rl_username_completion_function);
if (matches) {
result = rb_ary_new();
for (i = 0; matches[i]; i++) {
rb_ary_push(result, rb_locale_str_new_cstr(matches[i]));
free(matches[i]);
}
free(matches);
if (RARRAY_LEN(result) >= 2)
rb_ary_shift(result);
}
else {
result = Qnil;
}
return result;
}
void
Init_readline()
{
VALUE history, fcomp, ucomp, version;
/* Allow conditional parsing of the ~/.inputrc file. */
rl_readline_name = (char *)"Ruby";
using_history();
completion_proc = rb_intern(COMPLETION_PROC);
completion_case_fold = rb_intern(COMPLETION_CASE_FOLD);
mReadline = rb_define_module("Readline");
rb_objc_define_module_function(mReadline, "readline",
readline_readline, -1);
rb_objc_define_method(*(VALUE *)mReadline, "input=",
readline_s_set_input, 1);
rb_objc_define_method(*(VALUE *)mReadline, "output=",
readline_s_set_output, 1);
rb_objc_define_method(*(VALUE *)mReadline, "completion_proc=",
readline_s_set_completion_proc, 1);
rb_objc_define_method(*(VALUE *)mReadline, "completion_proc",
readline_s_get_completion_proc, 0);
rb_objc_define_method(*(VALUE *)mReadline, "completion_case_fold=",
readline_s_set_completion_case_fold, 1);
rb_objc_define_method(*(VALUE *)mReadline, "completion_case_fold",
readline_s_get_completion_case_fold, 0);
rb_objc_define_method(*(VALUE *)mReadline, "line_buffer",
readline_s_get_line_buffer, 0);
rb_objc_define_method(*(VALUE *)mReadline, "point",
readline_s_get_point, 0);
rb_objc_define_method(*(VALUE *)mReadline, "set_screen_size",
readline_s_set_screen_size, 2);
rb_objc_define_method(*(VALUE *)mReadline, "get_screen_size",
readline_s_get_screen_size, 0);
rb_objc_define_method(*(VALUE *)mReadline, "vi_editing_mode",
readline_s_vi_editing_mode, 0);
rb_objc_define_method(*(VALUE *)mReadline, "vi_editing_mode?",
readline_s_vi_editing_mode_p, 0);
rb_objc_define_method(*(VALUE *)mReadline, "emacs_editing_mode",
readline_s_emacs_editing_mode, 0);
rb_objc_define_method(*(VALUE *)mReadline, "emacs_editing_mode?",
readline_s_emacs_editing_mode_p, 0);
rb_objc_define_method(*(VALUE *)mReadline, "completion_append_character=",
readline_s_set_completion_append_character, 1);
rb_objc_define_method(*(VALUE *)mReadline, "completion_append_character",
readline_s_get_completion_append_character, 0);
rb_objc_define_method(*(VALUE *)mReadline, "basic_word_break_characters=",
readline_s_set_basic_word_break_characters, 1);
rb_objc_define_method(*(VALUE *)mReadline, "basic_word_break_characters",
readline_s_get_basic_word_break_characters, 0);
rb_objc_define_method(*(VALUE *)mReadline, "completer_word_break_characters=",
readline_s_set_completer_word_break_characters, 1);
rb_objc_define_method(*(VALUE *)mReadline, "completer_word_break_characters",
readline_s_get_completer_word_break_characters, 0);
rb_objc_define_method(*(VALUE *)mReadline, "basic_quote_characters=",
readline_s_set_basic_quote_characters, 1);
rb_objc_define_method(*(VALUE *)mReadline, "basic_quote_characters",
readline_s_get_basic_quote_characters, 0);
rb_objc_define_method(*(VALUE *)mReadline, "completer_quote_characters=",
readline_s_set_completer_quote_characters, 1);
rb_objc_define_method(*(VALUE *)mReadline, "completer_quote_characters",
readline_s_get_completer_quote_characters, 0);
rb_objc_define_method(*(VALUE *)mReadline, "filename_quote_characters=",
readline_s_set_filename_quote_characters, 1);
rb_objc_define_method(*(VALUE *)mReadline, "filename_quote_characters",
readline_s_get_filename_quote_characters, 0);
rb_objc_define_method(*(VALUE *)mReadline, "refresh_line",
readline_s_refresh_line, 0);
history = rb_obj_alloc(rb_cObject);
rb_extend_object(history, rb_mEnumerable);
rb_objc_define_method(*(VALUE *)history, "to_s", hist_to_s, 0);
rb_objc_define_method(*(VALUE *)history, "[]", hist_get, 1);
rb_objc_define_method(*(VALUE *)history, "[]=", hist_set, 2);
rb_objc_define_method(*(VALUE *)history, "<<", hist_push, 1);
rb_objc_define_method(*(VALUE *)history, "push", hist_push_method, -1);
rb_objc_define_method(*(VALUE *)history, "pop", hist_pop, 0);
rb_objc_define_method(*(VALUE *)history, "shift", hist_shift, 0);
rb_objc_define_method(*(VALUE *)history, "each", hist_each, 0);
rb_objc_define_method(*(VALUE *)history, "length", hist_length, 0);
rb_objc_define_method(*(VALUE *)history, "size", hist_length, 0);
rb_objc_define_method(*(VALUE *)history, "empty?", hist_empty_p, 0);
rb_objc_define_method(*(VALUE *)history, "delete_at", hist_delete_at, 1);
rb_objc_define_method(*(VALUE *)history, "clear", hist_clear, 0);
/*
* The history buffer. It extends Enumerable module, so it behaves
* just like an array.
* For example, gets the fifth content that the user input by
* HISTORY[4].
*/
rb_define_const(mReadline, "HISTORY", history);
fcomp = rb_obj_alloc(rb_cObject);
rb_objc_define_method(rb_singleton_class(fcomp), "call",
filename_completion_proc_call, 1);
/*
* The Object with the call method that is a completion for filename.
* This is sets by Readline.completion_proc= method.
*/
rb_define_const(mReadline, "FILENAME_COMPLETION_PROC", fcomp);
ucomp = rb_obj_alloc(rb_cObject);
rb_objc_define_method(rb_singleton_class(ucomp), "call",
username_completion_proc_call, 1);
/*
* The Object with the call method that is a completion for usernames.
* This is sets by Readline.completion_proc= method.
*/
rb_define_const(mReadline, "USERNAME_COMPLETION_PROC", ucomp);
history_get_offset_func = history_get_offset_history_base;
#if defined HAVE_RL_LIBRARY_VERSION
version = rb_str_new_cstr(rl_library_version);
#if defined HAVE_CLEAR_HISTORY || defined HAVE_REMOVE_HISTORY
if (strncmp(rl_library_version, EDIT_LINE_LIBRARY_VERSION,
strlen(EDIT_LINE_LIBRARY_VERSION)) == 0) {
add_history("1");
if (history_get(history_get_offset_func(0)) == NULL) {
history_get_offset_func = history_get_offset_0;
}
#if !defined HAVE_CLEAR_HISTORY
clear_history();
#else
{
HIST_ENTRY *entry = remove_history(0);
if (entry) {
free((char *)entry->line);
free(entry);
}
}
#endif
}
#endif
#else
version = rb_str_new_cstr("2.0 or prior version");
#endif
/* Version string of GNU Readline or libedit. */
rb_define_const(mReadline, "VERSION", version);
rl_attempted_completion_function = readline_attempted_completion_function;
#if defined HAVE_RL_GETC_FUNCTION
rl_getc_function = (int (*)(FILE *))readline_getc;
#if WITH_OBJC
id_getbyte = rb_intern("getbyte");
#else
id_getbyte = rb_intern_const("getbyte");
#endif
#elif defined HAVE_RL_EVENT_HOOK
rl_event_hook = readline_event;
#endif
#ifdef HAVE_RL_CATCH_SIGNALS
rl_catch_signals = 0;
#endif
#ifdef HAVE_RL_CATCH_SIGWINCH
rl_catch_sigwinch = 0;
#endif
#ifdef HAVE_RL_CLEAR_SIGNALS
rl_clear_signals();
#endif
}
Jump to Line
Something went wrong with that request. Please try again.