lua

A copy of the Lua development repository
Log | Files | Refs | README

commit 366c85564874d560b3608349f752e9e490f9002d
parent 781219dbe16fc327f5b828e1ff6fa45ec3265cba
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date:   Thu,  4 Jul 2024 17:11:31 -0300

lua.c loads 'readline' dynamically

(See comments in luaconf.h.) This change allows easier compilation,
as Lua compiles and works even if the package 'readline' is absent
from the system. Moreover, non-interactive uses don't load the library,
making the stand-alone slightly faster for small loads.

Diffstat:
Mlua.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mluaconf.h | 11+++++++++++
Mmakefile | 4++--
Mtestes/main.lua | 16+++++++---------
4 files changed, 90 insertions(+), 29 deletions(-)

diff --git a/lua.c b/lua.c @@ -437,27 +437,80 @@ static int handle_luainit (lua_State *L) { ** lua_saveline defines how to "save" a read line in a "history". ** lua_freeline defines how to free a line read by lua_readline. */ -#if !defined(lua_readline) /* { */ -#if defined(LUA_USE_READLINE) /* { */ +#if defined(LUA_USE_READLINE) #include <readline/readline.h> #include <readline/history.h> #define lua_initreadline(L) ((void)L, rl_readline_name="lua") -#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) -#define lua_saveline(L,line) ((void)L, add_history(line)) -#define lua_freeline(L,b) ((void)L, free(b)) +#define lua_readline(b,p) ((void)b, readline(p)) +#define lua_saveline(line) add_history(line) +#define lua_freeline(b) free(b) + +#endif + + +#if !defined(lua_readline) /* { */ + +/* pointer to dynamically loaded 'readline' function (if any) */ +typedef char *(*l_readline_t) (const char *prompt); +static l_readline_t l_readline = NULL; + +static char *lua_readline (char *buff, const char *prompt) { + if (l_readline != NULL) /* is there a dynamic 'readline'? */ + return (*l_readline)(prompt); /* use it */ + else { /* emulate 'readline' over 'buff' */ + fputs(prompt, stdout); + fflush(stdout); /* show prompt */ + return fgets(buff, LUA_MAXINPUT, stdin); /* read line */ + } +} + + +/* pointer to dynamically loaded 'add_history' function (if any) */ +typedef void (*l_addhist_t) (const char *string); +static l_addhist_t l_addhist = NULL; + +static void lua_saveline (const char *line) { + if (l_addhist != NULL) /* is there a dynamic 'add_history'? */ + (*l_addhist)(line); /* use it */ + /* else nothing to be done */ +} -#else /* }{ */ + +static void lua_freeline (char *line) { + if (l_readline != NULL) /* is there a dynamic 'readline'? */ + free(line); /* free line created by it */ + /* else 'lua_readline' used an automatic buffer; nothing to free */ +} + + +#if !defined(LUA_USE_DLOPEN) || !defined(LUA_READLINELIB) #define lua_initreadline(L) ((void)L) -#define lua_readline(L,b,p) \ - ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ - fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ -#define lua_saveline(L,line) { (void)L; (void)line; } -#define lua_freeline(L,b) { (void)L; (void)b; } -#endif /* } */ +#else /* { */ + +#include <dlfcn.h> + + +static void lua_initreadline (lua_State *L) { + void *lib = dlopen(LUA_READLINELIB, RTLD_NOW | RTLD_LOCAL); + if (lib == NULL) + lua_warning(L, "library '" LUA_READLINELIB "'not found", 0); + else { + const char **name = cast(const char**, dlsym(lib, "rl_readline_name")); + if (name != NULL) + *name = "Lua"; + l_readline = cast(l_readline_t, cast_func(dlsym(lib, "readline"))); + if (l_readline == NULL) + lua_warning(L, "unable to load 'readline'", 0); + else + l_addhist = cast(l_addhist_t, cast_func(dlsym(lib, "add_history"))); + } +} + +#endif /* } */ #endif /* } */ @@ -505,11 +558,10 @@ static int incomplete (lua_State *L, int status) { */ static int pushline (lua_State *L, int firstline) { char buffer[LUA_MAXINPUT]; - char *b = buffer; size_t l; const char *prmt = get_prompt(L, firstline); - int readstatus = lua_readline(L, b, prmt); - if (readstatus == 0) + char *b = lua_readline(buffer, prmt); + if (b == NULL) return 0; /* no input (prompt will be popped by caller) */ lua_pop(L, 1); /* remove prompt */ l = strlen(b); @@ -519,7 +571,7 @@ static int pushline (lua_State *L, int firstline) { lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */ else lua_pushlstring(L, b, l); - lua_freeline(L, b); + lua_freeline(b); return 1; } @@ -535,7 +587,7 @@ static int addreturn (lua_State *L) { if (status == LUA_OK) { lua_remove(L, -2); /* remove modified line */ if (line[0] != '\0') /* non empty? */ - lua_saveline(L, line); /* keep history */ + lua_saveline(line); /* keep history */ } else lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */ @@ -552,7 +604,7 @@ static int multiline (lua_State *L) { const char *line = lua_tolstring(L, 1, &len); /* get what it has */ int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */ if (!incomplete(L, status) || !pushline(L, 0)) { - lua_saveline(L, line); /* keep history */ + lua_saveline(line); /* keep history */ return status; /* cannot or should not try to add continuation line */ } lua_pushliteral(L, "\n"); /* add newline... */ diff --git a/luaconf.h b/luaconf.h @@ -58,15 +58,26 @@ #endif +/* +** When Posix DLL ('LUA_USE_DLOPEN') is enabled, the Lua stand-alone +** application will try to dynamically link a 'readline' facility +** for its REPL. In that case, LUA_READLINELIB is the name of the +** library it will look for those facilities. If lua.c cannot open +** the specified library, it will generate a warning and then run +** without 'readline'. If that macro is not defined, lua.c will not +** use 'readline'. +*/ #if defined(LUA_USE_LINUX) #define LUA_USE_POSIX #define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#define LUA_READLINELIB "libreadline.so" #endif #if defined(LUA_USE_MACOSX) #define LUA_USE_POSIX #define LUA_USE_DLOPEN /* MacOS does not need -ldl */ +#define LUA_READLINELIB "libedit.dylib" #endif diff --git a/makefile b/makefile @@ -70,9 +70,9 @@ LOCAL = $(TESTS) $(CWARNS) # enable Linux goodies -MYCFLAGS= $(LOCAL) -std=c99 -DLUA_USE_LINUX -DLUA_USE_READLINE +MYCFLAGS= $(LOCAL) -std=c99 -DLUA_USE_LINUX MYLDFLAGS= $(LOCAL) -Wl,-E -MYLIBS= -ldl -lreadline +MYLIBS= -ldl CC= gcc diff --git a/testes/main.lua b/testes/main.lua @@ -368,20 +368,18 @@ assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt)) -- non-string prompt -prompt = - "local C = 0;\z - _PROMPT=setmetatable({},{__tostring = function () \z - C = C + 1; return C end})" +prompt = [[ + local C = 'X'; + _PROMPT=setmetatable({},{__tostring = function () + C = C .. 'X'; return C end}) +]] prepfile[[ -- a = 2 ]] RUN([[lua -e "%s" -i < %s > %s]], prompt, prog, out) local t = getoutput() -assert(string.find(t, [[ -1 -- -2a = 2 -3 -]], 1, true)) +-- skip version line and then check the presence of the three prompts +assert(string.find(t, "^.-\nXX[^\nX]*\n?XXX[^\nX]*\n?XXXX\n?$")) -- test for error objects