1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 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;
}
|