Last active
May 6, 2018 14:48
-
-
Save kubo/28a8a3c66858f6126ffc28f363c9e55f to your computer and use it in GitHub Desktop.
os.execute('exit -1') on windows. patch.dif is a patch file for lua 5.3.x. lauxlib.c, loslib.c and liolib.c are patched files of lua 5.3.4.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
** $Id: lauxlib.c,v 1.289 2016/12/20 18:37:00 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 | |
#if defined(LUA_USE_WINDOWS) | |
&& errno != 0 | |
#endif | |
) /* 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
** $Id: liolib.c,v 2.151 2016/12/20 18:37:00 roberto Exp $ | |
** Standard I/O (and system) library | |
** See Copyright Notice in lua.h | |
*/ | |
#define liolib_c | |
#define LUA_LIB | |
#include "lprefix.h" | |
#include <ctype.h> | |
#include <errno.h> | |
#include <locale.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include "lua.h" | |
#include "lauxlib.h" | |
#include "lualib.h" | |
/* | |
** Change this macro to accept other modes for 'fopen' besides | |
** the standard ones. | |
*/ | |
#if !defined(l_checkmode) | |
/* accepted extensions to 'mode' in 'fopen' */ | |
#if !defined(L_MODEEXT) | |
#define L_MODEEXT "b" | |
#endif | |
/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */ | |
static int l_checkmode (const char *mode) { | |
return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL && | |
(*mode != '+' || (++mode, 1)) && /* skip if char is '+' */ | |
(strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */ | |
} | |
#endif | |
/* | |
** {====================================================== | |
** l_popen spawns a new process connected to the current | |
** one through the file streams. | |
** ======================================================= | |
*/ | |
#if !defined(l_popen) /* { */ | |
#if defined(LUA_USE_POSIX) /* { */ | |
#define l_popen(L,c,m) (fflush(NULL), popen(c,m)) | |
#define l_pclose(L,file) (pclose(file)) | |
#elif defined(LUA_USE_WINDOWS) /* }{ */ | |
#define l_popen(L,c,m) (_popen(c,m)) | |
#define l_pclose(L,file) (_pclose(file)) | |
#else /* }{ */ | |
/* ISO C definitions */ | |
#define l_popen(L,c,m) \ | |
((void)((void)c, m), \ | |
luaL_error(L, "'popen' not supported"), \ | |
(FILE*)0) | |
#define l_pclose(L,file) ((void)L, (void)file, -1) | |
#endif /* } */ | |
#endif /* } */ | |
/* }====================================================== */ | |
#if !defined(l_getc) /* { */ | |
#if defined(LUA_USE_POSIX) | |
#define l_getc(f) getc_unlocked(f) | |
#define l_lockfile(f) flockfile(f) | |
#define l_unlockfile(f) funlockfile(f) | |
#else | |
#define l_getc(f) getc(f) | |
#define l_lockfile(f) ((void)0) | |
#define l_unlockfile(f) ((void)0) | |
#endif | |
#endif /* } */ | |
/* | |
** {====================================================== | |
** l_fseek: configuration for longer offsets | |
** ======================================================= | |
*/ | |
#if !defined(l_fseek) /* { */ | |
#if defined(LUA_USE_POSIX) /* { */ | |
#include <sys/types.h> | |
#define l_fseek(f,o,w) fseeko(f,o,w) | |
#define l_ftell(f) ftello(f) | |
#define l_seeknum off_t | |
#elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \ | |
&& defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */ | |
/* Windows (but not DDK) and Visual C++ 2005 or higher */ | |
#define l_fseek(f,o,w) _fseeki64(f,o,w) | |
#define l_ftell(f) _ftelli64(f) | |
#define l_seeknum __int64 | |
#else /* }{ */ | |
/* ISO C definitions */ | |
#define l_fseek(f,o,w) fseek(f,o,w) | |
#define l_ftell(f) ftell(f) | |
#define l_seeknum long | |
#endif /* } */ | |
#endif /* } */ | |
/* }====================================================== */ | |
#define IO_PREFIX "_IO_" | |
#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1) | |
#define IO_INPUT (IO_PREFIX "input") | |
#define IO_OUTPUT (IO_PREFIX "output") | |
typedef luaL_Stream LStream; | |
#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) | |
#define isclosed(p) ((p)->closef == NULL) | |
static int io_type (lua_State *L) { | |
LStream *p; | |
luaL_checkany(L, 1); | |
p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE); | |
if (p == NULL) | |
lua_pushnil(L); /* not a file */ | |
else if (isclosed(p)) | |
lua_pushliteral(L, "closed file"); | |
else | |
lua_pushliteral(L, "file"); | |
return 1; | |
} | |
static int f_tostring (lua_State *L) { | |
LStream *p = tolstream(L); | |
if (isclosed(p)) | |
lua_pushliteral(L, "file (closed)"); | |
else | |
lua_pushfstring(L, "file (%p)", p->f); | |
return 1; | |
} | |
static FILE *tofile (lua_State *L) { | |
LStream *p = tolstream(L); | |
if (isclosed(p)) | |
luaL_error(L, "attempt to use a closed file"); | |
lua_assert(p->f); | |
return p->f; | |
} | |
/* | |
** When creating file handles, always creates a 'closed' file handle | |
** before opening the actual file; so, if there is a memory error, the | |
** handle is in a consistent state. | |
*/ | |
static LStream *newprefile (lua_State *L) { | |
LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream)); | |
p->closef = NULL; /* mark file handle as 'closed' */ | |
luaL_setmetatable(L, LUA_FILEHANDLE); | |
return p; | |
} | |
/* | |
** Calls the 'close' function from a file handle. The 'volatile' avoids | |
** a bug in some versions of the Clang compiler (e.g., clang 3.0 for | |
** 32 bits). | |
*/ | |
static int aux_close (lua_State *L) { | |
LStream *p = tolstream(L); | |
volatile lua_CFunction cf = p->closef; | |
p->closef = NULL; /* mark stream as closed */ | |
return (*cf)(L); /* close it */ | |
} | |
static int io_close (lua_State *L) { | |
if (lua_isnone(L, 1)) /* no argument? */ | |
lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */ | |
tofile(L); /* make sure argument is an open stream */ | |
return aux_close(L); | |
} | |
static int f_gc (lua_State *L) { | |
LStream *p = tolstream(L); | |
if (!isclosed(p) && p->f != NULL) | |
aux_close(L); /* ignore closed and incompletely open files */ | |
return 0; | |
} | |
/* | |
** function to close regular files | |
*/ | |
static int io_fclose (lua_State *L) { | |
LStream *p = tolstream(L); | |
int res = fclose(p->f); | |
return luaL_fileresult(L, (res == 0), NULL); | |
} | |
static LStream *newfile (lua_State *L) { | |
LStream *p = newprefile(L); | |
p->f = NULL; | |
p->closef = &io_fclose; | |
return p; | |
} | |
static void opencheck (lua_State *L, const char *fname, const char *mode) { | |
LStream *p = newfile(L); | |
p->f = fopen(fname, mode); | |
if (p->f == NULL) | |
luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno)); | |
} | |
static int io_open (lua_State *L) { | |
const char *filename = luaL_checkstring(L, 1); | |
const char *mode = luaL_optstring(L, 2, "r"); | |
LStream *p = newfile(L); | |
const char *md = mode; /* to traverse/check mode */ | |
luaL_argcheck(L, l_checkmode(md), 2, "invalid mode"); | |
p->f = fopen(filename, mode); | |
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; | |
} | |
/* | |
** function to close 'popen' files | |
*/ | |
static int io_pclose (lua_State *L) { | |
LStream *p = tolstream(L); | |
#if defined(LUA_USE_WINDOWS) | |
errno = 0; | |
#endif | |
return luaL_execresult(L, l_pclose(L, p->f)); | |
} | |
static int io_popen (lua_State *L) { | |
const char *filename = luaL_checkstring(L, 1); | |
const char *mode = luaL_optstring(L, 2, "r"); | |
LStream *p = newprefile(L); | |
p->f = l_popen(L, filename, mode); | |
p->closef = &io_pclose; | |
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; | |
} | |
static int io_tmpfile (lua_State *L) { | |
LStream *p = newfile(L); | |
p->f = tmpfile(); | |
return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; | |
} | |
static FILE *getiofile (lua_State *L, const char *findex) { | |
LStream *p; | |
lua_getfield(L, LUA_REGISTRYINDEX, findex); | |
p = (LStream *)lua_touserdata(L, -1); | |
if (isclosed(p)) | |
luaL_error(L, "standard %s file is closed", findex + IOPREF_LEN); | |
return p->f; | |
} | |
static int g_iofile (lua_State *L, const char *f, const char *mode) { | |
if (!lua_isnoneornil(L, 1)) { | |
const char *filename = lua_tostring(L, 1); | |
if (filename) | |
opencheck(L, filename, mode); | |
else { | |
tofile(L); /* check that it's a valid file handle */ | |
lua_pushvalue(L, 1); | |
} | |
lua_setfield(L, LUA_REGISTRYINDEX, f); | |
} | |
/* return current value */ | |
lua_getfield(L, LUA_REGISTRYINDEX, f); | |
return 1; | |
} | |
static int io_input (lua_State *L) { | |
return g_iofile(L, IO_INPUT, "r"); | |
} | |
static int io_output (lua_State *L) { | |
return g_iofile(L, IO_OUTPUT, "w"); | |
} | |
static int io_readline (lua_State *L); | |
/* | |
** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit | |
** in the limit for upvalues of a closure) | |
*/ | |
#define MAXARGLINE 250 | |
static void aux_lines (lua_State *L, int toclose) { | |
int n = lua_gettop(L) - 1; /* number of arguments to read */ | |
luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments"); | |
lua_pushinteger(L, n); /* number of arguments to read */ | |
lua_pushboolean(L, toclose); /* close/not close file when finished */ | |
lua_rotate(L, 2, 2); /* move 'n' and 'toclose' to their positions */ | |
lua_pushcclosure(L, io_readline, 3 + n); | |
} | |
static int f_lines (lua_State *L) { | |
tofile(L); /* check that it's a valid file handle */ | |
aux_lines(L, 0); | |
return 1; | |
} | |
static int io_lines (lua_State *L) { | |
int toclose; | |
if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */ | |
if (lua_isnil(L, 1)) { /* no file name? */ | |
lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */ | |
lua_replace(L, 1); /* put it at index 1 */ | |
tofile(L); /* check that it's a valid file handle */ | |
toclose = 0; /* do not close it after iteration */ | |
} | |
else { /* open a new file */ | |
const char *filename = luaL_checkstring(L, 1); | |
opencheck(L, filename, "r"); | |
lua_replace(L, 1); /* put file at index 1 */ | |
toclose = 1; /* close it after iteration */ | |
} | |
aux_lines(L, toclose); | |
return 1; | |
} | |
/* | |
** {====================================================== | |
** READ | |
** ======================================================= | |
*/ | |
/* maximum length of a numeral */ | |
#if !defined (L_MAXLENNUM) | |
#define L_MAXLENNUM 200 | |
#endif | |
/* auxiliary structure used by 'read_number' */ | |
typedef struct { | |
FILE *f; /* file being read */ | |
int c; /* current character (look ahead) */ | |
int n; /* number of elements in buffer 'buff' */ | |
char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */ | |
} RN; | |
/* | |
** Add current char to buffer (if not out of space) and read next one | |
*/ | |
static int nextc (RN *rn) { | |
if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */ | |
rn->buff[0] = '\0'; /* invalidate result */ | |
return 0; /* fail */ | |
} | |
else { | |
rn->buff[rn->n++] = rn->c; /* save current char */ | |
rn->c = l_getc(rn->f); /* read next one */ | |
return 1; | |
} | |
} | |
/* | |
** Accept current char if it is in 'set' (of size 2) | |
*/ | |
static int test2 (RN *rn, const char *set) { | |
if (rn->c == set[0] || rn->c == set[1]) | |
return nextc(rn); | |
else return 0; | |
} | |
/* | |
** Read a sequence of (hex)digits | |
*/ | |
static int readdigits (RN *rn, int hex) { | |
int count = 0; | |
while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn)) | |
count++; | |
return count; | |
} | |
/* | |
** Read a number: first reads a valid prefix of a numeral into a buffer. | |
** Then it calls 'lua_stringtonumber' to check whether the format is | |
** correct and to convert it to a Lua number | |
*/ | |
static int read_number (lua_State *L, FILE *f) { | |
RN rn; | |
int count = 0; | |
int hex = 0; | |
char decp[2]; | |
rn.f = f; rn.n = 0; | |
decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */ | |
decp[1] = '.'; /* always accept a dot */ | |
l_lockfile(rn.f); | |
do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */ | |
test2(&rn, "-+"); /* optional signal */ | |
if (test2(&rn, "00")) { | |
if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */ | |
else count = 1; /* count initial '0' as a valid digit */ | |
} | |
count += readdigits(&rn, hex); /* integral part */ | |
if (test2(&rn, decp)) /* decimal point? */ | |
count += readdigits(&rn, hex); /* fractional part */ | |
if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */ | |
test2(&rn, "-+"); /* exponent signal */ | |
readdigits(&rn, 0); /* exponent digits */ | |
} | |
ungetc(rn.c, rn.f); /* unread look-ahead char */ | |
l_unlockfile(rn.f); | |
rn.buff[rn.n] = '\0'; /* finish string */ | |
if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */ | |
return 1; /* ok */ | |
else { /* invalid format */ | |
lua_pushnil(L); /* "result" to be removed */ | |
return 0; /* read fails */ | |
} | |
} | |
static int test_eof (lua_State *L, FILE *f) { | |
int c = getc(f); | |
ungetc(c, f); /* no-op when c == EOF */ | |
lua_pushliteral(L, ""); | |
return (c != EOF); | |
} | |
static int read_line (lua_State *L, FILE *f, int chop) { | |
luaL_Buffer b; | |
int c = '\0'; | |
luaL_buffinit(L, &b); | |
while (c != EOF && c != '\n') { /* repeat until end of line */ | |
char *buff = luaL_prepbuffer(&b); /* preallocate buffer */ | |
int i = 0; | |
l_lockfile(f); /* no memory errors can happen inside the lock */ | |
while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n') | |
buff[i++] = c; | |
l_unlockfile(f); | |
luaL_addsize(&b, i); | |
} | |
if (!chop && c == '\n') /* want a newline and have one? */ | |
luaL_addchar(&b, c); /* add ending newline to result */ | |
luaL_pushresult(&b); /* close buffer */ | |
/* return ok if read something (either a newline or something else) */ | |
return (c == '\n' || lua_rawlen(L, -1) > 0); | |
} | |
static void read_all (lua_State *L, FILE *f) { | |
size_t nr; | |
luaL_Buffer b; | |
luaL_buffinit(L, &b); | |
do { /* read file in chunks of LUAL_BUFFERSIZE bytes */ | |
char *p = luaL_prepbuffer(&b); | |
nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f); | |
luaL_addsize(&b, nr); | |
} while (nr == LUAL_BUFFERSIZE); | |
luaL_pushresult(&b); /* close buffer */ | |
} | |
static int read_chars (lua_State *L, FILE *f, size_t n) { | |
size_t nr; /* number of chars actually read */ | |
char *p; | |
luaL_Buffer b; | |
luaL_buffinit(L, &b); | |
p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */ | |
nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */ | |
luaL_addsize(&b, nr); | |
luaL_pushresult(&b); /* close buffer */ | |
return (nr > 0); /* true iff read something */ | |
} | |
static int g_read (lua_State *L, FILE *f, int first) { | |
int nargs = lua_gettop(L) - 1; | |
int success; | |
int n; | |
clearerr(f); | |
if (nargs == 0) { /* no arguments? */ | |
success = read_line(L, f, 1); | |
n = first+1; /* to return 1 result */ | |
} | |
else { /* ensure stack space for all results and for auxlib's buffer */ | |
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); | |
success = 1; | |
for (n = first; nargs-- && success; n++) { | |
if (lua_type(L, n) == LUA_TNUMBER) { | |
size_t l = (size_t)luaL_checkinteger(L, n); | |
success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); | |
} | |
else { | |
const char *p = luaL_checkstring(L, n); | |
if (*p == '*') p++; /* skip optional '*' (for compatibility) */ | |
switch (*p) { | |
case 'n': /* number */ | |
success = read_number(L, f); | |
break; | |
case 'l': /* line */ | |
success = read_line(L, f, 1); | |
break; | |
case 'L': /* line with end-of-line */ | |
success = read_line(L, f, 0); | |
break; | |
case 'a': /* file */ | |
read_all(L, f); /* read entire file */ | |
success = 1; /* always success */ | |
break; | |
default: | |
return luaL_argerror(L, n, "invalid format"); | |
} | |
} | |
} | |
} | |
if (ferror(f)) | |
return luaL_fileresult(L, 0, NULL); | |
if (!success) { | |
lua_pop(L, 1); /* remove last result */ | |
lua_pushnil(L); /* push nil instead */ | |
} | |
return n - first; | |
} | |
static int io_read (lua_State *L) { | |
return g_read(L, getiofile(L, IO_INPUT), 1); | |
} | |
static int f_read (lua_State *L) { | |
return g_read(L, tofile(L), 2); | |
} | |
static int io_readline (lua_State *L) { | |
LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1)); | |
int i; | |
int n = (int)lua_tointeger(L, lua_upvalueindex(2)); | |
if (isclosed(p)) /* file is already closed? */ | |
return luaL_error(L, "file is already closed"); | |
lua_settop(L , 1); | |
luaL_checkstack(L, n, "too many arguments"); | |
for (i = 1; i <= n; i++) /* push arguments to 'g_read' */ | |
lua_pushvalue(L, lua_upvalueindex(3 + i)); | |
n = g_read(L, p->f, 2); /* 'n' is number of results */ | |
lua_assert(n > 0); /* should return at least a nil */ | |
if (lua_toboolean(L, -n)) /* read at least one value? */ | |
return n; /* return them */ | |
else { /* first result is nil: EOF or error */ | |
if (n > 1) { /* is there error information? */ | |
/* 2nd result is error message */ | |
return luaL_error(L, "%s", lua_tostring(L, -n + 1)); | |
} | |
if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ | |
lua_settop(L, 0); | |
lua_pushvalue(L, lua_upvalueindex(1)); | |
aux_close(L); /* close it */ | |
} | |
return 0; | |
} | |
} | |
/* }====================================================== */ | |
static int g_write (lua_State *L, FILE *f, int arg) { | |
int nargs = lua_gettop(L) - arg; | |
int status = 1; | |
for (; nargs--; arg++) { | |
if (lua_type(L, arg) == LUA_TNUMBER) { | |
/* optimization: could be done exactly as for strings */ | |
int len = lua_isinteger(L, arg) | |
? fprintf(f, LUA_INTEGER_FMT, | |
(LUAI_UACINT)lua_tointeger(L, arg)) | |
: fprintf(f, LUA_NUMBER_FMT, | |
(LUAI_UACNUMBER)lua_tonumber(L, arg)); | |
status = status && (len > 0); | |
} | |
else { | |
size_t l; | |
const char *s = luaL_checklstring(L, arg, &l); | |
status = status && (fwrite(s, sizeof(char), l, f) == l); | |
} | |
} | |
if (status) return 1; /* file handle already on stack top */ | |
else return luaL_fileresult(L, status, NULL); | |
} | |
static int io_write (lua_State *L) { | |
return g_write(L, getiofile(L, IO_OUTPUT), 1); | |
} | |
static int f_write (lua_State *L) { | |
FILE *f = tofile(L); | |
lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */ | |
return g_write(L, f, 2); | |
} | |
static int f_seek (lua_State *L) { | |
static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; | |
static const char *const modenames[] = {"set", "cur", "end", NULL}; | |
FILE *f = tofile(L); | |
int op = luaL_checkoption(L, 2, "cur", modenames); | |
lua_Integer p3 = luaL_optinteger(L, 3, 0); | |
l_seeknum offset = (l_seeknum)p3; | |
luaL_argcheck(L, (lua_Integer)offset == p3, 3, | |
"not an integer in proper range"); | |
op = l_fseek(f, offset, mode[op]); | |
if (op) | |
return luaL_fileresult(L, 0, NULL); /* error */ | |
else { | |
lua_pushinteger(L, (lua_Integer)l_ftell(f)); | |
return 1; | |
} | |
} | |
static int f_setvbuf (lua_State *L) { | |
static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; | |
static const char *const modenames[] = {"no", "full", "line", NULL}; | |
FILE *f = tofile(L); | |
int op = luaL_checkoption(L, 2, NULL, modenames); | |
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); | |
int res = setvbuf(f, NULL, mode[op], (size_t)sz); | |
return luaL_fileresult(L, res == 0, NULL); | |
} | |
static int io_flush (lua_State *L) { | |
return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); | |
} | |
static int f_flush (lua_State *L) { | |
return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL); | |
} | |
/* | |
** functions for 'io' library | |
*/ | |
static const luaL_Reg iolib[] = { | |
{"close", io_close}, | |
{"flush", io_flush}, | |
{"input", io_input}, | |
{"lines", io_lines}, | |
{"open", io_open}, | |
{"output", io_output}, | |
{"popen", io_popen}, | |
{"read", io_read}, | |
{"tmpfile", io_tmpfile}, | |
{"type", io_type}, | |
{"write", io_write}, | |
{NULL, NULL} | |
}; | |
/* | |
** methods for file handles | |
*/ | |
static const luaL_Reg flib[] = { | |
{"close", io_close}, | |
{"flush", f_flush}, | |
{"lines", f_lines}, | |
{"read", f_read}, | |
{"seek", f_seek}, | |
{"setvbuf", f_setvbuf}, | |
{"write", f_write}, | |
{"__gc", f_gc}, | |
{"__tostring", f_tostring}, | |
{NULL, NULL} | |
}; | |
static void createmeta (lua_State *L) { | |
luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ | |
lua_pushvalue(L, -1); /* push metatable */ | |
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ | |
luaL_setfuncs(L, flib, 0); /* add file methods to new metatable */ | |
lua_pop(L, 1); /* pop new metatable */ | |
} | |
/* | |
** function to (not) close the standard files stdin, stdout, and stderr | |
*/ | |
static int io_noclose (lua_State *L) { | |
LStream *p = tolstream(L); | |
p->closef = &io_noclose; /* keep file opened */ | |
lua_pushnil(L); | |
lua_pushliteral(L, "cannot close standard file"); | |
return 2; | |
} | |
static void createstdfile (lua_State *L, FILE *f, const char *k, | |
const char *fname) { | |
LStream *p = newprefile(L); | |
p->f = f; | |
p->closef = &io_noclose; | |
if (k != NULL) { | |
lua_pushvalue(L, -1); | |
lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */ | |
} | |
lua_setfield(L, -2, fname); /* add file to module */ | |
} | |
LUAMOD_API int luaopen_io (lua_State *L) { | |
luaL_newlib(L, iolib); /* new module */ | |
createmeta(L); | |
/* create (and set) default files */ | |
createstdfile(L, stdin, IO_INPUT, "stdin"); | |
createstdfile(L, stdout, IO_OUTPUT, "stdout"); | |
createstdfile(L, stderr, NULL, "stderr"); | |
return 1; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
** $Id: loslib.c,v 1.65 2016/07/18 17:58:58 roberto Exp $ | |
** Standard Operating System library | |
** See Copyright Notice in lua.h | |
*/ | |
#define loslib_c | |
#define LUA_LIB | |
#include "lprefix.h" | |
#include <errno.h> | |
#include <locale.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <time.h> | |
#include "lua.h" | |
#include "lauxlib.h" | |
#include "lualib.h" | |
/* | |
** {================================================================== | |
** List of valid conversion specifiers for the 'strftime' function; | |
** options are grouped by length; group of length 2 start with '||'. | |
** =================================================================== | |
*/ | |
#if !defined(LUA_STRFTIMEOPTIONS) /* { */ | |
/* options for ANSI C 89 (only 1-char options) */ | |
#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%" | |
/* options for ISO C 99 and POSIX */ | |
#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ | |
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ | |
/* options for Windows */ | |
#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \ | |
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ | |
#if defined(LUA_USE_WINDOWS) | |
#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN | |
#elif defined(LUA_USE_C89) | |
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC89 | |
#else /* C99 specification */ | |
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC99 | |
#endif | |
#endif /* } */ | |
/* }================================================================== */ | |
/* | |
** {================================================================== | |
** Configuration for time-related stuff | |
** =================================================================== | |
*/ | |
#if !defined(l_time_t) /* { */ | |
/* | |
** type to represent time_t in Lua | |
*/ | |
#define l_timet lua_Integer | |
#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) | |
static time_t l_checktime (lua_State *L, int arg) { | |
lua_Integer t = luaL_checkinteger(L, arg); | |
luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); | |
return (time_t)t; | |
} | |
#endif /* } */ | |
#if !defined(l_gmtime) /* { */ | |
/* | |
** By default, Lua uses gmtime/localtime, except when POSIX is available, | |
** where it uses gmtime_r/localtime_r | |
*/ | |
#if defined(LUA_USE_POSIX) /* { */ | |
#define l_gmtime(t,r) gmtime_r(t,r) | |
#define l_localtime(t,r) localtime_r(t,r) | |
#else /* }{ */ | |
/* ISO C definitions */ | |
#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t)) | |
#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t)) | |
#endif /* } */ | |
#endif /* } */ | |
/* }================================================================== */ | |
/* | |
** {================================================================== | |
** Configuration for 'tmpnam': | |
** By default, Lua uses tmpnam except when POSIX is available, where | |
** it uses mkstemp. | |
** =================================================================== | |
*/ | |
#if !defined(lua_tmpnam) /* { */ | |
#if defined(LUA_USE_POSIX) /* { */ | |
#include <unistd.h> | |
#define LUA_TMPNAMBUFSIZE 32 | |
#if !defined(LUA_TMPNAMTEMPLATE) | |
#define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX" | |
#endif | |
#define lua_tmpnam(b,e) { \ | |
strcpy(b, LUA_TMPNAMTEMPLATE); \ | |
e = mkstemp(b); \ | |
if (e != -1) close(e); \ | |
e = (e == -1); } | |
#else /* }{ */ | |
/* ISO C definitions */ | |
#define LUA_TMPNAMBUFSIZE L_tmpnam | |
#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } | |
#endif /* } */ | |
#endif /* } */ | |
/* }================================================================== */ | |
static int os_execute (lua_State *L) { | |
const char *cmd = luaL_optstring(L, 1, NULL); | |
int stat; | |
#if defined(LUA_USE_WINDOWS) | |
errno = 0; | |
#endif | |
stat = system(cmd); | |
if (cmd != NULL) | |
return luaL_execresult(L, stat); | |
else { | |
lua_pushboolean(L, stat); /* true if there is a shell */ | |
return 1; | |
} | |
} | |
static int os_remove (lua_State *L) { | |
const char *filename = luaL_checkstring(L, 1); | |
return luaL_fileresult(L, remove(filename) == 0, filename); | |
} | |
static int os_rename (lua_State *L) { | |
const char *fromname = luaL_checkstring(L, 1); | |
const char *toname = luaL_checkstring(L, 2); | |
return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); | |
} | |
static int os_tmpname (lua_State *L) { | |
char buff[LUA_TMPNAMBUFSIZE]; | |
int err; | |
lua_tmpnam(buff, err); | |
if (err) | |
return luaL_error(L, "unable to generate a unique filename"); | |
lua_pushstring(L, buff); | |
return 1; | |
} | |
static int os_getenv (lua_State *L) { | |
lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ | |
return 1; | |
} | |
static int os_clock (lua_State *L) { | |
lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); | |
return 1; | |
} | |
/* | |
** {====================================================== | |
** Time/Date operations | |
** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, | |
** wday=%w+1, yday=%j, isdst=? } | |
** ======================================================= | |
*/ | |
static void setfield (lua_State *L, const char *key, int value) { | |
lua_pushinteger(L, value); | |
lua_setfield(L, -2, key); | |
} | |
static void setboolfield (lua_State *L, const char *key, int value) { | |
if (value < 0) /* undefined? */ | |
return; /* does not set field */ | |
lua_pushboolean(L, value); | |
lua_setfield(L, -2, key); | |
} | |
/* | |
** Set all fields from structure 'tm' in the table on top of the stack | |
*/ | |
static void setallfields (lua_State *L, struct tm *stm) { | |
setfield(L, "sec", stm->tm_sec); | |
setfield(L, "min", stm->tm_min); | |
setfield(L, "hour", stm->tm_hour); | |
setfield(L, "day", stm->tm_mday); | |
setfield(L, "month", stm->tm_mon + 1); | |
setfield(L, "year", stm->tm_year + 1900); | |
setfield(L, "wday", stm->tm_wday + 1); | |
setfield(L, "yday", stm->tm_yday + 1); | |
setboolfield(L, "isdst", stm->tm_isdst); | |
} | |
static int getboolfield (lua_State *L, const char *key) { | |
int res; | |
res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1); | |
lua_pop(L, 1); | |
return res; | |
} | |
/* maximum value for date fields (to avoid arithmetic overflows with 'int') */ | |
#if !defined(L_MAXDATEFIELD) | |
#define L_MAXDATEFIELD (INT_MAX / 2) | |
#endif | |
static int getfield (lua_State *L, const char *key, int d, int delta) { | |
int isnum; | |
int t = lua_getfield(L, -1, key); /* get field and its type */ | |
lua_Integer res = lua_tointegerx(L, -1, &isnum); | |
if (!isnum) { /* field is not an integer? */ | |
if (t != LUA_TNIL) /* some other value? */ | |
return luaL_error(L, "field '%s' is not an integer", key); | |
else if (d < 0) /* absent field; no default? */ | |
return luaL_error(L, "field '%s' missing in date table", key); | |
res = d; | |
} | |
else { | |
if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD)) | |
return luaL_error(L, "field '%s' is out-of-bound", key); | |
res -= delta; | |
} | |
lua_pop(L, 1); | |
return (int)res; | |
} | |
static const char *checkoption (lua_State *L, const char *conv, | |
ptrdiff_t convlen, char *buff) { | |
const char *option = LUA_STRFTIMEOPTIONS; | |
int oplen = 1; /* length of options being checked */ | |
for (; *option != '\0' && oplen <= convlen; option += oplen) { | |
if (*option == '|') /* next block? */ | |
oplen++; /* will check options with next length (+1) */ | |
else if (memcmp(conv, option, oplen) == 0) { /* match? */ | |
memcpy(buff, conv, oplen); /* copy valid option to buffer */ | |
buff[oplen] = '\0'; | |
return conv + oplen; /* return next item */ | |
} | |
} | |
luaL_argerror(L, 1, | |
lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv)); | |
return conv; /* to avoid warnings */ | |
} | |
/* maximum size for an individual 'strftime' item */ | |
#define SIZETIMEFMT 250 | |
static int os_date (lua_State *L) { | |
size_t slen; | |
const char *s = luaL_optlstring(L, 1, "%c", &slen); | |
time_t t = luaL_opt(L, l_checktime, 2, time(NULL)); | |
const char *se = s + slen; /* 's' end */ | |
struct tm tmr, *stm; | |
if (*s == '!') { /* UTC? */ | |
stm = l_gmtime(&t, &tmr); | |
s++; /* skip '!' */ | |
} | |
else | |
stm = l_localtime(&t, &tmr); | |
if (stm == NULL) /* invalid date? */ | |
luaL_error(L, "time result cannot be represented in this installation"); | |
if (strcmp(s, "*t") == 0) { | |
lua_createtable(L, 0, 9); /* 9 = number of fields */ | |
setallfields(L, stm); | |
} | |
else { | |
char cc[4]; /* buffer for individual conversion specifiers */ | |
luaL_Buffer b; | |
cc[0] = '%'; | |
luaL_buffinit(L, &b); | |
while (s < se) { | |
if (*s != '%') /* not a conversion specifier? */ | |
luaL_addchar(&b, *s++); | |
else { | |
size_t reslen; | |
char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); | |
s++; /* skip '%' */ | |
s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */ | |
reslen = strftime(buff, SIZETIMEFMT, cc, stm); | |
luaL_addsize(&b, reslen); | |
} | |
} | |
luaL_pushresult(&b); | |
} | |
return 1; | |
} | |
static int os_time (lua_State *L) { | |
time_t t; | |
if (lua_isnoneornil(L, 1)) /* called without args? */ | |
t = time(NULL); /* get current time */ | |
else { | |
struct tm ts; | |
luaL_checktype(L, 1, LUA_TTABLE); | |
lua_settop(L, 1); /* make sure table is at the top */ | |
ts.tm_sec = getfield(L, "sec", 0, 0); | |
ts.tm_min = getfield(L, "min", 0, 0); | |
ts.tm_hour = getfield(L, "hour", 12, 0); | |
ts.tm_mday = getfield(L, "day", -1, 0); | |
ts.tm_mon = getfield(L, "month", -1, 1); | |
ts.tm_year = getfield(L, "year", -1, 1900); | |
ts.tm_isdst = getboolfield(L, "isdst"); | |
t = mktime(&ts); | |
setallfields(L, &ts); /* update fields with normalized values */ | |
} | |
if (t != (time_t)(l_timet)t || t == (time_t)(-1)) | |
luaL_error(L, "time result cannot be represented in this installation"); | |
l_pushtime(L, t); | |
return 1; | |
} | |
static int os_difftime (lua_State *L) { | |
time_t t1 = l_checktime(L, 1); | |
time_t t2 = l_checktime(L, 2); | |
lua_pushnumber(L, (lua_Number)difftime(t1, t2)); | |
return 1; | |
} | |
/* }====================================================== */ | |
static int os_setlocale (lua_State *L) { | |
static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, | |
LC_NUMERIC, LC_TIME}; | |
static const char *const catnames[] = {"all", "collate", "ctype", "monetary", | |
"numeric", "time", NULL}; | |
const char *l = luaL_optstring(L, 1, NULL); | |
int op = luaL_checkoption(L, 2, "all", catnames); | |
lua_pushstring(L, setlocale(cat[op], l)); | |
return 1; | |
} | |
static int os_exit (lua_State *L) { | |
int status; | |
if (lua_isboolean(L, 1)) | |
status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE); | |
else | |
status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS); | |
if (lua_toboolean(L, 2)) | |
lua_close(L); | |
if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */ | |
return 0; | |
} | |
static const luaL_Reg syslib[] = { | |
{"clock", os_clock}, | |
{"date", os_date}, | |
{"difftime", os_difftime}, | |
{"execute", os_execute}, | |
{"exit", os_exit}, | |
{"getenv", os_getenv}, | |
{"remove", os_remove}, | |
{"rename", os_rename}, | |
{"setlocale", os_setlocale}, | |
{"time", os_time}, | |
{"tmpname", os_tmpname}, | |
{NULL, NULL} | |
}; | |
/* }====================================================== */ | |
LUAMOD_API int luaopen_os (lua_State *L) { | |
luaL_newlib(L, syslib); | |
return 1; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff -ur lua-5.3.4.orig/src/lauxlib.c lua-5.3.4/src/lauxlib.c | |
--- lua-5.3.4.orig/src/lauxlib.c 2016-12-21 03:37:00.000000000 +0900 | |
+++ lua-5.3.4/src/lauxlib.c 2018-05-06 19:47:15.658730497 +0900 | |
@@ -273,7 +273,11 @@ | |
LUALIB_API int luaL_execresult (lua_State *L, int stat) { | |
const char *what = "exit"; /* type of termination */ | |
- if (stat == -1) /* error? */ | |
+ if (stat == -1 | |
+#if defined(LUA_USE_WINDOWS) | |
+ && errno != 0 | |
+#endif | |
+ ) /* error? */ | |
return luaL_fileresult(L, 0, NULL); | |
else { | |
l_inspectstat(stat, what); /* interpret result */ | |
diff -ur lua-5.3.4.orig/src/liolib.c lua-5.3.4/src/liolib.c | |
--- lua-5.3.4.orig/src/liolib.c 2016-12-21 03:37:00.000000000 +0900 | |
+++ lua-5.3.4/src/liolib.c 2018-05-06 19:46:52.796487629 +0900 | |
@@ -264,6 +264,9 @@ | |
*/ | |
static int io_pclose (lua_State *L) { | |
LStream *p = tolstream(L); | |
+#if defined(LUA_USE_WINDOWS) | |
+ errno = 0; | |
+#endif | |
return luaL_execresult(L, l_pclose(L, p->f)); | |
} | |
diff -ur lua-5.3.4.orig/src/loslib.c lua-5.3.4/src/loslib.c | |
--- lua-5.3.4.orig/src/loslib.c 2016-07-19 02:58:58.000000000 +0900 | |
+++ lua-5.3.4/src/loslib.c 2018-05-06 19:47:01.699582200 +0900 | |
@@ -140,7 +140,11 @@ | |
static int os_execute (lua_State *L) { | |
const char *cmd = luaL_optstring(L, 1, NULL); | |
- int stat = system(cmd); | |
+ int stat; | |
+#if defined(LUA_USE_WINDOWS) | |
+ errno = 0; | |
+#endif | |
+ stat = system(cmd); | |
if (cmd != NULL) | |
return luaL_execresult(L, stat); | |
else { |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment