diff options
Diffstat (limited to 'ext/readline/readline.c')
-rw-r--r-- | ext/readline/readline.c | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/ext/readline/readline.c b/ext/readline/readline.c new file mode 100644 index 0000000000..9e471195e6 --- /dev/null +++ b/ext/readline/readline.c @@ -0,0 +1,386 @@ +/* readline.c -- GNU Readline module + Copyright (C) 1997-1998 Shugo Maeda */ + +#include <stdio.h> +#include <readline/readline.h> +#include <readline/history.h> + +#include "ruby.h" +#include "rubysig.h" + +static VALUE mReadline; + +#define TOLOWER(c) (isupper(c) ? tolower(c) : c) + +#define COMPLETION_PROC "completion_proc" +#define COMPLETION_CASE_FOLD "completion_case_fold" + +static int +readline_event() +{ + CHECK_INTS; +#ifdef USE_THREAD + rb_thread_schedule(); +#endif +} + +static VALUE +readline_readline(int argc, VALUE *argv, VALUE self) +{ + VALUE tmp, add_hist, result; + char *prompt = NULL; + char *buff; + + if (rb_scan_args(argc, argv, "02", &tmp, &add_hist) > 0) { + prompt = STR2CSTR(tmp); + } + buff = readline(prompt); + if (RTEST(add_hist) && buff) { + add_history(buff); + } + if (buff) + result = rb_str_new2(buff); + else + result = Qnil; + if (buff) free(buff); + return result; +} + +static VALUE +readline_s_set_completion_proc(VALUE self, VALUE proc) +{ + if (!rb_respond_to(proc, rb_intern("call"))) + rb_raise(rb_eArgError, "argument have to respond to `call'"); + return rb_iv_set(mReadline, COMPLETION_PROC, proc); +} + +static VALUE +readline_s_get_completion_proc(VALUE self) +{ + return rb_iv_get(mReadline, COMPLETION_PROC); +} + +static VALUE +readline_s_set_completion_case_fold(VALUE self, VALUE val) +{ + return rb_iv_set(mReadline, COMPLETION_CASE_FOLD, val); +} + +static VALUE +readline_s_get_completion_case_fold(VALUE self) +{ + return rb_iv_get(mReadline, COMPLETION_CASE_FOLD); +} + +static char ** +readline_attempted_completion_function(char *text, int start, int end) +{ + VALUE proc, ary, temp; + char **result; + int case_fold; + int i, matches; + + proc = rb_iv_get(mReadline, COMPLETION_PROC); + rl_attempted_completion_over = 1; + case_fold = RTEST(rb_iv_get(mReadline, COMPLETION_CASE_FOLD)); + ary = rb_funcall(proc, rb_intern("call"), 1, rb_str_new2(text)); + if (TYPE(ary) != T_ARRAY) + ary = rb_Array(ary); + matches = RARRAY(ary)->len; + if (matches == 0) + return NULL; + result = ALLOC_N(char *, matches + 2); + for (i = 0; i < matches; i++) { + temp = rb_obj_as_string(RARRAY(ary)->ptr[i]); + result[i + 1] = ALLOC_N(char, RSTRING(temp)->len + 1); + strcpy(result[i + 1], RSTRING(temp)->ptr); + } + result[matches + 1] = NULL; + + if (matches == 1) { + result[0] = result[1]; + result[1] = NULL; + } 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; +} + +static VALUE +readline_s_vi_editing_mode(VALUE self) +{ + rl_vi_editing_mode(); + return Qnil; +} + +static VALUE +readline_s_emacs_editing_mode(VALUE self) +{ + rl_emacs_editing_mode(); + return Qnil; +} + +static VALUE +hist_to_s(VALUE self) +{ + return rb_str_new2("HISTORY"); +} + +static VALUE +hist_get(VALUE self, VALUE index) +{ + HISTORY_STATE *state; + int i; + + state = history_get_history_state(); + i = NUM2INT(index); + if (i < 0 || i > state->length - 1) { + rb_raise(rb_eIndexError, "Invalid index"); + } + return rb_str_new2(state->entries[i]->line); +} + +static VALUE +hist_set(VALUE self, VALUE index, VALUE str) +{ + HISTORY_STATE *state; + int i; + + state = history_get_history_state(); + i = NUM2INT(index); + if (i < 0 || i > state->length - 1) { + rb_raise(rb_eIndexError, "Invalid index"); + } + replace_history_entry(i, STR2CSTR(str), NULL); + return str; +} + +static VALUE +hist_push(VALUE self, VALUE str) +{ + add_history(STR2CSTR(str)); + return self; +} + +static VALUE +hist_push_method(int argc, VALUE *argv, + VALUE self) +{ + VALUE str; + + while (argc--) { + str = *argv++; + add_history(STR2CSTR(str)); + } + return self; +} + +static VALUE +hist_pop(VALUE self) +{ + HISTORY_STATE *state; + HIST_ENTRY *entry; + + state = history_get_history_state(); + if (state->length > 0) { + entry = remove_history(state->length - 1); + return rb_str_new2(entry->line); + } else { + return Qnil; + } +} + +static VALUE +hist_shift(VALUE self) +{ + HISTORY_STATE *state; + HIST_ENTRY *entry; + + state = history_get_history_state(); + if (state->length > 0) { + entry = remove_history(0); + return rb_str_new2(entry->line); + } else { + return Qnil; + } +} + +static VALUE +hist_each(VALUE self) +{ + HISTORY_STATE *state; + int i; + + state = history_get_history_state(); + for (i = 0; i < state->length; i++) { + rb_yield(rb_str_new2(state->entries[i]->line)); + } + return Qnil; +} + +static VALUE +hist_length(VALUE self) +{ + HISTORY_STATE *state; + + state = history_get_history_state(); + return INT2NUM(state->length); +} + +static VALUE +hist_empty_p(VALUE self) +{ + HISTORY_STATE *state; + + state = history_get_history_state(); + if (state->length == 0) + return Qtrue; + else + return Qfalse; +} + +static VALUE +hist_delete_at(VALUE self, VALUE index) +{ + HISTORY_STATE *state; + HIST_ENTRY *entry; + int i; + + state = history_get_history_state(); + i = NUM2INT(index); + if (i < 0 || i > state->length - 1) { + rb_raise(rb_eIndexError, "Invalid index"); + } + entry = remove_history(NUM2INT(index)); + return rb_str_new2(entry->line); +} + +static VALUE +filename_completion_proc_call(VALUE self, VALUE str) +{ + VALUE result; + char **matches; + int i; + + matches = completion_matches(STR2CSTR(str), + filename_completion_function); + if (matches) { + result = rb_ary_new(); + for (i = 0; matches[i]; i++) { + rb_ary_push(result, rb_str_new2(matches[i])); + free(matches[i]); + } + free(matches); + if (RARRAY(result)->len >= 2) + rb_ary_shift(result); + } + else { + result = Qnil; + } + return result; +} + +static VALUE +username_completion_proc_call(VALUE self, VALUE str) +{ + VALUE result; + char **matches; + int i; + + matches = completion_matches(STR2CSTR(str), + username_completion_function); + if (matches) { + result = rb_ary_new(); + for (i = 0; matches[i]; i++) { + rb_ary_push(result, rb_str_new2(matches[i])); + free(matches[i]); + } + free(matches); + if (RARRAY(result)->len >= 2) + rb_ary_shift(result); + } + else { + result = Qnil; + } + return result; +} + +void +Init_readline(void) +{ + VALUE histary, fcomp, ucomp; + + using_history(); + + mReadline = rb_define_module("Readline"); + rb_define_module_function(mReadline, "readline", + readline_readline, -1); + rb_define_singleton_method(mReadline, "completion_proc=", + readline_s_set_completion_proc, 1); + rb_define_singleton_method(mReadline, "completion_proc", + readline_s_get_completion_proc, 0); + rb_define_singleton_method(mReadline, "completion_case_fold=", + readline_s_set_completion_case_fold, 1); + rb_define_singleton_method(mReadline, "completion_case_fold", + readline_s_get_completion_case_fold, 0); + rb_define_singleton_method(mReadline, "vi_editing_mode", + readline_s_vi_editing_mode, 0); + rb_define_singleton_method(mReadline, "emacs_editing_mode", + readline_s_emacs_editing_mode, 0); + + histary = rb_obj_alloc(rb_cObject); + rb_extend_object(histary, rb_mEnumerable); + rb_define_singleton_method(histary,"to_s", hist_to_s, 0); + rb_define_singleton_method(histary,"[]", hist_get, 1); + rb_define_singleton_method(histary,"[]=", hist_set, 2); + rb_define_singleton_method(histary,"<<", hist_push, 1); + rb_define_singleton_method(histary,"push", hist_push_method, -1); + rb_define_singleton_method(histary,"pop", hist_pop, 0); + rb_define_singleton_method(histary,"shift", hist_shift, 0); + rb_define_singleton_method(histary,"each", hist_each, 0); + rb_define_singleton_method(histary,"length", hist_length, 0); + rb_define_singleton_method(histary,"empty?", hist_empty_p, 0); + rb_define_singleton_method(histary,"delete_at", hist_delete_at, 1); + rb_define_const(mReadline, "HISTORY", histary); + + fcomp = rb_obj_alloc(rb_cObject); + rb_define_singleton_method(fcomp, "call", + filename_completion_proc_call, 1); + rb_define_const(mReadline, "FILENAME_COMPLETION_PROC", fcomp); + + ucomp = rb_obj_alloc(rb_cObject); + rb_define_singleton_method(ucomp, "call", + username_completion_proc_call, 1); + rb_define_const(mReadline, "USERNAME_COMPLETION_PROC", ucomp); + + rl_attempted_completion_function + = (CPPFunction *) readline_attempted_completion_function; + rl_event_hook = readline_event; + rl_clear_signals(); +} |