Skip to content

Commit

Permalink
Add source code of version v3.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladimir Motyka committed Mar 20, 2024
1 parent bd64d15 commit 7ec13b1
Show file tree
Hide file tree
Showing 5 changed files with 1,754 additions and 0 deletions.
370 changes: 370 additions & 0 deletions C-readline.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,370 @@
/*
C-readline.c - readline and history bindings for Lua
This Lua5 module is Copyright (c) 2013, Peter J Billam
www.pjb.com.au
This module is free software; you can redistribute it and/or
modify it under the same terms as Lua5 itself.
*/

#include <lua.h>
#include <lauxlib.h>
/* #include <string.h> strlen() & friends, including strerror */
/* #include <unistd.h> isatty() */

/* --------------- from man readline -------------------- */
#include <stdio.h>
#include <stdlib.h>
/* #include <strings.h> 2.8 20210106 strerror is not in string.h ? */
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>
/* http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html
http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC52
(totally alarming :-( )
http://cnswww.cns.cwru.edu/php/chet/readline/history.html#IDX5
(only moderately alarming)
http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#IDX207
Variable: FILE * rl_instream
The stdio stream from which Readline reads input.
If NULL, Readline defaults to stdin.
Variable: FILE * rl_outstream
The stdio stream to which Readline performs output.
If NULL, Readline defaults to stdout.
http://man7.org/linux/man-pages/man3/ctermid.3.html
http://man7.org/linux/man-pages/man3/fopen.3.html
http://man7.org/linux/man-pages/man3/fileno.3.html
http://man7.org/linux/man-pages/man3/isatty.3.html
*/
/* see Programming in Lua p.233 */
/* apparently a BUG: after being invoked, c_readline leaves SIGWINCH
handling messed up, and the kernel unable to follow further changes
in size; thence also tput, stty size, resize, $COLS $ROWS, etc...
Only xwininfo -id $WINDOWID seems to get up-to-date data.
Surprisingly, rl_catch_sigwinch and rl_cleanup_after_signal have no effect
http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC43
*/

static int c_readline(lua_State *L) { /* prompt in, line out */
size_t len;
const char *prompt = lua_tolstring(L, 1, &len);
char buffer[L_ctermid];
const char *devtty = ctermid(buffer); /* 20130919 1.1 */
FILE *tty_stream;
if (devtty != NULL) {
tty_stream = fopen(devtty, "a+");
if (tty_stream != NULL) {
/* int tty_fd = fileno(tty_stream); */
rl_instream = tty_stream;
rl_outstream = tty_stream;
}
}
/* rl_catch_sigwinch = 0; rl_set_signals(); no effect :-( 1.3 */
char *line = readline(prompt); /* 3.2 it's not a const */
/* rl_cleanup_after_signal(); rl_clear_signals(); no effect :-( 1.3 */
/* lua_pushstring(L, line); */
/* 3.2 did lua_pushstring create a copy of the string ? */
/* lua_pushfstring(L, "%s", line); 3.2 */
if (line == NULL) { /* 3.3 fix by zash.se, Prosody developer */
lua_pushnil(L);
} else {
lua_pushfstring(L, "%s", line);
// lua_pushstring(L, line); should be fine as well
}
if (tty_stream != NULL) { fclose(tty_stream); }
free(line); /* 3.2 fixes memory leak */
return 1;
}

static int c_tabcompletion(lua_State *L) { /* Lua stack: is_on */
int is_on = lua_toboolean(L, 1);
if (is_on) {
rl_bind_key ('\t', rl_complete);
} else {
rl_bind_key ('\t', rl_insert);
}
return 0;
}

static int c_history_length(lua_State *L) { /* void in, length out */
lua_Integer n = history_length;
lua_pushinteger(L, n);
return 1;
}

static int c_using_history(lua_State *L) { /* void in, void out */
using_history();
return 0;
}

static int c_clear_history(lua_State *L) { /* void in, void out */
clear_history();
return 0;
}

static int c_add_history(lua_State *L) { /* Lua stack: str to be added */
size_t len;
const char *str = lua_tolstring(L, 1, &len);
add_history(str);
return 0;
}

