mirror of
https://github.com/lua/lua
synced 2024-11-22 12:51:30 +03:00
063d4e4543
This is the first commit for the branch Lua 5.3. All source files were copied from the official distribution of 5.3.5 in the Lua site. The test files are the same of 5.3.4. The manual came from the previous RCS repository, revision 1.167.1.2.
1044 lines
30 KiB
C
1044 lines
30 KiB
C
/*
|
|
** $Id: lauxlib.c,v 1.289.1.1 2017/04/19 17:20:42 roberto Exp $
|
|
** Auxiliary functions for building Lua libraries
|
|
** See Copyright Notice in lua.h
|
|
*/
|
|
|
|
#define lauxlib_c
|
|
#define LUA_LIB
|
|
|
|
#include "lprefix.h"
|
|
|
|
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
|
|
/*
|
|
** This file uses only the official API of Lua.
|
|
** Any function declared here could be written as an application function.
|
|
*/
|
|
|
|
#include "lua.h"
|
|
|
|
#include "lauxlib.h"
|
|
|
|
|
|
/*
|
|
** {======================================================
|
|
** Traceback
|
|
** =======================================================
|
|
*/
|
|
|
|
|
|
#define LEVELS1 10 /* size of the first part of the stack */
|
|
#define LEVELS2 11 /* size of the second part of the stack */
|
|
|
|
|
|
|
|
/*
|
|
** search for 'objidx' in table at index -1.
|
|
** return 1 + string at top if find a good name.
|
|
*/
|
|
static int findfield (lua_State *L, int objidx, int level) {
|
|
if (level == 0 || !lua_istable(L, -1))
|
|
return 0; /* not found */
|
|
lua_pushnil(L); /* start 'next' loop */
|
|
while (lua_next(L, -2)) { /* for each pair in table */
|
|
if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */
|
|
if (lua_rawequal(L, objidx, -1)) { /* found object? */
|
|
lua_pop(L, 1); /* remove value (but keep name) */
|
|
return 1;
|
|
}
|
|
else if (findfield(L, objidx, level - 1)) { /* try recursively */
|
|
lua_remove(L, -2); /* remove table (but keep name) */
|
|
lua_pushliteral(L, ".");
|
|
lua_insert(L, -2); /* place '.' between the two names */
|
|
lua_concat(L, 3);
|
|
return 1;
|
|
}
|
|
}
|
|
lua_pop(L, 1); /* remove value */
|
|
}
|
|
return 0; /* not found */
|
|
}
|
|
|
|
|
|
/*
|
|
** Search for a name for a function in all loaded modules
|
|
*/
|
|
static int pushglobalfuncname (lua_State *L, lua_Debug *ar) {
|
|
int top = lua_gettop(L);
|
|
lua_getinfo(L, "f", ar); /* push function */
|
|
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
|
|
if (findfield(L, top + 1, 2)) {
|
|
const char *name = lua_tostring(L, -1);
|
|
if (strncmp(name, "_G.", 3) == 0) { /* name start with '_G.'? */
|
|
lua_pushstring(L, name + 3); /* push name without prefix */
|
|
lua_remove(L, -2); /* remove original name */
|
|
}
|
|
lua_copy(L, -1, top + 1); /* move name to proper place */
|
|
lua_pop(L, 2); /* remove pushed values */
|
|
return 1;
|
|
}
|
|
else {
|
|
lua_settop(L, top); /* remove function and global table */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void pushfuncname (lua_State *L, lua_Debug *ar) {
|
|
if (pushglobalfuncname(L, ar)) { /* try first a global name */
|
|
lua_pushfstring(L, "function '%s'", lua_tostring(L, -1));
|
|
lua_remove(L, -2); /* remove name */
|
|
}
|
|
else if (*ar->namewhat != '\0') /* is there a name from code? */
|
|
lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */
|
|
else if (*ar->what == 'm') /* main? */
|
|
lua_pushliteral(L, "main chunk");
|
|
else if (*ar->what != 'C') /* for Lua functions, use <file:line> */
|
|
lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined);
|
|
else /* nothing left... */
|
|
lua_pushliteral(L, "?");
|
|
}
|
|
|
|
|
|
static int lastlevel (lua_State *L) {
|
|
lua_Debug ar;
|
|
int li = 1, le = 1;
|
|
/* find an upper bound */
|
|
while (lua_getstack(L, le, &ar)) { li = le; le *= 2; }
|
|
/* do a binary search */
|
|
while (li < le) {
|
|
int m = (li + le)/2;
|
|
if (lua_getstack(L, m, &ar)) li = m + 1;
|
|
else le = m;
|
|
}
|
|
return le - 1;
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1,
|
|
const char *msg, int level) {
|
|
lua_Debug ar;
|
|
int top = lua_gettop(L);
|
|
int last = lastlevel(L1);
|
|
int n1 = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1;
|
|
if (msg)
|
|
lua_pushfstring(L, "%s\n", msg);
|
|
luaL_checkstack(L, 10, NULL);
|
|
lua_pushliteral(L, "stack traceback:");
|
|
while (lua_getstack(L1, level++, &ar)) {
|
|
if (n1-- == 0) { /* too many levels? */
|
|
lua_pushliteral(L, "\n\t..."); /* add a '...' */
|
|
level = last - LEVELS2 + 1; /* and skip to last ones */
|
|
}
|
|
else {
|
|
lua_getinfo(L1, "Slnt", &ar);
|
|
lua_pushfstring(L, "\n\t%s:", ar.short_src);
|
|
if (ar.currentline > 0)
|
|
lua_pushfstring(L, "%d:", ar.currentline);
|
|
lua_pushliteral(L, " in ");
|
|
pushfuncname(L, &ar);
|
|
if (ar.istailcall)
|
|
lua_pushliteral(L, "\n\t(...tail calls...)");
|
|
lua_concat(L, lua_gettop(L) - top);
|
|
}
|
|
}
|
|
lua_concat(L, lua_gettop(L) - top);
|
|
}
|
|
|
|
/* }====================================================== */
|
|
|
|
|
|
/*
|
|
** {======================================================
|
|
** Error-report functions
|
|
** =======================================================
|
|
*/
|
|
|
|
LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
|
|
lua_Debug ar;
|
|
if (!lua_getstack(L, 0, &ar)) /* no stack frame? */
|
|
return luaL_error(L, "bad argument #%d (%s)", arg, extramsg);
|
|
lua_getinfo(L, "n", &ar);
|
|
if (strcmp(ar.namewhat, "method") == 0) {
|
|
arg--; /* do not count 'self' */
|
|
if (arg == 0) /* error is in the self argument itself? */
|
|
return luaL_error(L, "calling '%s' on bad self (%s)",
|
|
ar.name, extramsg);
|
|
}
|
|
if (ar.name == NULL)
|
|
ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?";
|
|
return luaL_error(L, "bad argument #%d to '%s' (%s)",
|
|
arg, ar.name, extramsg);
|
|
}
|
|
|
|
|
|
static int typeerror (lua_State *L, int arg, const char *tname) {
|
|
const char *msg;
|
|
const char *typearg; /* name for the type of the actual argument */
|
|
if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
|
|
typearg = lua_tostring(L, -1); /* use the given type name */
|
|
else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA)
|
|
typearg = "light userdata"; /* special name for messages */
|
|
else
|
|
typearg = luaL_typename(L, arg); /* standard name */
|
|
msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg);
|
|
return luaL_argerror(L, arg, msg);
|
|
}
|
|
|
|
|
|
static void tag_error (lua_State *L, int arg, int tag) {
|
|
typeerror(L, arg, lua_typename(L, tag));
|
|
}
|
|
|
|
|
|
/*
|
|
** The use of 'lua_pushfstring' ensures this function does not
|
|
** need reserved stack space when called.
|
|
*/
|
|
LUALIB_API void luaL_where (lua_State *L, int level) {
|
|
lua_Debug ar;
|
|
if (lua_getstack(L, level, &ar)) { /* check function at level */
|
|
lua_getinfo(L, "Sl", &ar); /* get info about it */
|
|
if (ar.currentline > 0) { /* is there info? */
|
|
lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline);
|
|
return;
|
|
}
|
|
}
|
|
lua_pushfstring(L, ""); /* else, no information available... */
|
|
}
|
|
|
|
|
|
/*
|
|
** Again, the use of 'lua_pushvfstring' ensures this function does
|
|
** not need reserved stack space when called. (At worst, it generates
|
|
** an error with "stack overflow" instead of the given message.)
|
|
*/
|
|
LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) {
|
|
va_list argp;
|
|
va_start(argp, fmt);
|
|
luaL_where(L, 1);
|
|
lua_pushvfstring(L, fmt, argp);
|
|
va_end(argp);
|
|
lua_concat(L, 2);
|
|
return lua_error(L);
|
|
}
|
|
|
|
|
|
LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
|
|
int en = errno; /* calls to Lua API may change this value */
|
|
if (stat) {
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
}
|
|
else {
|
|
lua_pushnil(L);
|
|
if (fname)
|
|
lua_pushfstring(L, "%s: %s", fname, strerror(en));
|
|
else
|
|
lua_pushstring(L, strerror(en));
|
|
lua_pushinteger(L, en);
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
|
|
#if !defined(l_inspectstat) /* { */
|
|
|
|
#if defined(LUA_USE_POSIX)
|
|
|
|
#include <sys/wait.h>
|
|
|
|
/*
|
|
** use appropriate macros to interpret 'pclose' return status
|
|
*/
|
|
#define l_inspectstat(stat,what) \
|
|
if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \
|
|
else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; }
|
|
|
|
#else
|
|
|
|
#define l_inspectstat(stat,what) /* no op */
|
|
|
|
#endif
|
|
|
|
#endif /* } */
|
|
|
|
|
|
LUALIB_API int luaL_execresult (lua_State *L, int stat) {
|
|
const char *what = "exit"; /* type of termination */
|
|
if (stat == -1) /* error? */
|
|
return luaL_fileresult(L, 0, NULL);
|
|
else {
|
|
l_inspectstat(stat, what); /* interpret result */
|
|
if (*what == 'e' && stat == 0) /* successful termination? */
|
|
lua_pushboolean(L, 1);
|
|
else
|
|
lua_pushnil(L);
|
|
lua_pushstring(L, what);
|
|
lua_pushinteger(L, stat);
|
|
return 3; /* return true/nil,what,code */
|
|
}
|
|
}
|
|
|
|
/* }====================================================== */
|
|
|
|
|
|
/*
|
|
** {======================================================
|
|
** Userdata's metatable manipulation
|
|
** =======================================================
|
|
*/
|
|
|
|
LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {
|
|
if (luaL_getmetatable(L, tname) != LUA_TNIL) /* name already in use? */
|
|
return 0; /* leave previous value on top, but return 0 */
|
|
lua_pop(L, 1);
|
|
lua_createtable(L, 0, 2); /* create metatable */
|
|
lua_pushstring(L, tname);
|
|
lua_setfield(L, -2, "__name"); /* metatable.__name = tname */
|
|
lua_pushvalue(L, -1);
|
|
lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */
|
|
return 1;
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_setmetatable (lua_State *L, const char *tname) {
|
|
luaL_getmetatable(L, tname);
|
|
lua_setmetatable(L, -2);
|
|
}
|
|
|
|
|
|
LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) {
|
|
void *p = lua_touserdata(L, ud);
|
|
if (p != NULL) { /* value is a userdata? */
|
|
if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
|
|
luaL_getmetatable(L, tname); /* get correct metatable */
|
|
if (!lua_rawequal(L, -1, -2)) /* not the same? */
|
|
p = NULL; /* value is a userdata with wrong metatable */
|
|
lua_pop(L, 2); /* remove both metatables */
|
|
return p;
|
|
}
|
|
}
|
|
return NULL; /* value is not a userdata with a metatable */
|
|
}
|
|
|
|
|
|
LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {
|
|
void *p = luaL_testudata(L, ud, tname);
|
|
if (p == NULL) typeerror(L, ud, tname);
|
|
return p;
|
|
}
|
|
|
|
/* }====================================================== */
|
|
|
|
|
|
/*
|
|
** {======================================================
|
|
** Argument check functions
|
|
** =======================================================
|
|
*/
|
|
|
|
LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def,
|
|
const char *const lst[]) {
|
|
const char *name = (def) ? luaL_optstring(L, arg, def) :
|
|
luaL_checkstring(L, arg);
|
|
int i;
|
|
for (i=0; lst[i]; i++)
|
|
if (strcmp(lst[i], name) == 0)
|
|
return i;
|
|
return luaL_argerror(L, arg,
|
|
lua_pushfstring(L, "invalid option '%s'", name));
|
|
}
|
|
|
|
|
|
/*
|
|
** Ensures the stack has at least 'space' extra slots, raising an error
|
|
** if it cannot fulfill the request. (The error handling needs a few
|
|
** extra slots to format the error message. In case of an error without
|
|
** this extra space, Lua will generate the same 'stack overflow' error,
|
|
** but without 'msg'.)
|
|
*/
|
|
LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) {
|
|
if (!lua_checkstack(L, space)) {
|
|
if (msg)
|
|
luaL_error(L, "stack overflow (%s)", msg);
|
|
else
|
|
luaL_error(L, "stack overflow");
|
|
}
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) {
|
|
if (lua_type(L, arg) != t)
|
|
tag_error(L, arg, t);
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_checkany (lua_State *L, int arg) {
|
|
if (lua_type(L, arg) == LUA_TNONE)
|
|
luaL_argerror(L, arg, "value expected");
|
|
}
|
|
|
|
|
|
LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) {
|
|
const char *s = lua_tolstring(L, arg, len);
|
|
if (!s) tag_error(L, arg, LUA_TSTRING);
|
|
return s;
|
|
}
|
|
|
|
|
|
LUALIB_API const char *luaL_optlstring (lua_State *L, int arg,
|
|
const char *def, size_t *len) {
|
|
if (lua_isnoneornil(L, arg)) {
|
|
if (len)
|
|
*len = (def ? strlen(def) : 0);
|
|
return def;
|
|
}
|
|
else return luaL_checklstring(L, arg, len);
|
|
}
|
|
|
|
|
|
LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) {
|
|
int isnum;
|
|
lua_Number d = lua_tonumberx(L, arg, &isnum);
|
|
if (!isnum)
|
|
tag_error(L, arg, LUA_TNUMBER);
|
|
return d;
|
|
}
|
|
|
|
|
|
LUALIB_API lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number def) {
|
|
return luaL_opt(L, luaL_checknumber, arg, def);
|
|
}
|
|
|
|
|
|
static void interror (lua_State *L, int arg) {
|
|
if (lua_isnumber(L, arg))
|
|
luaL_argerror(L, arg, "number has no integer representation");
|
|
else
|
|
tag_error(L, arg, LUA_TNUMBER);
|
|
}
|
|
|
|
|
|
LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) {
|
|
int isnum;
|
|
lua_Integer d = lua_tointegerx(L, arg, &isnum);
|
|
if (!isnum) {
|
|
interror(L, arg);
|
|
}
|
|
return d;
|
|
}
|
|
|
|
|
|
LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int arg,
|
|
lua_Integer def) {
|
|
return luaL_opt(L, luaL_checkinteger, arg, def);
|
|
}
|
|
|
|
/* }====================================================== */
|
|
|
|
|
|
/*
|
|
** {======================================================
|
|
** Generic Buffer manipulation
|
|
** =======================================================
|
|
*/
|
|
|
|
/* userdata to box arbitrary data */
|
|
typedef struct UBox {
|
|
void *box;
|
|
size_t bsize;
|
|
} UBox;
|
|
|
|
|
|
static void *resizebox (lua_State *L, int idx, size_t newsize) {
|
|
void *ud;
|
|
lua_Alloc allocf = lua_getallocf(L, &ud);
|
|
UBox *box = (UBox *)lua_touserdata(L, idx);
|
|
void *temp = allocf(ud, box->box, box->bsize, newsize);
|
|
if (temp == NULL && newsize > 0) { /* allocation error? */
|
|
resizebox(L, idx, 0); /* free buffer */
|
|
luaL_error(L, "not enough memory for buffer allocation");
|
|
}
|
|
box->box = temp;
|
|
box->bsize = newsize;
|
|
return temp;
|
|
}
|
|
|
|
|
|
static int boxgc (lua_State *L) {
|
|
resizebox(L, 1, 0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void *newbox (lua_State *L, size_t newsize) {
|
|
UBox *box = (UBox *)lua_newuserdata(L, sizeof(UBox));
|
|
box->box = NULL;
|
|
box->bsize = 0;
|
|
if (luaL_newmetatable(L, "LUABOX")) { /* creating metatable? */
|
|
lua_pushcfunction(L, boxgc);
|
|
lua_setfield(L, -2, "__gc"); /* metatable.__gc = boxgc */
|
|
}
|
|
lua_setmetatable(L, -2);
|
|
return resizebox(L, -1, newsize);
|
|
}
|
|
|
|
|
|
/*
|
|
** check whether buffer is using a userdata on the stack as a temporary
|
|
** buffer
|
|
*/
|
|
#define buffonstack(B) ((B)->b != (B)->initb)
|
|
|
|
|
|
/*
|
|
** returns a pointer to a free area with at least 'sz' bytes
|
|
*/
|
|
LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) {
|
|
lua_State *L = B->L;
|
|
if (B->size - B->n < sz) { /* not enough space? */
|
|
char *newbuff;
|
|
size_t newsize = B->size * 2; /* double buffer size */
|
|
if (newsize - B->n < sz) /* not big enough? */
|
|
newsize = B->n + sz;
|
|
if (newsize < B->n || newsize - B->n < sz)
|
|
luaL_error(L, "buffer too large");
|
|
/* create larger buffer */
|
|
if (buffonstack(B))
|
|
newbuff = (char *)resizebox(L, -1, newsize);
|
|
else { /* no buffer yet */
|
|
newbuff = (char *)newbox(L, newsize);
|
|
memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
|
|
}
|
|
B->b = newbuff;
|
|
B->size = newsize;
|
|
}
|
|
return &B->b[B->n];
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) {
|
|
if (l > 0) { /* avoid 'memcpy' when 's' can be NULL */
|
|
char *b = luaL_prepbuffsize(B, l);
|
|
memcpy(b, s, l * sizeof(char));
|
|
luaL_addsize(B, l);
|
|
}
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
|
|
luaL_addlstring(B, s, strlen(s));
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
|
|
lua_State *L = B->L;
|
|
lua_pushlstring(L, B->b, B->n);
|
|
if (buffonstack(B)) {
|
|
resizebox(L, -2, 0); /* delete old buffer */
|
|
lua_remove(L, -2); /* remove its header from the stack */
|
|
}
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) {
|
|
luaL_addsize(B, sz);
|
|
luaL_pushresult(B);
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_addvalue (luaL_Buffer *B) {
|
|
lua_State *L = B->L;
|
|
size_t l;
|
|
const char *s = lua_tolstring(L, -1, &l);
|
|
if (buffonstack(B))
|
|
lua_insert(L, -2); /* put value below buffer */
|
|
luaL_addlstring(B, s, l);
|
|
lua_remove(L, (buffonstack(B)) ? -2 : -1); /* remove value */
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
|
|
B->L = L;
|
|
B->b = B->initb;
|
|
B->n = 0;
|
|
B->size = LUAL_BUFFERSIZE;
|
|
}
|
|
|
|
|
|
LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) {
|
|
luaL_buffinit(L, B);
|
|
return luaL_prepbuffsize(B, sz);
|
|
}
|
|
|
|
/* }====================================================== */
|
|
|
|
|
|
/*
|
|
** {======================================================
|
|
** Reference system
|
|
** =======================================================
|
|
*/
|
|
|
|
/* index of free-list header */
|
|
#define freelist 0
|
|
|
|
|
|
LUALIB_API int luaL_ref (lua_State *L, int t) {
|
|
int ref;
|
|
if (lua_isnil(L, -1)) {
|
|
lua_pop(L, 1); /* remove from stack */
|
|
return LUA_REFNIL; /* 'nil' has a unique fixed reference */
|
|
}
|
|
t = lua_absindex(L, t);
|
|
lua_rawgeti(L, t, freelist); /* get first free element */
|
|
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
|
|
lua_pop(L, 1); /* remove it from stack */
|
|
if (ref != 0) { /* any free element? */
|
|
lua_rawgeti(L, t, ref); /* remove it from list */
|
|
lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */
|
|
}
|
|
else /* no free elements */
|
|
ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */
|
|
lua_rawseti(L, t, ref);
|
|
return ref;
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
|
|
if (ref >= 0) {
|
|
t = lua_absindex(L, t);
|
|
lua_rawgeti(L, t, freelist);
|
|
lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */
|
|
lua_pushinteger(L, ref);
|
|
lua_rawseti(L, t, freelist); /* t[freelist] = ref */
|
|
}
|
|
}
|
|
|
|
/* }====================================================== */
|
|
|
|
|
|
/*
|
|
** {======================================================
|
|
** Load functions
|
|
** =======================================================
|
|
*/
|
|
|
|
typedef struct LoadF {
|
|
int n; /* number of pre-read characters */
|
|
FILE *f; /* file being read */
|
|
char buff[BUFSIZ]; /* area for reading file */
|
|
} LoadF;
|
|
|
|
|
|
static const char *getF (lua_State *L, void *ud, size_t *size) {
|
|
LoadF *lf = (LoadF *)ud;
|
|
(void)L; /* not used */
|
|
if (lf->n > 0) { /* are there pre-read characters to be read? */
|
|
*size = lf->n; /* return them (chars already in buffer) */
|
|
lf->n = 0; /* no more pre-read characters */
|
|
}
|
|
else { /* read a block from file */
|
|
/* 'fread' can return > 0 *and* set the EOF flag. If next call to
|
|
'getF' called 'fread', it might still wait for user input.
|
|
The next check avoids this problem. */
|
|
if (feof(lf->f)) return NULL;
|
|
*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */
|
|
}
|
|
return lf->buff;
|
|
}
|
|
|
|
|
|
static int errfile (lua_State *L, const char *what, int fnameindex) {
|
|
const char *serr = strerror(errno);
|
|
const char *filename = lua_tostring(L, fnameindex) + 1;
|
|
lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
|
|
lua_remove(L, fnameindex);
|
|
return LUA_ERRFILE;
|
|
}
|
|
|
|
|
|
static int skipBOM (LoadF *lf) {
|
|
const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */
|
|
int c;
|
|
lf->n = 0;
|
|
do {
|
|
c = getc(lf->f);
|
|
if (c == EOF || c != *(const unsigned char *)p++) return c;
|
|
lf->buff[lf->n++] = c; /* to be read by the parser */
|
|
} while (*p != '\0');
|
|
lf->n = 0; /* prefix matched; discard it */
|
|
return getc(lf->f); /* return next character */
|
|
}
|
|
|
|
|
|
/*
|
|
** reads the first character of file 'f' and skips an optional BOM mark
|
|
** in its beginning plus its first line if it starts with '#'. Returns
|
|
** true if it skipped the first line. In any case, '*cp' has the
|
|
** first "valid" character of the file (after the optional BOM and
|
|
** a first-line comment).
|
|
*/
|
|
static int skipcomment (LoadF *lf, int *cp) {
|
|
int c = *cp = skipBOM(lf);
|
|
if (c == '#') { /* first line is a comment (Unix exec. file)? */
|
|
do { /* skip first line */
|
|
c = getc(lf->f);
|
|
} while (c != EOF && c != '\n');
|
|
*cp = getc(lf->f); /* skip end-of-line, if present */
|
|
return 1; /* there was a comment */
|
|
}
|
|
else return 0; /* no comment */
|
|
}
|
|
|
|
|
|
LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
|
|
const char *mode) {
|
|
LoadF lf;
|
|
int status, readstatus;
|
|
int c;
|
|
int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
|
|
if (filename == NULL) {
|
|
lua_pushliteral(L, "=stdin");
|
|
lf.f = stdin;
|
|
}
|
|
else {
|
|
lua_pushfstring(L, "@%s", filename);
|
|
lf.f = fopen(filename, "r");
|
|
if (lf.f == NULL) return errfile(L, "open", fnameindex);
|
|
}
|
|
if (skipcomment(&lf, &c)) /* read initial portion */
|
|
lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
|
|
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
|
|
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
|
|
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
|
|
skipcomment(&lf, &c); /* re-read initial portion */
|
|
}
|
|
if (c != EOF)
|
|
lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
|
|
status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode);
|
|
readstatus = ferror(lf.f);
|
|
if (filename) fclose(lf.f); /* close file (even in case of errors) */
|
|
if (readstatus) {
|
|
lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
|
|
return errfile(L, "read", fnameindex);
|
|
}
|
|
lua_remove(L, fnameindex);
|
|
return status;
|
|
}
|
|
|
|
|
|
typedef struct LoadS {
|
|
const char *s;
|
|
size_t size;
|
|
} LoadS;
|
|
|
|
|
|
static const char *getS (lua_State *L, void *ud, size_t *size) {
|
|
LoadS *ls = (LoadS *)ud;
|
|
(void)L; /* not used */
|
|
if (ls->size == 0) return NULL;
|
|
*size = ls->size;
|
|
ls->size = 0;
|
|
return ls->s;
|
|
}
|
|
|
|
|
|
LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size,
|
|
const char *name, const char *mode) {
|
|
LoadS ls;
|
|
ls.s = buff;
|
|
ls.size = size;
|
|
return lua_load(L, getS, &ls, name, mode);
|
|
}
|
|
|
|
|
|
LUALIB_API int luaL_loadstring (lua_State *L, const char *s) {
|
|
return luaL_loadbuffer(L, s, strlen(s), s);
|
|
}
|
|
|
|
/* }====================================================== */
|
|
|
|
|
|
|
|
LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) {
|
|
if (!lua_getmetatable(L, obj)) /* no metatable? */
|
|
return LUA_TNIL;
|
|
else {
|
|
int tt;
|
|
lua_pushstring(L, event);
|
|
tt = lua_rawget(L, -2);
|
|
if (tt == LUA_TNIL) /* is metafield nil? */
|
|
lua_pop(L, 2); /* remove metatable and metafield */
|
|
else
|
|
lua_remove(L, -2); /* remove only metatable */
|
|
return tt; /* return metafield type */
|
|
}
|
|
}
|
|
|
|
|
|
LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) {
|
|
obj = lua_absindex(L, obj);
|
|
if (luaL_getmetafield(L, obj, event) == LUA_TNIL) /* no metafield? */
|
|
return 0;
|
|
lua_pushvalue(L, obj);
|
|
lua_call(L, 1, 1);
|
|
return 1;
|
|
}
|
|
|
|
|
|
LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) {
|
|
lua_Integer l;
|
|
int isnum;
|
|
lua_len(L, idx);
|
|
l = lua_tointegerx(L, -1, &isnum);
|
|
if (!isnum)
|
|
luaL_error(L, "object length is not an integer");
|
|
lua_pop(L, 1); /* remove object */
|
|
return l;
|
|
}
|
|
|
|
|
|
LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
|
|
if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */
|
|
if (!lua_isstring(L, -1))
|
|
luaL_error(L, "'__tostring' must return a string");
|
|
}
|
|
else {
|
|
switch (lua_type(L, idx)) {
|
|
case LUA_TNUMBER: {
|
|
if (lua_isinteger(L, idx))
|
|
lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx));
|
|
else
|
|
lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx));
|
|
break;
|
|
}
|
|
case LUA_TSTRING:
|
|
lua_pushvalue(L, idx);
|
|
break;
|
|
case LUA_TBOOLEAN:
|
|
lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false"));
|
|
break;
|
|
case LUA_TNIL:
|
|
lua_pushliteral(L, "nil");
|
|
break;
|
|
default: {
|
|
int tt = luaL_getmetafield(L, idx, "__name"); /* try name */
|
|
const char *kind = (tt == LUA_TSTRING) ? lua_tostring(L, -1) :
|
|
luaL_typename(L, idx);
|
|
lua_pushfstring(L, "%s: %p", kind, lua_topointer(L, idx));
|
|
if (tt != LUA_TNIL)
|
|
lua_remove(L, -2); /* remove '__name' */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return lua_tolstring(L, -1, len);
|
|
}
|
|
|
|
|
|
/*
|
|
** {======================================================
|
|
** Compatibility with 5.1 module functions
|
|
** =======================================================
|
|
*/
|
|
#if defined(LUA_COMPAT_MODULE)
|
|
|
|
static const char *luaL_findtable (lua_State *L, int idx,
|
|
const char *fname, int szhint) {
|
|
const char *e;
|
|
if (idx) lua_pushvalue(L, idx);
|
|
do {
|
|
e = strchr(fname, '.');
|
|
if (e == NULL) e = fname + strlen(fname);
|
|
lua_pushlstring(L, fname, e - fname);
|
|
if (lua_rawget(L, -2) == LUA_TNIL) { /* no such field? */
|
|
lua_pop(L, 1); /* remove this nil */
|
|
lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */
|
|
lua_pushlstring(L, fname, e - fname);
|
|
lua_pushvalue(L, -2);
|
|
lua_settable(L, -4); /* set new table into field */
|
|
}
|
|
else if (!lua_istable(L, -1)) { /* field has a non-table value? */
|
|
lua_pop(L, 2); /* remove table and value */
|
|
return fname; /* return problematic part of the name */
|
|
}
|
|
lua_remove(L, -2); /* remove previous table */
|
|
fname = e + 1;
|
|
} while (*e == '.');
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
** Count number of elements in a luaL_Reg list.
|
|
*/
|
|
static int libsize (const luaL_Reg *l) {
|
|
int size = 0;
|
|
for (; l && l->name; l++) size++;
|
|
return size;
|
|
}
|
|
|
|
|
|
/*
|
|
** Find or create a module table with a given name. The function
|
|
** first looks at the LOADED table and, if that fails, try a
|
|
** global variable with that name. In any case, leaves on the stack
|
|
** the module table.
|
|
*/
|
|
LUALIB_API void luaL_pushmodule (lua_State *L, const char *modname,
|
|
int sizehint) {
|
|
luaL_findtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE, 1);
|
|
if (lua_getfield(L, -1, modname) != LUA_TTABLE) { /* no LOADED[modname]? */
|
|
lua_pop(L, 1); /* remove previous result */
|
|
/* try global variable (and create one if it does not exist) */
|
|
lua_pushglobaltable(L);
|
|
if (luaL_findtable(L, 0, modname, sizehint) != NULL)
|
|
luaL_error(L, "name conflict for module '%s'", modname);
|
|
lua_pushvalue(L, -1);
|
|
lua_setfield(L, -3, modname); /* LOADED[modname] = new table */
|
|
}
|
|
lua_remove(L, -2); /* remove LOADED table */
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_openlib (lua_State *L, const char *libname,
|
|
const luaL_Reg *l, int nup) {
|
|
luaL_checkversion(L);
|
|
if (libname) {
|
|
luaL_pushmodule(L, libname, libsize(l)); /* get/create library table */
|
|
lua_insert(L, -(nup + 1)); /* move library table to below upvalues */
|
|
}
|
|
if (l)
|
|
luaL_setfuncs(L, l, nup);
|
|
else
|
|
lua_pop(L, nup); /* remove upvalues */
|
|
}
|
|
|
|
#endif
|
|
/* }====================================================== */
|
|
|
|
/*
|
|
** set functions from list 'l' into table at top - 'nup'; each
|
|
** function gets the 'nup' elements at the top as upvalues.
|
|
** Returns with only the table at the stack.
|
|
*/
|
|
LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
|
|
luaL_checkstack(L, nup, "too many upvalues");
|
|
for (; l->name != NULL; l++) { /* fill the table with given functions */
|
|
int i;
|
|
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
|
lua_pushvalue(L, -nup);
|
|
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
|
lua_setfield(L, -(nup + 2), l->name);
|
|
}
|
|
lua_pop(L, nup); /* remove upvalues */
|
|
}
|
|
|
|
|
|
/*
|
|
** ensure that stack[idx][fname] has a table and push that table
|
|
** into the stack
|
|
*/
|
|
LUALIB_API int luaL_getsubtable (lua_State *L, int idx, const char *fname) {
|
|
if (lua_getfield(L, idx, fname) == LUA_TTABLE)
|
|
return 1; /* table already there */
|
|
else {
|
|
lua_pop(L, 1); /* remove previous result */
|
|
idx = lua_absindex(L, idx);
|
|
lua_newtable(L);
|
|
lua_pushvalue(L, -1); /* copy to be left at top */
|
|
lua_setfield(L, idx, fname); /* assign new table to field */
|
|
return 0; /* false, because did not find table there */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Stripped-down 'require': After checking "loaded" table, calls 'openf'
|
|
** to open a module, registers the result in 'package.loaded' table and,
|
|
** if 'glb' is true, also registers the result in the global table.
|
|
** Leaves resulting module on the top.
|
|
*/
|
|
LUALIB_API void luaL_requiref (lua_State *L, const char *modname,
|
|
lua_CFunction openf, int glb) {
|
|
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
|
|
lua_getfield(L, -1, modname); /* LOADED[modname] */
|
|
if (!lua_toboolean(L, -1)) { /* package not already loaded? */
|
|
lua_pop(L, 1); /* remove field */
|
|
lua_pushcfunction(L, openf);
|
|
lua_pushstring(L, modname); /* argument to open function */
|
|
lua_call(L, 1, 1); /* call 'openf' to open module */
|
|
lua_pushvalue(L, -1); /* make copy of module (call result) */
|
|
lua_setfield(L, -3, modname); /* LOADED[modname] = module */
|
|
}
|
|
lua_remove(L, -2); /* remove LOADED table */
|
|
if (glb) {
|
|
lua_pushvalue(L, -1); /* copy of module */
|
|
lua_setglobal(L, modname); /* _G[modname] = module */
|
|
}
|
|
}
|
|
|
|
|
|
LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p,
|
|
const char *r) {
|
|
const char *wild;
|
|
size_t l = strlen(p);
|
|
luaL_Buffer b;
|
|
luaL_buffinit(L, &b);
|
|
while ((wild = strstr(s, p)) != NULL) {
|
|
luaL_addlstring(&b, s, wild - s); /* push prefix */
|
|
luaL_addstring(&b, r); /* push replacement in place of pattern */
|
|
s = wild + l; /* continue after 'p' */
|
|
}
|
|
luaL_addstring(&b, s); /* push last suffix */
|
|
luaL_pushresult(&b);
|
|
return lua_tostring(L, -1);
|
|
}
|
|
|
|
|
|
static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
|
|
(void)ud; (void)osize; /* not used */
|
|
if (nsize == 0) {
|
|
free(ptr);
|
|
return NULL;
|
|
}
|
|
else
|
|
return realloc(ptr, nsize);
|
|
}
|
|
|
|
|
|
static int panic (lua_State *L) {
|
|
lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n",
|
|
lua_tostring(L, -1));
|
|
return 0; /* return to Lua to abort */
|
|
}
|
|
|
|
|
|
LUALIB_API lua_State *luaL_newstate (void) {
|
|
lua_State *L = lua_newstate(l_alloc, NULL);
|
|
if (L) lua_atpanic(L, &panic);
|
|
return L;
|
|
}
|
|
|
|
|
|
LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver, size_t sz) {
|
|
const lua_Number *v = lua_version(L);
|
|
if (sz != LUAL_NUMSIZES) /* check numeric types */
|
|
luaL_error(L, "core and library have incompatible numeric types");
|
|
if (v != lua_version(NULL))
|
|
luaL_error(L, "multiple Lua VMs detected");
|
|
else if (*v != ver)
|
|
luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f",
|
|
(LUAI_UACNUMBER)ver, (LUAI_UACNUMBER)*v);
|
|
}
|
|
|