File: C-readline.c

package info (click to toggle)
lua-readline 2.9-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 112 kB
  • sloc: ansic: 253; makefile: 4
file content (359 lines) | stat: -rw-r--r-- 11,410 bytes parent folder | download | duplicates (2)
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
/*
    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 */
    const char *line   = readline(prompt);
	/* rl_cleanup_after_signal(); rl_clear_signals();  no effect :-( 1.3 */
	lua_pushstring(L, line);
	if (tty_stream != NULL) { fclose(tty_stream); }
	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;
}

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;
}