static int c_append_history(lua_State *L) { /* num,filename in, rc out */
lua_Integer num = lua_tointeger(L, 1);
size_t len;
const char *filename = lua_tolstring(L, 2, &len);
lua_Integer rc = append_history(num, filename);
lua_pushinteger(L, rc);
return 1;
}

static int c_read_history(lua_State *L) { /* filename in, returncode out */
size_t len;
const char *filename = lua_tolstring(L, 1, &len);
lua_Integer rc = read_history(filename);
lua_pushinteger(L, rc);
return 1;
/* so maybe we should provide access to char *strerror(int errnum); */
}

static int c_strerror(lua_State *L) { /* errnum in, errstr out */
lua_Integer errnum = lua_tointeger(L, 1);
const char * str = strerror(errnum);
lua_pushstring(L, str);
return 1;
}

static int c_stifle_history(lua_State *L) { /* Lua stack: num */
lua_Integer num = lua_tointeger(L, 1);
stifle_history(num);
return 0;
}

/* unused ...
static int c_write_history(lua_State *L) { // filename in, returncode out
size_t len;
const char *filename = lua_tolstring(L, 1, &len);
lua_Integer rc = write_history(filename);
lua_pushinteger(L, rc);
return 1;
}
*/

static int c_history_truncate_file(lua_State *L) { /* filename,num in rc out */
size_t len;
const char *filename = lua_tolstring(L, 1, &len);
lua_Integer num = lua_tointeger(L, 2);
lua_Integer rc = history_truncate_file(filename, num);
lua_pushinteger(L, rc);
return 1;
}

/* ------------------ alternate interface ---------------------- */

/*
* saves the last given callback handler and its Lua state
*
* ouch: this is not reentrant!
*/
static int alternate_interface_callback = LUA_NOREF;
static lua_State *last_state = NULL;
static FILE *callback_tty_stream; /* <-- new */

/*
* calls the registered callback handler with `line`
*/
static void handler_calls_lua_callback (char *line) {
lua_rawgeti(last_state, LUA_REGISTRYINDEX, alternate_interface_callback);
lua_pushstring(last_state, line);
lua_call(last_state, 1, 0);
}

