From 366c85564874d560b3608349f752e9e490f9002d Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 4 Jul 2024 17:11:58 -0300 Subject: [PATCH] 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. --- lua.c | 88 +++++++++++++++++++++++++++++++++++++++---------- luaconf.h | 11 +++++++ makefile | 4 +-- testes/main.lua | 16 ++++----- 4 files changed, 90 insertions(+), 29 deletions(-) diff --git a/lua.c b/lua.c index 88fb8793..51979a8b 100644 --- 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 #include #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) -#else /* }{ */ +#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 */ +} + + +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 + + +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 index fe98d9a9..65715c8b 100644 --- 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 index 74801eef..58b12f8e 100644 --- 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 index 5c7d0a10..9a86fb5a 100644 --- 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