static int c_callback_handler_install(lua_State *L) {
char buffer[L_ctermid];
const char *prompt;
/* copied from c_readline */
const char *devtty = ctermid(buffer); /* 20130919 1.1 */
if (devtty != NULL) {
callback_tty_stream = fopen(devtty, "a+");
if (callback_tty_stream != NULL) {
rl_instream = callback_tty_stream;
rl_outstream = callback_tty_stream;
}
}
prompt = luaL_checkstring(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
luaL_unref(L, LUA_REGISTRYINDEX, alternate_interface_callback);
alternate_interface_callback = luaL_ref(L, LUA_REGISTRYINDEX);
rl_callback_handler_install(prompt, handler_calls_lua_callback);
last_state = L;
return 0;
}

static int c_callback_read_char(lua_State *L) {
rl_callback_read_char();
return 0;
}

#ifdef RL_VERSION_MAJOR
#if RL_VERSION_MAJOR >= 7
static int c_callback_sigcleanup(lua_State *L) {
rl_callback_sigcleanup();
return 0;
}
#endif
#endif

static int c_callback_handler_remove(lua_State *L) {
if (callback_tty_stream != NULL) { fclose(callback_tty_stream); } /* new */
rl_callback_handler_remove();
return 0;
}

/* --------------- interface to custom completion ------------- */

/*
* this isn't reentrant either — and reuses last_state
*/

static int complete_callback = LUA_NOREF;
static char **completions = NULL;

char *dummy_generator(const char *text, int state) {
return completions[state];
}

static char **handler_calls_completion_callback(const char *text, int start, int end) {
size_t i; /* ? int ? */
size_t number_of_completions;

rl_attempted_completion_over = 1;
lua_settop(last_state, 0); /* 2.1 */
lua_rawgeti(last_state, LUA_REGISTRYINDEX, complete_callback);
lua_pushstring(last_state, rl_line_buffer);
lua_pushinteger(last_state, (lua_Integer) start+1);
lua_pushinteger(last_state, (lua_Integer) end+1);
lua_call(last_state, 3, 1);
luaL_checktype(last_state, 1, LUA_TTABLE);
/* lua_rawlen is not available in lua5.1. Use lua_objlen instead */
/* http://www.lua.org/manual/5.1/manual.html#lua_objlen */
#if LUA_VERSION_NUM >= 502
number_of_completions = lua_rawlen(last_state, 1);
#else
number_of_completions = lua_objlen(last_state, 1);
#endif
if (!number_of_completions) return NULL;

/* malloc never fails due to overcommit */
completions = malloc(sizeof(char *)*(1+number_of_completions));

for (i = 0; i < number_of_completions; i++) {
size_t length;
const char *tmp;
lua_rawgeti(last_state, 1, i+1);
tmp = luaL_checkstring(last_state, 2);
#if LUA_VERSION_NUM >= 502
length = 1 + lua_rawlen(last_state, 2);
#else
length = 1 + lua_objlen(last_state, 2);
#endif
completions[i] = malloc(sizeof(char)*length);
strncpy(completions[i], tmp, length);
lua_remove(last_state, 2);
}

/* sentinel NULL means: end of list */
completions[number_of_completions] = NULL;

return rl_completion_matches(text, dummy_generator);
}

static int c_set_readline_name(lua_State *L) {
luaL_checktype(L, 1, LUA_TSTRING);
rl_readline_name = (const char *) lua_tolstring(L, 1, NULL); /* 2.8 */
return 0;
}

static int c_set_complete_function(lua_State *L) {
luaL_checktype(L, 1, LUA_TFUNCTION);
luaL_unref(L, LUA_REGISTRYINDEX, complete_callback);
complete_callback = luaL_ref(L, LUA_REGISTRYINDEX);
rl_attempted_completion_function = handler_calls_completion_callback;
last_state = L;
return 0;
}

static int c_set_default_completer(lua_State *L) {
rl_attempted_completion_function = NULL;
return 0;
}

static int c_set_completion_append_character(lua_State *L) { /* 2.2 */
size_t len;
const char *s = lua_tolstring(L, -1, &len); /* PiL4 p.280 */
rl_completion_append_character = s[0];
return 0;
}

/* ----------------- evolved from C-midialsa.c ---------------- */
struct constant { /* Gems p. 334 */
const char * name;
int value;
};
static const struct constant constants[] = {
/* {"Version", Version}, */
{NULL, 0}
};

static const luaL_Reg prv[] = { /* private functions */
{"add_history", c_add_history},
{"append_history", c_append_history},
{"clear_history", c_clear_history},
{"history_length", c_history_length},
{"history_truncate_file", c_history_truncate_file},
{"read_history", c_read_history},
{"readline", c_readline},
{"stifle_history", c_stifle_history},
{"strerror", c_strerror},
{"tabcompletion", c_tabcompletion},
{"using_history", c_using_history},
{"callback_handler_install", c_callback_handler_install},
{"callback_read_char", c_callback_read_char},
#ifdef RL_VERSION_MAJOR
#if RL_VERSION_MAJOR >= 7
{"callback_sigcleanup", c_callback_sigcleanup},
#endif
#endif
{"callback_handler_remove", c_callback_handler_remove},
{"set_readline_name", c_set_readline_name},
{"set_complete_function", c_set_complete_function},
{"set_default_complete_function", c_set_default_completer},
{"set_completion_append_character", c_set_completion_append_character},
{NULL, NULL}
};

static int initialise(lua_State *L) { /* Lua Programming Gems p. 335 */
/* Lua stack: aux table, prv table, dat table */
int index; /* define constants in module namespace */
for (index = 0; constants[index].name != NULL; ++index) {
lua_pushinteger(L, constants[index].value);
lua_setfield(L, 3, constants[index].name);
}
/* lua_pushvalue(L, 1); * set the aux table as environment */
/* lua_replace(L, LUA_ENVIRONINDEX);
unnecessary here, fortunately, because it fails in 5.2 */
lua_pushvalue(L, 2); /* register the private functions */
#if LUA_VERSION_NUM >= 502
luaL_setfuncs(L, prv, 0); /* 5.2 */
return 0;
#else
luaL_register(L, NULL, prv); /* 5.1 */
return 0;
#endif
}

int luaopen_readline(lua_State *L) {
lua_pushcfunction(L, initialise);
return 1;
}

Loading

0 comments on commit 7ec13b1

Please sign in to comment.