Initial import
This commit is contained in:
commit
b124d99c36
155 changed files with 49639 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
*.a
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
csto
|
||||||
|
doc/
|
||||||
69
Makefile
Normal file
69
Makefile
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
PREFIX = /usr/local
|
||||||
|
|
||||||
|
CC = cc
|
||||||
|
CFLAGS = -std=c99 -pedantic -fpic -O2 -Wall -Wextra -Wno-override-init -I. -Ilua-5.4
|
||||||
|
CPPFLAGS = -D_DEFAULT_SOURCE -DLUA_USE_READLINE
|
||||||
|
LDFLAGS = -Wl,--gc-sections -lbsd -ldl -lm -lreadline
|
||||||
|
|
||||||
|
OBJS = csto.o lcallisto.o lcl.o lenvironment.o lfile.o \
|
||||||
|
ljson.o lmath.o los.o lprocess.o lsocket.o util.o
|
||||||
|
LIBS = liblua.a cjson.a socket.a
|
||||||
|
|
||||||
|
all: csto libcallisto.so
|
||||||
|
|
||||||
|
csto: ${OBJS} ${LIBS}
|
||||||
|
${CC} ${CFLAGS} -o $@ ${OBJS} ${LIBS} ${LDFLAGS}
|
||||||
|
|
||||||
|
libcallisto.so: ${OBJS} ${LIBS}
|
||||||
|
${CC} -shared ${CFLAGS} ${LDFLAGS} -o $@ ${OBJS} ${LIBS}
|
||||||
|
csto.o: csto.c lcallisto.h
|
||||||
|
${CC} ${CFLAGS} ${CPPFLAGS} -c csto.c
|
||||||
|
lcallisto.o: lcallisto.c lenvironment.h lfile.h los.h lprocess.h
|
||||||
|
${CC} ${CFLAGS} ${CPPFLAGS} -c lcallisto.c
|
||||||
|
lcl.o: lcl.c lcallisto.h
|
||||||
|
${CC} ${CFLAGS} ${CPPFLAGS} -c lcl.c
|
||||||
|
lenvironment.o: lenvironment.c lcallisto.h
|
||||||
|
${CC} ${CFLAGS} ${CPPFLAGS} -c lenvironment.c
|
||||||
|
lfile.o: lfile.c lcallisto.h
|
||||||
|
${CC} ${CFLAGS} ${CPPFLAGS} -c lfile.c
|
||||||
|
ljson.o: ljson.c lcallisto.h
|
||||||
|
${CC} ${CFLAGS} ${CPPFLAGS} -c ljson.c
|
||||||
|
lmath.o: lmath.c lcallisto.h
|
||||||
|
${CC} ${CFLAGS} ${CPPFLAGS} -c lmath.c
|
||||||
|
los.o: los.c lcallisto.h
|
||||||
|
${CC} ${CFLAGS} ${CPPFLAGS} -c los.c
|
||||||
|
lprocess.o: lprocess.c lcallisto.h
|
||||||
|
${CC} ${CFLAGS} ${CPPFLAGS} -c lprocess.c
|
||||||
|
lsocket.o: lsocket.c lcallisto.h
|
||||||
|
${CC} ${CFLAGS} ${CPPFLAGS} -c lsocket.c
|
||||||
|
|
||||||
|
util.o: util.c
|
||||||
|
${CC} ${CFLAGS} ${CPPFLAGS} -c util.c -o $@
|
||||||
|
|
||||||
|
cjson.a: external/json/*.c
|
||||||
|
${MAKE} -Cexternal/json
|
||||||
|
mv -f external/json/cjson.a cjson.a
|
||||||
|
socket.a: external/socket/src/*.c
|
||||||
|
${MAKE} -Cexternal/socket
|
||||||
|
mv -f external/socket/src/socket.a socket.a
|
||||||
|
|
||||||
|
liblua.a: lua-5.4/*.c
|
||||||
|
${MAKE} -Clua-5.4
|
||||||
|
mv -f lua-5.4/liblua.a .
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f csto libcallisto.so ${OBJS} ${LIBS}
|
||||||
|
rm -fr doc/*.html doc/modules
|
||||||
|
${MAKE} -s -Clua-5.4 clean
|
||||||
|
${MAKE} -s -Cexternal/json clean
|
||||||
|
${MAKE} -s -Cexternal/socket clean
|
||||||
|
|
||||||
|
doc:
|
||||||
|
ldoc -q . >/dev/null
|
||||||
|
|
||||||
|
install:
|
||||||
|
mkdir -p ${DESTDIR}${PREFIX}/{bin,lib}
|
||||||
|
chmod +x csto libcallisto.so
|
||||||
|
cp -f csto ${DESTDIR}${PREFIX}/bin
|
||||||
|
|
||||||
|
.PHONY: all clean doc install
|
||||||
29
config.ld
Normal file
29
config.ld
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
project = "Callisto"
|
||||||
|
title = "Callisto library documentation"
|
||||||
|
description = "Callisto - A featureful runtime for Lua 5.4"
|
||||||
|
full_description = [[
|
||||||
|
Callisto is a runtime for Lua 5.4 that includes
|
||||||
|
many useful libraries relating to file manipulation,
|
||||||
|
processes and signals, networking & sockets,
|
||||||
|
command-line option parsing, environment
|
||||||
|
variables and more. It is designed to make
|
||||||
|
Lua into a general purpose standalone scripting
|
||||||
|
language, but that doesn't mean you can't embed
|
||||||
|
it into your own program.
|
||||||
|
]]
|
||||||
|
dir = "doc"
|
||||||
|
format = "markdown"
|
||||||
|
style = "."
|
||||||
|
|
||||||
|
file = {
|
||||||
|
".",
|
||||||
|
exclude = {
|
||||||
|
"external",
|
||||||
|
"lua-5.4",
|
||||||
|
"test.lua"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_type("setting", "Settings")
|
||||||
|
|
||||||
|
-- vi: ft=lua
|
||||||
660
csto.c
Normal file
660
csto.c
Normal file
|
|
@ -0,0 +1,660 @@
|
||||||
|
/*
|
||||||
|
* Callisto stand-alone interpreter
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <lprefix.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lualib.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include <lcallisto.h>
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined(LUA_PROGNAME)
|
||||||
|
#define LUA_PROGNAME "csto"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LUA_INIT_VAR)
|
||||||
|
#define LUA_INIT_VAR "CALLISTO_INIT"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX
|
||||||
|
|
||||||
|
|
||||||
|
static lua_State *globalL = NULL;
|
||||||
|
|
||||||
|
static const char *progname = LUA_PROGNAME;
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(LUA_USE_POSIX) /* { */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Use 'sigaction' when available.
|
||||||
|
*/
|
||||||
|
static void setsignal (int sig, void (*handler)(int)) {
|
||||||
|
struct sigaction sa;
|
||||||
|
sa.sa_handler = handler;
|
||||||
|
sa.sa_flags = 0;
|
||||||
|
sigemptyset(&sa.sa_mask); /* do not mask any signal */
|
||||||
|
sigaction(sig, &sa, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* }{ */
|
||||||
|
|
||||||
|
#define setsignal signal
|
||||||
|
|
||||||
|
#endif /* } */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Hook set by signal function to stop the interpreter.
|
||||||
|
*/
|
||||||
|
static void lstop (lua_State *L, lua_Debug *ar) {
|
||||||
|
(void)ar; /* unused arg. */
|
||||||
|
lua_sethook(L, NULL, 0, 0); /* reset hook */
|
||||||
|
luaL_error(L, "interrupted!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Function to be called at a C signal. Because a C signal cannot
|
||||||
|
** just change a Lua state (as there is no proper synchronization),
|
||||||
|
** this function only sets a hook that, when called, will stop the
|
||||||
|
** interpreter.
|
||||||
|
*/
|
||||||
|
static void laction (int i) {
|
||||||
|
int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT;
|
||||||
|
setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
|
||||||
|
lua_sethook(globalL, lstop, flag, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void print_usage (const char *badoption) {
|
||||||
|
lua_writestringerror("%s: ", progname);
|
||||||
|
if (badoption[1] == 'e' || badoption[1] == 'l')
|
||||||
|
lua_writestringerror("'%s' needs argument\n", badoption);
|
||||||
|
else
|
||||||
|
lua_writestringerror("unrecognized option '%s'\n", badoption);
|
||||||
|
lua_writestringerror(
|
||||||
|
"usage: %s [-e stat] [-l mod|g=mod] [-EivW] [script [args]]\n"
|
||||||
|
"available options are:\n"
|
||||||
|
" -e stat execute string 'stat'\n"
|
||||||
|
" -i enter interactive mode after executing 'script'\n"
|
||||||
|
" -l mod require library 'mod' into global 'mod'\n"
|
||||||
|
" -l g=mod require library 'mod' into global 'g'\n"
|
||||||
|
" -v show version information\n"
|
||||||
|
" -E ignore environment variables\n"
|
||||||
|
" -W turn warnings on\n"
|
||||||
|
" -- stop handling options\n"
|
||||||
|
" - stop handling options and execute stdin\n"
|
||||||
|
,
|
||||||
|
progname);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Prints an error message, adding the program name in front of it
|
||||||
|
** (if present)
|
||||||
|
*/
|
||||||
|
static void l_message (const char *pname, const char *msg) {
|
||||||
|
if (pname) lua_writestringerror("%s: ", pname);
|
||||||
|
lua_writestringerror("%s\n", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Check whether 'status' is not OK and, if so, prints the error
|
||||||
|
** message on the top of the stack. It assumes that the error object
|
||||||
|
** is a string, as it was either generated by Lua or by 'msghandler'.
|
||||||
|
*/
|
||||||
|
static int report (lua_State *L, int status) {
|
||||||
|
if (status != LUA_OK) {
|
||||||
|
const char *msg = lua_tostring(L, -1);
|
||||||
|
l_message(progname, msg);
|
||||||
|
lua_pop(L, 1); /* remove message */
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Message handler used to run all chunks
|
||||||
|
*/
|
||||||
|
static int msghandler (lua_State *L) {
|
||||||
|
const char *msg = lua_tostring(L, 1);
|
||||||
|
if (msg == NULL) { /* is error object not a string? */
|
||||||
|
if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */
|
||||||
|
lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */
|
||||||
|
return 1; /* that is the message */
|
||||||
|
else
|
||||||
|
msg = lua_pushfstring(L, "(error object is a %s value)",
|
||||||
|
luaL_typename(L, 1));
|
||||||
|
}
|
||||||
|
luaL_traceback(L, L, msg, 1); /* append a standard traceback */
|
||||||
|
return 1; /* return the traceback */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Interface to 'lua_pcall', which sets appropriate message function
|
||||||
|
** and C-signal handler. Used to run all chunks.
|
||||||
|
*/
|
||||||
|
static int docall (lua_State *L, int narg, int nres) {
|
||||||
|
int status;
|
||||||
|
int base = lua_gettop(L) - narg; /* function index */
|
||||||
|
lua_pushcfunction(L, msghandler); /* push message handler */
|
||||||
|
lua_insert(L, base); /* put it under function and args */
|
||||||
|
globalL = L; /* to be available to 'laction' */
|
||||||
|
setsignal(SIGINT, laction); /* set C-signal handler */
|
||||||
|
status = lua_pcall(L, narg, nres, base);
|
||||||
|
setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */
|
||||||
|
lua_remove(L, base); /* remove message handler from the stack */
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void print_version (void) {
|
||||||
|
lua_writestring(CALLISTO_COPYRIGHT, strlen(CALLISTO_COPYRIGHT));
|
||||||
|
lua_writeline();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Create the 'arg' table, which stores all arguments from the
|
||||||
|
** command line ('argv'). It should be aligned so that, at index 0,
|
||||||
|
** it has 'argv[script]', which is the script name. The arguments
|
||||||
|
** to the script (everything after 'script') go to positive indices;
|
||||||
|
** other arguments (before the script name) go to negative indices.
|
||||||
|
** If there is no script name, assume interpreter's name as base.
|
||||||
|
*/
|
||||||
|
static void createargtable (lua_State *L, char **argv, int argc, int script) {
|
||||||
|
int i, narg;
|
||||||
|
if (script == argc) script = 0; /* no script name? */
|
||||||
|
narg = argc - (script + 1); /* number of positive indices */
|
||||||
|
lua_createtable(L, narg, script + 1);
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
lua_pushstring(L, argv[i]);
|
||||||
|
lua_rawseti(L, -2, i - script);
|
||||||
|
}
|
||||||
|
lua_setglobal(L, "arg");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int dochunk (lua_State *L, int status) {
|
||||||
|
if (status == LUA_OK) status = docall(L, 0, 0);
|
||||||
|
return report(L, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int dofile (lua_State *L, const char *name) {
|
||||||
|
return dochunk(L, luaL_loadfile(L, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int dostring (lua_State *L, const char *s, const char *name) {
|
||||||
|
return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Receives 'globname[=modname]' and runs 'globname = require(modname)'.
|
||||||
|
*/
|
||||||
|
static int dolibrary (lua_State *L, char *globname) {
|
||||||
|
int status;
|
||||||
|
char *modname = strchr(globname, '=');
|
||||||
|
if (modname == NULL) /* no explicit name? */
|
||||||
|
modname = globname; /* module name is equal to global name */
|
||||||
|
else {
|
||||||
|
*modname = '\0'; /* global name ends here */
|
||||||
|
modname++; /* module name starts after the '=' */
|
||||||
|
}
|
||||||
|
lua_getglobal(L, "require");
|
||||||
|
lua_pushstring(L, modname);
|
||||||
|
status = docall(L, 1, 1); /* call 'require(modname)' */
|
||||||
|
if (status == LUA_OK)
|
||||||
|
lua_setglobal(L, globname); /* globname = require(modname) */
|
||||||
|
return report(L, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Push on the stack the contents of table 'arg' from 1 to #arg
|
||||||
|
*/
|
||||||
|
static int pushargs (lua_State *L) {
|
||||||
|
int i, n;
|
||||||
|
if (lua_getglobal(L, "arg") != LUA_TTABLE)
|
||||||
|
luaL_error(L, "'arg' is not a table");
|
||||||
|
n = (int)luaL_len(L, -1);
|
||||||
|
luaL_checkstack(L, n + 3, "too many arguments to script");
|
||||||
|
for (i = 1; i <= n; i++)
|
||||||
|
lua_rawgeti(L, -i, i);
|
||||||
|
lua_remove(L, -i); /* remove table from the stack */
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int handle_script (lua_State *L, char **argv) {
|
||||||
|
int status;
|
||||||
|
const char *fname = argv[0];
|
||||||
|
if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0)
|
||||||
|
fname = NULL; /* stdin */
|
||||||
|
status = luaL_loadfile(L, fname);
|
||||||
|
if (status == LUA_OK) {
|
||||||
|
int n = pushargs(L); /* push arguments to script */
|
||||||
|
status = docall(L, n, LUA_MULTRET);
|
||||||
|
}
|
||||||
|
return report(L, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* bits of various argument indicators in 'args' */
|
||||||
|
#define has_error 1 /* bad option */
|
||||||
|
#define has_i 2 /* -i */
|
||||||
|
#define has_v 4 /* -v */
|
||||||
|
#define has_e 8 /* -e */
|
||||||
|
#define has_E 16 /* -E */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Traverses all arguments from 'argv', returning a mask with those
|
||||||
|
** needed before running any Lua code (or an error code if it finds
|
||||||
|
** any invalid argument). 'first' returns the first not-handled argument
|
||||||
|
** (either the script name or a bad argument in case of error).
|
||||||
|
*/
|
||||||
|
static int collectargs (char **argv, int *first) {
|
||||||
|
int args = 0;
|
||||||
|
int i;
|
||||||
|
for (i = 1; argv[i] != NULL; i++) {
|
||||||
|
*first = i;
|
||||||
|
if (argv[i][0] != '-') /* not an option? */
|
||||||
|
return args; /* stop handling options */
|
||||||
|
switch (argv[i][1]) { /* else check option */
|
||||||
|
case '-': /* '--' */
|
||||||
|
if (argv[i][2] != '\0') /* extra characters after '--'? */
|
||||||
|
return has_error; /* invalid option */
|
||||||
|
*first = i + 1;
|
||||||
|
return args;
|
||||||
|
case '\0': /* '-' */
|
||||||
|
return args; /* script "name" is '-' */
|
||||||
|
case 'E':
|
||||||
|
if (argv[i][2] != '\0') /* extra characters? */
|
||||||
|
return has_error; /* invalid option */
|
||||||
|
args |= has_E;
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
if (argv[i][2] != '\0') /* extra characters? */
|
||||||
|
return has_error; /* invalid option */
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */
|
||||||
|
case 'v':
|
||||||
|
if (argv[i][2] != '\0') /* extra characters? */
|
||||||
|
return has_error; /* invalid option */
|
||||||
|
args |= has_v;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
args |= has_e; /* FALLTHROUGH */
|
||||||
|
case 'l': /* both options need an argument */
|
||||||
|
if (argv[i][2] == '\0') { /* no concatenated argument? */
|
||||||
|
i++; /* try next 'argv' */
|
||||||
|
if (argv[i] == NULL || argv[i][0] == '-')
|
||||||
|
return has_error; /* no next argument or it is another option */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: /* invalid option */
|
||||||
|
return has_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*first = i; /* no script name */
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Processes options 'e' and 'l', which involve running Lua code, and
|
||||||
|
** 'W', which also affects the state.
|
||||||
|
** Returns 0 if some code raises an error.
|
||||||
|
*/
|
||||||
|
static int runargs (lua_State *L, char **argv, int n) {
|
||||||
|
int i;
|
||||||
|
for (i = 1; i < n; i++) {
|
||||||
|
int option = argv[i][1];
|
||||||
|
lua_assert(argv[i][0] == '-'); /* already checked */
|
||||||
|
switch (option) {
|
||||||
|
case 'e': case 'l': {
|
||||||
|
int status;
|
||||||
|
char *extra = argv[i] + 2; /* both options need an argument */
|
||||||
|
if (*extra == '\0') extra = argv[++i];
|
||||||
|
lua_assert(extra != NULL);
|
||||||
|
status = (option == 'e')
|
||||||
|
? dostring(L, extra, "=(command line)")
|
||||||
|
: dolibrary(L, extra);
|
||||||
|
if (status != LUA_OK) return 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'W':
|
||||||
|
lua_warning(L, "@on", 0); /* warnings on */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int handle_luainit (lua_State *L) {
|
||||||
|
const char *name = "=" LUA_INITVARVERSION;
|
||||||
|
const char *init = getenv(name + 1);
|
||||||
|
if (init == NULL) {
|
||||||
|
name = "=" LUA_INIT_VAR;
|
||||||
|
init = getenv(name + 1); /* try alternative name */
|
||||||
|
}
|
||||||
|
if (init == NULL) return LUA_OK;
|
||||||
|
else if (init[0] == '@')
|
||||||
|
return dofile(L, init+1);
|
||||||
|
else
|
||||||
|
return dostring(L, init, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** {==================================================================
|
||||||
|
** Read-Eval-Print Loop (REPL)
|
||||||
|
** ===================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(LUA_PROMPT)
|
||||||
|
#define LUA_PROMPT "> "
|
||||||
|
#define LUA_PROMPT2 ">> "
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(LUA_MAXINPUT)
|
||||||
|
#define LUA_MAXINPUT 512
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** lua_stdin_is_tty detects whether the standard input is a 'tty' (that
|
||||||
|
** is, whether we're running lua interactively).
|
||||||
|
*/
|
||||||
|
#if !defined(lua_stdin_is_tty) /* { */
|
||||||
|
|
||||||
|
#if defined(LUA_USE_POSIX) /* { */
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#define lua_stdin_is_tty() isatty(0)
|
||||||
|
|
||||||
|
#elif defined(LUA_USE_WINDOWS) /* }{ */
|
||||||
|
|
||||||
|
#include <io.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#define lua_stdin_is_tty() _isatty(_fileno(stdin))
|
||||||
|
|
||||||
|
#else /* }{ */
|
||||||
|
|
||||||
|
/* ISO C definition */
|
||||||
|
#define lua_stdin_is_tty() 1 /* assume stdin is a tty */
|
||||||
|
|
||||||
|
#endif /* } */
|
||||||
|
|
||||||
|
#endif /* } */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** lua_readline defines how to show a prompt and then read a line from
|
||||||
|
** the standard input.
|
||||||
|
** lua_saveline defines how to "save" a read line in a "history".
|
||||||
|
** lua_freeline defines how to free a line read by lua_readline.
|
||||||
|
*/
|
||||||
|
#if !defined(lua_readline) /* { */
|
||||||
|
|
||||||
|
#if defined(LUA_USE_READLINE) /* { */
|
||||||
|
|
||||||
|
#include <readline/readline.h>
|
||||||
|
#include <readline/history.h>
|
||||||
|
#define lua_initreadline(L) ((void)L, rl_readline_name="lua")
|
||||||
|
#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
|
||||||
|
#define lua_saveline(L,line) ((void)L, add_history(line))
|
||||||
|
#define lua_freeline(L,b) ((void)L, free(b))
|
||||||
|
|
||||||
|
#else /* }{ */
|
||||||
|
|
||||||
|
#define lua_initreadline(L) ((void)L)
|
||||||
|
#define lua_readline(L,b,p) \
|
||||||
|
((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \
|
||||||
|
fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */
|
||||||
|
#define lua_saveline(L,line) { (void)L; (void)line; }
|
||||||
|
#define lua_freeline(L,b) { (void)L; (void)b; }
|
||||||
|
|
||||||
|
#endif /* } */
|
||||||
|
|
||||||
|
#endif /* } */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return the string to be used as a prompt by the interpreter. Leave
|
||||||
|
** the string (or nil, if using the default value) on the stack, to keep
|
||||||
|
** it anchored.
|
||||||
|
*/
|
||||||
|
static const char *get_prompt (lua_State *L, int firstline) {
|
||||||
|
if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL)
|
||||||
|
return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */
|
||||||
|
else { /* apply 'tostring' over the value */
|
||||||
|
const char *p = luaL_tolstring(L, -1, NULL);
|
||||||
|
lua_remove(L, -2); /* remove original value */
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mark in error messages for incomplete statements */
|
||||||
|
#define EOFMARK "<eof>"
|
||||||
|
#define marklen (sizeof(EOFMARK)/sizeof(char) - 1)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Check whether 'status' signals a syntax error and the error
|
||||||
|
** message at the top of the stack ends with the above mark for
|
||||||
|
** incomplete statements.
|
||||||
|
*/
|
||||||
|
static int incomplete (lua_State *L, int status) {
|
||||||
|
if (status == LUA_ERRSYNTAX) {
|
||||||
|
size_t lmsg;
|
||||||
|
const char *msg = lua_tolstring(L, -1, &lmsg);
|
||||||
|
if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0; /* else... */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Prompt the user, read a line, and push it into the Lua stack.
|
||||||
|
*/
|
||||||
|
static int pushline (lua_State *L, int firstline) {
|
||||||
|
char buffer[LUA_MAXINPUT];
|
||||||
|
char *b = buffer;
|
||||||
|
size_t l;
|
||||||
|
const char *prmt = get_prompt(L, firstline);
|
||||||
|
int readstatus = lua_readline(L, b, prmt);
|
||||||
|
if (readstatus == 0)
|
||||||
|
return 0; /* no input (prompt will be popped by caller) */
|
||||||
|
lua_pop(L, 1); /* remove prompt */
|
||||||
|
l = strlen(b);
|
||||||
|
if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
|
||||||
|
b[--l] = '\0'; /* remove it */
|
||||||
|
if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */
|
||||||
|
lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */
|
||||||
|
else
|
||||||
|
lua_pushlstring(L, b, l);
|
||||||
|
lua_freeline(L, b);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Try to compile line on the stack as 'return <line>;'; on return, stack
|
||||||
|
** has either compiled chunk or original line (if compilation failed).
|
||||||
|
*/
|
||||||
|
static int addreturn (lua_State *L) {
|
||||||
|
const char *line = lua_tostring(L, -1); /* original line */
|
||||||
|
const char *retline = lua_pushfstring(L, "return %s;", line);
|
||||||
|
int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin");
|
||||||
|
if (status == LUA_OK) {
|
||||||
|
lua_remove(L, -2); /* remove modified line */
|
||||||
|
if (line[0] != '\0') /* non empty? */
|
||||||
|
lua_saveline(L, line); /* keep history */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Read multiple lines until a complete Lua statement
|
||||||
|
*/
|
||||||
|
static int multiline (lua_State *L) {
|
||||||
|
for (;;) { /* repeat until gets a complete statement */
|
||||||
|
size_t len;
|
||||||
|
const char *line = lua_tolstring(L, 1, &len); /* get what it has */
|
||||||
|
int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */
|
||||||
|
if (!incomplete(L, status) || !pushline(L, 0)) {
|
||||||
|
lua_saveline(L, line); /* keep history */
|
||||||
|
return status; /* cannot or should not try to add continuation line */
|
||||||
|
}
|
||||||
|
lua_pushliteral(L, "\n"); /* add newline... */
|
||||||
|
lua_insert(L, -2); /* ...between the two lines */
|
||||||
|
lua_concat(L, 3); /* join them */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Read a line and try to load (compile) it first as an expression (by
|
||||||
|
** adding "return " in front of it) and second as a statement. Return
|
||||||
|
** the final status of load/call with the resulting function (if any)
|
||||||
|
** in the top of the stack.
|
||||||
|
*/
|
||||||
|
static int loadline (lua_State *L) {
|
||||||
|
int status;
|
||||||
|
lua_settop(L, 0);
|
||||||
|
if (!pushline(L, 1))
|
||||||
|
return -1; /* no input */
|
||||||
|
if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */
|
||||||
|
status = multiline(L); /* try as command, maybe with continuation lines */
|
||||||
|
lua_remove(L, 1); /* remove line from the stack */
|
||||||
|
lua_assert(lua_gettop(L) == 1);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Prints (calling the Lua 'print' function) any values on the stack
|
||||||
|
*/
|
||||||
|
static void l_print (lua_State *L) {
|
||||||
|
int n = lua_gettop(L);
|
||||||
|
if (n > 0) { /* any result to be printed? */
|
||||||
|
luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
|
||||||
|
lua_getglobal(L, "print");
|
||||||
|
lua_insert(L, 1);
|
||||||
|
if (lua_pcall(L, n, 0, 0) != LUA_OK)
|
||||||
|
l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)",
|
||||||
|
lua_tostring(L, -1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and
|
||||||
|
** print any results.
|
||||||
|
*/
|
||||||
|
static void doREPL (lua_State *L) {
|
||||||
|
int status;
|
||||||
|
const char *oldprogname = progname;
|
||||||
|
progname = NULL; /* no 'progname' on errors in interactive mode */
|
||||||
|
lua_initreadline(L);
|
||||||
|
while ((status = loadline(L)) != -1) {
|
||||||
|
if (status == LUA_OK)
|
||||||
|
status = docall(L, 0, LUA_MULTRET);
|
||||||
|
if (status == LUA_OK) l_print(L);
|
||||||
|
else report(L, status);
|
||||||
|
}
|
||||||
|
lua_settop(L, 0); /* clear stack */
|
||||||
|
lua_writeline();
|
||||||
|
progname = oldprogname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* }================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Main body of stand-alone interpreter (to be called in protected mode).
|
||||||
|
** Reads the options and handles them all.
|
||||||
|
*/
|
||||||
|
static int pmain (lua_State *L) {
|
||||||
|
int argc = (int)lua_tointeger(L, 1);
|
||||||
|
char **argv = (char **)lua_touserdata(L, 2);
|
||||||
|
int script;
|
||||||
|
int args = collectargs(argv, &script);
|
||||||
|
luaL_checkversion(L); /* check that interpreter has correct version */
|
||||||
|
if (argv[0] && argv[0][0]) progname = argv[0];
|
||||||
|
if (args == has_error) { /* bad arg? */
|
||||||
|
print_usage(argv[script]); /* 'script' has index of bad arg. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (args & has_v) /* option '-v'? */
|
||||||
|
print_version();
|
||||||
|
if (args & has_E) { /* option '-E'? */
|
||||||
|
lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */
|
||||||
|
lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
|
||||||
|
}
|
||||||
|
createargtable(L, argv, argc, script); /* create table 'arg' */
|
||||||
|
lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */
|
||||||
|
if (!(args & has_E)) { /* no option '-E'? */
|
||||||
|
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
|
||||||
|
return 0; /* error running LUA_INIT */
|
||||||
|
}
|
||||||
|
if (!runargs(L, argv, script)) /* execute arguments -e and -l */
|
||||||
|
return 0; /* something failed */
|
||||||
|
if (script < argc && /* execute main script (if there is one) */
|
||||||
|
handle_script(L, argv + script) != LUA_OK)
|
||||||
|
return 0;
|
||||||
|
if (args & has_i) /* -i option? */
|
||||||
|
doREPL(L); /* do read-eval-print loop */
|
||||||
|
else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */
|
||||||
|
if (lua_stdin_is_tty()) { /* running in interactive mode? */
|
||||||
|
print_version();
|
||||||
|
doREPL(L); /* do read-eval-print loop */
|
||||||
|
}
|
||||||
|
else dofile(L, NULL); /* executes stdin as a file */
|
||||||
|
}
|
||||||
|
lua_pushboolean(L, 1); /* signal no errors */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main (int argc, char **argv) {
|
||||||
|
int status, result;
|
||||||
|
lua_State *L = callisto_newstate(); /* create state */
|
||||||
|
if (L == NULL) {
|
||||||
|
l_message(argv[0], "cannot create state: not enough memory");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
|
||||||
|
lua_pushinteger(L, argc); /* 1st argument */
|
||||||
|
lua_pushlightuserdata(L, argv); /* 2nd argument */
|
||||||
|
status = lua_pcall(L, 2, 1, 0); /* do the call */
|
||||||
|
result = lua_toboolean(L, -1); /* get result */
|
||||||
|
report(L, status);
|
||||||
|
lua_close(L);
|
||||||
|
return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
11
external/json/.gitignore
vendored
Normal file
11
external/json/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
*.html
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
notes
|
||||||
|
packages
|
||||||
|
tags
|
||||||
|
tests/utf8.dat
|
||||||
|
*~
|
||||||
|
*.swp
|
||||||
|
go
|
||||||
|
test_case.lua
|
||||||
63
external/json/.travis.yml
vendored
Normal file
63
external/json/.travis.yml
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
sudo: required
|
||||||
|
dist: Focal
|
||||||
|
|
||||||
|
os: linux
|
||||||
|
|
||||||
|
language: c
|
||||||
|
|
||||||
|
compiler:
|
||||||
|
- gcc
|
||||||
|
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- cppcheck
|
||||||
|
- valgrind
|
||||||
|
- cpanminus
|
||||||
|
- libipc-run3-perl
|
||||||
|
- lua5.1
|
||||||
|
- lua5.1-dev
|
||||||
|
- cmake
|
||||||
|
|
||||||
|
cache:
|
||||||
|
apt: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- JOBS=3
|
||||||
|
- LUAROCKS_VER=2.4.2
|
||||||
|
matrix:
|
||||||
|
#- LUA=1 LUA_DIR=/usr LUA_INCLUDE_DIR=$LUA_DIR/include/lua5.1
|
||||||
|
- LUAJIT=1 LUA_DIR=/usr/local LUA_INCLUDE_DIR=$LUA_DIR/include/luajit-2.1 LUA_SUFFIX=--lua-suffix=jit
|
||||||
|
|
||||||
|
install:
|
||||||
|
- sudo ln -s /usr/bin/cmake /usr/local/bin/cmake
|
||||||
|
- if [ -n "$LUAJIT" ]; then git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git; fi
|
||||||
|
- if [ -n "$LUAJIT" ]; then cd ./luajit2; fi
|
||||||
|
- if [ -n "$LUAJIT" ]; then make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT' > build.log 2>&1 || (cat build.log && exit 1); fi
|
||||||
|
- if [ -n "$LUAJIT" ]; then sudo make install > build.log 2>&1 || (cat build.log && exit 1); fi
|
||||||
|
- if [ -n "$LUAJIT" ]; then cd ..; fi
|
||||||
|
- if [ -n "$LUAJIT" ]; then sudo ln -s $LUA_DIR/bin/luajit $LUA_DIR/bin/lua; fi
|
||||||
|
- sudo cpanm --notest Test::Base Test::LongString > build.log 2>&1 || (cat build.log && exit 1)
|
||||||
|
- wget https://luarocks.github.io/luarocks/releases/luarocks-$LUAROCKS_VER.tar.gz
|
||||||
|
- tar -zxf luarocks-$LUAROCKS_VER.tar.gz
|
||||||
|
- cd luarocks-$LUAROCKS_VER
|
||||||
|
- ./configure --with-lua=$LUA_DIR --with-lua-include=$LUA_INCLUDE_DIR $LUA_SUFFIX
|
||||||
|
- make build
|
||||||
|
- sudo make install
|
||||||
|
- cd ..
|
||||||
|
|
||||||
|
script:
|
||||||
|
- cppcheck -i ./luajit2 --force --error-exitcode=1 --enable=warning . > build.log 2>&1 || (cat build.log && exit 1)
|
||||||
|
- bash runtests.sh
|
||||||
|
- make
|
||||||
|
- prove -Itests tests
|
||||||
|
- TEST_LUA_USE_VALGRIND=1 prove -Itests tests > build.log 2>&1; export e=$?
|
||||||
|
- cat build.log
|
||||||
|
- grep -E '^==[0-9]+==' build.log; if [ "$?" == 0 ]; then exit 1; else exit $e; fi
|
||||||
|
- cmake -DUSE_INTERNAL_FPCONV=1 .
|
||||||
|
- make
|
||||||
|
- prove -Itests tests
|
||||||
|
- TEST_LUA_USE_VALGRIND=1 prove -Itests tests > build.log 2>&1; export e=$?
|
||||||
|
- cat build.log
|
||||||
|
- grep -E '^==[0-9]+==' build.log; if [ "$?" == 0 ]; then exit 1; else exit $e; fi
|
||||||
20
external/json/LICENSE
vendored
Normal file
20
external/json/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
40
external/json/Makefile
vendored
Normal file
40
external/json/Makefile
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
##### Available defines for CJSON_CFLAGS #####
|
||||||
|
##
|
||||||
|
## USE_INTERNAL_ISINF: Workaround for Solaris platforms missing isinf().
|
||||||
|
## DISABLE_INVALID_NUMBERS: Permanently disable invalid JSON numbers:
|
||||||
|
## NaN, Infinity, hex.
|
||||||
|
##
|
||||||
|
## Optional built-in number conversion uses the following defines:
|
||||||
|
## USE_INTERNAL_FPCONV: Use builtin strtod/dtoa for numeric conversions.
|
||||||
|
## IEEE_BIG_ENDIAN: Required on big endian architectures.
|
||||||
|
## MULTIPLE_THREADS: Must be set when Lua CJSON may be used in a
|
||||||
|
## multi-threaded application. Requries _pthreads_.
|
||||||
|
|
||||||
|
##### Build defaults #####
|
||||||
|
CC = cc
|
||||||
|
AR = ar rcu
|
||||||
|
CFLAGS = -O2 -Wall -Wno-unused-function -pedantic -fpic -DNDEBUG -I../../lua-5.4
|
||||||
|
OBJS = dtoa.o fpconv.o g_fmt.o lua_cjson.o strbuf.o
|
||||||
|
|
||||||
|
##### End customisable sections #####
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
all: cjson.a
|
||||||
|
|
||||||
|
cjson.a: ${OBJS}
|
||||||
|
${AR} $@ ${OBJS}
|
||||||
|
|
||||||
|
dtoa.o:
|
||||||
|
${CC} ${CFLAGS} -o $@ -c dtoa.c
|
||||||
|
fpconv.o:
|
||||||
|
${CC} ${CFLAGS} -o $@ -c fpconv.c
|
||||||
|
g_fmt.o:
|
||||||
|
${CC} ${CFLAGS} -o $@ -c g_fmt.c
|
||||||
|
lua_cjson.o:
|
||||||
|
${CC} ${CFLAGS} -o $@ -c lua_cjson.c
|
||||||
|
strbuf.o:
|
||||||
|
${CC} ${CFLAGS} -o $@ -c strbuf.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f cjson.a ${OBJS}
|
||||||
208
external/json/README.md
vendored
Normal file
208
external/json/README.md
vendored
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
Name
|
||||||
|
====
|
||||||
|
|
||||||
|
lua-cjson - Fast JSON encoding/parsing
|
||||||
|
|
||||||
|
Table of Contents
|
||||||
|
=================
|
||||||
|
|
||||||
|
* [Name](#name)
|
||||||
|
* [Description](#description)
|
||||||
|
* [Additions to mpx/lua](#additions)
|
||||||
|
* [encode_empty_table_as_object](#encode_empty_table_as_object)
|
||||||
|
* [empty_array](#empty_array)
|
||||||
|
* [array_mt](#array_mt)
|
||||||
|
* [empty_array_mt](#empty_array_mt)
|
||||||
|
* [encode_number_precision](#encode_number_precision)
|
||||||
|
* [encode_escape_forward_slash](#encode_escape_forward_slash)
|
||||||
|
* [decode_array_with_array_mt](#decode_array_with_array_mt)
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
This fork of [mpx/lua-cjson](https://github.com/mpx/lua-cjson) is included in
|
||||||
|
the [OpenResty](https://openresty.org/) bundle and includes a few bugfixes and
|
||||||
|
improvements, especially to facilitate the encoding of empty tables as JSON Arrays.
|
||||||
|
|
||||||
|
Please refer to the [lua-cjson documentation](http://www.kyne.com.au/~mark/software/lua-cjson.php)
|
||||||
|
for standard usage, this README only provides informations regarding this fork's additions.
|
||||||
|
|
||||||
|
See [`mpx/master..openresty/master`](https://github.com/mpx/lua-cjson/compare/master...openresty:master)
|
||||||
|
for the complete history of changes.
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
||||||
|
|
||||||
|
Additions
|
||||||
|
=========
|
||||||
|
|
||||||
|
encode_empty_table_as_object
|
||||||
|
----------------------------
|
||||||
|
**syntax:** `cjson.encode_empty_table_as_object(true|false|"on"|"off")`
|
||||||
|
|
||||||
|
Change the default behavior when encoding an empty Lua table.
|
||||||
|
|
||||||
|
By default, empty Lua tables are encoded as empty JSON Objects (`{}`). If this is set to false,
|
||||||
|
empty Lua tables will be encoded as empty JSON Arrays instead (`[]`).
|
||||||
|
|
||||||
|
This method either accepts a boolean or a string (`"on"`, `"off"`).
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
||||||
|
|
||||||
|
empty_array
|
||||||
|
-----------
|
||||||
|
**syntax:** `cjson.empty_array`
|
||||||
|
|
||||||
|
A lightuserdata, similar to `cjson.null`, which will be encoded as an empty JSON Array by
|
||||||
|
`cjson.encode()`.
|
||||||
|
|
||||||
|
For example, since `encode_empty_table_as_object` is `true` by default:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local cjson = require "cjson"
|
||||||
|
|
||||||
|
local json = cjson.encode({
|
||||||
|
foo = "bar",
|
||||||
|
some_object = {},
|
||||||
|
some_array = cjson.empty_array
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
This will generate:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"foo": "bar",
|
||||||
|
"some_object": {},
|
||||||
|
"some_array": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
||||||
|
|
||||||
|
array_mt
|
||||||
|
--------
|
||||||
|
**syntax:** `setmetatable({}, cjson.array_mt)`
|
||||||
|
|
||||||
|
When lua-cjson encodes a table with this metatable, it will systematically
|
||||||
|
encode it as a JSON Array. The resulting, encoded Array will contain the array
|
||||||
|
part of the table, and will be of the same length as the `#` operator on that
|
||||||
|
table. Holes in the table will be encoded with the `null` JSON value.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local t = { "hello", "world" }
|
||||||
|
setmetatable(t, cjson.array_mt)
|
||||||
|
cjson.encode(t) -- ["hello","world"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Or:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local t = {}
|
||||||
|
t[1] = "one"
|
||||||
|
t[2] = "two"
|
||||||
|
t[4] = "three"
|
||||||
|
t.foo = "bar"
|
||||||
|
setmetatable(t, cjson.array_mt)
|
||||||
|
cjson.encode(t) -- ["one","two",null,"three"]
|
||||||
|
```
|
||||||
|
|
||||||
|
This value was introduced in the `2.1.0.5` release of this module.
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
||||||
|
|
||||||
|
empty_array_mt
|
||||||
|
--------------
|
||||||
|
**syntax:** `setmetatable({}, cjson.empty_array_mt)`
|
||||||
|
|
||||||
|
A metatable which can "tag" a table as a JSON Array in case it is empty (that is, if the
|
||||||
|
table has no elements, `cjson.encode()` will encode it as an empty JSON Array).
|
||||||
|
|
||||||
|
Instead of:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local function serialize(arr)
|
||||||
|
if #arr < 1 then
|
||||||
|
arr = cjson.empty_array
|
||||||
|
end
|
||||||
|
|
||||||
|
return cjson.encode({some_array = arr})
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
This is more concise:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local function serialize(arr)
|
||||||
|
setmetatable(arr, cjson.empty_array_mt)
|
||||||
|
|
||||||
|
return cjson.encode({some_array = arr})
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Both will generate:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"some_array": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
||||||
|
|
||||||
|
encode_number_precision
|
||||||
|
-----------------------
|
||||||
|
**syntax:** `cjson.encode_number_precision(precision)`
|
||||||
|
|
||||||
|
This fork allows encoding of numbers with a `precision` up to 16 decimals (vs. 14 in mpx/lua-cjson).
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
||||||
|
|
||||||
|
encode_escape_forward_slash
|
||||||
|
---------------------------
|
||||||
|
**syntax:** `cjson.encode_escape_forward_slash(enabled)`
|
||||||
|
|
||||||
|
**default:** true
|
||||||
|
|
||||||
|
If enabled, forward slash '/' will be encoded as '\\/'.
|
||||||
|
|
||||||
|
If disabled, forward slash '/' will be encoded as '/' (no escape is applied).
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
||||||
|
|
||||||
|
decode_array_with_array_mt
|
||||||
|
--------------------------
|
||||||
|
**syntax:** `cjson.decode_array_with_array_mt(enabled)`
|
||||||
|
|
||||||
|
**default:** false
|
||||||
|
|
||||||
|
If enabled, JSON Arrays decoded by `cjson.decode` will result in Lua
|
||||||
|
tables with the [`array_mt`](#array_mt) metatable. This can ensure a 1-to-1
|
||||||
|
relationship between arrays upon multiple encoding/decoding of your
|
||||||
|
JSON data with this module.
|
||||||
|
|
||||||
|
If disabled, JSON Arrays will be decoded to plain Lua tables, without
|
||||||
|
the `array_mt` metatable.
|
||||||
|
|
||||||
|
The `enabled` argument is a boolean.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local cjson = require "cjson"
|
||||||
|
|
||||||
|
-- default behavior
|
||||||
|
local my_json = [[{"my_array":[]}]]
|
||||||
|
local t = cjson.decode(my_json)
|
||||||
|
cjson.encode(t) -- {"my_array":{}} back to an object
|
||||||
|
|
||||||
|
-- now, if this behavior is enabled
|
||||||
|
cjson.decode_array_with_array_mt(true)
|
||||||
|
|
||||||
|
local my_json = [[{"my_array":[]}]]
|
||||||
|
local t = cjson.decode(my_json)
|
||||||
|
cjson.encode(t) -- {"my_array":[]} properly re-encoded as an array
|
||||||
|
```
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
||||||
9
external/json/THANKS
vendored
Normal file
9
external/json/THANKS
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
The following people have helped with bug reports, testing and/or
|
||||||
|
suggestions:
|
||||||
|
|
||||||
|
- Louis-Philippe Perron (@loopole)
|
||||||
|
- Ondřej Jirman
|
||||||
|
- Steve Donovan <steve.j.donovan@gmail.com>
|
||||||
|
- Zhang "agentzh" Yichun <agentzh@gmail.com>
|
||||||
|
|
||||||
|
Thanks!
|
||||||
6205
external/json/dtoa.c
vendored
Normal file
6205
external/json/dtoa.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
78
external/json/dtoa_config.h
vendored
Normal file
78
external/json/dtoa_config.h
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
#ifndef _DTOA_CONFIG_H
|
||||||
|
#define _DTOA_CONFIG_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Ensure dtoa.c does not USE_LOCALE. Lua CJSON must not use locale
|
||||||
|
* aware conversion routines. */
|
||||||
|
#undef USE_LOCALE
|
||||||
|
|
||||||
|
/* dtoa.c should not touch errno, Lua CJSON does not use it, and it
|
||||||
|
* may not be threadsafe */
|
||||||
|
#define NO_ERRNO
|
||||||
|
|
||||||
|
#define Long int32_t
|
||||||
|
#define ULong uint32_t
|
||||||
|
#define Llong int64_t
|
||||||
|
#define ULLong uint64_t
|
||||||
|
|
||||||
|
#ifdef IEEE_BIG_ENDIAN
|
||||||
|
#define IEEE_MC68k
|
||||||
|
#else
|
||||||
|
#define IEEE_8087
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MALLOC xmalloc
|
||||||
|
|
||||||
|
static void *xmalloc(size_t size)
|
||||||
|
{
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
p = malloc(size);
|
||||||
|
if (!p) {
|
||||||
|
fprintf(stderr, "Out of memory");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MULTIPLE_THREADS
|
||||||
|
|
||||||
|
/* Enable locking to support multi-threaded applications */
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
static pthread_mutex_t private_dtoa_lock[2] = {
|
||||||
|
PTHREAD_MUTEX_INITIALIZER,
|
||||||
|
PTHREAD_MUTEX_INITIALIZER
|
||||||
|
};
|
||||||
|
|
||||||
|
#define dtoa_get_threadno pthread_self
|
||||||
|
void
|
||||||
|
set_max_dtoa_threads(unsigned int n);
|
||||||
|
|
||||||
|
#define ACQUIRE_DTOA_LOCK(n) do { \
|
||||||
|
int r = pthread_mutex_lock(&private_dtoa_lock[n]); \
|
||||||
|
if (r) { \
|
||||||
|
fprintf(stderr, "pthread_mutex_lock failed with %d\n", r); \
|
||||||
|
abort(); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define FREE_DTOA_LOCK(n) do { \
|
||||||
|
int r = pthread_mutex_unlock(&private_dtoa_lock[n]); \
|
||||||
|
if (r) { \
|
||||||
|
fprintf(stderr, "pthread_mutex_unlock failed with %d\n", r);\
|
||||||
|
abort(); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif /* MULTIPLE_THREADS */
|
||||||
|
|
||||||
|
#endif /* _DTOA_CONFIG_H */
|
||||||
|
|
||||||
|
/* vi:ai et sw=4 ts=4:
|
||||||
|
*/
|
||||||
211
external/json/fpconv.c
vendored
Normal file
211
external/json/fpconv.c
vendored
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
/* fpconv - Floating point conversion routines
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011-2012 Mark Pulford <mark@kyne.com.au>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries
|
||||||
|
* with locale support will break when the decimal separator is a comma.
|
||||||
|
*
|
||||||
|
* fpconv_* will around these issues with a translation buffer if required.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "fpconv.h"
|
||||||
|
|
||||||
|
/* Workaround for MSVC */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define inline __inline
|
||||||
|
#define snprintf sprintf_s
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Lua CJSON assumes the locale is the same for all threads within a
|
||||||
|
* process and doesn't change after initialisation.
|
||||||
|
*
|
||||||
|
* This avoids the need for per thread storage or expensive checks
|
||||||
|
* for call. */
|
||||||
|
static char locale_decimal_point = '.';
|
||||||
|
|
||||||
|
/* In theory multibyte decimal_points are possible, but
|
||||||
|
* Lua CJSON only supports UTF-8 and known locales only have
|
||||||
|
* single byte decimal points ([.,]).
|
||||||
|
*
|
||||||
|
* localconv() may not be thread safe (=>crash), and nl_langinfo() is
|
||||||
|
* not supported on some platforms. Use sprintf() instead - if the
|
||||||
|
* locale does change, at least Lua CJSON won't crash. */
|
||||||
|
static void fpconv_update_locale(void)
|
||||||
|
{
|
||||||
|
char buf[8];
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%g", 0.5);
|
||||||
|
|
||||||
|
/* Failing this test might imply the platform has a buggy dtoa
|
||||||
|
* implementation or wide characters */
|
||||||
|
if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) {
|
||||||
|
fprintf(stderr, "Error: wide characters found or printf() bug.");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
locale_decimal_point = buf[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for a valid number character: [-+0-9a-yA-Y.]
|
||||||
|
* Eg: -0.6e+5, infinity, 0xF0.F0pF0
|
||||||
|
*
|
||||||
|
* Used to find the probable end of a number. It doesn't matter if
|
||||||
|
* invalid characters are counted - strtod() will find the valid
|
||||||
|
* number if it exists. The risk is that slightly more memory might
|
||||||
|
* be allocated before a parse error occurs. */
|
||||||
|
static inline int valid_number_character(char ch)
|
||||||
|
{
|
||||||
|
char lower_ch;
|
||||||
|
|
||||||
|
if ('0' <= ch && ch <= '9')
|
||||||
|
return 1;
|
||||||
|
if (ch == '-' || ch == '+' || ch == '.')
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Hex digits, exponent (e), base (p), "infinity",.. */
|
||||||
|
lower_ch = ch | 0x20;
|
||||||
|
if ('a' <= lower_ch && lower_ch <= 'y')
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the size of the buffer required for a strtod locale
|
||||||
|
* conversion. */
|
||||||
|
static int strtod_buffer_size(const char *s)
|
||||||
|
{
|
||||||
|
const char *p = s;
|
||||||
|
|
||||||
|
while (valid_number_character(*p))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
return p - s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Similar to strtod(), but must be passed the current locale's decimal point
|
||||||
|
* character. Guaranteed to be called at the start of any valid number in a string */
|
||||||
|
double fpconv_strtod(const char *nptr, char **endptr)
|
||||||
|
{
|
||||||
|
char localbuf[FPCONV_G_FMT_BUFSIZE];
|
||||||
|
char *buf, *endbuf, *dp;
|
||||||
|
int buflen;
|
||||||
|
double value;
|
||||||
|
|
||||||
|
/* System strtod() is fine when decimal point is '.' */
|
||||||
|
if (locale_decimal_point == '.')
|
||||||
|
return strtod(nptr, endptr);
|
||||||
|
|
||||||
|
buflen = strtod_buffer_size(nptr);
|
||||||
|
if (!buflen) {
|
||||||
|
/* No valid characters found, standard strtod() return */
|
||||||
|
*endptr = (char *)nptr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Duplicate number into buffer */
|
||||||
|
if (buflen >= FPCONV_G_FMT_BUFSIZE) {
|
||||||
|
/* Handle unusually large numbers */
|
||||||
|
buf = malloc(buflen + 1);
|
||||||
|
if (!buf) {
|
||||||
|
fprintf(stderr, "Out of memory");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* This is the common case.. */
|
||||||
|
buf = localbuf;
|
||||||
|
}
|
||||||
|
memcpy(buf, nptr, buflen);
|
||||||
|
buf[buflen] = 0;
|
||||||
|
|
||||||
|
/* Update decimal point character if found */
|
||||||
|
dp = strchr(buf, '.');
|
||||||
|
if (dp)
|
||||||
|
*dp = locale_decimal_point;
|
||||||
|
|
||||||
|
value = strtod(buf, &endbuf);
|
||||||
|
*endptr = (char *)&nptr[endbuf - buf];
|
||||||
|
if (buflen >= FPCONV_G_FMT_BUFSIZE)
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "fmt" must point to a buffer of at least 6 characters */
|
||||||
|
static void set_number_format(char *fmt, int precision)
|
||||||
|
{
|
||||||
|
int d1, d2, i;
|
||||||
|
|
||||||
|
assert(1 <= precision && precision <= 16);
|
||||||
|
|
||||||
|
/* Create printf format (%.14g) from precision */
|
||||||
|
d1 = precision / 10;
|
||||||
|
d2 = precision % 10;
|
||||||
|
fmt[0] = '%';
|
||||||
|
fmt[1] = '.';
|
||||||
|
i = 2;
|
||||||
|
if (d1) {
|
||||||
|
fmt[i++] = '0' + d1;
|
||||||
|
}
|
||||||
|
fmt[i++] = '0' + d2;
|
||||||
|
fmt[i++] = 'g';
|
||||||
|
fmt[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assumes there is always at least 32 characters available in the target buffer */
|
||||||
|
int fpconv_g_fmt(char *str, double num, int precision)
|
||||||
|
{
|
||||||
|
char buf[FPCONV_G_FMT_BUFSIZE];
|
||||||
|
char fmt[6];
|
||||||
|
int len;
|
||||||
|
char *b;
|
||||||
|
|
||||||
|
set_number_format(fmt, precision);
|
||||||
|
|
||||||
|
/* Pass through when decimal point character is dot. */
|
||||||
|
if (locale_decimal_point == '.')
|
||||||
|
return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num);
|
||||||
|
|
||||||
|
/* snprintf() to a buffer then translate for other decimal point characters */
|
||||||
|
len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num);
|
||||||
|
|
||||||
|
/* Copy into target location. Translate decimal point if required */
|
||||||
|
b = buf;
|
||||||
|
do {
|
||||||
|
*str++ = (*b == locale_decimal_point ? '.' : *b);
|
||||||
|
} while(*b++);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fpconv_init(void)
|
||||||
|
{
|
||||||
|
fpconv_update_locale();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vi:ai et sw=4 ts=4:
|
||||||
|
*/
|
||||||
32
external/json/fpconv.h
vendored
Normal file
32
external/json/fpconv.h
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Lua CJSON floating point conversion routines */
|
||||||
|
|
||||||
|
/* Buffer required to store the largest string representation of a double.
|
||||||
|
*
|
||||||
|
* Longest double printed with %.14g is 21 characters long:
|
||||||
|
* -1.7976931348623e+308 */
|
||||||
|
# define FPCONV_G_FMT_BUFSIZE 32
|
||||||
|
|
||||||
|
#ifdef USE_INTERNAL_FPCONV
|
||||||
|
#ifdef MULTIPLE_THREADS
|
||||||
|
#include "dtoa_config.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
static inline void fpconv_init()
|
||||||
|
{
|
||||||
|
// Add one to try and avoid core id multiplier alignment
|
||||||
|
set_max_dtoa_threads((sysconf(_SC_NPROCESSORS_CONF) + 1) * 3);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline void fpconv_init()
|
||||||
|
{
|
||||||
|
/* Do nothing - not required */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
extern void fpconv_init(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int fpconv_g_fmt(char*, double, int);
|
||||||
|
extern double fpconv_strtod(const char*, char**);
|
||||||
|
|
||||||
|
/* vi:ai et sw=4 ts=4:
|
||||||
|
*/
|
||||||
111
external/json/g_fmt.c
vendored
Normal file
111
external/json/g_fmt.c
vendored
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
/****************************************************************
|
||||||
|
*
|
||||||
|
* The author of this software is David M. Gay.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1991, 1996 by Lucent Technologies.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose without fee is hereby granted, provided that this entire notice
|
||||||
|
* is included in all copies of any software which is or includes a copy
|
||||||
|
* or modification of this software and in all copies of the supporting
|
||||||
|
* documentation for such software.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
|
||||||
|
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
|
||||||
|
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
|
||||||
|
*
|
||||||
|
***************************************************************/
|
||||||
|
|
||||||
|
/* g_fmt(buf,x) stores the closest decimal approximation to x in buf;
|
||||||
|
* it suffices to declare buf
|
||||||
|
* char buf[32];
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
extern char *dtoa(double, int, int, int *, int *, char **);
|
||||||
|
extern int g_fmt(char *, double, int);
|
||||||
|
extern void freedtoa(char*);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
fpconv_g_fmt(char *b, double x, int precision)
|
||||||
|
{
|
||||||
|
register int i, k;
|
||||||
|
register char *s;
|
||||||
|
int decpt, j, sign;
|
||||||
|
char *b0, *s0, *se;
|
||||||
|
|
||||||
|
b0 = b;
|
||||||
|
#ifdef IGNORE_ZERO_SIGN
|
||||||
|
if (!x) {
|
||||||
|
*b++ = '0';
|
||||||
|
*b = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
s = s0 = dtoa(x, 2, precision, &decpt, &sign, &se);
|
||||||
|
if (sign)
|
||||||
|
*b++ = '-';
|
||||||
|
if (decpt == 9999) /* Infinity or Nan */ {
|
||||||
|
while((*b++ = *s++));
|
||||||
|
/* "b" is used to calculate the return length. Decrement to exclude the
|
||||||
|
* Null terminator from the length */
|
||||||
|
b--;
|
||||||
|
goto done0;
|
||||||
|
}
|
||||||
|
if (decpt <= -4 || decpt > precision) {
|
||||||
|
*b++ = *s++;
|
||||||
|
if (*s) {
|
||||||
|
*b++ = '.';
|
||||||
|
while((*b = *s++))
|
||||||
|
b++;
|
||||||
|
}
|
||||||
|
*b++ = 'e';
|
||||||
|
/* sprintf(b, "%+.2d", decpt - 1); */
|
||||||
|
if (--decpt < 0) {
|
||||||
|
*b++ = '-';
|
||||||
|
decpt = -decpt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*b++ = '+';
|
||||||
|
for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10);
|
||||||
|
for(;;) {
|
||||||
|
i = decpt / k;
|
||||||
|
*b++ = i + '0';
|
||||||
|
if (--j <= 0)
|
||||||
|
break;
|
||||||
|
decpt -= i*k;
|
||||||
|
decpt *= 10;
|
||||||
|
}
|
||||||
|
*b = 0;
|
||||||
|
}
|
||||||
|
else if (decpt <= 0) {
|
||||||
|
*b++ = '0';
|
||||||
|
*b++ = '.';
|
||||||
|
for(; decpt < 0; decpt++)
|
||||||
|
*b++ = '0';
|
||||||
|
while((*b++ = *s++));
|
||||||
|
b--;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
while((*b = *s++)) {
|
||||||
|
b++;
|
||||||
|
if (--decpt == 0 && *s)
|
||||||
|
*b++ = '.';
|
||||||
|
}
|
||||||
|
for(; decpt > 0; decpt--)
|
||||||
|
*b++ = '0';
|
||||||
|
*b = 0;
|
||||||
|
}
|
||||||
|
done0:
|
||||||
|
freedtoa(s0);
|
||||||
|
#ifdef IGNORE_ZERO_SIGN
|
||||||
|
done:
|
||||||
|
#endif
|
||||||
|
return b - b0;
|
||||||
|
}
|
||||||
312
external/json/lua/cjson/util.lua
vendored
Normal file
312
external/json/lua/cjson/util.lua
vendored
Normal file
|
|
@ -0,0 +1,312 @@
|
||||||
|
local json = require "cjson"
|
||||||
|
|
||||||
|
local unpack = unpack or table.unpack
|
||||||
|
|
||||||
|
local maxn = table.maxn or function(t)
|
||||||
|
local max = 0
|
||||||
|
for k,v in pairs(t) do
|
||||||
|
if type(k) == "number" and k > max then
|
||||||
|
max = k
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return max
|
||||||
|
end
|
||||||
|
|
||||||
|
local _one_of_mt = {}
|
||||||
|
|
||||||
|
local function one_of(t)
|
||||||
|
setmetatable(t, _one_of_mt)
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_one_of(t)
|
||||||
|
return type(t) == "table" and getmetatable(t) == _one_of_mt
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Various common routines used by the Lua CJSON package
|
||||||
|
--
|
||||||
|
-- Mark Pulford <mark@kyne.com.au>
|
||||||
|
|
||||||
|
-- Determine with a Lua table can be treated as an array.
|
||||||
|
-- Explicitly returns "not an array" for very sparse arrays.
|
||||||
|
-- Returns:
|
||||||
|
-- -1 Not an array
|
||||||
|
-- 0 Empty table
|
||||||
|
-- >0 Highest index in the array
|
||||||
|
local function is_array(table)
|
||||||
|
local max = 0
|
||||||
|
local count = 0
|
||||||
|
for k, v in pairs(table) do
|
||||||
|
if type(k) == "number" then
|
||||||
|
if k > max then max = k end
|
||||||
|
count = count + 1
|
||||||
|
else
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if max > count * 2 then
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
|
||||||
|
return max
|
||||||
|
end
|
||||||
|
|
||||||
|
local serialise_value
|
||||||
|
|
||||||
|
local function serialise_table(value, indent, depth)
|
||||||
|
local spacing, spacing2, indent2
|
||||||
|
if indent then
|
||||||
|
spacing = "\n" .. indent
|
||||||
|
spacing2 = spacing .. " "
|
||||||
|
indent2 = indent .. " "
|
||||||
|
else
|
||||||
|
spacing, spacing2, indent2 = " ", " ", false
|
||||||
|
end
|
||||||
|
depth = depth + 1
|
||||||
|
if depth > 50 then
|
||||||
|
return "Cannot serialise any further: too many nested tables"
|
||||||
|
end
|
||||||
|
|
||||||
|
local max = is_array(value)
|
||||||
|
|
||||||
|
local comma = false
|
||||||
|
local prefix = "{"
|
||||||
|
if is_one_of(value) then
|
||||||
|
prefix = "ONE_OF{"
|
||||||
|
end
|
||||||
|
local fragment = { prefix .. spacing2 }
|
||||||
|
if max > 0 then
|
||||||
|
-- Serialise array
|
||||||
|
for i = 1, max do
|
||||||
|
if comma then
|
||||||
|
table.insert(fragment, "," .. spacing2)
|
||||||
|
end
|
||||||
|
table.insert(fragment, serialise_value(value[i], indent2, depth))
|
||||||
|
comma = true
|
||||||
|
end
|
||||||
|
elseif max < 0 then
|
||||||
|
-- Serialise table
|
||||||
|
for k, v in pairs(value) do
|
||||||
|
if comma then
|
||||||
|
table.insert(fragment, "," .. spacing2)
|
||||||
|
end
|
||||||
|
table.insert(fragment,
|
||||||
|
("[%s] = %s"):format(serialise_value(k, indent2, depth),
|
||||||
|
serialise_value(v, indent2, depth)))
|
||||||
|
comma = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(fragment, spacing .. "}")
|
||||||
|
|
||||||
|
return table.concat(fragment)
|
||||||
|
end
|
||||||
|
|
||||||
|
function serialise_value(value, indent, depth)
|
||||||
|
if indent == nil then indent = "" end
|
||||||
|
if depth == nil then depth = 0 end
|
||||||
|
|
||||||
|
if value == json.null then
|
||||||
|
return "json.null"
|
||||||
|
elseif type(value) == "string" then
|
||||||
|
return ("%q"):format(value)
|
||||||
|
elseif type(value) == "nil" or type(value) == "number" or
|
||||||
|
type(value) == "boolean" then
|
||||||
|
return tostring(value)
|
||||||
|
elseif type(value) == "table" then
|
||||||
|
return serialise_table(value, indent, depth)
|
||||||
|
else
|
||||||
|
return "\"<" .. type(value) .. ">\""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function file_load(filename)
|
||||||
|
local file
|
||||||
|
if filename == nil then
|
||||||
|
file = io.stdin
|
||||||
|
else
|
||||||
|
local err
|
||||||
|
file, err = io.open(filename, "rb")
|
||||||
|
if file == nil then
|
||||||
|
error(("Unable to read '%s': %s"):format(filename, err))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local data = file:read("*a")
|
||||||
|
|
||||||
|
if filename ~= nil then
|
||||||
|
file:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
if data == nil then
|
||||||
|
error("Failed to read " .. filename)
|
||||||
|
end
|
||||||
|
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
local function file_save(filename, data)
|
||||||
|
local file
|
||||||
|
if filename == nil then
|
||||||
|
file = io.stdout
|
||||||
|
else
|
||||||
|
local err
|
||||||
|
file, err = io.open(filename, "wb")
|
||||||
|
if file == nil then
|
||||||
|
error(("Unable to write '%s': %s"):format(filename, err))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
file:write(data)
|
||||||
|
if filename ~= nil then
|
||||||
|
file:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function compare_values(val1, val2)
|
||||||
|
if is_one_of(val2) then
|
||||||
|
for _, option in ipairs(val2) do
|
||||||
|
if compare_values(val1, option) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local type1 = type(val1)
|
||||||
|
local type2 = type(val2)
|
||||||
|
if type1 ~= type2 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check for NaN
|
||||||
|
if type1 == "number" and val1 ~= val1 and val2 ~= val2 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if type1 ~= "table" then
|
||||||
|
return val1 == val2
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check_keys stores all the keys that must be checked in val2
|
||||||
|
local check_keys = {}
|
||||||
|
for k, _ in pairs(val1) do
|
||||||
|
check_keys[k] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in pairs(val2) do
|
||||||
|
if not check_keys[k] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if not compare_values(val1[k], val2[k]) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
check_keys[k] = nil
|
||||||
|
end
|
||||||
|
for k, _ in pairs(check_keys) do
|
||||||
|
-- Not the same if any keys from val1 were not found in val2
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local test_count_pass = 0
|
||||||
|
local test_count_total = 0
|
||||||
|
|
||||||
|
local function run_test_summary()
|
||||||
|
return test_count_pass, test_count_total
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run_test(testname, func, input, should_work, output)
|
||||||
|
local function status_line(name, status, value)
|
||||||
|
local statusmap = { [true] = ":success", [false] = ":error" }
|
||||||
|
if status ~= nil then
|
||||||
|
name = name .. statusmap[status]
|
||||||
|
end
|
||||||
|
print(("[%s] %s"):format(name, serialise_value(value, false)))
|
||||||
|
end
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
local tmp = { pcall(func, unpack(input)) }
|
||||||
|
local success = tmp[1]
|
||||||
|
for i = 2, maxn(tmp) do
|
||||||
|
result[i - 1] = tmp[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
local correct = false
|
||||||
|
if success == should_work and compare_values(result, output) then
|
||||||
|
correct = true
|
||||||
|
test_count_pass = test_count_pass + 1
|
||||||
|
end
|
||||||
|
test_count_total = test_count_total + 1
|
||||||
|
|
||||||
|
local teststatus = { [true] = "PASS", [false] = "FAIL" }
|
||||||
|
print(("==> Test [%d] %s: %s"):format(test_count_total, testname,
|
||||||
|
teststatus[correct]))
|
||||||
|
|
||||||
|
status_line("Input", nil, input)
|
||||||
|
if not correct then
|
||||||
|
status_line("Expected", should_work, output)
|
||||||
|
end
|
||||||
|
status_line("Received", success, result)
|
||||||
|
print()
|
||||||
|
|
||||||
|
return correct, result
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run_test_group(tests)
|
||||||
|
local function run_helper(name, func, input)
|
||||||
|
if type(name) == "string" and #name > 0 then
|
||||||
|
print("==> " .. name)
|
||||||
|
end
|
||||||
|
-- Not a protected call, these functions should never generate errors.
|
||||||
|
func(unpack(input or {}))
|
||||||
|
print()
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, v in ipairs(tests) do
|
||||||
|
-- Run the helper if "should_work" is missing
|
||||||
|
if v[4] == nil then
|
||||||
|
run_helper(unpack(v))
|
||||||
|
else
|
||||||
|
run_test(unpack(v))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Run a Lua script in a separate environment
|
||||||
|
local function run_script(script, env)
|
||||||
|
local env = env or {}
|
||||||
|
local func
|
||||||
|
|
||||||
|
-- Use setfenv() if it exists, otherwise assume Lua 5.2 load() exists
|
||||||
|
if _G.setfenv then
|
||||||
|
func = loadstring(script)
|
||||||
|
if func then
|
||||||
|
setfenv(func, env)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
func = load(script, nil, nil, env)
|
||||||
|
end
|
||||||
|
|
||||||
|
if func == nil then
|
||||||
|
error("Invalid syntax.")
|
||||||
|
end
|
||||||
|
func()
|
||||||
|
|
||||||
|
return env
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Export functions
|
||||||
|
return {
|
||||||
|
serialise_value = serialise_value,
|
||||||
|
file_load = file_load,
|
||||||
|
file_save = file_save,
|
||||||
|
compare_values = compare_values,
|
||||||
|
run_test_summary = run_test_summary,
|
||||||
|
run_test = run_test,
|
||||||
|
run_test_group = run_test_group,
|
||||||
|
run_script = run_script,
|
||||||
|
one_of = one_of
|
||||||
|
}
|
||||||
|
|
||||||
|
-- vi:ai et sw=4 ts=4:
|
||||||
14
external/json/lua/json2lua.lua
vendored
Executable file
14
external/json/lua/json2lua.lua
vendored
Executable file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/usr/bin/env lua
|
||||||
|
|
||||||
|
-- usage: json2lua.lua [json_file]
|
||||||
|
--
|
||||||
|
-- Eg:
|
||||||
|
-- echo '[ "testing" ]' | ./json2lua.lua
|
||||||
|
-- ./json2lua.lua test.json
|
||||||
|
|
||||||
|
local json = require "cjson"
|
||||||
|
local util = require "cjson.util"
|
||||||
|
|
||||||
|
local json_text = util.file_load(arg[1])
|
||||||
|
local t = json.decode(json_text)
|
||||||
|
print(util.serialise_value(t))
|
||||||
20
external/json/lua/lua2json.lua
vendored
Executable file
20
external/json/lua/lua2json.lua
vendored
Executable file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env lua
|
||||||
|
|
||||||
|
-- usage: lua2json.lua [lua_file]
|
||||||
|
--
|
||||||
|
-- Eg:
|
||||||
|
-- echo '{ "testing" }' | ./lua2json.lua
|
||||||
|
-- ./lua2json.lua test.lua
|
||||||
|
|
||||||
|
local json = require "cjson"
|
||||||
|
local util = require "cjson.util"
|
||||||
|
|
||||||
|
local env = {
|
||||||
|
json = { null = json.null },
|
||||||
|
null = json.null
|
||||||
|
}
|
||||||
|
|
||||||
|
local t = util.run_script("data = " .. util.file_load(arg[1]), env)
|
||||||
|
print(json.encode(t.data))
|
||||||
|
|
||||||
|
-- vi:ai et sw=4 ts=4:
|
||||||
1710
external/json/lua_cjson.c
vendored
Normal file
1710
external/json/lua_cjson.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
251
external/json/strbuf.c
vendored
Normal file
251
external/json/strbuf.c
vendored
Normal file
|
|
@ -0,0 +1,251 @@
|
||||||
|
/* strbuf - String buffer routines
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "strbuf.h"
|
||||||
|
|
||||||
|
static void die(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list arg;
|
||||||
|
|
||||||
|
va_start(arg, fmt);
|
||||||
|
vfprintf(stderr, fmt, arg);
|
||||||
|
va_end(arg);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void strbuf_init(strbuf_t *s, int len)
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
|
||||||
|
if (len <= 0)
|
||||||
|
size = STRBUF_DEFAULT_SIZE;
|
||||||
|
else
|
||||||
|
size = len + 1; /* \0 terminator */
|
||||||
|
|
||||||
|
s->buf = NULL;
|
||||||
|
s->size = size;
|
||||||
|
s->length = 0;
|
||||||
|
s->increment = STRBUF_DEFAULT_INCREMENT;
|
||||||
|
s->dynamic = 0;
|
||||||
|
s->reallocs = 0;
|
||||||
|
s->debug = 0;
|
||||||
|
|
||||||
|
s->buf = malloc(size);
|
||||||
|
if (!s->buf)
|
||||||
|
die("Out of memory");
|
||||||
|
|
||||||
|
strbuf_ensure_null(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
strbuf_t *strbuf_new(int len)
|
||||||
|
{
|
||||||
|
strbuf_t *s;
|
||||||
|
|
||||||
|
s = malloc(sizeof(strbuf_t));
|
||||||
|
if (!s)
|
||||||
|
die("Out of memory");
|
||||||
|
|
||||||
|
strbuf_init(s, len);
|
||||||
|
|
||||||
|
/* Dynamic strbuf allocation / deallocation */
|
||||||
|
s->dynamic = 1;
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void strbuf_set_increment(strbuf_t *s, int increment)
|
||||||
|
{
|
||||||
|
/* Increment > 0: Linear buffer growth rate
|
||||||
|
* Increment < -1: Exponential buffer growth rate */
|
||||||
|
if (increment == 0 || increment == -1)
|
||||||
|
die("BUG: Invalid string increment");
|
||||||
|
|
||||||
|
s->increment = increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void debug_stats(strbuf_t *s)
|
||||||
|
{
|
||||||
|
if (s->debug) {
|
||||||
|
fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
|
||||||
|
(long)s, s->reallocs, s->length, s->size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If strbuf_t has not been dynamically allocated, strbuf_free() can
|
||||||
|
* be called any number of times strbuf_init() */
|
||||||
|
void strbuf_free(strbuf_t *s)
|
||||||
|
{
|
||||||
|
debug_stats(s);
|
||||||
|
|
||||||
|
if (s->buf) {
|
||||||
|
free(s->buf);
|
||||||
|
s->buf = NULL;
|
||||||
|
}
|
||||||
|
if (s->dynamic)
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strbuf_free_to_string(strbuf_t *s, int *len)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
debug_stats(s);
|
||||||
|
|
||||||
|
strbuf_ensure_null(s);
|
||||||
|
|
||||||
|
buf = s->buf;
|
||||||
|
if (len)
|
||||||
|
*len = s->length;
|
||||||
|
|
||||||
|
if (s->dynamic)
|
||||||
|
free(s);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int calculate_new_size(strbuf_t *s, int len)
|
||||||
|
{
|
||||||
|
int reqsize, newsize;
|
||||||
|
|
||||||
|
if (len <= 0)
|
||||||
|
die("BUG: Invalid strbuf length requested");
|
||||||
|
|
||||||
|
/* Ensure there is room for optional NULL termination */
|
||||||
|
reqsize = len + 1;
|
||||||
|
|
||||||
|
/* If the user has requested to shrink the buffer, do it exactly */
|
||||||
|
if (s->size > reqsize)
|
||||||
|
return reqsize;
|
||||||
|
|
||||||
|
newsize = s->size;
|
||||||
|
if (s->increment < 0) {
|
||||||
|
/* Exponential sizing */
|
||||||
|
while (newsize < reqsize)
|
||||||
|
newsize *= -s->increment;
|
||||||
|
} else if (s->increment != 0) {
|
||||||
|
/* Linear sizing */
|
||||||
|
newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
|
||||||
|
* optional termination). */
|
||||||
|
void strbuf_resize(strbuf_t *s, int len)
|
||||||
|
{
|
||||||
|
int newsize;
|
||||||
|
|
||||||
|
newsize = calculate_new_size(s, len);
|
||||||
|
|
||||||
|
if (s->debug > 1) {
|
||||||
|
fprintf(stderr, "strbuf(%lx) resize: %d => %d\n",
|
||||||
|
(long)s, s->size, newsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->size = newsize;
|
||||||
|
s->buf = realloc(s->buf, s->size);
|
||||||
|
if (!s->buf)
|
||||||
|
die("Out of memory");
|
||||||
|
s->reallocs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void strbuf_append_string(strbuf_t *s, const char *str)
|
||||||
|
{
|
||||||
|
int space, i;
|
||||||
|
|
||||||
|
space = strbuf_empty_length(s);
|
||||||
|
|
||||||
|
for (i = 0; str[i]; i++) {
|
||||||
|
if (space < 1) {
|
||||||
|
strbuf_resize(s, s->length + 1);
|
||||||
|
space = strbuf_empty_length(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->buf[s->length] = str[i];
|
||||||
|
s->length++;
|
||||||
|
space--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* strbuf_append_fmt() should only be used when an upper bound
|
||||||
|
* is known for the output string. */
|
||||||
|
void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list arg;
|
||||||
|
int fmt_len;
|
||||||
|
|
||||||
|
strbuf_ensure_empty_length(s, len);
|
||||||
|
|
||||||
|
va_start(arg, fmt);
|
||||||
|
fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
if (fmt_len < 0)
|
||||||
|
die("BUG: Unable to convert number"); /* This should never happen.. */
|
||||||
|
|
||||||
|
s->length += fmt_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* strbuf_append_fmt_retry() can be used when the there is no known
|
||||||
|
* upper bound for the output string. */
|
||||||
|
void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list arg;
|
||||||
|
int fmt_len, try;
|
||||||
|
int empty_len;
|
||||||
|
|
||||||
|
/* If the first attempt to append fails, resize the buffer appropriately
|
||||||
|
* and try again */
|
||||||
|
for (try = 0; ; try++) {
|
||||||
|
va_start(arg, fmt);
|
||||||
|
/* Append the new formatted string */
|
||||||
|
/* fmt_len is the length of the string required, excluding the
|
||||||
|
* trailing NULL */
|
||||||
|
empty_len = strbuf_empty_length(s);
|
||||||
|
/* Add 1 since there is also space to store the terminating NULL. */
|
||||||
|
fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
if (fmt_len <= empty_len)
|
||||||
|
break; /* SUCCESS */
|
||||||
|
if (try > 0)
|
||||||
|
die("BUG: length of formatted string changed");
|
||||||
|
|
||||||
|
strbuf_resize(s, s->length + fmt_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->length += fmt_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vi:ai et sw=4 ts=4:
|
||||||
|
*/
|
||||||
165
external/json/strbuf.h
vendored
Normal file
165
external/json/strbuf.h
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
/* strbuf - String buffer routines
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
/* Workaround for MSVC */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define inline __inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Size: Total bytes allocated to *buf
|
||||||
|
* Length: String length, excluding optional NULL terminator.
|
||||||
|
* Increment: Allocation increments when resizing the string buffer.
|
||||||
|
* Dynamic: True if created via strbuf_new()
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *buf;
|
||||||
|
int size;
|
||||||
|
int length;
|
||||||
|
int increment;
|
||||||
|
int dynamic;
|
||||||
|
int reallocs;
|
||||||
|
int debug;
|
||||||
|
} strbuf_t;
|
||||||
|
|
||||||
|
#ifndef STRBUF_DEFAULT_SIZE
|
||||||
|
#define STRBUF_DEFAULT_SIZE 1023
|
||||||
|
#endif
|
||||||
|
#ifndef STRBUF_DEFAULT_INCREMENT
|
||||||
|
#define STRBUF_DEFAULT_INCREMENT -2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initialise */
|
||||||
|
extern strbuf_t *strbuf_new(int len);
|
||||||
|
extern void strbuf_init(strbuf_t *s, int len);
|
||||||
|
extern void strbuf_set_increment(strbuf_t *s, int increment);
|
||||||
|
|
||||||
|
/* Release */
|
||||||
|
extern void strbuf_free(strbuf_t *s);
|
||||||
|
extern char *strbuf_free_to_string(strbuf_t *s, int *len);
|
||||||
|
|
||||||
|
/* Management */
|
||||||
|
extern void strbuf_resize(strbuf_t *s, int len);
|
||||||
|
static int strbuf_empty_length(strbuf_t *s);
|
||||||
|
static int strbuf_length(strbuf_t *s);
|
||||||
|
static char *strbuf_string(strbuf_t *s, int *len);
|
||||||
|
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
|
||||||
|
static char *strbuf_empty_ptr(strbuf_t *s);
|
||||||
|
static void strbuf_extend_length(strbuf_t *s, int len);
|
||||||
|
static void strbuf_set_length(strbuf_t *s, int len);
|
||||||
|
|
||||||
|
/* Update */
|
||||||
|
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
|
||||||
|
extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);
|
||||||
|
static void strbuf_append_mem(strbuf_t *s, const char *c, int len);
|
||||||
|
extern void strbuf_append_string(strbuf_t *s, const char *str);
|
||||||
|
static void strbuf_append_char(strbuf_t *s, const char c);
|
||||||
|
static void strbuf_ensure_null(strbuf_t *s);
|
||||||
|
|
||||||
|
/* Reset string for before use */
|
||||||
|
static inline void strbuf_reset(strbuf_t *s)
|
||||||
|
{
|
||||||
|
s->length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int strbuf_allocated(strbuf_t *s)
|
||||||
|
{
|
||||||
|
return s->buf != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return bytes remaining in the string buffer
|
||||||
|
* Ensure there is space for a NULL terminator. */
|
||||||
|
static inline int strbuf_empty_length(strbuf_t *s)
|
||||||
|
{
|
||||||
|
return s->size - s->length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_ensure_empty_length(strbuf_t *s, int len)
|
||||||
|
{
|
||||||
|
if (len > strbuf_empty_length(s))
|
||||||
|
strbuf_resize(s, s->length + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *strbuf_empty_ptr(strbuf_t *s)
|
||||||
|
{
|
||||||
|
return s->buf + s->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_set_length(strbuf_t *s, int len)
|
||||||
|
{
|
||||||
|
s->length = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_extend_length(strbuf_t *s, int len)
|
||||||
|
{
|
||||||
|
s->length += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int strbuf_length(strbuf_t *s)
|
||||||
|
{
|
||||||
|
return s->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_append_char(strbuf_t *s, const char c)
|
||||||
|
{
|
||||||
|
strbuf_ensure_empty_length(s, 1);
|
||||||
|
s->buf[s->length++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
|
||||||
|
{
|
||||||
|
s->buf[s->length++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len)
|
||||||
|
{
|
||||||
|
strbuf_ensure_empty_length(s, len);
|
||||||
|
memcpy(s->buf + s->length, c, len);
|
||||||
|
s->length += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len)
|
||||||
|
{
|
||||||
|
memcpy(s->buf + s->length, c, len);
|
||||||
|
s->length += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void strbuf_ensure_null(strbuf_t *s)
|
||||||
|
{
|
||||||
|
s->buf[s->length] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *strbuf_string(strbuf_t *s, int *len)
|
||||||
|
{
|
||||||
|
if (len)
|
||||||
|
*len = s->length;
|
||||||
|
|
||||||
|
return s->buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vi:ai et sw=4 ts=4:
|
||||||
|
*/
|
||||||
23
external/socket/.editorconfig
vendored
Normal file
23
external/socket/.editorconfig
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
[{*.lua,*.rockspec,.luacheckrc}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.html]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{c,h}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
15
external/socket/.gitignore
vendored
Normal file
15
external/socket/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.obj
|
||||||
|
*.lib
|
||||||
|
*.dll*
|
||||||
|
*.user
|
||||||
|
*.sdf
|
||||||
|
Debug
|
||||||
|
Release
|
||||||
|
*.manifest
|
||||||
|
*.swp
|
||||||
|
*.suo
|
||||||
|
x64
|
||||||
|
|
||||||
29
external/socket/.luacheckrc
vendored
Normal file
29
external/socket/.luacheckrc
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
unused_args = false
|
||||||
|
redefined = false
|
||||||
|
max_line_length = false
|
||||||
|
|
||||||
|
not_globals = {
|
||||||
|
"string.len",
|
||||||
|
"table.getn",
|
||||||
|
}
|
||||||
|
|
||||||
|
include_files = {
|
||||||
|
"**/*.lua",
|
||||||
|
"**/*.rockspec",
|
||||||
|
".busted",
|
||||||
|
".luacheckrc",
|
||||||
|
}
|
||||||
|
|
||||||
|
exclude_files = {
|
||||||
|
"test/*.lua",
|
||||||
|
"test/**/*.lua",
|
||||||
|
"samples/*.lua",
|
||||||
|
"samples/**/*.lua",
|
||||||
|
"gem/*.lua",
|
||||||
|
"gem/**/*.lua",
|
||||||
|
-- GH Actions Lua Environment
|
||||||
|
".lua",
|
||||||
|
".luarocks",
|
||||||
|
".install",
|
||||||
|
}
|
||||||
|
|
||||||
19
external/socket/LICENSE
vendored
Normal file
19
external/socket/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (C) 2004-2022 Diego Nehab
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
12
external/socket/README.md
vendored
Normal file
12
external/socket/README.md
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# LuaSocket
|
||||||
|
|
||||||
|
|
||||||
|
[](https://github.com/lunarmodules/luasocket/actions?workflow=Build)
|
||||||
|
[](https://github.com/lunarmodules/luasocket/actions?workflow=Luacheck)
|
||||||
|
[](https://github.com/lunarmodules/luasocket/releases)
|
||||||
|
[](https://luarocks.org/modules/lunarmodules/luasocket)
|
||||||
|
|
||||||
|
LuaSocket is a Lua extension library composed of two parts:
|
||||||
|
|
||||||
|
1. a set of C modules that provide support for the TCP and UDP transport layers, and
|
||||||
|
2. a set of Lua modules that provide functions commonly needed by applications that deal with the Internet.
|
||||||
6
external/socket/makefile
vendored
Normal file
6
external/socket/makefile
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# luasocket makefile
|
||||||
|
#
|
||||||
|
# see src/makefile for description of how to customize the build
|
||||||
|
|
||||||
|
all clean:
|
||||||
|
${MAKE} -Csrc $@
|
||||||
154
external/socket/src/auxiliar.c
vendored
Normal file
154
external/socket/src/auxiliar.c
vendored
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Auxiliar routines for class hierarchy manipulation
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "auxiliar.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes the module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int auxiliar_open(lua_State *L) {
|
||||||
|
(void) L;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Creates a new class with given methods
|
||||||
|
* Methods whose names start with __ are passed directly to the metatable.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func) {
|
||||||
|
luaL_newmetatable(L, classname); /* mt */
|
||||||
|
/* create __index table to place methods */
|
||||||
|
lua_pushstring(L, "__index"); /* mt,"__index" */
|
||||||
|
lua_newtable(L); /* mt,"__index",it */
|
||||||
|
/* put class name into class metatable */
|
||||||
|
lua_pushstring(L, "class"); /* mt,"__index",it,"class" */
|
||||||
|
lua_pushstring(L, classname); /* mt,"__index",it,"class",classname */
|
||||||
|
lua_rawset(L, -3); /* mt,"__index",it */
|
||||||
|
/* pass all methods that start with _ to the metatable, and all others
|
||||||
|
* to the index table */
|
||||||
|
for (; func->name; func++) { /* mt,"__index",it */
|
||||||
|
lua_pushstring(L, func->name);
|
||||||
|
lua_pushcfunction(L, func->func);
|
||||||
|
lua_rawset(L, func->name[0] == '_' ? -5: -3);
|
||||||
|
}
|
||||||
|
lua_rawset(L, -3); /* mt */
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Prints the value of a class in a nice way
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int auxiliar_tostring(lua_State *L) {
|
||||||
|
char buf[32];
|
||||||
|
if (!lua_getmetatable(L, 1)) goto error;
|
||||||
|
lua_pushstring(L, "__index");
|
||||||
|
lua_gettable(L, -2);
|
||||||
|
if (!lua_istable(L, -1)) goto error;
|
||||||
|
lua_pushstring(L, "class");
|
||||||
|
lua_gettable(L, -2);
|
||||||
|
if (!lua_isstring(L, -1)) goto error;
|
||||||
|
sprintf(buf, "%p", lua_touserdata(L, 1));
|
||||||
|
lua_pushfstring(L, "%s: %s", lua_tostring(L, -1), buf);
|
||||||
|
return 1;
|
||||||
|
error:
|
||||||
|
lua_pushstring(L, "invalid object passed to 'auxiliar.c:__tostring'");
|
||||||
|
lua_error(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Insert class into group
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void auxiliar_add2group(lua_State *L, const char *classname, const char *groupname) {
|
||||||
|
luaL_getmetatable(L, classname);
|
||||||
|
lua_pushstring(L, groupname);
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Make sure argument is a boolean
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int auxiliar_checkboolean(lua_State *L, int objidx) {
|
||||||
|
if (!lua_isboolean(L, objidx))
|
||||||
|
auxiliar_typeerror(L, objidx, lua_typename(L, LUA_TBOOLEAN));
|
||||||
|
return lua_toboolean(L, objidx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Return userdata pointer if object belongs to a given class, abort with
|
||||||
|
* error otherwise
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx) {
|
||||||
|
void *data = auxiliar_getclassudata(L, classname, objidx);
|
||||||
|
if (!data) {
|
||||||
|
char msg[45];
|
||||||
|
sprintf(msg, "%.35s expected", classname);
|
||||||
|
luaL_argerror(L, objidx, msg);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Return userdata pointer if object belongs to a given group, abort with
|
||||||
|
* error otherwise
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx) {
|
||||||
|
void *data = auxiliar_getgroupudata(L, groupname, objidx);
|
||||||
|
if (!data) {
|
||||||
|
char msg[45];
|
||||||
|
sprintf(msg, "%.35s expected", groupname);
|
||||||
|
luaL_argerror(L, objidx, msg);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Set object class
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void auxiliar_setclass(lua_State *L, const char *classname, int objidx) {
|
||||||
|
luaL_getmetatable(L, classname);
|
||||||
|
if (objidx < 0) objidx--;
|
||||||
|
lua_setmetatable(L, objidx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Get a userdata pointer if object belongs to a given group. Return NULL
|
||||||
|
* otherwise
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx) {
|
||||||
|
if (!lua_getmetatable(L, objidx))
|
||||||
|
return NULL;
|
||||||
|
lua_pushstring(L, groupname);
|
||||||
|
lua_rawget(L, -2);
|
||||||
|
if (lua_isnil(L, -1)) {
|
||||||
|
lua_pop(L, 2);
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
lua_pop(L, 2);
|
||||||
|
return lua_touserdata(L, objidx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Get a userdata pointer if object belongs to a given class. Return NULL
|
||||||
|
* otherwise
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void *auxiliar_getclassudata(lua_State *L, const char *classname, int objidx) {
|
||||||
|
return luaL_testudata(L, objidx, classname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Throws error when argument does not have correct type.
|
||||||
|
* Used to be part of lauxlib in Lua 5.1, was dropped from 5.2.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int auxiliar_typeerror (lua_State *L, int narg, const char *tname) {
|
||||||
|
const char *msg = lua_pushfstring(L, "%s expected, got %s", tname,
|
||||||
|
luaL_typename(L, narg));
|
||||||
|
return luaL_argerror(L, narg, msg);
|
||||||
|
}
|
||||||
54
external/socket/src/auxiliar.h
vendored
Normal file
54
external/socket/src/auxiliar.h
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef AUXILIAR_H
|
||||||
|
#define AUXILIAR_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Auxiliar routines for class hierarchy manipulation
|
||||||
|
* LuaSocket toolkit (but completely independent of other LuaSocket modules)
|
||||||
|
*
|
||||||
|
* A LuaSocket class is a name associated with Lua metatables. A LuaSocket
|
||||||
|
* group is a name associated with a class. A class can belong to any number
|
||||||
|
* of groups. This module provides the functionality to:
|
||||||
|
*
|
||||||
|
* - create new classes
|
||||||
|
* - add classes to groups
|
||||||
|
* - set the class of objects
|
||||||
|
* - check if an object belongs to a given class or group
|
||||||
|
* - get the userdata associated to objects
|
||||||
|
* - print objects in a pretty way
|
||||||
|
*
|
||||||
|
* LuaSocket class names follow the convention <module>{<class>}. Modules
|
||||||
|
* can define any number of classes and groups. The module tcp.c, for
|
||||||
|
* example, defines the classes tcp{master}, tcp{client} and tcp{server} and
|
||||||
|
* the groups tcp{client,server} and tcp{any}. Module functions can then
|
||||||
|
* perform type-checking on their arguments by either class or group.
|
||||||
|
*
|
||||||
|
* LuaSocket metatables define the __index metamethod as being a table. This
|
||||||
|
* table has one field for each method supported by the class, and a field
|
||||||
|
* "class" with the class name.
|
||||||
|
*
|
||||||
|
* The mapping from class name to the corresponding metatable and the
|
||||||
|
* reverse mapping are done using lauxlib.
|
||||||
|
\*=========================================================================*/
|
||||||
|
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int auxiliar_open(lua_State *L);
|
||||||
|
void auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func);
|
||||||
|
int auxiliar_tostring(lua_State *L);
|
||||||
|
void auxiliar_add2group(lua_State *L, const char *classname, const char *group);
|
||||||
|
int auxiliar_checkboolean(lua_State *L, int objidx);
|
||||||
|
void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx);
|
||||||
|
void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx);
|
||||||
|
void auxiliar_setclass(lua_State *L, const char *classname, int objidx);
|
||||||
|
void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx);
|
||||||
|
void *auxiliar_getclassudata(lua_State *L, const char *groupname, int objidx);
|
||||||
|
int auxiliar_typeerror(lua_State *L, int narg, const char *tname);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* AUXILIAR_H */
|
||||||
273
external/socket/src/buffer.c
vendored
Normal file
273
external/socket/src/buffer.c
vendored
Normal file
|
|
@ -0,0 +1,273 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Input/Output interface for Lua programs
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal function prototypes
|
||||||
|
\*=========================================================================*/
|
||||||
|
static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b);
|
||||||
|
static int recvline(p_buffer buf, luaL_Buffer *b);
|
||||||
|
static int recvall(p_buffer buf, luaL_Buffer *b);
|
||||||
|
static int buffer_get(p_buffer buf, const char **data, size_t *count);
|
||||||
|
static void buffer_skip(p_buffer buf, size_t count);
|
||||||
|
static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent);
|
||||||
|
|
||||||
|
/* min and max macros */
|
||||||
|
#ifndef MIN
|
||||||
|
#define MIN(x, y) ((x) < (y) ? x : y)
|
||||||
|
#endif
|
||||||
|
#ifndef MAX
|
||||||
|
#define MAX(x, y) ((x) > (y) ? x : y)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Exported functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int buffer_open(lua_State *L) {
|
||||||
|
(void) L;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes C structure
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void buffer_init(p_buffer buf, p_io io, p_timeout tm) {
|
||||||
|
buf->first = buf->last = 0;
|
||||||
|
buf->io = io;
|
||||||
|
buf->tm = tm;
|
||||||
|
buf->received = buf->sent = 0;
|
||||||
|
buf->birthday = timeout_gettime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* object:getstats() interface
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int buffer_meth_getstats(lua_State *L, p_buffer buf) {
|
||||||
|
lua_pushnumber(L, (lua_Number) buf->received);
|
||||||
|
lua_pushnumber(L, (lua_Number) buf->sent);
|
||||||
|
lua_pushnumber(L, timeout_gettime() - buf->birthday);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* object:setstats() interface
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int buffer_meth_setstats(lua_State *L, p_buffer buf) {
|
||||||
|
buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received);
|
||||||
|
buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent);
|
||||||
|
if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4);
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* object:send() interface
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int buffer_meth_send(lua_State *L, p_buffer buf) {
|
||||||
|
int top = lua_gettop(L);
|
||||||
|
int err = IO_DONE;
|
||||||
|
size_t size = 0, sent = 0;
|
||||||
|
const char *data = luaL_checklstring(L, 2, &size);
|
||||||
|
long start = (long) luaL_optnumber(L, 3, 1);
|
||||||
|
long end = (long) luaL_optnumber(L, 4, -1);
|
||||||
|
timeout_markstart(buf->tm);
|
||||||
|
if (start < 0) start = (long) (size+start+1);
|
||||||
|
if (end < 0) end = (long) (size+end+1);
|
||||||
|
if (start < 1) start = (long) 1;
|
||||||
|
if (end > (long) size) end = (long) size;
|
||||||
|
if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent);
|
||||||
|
/* check if there was an error */
|
||||||
|
if (err != IO_DONE) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, buf->io->error(buf->io->ctx, err));
|
||||||
|
lua_pushnumber(L, (lua_Number) (sent+start-1));
|
||||||
|
} else {
|
||||||
|
lua_pushnumber(L, (lua_Number) (sent+start-1));
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushnil(L);
|
||||||
|
}
|
||||||
|
#ifdef LUASOCKET_DEBUG
|
||||||
|
/* push time elapsed during operation as the last return value */
|
||||||
|
lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm));
|
||||||
|
#endif
|
||||||
|
return lua_gettop(L) - top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* object:receive() interface
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int buffer_meth_receive(lua_State *L, p_buffer buf) {
|
||||||
|
int err = IO_DONE, top;
|
||||||
|
luaL_Buffer b;
|
||||||
|
size_t size;
|
||||||
|
const char *part = luaL_optlstring(L, 3, "", &size);
|
||||||
|
timeout_markstart(buf->tm);
|
||||||
|
/* make sure we don't confuse buffer stuff with arguments */
|
||||||
|
lua_settop(L, 3);
|
||||||
|
top = lua_gettop(L);
|
||||||
|
/* initialize buffer with optional extra prefix
|
||||||
|
* (useful for concatenating previous partial results) */
|
||||||
|
luaL_buffinit(L, &b);
|
||||||
|
luaL_addlstring(&b, part, size);
|
||||||
|
/* receive new patterns */
|
||||||
|
if (!lua_isnumber(L, 2)) {
|
||||||
|
const char *p= luaL_optstring(L, 2, "*l");
|
||||||
|
if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b);
|
||||||
|
else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b);
|
||||||
|
else luaL_argcheck(L, 0, 2, "invalid receive pattern");
|
||||||
|
/* get a fixed number of bytes (minus what was already partially
|
||||||
|
* received) */
|
||||||
|
} else {
|
||||||
|
double n = lua_tonumber(L, 2);
|
||||||
|
size_t wanted = (size_t) n;
|
||||||
|
luaL_argcheck(L, n >= 0, 2, "invalid receive pattern");
|
||||||
|
if (size == 0 || wanted > size)
|
||||||
|
err = recvraw(buf, wanted-size, &b);
|
||||||
|
}
|
||||||
|
/* check if there was an error */
|
||||||
|
if (err != IO_DONE) {
|
||||||
|
/* we can't push anyting in the stack before pushing the
|
||||||
|
* contents of the buffer. this is the reason for the complication */
|
||||||
|
luaL_pushresult(&b);
|
||||||
|
lua_pushstring(L, buf->io->error(buf->io->ctx, err));
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_replace(L, -4);
|
||||||
|
} else {
|
||||||
|
luaL_pushresult(&b);
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushnil(L);
|
||||||
|
}
|
||||||
|
#ifdef LUASOCKET_DEBUG
|
||||||
|
/* push time elapsed during operation as the last return value */
|
||||||
|
lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm));
|
||||||
|
#endif
|
||||||
|
return lua_gettop(L) - top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Determines if there is any data in the read buffer
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int buffer_isempty(p_buffer buf) {
|
||||||
|
return buf->first >= buf->last;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Sends a block of data (unbuffered)
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
#define STEPSIZE 8192
|
||||||
|
static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) {
|
||||||
|
p_io io = buf->io;
|
||||||
|
p_timeout tm = buf->tm;
|
||||||
|
size_t total = 0;
|
||||||
|
int err = IO_DONE;
|
||||||
|
while (total < count && err == IO_DONE) {
|
||||||
|
size_t done = 0;
|
||||||
|
size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE;
|
||||||
|
err = io->send(io->ctx, data+total, step, &done, tm);
|
||||||
|
total += done;
|
||||||
|
}
|
||||||
|
*sent = total;
|
||||||
|
buf->sent += total;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Reads a fixed number of bytes (buffered)
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) {
|
||||||
|
int err = IO_DONE;
|
||||||
|
size_t total = 0;
|
||||||
|
while (err == IO_DONE) {
|
||||||
|
size_t count; const char *data;
|
||||||
|
err = buffer_get(buf, &data, &count);
|
||||||
|
count = MIN(count, wanted - total);
|
||||||
|
luaL_addlstring(b, data, count);
|
||||||
|
buffer_skip(buf, count);
|
||||||
|
total += count;
|
||||||
|
if (total >= wanted) break;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Reads everything until the connection is closed (buffered)
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int recvall(p_buffer buf, luaL_Buffer *b) {
|
||||||
|
int err = IO_DONE;
|
||||||
|
size_t total = 0;
|
||||||
|
while (err == IO_DONE) {
|
||||||
|
const char *data; size_t count;
|
||||||
|
err = buffer_get(buf, &data, &count);
|
||||||
|
total += count;
|
||||||
|
luaL_addlstring(b, data, count);
|
||||||
|
buffer_skip(buf, count);
|
||||||
|
}
|
||||||
|
if (err == IO_CLOSED) {
|
||||||
|
if (total > 0) return IO_DONE;
|
||||||
|
else return IO_CLOSED;
|
||||||
|
} else return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF
|
||||||
|
* are not returned by the function and are discarded from the buffer
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int recvline(p_buffer buf, luaL_Buffer *b) {
|
||||||
|
int err = IO_DONE;
|
||||||
|
while (err == IO_DONE) {
|
||||||
|
size_t count, pos; const char *data;
|
||||||
|
err = buffer_get(buf, &data, &count);
|
||||||
|
pos = 0;
|
||||||
|
while (pos < count && data[pos] != '\n') {
|
||||||
|
/* we ignore all \r's */
|
||||||
|
if (data[pos] != '\r') luaL_addchar(b, data[pos]);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
if (pos < count) { /* found '\n' */
|
||||||
|
buffer_skip(buf, pos+1); /* skip '\n' too */
|
||||||
|
break; /* we are done */
|
||||||
|
} else /* reached the end of the buffer */
|
||||||
|
buffer_skip(buf, pos);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Skips a given number of bytes from read buffer. No data is read from the
|
||||||
|
* transport layer
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static void buffer_skip(p_buffer buf, size_t count) {
|
||||||
|
buf->received += count;
|
||||||
|
buf->first += count;
|
||||||
|
if (buffer_isempty(buf))
|
||||||
|
buf->first = buf->last = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Return any data available in buffer, or get more data from transport layer
|
||||||
|
* if buffer is empty
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int buffer_get(p_buffer buf, const char **data, size_t *count) {
|
||||||
|
int err = IO_DONE;
|
||||||
|
p_io io = buf->io;
|
||||||
|
p_timeout tm = buf->tm;
|
||||||
|
if (buffer_isempty(buf)) {
|
||||||
|
size_t got;
|
||||||
|
err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm);
|
||||||
|
buf->first = 0;
|
||||||
|
buf->last = got;
|
||||||
|
}
|
||||||
|
*count = buf->last - buf->first;
|
||||||
|
*data = buf->data + buf->first;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
52
external/socket/src/buffer.h
vendored
Normal file
52
external/socket/src/buffer.h
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
#ifndef BUF_H
|
||||||
|
#define BUF_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Input/Output interface for Lua programs
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* Line patterns require buffering. Reading one character at a time involves
|
||||||
|
* too many system calls and is very slow. This module implements the
|
||||||
|
* LuaSocket interface for input/output on connected objects, as seen by
|
||||||
|
* Lua programs.
|
||||||
|
*
|
||||||
|
* Input is buffered. Output is *not* buffered because there was no simple
|
||||||
|
* way of making sure the buffered output data would ever be sent.
|
||||||
|
*
|
||||||
|
* The module is built on top of the I/O abstraction defined in io.h and the
|
||||||
|
* timeout management is done with the timeout.h interface.
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "timeout.h"
|
||||||
|
|
||||||
|
/* buffer size in bytes */
|
||||||
|
#define BUF_SIZE 8192
|
||||||
|
|
||||||
|
/* buffer control structure */
|
||||||
|
typedef struct t_buffer_ {
|
||||||
|
double birthday; /* throttle support info: creation time, */
|
||||||
|
size_t sent, received; /* bytes sent, and bytes received */
|
||||||
|
p_io io; /* IO driver used for this buffer */
|
||||||
|
p_timeout tm; /* timeout management for this buffer */
|
||||||
|
size_t first, last; /* index of first and last bytes of stored data */
|
||||||
|
char data[BUF_SIZE]; /* storage space for buffer data */
|
||||||
|
} t_buffer;
|
||||||
|
typedef t_buffer *p_buffer;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int buffer_open(lua_State *L);
|
||||||
|
void buffer_init(p_buffer buf, p_io io, p_timeout tm);
|
||||||
|
int buffer_meth_getstats(lua_State *L, p_buffer buf);
|
||||||
|
int buffer_meth_setstats(lua_State *L, p_buffer buf);
|
||||||
|
int buffer_meth_send(lua_State *L, p_buffer buf);
|
||||||
|
int buffer_meth_receive(lua_State *L, p_buffer buf);
|
||||||
|
int buffer_isempty(p_buffer buf);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BUF_H */
|
||||||
39
external/socket/src/compat.c
vendored
Normal file
39
external/socket/src/compat.c
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM==501
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Adapted from Lua 5.2
|
||||||
|
*/
|
||||||
|
void luasocket_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
|
||||||
|
luaL_checkstack(L, nup+1, "too many upvalues");
|
||||||
|
for (; l->name != NULL; l++) { /* fill the table with given functions */
|
||||||
|
int i;
|
||||||
|
lua_pushstring(L, l->name);
|
||||||
|
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
||||||
|
lua_pushvalue(L, -(nup+1));
|
||||||
|
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
||||||
|
lua_settable(L, -(nup + 3));
|
||||||
|
}
|
||||||
|
lua_pop(L, nup); /* remove upvalues */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Duplicated from Lua 5.2
|
||||||
|
*/
|
||||||
|
void *luasocket_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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
22
external/socket/src/compat.h
vendored
Normal file
22
external/socket/src/compat.h
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef COMPAT_H
|
||||||
|
#define COMPAT_H
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM==501
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void luasocket_setfuncs (lua_State *L, const luaL_Reg *l, int nup);
|
||||||
|
void *luasocket_testudata ( lua_State *L, int arg, const char *tname);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define luaL_setfuncs luasocket_setfuncs
|
||||||
|
#define luaL_testudata luasocket_testudata
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
129
external/socket/src/except.c
vendored
Normal file
129
external/socket/src/except.c
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Simple exception support
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "except.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM < 502
|
||||||
|
#define lua_pcallk(L, na, nr, err, ctx, cont) \
|
||||||
|
(((void)ctx),((void)cont),lua_pcall(L, na, nr, err))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM < 503
|
||||||
|
typedef int lua_KContext;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal function prototypes.
|
||||||
|
\*=========================================================================*/
|
||||||
|
static int global_protect(lua_State *L);
|
||||||
|
static int global_newtry(lua_State *L);
|
||||||
|
static int protected_(lua_State *L);
|
||||||
|
static int finalize(lua_State *L);
|
||||||
|
static int do_nothing(lua_State *L);
|
||||||
|
|
||||||
|
/* except functions */
|
||||||
|
static luaL_Reg func[] = {
|
||||||
|
{"newtry", global_newtry},
|
||||||
|
{"protect", global_protect},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Try factory
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static void wrap(lua_State *L) {
|
||||||
|
lua_createtable(L, 1, 0);
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
lua_rawseti(L, -2, 1);
|
||||||
|
lua_pushvalue(L, lua_upvalueindex(1));
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int finalize(lua_State *L) {
|
||||||
|
if (!lua_toboolean(L, 1)) {
|
||||||
|
lua_pushvalue(L, lua_upvalueindex(2));
|
||||||
|
lua_call(L, 0, 0);
|
||||||
|
lua_settop(L, 2);
|
||||||
|
wrap(L);
|
||||||
|
lua_error(L);
|
||||||
|
return 0;
|
||||||
|
} else return lua_gettop(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_nothing(lua_State *L) {
|
||||||
|
(void) L;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int global_newtry(lua_State *L) {
|
||||||
|
lua_settop(L, 1);
|
||||||
|
if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing);
|
||||||
|
lua_pushvalue(L, lua_upvalueindex(1));
|
||||||
|
lua_insert(L, -2);
|
||||||
|
lua_pushcclosure(L, finalize, 2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Protect factory
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int unwrap(lua_State *L) {
|
||||||
|
if (lua_istable(L, -1) && lua_getmetatable(L, -1)) {
|
||||||
|
int r = lua_rawequal(L, -1, lua_upvalueindex(1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
if (r) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_rawgeti(L, -2, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int protected_finish(lua_State *L, int status, lua_KContext ctx) {
|
||||||
|
(void)ctx;
|
||||||
|
if (status != 0 && status != LUA_YIELD) {
|
||||||
|
if (unwrap(L)) return 2;
|
||||||
|
else return lua_error(L);
|
||||||
|
} else return lua_gettop(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM == 502
|
||||||
|
static int protected_cont(lua_State *L) {
|
||||||
|
int ctx = 0;
|
||||||
|
int status = lua_getctx(L, &ctx);
|
||||||
|
return protected_finish(L, status, ctx);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define protected_cont protected_finish
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int protected_(lua_State *L) {
|
||||||
|
int status;
|
||||||
|
lua_pushvalue(L, lua_upvalueindex(2));
|
||||||
|
lua_insert(L, 1);
|
||||||
|
status = lua_pcallk(L, lua_gettop(L) - 1, LUA_MULTRET, 0, 0, protected_cont);
|
||||||
|
return protected_finish(L, status, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int global_protect(lua_State *L) {
|
||||||
|
lua_settop(L, 1);
|
||||||
|
lua_pushvalue(L, lua_upvalueindex(1));
|
||||||
|
lua_insert(L, 1);
|
||||||
|
lua_pushcclosure(L, protected_, 2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Init module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int except_open(lua_State *L) {
|
||||||
|
lua_newtable(L); /* metatable for wrapped exceptions */
|
||||||
|
lua_pushboolean(L, 0);
|
||||||
|
lua_setfield(L, -2, "__metatable");
|
||||||
|
luaL_setfuncs(L, func, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
46
external/socket/src/except.h
vendored
Normal file
46
external/socket/src/except.h
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef EXCEPT_H
|
||||||
|
#define EXCEPT_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Exception control
|
||||||
|
* LuaSocket toolkit (but completely independent from other modules)
|
||||||
|
*
|
||||||
|
* This provides support for simple exceptions in Lua. During the
|
||||||
|
* development of the HTTP/FTP/SMTP support, it became aparent that
|
||||||
|
* error checking was taking a substantial amount of the coding. These
|
||||||
|
* function greatly simplify the task of checking errors.
|
||||||
|
*
|
||||||
|
* The main idea is that functions should return nil as their first return
|
||||||
|
* values when they find an error, and return an error message (or value)
|
||||||
|
* following nil. In case of success, as long as the first value is not nil,
|
||||||
|
* the other values don't matter.
|
||||||
|
*
|
||||||
|
* The idea is to nest function calls with the "try" function. This function
|
||||||
|
* checks the first value, and, if it's falsy, wraps the second value in a
|
||||||
|
* table with metatable and calls "error" on it. Otherwise, it returns all
|
||||||
|
* values it received. Basically, it works like the Lua "assert" function,
|
||||||
|
* but it creates errors targeted specifically at "protect".
|
||||||
|
*
|
||||||
|
* The "newtry" function is a factory for "try" functions that call a
|
||||||
|
* finalizer in protected mode before calling "error".
|
||||||
|
*
|
||||||
|
* The "protect" function returns a new function that behaves exactly like
|
||||||
|
* the function it receives, but the new function catches exceptions thrown
|
||||||
|
* by "try" functions and returns nil followed by the error message instead.
|
||||||
|
*
|
||||||
|
* With these three functions, it's easy to write functions that throw
|
||||||
|
* exceptions on error, but that don't interrupt the user script.
|
||||||
|
\*=========================================================================*/
|
||||||
|
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int except_open(lua_State *L);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
537
external/socket/src/inet.c
vendored
Normal file
537
external/socket/src/inet.c
vendored
Normal file
|
|
@ -0,0 +1,537 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internet domain functions
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "inet.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal function prototypes.
|
||||||
|
\*=========================================================================*/
|
||||||
|
static int inet_global_toip(lua_State *L);
|
||||||
|
static int inet_global_getaddrinfo(lua_State *L);
|
||||||
|
static int inet_global_tohostname(lua_State *L);
|
||||||
|
static int inet_global_getnameinfo(lua_State *L);
|
||||||
|
static void inet_pushresolved(lua_State *L, struct hostent *hp);
|
||||||
|
static int inet_global_gethostname(lua_State *L);
|
||||||
|
|
||||||
|
/* DNS functions */
|
||||||
|
static luaL_Reg func[] = {
|
||||||
|
{ "toip", inet_global_toip},
|
||||||
|
{ "getaddrinfo", inet_global_getaddrinfo},
|
||||||
|
{ "tohostname", inet_global_tohostname},
|
||||||
|
{ "getnameinfo", inet_global_getnameinfo},
|
||||||
|
{ "gethostname", inet_global_gethostname},
|
||||||
|
{ NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int inet_open(lua_State *L)
|
||||||
|
{
|
||||||
|
lua_pushstring(L, "dns");
|
||||||
|
lua_newtable(L);
|
||||||
|
luaL_setfuncs(L, func, 0);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Global Lua functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Returns all information provided by the resolver given a host name
|
||||||
|
* or ip address
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int inet_gethost(const char *address, struct hostent **hp) {
|
||||||
|
struct in_addr addr;
|
||||||
|
if (inet_aton(address, &addr))
|
||||||
|
return socket_gethostbyaddr((char *) &addr, sizeof(addr), hp);
|
||||||
|
else
|
||||||
|
return socket_gethostbyname(address, hp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Returns all information provided by the resolver given a host name
|
||||||
|
* or ip address
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int inet_global_tohostname(lua_State *L) {
|
||||||
|
const char *address = luaL_checkstring(L, 1);
|
||||||
|
struct hostent *hp = NULL;
|
||||||
|
int err = inet_gethost(address, &hp);
|
||||||
|
if (err != IO_DONE) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_hoststrerror(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushstring(L, hp->h_name);
|
||||||
|
inet_pushresolved(L, hp);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int inet_global_getnameinfo(lua_State *L) {
|
||||||
|
char hbuf[NI_MAXHOST];
|
||||||
|
char sbuf[NI_MAXSERV];
|
||||||
|
int i, ret;
|
||||||
|
struct addrinfo hints;
|
||||||
|
struct addrinfo *resolved, *iter;
|
||||||
|
const char *host = luaL_optstring(L, 1, NULL);
|
||||||
|
const char *serv = luaL_optstring(L, 2, NULL);
|
||||||
|
|
||||||
|
if (!(host || serv))
|
||||||
|
luaL_error(L, "host and serv cannot be both nil");
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
|
||||||
|
ret = getaddrinfo(host, serv, &hints, &resolved);
|
||||||
|
if (ret != 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_gaistrerror(ret));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
for (i = 1, iter = resolved; iter; i++, iter = iter->ai_next) {
|
||||||
|
getnameinfo(iter->ai_addr, (socklen_t) iter->ai_addrlen,
|
||||||
|
hbuf, host? (socklen_t) sizeof(hbuf): 0,
|
||||||
|
sbuf, serv? (socklen_t) sizeof(sbuf): 0, 0);
|
||||||
|
if (host) {
|
||||||
|
lua_pushnumber(L, i);
|
||||||
|
lua_pushstring(L, hbuf);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeaddrinfo(resolved);
|
||||||
|
|
||||||
|
if (serv) {
|
||||||
|
lua_pushstring(L, sbuf);
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Returns all information provided by the resolver given a host name
|
||||||
|
* or ip address
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int inet_global_toip(lua_State *L)
|
||||||
|
{
|
||||||
|
const char *address = luaL_checkstring(L, 1);
|
||||||
|
struct hostent *hp = NULL;
|
||||||
|
int err = inet_gethost(address, &hp);
|
||||||
|
if (err != IO_DONE) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_hoststrerror(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushstring(L, inet_ntoa(*((struct in_addr *) hp->h_addr)));
|
||||||
|
inet_pushresolved(L, hp);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int inet_optfamily(lua_State* L, int narg, const char* def)
|
||||||
|
{
|
||||||
|
static const char* optname[] = { "unspec", "inet", "inet6", NULL };
|
||||||
|
static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 };
|
||||||
|
|
||||||
|
return optvalue[luaL_checkoption(L, narg, def, optname)];
|
||||||
|
}
|
||||||
|
|
||||||
|
int inet_optsocktype(lua_State* L, int narg, const char* def)
|
||||||
|
{
|
||||||
|
static const char* optname[] = { "stream", "dgram", NULL };
|
||||||
|
static int optvalue[] = { SOCK_STREAM, SOCK_DGRAM, 0 };
|
||||||
|
|
||||||
|
return optvalue[luaL_checkoption(L, narg, def, optname)];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int inet_global_getaddrinfo(lua_State *L)
|
||||||
|
{
|
||||||
|
const char *hostname = luaL_checkstring(L, 1);
|
||||||
|
struct addrinfo *iterator = NULL, *resolved = NULL;
|
||||||
|
struct addrinfo hints;
|
||||||
|
int i = 1, ret = 0;
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
ret = getaddrinfo(hostname, NULL, &hints, &resolved);
|
||||||
|
if (ret != 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_gaistrerror(ret));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_newtable(L);
|
||||||
|
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
|
||||||
|
char hbuf[NI_MAXHOST];
|
||||||
|
ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen,
|
||||||
|
hbuf, (socklen_t) sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
|
||||||
|
if (ret){
|
||||||
|
freeaddrinfo(resolved);
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_gaistrerror(ret));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, i);
|
||||||
|
lua_newtable(L);
|
||||||
|
switch (iterator->ai_family) {
|
||||||
|
case AF_INET:
|
||||||
|
lua_pushliteral(L, "family");
|
||||||
|
lua_pushliteral(L, "inet");
|
||||||
|
lua_settable(L, -3);
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
lua_pushliteral(L, "family");
|
||||||
|
lua_pushliteral(L, "inet6");
|
||||||
|
lua_settable(L, -3);
|
||||||
|
break;
|
||||||
|
case AF_UNSPEC:
|
||||||
|
lua_pushliteral(L, "family");
|
||||||
|
lua_pushliteral(L, "unspec");
|
||||||
|
lua_settable(L, -3);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
lua_pushliteral(L, "family");
|
||||||
|
lua_pushliteral(L, "unknown");
|
||||||
|
lua_settable(L, -3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lua_pushliteral(L, "addr");
|
||||||
|
lua_pushstring(L, hbuf);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
freeaddrinfo(resolved);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Gets the host name
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int inet_global_gethostname(lua_State *L)
|
||||||
|
{
|
||||||
|
char name[257];
|
||||||
|
name[256] = '\0';
|
||||||
|
if (gethostname(name, 256) < 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_strerror(errno));
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
lua_pushstring(L, name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Lua methods
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Retrieves socket peer name
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int inet_meth_getpeername(lua_State *L, p_socket ps, int family)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct sockaddr_storage peer;
|
||||||
|
socklen_t peer_len = sizeof(peer);
|
||||||
|
char name[INET6_ADDRSTRLEN];
|
||||||
|
char port[6]; /* 65535 = 5 bytes + 0 to terminate it */
|
||||||
|
if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_strerror(errno));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
err = getnameinfo((struct sockaddr *) &peer, peer_len,
|
||||||
|
name, INET6_ADDRSTRLEN,
|
||||||
|
port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, LUA_GAI_STRERROR(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushstring(L, name);
|
||||||
|
lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10));
|
||||||
|
switch (family) {
|
||||||
|
case AF_INET: lua_pushliteral(L, "inet"); break;
|
||||||
|
case AF_INET6: lua_pushliteral(L, "inet6"); break;
|
||||||
|
case AF_UNSPEC: lua_pushliteral(L, "unspec"); break;
|
||||||
|
default: lua_pushliteral(L, "unknown"); break;
|
||||||
|
}
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Retrieves socket local name
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int inet_meth_getsockname(lua_State *L, p_socket ps, int family)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct sockaddr_storage peer;
|
||||||
|
socklen_t peer_len = sizeof(peer);
|
||||||
|
char name[INET6_ADDRSTRLEN];
|
||||||
|
char port[6]; /* 65535 = 5 bytes + 0 to terminate it */
|
||||||
|
if (getsockname(*ps, (SA *) &peer, &peer_len) < 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_strerror(errno));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
err=getnameinfo((struct sockaddr *)&peer, peer_len,
|
||||||
|
name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, LUA_GAI_STRERROR(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushstring(L, name);
|
||||||
|
lua_pushstring(L, port);
|
||||||
|
switch (family) {
|
||||||
|
case AF_INET: lua_pushliteral(L, "inet"); break;
|
||||||
|
case AF_INET6: lua_pushliteral(L, "inet6"); break;
|
||||||
|
case AF_UNSPEC: lua_pushliteral(L, "unspec"); break;
|
||||||
|
default: lua_pushliteral(L, "unknown"); break;
|
||||||
|
}
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Passes all resolver information to Lua as a table
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static void inet_pushresolved(lua_State *L, struct hostent *hp)
|
||||||
|
{
|
||||||
|
char **alias;
|
||||||
|
struct in_addr **addr;
|
||||||
|
int i, resolved;
|
||||||
|
lua_newtable(L); resolved = lua_gettop(L);
|
||||||
|
lua_pushstring(L, "name");
|
||||||
|
lua_pushstring(L, hp->h_name);
|
||||||
|
lua_settable(L, resolved);
|
||||||
|
lua_pushstring(L, "ip");
|
||||||
|
lua_pushstring(L, "alias");
|
||||||
|
i = 1;
|
||||||
|
alias = hp->h_aliases;
|
||||||
|
lua_newtable(L);
|
||||||
|
if (alias) {
|
||||||
|
while (*alias) {
|
||||||
|
lua_pushnumber(L, i);
|
||||||
|
lua_pushstring(L, *alias);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
i++; alias++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_settable(L, resolved);
|
||||||
|
i = 1;
|
||||||
|
lua_newtable(L);
|
||||||
|
addr = (struct in_addr **) hp->h_addr_list;
|
||||||
|
if (addr) {
|
||||||
|
while (*addr) {
|
||||||
|
lua_pushnumber(L, i);
|
||||||
|
lua_pushstring(L, inet_ntoa(**addr));
|
||||||
|
lua_settable(L, -3);
|
||||||
|
i++; addr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_settable(L, resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Tries to create a new inet socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
const char *inet_trycreate(p_socket ps, int family, int type, int protocol) {
|
||||||
|
const char *err = socket_strerror(socket_create(ps, family, type, protocol));
|
||||||
|
if (err == NULL && family == AF_INET6) {
|
||||||
|
int yes = 1;
|
||||||
|
setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes));
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* "Disconnects" a DGRAM socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm)
|
||||||
|
{
|
||||||
|
switch (family) {
|
||||||
|
case AF_INET: {
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
memset((char *) &sin, 0, sizeof(sin));
|
||||||
|
sin.sin_family = AF_UNSPEC;
|
||||||
|
sin.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
return socket_strerror(socket_connect(ps, (SA *) &sin,
|
||||||
|
sizeof(sin), tm));
|
||||||
|
}
|
||||||
|
case AF_INET6: {
|
||||||
|
struct sockaddr_in6 sin6;
|
||||||
|
struct in6_addr addrany = IN6ADDR_ANY_INIT;
|
||||||
|
memset((char *) &sin6, 0, sizeof(sin6));
|
||||||
|
sin6.sin6_family = AF_UNSPEC;
|
||||||
|
sin6.sin6_addr = addrany;
|
||||||
|
return socket_strerror(socket_connect(ps, (SA *) &sin6,
|
||||||
|
sizeof(sin6), tm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Tries to connect to remote address (address, port)
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
const char *inet_tryconnect(p_socket ps, int *family, const char *address,
|
||||||
|
const char *serv, p_timeout tm, struct addrinfo *connecthints)
|
||||||
|
{
|
||||||
|
struct addrinfo *iterator = NULL, *resolved = NULL;
|
||||||
|
const char *err = NULL;
|
||||||
|
int current_family = *family;
|
||||||
|
/* try resolving */
|
||||||
|
err = socket_gaistrerror(getaddrinfo(address, serv,
|
||||||
|
connecthints, &resolved));
|
||||||
|
if (err != NULL) {
|
||||||
|
if (resolved) freeaddrinfo(resolved);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
|
||||||
|
timeout_markstart(tm);
|
||||||
|
/* create new socket if necessary. if there was no
|
||||||
|
* bind, we need to create one for every new family
|
||||||
|
* that shows up while iterating. if there was a
|
||||||
|
* bind, all families will be the same and we will
|
||||||
|
* not enter this branch. */
|
||||||
|
if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) {
|
||||||
|
socket_destroy(ps);
|
||||||
|
err = inet_trycreate(ps, iterator->ai_family,
|
||||||
|
iterator->ai_socktype, iterator->ai_protocol);
|
||||||
|
if (err) continue;
|
||||||
|
current_family = iterator->ai_family;
|
||||||
|
/* set non-blocking before connect */
|
||||||
|
socket_setnonblocking(ps);
|
||||||
|
}
|
||||||
|
/* try connecting to remote address */
|
||||||
|
err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr,
|
||||||
|
(socklen_t) iterator->ai_addrlen, tm));
|
||||||
|
/* if success or timeout is zero, break out of loop */
|
||||||
|
if (err == NULL || timeout_iszero(tm)) {
|
||||||
|
*family = current_family;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeaddrinfo(resolved);
|
||||||
|
/* here, if err is set, we failed */
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Tries to accept a socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
const char *inet_tryaccept(p_socket server, int family, p_socket client,
|
||||||
|
p_timeout tm) {
|
||||||
|
socklen_t len;
|
||||||
|
t_sockaddr_storage addr;
|
||||||
|
switch (family) {
|
||||||
|
case AF_INET6: len = sizeof(struct sockaddr_in6); break;
|
||||||
|
case AF_INET: len = sizeof(struct sockaddr_in); break;
|
||||||
|
default: len = sizeof(addr); break;
|
||||||
|
}
|
||||||
|
return socket_strerror(socket_accept(server, client, (SA *) &addr,
|
||||||
|
&len, tm));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Tries to bind socket to (address, port)
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
const char *inet_trybind(p_socket ps, int *family, const char *address,
|
||||||
|
const char *serv, struct addrinfo *bindhints) {
|
||||||
|
struct addrinfo *iterator = NULL, *resolved = NULL;
|
||||||
|
const char *err = NULL;
|
||||||
|
int current_family = *family;
|
||||||
|
/* translate luasocket special values to C */
|
||||||
|
if (strcmp(address, "*") == 0) address = NULL;
|
||||||
|
if (!serv) serv = "0";
|
||||||
|
/* try resolving */
|
||||||
|
err = socket_gaistrerror(getaddrinfo(address, serv, bindhints, &resolved));
|
||||||
|
if (err) {
|
||||||
|
if (resolved) freeaddrinfo(resolved);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
/* iterate over resolved addresses until one is good */
|
||||||
|
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
|
||||||
|
if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) {
|
||||||
|
socket_destroy(ps);
|
||||||
|
err = inet_trycreate(ps, iterator->ai_family,
|
||||||
|
iterator->ai_socktype, iterator->ai_protocol);
|
||||||
|
if (err) continue;
|
||||||
|
current_family = iterator->ai_family;
|
||||||
|
}
|
||||||
|
/* try binding to local address */
|
||||||
|
err = socket_strerror(socket_bind(ps, (SA *) iterator->ai_addr,
|
||||||
|
(socklen_t) iterator->ai_addrlen));
|
||||||
|
/* keep trying unless bind succeeded */
|
||||||
|
if (err == NULL) {
|
||||||
|
*family = current_family;
|
||||||
|
/* set to non-blocking after bind */
|
||||||
|
socket_setnonblocking(ps);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* cleanup and return error */
|
||||||
|
freeaddrinfo(resolved);
|
||||||
|
/* here, if err is set, we failed */
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Some systems do not provide these so that we provide our own.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
#ifdef LUASOCKET_INET_ATON
|
||||||
|
int inet_aton(const char *cp, struct in_addr *inp)
|
||||||
|
{
|
||||||
|
unsigned int a = 0, b = 0, c = 0, d = 0;
|
||||||
|
int n = 0, r;
|
||||||
|
unsigned long int addr = 0;
|
||||||
|
r = sscanf(cp, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n);
|
||||||
|
if (r == 0 || n == 0) return 0;
|
||||||
|
cp += n;
|
||||||
|
if (*cp) return 0;
|
||||||
|
if (a > 255 || b > 255 || c > 255 || d > 255) return 0;
|
||||||
|
if (inp) {
|
||||||
|
addr += a; addr <<= 8;
|
||||||
|
addr += b; addr <<= 8;
|
||||||
|
addr += c; addr <<= 8;
|
||||||
|
addr += d;
|
||||||
|
inp->s_addr = htonl(addr);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LUASOCKET_INET_PTON
|
||||||
|
int inet_pton(int af, const char *src, void *dst)
|
||||||
|
{
|
||||||
|
struct addrinfo hints, *res;
|
||||||
|
int ret = 1;
|
||||||
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||||||
|
hints.ai_family = af;
|
||||||
|
hints.ai_flags = AI_NUMERICHOST;
|
||||||
|
if (getaddrinfo(src, NULL, &hints, &res) != 0) return -1;
|
||||||
|
if (af == AF_INET) {
|
||||||
|
struct sockaddr_in *in = (struct sockaddr_in *) res->ai_addr;
|
||||||
|
memcpy(dst, &in->sin_addr, sizeof(in->sin_addr));
|
||||||
|
} else if (af == AF_INET6) {
|
||||||
|
struct sockaddr_in6 *in = (struct sockaddr_in6 *) res->ai_addr;
|
||||||
|
memcpy(dst, &in->sin6_addr, sizeof(in->sin6_addr));
|
||||||
|
} else {
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
56
external/socket/src/inet.h
vendored
Normal file
56
external/socket/src/inet.h
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef INET_H
|
||||||
|
#define INET_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internet domain functions
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* This module implements the creation and connection of internet domain
|
||||||
|
* sockets, on top of the socket.h interface, and the interface of with the
|
||||||
|
* resolver.
|
||||||
|
*
|
||||||
|
* The function inet_aton is provided for the platforms where it is not
|
||||||
|
* available. The module also implements the interface of the internet
|
||||||
|
* getpeername and getsockname functions as seen by Lua programs.
|
||||||
|
*
|
||||||
|
* The Lua functions toip and tohostname are also implemented here.
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "timeout.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define LUASOCKET_INET_ATON
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int inet_open(lua_State *L);
|
||||||
|
|
||||||
|
int inet_optfamily(lua_State* L, int narg, const char* def);
|
||||||
|
int inet_optsocktype(lua_State* L, int narg, const char* def);
|
||||||
|
|
||||||
|
int inet_meth_getpeername(lua_State *L, p_socket ps, int family);
|
||||||
|
int inet_meth_getsockname(lua_State *L, p_socket ps, int family);
|
||||||
|
|
||||||
|
const char *inet_trycreate(p_socket ps, int family, int type, int protocol);
|
||||||
|
const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm);
|
||||||
|
const char *inet_tryconnect(p_socket ps, int *family, const char *address, const char *serv, p_timeout tm, struct addrinfo *connecthints);
|
||||||
|
const char *inet_tryaccept(p_socket server, int family, p_socket client, p_timeout tm);
|
||||||
|
const char *inet_trybind(p_socket ps, int *family, const char *address, const char *serv, struct addrinfo *bindhints);
|
||||||
|
|
||||||
|
#ifdef LUASOCKET_INET_ATON
|
||||||
|
int inet_aton(const char *cp, struct in_addr *inp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LUASOCKET_INET_PTON
|
||||||
|
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
|
||||||
|
int inet_pton(int af, const char *src, void *dst);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* INET_H */
|
||||||
28
external/socket/src/io.c
vendored
Normal file
28
external/socket/src/io.c
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Input/Output abstraction
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes C structure
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx) {
|
||||||
|
io->send = send;
|
||||||
|
io->recv = recv;
|
||||||
|
io->error = error;
|
||||||
|
io->ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* I/O error strings
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
const char *io_strerror(int err) {
|
||||||
|
switch (err) {
|
||||||
|
case IO_DONE: return NULL;
|
||||||
|
case IO_CLOSED: return "closed";
|
||||||
|
case IO_TIMEOUT: return "timeout";
|
||||||
|
default: return "unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
70
external/socket/src/io.h
vendored
Normal file
70
external/socket/src/io.h
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
#ifndef IO_H
|
||||||
|
#define IO_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Input/Output abstraction
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* This module defines the interface that LuaSocket expects from the
|
||||||
|
* transport layer for streamed input/output. The idea is that if any
|
||||||
|
* transport implements this interface, then the buffer.c functions
|
||||||
|
* automatically work on it.
|
||||||
|
*
|
||||||
|
* The module socket.h implements this interface, and thus the module tcp.h
|
||||||
|
* is very simple.
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "timeout.h"
|
||||||
|
|
||||||
|
/* IO error codes */
|
||||||
|
enum {
|
||||||
|
IO_DONE = 0, /* operation completed successfully */
|
||||||
|
IO_TIMEOUT = -1, /* operation timed out */
|
||||||
|
IO_CLOSED = -2, /* the connection has been closed */
|
||||||
|
IO_UNKNOWN = -3
|
||||||
|
};
|
||||||
|
|
||||||
|
/* interface to error message function */
|
||||||
|
typedef const char *(*p_error) (
|
||||||
|
void *ctx, /* context needed by send */
|
||||||
|
int err /* error code */
|
||||||
|
);
|
||||||
|
|
||||||
|
/* interface to send function */
|
||||||
|
typedef int (*p_send) (
|
||||||
|
void *ctx, /* context needed by send */
|
||||||
|
const char *data, /* pointer to buffer with data to send */
|
||||||
|
size_t count, /* number of bytes to send from buffer */
|
||||||
|
size_t *sent, /* number of bytes sent uppon return */
|
||||||
|
p_timeout tm /* timeout control */
|
||||||
|
);
|
||||||
|
|
||||||
|
/* interface to recv function */
|
||||||
|
typedef int (*p_recv) (
|
||||||
|
void *ctx, /* context needed by recv */
|
||||||
|
char *data, /* pointer to buffer where data will be writen */
|
||||||
|
size_t count, /* number of bytes to receive into buffer */
|
||||||
|
size_t *got, /* number of bytes received uppon return */
|
||||||
|
p_timeout tm /* timeout control */
|
||||||
|
);
|
||||||
|
|
||||||
|
/* IO driver definition */
|
||||||
|
typedef struct t_io_ {
|
||||||
|
void *ctx; /* context needed by send/recv */
|
||||||
|
p_send send; /* send function pointer */
|
||||||
|
p_recv recv; /* receive function pointer */
|
||||||
|
p_error error; /* strerror function */
|
||||||
|
} t_io;
|
||||||
|
typedef t_io *p_io;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx);
|
||||||
|
const char *io_strerror(int err);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* IO_H */
|
||||||
104
external/socket/src/luasocket.c
vendored
Normal file
104
external/socket/src/luasocket.c
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* LuaSocket toolkit
|
||||||
|
* Networking support for the Lua language
|
||||||
|
* Diego Nehab
|
||||||
|
* 26/11/1999
|
||||||
|
*
|
||||||
|
* This library is part of an effort to progressively increase the network
|
||||||
|
* connectivity of the Lua language. The Lua interface to networking
|
||||||
|
* functions follows the Sockets API closely, trying to simplify all tasks
|
||||||
|
* involved in setting up both client and server connections. The provided
|
||||||
|
* IO routines, however, follow the Lua style, being very similar to the
|
||||||
|
* standard Lua read and write functions.
|
||||||
|
\*=========================================================================*/
|
||||||
|
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "auxiliar.h"
|
||||||
|
#include "except.h"
|
||||||
|
#include "timeout.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "inet.h"
|
||||||
|
#include "tcp.h"
|
||||||
|
#include "udp.h"
|
||||||
|
#include "select.h"
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Internal function prototypes
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int global_skip(lua_State *L);
|
||||||
|
static int global_unload(lua_State *L);
|
||||||
|
static int base_open(lua_State *L);
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Modules and functions
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static const luaL_Reg mod[] = {
|
||||||
|
{"auxiliar", auxiliar_open},
|
||||||
|
{"except", except_open},
|
||||||
|
{"timeout", timeout_open},
|
||||||
|
{"buffer", buffer_open},
|
||||||
|
{"inet", inet_open},
|
||||||
|
{"tcp", tcp_open},
|
||||||
|
{"udp", udp_open},
|
||||||
|
{"select", select_open},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static luaL_Reg func[] = {
|
||||||
|
{"skip", global_skip},
|
||||||
|
{"__unload", global_unload},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Skip a few arguments
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int global_skip(lua_State *L) {
|
||||||
|
int amount = (int) luaL_checkinteger(L, 1);
|
||||||
|
int ret = lua_gettop(L) - amount - 1;
|
||||||
|
return ret >= 0 ? ret : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Unloads the library
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int global_unload(lua_State *L) {
|
||||||
|
(void) L;
|
||||||
|
socket_close();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Setup basic stuff.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int base_open(lua_State *L) {
|
||||||
|
if (socket_open()) {
|
||||||
|
/* export functions (and leave namespace table on top of stack) */
|
||||||
|
lua_newtable(L);
|
||||||
|
luaL_setfuncs(L, func, 0);
|
||||||
|
#ifdef LUASOCKET_DEBUG
|
||||||
|
lua_pushstring(L, "_DEBUG");
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
#endif
|
||||||
|
/* make version string available to scripts */
|
||||||
|
lua_pushstring(L, "_VERSION");
|
||||||
|
lua_pushstring(L, LUASOCKET_VERSION);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
lua_pushstring(L, "unable to initialize library");
|
||||||
|
lua_error(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes all library modules.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
LUASOCKET_API int luaopen_socket_core(lua_State *L) {
|
||||||
|
int i;
|
||||||
|
base_open(L);
|
||||||
|
for (i = 0; mod[i].name; i++) mod[i].func(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
36
external/socket/src/luasocket.h
vendored
Normal file
36
external/socket/src/luasocket.h
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef LUASOCKET_H
|
||||||
|
#define LUASOCKET_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* LuaSocket toolkit
|
||||||
|
* Networking support for the Lua language
|
||||||
|
* Diego Nehab
|
||||||
|
* 9/11/1999
|
||||||
|
\*=========================================================================*/
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------* \
|
||||||
|
* Current socket library version
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
#define LUASOCKET_VERSION "LuaSocket 3.0.0"
|
||||||
|
#define LUASOCKET_COPYRIGHT "Copyright (C) 1999-2013 Diego Nehab"
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* This macro prefixes all exported API functions
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
#ifndef LUASOCKET_API
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define LUASOCKET_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define LUASOCKET_API __attribute__ ((visibility ("default")))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "lua.h"
|
||||||
|
#include "lauxlib.h"
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes the library.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
LUASOCKET_API int luaopen_socket_core(lua_State *L);
|
||||||
|
|
||||||
|
#endif /* LUASOCKET_H */
|
||||||
139
external/socket/src/makefile
vendored
Normal file
139
external/socket/src/makefile
vendored
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
# luasocket src/makefile
|
||||||
|
#
|
||||||
|
# Definitions in this section can be overriden on the command line or in the
|
||||||
|
# environment.
|
||||||
|
|
||||||
|
# LUAV: 5.1 5.2 5.3 5.4
|
||||||
|
# lua version to build against
|
||||||
|
LUAV?=5.4
|
||||||
|
|
||||||
|
# MYCFLAGS: to be set by user if needed
|
||||||
|
MYCFLAGS?=
|
||||||
|
|
||||||
|
# MYLDFLAGS: to be set by user if needed
|
||||||
|
MYLDFLAGS?=
|
||||||
|
|
||||||
|
# DEBUG: NODEBUG DEBUG
|
||||||
|
# debug mode causes luasocket to collect and returns timing information useful
|
||||||
|
# for testing and debugging luasocket itself
|
||||||
|
DEBUG?=NODEBUG
|
||||||
|
|
||||||
|
# DESTDIR: (no default)
|
||||||
|
# used by package managers to install into a temporary destination
|
||||||
|
DESTDIR?=
|
||||||
|
|
||||||
|
#------
|
||||||
|
# Definitions below can be overridden on the make command line, but
|
||||||
|
# shouldn't have to be.
|
||||||
|
|
||||||
|
#------
|
||||||
|
# Compiler and linker settings
|
||||||
|
AR = ar rcu
|
||||||
|
CC = cc
|
||||||
|
DEF = -DLUASOCKET_${DEBUG}
|
||||||
|
INCS = -I../../../lua-5.4
|
||||||
|
CFLAGS = ${INCS} ${DEF} \
|
||||||
|
-Wall -Wshadow -Wextra -Wimplicit -O2 -fpic
|
||||||
|
|
||||||
|
.SUFFIXES: .obj
|
||||||
|
|
||||||
|
.c.obj:
|
||||||
|
${CC} ${CFLAGS} //Fo"$@" //c $<
|
||||||
|
|
||||||
|
#------
|
||||||
|
# Output file names
|
||||||
|
#
|
||||||
|
SOCKET_V=3.0.0
|
||||||
|
MIME_V=1.0.3
|
||||||
|
SOCKET_A=socket.a
|
||||||
|
|
||||||
|
#------
|
||||||
|
# Modules belonging to socket-core
|
||||||
|
#
|
||||||
|
SOCKET_OBJS= \
|
||||||
|
luasocket.o \
|
||||||
|
timeout.o \
|
||||||
|
buffer.o \
|
||||||
|
io.o \
|
||||||
|
auxiliar.o \
|
||||||
|
compat.o \
|
||||||
|
options.o \
|
||||||
|
inet.o \
|
||||||
|
usocket.o \
|
||||||
|
except.o \
|
||||||
|
select.o \
|
||||||
|
tcp.o \
|
||||||
|
udp.o
|
||||||
|
|
||||||
|
#------
|
||||||
|
# Modules belonging mime-core
|
||||||
|
#
|
||||||
|
MIME_OBJS= \
|
||||||
|
mime.o \
|
||||||
|
compat.o
|
||||||
|
|
||||||
|
#------
|
||||||
|
# Modules belonging unix (local domain sockets)
|
||||||
|
#
|
||||||
|
UNIX_OBJS=\
|
||||||
|
buffer.o \
|
||||||
|
auxiliar.o \
|
||||||
|
options.o \
|
||||||
|
timeout.o \
|
||||||
|
io.o \
|
||||||
|
usocket.o \
|
||||||
|
unixstream.o \
|
||||||
|
unixdgram.o \
|
||||||
|
compat.o \
|
||||||
|
unix.o
|
||||||
|
|
||||||
|
#------
|
||||||
|
# Modules belonging to serial (device streams)
|
||||||
|
#
|
||||||
|
SERIAL_OBJS=\
|
||||||
|
buffer.o \
|
||||||
|
compat.o \
|
||||||
|
auxiliar.o \
|
||||||
|
options.o \
|
||||||
|
timeout.o \
|
||||||
|
io.o \
|
||||||
|
usocket.o \
|
||||||
|
serial.o
|
||||||
|
|
||||||
|
all: ${SOCKET_A}
|
||||||
|
|
||||||
|
${SOCKET_A}: ${SOCKET_OBJS} ${MIME_OBJS} ${UNIX_OBJS} ${SERIAL_OBJS}
|
||||||
|
${AR} $@ ${SOCKET_OBJS} ${MIME_OBJS} ${UNIX_OBJS} ${SERIAL_OBJS}
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f ${SOCKET_OBJS} ${SERIAL_OBJS} ${MIME_OBJS} ${UNIX_OBJS}
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
#------
|
||||||
|
# List of dependencies
|
||||||
|
#
|
||||||
|
compat.o: compat.c compat.h
|
||||||
|
auxiliar.o: auxiliar.c auxiliar.h
|
||||||
|
buffer.o: buffer.c buffer.h io.h timeout.h
|
||||||
|
except.o: except.c except.h
|
||||||
|
inet.o: inet.c inet.h socket.h io.h timeout.h usocket.h
|
||||||
|
io.o: io.c io.h timeout.h
|
||||||
|
luasocket.o: luasocket.c luasocket.h auxiliar.h except.h \
|
||||||
|
timeout.h buffer.h io.h inet.h socket.h usocket.h tcp.h \
|
||||||
|
udp.h select.h
|
||||||
|
mime.o: mime.c mime.h
|
||||||
|
options.o: options.c auxiliar.h options.h socket.h io.h \
|
||||||
|
timeout.h usocket.h inet.h
|
||||||
|
select.o: select.c socket.h io.h timeout.h usocket.h select.h
|
||||||
|
serial.o: serial.c auxiliar.h socket.h io.h timeout.h usocket.h \
|
||||||
|
options.h unix.h buffer.h
|
||||||
|
tcp.o: tcp.c auxiliar.h socket.h io.h timeout.h usocket.h \
|
||||||
|
inet.h options.h tcp.h buffer.h
|
||||||
|
timeout.o: timeout.c auxiliar.h timeout.h
|
||||||
|
udp.o: udp.c auxiliar.h socket.h io.h timeout.h usocket.h \
|
||||||
|
inet.h options.h udp.h
|
||||||
|
unix.o: unix.c auxiliar.h socket.h io.h timeout.h usocket.h \
|
||||||
|
options.h unix.h buffer.h
|
||||||
|
usocket.o: usocket.c socket.h io.h timeout.h
|
||||||
|
wsocket.o: wsocket.c socket.h io.h timeout.h usocket.h
|
||||||
852
external/socket/src/mime.c
vendored
Normal file
852
external/socket/src/mime.c
vendored
Normal file
|
|
@ -0,0 +1,852 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* MIME support functions
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "mime.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Don't want to trust escape character constants
|
||||||
|
\*=========================================================================*/
|
||||||
|
typedef unsigned char UC;
|
||||||
|
static const char CRLF[] = "\r\n";
|
||||||
|
static const char EQCRLF[] = "=\r\n";
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal function prototypes.
|
||||||
|
\*=========================================================================*/
|
||||||
|
static int mime_global_wrp(lua_State *L);
|
||||||
|
static int mime_global_b64(lua_State *L);
|
||||||
|
static int mime_global_unb64(lua_State *L);
|
||||||
|
static int mime_global_qp(lua_State *L);
|
||||||
|
static int mime_global_unqp(lua_State *L);
|
||||||
|
static int mime_global_qpwrp(lua_State *L);
|
||||||
|
static int mime_global_eol(lua_State *L);
|
||||||
|
static int mime_global_dot(lua_State *L);
|
||||||
|
|
||||||
|
static size_t dot(int c, size_t state, luaL_Buffer *buffer);
|
||||||
|
/*static void b64setup(UC *base);*/
|
||||||
|
static size_t b64encode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
|
||||||
|
static size_t b64pad(const UC *input, size_t size, luaL_Buffer *buffer);
|
||||||
|
static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
|
||||||
|
|
||||||
|
/*static void qpsetup(UC *class, UC *unbase);*/
|
||||||
|
static void qpquote(UC c, luaL_Buffer *buffer);
|
||||||
|
static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
|
||||||
|
static size_t qpencode(UC c, UC *input, size_t size,
|
||||||
|
const char *marker, luaL_Buffer *buffer);
|
||||||
|
static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer);
|
||||||
|
|
||||||
|
/* code support functions */
|
||||||
|
static luaL_Reg func[] = {
|
||||||
|
{ "dot", mime_global_dot },
|
||||||
|
{ "b64", mime_global_b64 },
|
||||||
|
{ "eol", mime_global_eol },
|
||||||
|
{ "qp", mime_global_qp },
|
||||||
|
{ "qpwrp", mime_global_qpwrp },
|
||||||
|
{ "unb64", mime_global_unb64 },
|
||||||
|
{ "unqp", mime_global_unqp },
|
||||||
|
{ "wrp", mime_global_wrp },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Quoted-printable globals
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
enum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST};
|
||||||
|
|
||||||
|
static const UC qpclass[] = {
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_IF_LAST, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_CR, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_IF_LAST, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_QUOTED, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,
|
||||||
|
QP_PLAIN, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,
|
||||||
|
QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED
|
||||||
|
};
|
||||||
|
|
||||||
|
static const UC qpbase[] = "0123456789ABCDEF";
|
||||||
|
|
||||||
|
static const UC qpunbase[] = {
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Base64 globals
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static const UC b64base[] =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
static const UC b64unbase[] = {
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
|
||||||
|
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0,
|
||||||
|
255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
||||||
|
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255,
|
||||||
|
255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
|
||||||
|
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
|
||||||
|
51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255
|
||||||
|
};
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Exported functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
LUASOCKET_API int luaopen_mime_core(lua_State *L)
|
||||||
|
{
|
||||||
|
lua_newtable(L);
|
||||||
|
luaL_setfuncs(L, func, 0);
|
||||||
|
/* make version string available to scripts */
|
||||||
|
lua_pushstring(L, "_VERSION");
|
||||||
|
lua_pushstring(L, MIME_VERSION);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
/* initialize lookup tables */
|
||||||
|
/*qpsetup(qpclass, qpunbase);*/
|
||||||
|
/*b64setup(b64unbase);*/
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Global Lua functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Incrementaly breaks a string into lines. The string can have CRLF breaks.
|
||||||
|
* A, n = wrp(l, B, length)
|
||||||
|
* A is a copy of B, broken into lines of at most 'length' bytes.
|
||||||
|
* 'l' is how many bytes are left for the first line of B.
|
||||||
|
* 'n' is the number of bytes left in the last line of A.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int mime_global_wrp(lua_State *L)
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
int left = (int) luaL_checknumber(L, 1);
|
||||||
|
const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size);
|
||||||
|
const UC *last = input + size;
|
||||||
|
int length = (int) luaL_optnumber(L, 3, 76);
|
||||||
|
luaL_Buffer buffer;
|
||||||
|
/* end of input black-hole */
|
||||||
|
if (!input) {
|
||||||
|
/* if last line has not been terminated, add a line break */
|
||||||
|
if (left < length) lua_pushstring(L, CRLF);
|
||||||
|
/* otherwise, we are done */
|
||||||
|
else lua_pushnil(L);
|
||||||
|
lua_pushnumber(L, length);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
luaL_buffinit(L, &buffer);
|
||||||
|
while (input < last) {
|
||||||
|
switch (*input) {
|
||||||
|
case '\r':
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
luaL_addstring(&buffer, CRLF);
|
||||||
|
left = length;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (left <= 0) {
|
||||||
|
left = length;
|
||||||
|
luaL_addstring(&buffer, CRLF);
|
||||||
|
}
|
||||||
|
luaL_addchar(&buffer, *input);
|
||||||
|
left--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
input++;
|
||||||
|
}
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
lua_pushnumber(L, left);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Fill base64 decode map.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static void b64setup(UC *unbase)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i <= 255; i++) unbase[i] = (UC) 255;
|
||||||
|
for (i = 0; i < 64; i++) unbase[b64base[i]] = (UC) i;
|
||||||
|
unbase['='] = 0;
|
||||||
|
|
||||||
|
printf("static const UC b64unbase[] = {\n");
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
printf("%d, ", unbase[i]);
|
||||||
|
}
|
||||||
|
printf("\n}\n;");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Acumulates bytes in input buffer until 3 bytes are available.
|
||||||
|
* Translate the 3 bytes into Base64 form and append to buffer.
|
||||||
|
* Returns new number of bytes in buffer.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static size_t b64encode(UC c, UC *input, size_t size,
|
||||||
|
luaL_Buffer *buffer)
|
||||||
|
{
|
||||||
|
input[size++] = c;
|
||||||
|
if (size == 3) {
|
||||||
|
UC code[4];
|
||||||
|
unsigned long value = 0;
|
||||||
|
value += input[0]; value <<= 8;
|
||||||
|
value += input[1]; value <<= 8;
|
||||||
|
value += input[2];
|
||||||
|
code[3] = b64base[value & 0x3f]; value >>= 6;
|
||||||
|
code[2] = b64base[value & 0x3f]; value >>= 6;
|
||||||
|
code[1] = b64base[value & 0x3f]; value >>= 6;
|
||||||
|
code[0] = b64base[value];
|
||||||
|
luaL_addlstring(buffer, (char *) code, 4);
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Encodes the Base64 last 1 or 2 bytes and adds padding '='
|
||||||
|
* Result, if any, is appended to buffer.
|
||||||
|
* Returns 0.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static size_t b64pad(const UC *input, size_t size,
|
||||||
|
luaL_Buffer *buffer)
|
||||||
|
{
|
||||||
|
unsigned long value = 0;
|
||||||
|
UC code[4] = {'=', '=', '=', '='};
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
value = input[0] << 4;
|
||||||
|
code[1] = b64base[value & 0x3f]; value >>= 6;
|
||||||
|
code[0] = b64base[value];
|
||||||
|
luaL_addlstring(buffer, (char *) code, 4);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
value = input[0]; value <<= 8;
|
||||||
|
value |= input[1]; value <<= 2;
|
||||||
|
code[2] = b64base[value & 0x3f]; value >>= 6;
|
||||||
|
code[1] = b64base[value & 0x3f]; value >>= 6;
|
||||||
|
code[0] = b64base[value];
|
||||||
|
luaL_addlstring(buffer, (char *) code, 4);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Acumulates bytes in input buffer until 4 bytes are available.
|
||||||
|
* Translate the 4 bytes from Base64 form and append to buffer.
|
||||||
|
* Returns new number of bytes in buffer.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static size_t b64decode(UC c, UC *input, size_t size,
|
||||||
|
luaL_Buffer *buffer)
|
||||||
|
{
|
||||||
|
/* ignore invalid characters */
|
||||||
|
if (b64unbase[c] > 64) return size;
|
||||||
|
input[size++] = c;
|
||||||
|
/* decode atom */
|
||||||
|
if (size == 4) {
|
||||||
|
UC decoded[3];
|
||||||
|
int valid, value = 0;
|
||||||
|
value = b64unbase[input[0]]; value <<= 6;
|
||||||
|
value |= b64unbase[input[1]]; value <<= 6;
|
||||||
|
value |= b64unbase[input[2]]; value <<= 6;
|
||||||
|
value |= b64unbase[input[3]];
|
||||||
|
decoded[2] = (UC) (value & 0xff); value >>= 8;
|
||||||
|
decoded[1] = (UC) (value & 0xff); value >>= 8;
|
||||||
|
decoded[0] = (UC) value;
|
||||||
|
/* take care of paddding */
|
||||||
|
valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3;
|
||||||
|
luaL_addlstring(buffer, (char *) decoded, valid);
|
||||||
|
return 0;
|
||||||
|
/* need more data */
|
||||||
|
} else return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Incrementally applies the Base64 transfer content encoding to a string
|
||||||
|
* A, B = b64(C, D)
|
||||||
|
* A is the encoded version of the largest prefix of C .. D that is
|
||||||
|
* divisible by 3. B has the remaining bytes of C .. D, *without* encoding.
|
||||||
|
* The easiest thing would be to concatenate the two strings and
|
||||||
|
* encode the result, but we can't afford that or Lua would dupplicate
|
||||||
|
* every chunk we received.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int mime_global_b64(lua_State *L)
|
||||||
|
{
|
||||||
|
UC atom[3];
|
||||||
|
size_t isize = 0, asize = 0;
|
||||||
|
const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
|
||||||
|
const UC *last = input + isize;
|
||||||
|
luaL_Buffer buffer;
|
||||||
|
/* end-of-input blackhole */
|
||||||
|
if (!input) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* make sure we don't confuse buffer stuff with arguments */
|
||||||
|
lua_settop(L, 2);
|
||||||
|
/* process first part of the input */
|
||||||
|
luaL_buffinit(L, &buffer);
|
||||||
|
while (input < last)
|
||||||
|
asize = b64encode(*input++, atom, asize, &buffer);
|
||||||
|
input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
|
||||||
|
/* if second part is nil, we are done */
|
||||||
|
if (!input) {
|
||||||
|
size_t osize = 0;
|
||||||
|
asize = b64pad(atom, asize, &buffer);
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
/* if the output is empty and the input is nil, return nil */
|
||||||
|
lua_tolstring(L, -1, &osize);
|
||||||
|
if (osize == 0) lua_pushnil(L);
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* otherwise process the second part */
|
||||||
|
last = input + isize;
|
||||||
|
while (input < last)
|
||||||
|
asize = b64encode(*input++, atom, asize, &buffer);
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
lua_pushlstring(L, (char *) atom, asize);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Incrementally removes the Base64 transfer content encoding from a string
|
||||||
|
* A, B = b64(C, D)
|
||||||
|
* A is the encoded version of the largest prefix of C .. D that is
|
||||||
|
* divisible by 4. B has the remaining bytes of C .. D, *without* encoding.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int mime_global_unb64(lua_State *L)
|
||||||
|
{
|
||||||
|
UC atom[4];
|
||||||
|
size_t isize = 0, asize = 0;
|
||||||
|
const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
|
||||||
|
const UC *last = input + isize;
|
||||||
|
luaL_Buffer buffer;
|
||||||
|
/* end-of-input blackhole */
|
||||||
|
if (!input) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* make sure we don't confuse buffer stuff with arguments */
|
||||||
|
lua_settop(L, 2);
|
||||||
|
/* process first part of the input */
|
||||||
|
luaL_buffinit(L, &buffer);
|
||||||
|
while (input < last)
|
||||||
|
asize = b64decode(*input++, atom, asize, &buffer);
|
||||||
|
input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
|
||||||
|
/* if second is nil, we are done */
|
||||||
|
if (!input) {
|
||||||
|
size_t osize = 0;
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
/* if the output is empty and the input is nil, return nil */
|
||||||
|
lua_tolstring(L, -1, &osize);
|
||||||
|
if (osize == 0) lua_pushnil(L);
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* otherwise, process the rest of the input */
|
||||||
|
last = input + isize;
|
||||||
|
while (input < last)
|
||||||
|
asize = b64decode(*input++, atom, asize, &buffer);
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
lua_pushlstring(L, (char *) atom, asize);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Quoted-printable encoding scheme
|
||||||
|
* all (except CRLF in text) can be =XX
|
||||||
|
* CLRL in not text must be =XX=XX
|
||||||
|
* 33 through 60 inclusive can be plain
|
||||||
|
* 62 through 126 inclusive can be plain
|
||||||
|
* 9 and 32 can be plain, unless in the end of a line, where must be =XX
|
||||||
|
* encoded lines must be no longer than 76 not counting CRLF
|
||||||
|
* soft line-break are =CRLF
|
||||||
|
* To encode one byte, we need to see the next two.
|
||||||
|
* Worst case is when we see a space, and wonder if a CRLF is comming
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
#if 0
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Split quoted-printable characters into classes
|
||||||
|
* Precompute reverse map for encoding
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static void qpsetup(UC *cl, UC *unbase)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 256; i++) cl[i] = QP_QUOTED;
|
||||||
|
for (i = 33; i <= 60; i++) cl[i] = QP_PLAIN;
|
||||||
|
for (i = 62; i <= 126; i++) cl[i] = QP_PLAIN;
|
||||||
|
cl['\t'] = QP_IF_LAST;
|
||||||
|
cl[' '] = QP_IF_LAST;
|
||||||
|
cl['\r'] = QP_CR;
|
||||||
|
for (i = 0; i < 256; i++) unbase[i] = 255;
|
||||||
|
unbase['0'] = 0; unbase['1'] = 1; unbase['2'] = 2;
|
||||||
|
unbase['3'] = 3; unbase['4'] = 4; unbase['5'] = 5;
|
||||||
|
unbase['6'] = 6; unbase['7'] = 7; unbase['8'] = 8;
|
||||||
|
unbase['9'] = 9; unbase['A'] = 10; unbase['a'] = 10;
|
||||||
|
unbase['B'] = 11; unbase['b'] = 11; unbase['C'] = 12;
|
||||||
|
unbase['c'] = 12; unbase['D'] = 13; unbase['d'] = 13;
|
||||||
|
unbase['E'] = 14; unbase['e'] = 14; unbase['F'] = 15;
|
||||||
|
unbase['f'] = 15;
|
||||||
|
|
||||||
|
printf("static UC qpclass[] = {");
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
if (i % 6 == 0) {
|
||||||
|
printf("\n ");
|
||||||
|
}
|
||||||
|
switch(cl[i]) {
|
||||||
|
case QP_QUOTED:
|
||||||
|
printf("QP_QUOTED, ");
|
||||||
|
break;
|
||||||
|
case QP_PLAIN:
|
||||||
|
printf("QP_PLAIN, ");
|
||||||
|
break;
|
||||||
|
case QP_CR:
|
||||||
|
printf("QP_CR, ");
|
||||||
|
break;
|
||||||
|
case QP_IF_LAST:
|
||||||
|
printf("QP_IF_LAST, ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n};\n");
|
||||||
|
|
||||||
|
printf("static const UC qpunbase[] = {");
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
int c = qpunbase[i];
|
||||||
|
printf("%d, ", c);
|
||||||
|
}
|
||||||
|
printf("\";\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Output one character in form =XX
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static void qpquote(UC c, luaL_Buffer *buffer)
|
||||||
|
{
|
||||||
|
luaL_addchar(buffer, '=');
|
||||||
|
luaL_addchar(buffer, qpbase[c >> 4]);
|
||||||
|
luaL_addchar(buffer, qpbase[c & 0x0F]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Accumulate characters until we are sure about how to deal with them.
|
||||||
|
* Once we are sure, output to the buffer, in the correct form.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static size_t qpencode(UC c, UC *input, size_t size,
|
||||||
|
const char *marker, luaL_Buffer *buffer)
|
||||||
|
{
|
||||||
|
input[size++] = c;
|
||||||
|
/* deal with all characters we can have */
|
||||||
|
while (size > 0) {
|
||||||
|
switch (qpclass[input[0]]) {
|
||||||
|
/* might be the CR of a CRLF sequence */
|
||||||
|
case QP_CR:
|
||||||
|
if (size < 2) return size;
|
||||||
|
if (input[1] == '\n') {
|
||||||
|
luaL_addstring(buffer, marker);
|
||||||
|
return 0;
|
||||||
|
} else qpquote(input[0], buffer);
|
||||||
|
break;
|
||||||
|
/* might be a space and that has to be quoted if last in line */
|
||||||
|
case QP_IF_LAST:
|
||||||
|
if (size < 3) return size;
|
||||||
|
/* if it is the last, quote it and we are done */
|
||||||
|
if (input[1] == '\r' && input[2] == '\n') {
|
||||||
|
qpquote(input[0], buffer);
|
||||||
|
luaL_addstring(buffer, marker);
|
||||||
|
return 0;
|
||||||
|
} else luaL_addchar(buffer, input[0]);
|
||||||
|
break;
|
||||||
|
/* might have to be quoted always */
|
||||||
|
case QP_QUOTED:
|
||||||
|
qpquote(input[0], buffer);
|
||||||
|
break;
|
||||||
|
/* might never have to be quoted */
|
||||||
|
default:
|
||||||
|
luaL_addchar(buffer, input[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
input[0] = input[1]; input[1] = input[2];
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Deal with the final characters
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
if (qpclass[input[i]] == QP_PLAIN) luaL_addchar(buffer, input[i]);
|
||||||
|
else qpquote(input[i], buffer);
|
||||||
|
}
|
||||||
|
if (size > 0) luaL_addstring(buffer, EQCRLF);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Incrementally converts a string to quoted-printable
|
||||||
|
* A, B = qp(C, D, marker)
|
||||||
|
* Marker is the text to be used to replace CRLF sequences found in A.
|
||||||
|
* A is the encoded version of the largest prefix of C .. D that
|
||||||
|
* can be encoded without doubts.
|
||||||
|
* B has the remaining bytes of C .. D, *without* encoding.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int mime_global_qp(lua_State *L)
|
||||||
|
{
|
||||||
|
size_t asize = 0, isize = 0;
|
||||||
|
UC atom[3];
|
||||||
|
const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
|
||||||
|
const UC *last = input + isize;
|
||||||
|
const char *marker = luaL_optstring(L, 3, CRLF);
|
||||||
|
luaL_Buffer buffer;
|
||||||
|
/* end-of-input blackhole */
|
||||||
|
if (!input) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* make sure we don't confuse buffer stuff with arguments */
|
||||||
|
lua_settop(L, 3);
|
||||||
|
/* process first part of input */
|
||||||
|
luaL_buffinit(L, &buffer);
|
||||||
|
while (input < last)
|
||||||
|
asize = qpencode(*input++, atom, asize, marker, &buffer);
|
||||||
|
input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
|
||||||
|
/* if second part is nil, we are done */
|
||||||
|
if (!input) {
|
||||||
|
asize = qppad(atom, asize, &buffer);
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
if (!(*lua_tostring(L, -1))) lua_pushnil(L);
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* otherwise process rest of input */
|
||||||
|
last = input + isize;
|
||||||
|
while (input < last)
|
||||||
|
asize = qpencode(*input++, atom, asize, marker, &buffer);
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
lua_pushlstring(L, (char *) atom, asize);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Accumulate characters until we are sure about how to deal with them.
|
||||||
|
* Once we are sure, output the to the buffer, in the correct form.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) {
|
||||||
|
int d;
|
||||||
|
input[size++] = c;
|
||||||
|
/* deal with all characters we can deal */
|
||||||
|
switch (input[0]) {
|
||||||
|
/* if we have an escape character */
|
||||||
|
case '=':
|
||||||
|
if (size < 3) return size;
|
||||||
|
/* eliminate soft line break */
|
||||||
|
if (input[1] == '\r' && input[2] == '\n') return 0;
|
||||||
|
/* decode quoted representation */
|
||||||
|
c = qpunbase[input[1]]; d = qpunbase[input[2]];
|
||||||
|
/* if it is an invalid, do not decode */
|
||||||
|
if (c > 15 || d > 15) luaL_addlstring(buffer, (char *)input, 3);
|
||||||
|
else luaL_addchar(buffer, (char) ((c << 4) + d));
|
||||||
|
return 0;
|
||||||
|
case '\r':
|
||||||
|
if (size < 2) return size;
|
||||||
|
if (input[1] == '\n') luaL_addlstring(buffer, (char *)input, 2);
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
if (input[0] == '\t' || (input[0] > 31 && input[0] < 127))
|
||||||
|
luaL_addchar(buffer, input[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Incrementally decodes a string in quoted-printable
|
||||||
|
* A, B = qp(C, D)
|
||||||
|
* A is the decoded version of the largest prefix of C .. D that
|
||||||
|
* can be decoded without doubts.
|
||||||
|
* B has the remaining bytes of C .. D, *without* decoding.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int mime_global_unqp(lua_State *L)
|
||||||
|
{
|
||||||
|
size_t asize = 0, isize = 0;
|
||||||
|
UC atom[3];
|
||||||
|
const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);
|
||||||
|
const UC *last = input + isize;
|
||||||
|
luaL_Buffer buffer;
|
||||||
|
/* end-of-input blackhole */
|
||||||
|
if (!input) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* make sure we don't confuse buffer stuff with arguments */
|
||||||
|
lua_settop(L, 2);
|
||||||
|
/* process first part of input */
|
||||||
|
luaL_buffinit(L, &buffer);
|
||||||
|
while (input < last)
|
||||||
|
asize = qpdecode(*input++, atom, asize, &buffer);
|
||||||
|
input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);
|
||||||
|
/* if second part is nil, we are done */
|
||||||
|
if (!input) {
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
if (!(*lua_tostring(L, -1))) lua_pushnil(L);
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* otherwise process rest of input */
|
||||||
|
last = input + isize;
|
||||||
|
while (input < last)
|
||||||
|
asize = qpdecode(*input++, atom, asize, &buffer);
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
lua_pushlstring(L, (char *) atom, asize);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Incrementally breaks a quoted-printed string into lines
|
||||||
|
* A, n = qpwrp(l, B, length)
|
||||||
|
* A is a copy of B, broken into lines of at most 'length' bytes.
|
||||||
|
* 'l' is how many bytes are left for the first line of B.
|
||||||
|
* 'n' is the number of bytes left in the last line of A.
|
||||||
|
* There are two complications: lines can't be broken in the middle
|
||||||
|
* of an encoded =XX, and there might be line breaks already
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int mime_global_qpwrp(lua_State *L)
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
int left = (int) luaL_checknumber(L, 1);
|
||||||
|
const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size);
|
||||||
|
const UC *last = input + size;
|
||||||
|
int length = (int) luaL_optnumber(L, 3, 76);
|
||||||
|
luaL_Buffer buffer;
|
||||||
|
/* end-of-input blackhole */
|
||||||
|
if (!input) {
|
||||||
|
if (left < length) lua_pushstring(L, EQCRLF);
|
||||||
|
else lua_pushnil(L);
|
||||||
|
lua_pushnumber(L, length);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* process all input */
|
||||||
|
luaL_buffinit(L, &buffer);
|
||||||
|
while (input < last) {
|
||||||
|
switch (*input) {
|
||||||
|
case '\r':
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
left = length;
|
||||||
|
luaL_addstring(&buffer, CRLF);
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
if (left <= 3) {
|
||||||
|
left = length;
|
||||||
|
luaL_addstring(&buffer, EQCRLF);
|
||||||
|
}
|
||||||
|
luaL_addchar(&buffer, *input);
|
||||||
|
left--;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (left <= 1) {
|
||||||
|
left = length;
|
||||||
|
luaL_addstring(&buffer, EQCRLF);
|
||||||
|
}
|
||||||
|
luaL_addchar(&buffer, *input);
|
||||||
|
left--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
input++;
|
||||||
|
}
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
lua_pushnumber(L, left);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Here is what we do: \n, and \r are considered candidates for line
|
||||||
|
* break. We issue *one* new line marker if any of them is seen alone, or
|
||||||
|
* followed by a different one. That is, \n\n and \r\r will issue two
|
||||||
|
* end of line markers each, but \r\n, \n\r etc will only issue *one*
|
||||||
|
* marker. This covers Mac OS, Mac OS X, VMS, Unix and DOS, as well as
|
||||||
|
* probably other more obscure conventions.
|
||||||
|
*
|
||||||
|
* c is the current character being processed
|
||||||
|
* last is the previous character
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
#define eolcandidate(c) (c == '\r' || c == '\n')
|
||||||
|
static int eolprocess(int c, int last, const char *marker,
|
||||||
|
luaL_Buffer *buffer)
|
||||||
|
{
|
||||||
|
if (eolcandidate(c)) {
|
||||||
|
if (eolcandidate(last)) {
|
||||||
|
if (c == last) luaL_addstring(buffer, marker);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
luaL_addstring(buffer, marker);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
luaL_addchar(buffer, (char) c);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Converts a string to uniform EOL convention.
|
||||||
|
* A, n = eol(o, B, marker)
|
||||||
|
* A is the converted version of the largest prefix of B that can be
|
||||||
|
* converted unambiguously. 'o' is the context returned by the previous
|
||||||
|
* call. 'n' is the new context.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int mime_global_eol(lua_State *L)
|
||||||
|
{
|
||||||
|
int ctx = (int) luaL_checkinteger(L, 1);
|
||||||
|
size_t isize = 0;
|
||||||
|
const char *input = luaL_optlstring(L, 2, NULL, &isize);
|
||||||
|
const char *last = input + isize;
|
||||||
|
const char *marker = luaL_optstring(L, 3, CRLF);
|
||||||
|
luaL_Buffer buffer;
|
||||||
|
luaL_buffinit(L, &buffer);
|
||||||
|
/* end of input blackhole */
|
||||||
|
if (!input) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushnumber(L, 0);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* process all input */
|
||||||
|
while (input < last)
|
||||||
|
ctx = eolprocess(*input++, ctx, marker, &buffer);
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
lua_pushnumber(L, ctx);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Takes one byte and stuff it if needed.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static size_t dot(int c, size_t state, luaL_Buffer *buffer)
|
||||||
|
{
|
||||||
|
luaL_addchar(buffer, (char) c);
|
||||||
|
switch (c) {
|
||||||
|
case '\r':
|
||||||
|
return 1;
|
||||||
|
case '\n':
|
||||||
|
return (state == 1)? 2: 0;
|
||||||
|
case '.':
|
||||||
|
if (state == 2)
|
||||||
|
luaL_addchar(buffer, '.');
|
||||||
|
/* Falls through. */
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Incrementally applies smtp stuffing to a string
|
||||||
|
* A, n = dot(l, D)
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int mime_global_dot(lua_State *L)
|
||||||
|
{
|
||||||
|
size_t isize = 0, state = (size_t) luaL_checknumber(L, 1);
|
||||||
|
const char *input = luaL_optlstring(L, 2, NULL, &isize);
|
||||||
|
const char *last = input + isize;
|
||||||
|
luaL_Buffer buffer;
|
||||||
|
/* end-of-input blackhole */
|
||||||
|
if (!input) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushnumber(L, 2);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* process all input */
|
||||||
|
luaL_buffinit(L, &buffer);
|
||||||
|
while (input < last)
|
||||||
|
state = dot(*input++, state, &buffer);
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
lua_pushnumber(L, (lua_Number) state);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
22
external/socket/src/mime.h
vendored
Normal file
22
external/socket/src/mime.h
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef MIME_H
|
||||||
|
#define MIME_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Core MIME support
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* This module provides functions to implement transfer content encodings
|
||||||
|
* and formatting conforming to RFC 2045. It is used by mime.lua, which
|
||||||
|
* provide a higher level interface to this functionality.
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Current MIME library version
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
#define MIME_VERSION "MIME 1.0.3"
|
||||||
|
#define MIME_COPYRIGHT "Copyright (C) 2004-2013 Diego Nehab"
|
||||||
|
#define MIME_AUTHORS "Diego Nehab"
|
||||||
|
|
||||||
|
LUASOCKET_API int luaopen_mime_core(lua_State *L);
|
||||||
|
|
||||||
|
#endif /* MIME_H */
|
||||||
480
external/socket/src/options.c
vendored
Normal file
480
external/socket/src/options.c
vendored
Normal file
|
|
@ -0,0 +1,480 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Common option interface
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "auxiliar.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "inet.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal functions prototypes
|
||||||
|
\*=========================================================================*/
|
||||||
|
static int opt_setmembership(lua_State *L, p_socket ps, int level, int name);
|
||||||
|
static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name);
|
||||||
|
static int opt_setboolean(lua_State *L, p_socket ps, int level, int name);
|
||||||
|
static int opt_getboolean(lua_State *L, p_socket ps, int level, int name);
|
||||||
|
static int opt_setint(lua_State *L, p_socket ps, int level, int name);
|
||||||
|
static int opt_getint(lua_State *L, p_socket ps, int level, int name);
|
||||||
|
static int opt_set(lua_State *L, p_socket ps, int level, int name,
|
||||||
|
void *val, int len);
|
||||||
|
static int opt_get(lua_State *L, p_socket ps, int level, int name,
|
||||||
|
void *val, int* len);
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Exported functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Calls appropriate option handler
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps)
|
||||||
|
{
|
||||||
|
const char *name = luaL_checkstring(L, 2); /* obj, name, ... */
|
||||||
|
while (opt->name && strcmp(name, opt->name))
|
||||||
|
opt++;
|
||||||
|
if (!opt->func) {
|
||||||
|
char msg[57];
|
||||||
|
sprintf(msg, "unsupported option `%.35s'", name);
|
||||||
|
luaL_argerror(L, 2, msg);
|
||||||
|
}
|
||||||
|
return opt->func(L, ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps)
|
||||||
|
{
|
||||||
|
const char *name = luaL_checkstring(L, 2); /* obj, name, ... */
|
||||||
|
while (opt->name && strcmp(name, opt->name))
|
||||||
|
opt++;
|
||||||
|
if (!opt->func) {
|
||||||
|
char msg[57];
|
||||||
|
sprintf(msg, "unsupported option `%.35s'", name);
|
||||||
|
luaL_argerror(L, 2, msg);
|
||||||
|
}
|
||||||
|
return opt->func(L, ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
/* enables reuse of local address */
|
||||||
|
int opt_set_reuseaddr(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_reuseaddr(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEADDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
/* enables reuse of local port */
|
||||||
|
int opt_set_reuseport(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEPORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_reuseport(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEPORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
/* disables the Nagle algorithm */
|
||||||
|
int opt_set_tcp_nodelay(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_tcp_nodelay(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getboolean(L, ps, IPPROTO_TCP, TCP_NODELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
#ifdef TCP_KEEPIDLE
|
||||||
|
|
||||||
|
int opt_get_tcp_keepidle(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getint(L, ps, IPPROTO_TCP, TCP_KEEPIDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_set_tcp_keepidle(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setint(L, ps, IPPROTO_TCP, TCP_KEEPIDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
#ifdef TCP_KEEPCNT
|
||||||
|
|
||||||
|
int opt_get_tcp_keepcnt(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getint(L, ps, IPPROTO_TCP, TCP_KEEPCNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_set_tcp_keepcnt(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setint(L, ps, IPPROTO_TCP, TCP_KEEPCNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
#ifdef TCP_KEEPINTVL
|
||||||
|
|
||||||
|
int opt_get_tcp_keepintvl(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getint(L, ps, IPPROTO_TCP, TCP_KEEPINTVL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_set_tcp_keepintvl(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setint(L, ps, IPPROTO_TCP, TCP_KEEPINTVL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_keepalive(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_keepalive(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_dontroute(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_dontroute(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getboolean(L, ps, SOL_SOCKET, SO_DONTROUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_broadcast(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_broadcast(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getboolean(L, ps, SOL_SOCKET, SO_BROADCAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_recv_buf_size(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setint(L, ps, SOL_SOCKET, SO_RCVBUF);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_recv_buf_size(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getint(L, ps, SOL_SOCKET, SO_RCVBUF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_get_send_buf_size(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getint(L, ps, SOL_SOCKET, SO_SNDBUF);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_set_send_buf_size(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setint(L, ps, SOL_SOCKET, SO_SNDBUF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifdef TCP_FASTOPEN
|
||||||
|
int opt_set_tcp_fastopen(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setint(L, ps, IPPROTO_TCP, TCP_FASTOPEN);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TCP_FASTOPEN_CONNECT
|
||||||
|
int opt_set_tcp_fastopen_connect(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setint(L, ps, IPPROTO_TCP, TCP_FASTOPEN_CONNECT);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifdef TCP_DEFER_ACCEPT
|
||||||
|
int opt_set_tcp_defer_accept(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setint(L, ps, IPPROTO_TCP, TCP_DEFER_ACCEPT);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_ip_multicast_loop(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_ip_multicast_loop(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_linger(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
struct linger li; /* obj, name, table */
|
||||||
|
if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE));
|
||||||
|
lua_pushstring(L, "on");
|
||||||
|
lua_gettable(L, 3);
|
||||||
|
if (!lua_isboolean(L, -1))
|
||||||
|
luaL_argerror(L, 3, "boolean 'on' field expected");
|
||||||
|
li.l_onoff = (u_short) lua_toboolean(L, -1);
|
||||||
|
lua_pushstring(L, "timeout");
|
||||||
|
lua_gettable(L, 3);
|
||||||
|
if (!lua_isnumber(L, -1))
|
||||||
|
luaL_argerror(L, 3, "number 'timeout' field expected");
|
||||||
|
li.l_linger = (u_short) lua_tonumber(L, -1);
|
||||||
|
return opt_set(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_linger(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
struct linger li; /* obj, name */
|
||||||
|
int len = sizeof(li);
|
||||||
|
int err = opt_get(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, &len);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
lua_newtable(L);
|
||||||
|
lua_pushboolean(L, li.l_onoff);
|
||||||
|
lua_setfield(L, -2, "on");
|
||||||
|
lua_pushinteger(L, li.l_linger);
|
||||||
|
lua_setfield(L, -2, "timeout");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setint(L, ps, IPPROTO_IP, IP_MULTICAST_TTL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_ip_multicast_if(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
const char *address = luaL_checkstring(L, 3); /* obj, name, ip */
|
||||||
|
struct in_addr val;
|
||||||
|
val.s_addr = htonl(INADDR_ANY);
|
||||||
|
if (strcmp(address, "*") && !inet_aton(address, &val))
|
||||||
|
luaL_argerror(L, 3, "ip expected");
|
||||||
|
return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_IF,
|
||||||
|
(char *) &val, sizeof(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_get_ip_multicast_if(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
struct in_addr val;
|
||||||
|
socklen_t len = sizeof(val);
|
||||||
|
if (getsockopt(*ps, IPPROTO_IP, IP_MULTICAST_IF, (char *) &val, &len) < 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, "getsockopt failed");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushstring(L, inet_ntoa(val));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_ip_add_membership(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setmembership(L, ps, IPPROTO_IP, IP_ADD_MEMBERSHIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_set_ip_drop_membersip(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setmembership(L, ps, IPPROTO_IP, IP_DROP_MEMBERSHIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_set_ip6_add_membership(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_get_ip6_v6only(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
int opt_set_ip6_v6only(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------*/
|
||||||
|
int opt_get_error(lua_State *L, p_socket ps)
|
||||||
|
{
|
||||||
|
int val = 0;
|
||||||
|
socklen_t len = sizeof(val);
|
||||||
|
if (getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *) &val, &len) < 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, "getsockopt failed");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushstring(L, socket_strerror(val));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Auxiliar functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
static int opt_setmembership(lua_State *L, p_socket ps, int level, int name)
|
||||||
|
{
|
||||||
|
struct ip_mreq val; /* obj, name, table */
|
||||||
|
if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE));
|
||||||
|
lua_pushstring(L, "multiaddr");
|
||||||
|
lua_gettable(L, 3);
|
||||||
|
if (!lua_isstring(L, -1))
|
||||||
|
luaL_argerror(L, 3, "string 'multiaddr' field expected");
|
||||||
|
if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr))
|
||||||
|
luaL_argerror(L, 3, "invalid 'multiaddr' ip address");
|
||||||
|
lua_pushstring(L, "interface");
|
||||||
|
lua_gettable(L, 3);
|
||||||
|
if (!lua_isstring(L, -1))
|
||||||
|
luaL_argerror(L, 3, "string 'interface' field expected");
|
||||||
|
val.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||||
|
if (strcmp(lua_tostring(L, -1), "*") &&
|
||||||
|
!inet_aton(lua_tostring(L, -1), &val.imr_interface))
|
||||||
|
luaL_argerror(L, 3, "invalid 'interface' ip address");
|
||||||
|
return opt_set(L, ps, level, name, (char *) &val, sizeof(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name)
|
||||||
|
{
|
||||||
|
struct ipv6_mreq val; /* obj, opt-name, table */
|
||||||
|
memset(&val, 0, sizeof(val));
|
||||||
|
if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE));
|
||||||
|
lua_pushstring(L, "multiaddr");
|
||||||
|
lua_gettable(L, 3);
|
||||||
|
if (!lua_isstring(L, -1))
|
||||||
|
luaL_argerror(L, 3, "string 'multiaddr' field expected");
|
||||||
|
if (!inet_pton(AF_INET6, lua_tostring(L, -1), &val.ipv6mr_multiaddr))
|
||||||
|
luaL_argerror(L, 3, "invalid 'multiaddr' ip address");
|
||||||
|
lua_pushstring(L, "interface");
|
||||||
|
lua_gettable(L, 3);
|
||||||
|
/* By default we listen to interface on default route
|
||||||
|
* (sigh). However, interface= can override it. We should
|
||||||
|
* support either number, or name for it. Waiting for
|
||||||
|
* windows port of if_nametoindex */
|
||||||
|
if (!lua_isnil(L, -1)) {
|
||||||
|
if (lua_isnumber(L, -1)) {
|
||||||
|
val.ipv6mr_interface = (unsigned int) lua_tonumber(L, -1);
|
||||||
|
} else
|
||||||
|
luaL_argerror(L, -1, "number 'interface' field expected");
|
||||||
|
}
|
||||||
|
return opt_set(L, ps, level, name, (char *) &val, sizeof(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len)
|
||||||
|
{
|
||||||
|
socklen_t socklen = *len;
|
||||||
|
if (getsockopt(*ps, level, name, (char *) val, &socklen) < 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, "getsockopt failed");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
*len = socklen;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len)
|
||||||
|
{
|
||||||
|
if (setsockopt(*ps, level, name, (char *) val, len) < 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, "setsockopt failed");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt_getboolean(lua_State *L, p_socket ps, int level, int name)
|
||||||
|
{
|
||||||
|
int val = 0;
|
||||||
|
int len = sizeof(val);
|
||||||
|
int err = opt_get(L, ps, level, name, (char *) &val, &len);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
lua_pushboolean(L, val);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt_setboolean(lua_State *L, p_socket ps, int level, int name)
|
||||||
|
{
|
||||||
|
int val = auxiliar_checkboolean(L, 3); /* obj, name, bool */
|
||||||
|
return opt_set(L, ps, level, name, (char *) &val, sizeof(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt_getint(lua_State *L, p_socket ps, int level, int name)
|
||||||
|
{
|
||||||
|
int val = 0;
|
||||||
|
int len = sizeof(val);
|
||||||
|
int err = opt_get(L, ps, level, name, (char *) &val, &len);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
lua_pushnumber(L, val);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opt_setint(lua_State *L, p_socket ps, int level, int name)
|
||||||
|
{
|
||||||
|
int val = (int) lua_tonumber(L, 3); /* obj, name, int */
|
||||||
|
return opt_set(L, ps, level, name, (char *) &val, sizeof(val));
|
||||||
|
}
|
||||||
113
external/socket/src/options.h
vendored
Normal file
113
external/socket/src/options.h
vendored
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
#ifndef OPTIONS_H
|
||||||
|
#define OPTIONS_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Common option interface
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* This module provides a common interface to socket options, used mainly by
|
||||||
|
* modules UDP and TCP.
|
||||||
|
\*=========================================================================*/
|
||||||
|
|
||||||
|
#include "luasocket.h"
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
|
/* option registry */
|
||||||
|
typedef struct t_opt {
|
||||||
|
const char *name;
|
||||||
|
int (*func)(lua_State *L, p_socket ps);
|
||||||
|
} t_opt;
|
||||||
|
typedef t_opt *p_opt;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps);
|
||||||
|
int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_reuseaddr(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_reuseaddr(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_reuseport(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_reuseport(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_tcp_nodelay(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_tcp_nodelay(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
#ifdef TCP_KEEPIDLE
|
||||||
|
int opt_set_tcp_keepidle(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_tcp_keepidle(lua_State *L, p_socket ps);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TCP_KEEPCNT
|
||||||
|
int opt_set_tcp_keepcnt(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_tcp_keepcnt(lua_State *L, p_socket ps);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TCP_KEEPINTVL
|
||||||
|
int opt_set_tcp_keepintvl(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_tcp_keepintvl(lua_State *L, p_socket ps);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TCP_DEFER_ACCEPT
|
||||||
|
int opt_set_tcp_defer_accept(lua_State *L, p_socket ps);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int opt_set_keepalive(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_keepalive(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_dontroute(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_dontroute(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_broadcast(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_broadcast(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_recv_buf_size(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_recv_buf_size(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_send_buf_size(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_send_buf_size(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
#ifdef TCP_FASTOPEN
|
||||||
|
int opt_set_tcp_fastopen(lua_State *L, p_socket ps);
|
||||||
|
#endif
|
||||||
|
#ifdef TCP_FASTOPEN_CONNECT
|
||||||
|
int opt_set_tcp_fastopen_connect(lua_State *L, p_socket ps);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_ip6_multicast_hops(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_ip_multicast_loop(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_ip_multicast_loop(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_ip6_multicast_loop(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_linger(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_linger(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_ip_multicast_if(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_ip_multicast_if(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_ip_add_membership(lua_State *L, p_socket ps);
|
||||||
|
int opt_set_ip_drop_membersip(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_ip6_add_membership(lua_State *L, p_socket ps);
|
||||||
|
int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_set_ip6_v6only(lua_State *L, p_socket ps);
|
||||||
|
int opt_get_ip6_v6only(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
int opt_get_error(lua_State *L, p_socket ps);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
28
external/socket/src/pierror.h
vendored
Normal file
28
external/socket/src/pierror.h
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef PIERROR_H
|
||||||
|
#define PIERROR_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Error messages
|
||||||
|
* Defines platform independent error messages
|
||||||
|
\*=========================================================================*/
|
||||||
|
|
||||||
|
#define PIE_HOST_NOT_FOUND "host not found"
|
||||||
|
#define PIE_ADDRINUSE "address already in use"
|
||||||
|
#define PIE_ISCONN "already connected"
|
||||||
|
#define PIE_ACCESS "permission denied"
|
||||||
|
#define PIE_CONNREFUSED "connection refused"
|
||||||
|
#define PIE_CONNABORTED "closed"
|
||||||
|
#define PIE_CONNRESET "closed"
|
||||||
|
#define PIE_TIMEDOUT "timeout"
|
||||||
|
#define PIE_AGAIN "temporary failure in name resolution"
|
||||||
|
#define PIE_BADFLAGS "invalid value for ai_flags"
|
||||||
|
#define PIE_BADHINTS "invalid value for hints"
|
||||||
|
#define PIE_FAIL "non-recoverable failure in name resolution"
|
||||||
|
#define PIE_FAMILY "ai_family not supported"
|
||||||
|
#define PIE_MEMORY "memory allocation failure"
|
||||||
|
#define PIE_NONAME "host or service not provided, or not known"
|
||||||
|
#define PIE_OVERFLOW "argument buffer overflow"
|
||||||
|
#define PIE_PROTOCOL "resolved protocol is unknown"
|
||||||
|
#define PIE_SERVICE "service not supported for socket type"
|
||||||
|
#define PIE_SOCKTYPE "ai_socktype not supported"
|
||||||
|
|
||||||
|
#endif
|
||||||
214
external/socket/src/select.c
vendored
Normal file
214
external/socket/src/select.c
vendored
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Select implementation
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include "socket.h"
|
||||||
|
#include "timeout.h"
|
||||||
|
#include "select.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal function prototypes.
|
||||||
|
\*=========================================================================*/
|
||||||
|
static t_socket getfd(lua_State *L);
|
||||||
|
static int dirty(lua_State *L);
|
||||||
|
static void collect_fd(lua_State *L, int tab, int itab,
|
||||||
|
fd_set *set, t_socket *max_fd);
|
||||||
|
static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set);
|
||||||
|
static void return_fd(lua_State *L, fd_set *set, t_socket max_fd,
|
||||||
|
int itab, int tab, int start);
|
||||||
|
static void make_assoc(lua_State *L, int tab);
|
||||||
|
static int global_select(lua_State *L);
|
||||||
|
|
||||||
|
/* functions in library namespace */
|
||||||
|
static luaL_Reg func[] = {
|
||||||
|
{"select", global_select},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int select_open(lua_State *L) {
|
||||||
|
lua_pushstring(L, "_SETSIZE");
|
||||||
|
lua_pushinteger(L, FD_SETSIZE);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
lua_pushstring(L, "_SOCKETINVALID");
|
||||||
|
lua_pushinteger(L, SOCKET_INVALID);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
luaL_setfuncs(L, func, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Global Lua functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Waits for a set of sockets until a condition is met or timeout.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int global_select(lua_State *L) {
|
||||||
|
int rtab, wtab, itab, ret, ndirty;
|
||||||
|
t_socket max_fd = SOCKET_INVALID;
|
||||||
|
fd_set rset, wset;
|
||||||
|
t_timeout tm;
|
||||||
|
double t = luaL_optnumber(L, 3, -1);
|
||||||
|
FD_ZERO(&rset); FD_ZERO(&wset);
|
||||||
|
lua_settop(L, 3);
|
||||||
|
lua_newtable(L); itab = lua_gettop(L);
|
||||||
|
lua_newtable(L); rtab = lua_gettop(L);
|
||||||
|
lua_newtable(L); wtab = lua_gettop(L);
|
||||||
|
collect_fd(L, 1, itab, &rset, &max_fd);
|
||||||
|
collect_fd(L, 2, itab, &wset, &max_fd);
|
||||||
|
ndirty = check_dirty(L, 1, rtab, &rset);
|
||||||
|
t = ndirty > 0? 0.0: t;
|
||||||
|
timeout_init(&tm, t, -1);
|
||||||
|
timeout_markstart(&tm);
|
||||||
|
ret = socket_select(max_fd+1, &rset, &wset, NULL, &tm);
|
||||||
|
if (ret > 0 || ndirty > 0) {
|
||||||
|
return_fd(L, &rset, max_fd+1, itab, rtab, ndirty);
|
||||||
|
return_fd(L, &wset, max_fd+1, itab, wtab, 0);
|
||||||
|
make_assoc(L, rtab);
|
||||||
|
make_assoc(L, wtab);
|
||||||
|
return 2;
|
||||||
|
} else if (ret == 0) {
|
||||||
|
lua_pushstring(L, "timeout");
|
||||||
|
return 3;
|
||||||
|
} else {
|
||||||
|
luaL_error(L, "select failed");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
static t_socket getfd(lua_State *L) {
|
||||||
|
t_socket fd = SOCKET_INVALID;
|
||||||
|
lua_pushstring(L, "getfd");
|
||||||
|
lua_gettable(L, -2);
|
||||||
|
if (!lua_isnil(L, -1)) {
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
lua_call(L, 1, 1);
|
||||||
|
if (lua_isnumber(L, -1)) {
|
||||||
|
double numfd = lua_tonumber(L, -1);
|
||||||
|
fd = (numfd >= 0.0)? (t_socket) numfd: SOCKET_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dirty(lua_State *L) {
|
||||||
|
int is = 0;
|
||||||
|
lua_pushstring(L, "dirty");
|
||||||
|
lua_gettable(L, -2);
|
||||||
|
if (!lua_isnil(L, -1)) {
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
lua_call(L, 1, 1);
|
||||||
|
is = lua_toboolean(L, -1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void collect_fd(lua_State *L, int tab, int itab,
|
||||||
|
fd_set *set, t_socket *max_fd) {
|
||||||
|
int i = 1, n = 0;
|
||||||
|
/* nil is the same as an empty table */
|
||||||
|
if (lua_isnil(L, tab)) return;
|
||||||
|
/* otherwise we need it to be a table */
|
||||||
|
luaL_checktype(L, tab, LUA_TTABLE);
|
||||||
|
for ( ;; ) {
|
||||||
|
t_socket fd;
|
||||||
|
lua_pushnumber(L, i);
|
||||||
|
lua_gettable(L, tab);
|
||||||
|
if (lua_isnil(L, -1)) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* getfd figures out if this is a socket */
|
||||||
|
fd = getfd(L);
|
||||||
|
if (fd != SOCKET_INVALID) {
|
||||||
|
/* make sure we don't overflow the fd_set */
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (n >= FD_SETSIZE)
|
||||||
|
luaL_argerror(L, tab, "too many sockets");
|
||||||
|
#else
|
||||||
|
if (fd >= FD_SETSIZE)
|
||||||
|
luaL_argerror(L, tab, "descriptor too large for set size");
|
||||||
|
#endif
|
||||||
|
FD_SET(fd, set);
|
||||||
|
n++;
|
||||||
|
/* keep track of the largest descriptor so far */
|
||||||
|
if (*max_fd == SOCKET_INVALID || *max_fd < fd)
|
||||||
|
*max_fd = fd;
|
||||||
|
/* make sure we can map back from descriptor to the object */
|
||||||
|
lua_pushnumber(L, (lua_Number) fd);
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
lua_settable(L, itab);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) {
|
||||||
|
int ndirty = 0, i = 1;
|
||||||
|
if (lua_isnil(L, tab))
|
||||||
|
return 0;
|
||||||
|
for ( ;; ) {
|
||||||
|
t_socket fd;
|
||||||
|
lua_pushnumber(L, i);
|
||||||
|
lua_gettable(L, tab);
|
||||||
|
if (lua_isnil(L, -1)) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fd = getfd(L);
|
||||||
|
if (fd != SOCKET_INVALID && dirty(L)) {
|
||||||
|
lua_pushnumber(L, ++ndirty);
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
lua_settable(L, dtab);
|
||||||
|
FD_CLR(fd, set);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return ndirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void return_fd(lua_State *L, fd_set *set, t_socket max_fd,
|
||||||
|
int itab, int tab, int start) {
|
||||||
|
t_socket fd;
|
||||||
|
for (fd = 0; fd < max_fd; fd++) {
|
||||||
|
if (FD_ISSET(fd, set)) {
|
||||||
|
lua_pushnumber(L, ++start);
|
||||||
|
lua_pushnumber(L, (lua_Number) fd);
|
||||||
|
lua_gettable(L, itab);
|
||||||
|
lua_settable(L, tab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void make_assoc(lua_State *L, int tab) {
|
||||||
|
int i = 1, atab;
|
||||||
|
lua_newtable(L); atab = lua_gettop(L);
|
||||||
|
for ( ;; ) {
|
||||||
|
lua_pushnumber(L, i);
|
||||||
|
lua_gettable(L, tab);
|
||||||
|
if (!lua_isnil(L, -1)) {
|
||||||
|
lua_pushnumber(L, i);
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
lua_settable(L, atab);
|
||||||
|
lua_pushnumber(L, i);
|
||||||
|
lua_settable(L, atab);
|
||||||
|
} else {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i = i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
external/socket/src/select.h
vendored
Normal file
23
external/socket/src/select.h
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef SELECT_H
|
||||||
|
#define SELECT_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Select implementation
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* Each object that can be passed to the select function has to export
|
||||||
|
* method getfd() which returns the descriptor to be passed to the
|
||||||
|
* underlying select function. Another method, dirty(), should return
|
||||||
|
* true if there is data ready for reading (required for buffered input).
|
||||||
|
\*=========================================================================*/
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int select_open(lua_State *L);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SELECT_H */
|
||||||
171
external/socket/src/serial.c
vendored
Normal file
171
external/socket/src/serial.c
vendored
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Serial stream
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include "auxiliar.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "unix.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reuses userdata definition from unix.h, since it is useful for all
|
||||||
|
stream-like objects.
|
||||||
|
|
||||||
|
If we stored the serial path for use in error messages or userdata
|
||||||
|
printing, we might need our own userdata definition.
|
||||||
|
|
||||||
|
Group usage is semi-inherited from unix.c, but unnecessary since we
|
||||||
|
have only one object type.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal function prototypes
|
||||||
|
\*=========================================================================*/
|
||||||
|
static int global_create(lua_State *L);
|
||||||
|
static int meth_send(lua_State *L);
|
||||||
|
static int meth_receive(lua_State *L);
|
||||||
|
static int meth_close(lua_State *L);
|
||||||
|
static int meth_settimeout(lua_State *L);
|
||||||
|
static int meth_getfd(lua_State *L);
|
||||||
|
static int meth_setfd(lua_State *L);
|
||||||
|
static int meth_dirty(lua_State *L);
|
||||||
|
static int meth_getstats(lua_State *L);
|
||||||
|
static int meth_setstats(lua_State *L);
|
||||||
|
|
||||||
|
/* serial object methods */
|
||||||
|
static luaL_Reg serial_methods[] = {
|
||||||
|
{"__gc", meth_close},
|
||||||
|
{"__tostring", auxiliar_tostring},
|
||||||
|
{"close", meth_close},
|
||||||
|
{"dirty", meth_dirty},
|
||||||
|
{"getfd", meth_getfd},
|
||||||
|
{"getstats", meth_getstats},
|
||||||
|
{"setstats", meth_setstats},
|
||||||
|
{"receive", meth_receive},
|
||||||
|
{"send", meth_send},
|
||||||
|
{"setfd", meth_setfd},
|
||||||
|
{"settimeout", meth_settimeout},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
LUASOCKET_API int luaopen_socket_serial(lua_State *L) {
|
||||||
|
/* create classes */
|
||||||
|
auxiliar_newclass(L, "serial{client}", serial_methods);
|
||||||
|
/* create class groups */
|
||||||
|
auxiliar_add2group(L, "serial{client}", "serial{any}");
|
||||||
|
lua_pushcfunction(L, global_create);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Lua methods
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call buffered IO methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_send(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1);
|
||||||
|
return buffer_meth_send(L, &un->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_receive(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1);
|
||||||
|
return buffer_meth_receive(L, &un->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_getstats(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1);
|
||||||
|
return buffer_meth_getstats(L, &un->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_setstats(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "serial{client}", 1);
|
||||||
|
return buffer_meth_setstats(L, &un->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Select support methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_getfd(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1);
|
||||||
|
lua_pushnumber(L, (int) un->sock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is very dangerous, but can be handy for those that are brave enough */
|
||||||
|
static int meth_setfd(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1);
|
||||||
|
un->sock = (t_socket) luaL_checknumber(L, 2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_dirty(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1);
|
||||||
|
lua_pushboolean(L, !buffer_isempty(&un->buf));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Closes socket used by object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_close(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1);
|
||||||
|
socket_destroy(&un->sock);
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call tm methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_settimeout(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1);
|
||||||
|
return timeout_meth_settimeout(L, &un->tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Library functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Creates a serial object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int global_create(lua_State *L) {
|
||||||
|
const char* path = luaL_checkstring(L, 1);
|
||||||
|
|
||||||
|
/* allocate unix object */
|
||||||
|
p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix));
|
||||||
|
|
||||||
|
/* open serial device */
|
||||||
|
t_socket sock = open(path, O_NOCTTY|O_RDWR);
|
||||||
|
|
||||||
|
/*printf("open %s on %d\n", path, sock);*/
|
||||||
|
|
||||||
|
if (sock < 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_strerror(errno));
|
||||||
|
lua_pushnumber(L, errno);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
/* set its type as client object */
|
||||||
|
auxiliar_setclass(L, "serial{client}", -1);
|
||||||
|
/* initialize remaining structure fields */
|
||||||
|
socket_setnonblocking(&sock);
|
||||||
|
un->sock = sock;
|
||||||
|
io_init(&un->io, (p_send) socket_write, (p_recv) socket_read,
|
||||||
|
(p_error) socket_ioerror, &un->sock);
|
||||||
|
timeout_init(&un->tm, -1, -1);
|
||||||
|
buffer_init(&un->buf, &un->io, &un->tm);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
75
external/socket/src/socket.h
vendored
Normal file
75
external/socket/src/socket.h
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
#ifndef SOCKET_H
|
||||||
|
#define SOCKET_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Socket compatibilization module
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* BSD Sockets and WinSock are similar, but there are a few irritating
|
||||||
|
* differences. Also, not all *nix platforms behave the same. This module
|
||||||
|
* (and the associated usocket.h and wsocket.h) factor these differences and
|
||||||
|
* creates a interface compatible with the io.h module.
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Platform specific compatibilization
|
||||||
|
\*=========================================================================*/
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "wsocket.h"
|
||||||
|
#define LUA_GAI_STRERROR gai_strerrorA
|
||||||
|
#else
|
||||||
|
#include "usocket.h"
|
||||||
|
#define LUA_GAI_STRERROR gai_strerror
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* The connect and accept functions accept a timeout and their
|
||||||
|
* implementations are somewhat complicated. We chose to move
|
||||||
|
* the timeout control into this module for these functions in
|
||||||
|
* order to simplify the modules that use them.
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "timeout.h"
|
||||||
|
|
||||||
|
/* convenient shorthand */
|
||||||
|
typedef struct sockaddr SA;
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Functions bellow implement a comfortable platform independent
|
||||||
|
* interface to sockets
|
||||||
|
\*=========================================================================*/
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int socket_waitfd(p_socket ps, int sw, p_timeout tm);
|
||||||
|
int socket_open(void);
|
||||||
|
int socket_close(void);
|
||||||
|
void socket_destroy(p_socket ps);
|
||||||
|
int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_timeout tm);
|
||||||
|
int socket_create(p_socket ps, int domain, int type, int protocol);
|
||||||
|
int socket_bind(p_socket ps, SA *addr, socklen_t addr_len);
|
||||||
|
int socket_listen(p_socket ps, int backlog);
|
||||||
|
void socket_shutdown(p_socket ps, int how);
|
||||||
|
int socket_connect(p_socket ps, SA *addr, socklen_t addr_len, p_timeout tm);
|
||||||
|
int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *addr_len, p_timeout tm);
|
||||||
|
int socket_send(p_socket ps, const char *data, size_t count, size_t *sent, p_timeout tm);
|
||||||
|
int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, SA *addr, socklen_t addr_len, p_timeout tm);
|
||||||
|
int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm);
|
||||||
|
int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, SA *addr, socklen_t *addr_len, p_timeout tm);
|
||||||
|
int socket_write(p_socket ps, const char *data, size_t count, size_t *sent, p_timeout tm);
|
||||||
|
int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm);
|
||||||
|
void socket_setblocking(p_socket ps);
|
||||||
|
void socket_setnonblocking(p_socket ps);
|
||||||
|
int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp);
|
||||||
|
int socket_gethostbyname(const char *addr, struct hostent **hp);
|
||||||
|
const char *socket_hoststrerror(int err);
|
||||||
|
const char *socket_strerror(int err);
|
||||||
|
const char *socket_ioerror(p_socket ps, int err);
|
||||||
|
const char *socket_gaistrerror(int err);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SOCKET_H */
|
||||||
480
external/socket/src/tcp.c
vendored
Normal file
480
external/socket/src/tcp.c
vendored
Normal file
|
|
@ -0,0 +1,480 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* TCP object
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include "auxiliar.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "inet.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "tcp.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal function prototypes
|
||||||
|
\*=========================================================================*/
|
||||||
|
static int global_create(lua_State *L);
|
||||||
|
static int global_create4(lua_State *L);
|
||||||
|
static int global_create6(lua_State *L);
|
||||||
|
static int global_connect(lua_State *L);
|
||||||
|
static int meth_connect(lua_State *L);
|
||||||
|
static int meth_listen(lua_State *L);
|
||||||
|
static int meth_getfamily(lua_State *L);
|
||||||
|
static int meth_bind(lua_State *L);
|
||||||
|
static int meth_send(lua_State *L);
|
||||||
|
static int meth_getstats(lua_State *L);
|
||||||
|
static int meth_setstats(lua_State *L);
|
||||||
|
static int meth_getsockname(lua_State *L);
|
||||||
|
static int meth_getpeername(lua_State *L);
|
||||||
|
static int meth_shutdown(lua_State *L);
|
||||||
|
static int meth_receive(lua_State *L);
|
||||||
|
static int meth_accept(lua_State *L);
|
||||||
|
static int meth_close(lua_State *L);
|
||||||
|
static int meth_getoption(lua_State *L);
|
||||||
|
static int meth_setoption(lua_State *L);
|
||||||
|
static int meth_gettimeout(lua_State *L);
|
||||||
|
static int meth_settimeout(lua_State *L);
|
||||||
|
static int meth_getfd(lua_State *L);
|
||||||
|
static int meth_setfd(lua_State *L);
|
||||||
|
static int meth_dirty(lua_State *L);
|
||||||
|
|
||||||
|
/* tcp object methods */
|
||||||
|
static luaL_Reg tcp_methods[] = {
|
||||||
|
{"__gc", meth_close},
|
||||||
|
{"__tostring", auxiliar_tostring},
|
||||||
|
{"accept", meth_accept},
|
||||||
|
{"bind", meth_bind},
|
||||||
|
{"close", meth_close},
|
||||||
|
{"connect", meth_connect},
|
||||||
|
{"dirty", meth_dirty},
|
||||||
|
{"getfamily", meth_getfamily},
|
||||||
|
{"getfd", meth_getfd},
|
||||||
|
{"getoption", meth_getoption},
|
||||||
|
{"getpeername", meth_getpeername},
|
||||||
|
{"getsockname", meth_getsockname},
|
||||||
|
{"getstats", meth_getstats},
|
||||||
|
{"setstats", meth_setstats},
|
||||||
|
{"listen", meth_listen},
|
||||||
|
{"receive", meth_receive},
|
||||||
|
{"send", meth_send},
|
||||||
|
{"setfd", meth_setfd},
|
||||||
|
{"setoption", meth_setoption},
|
||||||
|
{"setpeername", meth_connect},
|
||||||
|
{"setsockname", meth_bind},
|
||||||
|
{"settimeout", meth_settimeout},
|
||||||
|
{"gettimeout", meth_gettimeout},
|
||||||
|
{"shutdown", meth_shutdown},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* socket option handlers */
|
||||||
|
static t_opt optget[] = {
|
||||||
|
{"keepalive", opt_get_keepalive},
|
||||||
|
{"reuseaddr", opt_get_reuseaddr},
|
||||||
|
{"reuseport", opt_get_reuseport},
|
||||||
|
{"tcp-nodelay", opt_get_tcp_nodelay},
|
||||||
|
#ifdef TCP_KEEPIDLE
|
||||||
|
{"tcp-keepidle", opt_get_tcp_keepidle},
|
||||||
|
#endif
|
||||||
|
#ifdef TCP_KEEPCNT
|
||||||
|
{"tcp-keepcnt", opt_get_tcp_keepcnt},
|
||||||
|
#endif
|
||||||
|
#ifdef TCP_KEEPINTVL
|
||||||
|
{"tcp-keepintvl", opt_get_tcp_keepintvl},
|
||||||
|
#endif
|
||||||
|
{"linger", opt_get_linger},
|
||||||
|
{"error", opt_get_error},
|
||||||
|
{"recv-buffer-size", opt_get_recv_buf_size},
|
||||||
|
{"send-buffer-size", opt_get_send_buf_size},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static t_opt optset[] = {
|
||||||
|
{"keepalive", opt_set_keepalive},
|
||||||
|
{"reuseaddr", opt_set_reuseaddr},
|
||||||
|
{"reuseport", opt_set_reuseport},
|
||||||
|
{"tcp-nodelay", opt_set_tcp_nodelay},
|
||||||
|
#ifdef TCP_KEEPIDLE
|
||||||
|
{"tcp-keepidle", opt_set_tcp_keepidle},
|
||||||
|
#endif
|
||||||
|
#ifdef TCP_KEEPCNT
|
||||||
|
{"tcp-keepcnt", opt_set_tcp_keepcnt},
|
||||||
|
#endif
|
||||||
|
#ifdef TCP_KEEPINTVL
|
||||||
|
{"tcp-keepintvl", opt_set_tcp_keepintvl},
|
||||||
|
#endif
|
||||||
|
{"ipv6-v6only", opt_set_ip6_v6only},
|
||||||
|
{"linger", opt_set_linger},
|
||||||
|
{"recv-buffer-size", opt_set_recv_buf_size},
|
||||||
|
{"send-buffer-size", opt_set_send_buf_size},
|
||||||
|
#ifdef TCP_DEFER_ACCEPT
|
||||||
|
{"tcp-defer-accept", opt_set_tcp_defer_accept},
|
||||||
|
#endif
|
||||||
|
#ifdef TCP_FASTOPEN
|
||||||
|
{"tcp-fastopen", opt_set_tcp_fastopen},
|
||||||
|
#endif
|
||||||
|
#ifdef TCP_FASTOPEN_CONNECT
|
||||||
|
{"tcp-fastopen-connect", opt_set_tcp_fastopen_connect},
|
||||||
|
#endif
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* functions in library namespace */
|
||||||
|
static luaL_Reg func[] = {
|
||||||
|
{"tcp", global_create},
|
||||||
|
{"tcp4", global_create4},
|
||||||
|
{"tcp6", global_create6},
|
||||||
|
{"connect", global_connect},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int tcp_open(lua_State *L)
|
||||||
|
{
|
||||||
|
/* create classes */
|
||||||
|
auxiliar_newclass(L, "tcp{master}", tcp_methods);
|
||||||
|
auxiliar_newclass(L, "tcp{client}", tcp_methods);
|
||||||
|
auxiliar_newclass(L, "tcp{server}", tcp_methods);
|
||||||
|
/* create class groups */
|
||||||
|
auxiliar_add2group(L, "tcp{master}", "tcp{any}");
|
||||||
|
auxiliar_add2group(L, "tcp{client}", "tcp{any}");
|
||||||
|
auxiliar_add2group(L, "tcp{server}", "tcp{any}");
|
||||||
|
/* define library functions */
|
||||||
|
luaL_setfuncs(L, func, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Lua methods
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call buffered IO methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_send(lua_State *L) {
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1);
|
||||||
|
return buffer_meth_send(L, &tcp->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_receive(lua_State *L) {
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1);
|
||||||
|
return buffer_meth_receive(L, &tcp->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_getstats(lua_State *L) {
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1);
|
||||||
|
return buffer_meth_getstats(L, &tcp->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_setstats(lua_State *L) {
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1);
|
||||||
|
return buffer_meth_setstats(L, &tcp->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call option handler
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_getoption(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
|
||||||
|
return opt_meth_getoption(L, optget, &tcp->sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_setoption(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
|
||||||
|
return opt_meth_setoption(L, optset, &tcp->sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Select support methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_getfd(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
|
||||||
|
lua_pushnumber(L, (int) tcp->sock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is very dangerous, but can be handy for those that are brave enough */
|
||||||
|
static int meth_setfd(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
|
||||||
|
tcp->sock = (t_socket) luaL_checknumber(L, 2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_dirty(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
|
||||||
|
lua_pushboolean(L, !buffer_isempty(&tcp->buf));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Waits for and returns a client object attempting connection to the
|
||||||
|
* server object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_accept(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp server = (p_tcp) auxiliar_checkclass(L, "tcp{server}", 1);
|
||||||
|
p_timeout tm = timeout_markstart(&server->tm);
|
||||||
|
t_socket sock;
|
||||||
|
const char *err = inet_tryaccept(&server->sock, server->family, &sock, tm);
|
||||||
|
/* if successful, push client socket */
|
||||||
|
if (err == NULL) {
|
||||||
|
p_tcp clnt = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
|
||||||
|
auxiliar_setclass(L, "tcp{client}", -1);
|
||||||
|
/* initialize structure fields */
|
||||||
|
memset(clnt, 0, sizeof(t_tcp));
|
||||||
|
socket_setnonblocking(&sock);
|
||||||
|
clnt->sock = sock;
|
||||||
|
io_init(&clnt->io, (p_send) socket_send, (p_recv) socket_recv,
|
||||||
|
(p_error) socket_ioerror, &clnt->sock);
|
||||||
|
timeout_init(&clnt->tm, -1, -1);
|
||||||
|
buffer_init(&clnt->buf, &clnt->io, &clnt->tm);
|
||||||
|
clnt->family = server->family;
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Binds an object to an address
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_bind(lua_State *L) {
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1);
|
||||||
|
const char *address = luaL_checkstring(L, 2);
|
||||||
|
const char *port = luaL_checkstring(L, 3);
|
||||||
|
const char *err;
|
||||||
|
struct addrinfo bindhints;
|
||||||
|
memset(&bindhints, 0, sizeof(bindhints));
|
||||||
|
bindhints.ai_socktype = SOCK_STREAM;
|
||||||
|
bindhints.ai_family = tcp->family;
|
||||||
|
bindhints.ai_flags = AI_PASSIVE;
|
||||||
|
err = inet_trybind(&tcp->sock, &tcp->family, address, port, &bindhints);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Turns a master tcp object into a client object.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_connect(lua_State *L) {
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
|
||||||
|
const char *address = luaL_checkstring(L, 2);
|
||||||
|
const char *port = luaL_checkstring(L, 3);
|
||||||
|
struct addrinfo connecthints;
|
||||||
|
const char *err;
|
||||||
|
memset(&connecthints, 0, sizeof(connecthints));
|
||||||
|
connecthints.ai_socktype = SOCK_STREAM;
|
||||||
|
/* make sure we try to connect only to the same family */
|
||||||
|
connecthints.ai_family = tcp->family;
|
||||||
|
timeout_markstart(&tcp->tm);
|
||||||
|
err = inet_tryconnect(&tcp->sock, &tcp->family, address, port,
|
||||||
|
&tcp->tm, &connecthints);
|
||||||
|
/* have to set the class even if it failed due to non-blocking connects */
|
||||||
|
auxiliar_setclass(L, "tcp{client}", 1);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Closes socket used by object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_close(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
|
||||||
|
socket_destroy(&tcp->sock);
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Returns family as string
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_getfamily(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
|
||||||
|
if (tcp->family == AF_INET6) {
|
||||||
|
lua_pushliteral(L, "inet6");
|
||||||
|
return 1;
|
||||||
|
} else if (tcp->family == AF_INET) {
|
||||||
|
lua_pushliteral(L, "inet4");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
lua_pushliteral(L, "inet4");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Puts the sockt in listen mode
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_listen(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1);
|
||||||
|
int backlog = (int) luaL_optnumber(L, 2, 32);
|
||||||
|
int err = socket_listen(&tcp->sock, backlog);
|
||||||
|
if (err != IO_DONE) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_strerror(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* turn master object into a server object */
|
||||||
|
auxiliar_setclass(L, "tcp{server}", 1);
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Shuts the connection down partially
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_shutdown(lua_State *L)
|
||||||
|
{
|
||||||
|
/* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */
|
||||||
|
static const char* methods[] = { "receive", "send", "both", NULL };
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1);
|
||||||
|
int how = luaL_checkoption(L, 2, "both", methods);
|
||||||
|
socket_shutdown(&tcp->sock, how);
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call inet methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_getpeername(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
|
||||||
|
return inet_meth_getpeername(L, &tcp->sock, tcp->family);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_getsockname(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
|
||||||
|
return inet_meth_getsockname(L, &tcp->sock, tcp->family);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call tm methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_settimeout(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
|
||||||
|
return timeout_meth_settimeout(L, &tcp->tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_gettimeout(lua_State *L)
|
||||||
|
{
|
||||||
|
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
|
||||||
|
return timeout_meth_gettimeout(L, &tcp->tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Library functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Creates a master tcp object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int tcp_create(lua_State *L, int family) {
|
||||||
|
p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
|
||||||
|
memset(tcp, 0, sizeof(t_tcp));
|
||||||
|
/* set its type as master object */
|
||||||
|
auxiliar_setclass(L, "tcp{master}", -1);
|
||||||
|
/* if family is AF_UNSPEC, we leave the socket invalid and
|
||||||
|
* store AF_UNSPEC into family. This will allow it to later be
|
||||||
|
* replaced with an AF_INET6 or AF_INET socket upon first use. */
|
||||||
|
tcp->sock = SOCKET_INVALID;
|
||||||
|
tcp->family = family;
|
||||||
|
io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv,
|
||||||
|
(p_error) socket_ioerror, &tcp->sock);
|
||||||
|
timeout_init(&tcp->tm, -1, -1);
|
||||||
|
buffer_init(&tcp->buf, &tcp->io, &tcp->tm);
|
||||||
|
if (family != AF_UNSPEC) {
|
||||||
|
const char *err = inet_trycreate(&tcp->sock, family, SOCK_STREAM, 0);
|
||||||
|
if (err != NULL) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
socket_setnonblocking(&tcp->sock);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int global_create(lua_State *L) {
|
||||||
|
return tcp_create(L, AF_UNSPEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int global_create4(lua_State *L) {
|
||||||
|
return tcp_create(L, AF_INET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int global_create6(lua_State *L) {
|
||||||
|
return tcp_create(L, AF_INET6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int global_connect(lua_State *L) {
|
||||||
|
const char *remoteaddr = luaL_checkstring(L, 1);
|
||||||
|
const char *remoteserv = luaL_checkstring(L, 2);
|
||||||
|
const char *localaddr = luaL_optstring(L, 3, NULL);
|
||||||
|
const char *localserv = luaL_optstring(L, 4, "0");
|
||||||
|
int family = inet_optfamily(L, 5, "unspec");
|
||||||
|
p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
|
||||||
|
struct addrinfo bindhints, connecthints;
|
||||||
|
const char *err = NULL;
|
||||||
|
/* initialize tcp structure */
|
||||||
|
memset(tcp, 0, sizeof(t_tcp));
|
||||||
|
io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv,
|
||||||
|
(p_error) socket_ioerror, &tcp->sock);
|
||||||
|
timeout_init(&tcp->tm, -1, -1);
|
||||||
|
buffer_init(&tcp->buf, &tcp->io, &tcp->tm);
|
||||||
|
tcp->sock = SOCKET_INVALID;
|
||||||
|
tcp->family = AF_UNSPEC;
|
||||||
|
/* allow user to pick local address and port */
|
||||||
|
memset(&bindhints, 0, sizeof(bindhints));
|
||||||
|
bindhints.ai_socktype = SOCK_STREAM;
|
||||||
|
bindhints.ai_family = family;
|
||||||
|
bindhints.ai_flags = AI_PASSIVE;
|
||||||
|
if (localaddr) {
|
||||||
|
err = inet_trybind(&tcp->sock, &tcp->family, localaddr,
|
||||||
|
localserv, &bindhints);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* try to connect to remote address and port */
|
||||||
|
memset(&connecthints, 0, sizeof(connecthints));
|
||||||
|
connecthints.ai_socktype = SOCK_STREAM;
|
||||||
|
/* make sure we try to connect only to the same family */
|
||||||
|
connecthints.ai_family = tcp->family;
|
||||||
|
err = inet_tryconnect(&tcp->sock, &tcp->family, remoteaddr, remoteserv,
|
||||||
|
&tcp->tm, &connecthints);
|
||||||
|
if (err) {
|
||||||
|
socket_destroy(&tcp->sock);
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
auxiliar_setclass(L, "tcp{client}", -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
43
external/socket/src/tcp.h
vendored
Normal file
43
external/socket/src/tcp.h
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef TCP_H
|
||||||
|
#define TCP_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* TCP object
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* The tcp.h module is basicly a glue that puts together modules buffer.h,
|
||||||
|
* timeout.h socket.h and inet.h to provide the LuaSocket TCP (AF_INET,
|
||||||
|
* SOCK_STREAM) support.
|
||||||
|
*
|
||||||
|
* Three classes are defined: master, client and server. The master class is
|
||||||
|
* a newly created tcp object, that has not been bound or connected. Server
|
||||||
|
* objects are tcp objects bound to some local address. Client objects are
|
||||||
|
* tcp objects either connected to some address or returned by the accept
|
||||||
|
* method of a server object.
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "timeout.h"
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
|
typedef struct t_tcp_ {
|
||||||
|
t_socket sock;
|
||||||
|
t_io io;
|
||||||
|
t_buffer buf;
|
||||||
|
t_timeout tm;
|
||||||
|
int family;
|
||||||
|
} t_tcp;
|
||||||
|
|
||||||
|
typedef t_tcp *p_tcp;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int tcp_open(lua_State *L);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* TCP_H */
|
||||||
226
external/socket/src/timeout.c
vendored
Normal file
226
external/socket/src/timeout.c
vendored
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Timeout management functions
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include "auxiliar.h"
|
||||||
|
#include "timeout.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <float.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* min and max macros */
|
||||||
|
#ifndef MIN
|
||||||
|
#define MIN(x, y) ((x) < (y) ? x : y)
|
||||||
|
#endif
|
||||||
|
#ifndef MAX
|
||||||
|
#define MAX(x, y) ((x) > (y) ? x : y)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal function prototypes
|
||||||
|
\*=========================================================================*/
|
||||||
|
static int timeout_lua_gettime(lua_State *L);
|
||||||
|
static int timeout_lua_sleep(lua_State *L);
|
||||||
|
|
||||||
|
static luaL_Reg func[] = {
|
||||||
|
{ "gettime", timeout_lua_gettime },
|
||||||
|
{ "sleep", timeout_lua_sleep },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Exported functions.
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initialize structure
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void timeout_init(p_timeout tm, double block, double total) {
|
||||||
|
tm->block = block;
|
||||||
|
tm->total = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Determines how much time we have left for the next system call,
|
||||||
|
* if the previous call was successful
|
||||||
|
* Input
|
||||||
|
* tm: timeout control structure
|
||||||
|
* Returns
|
||||||
|
* the number of ms left or -1 if there is no time limit
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
double timeout_get(p_timeout tm) {
|
||||||
|
if (tm->block < 0.0 && tm->total < 0.0) {
|
||||||
|
return -1;
|
||||||
|
} else if (tm->block < 0.0) {
|
||||||
|
double t = tm->total - timeout_gettime() + tm->start;
|
||||||
|
return MAX(t, 0.0);
|
||||||
|
} else if (tm->total < 0.0) {
|
||||||
|
return tm->block;
|
||||||
|
} else {
|
||||||
|
double t = tm->total - timeout_gettime() + tm->start;
|
||||||
|
return MIN(tm->block, MAX(t, 0.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Returns time since start of operation
|
||||||
|
* Input
|
||||||
|
* tm: timeout control structure
|
||||||
|
* Returns
|
||||||
|
* start field of structure
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
double timeout_getstart(p_timeout tm) {
|
||||||
|
return tm->start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Determines how much time we have left for the next system call,
|
||||||
|
* if the previous call was a failure
|
||||||
|
* Input
|
||||||
|
* tm: timeout control structure
|
||||||
|
* Returns
|
||||||
|
* the number of ms left or -1 if there is no time limit
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
double timeout_getretry(p_timeout tm) {
|
||||||
|
if (tm->block < 0.0 && tm->total < 0.0) {
|
||||||
|
return -1;
|
||||||
|
} else if (tm->block < 0.0) {
|
||||||
|
double t = tm->total - timeout_gettime() + tm->start;
|
||||||
|
return MAX(t, 0.0);
|
||||||
|
} else if (tm->total < 0.0) {
|
||||||
|
double t = tm->block - timeout_gettime() + tm->start;
|
||||||
|
return MAX(t, 0.0);
|
||||||
|
} else {
|
||||||
|
double t = tm->total - timeout_gettime() + tm->start;
|
||||||
|
return MIN(tm->block, MAX(t, 0.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Marks the operation start time in structure
|
||||||
|
* Input
|
||||||
|
* tm: timeout control structure
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
p_timeout timeout_markstart(p_timeout tm) {
|
||||||
|
tm->start = timeout_gettime();
|
||||||
|
return tm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Gets time in s, relative to January 1, 1970 (UTC)
|
||||||
|
* Returns
|
||||||
|
* time in s.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
#ifdef _WIN32
|
||||||
|
double timeout_gettime(void) {
|
||||||
|
FILETIME ft;
|
||||||
|
double t;
|
||||||
|
GetSystemTimeAsFileTime(&ft);
|
||||||
|
/* Windows file time (time since January 1, 1601 (UTC)) */
|
||||||
|
t = ft.dwLowDateTime/1.0e7 + ft.dwHighDateTime*(4294967296.0/1.0e7);
|
||||||
|
/* convert to Unix Epoch time (time since January 1, 1970 (UTC)) */
|
||||||
|
return (t - 11644473600.0);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
double timeout_gettime(void) {
|
||||||
|
struct timeval v;
|
||||||
|
gettimeofday(&v, (struct timezone *) NULL);
|
||||||
|
/* Unix Epoch time (time since January 1, 1970 (UTC)) */
|
||||||
|
return v.tv_sec + v.tv_usec/1.0e6;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int timeout_open(lua_State *L) {
|
||||||
|
luaL_setfuncs(L, func, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Sets timeout values for IO operations
|
||||||
|
* Lua Input: base, time [, mode]
|
||||||
|
* time: time out value in seconds
|
||||||
|
* mode: "b" for block timeout, "t" for total timeout. (default: b)
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int timeout_meth_settimeout(lua_State *L, p_timeout tm) {
|
||||||
|
double t = luaL_optnumber(L, 2, -1);
|
||||||
|
const char *mode = luaL_optstring(L, 3, "b");
|
||||||
|
switch (*mode) {
|
||||||
|
case 'b':
|
||||||
|
tm->block = t;
|
||||||
|
break;
|
||||||
|
case 'r': case 't':
|
||||||
|
tm->total = t;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
luaL_argcheck(L, 0, 3, "invalid timeout mode");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Gets timeout values for IO operations
|
||||||
|
* Lua Output: block, total
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int timeout_meth_gettimeout(lua_State *L, p_timeout tm) {
|
||||||
|
lua_pushnumber(L, tm->block);
|
||||||
|
lua_pushnumber(L, tm->total);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Test support functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Returns the time the system has been up, in secconds.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int timeout_lua_gettime(lua_State *L)
|
||||||
|
{
|
||||||
|
lua_pushnumber(L, timeout_gettime());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Sleep for n seconds.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
#ifdef _WIN32
|
||||||
|
int timeout_lua_sleep(lua_State *L)
|
||||||
|
{
|
||||||
|
double n = luaL_checknumber(L, 1);
|
||||||
|
if (n < 0.0) n = 0.0;
|
||||||
|
if (n < DBL_MAX/1000.0) n *= 1000.0;
|
||||||
|
if (n > INT_MAX) n = INT_MAX;
|
||||||
|
Sleep((int)n);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int timeout_lua_sleep(lua_State *L)
|
||||||
|
{
|
||||||
|
double n = luaL_checknumber(L, 1);
|
||||||
|
struct timespec t, r;
|
||||||
|
if (n < 0.0) n = 0.0;
|
||||||
|
if (n > INT_MAX) n = INT_MAX;
|
||||||
|
t.tv_sec = (int) n;
|
||||||
|
n -= t.tv_sec;
|
||||||
|
t.tv_nsec = (int) (n * 1000000000);
|
||||||
|
if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999;
|
||||||
|
while (nanosleep(&t, &r) != 0) {
|
||||||
|
t.tv_sec = r.tv_sec;
|
||||||
|
t.tv_nsec = r.tv_nsec;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
40
external/socket/src/timeout.h
vendored
Normal file
40
external/socket/src/timeout.h
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef TIMEOUT_H
|
||||||
|
#define TIMEOUT_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Timeout management functions
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
/* timeout control structure */
|
||||||
|
typedef struct t_timeout_ {
|
||||||
|
double block; /* maximum time for blocking calls */
|
||||||
|
double total; /* total number of miliseconds for operation */
|
||||||
|
double start; /* time of start of operation */
|
||||||
|
} t_timeout;
|
||||||
|
typedef t_timeout *p_timeout;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void timeout_init(p_timeout tm, double block, double total);
|
||||||
|
double timeout_get(p_timeout tm);
|
||||||
|
double timeout_getstart(p_timeout tm);
|
||||||
|
double timeout_getretry(p_timeout tm);
|
||||||
|
p_timeout timeout_markstart(p_timeout tm);
|
||||||
|
|
||||||
|
double timeout_gettime(void);
|
||||||
|
|
||||||
|
int timeout_open(lua_State *L);
|
||||||
|
|
||||||
|
int timeout_meth_settimeout(lua_State *L, p_timeout tm);
|
||||||
|
int timeout_meth_gettimeout(lua_State *L, p_timeout tm);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define timeout_iszero(tm) ((tm)->block == 0.0)
|
||||||
|
|
||||||
|
#endif /* TIMEOUT_H */
|
||||||
488
external/socket/src/udp.c
vendored
Normal file
488
external/socket/src/udp.c
vendored
Normal file
|
|
@ -0,0 +1,488 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* UDP object
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include "auxiliar.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "inet.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "udp.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* min and max macros */
|
||||||
|
#ifndef MIN
|
||||||
|
#define MIN(x, y) ((x) < (y) ? x : y)
|
||||||
|
#endif
|
||||||
|
#ifndef MAX
|
||||||
|
#define MAX(x, y) ((x) > (y) ? x : y)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal function prototypes
|
||||||
|
\*=========================================================================*/
|
||||||
|
static int global_create(lua_State *L);
|
||||||
|
static int global_create4(lua_State *L);
|
||||||
|
static int global_create6(lua_State *L);
|
||||||
|
static int meth_send(lua_State *L);
|
||||||
|
static int meth_sendto(lua_State *L);
|
||||||
|
static int meth_receive(lua_State *L);
|
||||||
|
static int meth_receivefrom(lua_State *L);
|
||||||
|
static int meth_getfamily(lua_State *L);
|
||||||
|
static int meth_getsockname(lua_State *L);
|
||||||
|
static int meth_getpeername(lua_State *L);
|
||||||
|
static int meth_gettimeout(lua_State *L);
|
||||||
|
static int meth_setsockname(lua_State *L);
|
||||||
|
static int meth_setpeername(lua_State *L);
|
||||||
|
static int meth_close(lua_State *L);
|
||||||
|
static int meth_setoption(lua_State *L);
|
||||||
|
static int meth_getoption(lua_State *L);
|
||||||
|
static int meth_settimeout(lua_State *L);
|
||||||
|
static int meth_getfd(lua_State *L);
|
||||||
|
static int meth_setfd(lua_State *L);
|
||||||
|
static int meth_dirty(lua_State *L);
|
||||||
|
|
||||||
|
/* udp object methods */
|
||||||
|
static luaL_Reg udp_methods[] = {
|
||||||
|
{"__gc", meth_close},
|
||||||
|
{"__tostring", auxiliar_tostring},
|
||||||
|
{"close", meth_close},
|
||||||
|
{"dirty", meth_dirty},
|
||||||
|
{"getfamily", meth_getfamily},
|
||||||
|
{"getfd", meth_getfd},
|
||||||
|
{"getpeername", meth_getpeername},
|
||||||
|
{"getsockname", meth_getsockname},
|
||||||
|
{"receive", meth_receive},
|
||||||
|
{"receivefrom", meth_receivefrom},
|
||||||
|
{"send", meth_send},
|
||||||
|
{"sendto", meth_sendto},
|
||||||
|
{"setfd", meth_setfd},
|
||||||
|
{"setoption", meth_setoption},
|
||||||
|
{"getoption", meth_getoption},
|
||||||
|
{"setpeername", meth_setpeername},
|
||||||
|
{"setsockname", meth_setsockname},
|
||||||
|
{"settimeout", meth_settimeout},
|
||||||
|
{"gettimeout", meth_gettimeout},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* socket options for setoption */
|
||||||
|
static t_opt optset[] = {
|
||||||
|
{"dontroute", opt_set_dontroute},
|
||||||
|
{"broadcast", opt_set_broadcast},
|
||||||
|
{"reuseaddr", opt_set_reuseaddr},
|
||||||
|
{"reuseport", opt_set_reuseport},
|
||||||
|
{"ip-multicast-if", opt_set_ip_multicast_if},
|
||||||
|
{"ip-multicast-ttl", opt_set_ip_multicast_ttl},
|
||||||
|
{"ip-multicast-loop", opt_set_ip_multicast_loop},
|
||||||
|
{"ip-add-membership", opt_set_ip_add_membership},
|
||||||
|
{"ip-drop-membership", opt_set_ip_drop_membersip},
|
||||||
|
{"ipv6-unicast-hops", opt_set_ip6_unicast_hops},
|
||||||
|
{"ipv6-multicast-hops", opt_set_ip6_unicast_hops},
|
||||||
|
{"ipv6-multicast-loop", opt_set_ip6_multicast_loop},
|
||||||
|
{"ipv6-add-membership", opt_set_ip6_add_membership},
|
||||||
|
{"ipv6-drop-membership", opt_set_ip6_drop_membersip},
|
||||||
|
{"ipv6-v6only", opt_set_ip6_v6only},
|
||||||
|
{"recv-buffer-size", opt_set_recv_buf_size},
|
||||||
|
{"send-buffer-size", opt_set_send_buf_size},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* socket options for getoption */
|
||||||
|
static t_opt optget[] = {
|
||||||
|
{"dontroute", opt_get_dontroute},
|
||||||
|
{"broadcast", opt_get_broadcast},
|
||||||
|
{"reuseaddr", opt_get_reuseaddr},
|
||||||
|
{"reuseport", opt_get_reuseport},
|
||||||
|
{"ip-multicast-if", opt_get_ip_multicast_if},
|
||||||
|
{"ip-multicast-loop", opt_get_ip_multicast_loop},
|
||||||
|
{"error", opt_get_error},
|
||||||
|
{"ipv6-unicast-hops", opt_get_ip6_unicast_hops},
|
||||||
|
{"ipv6-multicast-hops", opt_get_ip6_unicast_hops},
|
||||||
|
{"ipv6-multicast-loop", opt_get_ip6_multicast_loop},
|
||||||
|
{"ipv6-v6only", opt_get_ip6_v6only},
|
||||||
|
{"recv-buffer-size", opt_get_recv_buf_size},
|
||||||
|
{"send-buffer-size", opt_get_send_buf_size},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* functions in library namespace */
|
||||||
|
static luaL_Reg func[] = {
|
||||||
|
{"udp", global_create},
|
||||||
|
{"udp4", global_create4},
|
||||||
|
{"udp6", global_create6},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int udp_open(lua_State *L) {
|
||||||
|
/* create classes */
|
||||||
|
auxiliar_newclass(L, "udp{connected}", udp_methods);
|
||||||
|
auxiliar_newclass(L, "udp{unconnected}", udp_methods);
|
||||||
|
/* create class groups */
|
||||||
|
auxiliar_add2group(L, "udp{connected}", "udp{any}");
|
||||||
|
auxiliar_add2group(L, "udp{unconnected}", "udp{any}");
|
||||||
|
auxiliar_add2group(L, "udp{connected}", "select{able}");
|
||||||
|
auxiliar_add2group(L, "udp{unconnected}", "select{able}");
|
||||||
|
/* define library functions */
|
||||||
|
luaL_setfuncs(L, func, 0);
|
||||||
|
/* export default UDP size */
|
||||||
|
lua_pushliteral(L, "_DATAGRAMSIZE");
|
||||||
|
lua_pushinteger(L, UDP_DATAGRAMSIZE);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Lua methods
|
||||||
|
\*=========================================================================*/
|
||||||
|
static const char *udp_strerror(int err) {
|
||||||
|
/* a 'closed' error on an unconnected means the target address was not
|
||||||
|
* accepted by the transport layer */
|
||||||
|
if (err == IO_CLOSED) return "refused";
|
||||||
|
else return socket_strerror(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Send data through connected udp socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_send(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{connected}", 1);
|
||||||
|
p_timeout tm = &udp->tm;
|
||||||
|
size_t count, sent = 0;
|
||||||
|
int err;
|
||||||
|
const char *data = luaL_checklstring(L, 2, &count);
|
||||||
|
timeout_markstart(tm);
|
||||||
|
err = socket_send(&udp->sock, data, count, &sent, tm);
|
||||||
|
if (err != IO_DONE) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, udp_strerror(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, (lua_Number) sent);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Send data through unconnected udp socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_sendto(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1);
|
||||||
|
size_t count, sent = 0;
|
||||||
|
const char *data = luaL_checklstring(L, 2, &count);
|
||||||
|
const char *ip = luaL_checkstring(L, 3);
|
||||||
|
const char *port = luaL_checkstring(L, 4);
|
||||||
|
p_timeout tm = &udp->tm;
|
||||||
|
int err;
|
||||||
|
struct addrinfo aihint;
|
||||||
|
struct addrinfo *ai;
|
||||||
|
memset(&aihint, 0, sizeof(aihint));
|
||||||
|
aihint.ai_family = udp->family;
|
||||||
|
aihint.ai_socktype = SOCK_DGRAM;
|
||||||
|
aihint.ai_flags = AI_NUMERICHOST;
|
||||||
|
#ifdef AI_NUMERICSERV
|
||||||
|
aihint.ai_flags |= AI_NUMERICSERV;
|
||||||
|
#endif
|
||||||
|
err = getaddrinfo(ip, port, &aihint, &ai);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, LUA_GAI_STRERROR(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create socket if on first sendto if AF_UNSPEC was set */
|
||||||
|
if (udp->family == AF_UNSPEC && udp->sock == SOCKET_INVALID) {
|
||||||
|
struct addrinfo *ap;
|
||||||
|
const char *errstr = NULL;
|
||||||
|
for (ap = ai; ap != NULL; ap = ap->ai_next) {
|
||||||
|
errstr = inet_trycreate(&udp->sock, ap->ai_family, SOCK_DGRAM, 0);
|
||||||
|
if (errstr == NULL) {
|
||||||
|
socket_setnonblocking(&udp->sock);
|
||||||
|
udp->family = ap->ai_family;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errstr != NULL) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, errstr);
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout_markstart(tm);
|
||||||
|
err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr,
|
||||||
|
(socklen_t) ai->ai_addrlen, tm);
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
if (err != IO_DONE) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, udp_strerror(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, (lua_Number) sent);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Receives data from a UDP socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_receive(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
|
char buf[UDP_DATAGRAMSIZE];
|
||||||
|
size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf));
|
||||||
|
char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf;
|
||||||
|
int err;
|
||||||
|
p_timeout tm = &udp->tm;
|
||||||
|
timeout_markstart(tm);
|
||||||
|
if (!dgram) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushliteral(L, "out of memory");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
err = socket_recv(&udp->sock, dgram, wanted, &got, tm);
|
||||||
|
/* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */
|
||||||
|
if (err != IO_DONE && err != IO_CLOSED) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, udp_strerror(err));
|
||||||
|
if (wanted > sizeof(buf)) free(dgram);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushlstring(L, dgram, got);
|
||||||
|
if (wanted > sizeof(buf)) free(dgram);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Receives data and sender from a UDP socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_receivefrom(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1);
|
||||||
|
char buf[UDP_DATAGRAMSIZE];
|
||||||
|
size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf));
|
||||||
|
char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf;
|
||||||
|
struct sockaddr_storage addr;
|
||||||
|
socklen_t addr_len = sizeof(addr);
|
||||||
|
char addrstr[INET6_ADDRSTRLEN];
|
||||||
|
char portstr[6];
|
||||||
|
int err;
|
||||||
|
p_timeout tm = &udp->tm;
|
||||||
|
timeout_markstart(tm);
|
||||||
|
if (!dgram) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushliteral(L, "out of memory");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
err = socket_recvfrom(&udp->sock, dgram, wanted, &got, (SA *) &addr,
|
||||||
|
&addr_len, tm);
|
||||||
|
/* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */
|
||||||
|
if (err != IO_DONE && err != IO_CLOSED) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, udp_strerror(err));
|
||||||
|
if (wanted > sizeof(buf)) free(dgram);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr,
|
||||||
|
INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, LUA_GAI_STRERROR(err));
|
||||||
|
if (wanted > sizeof(buf)) free(dgram);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushlstring(L, dgram, got);
|
||||||
|
lua_pushstring(L, addrstr);
|
||||||
|
lua_pushinteger(L, (int) strtol(portstr, (char **) NULL, 10));
|
||||||
|
if (wanted > sizeof(buf)) free(dgram);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Returns family as string
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_getfamily(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
|
if (udp->family == AF_INET6) {
|
||||||
|
lua_pushliteral(L, "inet6");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
lua_pushliteral(L, "inet4");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Select support methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_getfd(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
|
lua_pushnumber(L, (int) udp->sock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is very dangerous, but can be handy for those that are brave enough */
|
||||||
|
static int meth_setfd(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
|
udp->sock = (t_socket) luaL_checknumber(L, 2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_dirty(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
|
(void) udp;
|
||||||
|
lua_pushboolean(L, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call inet methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_getpeername(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{connected}", 1);
|
||||||
|
return inet_meth_getpeername(L, &udp->sock, udp->family);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_getsockname(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
|
return inet_meth_getsockname(L, &udp->sock, udp->family);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call option handler
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_setoption(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
|
return opt_meth_setoption(L, optset, &udp->sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call option handler
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_getoption(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
|
return opt_meth_getoption(L, optget, &udp->sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call tm methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_settimeout(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
|
return timeout_meth_settimeout(L, &udp->tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_gettimeout(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
|
return timeout_meth_gettimeout(L, &udp->tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Turns a master udp object into a client object.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_setpeername(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
|
p_timeout tm = &udp->tm;
|
||||||
|
const char *address = luaL_checkstring(L, 2);
|
||||||
|
int connecting = strcmp(address, "*");
|
||||||
|
const char *port = connecting? luaL_checkstring(L, 3): "0";
|
||||||
|
struct addrinfo connecthints;
|
||||||
|
const char *err;
|
||||||
|
memset(&connecthints, 0, sizeof(connecthints));
|
||||||
|
connecthints.ai_socktype = SOCK_DGRAM;
|
||||||
|
/* make sure we try to connect only to the same family */
|
||||||
|
connecthints.ai_family = udp->family;
|
||||||
|
if (connecting) {
|
||||||
|
err = inet_tryconnect(&udp->sock, &udp->family, address,
|
||||||
|
port, tm, &connecthints);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
auxiliar_setclass(L, "udp{connected}", 1);
|
||||||
|
} else {
|
||||||
|
/* we ignore possible errors because Mac OS X always
|
||||||
|
* returns EAFNOSUPPORT */
|
||||||
|
inet_trydisconnect(&udp->sock, udp->family, tm);
|
||||||
|
auxiliar_setclass(L, "udp{unconnected}", 1);
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Closes socket used by object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_close(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
|
||||||
|
socket_destroy(&udp->sock);
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Turns a master object into a server object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_setsockname(lua_State *L) {
|
||||||
|
p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1);
|
||||||
|
const char *address = luaL_checkstring(L, 2);
|
||||||
|
const char *port = luaL_checkstring(L, 3);
|
||||||
|
const char *err;
|
||||||
|
struct addrinfo bindhints;
|
||||||
|
memset(&bindhints, 0, sizeof(bindhints));
|
||||||
|
bindhints.ai_socktype = SOCK_DGRAM;
|
||||||
|
bindhints.ai_family = udp->family;
|
||||||
|
bindhints.ai_flags = AI_PASSIVE;
|
||||||
|
err = inet_trybind(&udp->sock, &udp->family, address, port, &bindhints);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Library functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Creates a master udp object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int udp_create(lua_State *L, int family) {
|
||||||
|
/* allocate udp object */
|
||||||
|
p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp));
|
||||||
|
auxiliar_setclass(L, "udp{unconnected}", -1);
|
||||||
|
/* if family is AF_UNSPEC, we leave the socket invalid and
|
||||||
|
* store AF_UNSPEC into family. This will allow it to later be
|
||||||
|
* replaced with an AF_INET6 or AF_INET socket upon first use. */
|
||||||
|
udp->sock = SOCKET_INVALID;
|
||||||
|
timeout_init(&udp->tm, -1, -1);
|
||||||
|
udp->family = family;
|
||||||
|
if (family != AF_UNSPEC) {
|
||||||
|
const char *err = inet_trycreate(&udp->sock, family, SOCK_DGRAM, 0);
|
||||||
|
if (err != NULL) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
socket_setnonblocking(&udp->sock);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int global_create(lua_State *L) {
|
||||||
|
return udp_create(L, AF_UNSPEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int global_create4(lua_State *L) {
|
||||||
|
return udp_create(L, AF_INET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int global_create6(lua_State *L) {
|
||||||
|
return udp_create(L, AF_INET6);
|
||||||
|
}
|
||||||
39
external/socket/src/udp.h
vendored
Normal file
39
external/socket/src/udp.h
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef UDP_H
|
||||||
|
#define UDP_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* UDP object
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* The udp.h module provides LuaSocket with support for UDP protocol
|
||||||
|
* (AF_INET, SOCK_DGRAM).
|
||||||
|
*
|
||||||
|
* Two classes are defined: connected and unconnected. UDP objects are
|
||||||
|
* originally unconnected. They can be "connected" to a given address
|
||||||
|
* with a call to the setpeername function. The same function can be used to
|
||||||
|
* break the connection.
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include "timeout.h"
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
|
#define UDP_DATAGRAMSIZE 8192
|
||||||
|
|
||||||
|
typedef struct t_udp_ {
|
||||||
|
t_socket sock;
|
||||||
|
t_timeout tm;
|
||||||
|
int family;
|
||||||
|
} t_udp;
|
||||||
|
typedef t_udp *p_udp;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int udp_open(lua_State *L);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* UDP_H */
|
||||||
69
external/socket/src/unix.c
vendored
Normal file
69
external/socket/src/unix.c
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Unix domain socket
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include "unixstream.h"
|
||||||
|
#include "unixdgram.h"
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Modules and functions
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static const luaL_Reg mod[] = {
|
||||||
|
{"stream", unixstream_open},
|
||||||
|
{"dgram", unixdgram_open},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void add_alias(lua_State *L, int index, const char *name, const char *target)
|
||||||
|
{
|
||||||
|
lua_getfield(L, index, target);
|
||||||
|
lua_setfield(L, index, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compat_socket_unix_call(lua_State *L)
|
||||||
|
{
|
||||||
|
/* Look up socket.unix.stream in the socket.unix table (which is the first
|
||||||
|
* argument). */
|
||||||
|
lua_getfield(L, 1, "stream");
|
||||||
|
|
||||||
|
/* Replace the stack entry for the socket.unix table with the
|
||||||
|
* socket.unix.stream function. */
|
||||||
|
lua_replace(L, 1);
|
||||||
|
|
||||||
|
/* Call socket.unix.stream, passing along any arguments. */
|
||||||
|
int n = lua_gettop(L);
|
||||||
|
lua_call(L, n-1, LUA_MULTRET);
|
||||||
|
|
||||||
|
/* Pass along the return values from socket.unix.stream. */
|
||||||
|
n = lua_gettop(L);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
LUASOCKET_API int luaopen_socket_unix(lua_State *L)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
lua_newtable(L);
|
||||||
|
int socket_unix_table = lua_gettop(L);
|
||||||
|
|
||||||
|
for (i = 0; mod[i].name; i++)
|
||||||
|
mod[i].func(L);
|
||||||
|
|
||||||
|
/* Add backwards compatibility aliases "tcp" and "udp" for the "stream" and
|
||||||
|
* "dgram" functions. */
|
||||||
|
add_alias(L, socket_unix_table, "tcp", "stream");
|
||||||
|
add_alias(L, socket_unix_table, "udp", "dgram");
|
||||||
|
|
||||||
|
/* Add a backwards compatibility function and a metatable setup to call it
|
||||||
|
* for the old socket.unix() interface. */
|
||||||
|
lua_pushcfunction(L, compat_socket_unix_call);
|
||||||
|
lua_setfield(L, socket_unix_table, "__call");
|
||||||
|
lua_pushvalue(L, socket_unix_table);
|
||||||
|
lua_setmetatable(L, socket_unix_table);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
26
external/socket/src/unix.h
vendored
Normal file
26
external/socket/src/unix.h
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef UNIX_H
|
||||||
|
#define UNIX_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Unix domain object
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* This module is just an example of how to extend LuaSocket with a new
|
||||||
|
* domain.
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "timeout.h"
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
|
typedef struct t_unix_ {
|
||||||
|
t_socket sock;
|
||||||
|
t_io io;
|
||||||
|
t_buffer buf;
|
||||||
|
t_timeout tm;
|
||||||
|
} t_unix;
|
||||||
|
typedef t_unix *p_unix;
|
||||||
|
|
||||||
|
LUASOCKET_API int luaopen_socket_unix(lua_State *L);
|
||||||
|
|
||||||
|
#endif /* UNIX_H */
|
||||||
405
external/socket/src/unixdgram.c
vendored
Normal file
405
external/socket/src/unixdgram.c
vendored
Normal file
|
|
@ -0,0 +1,405 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Unix domain socket dgram submodule
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include "auxiliar.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "unix.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
#define UNIXDGRAM_DATAGRAMSIZE 8192
|
||||||
|
|
||||||
|
/* provide a SUN_LEN macro if sys/un.h doesn't (e.g. Android) */
|
||||||
|
#ifndef SUN_LEN
|
||||||
|
#define SUN_LEN(ptr) \
|
||||||
|
((size_t) (((struct sockaddr_un *) 0)->sun_path) \
|
||||||
|
+ strlen ((ptr)->sun_path))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal function prototypes
|
||||||
|
\*=========================================================================*/
|
||||||
|
static int global_create(lua_State *L);
|
||||||
|
static int meth_connect(lua_State *L);
|
||||||
|
static int meth_bind(lua_State *L);
|
||||||
|
static int meth_send(lua_State *L);
|
||||||
|
static int meth_receive(lua_State *L);
|
||||||
|
static int meth_close(lua_State *L);
|
||||||
|
static int meth_setoption(lua_State *L);
|
||||||
|
static int meth_settimeout(lua_State *L);
|
||||||
|
static int meth_gettimeout(lua_State *L);
|
||||||
|
static int meth_getfd(lua_State *L);
|
||||||
|
static int meth_setfd(lua_State *L);
|
||||||
|
static int meth_dirty(lua_State *L);
|
||||||
|
static int meth_receivefrom(lua_State *L);
|
||||||
|
static int meth_sendto(lua_State *L);
|
||||||
|
static int meth_getsockname(lua_State *L);
|
||||||
|
|
||||||
|
static const char *unixdgram_tryconnect(p_unix un, const char *path);
|
||||||
|
static const char *unixdgram_trybind(p_unix un, const char *path);
|
||||||
|
|
||||||
|
/* unixdgram object methods */
|
||||||
|
static luaL_Reg unixdgram_methods[] = {
|
||||||
|
{"__gc", meth_close},
|
||||||
|
{"__tostring", auxiliar_tostring},
|
||||||
|
{"bind", meth_bind},
|
||||||
|
{"close", meth_close},
|
||||||
|
{"connect", meth_connect},
|
||||||
|
{"dirty", meth_dirty},
|
||||||
|
{"getfd", meth_getfd},
|
||||||
|
{"send", meth_send},
|
||||||
|
{"sendto", meth_sendto},
|
||||||
|
{"receive", meth_receive},
|
||||||
|
{"receivefrom", meth_receivefrom},
|
||||||
|
{"setfd", meth_setfd},
|
||||||
|
{"setoption", meth_setoption},
|
||||||
|
{"setpeername", meth_connect},
|
||||||
|
{"setsockname", meth_bind},
|
||||||
|
{"getsockname", meth_getsockname},
|
||||||
|
{"settimeout", meth_settimeout},
|
||||||
|
{"gettimeout", meth_gettimeout},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* socket option handlers */
|
||||||
|
static t_opt optset[] = {
|
||||||
|
{"reuseaddr", opt_set_reuseaddr},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* functions in library namespace */
|
||||||
|
static luaL_Reg func[] = {
|
||||||
|
{"dgram", global_create},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int unixdgram_open(lua_State *L)
|
||||||
|
{
|
||||||
|
/* create classes */
|
||||||
|
auxiliar_newclass(L, "unixdgram{connected}", unixdgram_methods);
|
||||||
|
auxiliar_newclass(L, "unixdgram{unconnected}", unixdgram_methods);
|
||||||
|
/* create class groups */
|
||||||
|
auxiliar_add2group(L, "unixdgram{connected}", "unixdgram{any}");
|
||||||
|
auxiliar_add2group(L, "unixdgram{unconnected}", "unixdgram{any}");
|
||||||
|
auxiliar_add2group(L, "unixdgram{connected}", "select{able}");
|
||||||
|
auxiliar_add2group(L, "unixdgram{unconnected}", "select{able}");
|
||||||
|
|
||||||
|
luaL_setfuncs(L, func, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Lua methods
|
||||||
|
\*=========================================================================*/
|
||||||
|
static const char *unixdgram_strerror(int err)
|
||||||
|
{
|
||||||
|
/* a 'closed' error on an unconnected means the target address was not
|
||||||
|
* accepted by the transport layer */
|
||||||
|
if (err == IO_CLOSED) return "refused";
|
||||||
|
else return socket_strerror(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_send(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{connected}", 1);
|
||||||
|
p_timeout tm = &un->tm;
|
||||||
|
size_t count, sent = 0;
|
||||||
|
int err;
|
||||||
|
const char *data = luaL_checklstring(L, 2, &count);
|
||||||
|
timeout_markstart(tm);
|
||||||
|
err = socket_send(&un->sock, data, count, &sent, tm);
|
||||||
|
if (err != IO_DONE) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, unixdgram_strerror(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, (lua_Number) sent);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Send data through unconnected unixdgram socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_sendto(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1);
|
||||||
|
size_t count, sent = 0;
|
||||||
|
const char *data = luaL_checklstring(L, 2, &count);
|
||||||
|
const char *path = luaL_checkstring(L, 3);
|
||||||
|
p_timeout tm = &un->tm;
|
||||||
|
int err;
|
||||||
|
struct sockaddr_un remote;
|
||||||
|
size_t len = strlen(path);
|
||||||
|
|
||||||
|
if (len >= sizeof(remote.sun_path)) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, "path too long");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&remote, 0, sizeof(remote));
|
||||||
|
strcpy(remote.sun_path, path);
|
||||||
|
remote.sun_family = AF_UNIX;
|
||||||
|
timeout_markstart(tm);
|
||||||
|
#ifdef UNIX_HAS_SUN_LEN
|
||||||
|
remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len)
|
||||||
|
+ len + 1;
|
||||||
|
err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, remote.sun_len, tm);
|
||||||
|
#else
|
||||||
|
err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote,
|
||||||
|
sizeof(remote.sun_family) + len, tm);
|
||||||
|
#endif
|
||||||
|
if (err != IO_DONE) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, unixdgram_strerror(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, (lua_Number) sent);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_receive(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
|
||||||
|
char buf[UNIXDGRAM_DATAGRAMSIZE];
|
||||||
|
size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf));
|
||||||
|
char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf;
|
||||||
|
int err;
|
||||||
|
p_timeout tm = &un->tm;
|
||||||
|
timeout_markstart(tm);
|
||||||
|
if (!dgram) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushliteral(L, "out of memory");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
err = socket_recv(&un->sock, dgram, wanted, &got, tm);
|
||||||
|
/* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */
|
||||||
|
if (err != IO_DONE && err != IO_CLOSED) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, unixdgram_strerror(err));
|
||||||
|
if (wanted > sizeof(buf)) free(dgram);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushlstring(L, dgram, got);
|
||||||
|
if (wanted > sizeof(buf)) free(dgram);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Receives data and sender from a DGRAM socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_receivefrom(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1);
|
||||||
|
char buf[UNIXDGRAM_DATAGRAMSIZE];
|
||||||
|
size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf));
|
||||||
|
char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf;
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
socklen_t addr_len = sizeof(addr);
|
||||||
|
int err;
|
||||||
|
p_timeout tm = &un->tm;
|
||||||
|
timeout_markstart(tm);
|
||||||
|
if (!dgram) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushliteral(L, "out of memory");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
addr.sun_path[0] = '\0';
|
||||||
|
err = socket_recvfrom(&un->sock, dgram, wanted, &got, (SA *) &addr,
|
||||||
|
&addr_len, tm);
|
||||||
|
/* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */
|
||||||
|
if (err != IO_DONE && err != IO_CLOSED) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, unixdgram_strerror(err));
|
||||||
|
if (wanted > sizeof(buf)) free(dgram);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pushlstring(L, dgram, got);
|
||||||
|
/* the path may be empty, when client send without bind */
|
||||||
|
lua_pushstring(L, addr.sun_path);
|
||||||
|
if (wanted > sizeof(buf)) free(dgram);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call option handler
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_setoption(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
|
||||||
|
return opt_meth_setoption(L, optset, &un->sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Select support methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_getfd(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
|
||||||
|
lua_pushnumber(L, (int) un->sock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is very dangerous, but can be handy for those that are brave enough */
|
||||||
|
static int meth_setfd(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
|
||||||
|
un->sock = (t_socket) luaL_checknumber(L, 2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_dirty(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
|
||||||
|
(void) un;
|
||||||
|
lua_pushboolean(L, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Binds an object to an address
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static const char *unixdgram_trybind(p_unix un, const char *path) {
|
||||||
|
struct sockaddr_un local;
|
||||||
|
size_t len = strlen(path);
|
||||||
|
if (len >= sizeof(local.sun_path)) return "path too long";
|
||||||
|
memset(&local, 0, sizeof(local));
|
||||||
|
strcpy(local.sun_path, path);
|
||||||
|
local.sun_family = AF_UNIX;
|
||||||
|
size_t addrlen = SUN_LEN(&local);
|
||||||
|
#ifdef UNIX_HAS_SUN_LEN
|
||||||
|
local.sun_len = addrlen + 1;
|
||||||
|
#endif
|
||||||
|
int err = socket_bind(&un->sock, (SA *) &local, addrlen);
|
||||||
|
if (err != IO_DONE) socket_destroy(&un->sock);
|
||||||
|
return socket_strerror(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_bind(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "unixdgram{unconnected}", 1);
|
||||||
|
const char *path = luaL_checkstring(L, 2);
|
||||||
|
const char *err = unixdgram_trybind(un, path);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_getsockname(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
|
||||||
|
struct sockaddr_un peer = {0};
|
||||||
|
socklen_t peer_len = sizeof(peer);
|
||||||
|
|
||||||
|
if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_strerror(errno));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pushstring(L, peer.sun_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Turns a master unixdgram object into a client object.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static const char *unixdgram_tryconnect(p_unix un, const char *path)
|
||||||
|
{
|
||||||
|
struct sockaddr_un remote;
|
||||||
|
size_t len = strlen(path);
|
||||||
|
if (len >= sizeof(remote.sun_path)) return "path too long";
|
||||||
|
memset(&remote, 0, sizeof(remote));
|
||||||
|
strcpy(remote.sun_path, path);
|
||||||
|
remote.sun_family = AF_UNIX;
|
||||||
|
timeout_markstart(&un->tm);
|
||||||
|
size_t addrlen = SUN_LEN(&remote);
|
||||||
|
#ifdef UNIX_HAS_SUN_LEN
|
||||||
|
remote.sun_len = addrlen + 1;
|
||||||
|
#endif
|
||||||
|
int err = socket_connect(&un->sock, (SA *) &remote, addrlen, &un->tm);
|
||||||
|
if (err != IO_DONE) socket_destroy(&un->sock);
|
||||||
|
return socket_strerror(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_connect(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
|
||||||
|
const char *path = luaL_checkstring(L, 2);
|
||||||
|
const char *err = unixdgram_tryconnect(un, path);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* turn unconnected object into a connected object */
|
||||||
|
auxiliar_setclass(L, "unixdgram{connected}", 1);
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Closes socket used by object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_close(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
|
||||||
|
socket_destroy(&un->sock);
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call tm methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_settimeout(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
|
||||||
|
return timeout_meth_settimeout(L, &un->tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_gettimeout(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixdgram{any}", 1);
|
||||||
|
return timeout_meth_gettimeout(L, &un->tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Library functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Creates a master unixdgram object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int global_create(lua_State *L)
|
||||||
|
{
|
||||||
|
t_socket sock;
|
||||||
|
int err = socket_create(&sock, AF_UNIX, SOCK_DGRAM, 0);
|
||||||
|
/* try to allocate a system socket */
|
||||||
|
if (err == IO_DONE) {
|
||||||
|
/* allocate unixdgram object */
|
||||||
|
p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix));
|
||||||
|
/* set its type as master object */
|
||||||
|
auxiliar_setclass(L, "unixdgram{unconnected}", -1);
|
||||||
|
/* initialize remaining structure fields */
|
||||||
|
socket_setnonblocking(&sock);
|
||||||
|
un->sock = sock;
|
||||||
|
io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv,
|
||||||
|
(p_error) socket_ioerror, &un->sock);
|
||||||
|
timeout_init(&un->tm, -1, -1);
|
||||||
|
buffer_init(&un->buf, &un->io, &un->tm);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_strerror(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
28
external/socket/src/unixdgram.h
vendored
Normal file
28
external/socket/src/unixdgram.h
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef UNIXDGRAM_H
|
||||||
|
#define UNIXDGRAM_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* DGRAM object
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* The dgram.h module provides LuaSocket with support for DGRAM protocol
|
||||||
|
* (AF_INET, SOCK_DGRAM).
|
||||||
|
*
|
||||||
|
* Two classes are defined: connected and unconnected. DGRAM objects are
|
||||||
|
* originally unconnected. They can be "connected" to a given address
|
||||||
|
* with a call to the setpeername function. The same function can be used to
|
||||||
|
* break the connection.
|
||||||
|
\*=========================================================================*/
|
||||||
|
|
||||||
|
#include "unix.h"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int unixdgram_open(lua_State *L);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* UNIXDGRAM_H */
|
||||||
355
external/socket/src/unixstream.c
vendored
Normal file
355
external/socket/src/unixstream.c
vendored
Normal file
|
|
@ -0,0 +1,355 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Unix domain socket stream sub module
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include "auxiliar.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "unixstream.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Internal function prototypes
|
||||||
|
\*=========================================================================*/
|
||||||
|
static int global_create(lua_State *L);
|
||||||
|
static int meth_connect(lua_State *L);
|
||||||
|
static int meth_listen(lua_State *L);
|
||||||
|
static int meth_bind(lua_State *L);
|
||||||
|
static int meth_send(lua_State *L);
|
||||||
|
static int meth_shutdown(lua_State *L);
|
||||||
|
static int meth_receive(lua_State *L);
|
||||||
|
static int meth_accept(lua_State *L);
|
||||||
|
static int meth_close(lua_State *L);
|
||||||
|
static int meth_setoption(lua_State *L);
|
||||||
|
static int meth_settimeout(lua_State *L);
|
||||||
|
static int meth_getfd(lua_State *L);
|
||||||
|
static int meth_setfd(lua_State *L);
|
||||||
|
static int meth_dirty(lua_State *L);
|
||||||
|
static int meth_getstats(lua_State *L);
|
||||||
|
static int meth_setstats(lua_State *L);
|
||||||
|
static int meth_getsockname(lua_State *L);
|
||||||
|
|
||||||
|
static const char *unixstream_tryconnect(p_unix un, const char *path);
|
||||||
|
static const char *unixstream_trybind(p_unix un, const char *path);
|
||||||
|
|
||||||
|
/* unixstream object methods */
|
||||||
|
static luaL_Reg unixstream_methods[] = {
|
||||||
|
{"__gc", meth_close},
|
||||||
|
{"__tostring", auxiliar_tostring},
|
||||||
|
{"accept", meth_accept},
|
||||||
|
{"bind", meth_bind},
|
||||||
|
{"close", meth_close},
|
||||||
|
{"connect", meth_connect},
|
||||||
|
{"dirty", meth_dirty},
|
||||||
|
{"getfd", meth_getfd},
|
||||||
|
{"getstats", meth_getstats},
|
||||||
|
{"setstats", meth_setstats},
|
||||||
|
{"listen", meth_listen},
|
||||||
|
{"receive", meth_receive},
|
||||||
|
{"send", meth_send},
|
||||||
|
{"setfd", meth_setfd},
|
||||||
|
{"setoption", meth_setoption},
|
||||||
|
{"setpeername", meth_connect},
|
||||||
|
{"setsockname", meth_bind},
|
||||||
|
{"getsockname", meth_getsockname},
|
||||||
|
{"settimeout", meth_settimeout},
|
||||||
|
{"shutdown", meth_shutdown},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* socket option handlers */
|
||||||
|
static t_opt optset[] = {
|
||||||
|
{"keepalive", opt_set_keepalive},
|
||||||
|
{"reuseaddr", opt_set_reuseaddr},
|
||||||
|
{"linger", opt_set_linger},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* functions in library namespace */
|
||||||
|
static luaL_Reg func[] = {
|
||||||
|
{"stream", global_create},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int unixstream_open(lua_State *L)
|
||||||
|
{
|
||||||
|
/* create classes */
|
||||||
|
auxiliar_newclass(L, "unixstream{master}", unixstream_methods);
|
||||||
|
auxiliar_newclass(L, "unixstream{client}", unixstream_methods);
|
||||||
|
auxiliar_newclass(L, "unixstream{server}", unixstream_methods);
|
||||||
|
|
||||||
|
/* create class groups */
|
||||||
|
auxiliar_add2group(L, "unixstream{master}", "unixstream{any}");
|
||||||
|
auxiliar_add2group(L, "unixstream{client}", "unixstream{any}");
|
||||||
|
auxiliar_add2group(L, "unixstream{server}", "unixstream{any}");
|
||||||
|
|
||||||
|
luaL_setfuncs(L, func, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Lua methods
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call buffered IO methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_send(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1);
|
||||||
|
return buffer_meth_send(L, &un->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_receive(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1);
|
||||||
|
return buffer_meth_receive(L, &un->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_getstats(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1);
|
||||||
|
return buffer_meth_getstats(L, &un->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_setstats(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1);
|
||||||
|
return buffer_meth_setstats(L, &un->buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call option handler
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_setoption(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1);
|
||||||
|
return opt_meth_setoption(L, optset, &un->sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Select support methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_getfd(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1);
|
||||||
|
lua_pushnumber(L, (int) un->sock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is very dangerous, but can be handy for those that are brave enough */
|
||||||
|
static int meth_setfd(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1);
|
||||||
|
un->sock = (t_socket) luaL_checknumber(L, 2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_dirty(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1);
|
||||||
|
lua_pushboolean(L, !buffer_isempty(&un->buf));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Waits for and returns a client object attempting connection to the
|
||||||
|
* server object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_accept(lua_State *L) {
|
||||||
|
p_unix server = (p_unix) auxiliar_checkclass(L, "unixstream{server}", 1);
|
||||||
|
p_timeout tm = timeout_markstart(&server->tm);
|
||||||
|
t_socket sock;
|
||||||
|
int err = socket_accept(&server->sock, &sock, NULL, NULL, tm);
|
||||||
|
/* if successful, push client socket */
|
||||||
|
if (err == IO_DONE) {
|
||||||
|
p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix));
|
||||||
|
auxiliar_setclass(L, "unixstream{client}", -1);
|
||||||
|
/* initialize structure fields */
|
||||||
|
socket_setnonblocking(&sock);
|
||||||
|
clnt->sock = sock;
|
||||||
|
io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv,
|
||||||
|
(p_error) socket_ioerror, &clnt->sock);
|
||||||
|
timeout_init(&clnt->tm, -1, -1);
|
||||||
|
buffer_init(&clnt->buf, &clnt->io, &clnt->tm);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_strerror(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Binds an object to an address
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static const char *unixstream_trybind(p_unix un, const char *path) {
|
||||||
|
struct sockaddr_un local;
|
||||||
|
size_t len = strlen(path);
|
||||||
|
int err;
|
||||||
|
if (len >= sizeof(local.sun_path)) return "path too long";
|
||||||
|
memset(&local, 0, sizeof(local));
|
||||||
|
strcpy(local.sun_path, path);
|
||||||
|
local.sun_family = AF_UNIX;
|
||||||
|
#ifdef UNIX_HAS_SUN_LEN
|
||||||
|
local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len)
|
||||||
|
+ len + 1;
|
||||||
|
err = socket_bind(&un->sock, (SA *) &local, local.sun_len);
|
||||||
|
|
||||||
|
#else
|
||||||
|
err = socket_bind(&un->sock, (SA *) &local,
|
||||||
|
sizeof(local.sun_family) + len);
|
||||||
|
#endif
|
||||||
|
if (err != IO_DONE) socket_destroy(&un->sock);
|
||||||
|
return socket_strerror(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_bind(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1);
|
||||||
|
const char *path = luaL_checkstring(L, 2);
|
||||||
|
const char *err = unixstream_trybind(un, path);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_getsockname(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1);
|
||||||
|
struct sockaddr_un peer = {0};
|
||||||
|
socklen_t peer_len = sizeof(peer);
|
||||||
|
|
||||||
|
if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_strerror(errno));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pushstring(L, peer.sun_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Turns a master unixstream object into a client object.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static const char *unixstream_tryconnect(p_unix un, const char *path)
|
||||||
|
{
|
||||||
|
struct sockaddr_un remote;
|
||||||
|
int err;
|
||||||
|
size_t len = strlen(path);
|
||||||
|
if (len >= sizeof(remote.sun_path)) return "path too long";
|
||||||
|
memset(&remote, 0, sizeof(remote));
|
||||||
|
strcpy(remote.sun_path, path);
|
||||||
|
remote.sun_family = AF_UNIX;
|
||||||
|
timeout_markstart(&un->tm);
|
||||||
|
#ifdef UNIX_HAS_SUN_LEN
|
||||||
|
remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len)
|
||||||
|
+ len + 1;
|
||||||
|
err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm);
|
||||||
|
#else
|
||||||
|
err = socket_connect(&un->sock, (SA *) &remote,
|
||||||
|
sizeof(remote.sun_family) + len, &un->tm);
|
||||||
|
#endif
|
||||||
|
if (err != IO_DONE) socket_destroy(&un->sock);
|
||||||
|
return socket_strerror(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meth_connect(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1);
|
||||||
|
const char *path = luaL_checkstring(L, 2);
|
||||||
|
const char *err = unixstream_tryconnect(un, path);
|
||||||
|
if (err) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, err);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* turn master object into a client object */
|
||||||
|
auxiliar_setclass(L, "unixstream{client}", 1);
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Closes socket used by object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_close(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1);
|
||||||
|
socket_destroy(&un->sock);
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Puts the sockt in listen mode
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_listen(lua_State *L)
|
||||||
|
{
|
||||||
|
p_unix un = (p_unix) auxiliar_checkclass(L, "unixstream{master}", 1);
|
||||||
|
int backlog = (int) luaL_optnumber(L, 2, 32);
|
||||||
|
int err = socket_listen(&un->sock, backlog);
|
||||||
|
if (err != IO_DONE) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_strerror(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* turn master object into a server object */
|
||||||
|
auxiliar_setclass(L, "unixstream{server}", 1);
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Shuts the connection down partially
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_shutdown(lua_State *L)
|
||||||
|
{
|
||||||
|
/* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */
|
||||||
|
static const char* methods[] = { "receive", "send", "both", NULL };
|
||||||
|
p_unix stream = (p_unix) auxiliar_checkclass(L, "unixstream{client}", 1);
|
||||||
|
int how = luaL_checkoption(L, 2, "both", methods);
|
||||||
|
socket_shutdown(&stream->sock, how);
|
||||||
|
lua_pushnumber(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Just call tm methods
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int meth_settimeout(lua_State *L) {
|
||||||
|
p_unix un = (p_unix) auxiliar_checkgroup(L, "unixstream{any}", 1);
|
||||||
|
return timeout_meth_settimeout(L, &un->tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Library functions
|
||||||
|
\*=========================================================================*/
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Creates a master unixstream object
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
static int global_create(lua_State *L) {
|
||||||
|
t_socket sock;
|
||||||
|
int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
/* try to allocate a system socket */
|
||||||
|
if (err == IO_DONE) {
|
||||||
|
/* allocate unixstream object */
|
||||||
|
p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix));
|
||||||
|
/* set its type as master object */
|
||||||
|
auxiliar_setclass(L, "unixstream{master}", -1);
|
||||||
|
/* initialize remaining structure fields */
|
||||||
|
socket_setnonblocking(&sock);
|
||||||
|
un->sock = sock;
|
||||||
|
io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv,
|
||||||
|
(p_error) socket_ioerror, &un->sock);
|
||||||
|
timeout_init(&un->tm, -1, -1);
|
||||||
|
buffer_init(&un->buf, &un->io, &un->tm);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_pushstring(L, socket_strerror(err));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
external/socket/src/unixstream.h
vendored
Normal file
29
external/socket/src/unixstream.h
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef UNIXSTREAM_H
|
||||||
|
#define UNIXSTREAM_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* UNIX STREAM object
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* The unixstream.h module is basicly a glue that puts together modules buffer.h,
|
||||||
|
* timeout.h socket.h and inet.h to provide the LuaSocket UNIX STREAM (AF_UNIX,
|
||||||
|
* SOCK_STREAM) support.
|
||||||
|
*
|
||||||
|
* Three classes are defined: master, client and server. The master class is
|
||||||
|
* a newly created unixstream object, that has not been bound or connected. Server
|
||||||
|
* objects are unixstream objects bound to some local address. Client objects are
|
||||||
|
* unixstream objects either connected to some address or returned by the accept
|
||||||
|
* method of a server object.
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "unix.h"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility push(hidden)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int unixstream_open(lua_State *L);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* UNIXSTREAM_H */
|
||||||
454
external/socket/src/usocket.c
vendored
Normal file
454
external/socket/src/usocket.c
vendored
Normal file
|
|
@ -0,0 +1,454 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Socket compatibilization module for Unix
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* The code is now interrupt-safe.
|
||||||
|
* The penalty of calling select to avoid busy-wait is only paid when
|
||||||
|
* the I/O call fail in the first place.
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include "socket.h"
|
||||||
|
#include "pierror.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Wait for readable/writable/connected socket with timeout
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
#ifndef SOCKET_SELECT
|
||||||
|
#include <sys/poll.h>
|
||||||
|
|
||||||
|
#define WAITFD_R POLLIN
|
||||||
|
#define WAITFD_W POLLOUT
|
||||||
|
#define WAITFD_C (POLLIN|POLLOUT)
|
||||||
|
int socket_waitfd(p_socket ps, int sw, p_timeout tm) {
|
||||||
|
int ret;
|
||||||
|
struct pollfd pfd;
|
||||||
|
pfd.fd = *ps;
|
||||||
|
pfd.events = sw;
|
||||||
|
pfd.revents = 0;
|
||||||
|
if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */
|
||||||
|
do {
|
||||||
|
int t = (int)(timeout_getretry(tm)*1e3);
|
||||||
|
ret = poll(&pfd, 1, t >= 0? t: -1);
|
||||||
|
} while (ret == -1 && errno == EINTR);
|
||||||
|
if (ret == -1) return errno;
|
||||||
|
if (ret == 0) return IO_TIMEOUT;
|
||||||
|
if (sw == WAITFD_C && (pfd.revents & (POLLIN|POLLERR))) return IO_CLOSED;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define WAITFD_R 1
|
||||||
|
#define WAITFD_W 2
|
||||||
|
#define WAITFD_C (WAITFD_R|WAITFD_W)
|
||||||
|
|
||||||
|
int socket_waitfd(p_socket ps, int sw, p_timeout tm) {
|
||||||
|
int ret;
|
||||||
|
fd_set rfds, wfds, *rp, *wp;
|
||||||
|
struct timeval tv, *tp;
|
||||||
|
double t;
|
||||||
|
if (*ps >= FD_SETSIZE) return EINVAL;
|
||||||
|
if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */
|
||||||
|
do {
|
||||||
|
/* must set bits within loop, because select may have modifed them */
|
||||||
|
rp = wp = NULL;
|
||||||
|
if (sw & WAITFD_R) { FD_ZERO(&rfds); FD_SET(*ps, &rfds); rp = &rfds; }
|
||||||
|
if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; }
|
||||||
|
t = timeout_getretry(tm);
|
||||||
|
tp = NULL;
|
||||||
|
if (t >= 0.0) {
|
||||||
|
tv.tv_sec = (int)t;
|
||||||
|
tv.tv_usec = (int)((t-tv.tv_sec)*1.0e6);
|
||||||
|
tp = &tv;
|
||||||
|
}
|
||||||
|
ret = select(*ps+1, rp, wp, NULL, tp);
|
||||||
|
} while (ret == -1 && errno == EINTR);
|
||||||
|
if (ret == -1) return errno;
|
||||||
|
if (ret == 0) return IO_TIMEOUT;
|
||||||
|
if (sw == WAITFD_C && FD_ISSET(*ps, &rfds)) return IO_CLOSED;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_open(void) {
|
||||||
|
/* installs a handler to ignore sigpipe or it will crash us */
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Close module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_close(void) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Close and inutilize socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void socket_destroy(p_socket ps) {
|
||||||
|
if (*ps != SOCKET_INVALID) {
|
||||||
|
close(*ps);
|
||||||
|
*ps = SOCKET_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Select with timeout control
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds,
|
||||||
|
p_timeout tm) {
|
||||||
|
int ret;
|
||||||
|
do {
|
||||||
|
struct timeval tv;
|
||||||
|
double t = timeout_getretry(tm);
|
||||||
|
tv.tv_sec = (int) t;
|
||||||
|
tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6);
|
||||||
|
/* timeout = 0 means no wait */
|
||||||
|
ret = select(n, rfds, wfds, efds, t >= 0.0 ? &tv: NULL);
|
||||||
|
} while (ret < 0 && errno == EINTR);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Creates and sets up a socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_create(p_socket ps, int domain, int type, int protocol) {
|
||||||
|
*ps = socket(domain, type, protocol);
|
||||||
|
if (*ps != SOCKET_INVALID) return IO_DONE;
|
||||||
|
else return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Binds or returns error message
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_bind(p_socket ps, SA *addr, socklen_t len) {
|
||||||
|
int err = IO_DONE;
|
||||||
|
socket_setblocking(ps);
|
||||||
|
if (bind(*ps, addr, len) < 0) err = errno;
|
||||||
|
socket_setnonblocking(ps);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
*
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_listen(p_socket ps, int backlog) {
|
||||||
|
int err = IO_DONE;
|
||||||
|
if (listen(*ps, backlog)) err = errno;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
*
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void socket_shutdown(p_socket ps, int how) {
|
||||||
|
shutdown(*ps, how);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Connects or returns error message
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) {
|
||||||
|
int err;
|
||||||
|
/* avoid calling on closed sockets */
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
/* call connect until done or failed without being interrupted */
|
||||||
|
do if (connect(*ps, addr, len) == 0) return IO_DONE;
|
||||||
|
while ((err = errno) == EINTR);
|
||||||
|
/* if connection failed immediately, return error code */
|
||||||
|
if (err != EINPROGRESS && err != EAGAIN) return err;
|
||||||
|
/* zero timeout case optimization */
|
||||||
|
if (timeout_iszero(tm)) return IO_TIMEOUT;
|
||||||
|
/* wait until we have the result of the connection attempt or timeout */
|
||||||
|
err = socket_waitfd(ps, WAITFD_C, tm);
|
||||||
|
if (err == IO_CLOSED) {
|
||||||
|
if (recv(*ps, (char *) &err, 0, 0) == 0) return IO_DONE;
|
||||||
|
else return errno;
|
||||||
|
} else return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Accept with timeout
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, p_timeout tm) {
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
for ( ;; ) {
|
||||||
|
int err;
|
||||||
|
if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE;
|
||||||
|
err = errno;
|
||||||
|
if (err == EINTR) continue;
|
||||||
|
if (err != EAGAIN && err != ECONNABORTED) return err;
|
||||||
|
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
|
||||||
|
}
|
||||||
|
/* can't reach here */
|
||||||
|
return IO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Send with timeout
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_send(p_socket ps, const char *data, size_t count,
|
||||||
|
size_t *sent, p_timeout tm)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
*sent = 0;
|
||||||
|
/* avoid making system calls on closed sockets */
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
/* loop until we send something or we give up on error */
|
||||||
|
for ( ;; ) {
|
||||||
|
long put = (long) send(*ps, data, count, 0);
|
||||||
|
/* if we sent anything, we are done */
|
||||||
|
if (put >= 0) {
|
||||||
|
*sent = put;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
err = errno;
|
||||||
|
/* EPIPE means the connection was closed */
|
||||||
|
if (err == EPIPE) return IO_CLOSED;
|
||||||
|
/* EPROTOTYPE means the connection is being closed (on Yosemite!)*/
|
||||||
|
if (err == EPROTOTYPE) continue;
|
||||||
|
/* we call was interrupted, just try again */
|
||||||
|
if (err == EINTR) continue;
|
||||||
|
/* if failed fatal reason, report error */
|
||||||
|
if (err != EAGAIN) return err;
|
||||||
|
/* wait until we can send something or we timeout */
|
||||||
|
if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;
|
||||||
|
}
|
||||||
|
/* can't reach here */
|
||||||
|
return IO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Sendto with timeout
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,
|
||||||
|
SA *addr, socklen_t len, p_timeout tm)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
*sent = 0;
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
for ( ;; ) {
|
||||||
|
long put = (long) sendto(*ps, data, count, 0, addr, len);
|
||||||
|
if (put >= 0) {
|
||||||
|
*sent = put;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
err = errno;
|
||||||
|
if (err == EPIPE) return IO_CLOSED;
|
||||||
|
if (err == EPROTOTYPE) continue;
|
||||||
|
if (err == EINTR) continue;
|
||||||
|
if (err != EAGAIN) return err;
|
||||||
|
if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;
|
||||||
|
}
|
||||||
|
return IO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Receive with timeout
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) {
|
||||||
|
int err;
|
||||||
|
*got = 0;
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
for ( ;; ) {
|
||||||
|
long taken = (long) recv(*ps, data, count, 0);
|
||||||
|
if (taken > 0) {
|
||||||
|
*got = taken;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
err = errno;
|
||||||
|
if (taken == 0) return IO_CLOSED;
|
||||||
|
if (err == EINTR) continue;
|
||||||
|
if (err != EAGAIN) return err;
|
||||||
|
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
|
||||||
|
}
|
||||||
|
return IO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Recvfrom with timeout
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,
|
||||||
|
SA *addr, socklen_t *len, p_timeout tm) {
|
||||||
|
int err;
|
||||||
|
*got = 0;
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
for ( ;; ) {
|
||||||
|
long taken = (long) recvfrom(*ps, data, count, 0, addr, len);
|
||||||
|
if (taken > 0) {
|
||||||
|
*got = taken;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
err = errno;
|
||||||
|
if (taken == 0) return IO_CLOSED;
|
||||||
|
if (err == EINTR) continue;
|
||||||
|
if (err != EAGAIN) return err;
|
||||||
|
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
|
||||||
|
}
|
||||||
|
return IO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Write with timeout
|
||||||
|
*
|
||||||
|
* socket_read and socket_write are cut-n-paste of socket_send and socket_recv,
|
||||||
|
* with send/recv replaced with write/read. We can't just use write/read
|
||||||
|
* in the socket version, because behaviour when size is zero is different.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_write(p_socket ps, const char *data, size_t count,
|
||||||
|
size_t *sent, p_timeout tm)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
*sent = 0;
|
||||||
|
/* avoid making system calls on closed sockets */
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
/* loop until we send something or we give up on error */
|
||||||
|
for ( ;; ) {
|
||||||
|
long put = (long) write(*ps, data, count);
|
||||||
|
/* if we sent anything, we are done */
|
||||||
|
if (put >= 0) {
|
||||||
|
*sent = put;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
err = errno;
|
||||||
|
/* EPIPE means the connection was closed */
|
||||||
|
if (err == EPIPE) return IO_CLOSED;
|
||||||
|
/* EPROTOTYPE means the connection is being closed (on Yosemite!)*/
|
||||||
|
if (err == EPROTOTYPE) continue;
|
||||||
|
/* we call was interrupted, just try again */
|
||||||
|
if (err == EINTR) continue;
|
||||||
|
/* if failed fatal reason, report error */
|
||||||
|
if (err != EAGAIN) return err;
|
||||||
|
/* wait until we can send something or we timeout */
|
||||||
|
if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;
|
||||||
|
}
|
||||||
|
/* can't reach here */
|
||||||
|
return IO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Read with timeout
|
||||||
|
* See note for socket_write
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) {
|
||||||
|
int err;
|
||||||
|
*got = 0;
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
for ( ;; ) {
|
||||||
|
long taken = (long) read(*ps, data, count);
|
||||||
|
if (taken > 0) {
|
||||||
|
*got = taken;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
err = errno;
|
||||||
|
if (taken == 0) return IO_CLOSED;
|
||||||
|
if (err == EINTR) continue;
|
||||||
|
if (err != EAGAIN) return err;
|
||||||
|
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
|
||||||
|
}
|
||||||
|
return IO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Put socket into blocking mode
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void socket_setblocking(p_socket ps) {
|
||||||
|
int flags = fcntl(*ps, F_GETFL, 0);
|
||||||
|
flags &= (~(O_NONBLOCK));
|
||||||
|
fcntl(*ps, F_SETFL, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Put socket into non-blocking mode
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void socket_setnonblocking(p_socket ps) {
|
||||||
|
int flags = fcntl(*ps, F_GETFL, 0);
|
||||||
|
flags |= O_NONBLOCK;
|
||||||
|
fcntl(*ps, F_SETFL, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* DNS helpers
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) {
|
||||||
|
*hp = gethostbyaddr(addr, len, AF_INET);
|
||||||
|
if (*hp) return IO_DONE;
|
||||||
|
else if (h_errno) return h_errno;
|
||||||
|
else if (errno) return errno;
|
||||||
|
else return IO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int socket_gethostbyname(const char *addr, struct hostent **hp) {
|
||||||
|
*hp = gethostbyname(addr);
|
||||||
|
if (*hp) return IO_DONE;
|
||||||
|
else if (h_errno) return h_errno;
|
||||||
|
else if (errno) return errno;
|
||||||
|
else return IO_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Error translation functions
|
||||||
|
* Make sure important error messages are standard
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
const char *socket_hoststrerror(int err) {
|
||||||
|
if (err <= 0) return io_strerror(err);
|
||||||
|
switch (err) {
|
||||||
|
case HOST_NOT_FOUND: return PIE_HOST_NOT_FOUND;
|
||||||
|
default: return hstrerror(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *socket_strerror(int err) {
|
||||||
|
if (err <= 0) return io_strerror(err);
|
||||||
|
switch (err) {
|
||||||
|
case EADDRINUSE: return PIE_ADDRINUSE;
|
||||||
|
case EISCONN: return PIE_ISCONN;
|
||||||
|
case EACCES: return PIE_ACCESS;
|
||||||
|
case ECONNREFUSED: return PIE_CONNREFUSED;
|
||||||
|
case ECONNABORTED: return PIE_CONNABORTED;
|
||||||
|
case ECONNRESET: return PIE_CONNRESET;
|
||||||
|
case ETIMEDOUT: return PIE_TIMEDOUT;
|
||||||
|
default: {
|
||||||
|
return strerror(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *socket_ioerror(p_socket ps, int err) {
|
||||||
|
(void) ps;
|
||||||
|
return socket_strerror(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *socket_gaistrerror(int err) {
|
||||||
|
if (err == 0) return NULL;
|
||||||
|
switch (err) {
|
||||||
|
case EAI_AGAIN: return PIE_AGAIN;
|
||||||
|
case EAI_BADFLAGS: return PIE_BADFLAGS;
|
||||||
|
#ifdef EAI_BADHINTS
|
||||||
|
case EAI_BADHINTS: return PIE_BADHINTS;
|
||||||
|
#endif
|
||||||
|
case EAI_FAIL: return PIE_FAIL;
|
||||||
|
case EAI_FAMILY: return PIE_FAMILY;
|
||||||
|
case EAI_MEMORY: return PIE_MEMORY;
|
||||||
|
case EAI_NONAME: return PIE_NONAME;
|
||||||
|
#ifdef EAI_OVERFLOW
|
||||||
|
case EAI_OVERFLOW: return PIE_OVERFLOW;
|
||||||
|
#endif
|
||||||
|
#ifdef EAI_PROTOCOL
|
||||||
|
case EAI_PROTOCOL: return PIE_PROTOCOL;
|
||||||
|
#endif
|
||||||
|
case EAI_SERVICE: return PIE_SERVICE;
|
||||||
|
case EAI_SOCKTYPE: return PIE_SOCKTYPE;
|
||||||
|
case EAI_SYSTEM: return strerror(errno);
|
||||||
|
default: return LUA_GAI_STRERROR(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
external/socket/src/usocket.h
vendored
Normal file
59
external/socket/src/usocket.h
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
#ifndef USOCKET_H
|
||||||
|
#define USOCKET_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Socket compatibilization module for Unix
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* BSD include files
|
||||||
|
\*=========================================================================*/
|
||||||
|
/* error codes */
|
||||||
|
#include <errno.h>
|
||||||
|
/* close function */
|
||||||
|
#include <unistd.h>
|
||||||
|
/* fnctnl function and associated constants */
|
||||||
|
#include <fcntl.h>
|
||||||
|
/* struct sockaddr */
|
||||||
|
#include <sys/types.h>
|
||||||
|
/* socket function */
|
||||||
|
#include <sys/socket.h>
|
||||||
|
/* struct timeval */
|
||||||
|
#include <sys/time.h>
|
||||||
|
/* gethostbyname and gethostbyaddr functions */
|
||||||
|
#include <netdb.h>
|
||||||
|
/* sigpipe handling */
|
||||||
|
#include <signal.h>
|
||||||
|
/* IP stuff*/
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
/* TCP options (nagle algorithm disable) */
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
|
#ifndef SO_REUSEPORT
|
||||||
|
#define SO_REUSEPORT SO_REUSEADDR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Some platforms use IPV6_JOIN_GROUP instead if
|
||||||
|
* IPV6_ADD_MEMBERSHIP. The semantics are same, though. */
|
||||||
|
#ifndef IPV6_ADD_MEMBERSHIP
|
||||||
|
#ifdef IPV6_JOIN_GROUP
|
||||||
|
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
|
||||||
|
#endif /* IPV6_JOIN_GROUP */
|
||||||
|
#endif /* !IPV6_ADD_MEMBERSHIP */
|
||||||
|
|
||||||
|
/* Same with IPV6_DROP_MEMBERSHIP / IPV6_LEAVE_GROUP. */
|
||||||
|
#ifndef IPV6_DROP_MEMBERSHIP
|
||||||
|
#ifdef IPV6_LEAVE_GROUP
|
||||||
|
#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
|
||||||
|
#endif /* IPV6_LEAVE_GROUP */
|
||||||
|
#endif /* !IPV6_DROP_MEMBERSHIP */
|
||||||
|
|
||||||
|
typedef int t_socket;
|
||||||
|
typedef t_socket *p_socket;
|
||||||
|
typedef struct sockaddr_storage t_sockaddr_storage;
|
||||||
|
|
||||||
|
#define SOCKET_INVALID (-1)
|
||||||
|
|
||||||
|
#endif /* USOCKET_H */
|
||||||
434
external/socket/src/wsocket.c
vendored
Normal file
434
external/socket/src/wsocket.c
vendored
Normal file
|
|
@ -0,0 +1,434 @@
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Socket compatibilization module for Win32
|
||||||
|
* LuaSocket toolkit
|
||||||
|
*
|
||||||
|
* The penalty of calling select to avoid busy-wait is only paid when
|
||||||
|
* the I/O call fail in the first place.
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include "luasocket.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "socket.h"
|
||||||
|
#include "pierror.h"
|
||||||
|
|
||||||
|
/* WinSock doesn't have a strerror... */
|
||||||
|
static const char *wstrerror(int err);
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Initializes module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_open(void) {
|
||||||
|
WSADATA wsaData;
|
||||||
|
WORD wVersionRequested = MAKEWORD(2, 0);
|
||||||
|
int err = WSAStartup(wVersionRequested, &wsaData );
|
||||||
|
if (err != 0) return 0;
|
||||||
|
if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) &&
|
||||||
|
(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) {
|
||||||
|
WSACleanup();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Close module
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_close(void) {
|
||||||
|
WSACleanup();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Wait for readable/writable/connected socket with timeout
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
#define WAITFD_R 1
|
||||||
|
#define WAITFD_W 2
|
||||||
|
#define WAITFD_E 4
|
||||||
|
#define WAITFD_C (WAITFD_E|WAITFD_W)
|
||||||
|
|
||||||
|
int socket_waitfd(p_socket ps, int sw, p_timeout tm) {
|
||||||
|
int ret;
|
||||||
|
fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL;
|
||||||
|
struct timeval tv, *tp = NULL;
|
||||||
|
double t;
|
||||||
|
if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */
|
||||||
|
if (sw & WAITFD_R) {
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(*ps, &rfds);
|
||||||
|
rp = &rfds;
|
||||||
|
}
|
||||||
|
if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; }
|
||||||
|
if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; }
|
||||||
|
if ((t = timeout_get(tm)) >= 0.0) {
|
||||||
|
tv.tv_sec = (int) t;
|
||||||
|
tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6);
|
||||||
|
tp = &tv;
|
||||||
|
}
|
||||||
|
ret = select(0, rp, wp, ep, tp);
|
||||||
|
if (ret == -1) return WSAGetLastError();
|
||||||
|
if (ret == 0) return IO_TIMEOUT;
|
||||||
|
if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) return IO_CLOSED;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Select with int timeout in ms
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds,
|
||||||
|
p_timeout tm) {
|
||||||
|
struct timeval tv;
|
||||||
|
double t = timeout_get(tm);
|
||||||
|
tv.tv_sec = (int) t;
|
||||||
|
tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6);
|
||||||
|
if (n <= 0) {
|
||||||
|
Sleep((DWORD) (1000*t));
|
||||||
|
return 0;
|
||||||
|
} else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Close and inutilize socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void socket_destroy(p_socket ps) {
|
||||||
|
if (*ps != SOCKET_INVALID) {
|
||||||
|
socket_setblocking(ps); /* close can take a long time on WIN32 */
|
||||||
|
closesocket(*ps);
|
||||||
|
*ps = SOCKET_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
*
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void socket_shutdown(p_socket ps, int how) {
|
||||||
|
socket_setblocking(ps);
|
||||||
|
shutdown(*ps, how);
|
||||||
|
socket_setnonblocking(ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Creates and sets up a socket
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_create(p_socket ps, int domain, int type, int protocol) {
|
||||||
|
*ps = socket(domain, type, protocol);
|
||||||
|
if (*ps != SOCKET_INVALID) return IO_DONE;
|
||||||
|
else return WSAGetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Connects or returns error message
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) {
|
||||||
|
int err;
|
||||||
|
/* don't call on closed socket */
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
/* ask system to connect */
|
||||||
|
if (connect(*ps, addr, len) == 0) return IO_DONE;
|
||||||
|
/* make sure the system is trying to connect */
|
||||||
|
err = WSAGetLastError();
|
||||||
|
if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err;
|
||||||
|
/* zero timeout case optimization */
|
||||||
|
if (timeout_iszero(tm)) return IO_TIMEOUT;
|
||||||
|
/* we wait until something happens */
|
||||||
|
err = socket_waitfd(ps, WAITFD_C, tm);
|
||||||
|
if (err == IO_CLOSED) {
|
||||||
|
int elen = sizeof(err);
|
||||||
|
/* give windows time to set the error (yes, disgusting) */
|
||||||
|
Sleep(10);
|
||||||
|
/* find out why we failed */
|
||||||
|
getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &elen);
|
||||||
|
/* we KNOW there was an error. if 'why' is 0, we will return
|
||||||
|
* "unknown error", but it's not really our fault */
|
||||||
|
return err > 0? err: IO_UNKNOWN;
|
||||||
|
} else return err;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Binds or returns error message
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_bind(p_socket ps, SA *addr, socklen_t len) {
|
||||||
|
int err = IO_DONE;
|
||||||
|
socket_setblocking(ps);
|
||||||
|
if (bind(*ps, addr, len) < 0) err = WSAGetLastError();
|
||||||
|
socket_setnonblocking(ps);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
*
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_listen(p_socket ps, int backlog) {
|
||||||
|
int err = IO_DONE;
|
||||||
|
socket_setblocking(ps);
|
||||||
|
if (listen(*ps, backlog) < 0) err = WSAGetLastError();
|
||||||
|
socket_setnonblocking(ps);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Accept with timeout
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len,
|
||||||
|
p_timeout tm) {
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
for ( ;; ) {
|
||||||
|
int err;
|
||||||
|
/* try to get client socket */
|
||||||
|
if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE;
|
||||||
|
/* find out why we failed */
|
||||||
|
err = WSAGetLastError();
|
||||||
|
/* if we failed because there was no connectoin, keep trying */
|
||||||
|
if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err;
|
||||||
|
/* call select to avoid busy wait */
|
||||||
|
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Send with timeout
|
||||||
|
* On windows, if you try to send 10MB, the OS will buffer EVERYTHING
|
||||||
|
* this can take an awful lot of time and we will end up blocked.
|
||||||
|
* Therefore, whoever calls this function should not pass a huge buffer.
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_send(p_socket ps, const char *data, size_t count,
|
||||||
|
size_t *sent, p_timeout tm)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
*sent = 0;
|
||||||
|
/* avoid making system calls on closed sockets */
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
/* loop until we send something or we give up on error */
|
||||||
|
for ( ;; ) {
|
||||||
|
/* try to send something */
|
||||||
|
int put = send(*ps, data, (int) count, 0);
|
||||||
|
/* if we sent something, we are done */
|
||||||
|
if (put > 0) {
|
||||||
|
*sent = put;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
/* deal with failure */
|
||||||
|
err = WSAGetLastError();
|
||||||
|
/* we can only proceed if there was no serious error */
|
||||||
|
if (err != WSAEWOULDBLOCK) return err;
|
||||||
|
/* avoid busy wait */
|
||||||
|
if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Sendto with timeout
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,
|
||||||
|
SA *addr, socklen_t len, p_timeout tm)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
*sent = 0;
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
for ( ;; ) {
|
||||||
|
int put = sendto(*ps, data, (int) count, 0, addr, len);
|
||||||
|
if (put > 0) {
|
||||||
|
*sent = put;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
err = WSAGetLastError();
|
||||||
|
if (err != WSAEWOULDBLOCK) return err;
|
||||||
|
if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Receive with timeout
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_recv(p_socket ps, char *data, size_t count, size_t *got,
|
||||||
|
p_timeout tm)
|
||||||
|
{
|
||||||
|
int err, prev = IO_DONE;
|
||||||
|
*got = 0;
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
for ( ;; ) {
|
||||||
|
int taken = recv(*ps, data, (int) count, 0);
|
||||||
|
if (taken > 0) {
|
||||||
|
*got = taken;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
if (taken == 0) return IO_CLOSED;
|
||||||
|
err = WSAGetLastError();
|
||||||
|
/* On UDP, a connreset simply means the previous send failed.
|
||||||
|
* So we try again.
|
||||||
|
* On TCP, it means our socket is now useless, so the error passes.
|
||||||
|
* (We will loop again, exiting because the same error will happen) */
|
||||||
|
if (err != WSAEWOULDBLOCK) {
|
||||||
|
if (err != WSAECONNRESET || prev == WSAECONNRESET) return err;
|
||||||
|
prev = err;
|
||||||
|
}
|
||||||
|
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Recvfrom with timeout
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,
|
||||||
|
SA *addr, socklen_t *len, p_timeout tm)
|
||||||
|
{
|
||||||
|
int err, prev = IO_DONE;
|
||||||
|
*got = 0;
|
||||||
|
if (*ps == SOCKET_INVALID) return IO_CLOSED;
|
||||||
|
for ( ;; ) {
|
||||||
|
int taken = recvfrom(*ps, data, (int) count, 0, addr, len);
|
||||||
|
if (taken > 0) {
|
||||||
|
*got = taken;
|
||||||
|
return IO_DONE;
|
||||||
|
}
|
||||||
|
if (taken == 0) return IO_CLOSED;
|
||||||
|
err = WSAGetLastError();
|
||||||
|
/* On UDP, a connreset simply means the previous send failed.
|
||||||
|
* So we try again.
|
||||||
|
* On TCP, it means our socket is now useless, so the error passes.
|
||||||
|
* (We will loop again, exiting because the same error will happen) */
|
||||||
|
if (err != WSAEWOULDBLOCK) {
|
||||||
|
if (err != WSAECONNRESET || prev == WSAECONNRESET) return err;
|
||||||
|
prev = err;
|
||||||
|
}
|
||||||
|
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Put socket into blocking mode
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void socket_setblocking(p_socket ps) {
|
||||||
|
u_long argp = 0;
|
||||||
|
ioctlsocket(*ps, FIONBIO, &argp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Put socket into non-blocking mode
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
void socket_setnonblocking(p_socket ps) {
|
||||||
|
u_long argp = 1;
|
||||||
|
ioctlsocket(*ps, FIONBIO, &argp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* DNS helpers
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) {
|
||||||
|
*hp = gethostbyaddr(addr, len, AF_INET);
|
||||||
|
if (*hp) return IO_DONE;
|
||||||
|
else return WSAGetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
int socket_gethostbyname(const char *addr, struct hostent **hp) {
|
||||||
|
*hp = gethostbyname(addr);
|
||||||
|
if (*hp) return IO_DONE;
|
||||||
|
else return WSAGetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*\
|
||||||
|
* Error translation functions
|
||||||
|
\*-------------------------------------------------------------------------*/
|
||||||
|
const char *socket_hoststrerror(int err) {
|
||||||
|
if (err <= 0) return io_strerror(err);
|
||||||
|
switch (err) {
|
||||||
|
case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND;
|
||||||
|
default: return wstrerror(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *socket_strerror(int err) {
|
||||||
|
if (err <= 0) return io_strerror(err);
|
||||||
|
switch (err) {
|
||||||
|
case WSAEADDRINUSE: return PIE_ADDRINUSE;
|
||||||
|
case WSAECONNREFUSED : return PIE_CONNREFUSED;
|
||||||
|
case WSAEISCONN: return PIE_ISCONN;
|
||||||
|
case WSAEACCES: return PIE_ACCESS;
|
||||||
|
case WSAECONNABORTED: return PIE_CONNABORTED;
|
||||||
|
case WSAECONNRESET: return PIE_CONNRESET;
|
||||||
|
case WSAETIMEDOUT: return PIE_TIMEDOUT;
|
||||||
|
default: return wstrerror(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *socket_ioerror(p_socket ps, int err) {
|
||||||
|
(void) ps;
|
||||||
|
return socket_strerror(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *wstrerror(int err) {
|
||||||
|
switch (err) {
|
||||||
|
case WSAEINTR: return "Interrupted function call";
|
||||||
|
case WSAEACCES: return PIE_ACCESS; /* "Permission denied"; */
|
||||||
|
case WSAEFAULT: return "Bad address";
|
||||||
|
case WSAEINVAL: return "Invalid argument";
|
||||||
|
case WSAEMFILE: return "Too many open files";
|
||||||
|
case WSAEWOULDBLOCK: return "Resource temporarily unavailable";
|
||||||
|
case WSAEINPROGRESS: return "Operation now in progress";
|
||||||
|
case WSAEALREADY: return "Operation already in progress";
|
||||||
|
case WSAENOTSOCK: return "Socket operation on nonsocket";
|
||||||
|
case WSAEDESTADDRREQ: return "Destination address required";
|
||||||
|
case WSAEMSGSIZE: return "Message too long";
|
||||||
|
case WSAEPROTOTYPE: return "Protocol wrong type for socket";
|
||||||
|
case WSAENOPROTOOPT: return "Bad protocol option";
|
||||||
|
case WSAEPROTONOSUPPORT: return "Protocol not supported";
|
||||||
|
case WSAESOCKTNOSUPPORT: return PIE_SOCKTYPE; /* "Socket type not supported"; */
|
||||||
|
case WSAEOPNOTSUPP: return "Operation not supported";
|
||||||
|
case WSAEPFNOSUPPORT: return "Protocol family not supported";
|
||||||
|
case WSAEAFNOSUPPORT: return PIE_FAMILY; /* "Address family not supported by protocol family"; */
|
||||||
|
case WSAEADDRINUSE: return PIE_ADDRINUSE; /* "Address already in use"; */
|
||||||
|
case WSAEADDRNOTAVAIL: return "Cannot assign requested address";
|
||||||
|
case WSAENETDOWN: return "Network is down";
|
||||||
|
case WSAENETUNREACH: return "Network is unreachable";
|
||||||
|
case WSAENETRESET: return "Network dropped connection on reset";
|
||||||
|
case WSAECONNABORTED: return "Software caused connection abort";
|
||||||
|
case WSAECONNRESET: return PIE_CONNRESET; /* "Connection reset by peer"; */
|
||||||
|
case WSAENOBUFS: return "No buffer space available";
|
||||||
|
case WSAEISCONN: return PIE_ISCONN; /* "Socket is already connected"; */
|
||||||
|
case WSAENOTCONN: return "Socket is not connected";
|
||||||
|
case WSAESHUTDOWN: return "Cannot send after socket shutdown";
|
||||||
|
case WSAETIMEDOUT: return PIE_TIMEDOUT; /* "Connection timed out"; */
|
||||||
|
case WSAECONNREFUSED: return PIE_CONNREFUSED; /* "Connection refused"; */
|
||||||
|
case WSAEHOSTDOWN: return "Host is down";
|
||||||
|
case WSAEHOSTUNREACH: return "No route to host";
|
||||||
|
case WSAEPROCLIM: return "Too many processes";
|
||||||
|
case WSASYSNOTREADY: return "Network subsystem is unavailable";
|
||||||
|
case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range";
|
||||||
|
case WSANOTINITIALISED:
|
||||||
|
return "Successful WSAStartup not yet performed";
|
||||||
|
case WSAEDISCON: return "Graceful shutdown in progress";
|
||||||
|
case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; /* "Host not found"; */
|
||||||
|
case WSATRY_AGAIN: return "Nonauthoritative host not found";
|
||||||
|
case WSANO_RECOVERY: return PIE_FAIL; /* "Nonrecoverable name lookup error"; */
|
||||||
|
case WSANO_DATA: return "Valid name, no data record of requested type";
|
||||||
|
default: return "Unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *socket_gaistrerror(int err) {
|
||||||
|
if (err == 0) return NULL;
|
||||||
|
switch (err) {
|
||||||
|
case EAI_AGAIN: return PIE_AGAIN;
|
||||||
|
case EAI_BADFLAGS: return PIE_BADFLAGS;
|
||||||
|
#ifdef EAI_BADHINTS
|
||||||
|
case EAI_BADHINTS: return PIE_BADHINTS;
|
||||||
|
#endif
|
||||||
|
case EAI_FAIL: return PIE_FAIL;
|
||||||
|
case EAI_FAMILY: return PIE_FAMILY;
|
||||||
|
case EAI_MEMORY: return PIE_MEMORY;
|
||||||
|
case EAI_NONAME: return PIE_NONAME;
|
||||||
|
#ifdef EAI_OVERFLOW
|
||||||
|
case EAI_OVERFLOW: return PIE_OVERFLOW;
|
||||||
|
#endif
|
||||||
|
#ifdef EAI_PROTOCOL
|
||||||
|
case EAI_PROTOCOL: return PIE_PROTOCOL;
|
||||||
|
#endif
|
||||||
|
case EAI_SERVICE: return PIE_SERVICE;
|
||||||
|
case EAI_SOCKTYPE: return PIE_SOCKTYPE;
|
||||||
|
#ifdef EAI_SYSTEM
|
||||||
|
case EAI_SYSTEM: return strerror(errno);
|
||||||
|
#endif
|
||||||
|
default: return LUA_GAI_STRERROR(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
external/socket/src/wsocket.h
vendored
Normal file
33
external/socket/src/wsocket.h
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef WSOCKET_H
|
||||||
|
#define WSOCKET_H
|
||||||
|
/*=========================================================================*\
|
||||||
|
* Socket compatibilization module for Win32
|
||||||
|
* LuaSocket toolkit
|
||||||
|
\*=========================================================================*/
|
||||||
|
|
||||||
|
/*=========================================================================*\
|
||||||
|
* WinSock include files
|
||||||
|
\*=========================================================================*/
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
|
||||||
|
typedef int socklen_t;
|
||||||
|
typedef SOCKADDR_STORAGE t_sockaddr_storage;
|
||||||
|
typedef SOCKET t_socket;
|
||||||
|
typedef t_socket *p_socket;
|
||||||
|
|
||||||
|
#ifndef IPV6_V6ONLY
|
||||||
|
#define IPV6_V6ONLY 27
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SOCKET_INVALID (INVALID_SOCKET)
|
||||||
|
|
||||||
|
#ifndef SO_REUSEPORT
|
||||||
|
#define SO_REUSEPORT SO_REUSEADDR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef AI_NUMERICSERV
|
||||||
|
#define AI_NUMERICSERV (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* WSOCKET_H */
|
||||||
64
lcallisto.c
Normal file
64
lcallisto.c
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Callisto, a featureful runtime for Lua 5.4.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lualib.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include "lcallisto.h"
|
||||||
|
|
||||||
|
/* Callisto libraries */
|
||||||
|
#include "lcl.h"
|
||||||
|
#include "lenvironment.h"
|
||||||
|
#include "lfile.h"
|
||||||
|
#include "ljson.h"
|
||||||
|
#include "lmath.h"
|
||||||
|
#include "los.h"
|
||||||
|
#include "lprocess.h"
|
||||||
|
#include "lsocket.h"
|
||||||
|
|
||||||
|
|
||||||
|
static const luaL_Reg loadedlibs[] = {
|
||||||
|
{CALLISTO_CLLIBNAME, callistoopen_cl},
|
||||||
|
{CALLISTO_ENVLIBNAME, callistoopen_environment},
|
||||||
|
{CALLISTO_FILELIBNAME, callistoopen_file},
|
||||||
|
{CALLISTO_JSONLIBNAME, callistoopen_json},
|
||||||
|
{CALLISTO_MATHLIBNAME, callistoopen_math},
|
||||||
|
{CALLISTO_OSLIBNAME, callistoopen_os},
|
||||||
|
{CALLISTO_PROCLIBNAME, callistoopen_process},
|
||||||
|
{CALLISTO_SOCKLIBNAME, callistoopen_socket},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
lua_State *
|
||||||
|
callisto_newstate(void)
|
||||||
|
{
|
||||||
|
lua_State *L = luaL_newstate();
|
||||||
|
callisto_openlibs(L);
|
||||||
|
callisto_setversion(L);
|
||||||
|
return L;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
callisto_openlibs(lua_State *L)
|
||||||
|
{
|
||||||
|
const luaL_Reg *lib;
|
||||||
|
|
||||||
|
luaL_openlibs(L);
|
||||||
|
|
||||||
|
/* "require" functions from 'loadedlibs' and set results to global table */
|
||||||
|
for (lib = loadedlibs; lib->func; lib++) {
|
||||||
|
lua_newtable(L);
|
||||||
|
lib->func(L); /* load library */
|
||||||
|
lua_setglobal(L, lib->name);
|
||||||
|
//lua_pop(L, 1); /* remove lib */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
callisto_setversion(lua_State *L)
|
||||||
|
{
|
||||||
|
lua_pushliteral(L, CALLISTO_VERSION);
|
||||||
|
lua_setglobal(L, "_CALLISTOVERSION");
|
||||||
|
}
|
||||||
40
lcallisto.h
Normal file
40
lcallisto.h
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Callisto, a featureful runtime for Lua 5.4.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LCALLISTO_H_
|
||||||
|
|
||||||
|
#define _LCALLISTO_H_
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
#define CALLISTO_VERSION_MAJOR "0"
|
||||||
|
#define CALLISTO_VERSION_MINOR "1"
|
||||||
|
#define CALLISTO_VERSION_RELEASE "0"
|
||||||
|
|
||||||
|
#define CALLISTO_VERSION "Callisto " CALLISTO_VERSION_MAJOR "." CALLISTO_VERSION_MINOR
|
||||||
|
#define CALLISTO_COPYRIGHT CALLISTO_VERSION " (" LUA_RELEASE ") Copyright (C) 1994-2022 Lua.org, PUC-Rio"
|
||||||
|
|
||||||
|
#define CALLISTO_CLLIBNAME "cl"
|
||||||
|
#define CALLISTO_ENVLIBNAME "environment"
|
||||||
|
#define CALLISTO_FILELIBNAME "file"
|
||||||
|
#define CALLISTO_JSONLIBNAME "json"
|
||||||
|
#define CALLISTO_MATHLIBNAME "math"
|
||||||
|
#define CALLISTO_OSLIBNAME "os"
|
||||||
|
#define CALLISTO_PROCLIBNAME "process"
|
||||||
|
#define CALLISTO_SOCKLIBNAME "socket"
|
||||||
|
|
||||||
|
int callistoopen_cl(lua_State *);
|
||||||
|
int callistoopen_environment(lua_State *);
|
||||||
|
int callistoopen_file(lua_State *);
|
||||||
|
int callistoopen_json(lua_State *);
|
||||||
|
int callistoopen_math(lua_State *);
|
||||||
|
int callistoopen_os(lua_State *);
|
||||||
|
int callistoopen_process(lua_State *);
|
||||||
|
int callistoopen_socket(lua_State *);
|
||||||
|
|
||||||
|
lua_State *callisto_newstate(void);
|
||||||
|
void callisto_openlibs(lua_State *);
|
||||||
|
void callisto_setversion(lua_State *);
|
||||||
|
|
||||||
|
#endif
|
||||||
302
lcl.c
Normal file
302
lcl.c
Normal file
|
|
@ -0,0 +1,302 @@
|
||||||
|
/***
|
||||||
|
* Option parsing, and error formatting
|
||||||
|
* for the command line.
|
||||||
|
* @module cl
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#ifdef __linux__
|
||||||
|
# include <bsd/string.h>
|
||||||
|
#else
|
||||||
|
/* assume BSD */
|
||||||
|
# include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include "lcl.h"
|
||||||
|
#include "lcallisto.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
fmesg(lua_State *L, FILE* f, int shift)
|
||||||
|
{
|
||||||
|
int paramc, i;
|
||||||
|
char *progname; /* argv[0] */
|
||||||
|
|
||||||
|
paramc = lua_gettop(L); /* get parameter count */
|
||||||
|
|
||||||
|
lua_geti(L, 1 + shift, 0); /* get index 0 of table at index 1 (argv) */
|
||||||
|
if (lua_type(L, -1) != LUA_TSTRING) { /* if argv[0] is not a string... */
|
||||||
|
luaL_error(L, "invalid argument table passed (must have an string in index [0])");
|
||||||
|
}
|
||||||
|
progname = (char *)lua_tostring(L, -1); /* set progname to argv[0] */
|
||||||
|
|
||||||
|
/* format using string.format */
|
||||||
|
lua_getglobal(L, "string");
|
||||||
|
lua_getfield(L, -1, "format");
|
||||||
|
|
||||||
|
for (i = 2 + shift; i <= paramc; i++) /* for every parameter */
|
||||||
|
lua_pushvalue(L, i); /* push argument */
|
||||||
|
|
||||||
|
lua_call(L, paramc - (1 + shift), 1); /* call string.format */
|
||||||
|
|
||||||
|
fprintf(f, "%s: %s\n", basename(progname), lua_tostring(L, -1)); /* print */
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Prints a formatted error message to standard error.
|
||||||
|
* It looks like so:
|
||||||
|
*
|
||||||
|
* `progname: message`
|
||||||
|
*
|
||||||
|
* where *progname* is the name of the current script
|
||||||
|
* being executed and *message* is the *message* parameter.
|
||||||
|
*
|
||||||
|
* The *message* parameter may optionally be followed by
|
||||||
|
* a variable number of arguments which are formatted
|
||||||
|
* using *string.format*.
|
||||||
|
*
|
||||||
|
* @function error
|
||||||
|
* @usage
|
||||||
|
local succeeded, err = io.open("file.txt")
|
||||||
|
if not succeeded then
|
||||||
|
cl.error(arg, "could not open " .. err)
|
||||||
|
end
|
||||||
|
* @tparam table arg The command line argument table (this function only uses index *[0]*)
|
||||||
|
* @tparam string message The message to print after the program's name. Supports *string.format*-style format specifiers.
|
||||||
|
* @param ... Any additional values specified by the format specifiers in the *message* parameter.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
cl_error(lua_State *L)
|
||||||
|
{
|
||||||
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
luaL_checkstring(L, 2);
|
||||||
|
|
||||||
|
fmesg(L, stderr, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/***
|
||||||
|
* Prints a formatted message to standard output.
|
||||||
|
* It looks like so:
|
||||||
|
*
|
||||||
|
* `progname: message`
|
||||||
|
*
|
||||||
|
* where *progname* is the name of the current script
|
||||||
|
* being executed and *message* is the (optionally
|
||||||
|
* formatted) *message* parameter.
|
||||||
|
*
|
||||||
|
* The *message* parameter may optionally be followed by
|
||||||
|
* a variable number of arguments which are formatted
|
||||||
|
* using *string.format*.
|
||||||
|
*
|
||||||
|
* @function mesg
|
||||||
|
* @usage cl.mesg(arg, "message to stdout")
|
||||||
|
* @tparam table arg The command line argument table (this function only uses index *[0]*)
|
||||||
|
* @tparam string message The message to print after the program's name. Supports *string.format*-style format specifiers.
|
||||||
|
* @param ... Any additional values specified by the format specifiers in the *message* parameter.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
cl_mesg(lua_State *L)
|
||||||
|
{
|
||||||
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
luaL_checkstring(L, 2);
|
||||||
|
|
||||||
|
fmesg(L, stdout, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/***
|
||||||
|
* Convenience function that calls *cl.error* to
|
||||||
|
* print a formatted error message to standard error,
|
||||||
|
* then terminates script execution and calls *os.exit*.
|
||||||
|
*
|
||||||
|
* For more information on the *message* parameter,
|
||||||
|
* see the *cl.error* function.
|
||||||
|
*
|
||||||
|
* @function panic
|
||||||
|
* @usage
|
||||||
|
local succeeded, err, code = io.open("file.txt")
|
||||||
|
if not succeeded then
|
||||||
|
-- use a custom exit code:
|
||||||
|
cl.panic(code, arg, "could not open " .. err)
|
||||||
|
-- use the default exit code (1):
|
||||||
|
cl.panic(arg, "could not open " .. err)
|
||||||
|
end
|
||||||
|
* @tparam[opt] integer code The exit code to return to the OS.
|
||||||
|
* @tparam table arg The command line argument table (this function only uses index *[0]*)
|
||||||
|
* @tparam string message The message to print after the program's name. Supports *string.format*-style format specifiers.
|
||||||
|
* @param ... Any additional values specified by the format specifiers in the *message* parameter.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
cl_panic(lua_State *L)
|
||||||
|
{
|
||||||
|
int code;
|
||||||
|
|
||||||
|
if (lua_isinteger(L, 1)) { /* if an exit code was given... */
|
||||||
|
code = lua_tointeger(L, 1); /* get code */
|
||||||
|
luaL_checktype(L, 2, LUA_TTABLE);
|
||||||
|
luaL_checkstring(L, 3);
|
||||||
|
fmesg(L, stderr, 1);
|
||||||
|
} else {
|
||||||
|
code = 1;
|
||||||
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
luaL_checkstring(L, 2);
|
||||||
|
fmesg(L, stderr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* format using string.format */
|
||||||
|
lua_getglobal(L, "os");
|
||||||
|
lua_getfield(L, -1, "exit");
|
||||||
|
|
||||||
|
/* push arguments to os.exit */
|
||||||
|
lua_pushinteger(L, code);
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
|
||||||
|
lua_call(L, 2, 0); /* call os.exit */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/***
|
||||||
|
* Parses the command line argument list *arg*.
|
||||||
|
* The string *optstring* may contain the
|
||||||
|
* following elements: individual characters,
|
||||||
|
* and characters followed by a colon.
|
||||||
|
* A character followed by a single colon
|
||||||
|
* indicates that an argument is to follow
|
||||||
|
* the option on the command line. For example,
|
||||||
|
* an option string `"x"` permits a **-x** option,
|
||||||
|
* and an option string `"x:"` permits a **-x**
|
||||||
|
* option that must take an argument. An option
|
||||||
|
* string of `"x:yz"` permits a **-x** option that
|
||||||
|
* takes an argument, and **-y** and **-z** options,
|
||||||
|
* which do not.
|
||||||
|
*
|
||||||
|
* The function *fn* is run each time a new option
|
||||||
|
* is processed. of the argument list. It takes the
|
||||||
|
* parameters *opt*, *optarg*, *optindex*, and *opterror*.
|
||||||
|
* *opt* is a string containing the option used. It is set
|
||||||
|
* to the option the user specified on the command line.
|
||||||
|
* If the user specifies an unknown option (one that is
|
||||||
|
* not specified in *optstring*), the value of *opt* will
|
||||||
|
* be set to nil. If the user specifies an option that
|
||||||
|
* requires an argument, but does not specify its argument,
|
||||||
|
* the value of *opt* will be the string `"*"` (a single
|
||||||
|
* asterisk). The second parameter, *optarg*, is a string
|
||||||
|
* containing the option argument (if applicable).
|
||||||
|
* *optindex* is an integer that contains the index of the
|
||||||
|
* last command line argument processed. The last parameter,
|
||||||
|
* *opterror*, is set in case of an option error (if *opt*
|
||||||
|
* is nil or set to the string `"*"`), and is set to the
|
||||||
|
* option that caused an error (in the case of *opt* being
|
||||||
|
* nil, this will be the unknown option the user specified,
|
||||||
|
* or in the case of *opt* being the string `"*"`, this
|
||||||
|
* will be the option that required an argument).
|
||||||
|
*
|
||||||
|
* @function parseopts
|
||||||
|
* @usage
|
||||||
|
cl.parseopts(arg, "abc:", function(opt, optarg, optindex, opterror)
|
||||||
|
if opt == 'a' then
|
||||||
|
print("-a was used")
|
||||||
|
elseif opt == 'b' then
|
||||||
|
print("-b was used")
|
||||||
|
elseif opt == 'c' then
|
||||||
|
print("-c was used, with argument " .. optarg)
|
||||||
|
elseif opt == '*' then
|
||||||
|
print("missing argument for -" .. opterror)
|
||||||
|
elseif opt == nil then -- or ``if not opt``
|
||||||
|
print("unknown option -" .. opterror)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
* @tparam table arg The argument table to parse.
|
||||||
|
* @tparam string optstring The option string, containing options that should be parsed.
|
||||||
|
* @tparam function fn The function to be run each time a new option is specified on the command line.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
cl_parseopts(lua_State *L)
|
||||||
|
{
|
||||||
|
int argc; /* command line argument count */
|
||||||
|
int i;
|
||||||
|
char ch, s[2]; /* opt character and string */
|
||||||
|
char loptopt[2]; /* opterror, returned to Lua */
|
||||||
|
char **argv; /* parameter 1 (table), command line argument vector */
|
||||||
|
char *optstring; /* parameter 2 (string), optstring passed to getopt */
|
||||||
|
|
||||||
|
optstring = malloc(lua_rawlen(L, 2) * sizeof(char *));
|
||||||
|
|
||||||
|
/* parameter type checking */
|
||||||
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
strcpy(optstring, (char *)luaL_checkstring(L, 2));
|
||||||
|
luaL_checktype(L, 3, LUA_TFUNCTION);
|
||||||
|
|
||||||
|
argc = (int)lua_rawlen(L, 1) + 1; /* get argv length */
|
||||||
|
argv = lua_newuserdatauv(L, (argc + 1) * sizeof(char *), 0);
|
||||||
|
argv[argc] = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++) { /* for every argument */
|
||||||
|
lua_pushinteger(L, i); /* push argv index */
|
||||||
|
lua_gettable(L, 1); /* push argv[i] */
|
||||||
|
argv[i] = (char *)luaL_checkstring(L, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optstring[0] == ':')
|
||||||
|
return luaL_error(L, "option string may not start with a colon (:)");
|
||||||
|
|
||||||
|
strprepend(optstring, ":");
|
||||||
|
|
||||||
|
/* getopt loop */
|
||||||
|
while ((ch = getopt(argc, argv, optstring)) != -1) {
|
||||||
|
/* construct string containing ch */
|
||||||
|
s[0] = ch;
|
||||||
|
s[1] = 0;
|
||||||
|
lua_pushvalue(L, 3);
|
||||||
|
|
||||||
|
/* first function parameter: opt */
|
||||||
|
if (ch == '?')
|
||||||
|
lua_pushnil(L); /* in case of unknown option */
|
||||||
|
else if (ch == ':')
|
||||||
|
lua_pushliteral(L, "*"); /* in case of missing option argument */
|
||||||
|
else
|
||||||
|
lua_pushstring(L, s);
|
||||||
|
|
||||||
|
/* second function parameter: optarg */
|
||||||
|
lua_pushstring(L, optarg);
|
||||||
|
/* third function parameter: optindex */
|
||||||
|
lua_pushinteger(L, optind);
|
||||||
|
/* fourth function parameter: opterror
|
||||||
|
* (only non-nil on error) */
|
||||||
|
if (optopt == '?') /* error was not encountered */
|
||||||
|
lua_pushnil(L);
|
||||||
|
else {
|
||||||
|
loptopt[0] = optopt;
|
||||||
|
loptopt[1] = 0;
|
||||||
|
lua_pushstring(L, loptopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pcall(L, 4, 0, 0); /* call Lua function */
|
||||||
|
}
|
||||||
|
free(optstring);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const luaL_Reg cllib[] = {
|
||||||
|
{"mesg", cl_mesg},
|
||||||
|
{"error", cl_error},
|
||||||
|
{"panic", cl_panic},
|
||||||
|
{"parseopts", cl_parseopts},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
callistoopen_cl(lua_State *L)
|
||||||
|
{
|
||||||
|
luaL_newlib(L, cllib);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
9
lcl.h
Normal file
9
lcl.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _LCL_H_
|
||||||
|
|
||||||
|
#define _LCL_H_
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
int callistoopen_cl(lua_State *);
|
||||||
|
|
||||||
|
#endif
|
||||||
724
ldoc.css
Normal file
724
ldoc.css
Normal file
|
|
@ -0,0 +1,724 @@
|
||||||
|
/* BEGIN RESET
|
||||||
|
|
||||||
|
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
|
||||||
|
Code licensed under the BSD License:
|
||||||
|
http://developer.yahoo.com/yui/license.html
|
||||||
|
version: 2.8.2r1
|
||||||
|
*/
|
||||||
|
html {
|
||||||
|
color: #FFF;
|
||||||
|
background: #FFF;
|
||||||
|
}
|
||||||
|
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
fieldset,img {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
address,caption,cite,code,dfn,em,strong,th,var,optgroup {
|
||||||
|
font-style: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
del,ins {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
list-style: bullet;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
caption,th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
h1,h2,h3,h4,h5,h6 {
|
||||||
|
font-size: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
q:before,q:after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
abbr,acronym {
|
||||||
|
border: 0;
|
||||||
|
font-variant: normal;
|
||||||
|
}
|
||||||
|
sup {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
sub {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
legend {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
input,button,textarea,select,optgroup,option {
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
font-style: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
input,button,textarea,select {
|
||||||
|
font-size:100%;
|
||||||
|
}
|
||||||
|
/* END RESET */
|
||||||
|
|
||||||
|
/* LDoc styles customized for Callisto
|
||||||
|
* Original style taken from Lua4z */
|
||||||
|
|
||||||
|
@media screen {
|
||||||
|
html {
|
||||||
|
margin: 0;
|
||||||
|
background-color: #202020; /* Fills the page */
|
||||||
|
position: relative; /* Fix for absolute positioning */
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
position: relative;
|
||||||
|
padding: 1em;
|
||||||
|
background-color: #181818;
|
||||||
|
}
|
||||||
|
|
||||||
|
code,
|
||||||
|
tt,
|
||||||
|
span.parameter {
|
||||||
|
font-family: "Andale Mono", "Lucida Console", monospace;
|
||||||
|
background-color: #202020;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.parameter:after {
|
||||||
|
content: ":";
|
||||||
|
}
|
||||||
|
|
||||||
|
span.types:before {
|
||||||
|
content: "(";
|
||||||
|
}
|
||||||
|
|
||||||
|
span.types:after {
|
||||||
|
content: ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
.type {
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
ul {
|
||||||
|
margin: 1em 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
em,
|
||||||
|
var {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
var {
|
||||||
|
color: #606060;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
line-height: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0.25em 0 0.5em 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4 {
|
||||||
|
margin: 1em 0 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 1.5em;
|
||||||
|
padding: 0.25em;
|
||||||
|
background-color: #202020;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
border: 1px solid #A0A0A0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link {
|
||||||
|
font-weight: normal;
|
||||||
|
color: #004080;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
font-weight: normal;
|
||||||
|
color: #006699;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
color:#cccccc;
|
||||||
|
background: #00007F;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
padding-left: 3em;
|
||||||
|
border-left: 1px solid #CCCCCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: square;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.name {
|
||||||
|
font-family: "Andale Mono", monospace;
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.example,
|
||||||
|
pre {
|
||||||
|
background-color: #202020;
|
||||||
|
border: 1px solid #CCCCCC;
|
||||||
|
padding: 0.5em;
|
||||||
|
margin: 1em 0 0.5em 0;
|
||||||
|
font-family: "Andale Mono", "Lucida Console", monospace;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.index {
|
||||||
|
border: 1px solid #00007F;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.index td {
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
#product a {
|
||||||
|
outline: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#product {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#product_name {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.5em 0 0.5em 0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
|
background-color: #181818; /* was #F0F0F0 */
|
||||||
|
}
|
||||||
|
|
||||||
|
#navigation {
|
||||||
|
margin: 1em -1em 1em -1em;
|
||||||
|
background-color: #181818;
|
||||||
|
overflow: visible;
|
||||||
|
border-bottom: 1px solid #CCCCCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navigation h2 {
|
||||||
|
background-color: #181818;
|
||||||
|
font-size: 1em;
|
||||||
|
color: #F0F0F0;
|
||||||
|
text-align: left;
|
||||||
|
padding: 0.5em 0.5em 0.5em 1em;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navigation ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navigation ul ul {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navigation li {
|
||||||
|
display: block;
|
||||||
|
border-top: 1px solid #CCCCCC;
|
||||||
|
padding: 0.5em 0.5em 0.5em 1em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navigation li li {
|
||||||
|
border: none;
|
||||||
|
padding: 0 0 0.5em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
padding: 0;
|
||||||
|
background-color: #181818;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer {
|
||||||
|
margin: 1em;
|
||||||
|
color: #909090;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark item as Lua4z-only */
|
||||||
|
span.lua4z-only {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.lua4z-only:before {
|
||||||
|
content: "z";
|
||||||
|
line-height: 1.3em;
|
||||||
|
font-size: 1em;
|
||||||
|
float: right;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
height: 1.5em;
|
||||||
|
width: 1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #00007F;
|
||||||
|
border-radius: 0.75em;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the span is a child of a paragraph, do not float right */
|
||||||
|
p > span.lua4z-only:before {
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.lua4z-only:hover {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.module-std:before,
|
||||||
|
span.module-ext:before {
|
||||||
|
color: #909090;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.module-std:before {
|
||||||
|
content: "std";
|
||||||
|
}
|
||||||
|
|
||||||
|
span.module-ext:before {
|
||||||
|
content: "ext";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Introduce curved border around body */
|
||||||
|
@media only screen and (min-width: 30em) {
|
||||||
|
body {
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
#container {
|
||||||
|
border: solid #A0A0A0 1px;
|
||||||
|
border-radius: 1em;
|
||||||
|
box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pop out navigation as left sidebar */
|
||||||
|
@media only screen and (min-width: 50em) {
|
||||||
|
body {
|
||||||
|
margin-left: 18em;
|
||||||
|
}
|
||||||
|
#navigation {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 10;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 17em;
|
||||||
|
overflow-y: scroll;
|
||||||
|
-webkit-overflow-scrolling: touch; /* Enable inertial scrolling on iOS */
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
#navigation h2 {
|
||||||
|
border: none;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
#navigation > ul > li:first-child {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
#navigation * {
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Railroad syntax diagram heading */
|
||||||
|
p.ebnf-symbol {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin: 1em 0 1em 0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Limit maximum width of body */
|
||||||
|
@media only screen and (min-width: 80em) {
|
||||||
|
body {
|
||||||
|
max-width: 60em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Center body on wide screens, pop out sidebars */
|
||||||
|
@media only screen and (min-width: 100em) {
|
||||||
|
body {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
.sidebar {
|
||||||
|
display: block;
|
||||||
|
float: right;
|
||||||
|
clear: both;
|
||||||
|
top: -2em;
|
||||||
|
padding: 0.5em;
|
||||||
|
position: relative;
|
||||||
|
margin: 0 -19em 0 0 ;
|
||||||
|
width: 15em;
|
||||||
|
background-color: #EFF2B9;
|
||||||
|
border: solid #A0A0A0 1px;
|
||||||
|
box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a[href] {
|
||||||
|
color: #005faf;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
body {
|
||||||
|
font-size: 11pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #F0F0F0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a[href] {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #F0F0F0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 0;
|
||||||
|
border-bottom: 0.5pt solid #CCCCCC;
|
||||||
|
padding-bottom: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
}
|
||||||
|
|
||||||
|
#navigation {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
border-width: 0.5pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.example {
|
||||||
|
font-family: "Andale Mono", monospace;
|
||||||
|
font-size: 10pt;
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.lua4z-only:before {
|
||||||
|
color: #000000;
|
||||||
|
border: 1px solid #000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.module_list,
|
||||||
|
table.function_list {
|
||||||
|
border: 1px solid #CCCCCC;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.module_list td,
|
||||||
|
table.function_list td {
|
||||||
|
border: 1px solid #CCCCCC;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.module_list td.name {
|
||||||
|
background-color: #202020;
|
||||||
|
min-width: 14em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.function_list td.name {
|
||||||
|
background-color: #202020;
|
||||||
|
min-width: 14em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.module_list td.summary,
|
||||||
|
table.function_list td.summary {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
dl.table dt,
|
||||||
|
dl.function dt {
|
||||||
|
border-top: 1px solid #CCCCCC;
|
||||||
|
padding-top: 1em;
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Suppress top border and top padding on first item */
|
||||||
|
dl.table dt:first-child,
|
||||||
|
dl.function dt:first-child {
|
||||||
|
border-top: none;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dl.table dd,
|
||||||
|
dl.function dd {
|
||||||
|
padding-bottom: 1em;
|
||||||
|
margin: 0.5em 0 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
dl.table h3,
|
||||||
|
dl.function h3 {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.nowrap {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop sublists from having initial vertical space */
|
||||||
|
ul ul,
|
||||||
|
ol ul,
|
||||||
|
ol ol,
|
||||||
|
ul ol {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styles for prettification of source */
|
||||||
|
pre .comment {
|
||||||
|
color: #B0B0B0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .constant {
|
||||||
|
color: #a8660d;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .escape {
|
||||||
|
color: #844631;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .keyword {
|
||||||
|
color: #2239a8;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .library {
|
||||||
|
color: #0e7c6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .marker {
|
||||||
|
color: #512b1e;
|
||||||
|
background: #fedc56;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .string {
|
||||||
|
color: #2da250;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .number {
|
||||||
|
color: #f8660d;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .operator {
|
||||||
|
color: #2239a8;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .preprocessor,
|
||||||
|
pre .prepro {
|
||||||
|
color: #a33243;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .global {
|
||||||
|
color: #005faf;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .prompt {
|
||||||
|
color: #558817;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre .url {
|
||||||
|
color: #272fc2;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Markdown content style */
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 1em 0 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td,
|
||||||
|
th,
|
||||||
|
caption {
|
||||||
|
border: 1px solid #CCCCCC;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
caption {
|
||||||
|
background-color: #D0D0FF;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
vertical-align: bottom;
|
||||||
|
background-color: #EEEEFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Miscellaneous */
|
||||||
|
|
||||||
|
span.highlight {
|
||||||
|
background-color: #FFFF00;
|
||||||
|
padding: 0 0.1em 0 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.annotation {
|
||||||
|
background-color: #00FF00;
|
||||||
|
padding: 0 0.1em 0 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* License styles */
|
||||||
|
|
||||||
|
/* The license.md source file contains the empty element */
|
||||||
|
/* <div class="license-start"></div> */
|
||||||
|
/* to trigger the following style */
|
||||||
|
/* Indent paragraphs in license */
|
||||||
|
.license-start ~ p {
|
||||||
|
margin-left: 3em;
|
||||||
|
}
|
||||||
|
/* <div class="license-end"></div> */
|
||||||
|
/* resets the indent */
|
||||||
|
.license-end ~ p {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.license-clause-marker,
|
||||||
|
span.license-sub-clause-marker,
|
||||||
|
span.license-recital-marker {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.license-clause-marker + span,
|
||||||
|
span.license-recital-marker + span {
|
||||||
|
display: block;
|
||||||
|
margin-left: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 span.license-clause-marker + span,
|
||||||
|
h3 span.license-clause-marker + span {
|
||||||
|
margin-left: 2.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.license-sub-clause-marker {
|
||||||
|
float: left;
|
||||||
|
margin-left: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.license-sub-clause-marker + span {
|
||||||
|
display: block;
|
||||||
|
margin-left: 6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("Inter-Regular.woff2?v=3.19") format("woff2"),
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("Inter-Italic.woff2?v=3.19") format("woff2"),
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("Inter-Bold.woff2?v=3.19") format("woff2"),
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"),
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
span.annotation {
|
||||||
|
font-family: "Inter";
|
||||||
|
}
|
||||||
|
|
||||||
|
.name a {
|
||||||
|
color: #F0F0F0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 > a[href],
|
||||||
|
#navigation h2 {
|
||||||
|
color: #F0F0F0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
148
lenvironment.c
Normal file
148
lenvironment.c
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
/***
|
||||||
|
* Getting and setting
|
||||||
|
* environment variables.
|
||||||
|
* @module environment
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#ifdef __linux__
|
||||||
|
# include <bsd/string.h>
|
||||||
|
#else
|
||||||
|
/* assume BSD */
|
||||||
|
# include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include "lenvironment.h"
|
||||||
|
#include "lcallisto.h"
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Returns the value of the given environment
|
||||||
|
* variable.
|
||||||
|
*
|
||||||
|
* If *var* does not exist in the environment
|
||||||
|
* and *defaultvalue* is not nil,
|
||||||
|
* *defaultvalue* will be returned.
|
||||||
|
*
|
||||||
|
* @function get
|
||||||
|
* @usage environment.get("HOME", "/root")
|
||||||
|
* @tparam string var The environment variable to get.
|
||||||
|
* @param[opt] defaultvalue The value to return in case the environment variable was not found.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
environment_get(lua_State *L)
|
||||||
|
{
|
||||||
|
const char *variable;
|
||||||
|
char *ret;
|
||||||
|
|
||||||
|
variable = luaL_checkstring(L, 1);
|
||||||
|
ret = getenv(variable);
|
||||||
|
|
||||||
|
if (ret == NULL)
|
||||||
|
if (!lua_isnoneornil(L, 2))
|
||||||
|
lua_pushvalue(L, 2);
|
||||||
|
else
|
||||||
|
lua_pushnil(L);
|
||||||
|
else
|
||||||
|
lua_pushstring(L, ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Sets the value of the given environment variable.
|
||||||
|
*
|
||||||
|
* This function will throw an error if the value of
|
||||||
|
* *var* is invalid for the name of an environment
|
||||||
|
* variable, or if there is insufficient memory
|
||||||
|
* available to perform the operation.
|
||||||
|
*
|
||||||
|
* @function set
|
||||||
|
* @usage environment.set("MYVAR", "1")
|
||||||
|
* @tparam string var The environment variable to set.
|
||||||
|
* @tparam string value The value to set for *var*.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
environment_set(lua_State *L)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
const char *variable;
|
||||||
|
const char *value;
|
||||||
|
|
||||||
|
variable = luaL_checkstring(L, 1);
|
||||||
|
value = luaL_checkstring(L, 2);
|
||||||
|
ret = setenv(variable, value, 1);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (errno) {
|
||||||
|
case EINVAL:
|
||||||
|
return luaL_error(L, "invalid input string");
|
||||||
|
break;
|
||||||
|
case ENOMEM:
|
||||||
|
return luaL_error(L, "insufficient memory");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Clears the environment.
|
||||||
|
*
|
||||||
|
* If *var* is not nil, only clears the value of that
|
||||||
|
* environment variable (not the whole environment).
|
||||||
|
*
|
||||||
|
* This function will throw an error if the value of
|
||||||
|
* *var* is invalid for the name of an environment
|
||||||
|
* variable, or if there is insufficient memory
|
||||||
|
* available to perform the operation.
|
||||||
|
*
|
||||||
|
* @function clear
|
||||||
|
* @usage environment.clear("MYVAR")
|
||||||
|
* @tparam[opt] string var The environment variable to clear.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
environment_clear(lua_State *L)
|
||||||
|
{
|
||||||
|
const char *variable;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (lua_isnoneornil(L, 1)) {
|
||||||
|
clearenv();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
variable = luaL_checkstring(L, 1);
|
||||||
|
ret = unsetenv(variable);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (errno) {
|
||||||
|
case EINVAL:
|
||||||
|
return luaL_error(L, "invalid input string");
|
||||||
|
break;
|
||||||
|
case ENOMEM:
|
||||||
|
return luaL_error(L, "insufficient memory");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const luaL_Reg envlib[] = {
|
||||||
|
{"get", environment_get},
|
||||||
|
{"set", environment_set},
|
||||||
|
{"clear", environment_clear},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
callistoopen_environment(lua_State *L)
|
||||||
|
{
|
||||||
|
luaL_newlib(L, envlib);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
9
lenvironment.h
Normal file
9
lenvironment.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _LENVIRONMENT_H_
|
||||||
|
|
||||||
|
#define _LENVIRONMENT_H_
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
int callistoopen_environment(lua_State *);
|
||||||
|
|
||||||
|
#endif
|
||||||
361
lfile.c
Normal file
361
lfile.c
Normal file
|
|
@ -0,0 +1,361 @@
|
||||||
|
/***
|
||||||
|
* Files, path manipulation,
|
||||||
|
* and permissions.
|
||||||
|
* @module file
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#ifdef __linux__
|
||||||
|
# include <bsd/string.h>
|
||||||
|
#else
|
||||||
|
/* assume BSD */
|
||||||
|
# include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include "lfile.h"
|
||||||
|
#include "lcallisto.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Returns the last component of the given pathname,
|
||||||
|
* removing any trailing '/' characters. If *path*
|
||||||
|
* consists entirely of '/' characters, the string
|
||||||
|
* `"/"` is returned. If *path* is an empty string,
|
||||||
|
* the string `"."` is returned.
|
||||||
|
*
|
||||||
|
* This function may return nil if the given
|
||||||
|
* pathname is longer than the system's path length
|
||||||
|
* limit (On most Linux systems this will be 4096).
|
||||||
|
*
|
||||||
|
* @function basename
|
||||||
|
* @usage file.basename(arg[0])
|
||||||
|
* @tparam string path The path to process.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
file_basename(lua_State *L)
|
||||||
|
{
|
||||||
|
char *path; /* parameter 1 (string) */
|
||||||
|
char *ret;
|
||||||
|
|
||||||
|
path = (char *)luaL_checkstring(L, 1);
|
||||||
|
ret = basename(path);
|
||||||
|
|
||||||
|
if (ret == NULL && errno == ENAMETOOLONG) /* check if path is too long */
|
||||||
|
return lfail(L, "pathname too long");
|
||||||
|
|
||||||
|
lua_pushstring(L, ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Returns the parent directory of the pathname
|
||||||
|
* given. Any trailing '/' characters are not
|
||||||
|
* counted as part of the directory name.
|
||||||
|
* If *path* is an empty string or contains no
|
||||||
|
* '/' characters, the string `"."` is returned,
|
||||||
|
* signifying the current directory.
|
||||||
|
*
|
||||||
|
* This function may return nil if the given
|
||||||
|
* pathname is longer than the system's path length
|
||||||
|
* limit (On most Linux systems this will be 4096).
|
||||||
|
*
|
||||||
|
* @function dirname
|
||||||
|
* @usage file.dirname(arg[0])
|
||||||
|
* @tparam string path The path to process.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
file_dirname(lua_State *L)
|
||||||
|
{
|
||||||
|
char *path; /* parameter 1 (string) */
|
||||||
|
char *ret;
|
||||||
|
|
||||||
|
path = (char *)luaL_checkstring(L, 1);
|
||||||
|
ret = dirname(path);
|
||||||
|
|
||||||
|
if (ret == NULL && errno == ENAMETOOLONG) /* check if path is too long */
|
||||||
|
return lfail(L, "pathname too long");
|
||||||
|
|
||||||
|
lua_pushstring(L, ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Returns true if the given pathname exists
|
||||||
|
* in the file system, or returns false if it
|
||||||
|
* does not.
|
||||||
|
*
|
||||||
|
* This function may throw an error if the given
|
||||||
|
* pathname is longer than the system's path length
|
||||||
|
* limit (On most Linux systems this will be 4096).
|
||||||
|
*
|
||||||
|
* @function exists
|
||||||
|
* @usage
|
||||||
|
if file.exists("/etc/fstab") then
|
||||||
|
-- ...
|
||||||
|
end
|
||||||
|
* @tparam string path The path of the file to look for.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
file_exists(lua_State *L)
|
||||||
|
{
|
||||||
|
const char *path;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
path = luaL_checkstring(L, 1);
|
||||||
|
ret = access(path, F_OK); /* check if file exists */
|
||||||
|
|
||||||
|
if (ret == -1 && errno == ENAMETOOLONG) /* check if path is too long */
|
||||||
|
return lfail(L, "pathname too long");
|
||||||
|
|
||||||
|
lua_pushboolean(L, ret == 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Moves the file at the path *from*
|
||||||
|
* to the path *to*, moving it between directories
|
||||||
|
* if required. If *to* exists, it is first removed.
|
||||||
|
* Both *from* and *to* must be of the same type
|
||||||
|
* (that is, both directories or both non-directories)
|
||||||
|
* and must reside on the same file system.
|
||||||
|
*
|
||||||
|
* This function will return nil and an error
|
||||||
|
* message if one of the following conditions are met:
|
||||||
|
*
|
||||||
|
* - One of the given pathnames are longer than the system's path length limit
|
||||||
|
*
|
||||||
|
* - One of the given pathnames point to a file that does not exist
|
||||||
|
*
|
||||||
|
* - An attempt was made to move a parent directory of a pathname
|
||||||
|
*
|
||||||
|
* - The current user is denied permission to perform the action
|
||||||
|
*
|
||||||
|
* - One of the given pathnames could not be translated as a result
|
||||||
|
* of too many symbolic links
|
||||||
|
*
|
||||||
|
* - A component of one of the given pathnames is not a directory
|
||||||
|
*
|
||||||
|
* - *from* is a directory, but *to* is not
|
||||||
|
*
|
||||||
|
* - *to* is a directory, but *from* is not
|
||||||
|
*
|
||||||
|
* - The given pathnames are on different file systems
|
||||||
|
*
|
||||||
|
* - There is no space left on the file system
|
||||||
|
*
|
||||||
|
* - The current user's quota of disk blocks on the file system
|
||||||
|
* containing the directory of *to* has been exhausted
|
||||||
|
*
|
||||||
|
* - The directory containing *to* resides on a read-only file system
|
||||||
|
*
|
||||||
|
* This function will throw an error if:
|
||||||
|
*
|
||||||
|
* - An I/O error occurred
|
||||||
|
*
|
||||||
|
* - An internal memory error occurred
|
||||||
|
*
|
||||||
|
* @function move
|
||||||
|
* @usage file.move("file1", "file2")
|
||||||
|
* @tparam string from The pathname of the file to move.
|
||||||
|
* @tparam string to The pathname to move *from* to.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
file_move(lua_State *L)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
const char *from; /* parameter 1 (string) */
|
||||||
|
const char *to; /* parameter 2 (string) */
|
||||||
|
|
||||||
|
from = luaL_checkstring(L, 1);
|
||||||
|
to = luaL_checkstring(L, 2);
|
||||||
|
ret = rename(from, to); /* move file */
|
||||||
|
|
||||||
|
if (ret == 0) { /* check for success */
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (errno) {
|
||||||
|
case ENAMETOOLONG:
|
||||||
|
return lfail(L, "pathname too long");
|
||||||
|
break;
|
||||||
|
case ENOENT:
|
||||||
|
return lfail(L, "no such file or directory");
|
||||||
|
break;
|
||||||
|
case EACCES:
|
||||||
|
return lfail(L, "permission denied");
|
||||||
|
break;
|
||||||
|
case EPERM:
|
||||||
|
return lfail(L, "permission denied (sticky directory)");
|
||||||
|
break;
|
||||||
|
case ELOOP:
|
||||||
|
return lfail(L, "could not translate pathname; too many symbolic links");
|
||||||
|
break;
|
||||||
|
case EMLINK:
|
||||||
|
return lfail(L, "maximum link count reached");
|
||||||
|
break;
|
||||||
|
case ENOTDIR:
|
||||||
|
return lfail(L, "component of pathname is not a directory");
|
||||||
|
break;
|
||||||
|
case EISDIR:
|
||||||
|
return lfail(L, "cannot move a file to the name of a directory");
|
||||||
|
break;
|
||||||
|
case EXDEV:
|
||||||
|
return lfail(L, "pathnames are on different file systems");
|
||||||
|
break;
|
||||||
|
case ENOSPC:
|
||||||
|
return lfail(L, "insufficient space left on file system");
|
||||||
|
break;
|
||||||
|
case EDQUOT:
|
||||||
|
return lfail(L, "file system quota reached");
|
||||||
|
break;
|
||||||
|
case EIO:
|
||||||
|
return luaL_error(L, "I/O error");
|
||||||
|
break;
|
||||||
|
case EROFS:
|
||||||
|
return lfail(L, "read-only file system");
|
||||||
|
break;
|
||||||
|
case EFAULT:
|
||||||
|
return luaL_error(L, "internal error (EFAULT)");
|
||||||
|
break;
|
||||||
|
case EINVAL:
|
||||||
|
return lfail(L, "cannot move a parent directory of pathname");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Sets/returns the current working directory.
|
||||||
|
*
|
||||||
|
* If *dir* is nil, returns the current working
|
||||||
|
* directory. Otherwise, sets the current working
|
||||||
|
* directory to *dir*.
|
||||||
|
*
|
||||||
|
* This function will return nil and an error
|
||||||
|
* message if one of the following conditions are met:
|
||||||
|
*
|
||||||
|
* - One of the given pathnames are longer than the system's path length limit
|
||||||
|
*
|
||||||
|
* - The current user is denied permission to perform the action
|
||||||
|
*
|
||||||
|
* - The current working directory or *dir* no longer exists
|
||||||
|
*
|
||||||
|
* - *dir* is not nil, nor is it the name of a directory
|
||||||
|
*
|
||||||
|
* - *dir* could not be translated as a result of too many symbolic links
|
||||||
|
*
|
||||||
|
* This function will throw an error if:
|
||||||
|
*
|
||||||
|
* - Insufficient memory is available
|
||||||
|
*
|
||||||
|
* - An internal memory error occurred
|
||||||
|
*
|
||||||
|
* - An I/O error occurred
|
||||||
|
*
|
||||||
|
* @function workdir
|
||||||
|
* @usage
|
||||||
|
-- Store the current working directory in a variable:
|
||||||
|
local wd = file.workdir()
|
||||||
|
-- Set the current working directory to $HOME:
|
||||||
|
file.workdir(environment.get("HOME"))
|
||||||
|
* @tparam[opt] string dir The directory to set as the current working directory.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
file_workdir(lua_State *L)
|
||||||
|
{
|
||||||
|
char *workdir; /* parameter 1 (string) */
|
||||||
|
char *buffer; /* buffer used by getcwd() */
|
||||||
|
char *ret;
|
||||||
|
if (lua_isnoneornil(L, 1)) { /* if first argument was not given... */
|
||||||
|
buffer = malloc(sizeof(char *) * 512);
|
||||||
|
ret = getcwd(buffer, 512);
|
||||||
|
|
||||||
|
if (ret != NULL) {
|
||||||
|
lua_pushstring(L, buffer);
|
||||||
|
free(buffer);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
switch (errno) {
|
||||||
|
case EACCES:
|
||||||
|
return lfail(L, "permission denied");
|
||||||
|
break;
|
||||||
|
case EFAULT:
|
||||||
|
return luaL_error(L, "internal error (EFAULT)");
|
||||||
|
break;
|
||||||
|
case ENOENT:
|
||||||
|
return lfail(L, "working directory is no longer valid");
|
||||||
|
break;
|
||||||
|
case ENOMEM:
|
||||||
|
return luaL_error(L, "insufficient memory");
|
||||||
|
break;
|
||||||
|
case ERANGE:
|
||||||
|
case ENAMETOOLONG: /* glibc */
|
||||||
|
return lfail(L, "pathname too long");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
workdir = (char *)luaL_checkstring(L, 1);
|
||||||
|
|
||||||
|
if (chdir(workdir) == 0) {
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (errno) {
|
||||||
|
case ENOTDIR:
|
||||||
|
return lfail(L, "pathname is not a directory");
|
||||||
|
break;
|
||||||
|
case ENAMETOOLONG:
|
||||||
|
return lfail(L, "pathname too long");
|
||||||
|
break;
|
||||||
|
case ENOENT:
|
||||||
|
return lfail(L, "no such file or directory");
|
||||||
|
break;
|
||||||
|
case ELOOP:
|
||||||
|
return lfail(L, "could not translate pathname; too many symbolic links");
|
||||||
|
break;
|
||||||
|
case EACCES:
|
||||||
|
return lfail(L, "permission denied");
|
||||||
|
break;
|
||||||
|
case EFAULT:
|
||||||
|
return luaL_error(L, "internal error (EFAULT)");
|
||||||
|
break;
|
||||||
|
case EIO:
|
||||||
|
return luaL_error(L, "I/O error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const luaL_Reg filelib[] = {
|
||||||
|
{"basename", file_basename},
|
||||||
|
{"dirname", file_dirname},
|
||||||
|
{"exists", file_exists},
|
||||||
|
{"move", file_move},
|
||||||
|
{"workdir", file_workdir},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
callistoopen_file(lua_State *L)
|
||||||
|
{
|
||||||
|
luaL_newlib(L, filelib);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
9
lfile.h
Normal file
9
lfile.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _LFILE_H_
|
||||||
|
|
||||||
|
#define _LFILE_H_
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
int callistoopen_file(lua_State *);
|
||||||
|
|
||||||
|
#endif
|
||||||
148
ljson.c
Normal file
148
ljson.c
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
/***
|
||||||
|
* Manipulation of JavaScript Object Notation
|
||||||
|
* (JSON) values.
|
||||||
|
*
|
||||||
|
* Implementation from
|
||||||
|
* [lua-cjson](https://github.com/openresty/lua-cjson),
|
||||||
|
* with some additional modifications
|
||||||
|
*
|
||||||
|
* @module json
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include "ljson.h"
|
||||||
|
#include "lcallisto.h"
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Returns the given Lua table encoded
|
||||||
|
* as a JSON object.
|
||||||
|
*
|
||||||
|
* @function encode
|
||||||
|
* @usage
|
||||||
|
local t = {
|
||||||
|
key = "value",
|
||||||
|
x = 4,
|
||||||
|
y = 16.789,
|
||||||
|
t = {
|
||||||
|
hello = "world"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
local j = json.encode(t)
|
||||||
|
* @tparam table t The table to encode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Returns the given JSON object decoded
|
||||||
|
* into a Lua table.
|
||||||
|
*
|
||||||
|
* @function decode
|
||||||
|
* @usage
|
||||||
|
local j = [[
|
||||||
|
{
|
||||||
|
"key": "value",
|
||||||
|
"x": 4,
|
||||||
|
"y": 16.789,
|
||||||
|
"obj": {
|
||||||
|
"hello": "world"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]
|
||||||
|
local t = json.decode(j)
|
||||||
|
* @tparam string j The JSON object to decode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Gets/sets configuration values used when
|
||||||
|
* encoding or decoding JSON objects.
|
||||||
|
*
|
||||||
|
* The *setting* paramater is a string
|
||||||
|
* containing either "encode:" or "decode:"
|
||||||
|
* followed by the name of the setting.
|
||||||
|
* Settings can be found in the
|
||||||
|
* [Settings](#Settings) section.
|
||||||
|
*
|
||||||
|
* @function config
|
||||||
|
* @tparam string setting The setting to get/set.
|
||||||
|
* @param ...
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Creates and returns a new independent copy of the
|
||||||
|
* module, with default settings and a separate
|
||||||
|
* persistent encoding buffer.
|
||||||
|
*
|
||||||
|
* @function new
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* The name of the module, provided for
|
||||||
|
* compatibility with lua-cjson.
|
||||||
|
* Normally this will just be *"json"*.
|
||||||
|
*
|
||||||
|
* @field _NAME
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* The version of lua-cjson
|
||||||
|
* used in the library.
|
||||||
|
*
|
||||||
|
* @field _VERSION
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Configures handling of extremely sparse arrays.
|
||||||
|
*
|
||||||
|
* **Parameters:**
|
||||||
|
*
|
||||||
|
* @setting encode:sparse-array
|
||||||
|
* @tparam integer safe Always use an array when
|
||||||
|
* the max index is larger than this value.
|
||||||
|
* @tparam boolean convert Whether or not to convert
|
||||||
|
* extremely sparse arrays into objects.
|
||||||
|
* @tparam integer ration *0*: always allow sparse;
|
||||||
|
* *1*: never allow sparse; *>1*: use ratio
|
||||||
|
*/
|
||||||
|
/***
|
||||||
|
* Configures the maximum number of nested
|
||||||
|
* arrays/objects allowed when encoding.
|
||||||
|
*
|
||||||
|
* **Parameters:**
|
||||||
|
*
|
||||||
|
* @setting encode:max-depth
|
||||||
|
* @tparam integer depth Max depth allowed.
|
||||||
|
*/
|
||||||
|
/***
|
||||||
|
* Configures the maximum number of nested
|
||||||
|
* arrays/objects allowed when decoding.
|
||||||
|
*
|
||||||
|
* **Parameters:**
|
||||||
|
*
|
||||||
|
* @setting decode:max-depth
|
||||||
|
* @tparam integer depth Max depth allowed.
|
||||||
|
*/
|
||||||
|
/***
|
||||||
|
* Configures the amount of significant
|
||||||
|
* digits returned when encoding numbers.
|
||||||
|
* This can be used to balance accuracy
|
||||||
|
* versus performance.
|
||||||
|
*
|
||||||
|
* **Parameters:**
|
||||||
|
*
|
||||||
|
* @setting encode:number-precision
|
||||||
|
* @tparam integer precision Amount of significant
|
||||||
|
* digits to return in floating-point numbers (must
|
||||||
|
* be between 1 and 14, default 14)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
int luaopen_cjson(lua_State *L);
|
||||||
|
|
||||||
|
int
|
||||||
|
callistoopen_json(lua_State *L)
|
||||||
|
{
|
||||||
|
luaopen_cjson(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
9
ljson.h
Normal file
9
ljson.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _LJSON_H_
|
||||||
|
|
||||||
|
#define _LJSON_H_
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
int callistoopen_json(lua_State *);
|
||||||
|
|
||||||
|
#endif
|
||||||
39
lmath.c
Normal file
39
lmath.c
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
/***
|
||||||
|
* Math and algorithms.
|
||||||
|
* @module math
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include "lmath.h"
|
||||||
|
#include "lcallisto.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
math_lerp(lua_State *L)
|
||||||
|
{
|
||||||
|
double x; /* parameter 1 (number) */
|
||||||
|
double y; /* parameter 2 (number) */
|
||||||
|
double z; /* parameter 3 (number) */
|
||||||
|
|
||||||
|
x = luaL_checknumber(L, 1);
|
||||||
|
y = luaL_checknumber(L, 2);
|
||||||
|
z = luaL_checknumber(L, 3);
|
||||||
|
|
||||||
|
lua_pushnumber(L, x + (y - x) * z);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const luaL_Reg mathlib[] = {
|
||||||
|
{"lerp", math_lerp},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
callistoopen_math(lua_State *L)
|
||||||
|
{
|
||||||
|
newoverride(L, mathlib, CALLISTO_MATHLIBNAME);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
9
lmath.h
Normal file
9
lmath.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _LMATH_H_
|
||||||
|
|
||||||
|
#define _LMATH_H_
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
int callistoopen_math(lua_State *);
|
||||||
|
|
||||||
|
#endif
|
||||||
92
los.c
Normal file
92
los.c
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
/***
|
||||||
|
* Operating system related facilities.
|
||||||
|
* @module os
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
# include <bits/local_lim.h>
|
||||||
|
#else
|
||||||
|
/* assume OpenBSD/NetBSD */
|
||||||
|
# include <sys/syslimits.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include "los.h"
|
||||||
|
#include "lcallisto.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Returns the system hostname.
|
||||||
|
*
|
||||||
|
* @function hostname
|
||||||
|
* @usage local hostname = os.hostname()
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
os_hostname(lua_State *L)
|
||||||
|
{
|
||||||
|
char *buffer;
|
||||||
|
|
||||||
|
buffer = malloc(HOST_NAME_MAX * sizeof(char *));
|
||||||
|
|
||||||
|
gethostname(buffer, HOST_NAME_MAX); /* get hostname */
|
||||||
|
lua_pushstring(L, buffer);
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/***
|
||||||
|
* Waits the specified amount of seconds.
|
||||||
|
*
|
||||||
|
* @function sleep
|
||||||
|
* @usage
|
||||||
|
local minutes = 5
|
||||||
|
os.sleep(minutes * 60) -- 5 minutes
|
||||||
|
* @tparam number seconds The amount of seconds to wait.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
os_sleep(lua_State *L)
|
||||||
|
{
|
||||||
|
/* Implementation from luasocket */
|
||||||
|
double n;
|
||||||
|
struct timespec t, r;
|
||||||
|
|
||||||
|
n = luaL_checknumber(L, 1);
|
||||||
|
|
||||||
|
if (n < 0.0)
|
||||||
|
n = 0.0;
|
||||||
|
if (n > INT_MAX)
|
||||||
|
n = INT_MAX;
|
||||||
|
|
||||||
|
t.tv_sec = (int)n;
|
||||||
|
n -= t.tv_sec;
|
||||||
|
t.tv_nsec = (int)(n * 1000000000);
|
||||||
|
|
||||||
|
if (t.tv_nsec >= 1000000000)
|
||||||
|
t.tv_nsec = 999999999;
|
||||||
|
|
||||||
|
while (nanosleep(&t, &r) != 0) {
|
||||||
|
t.tv_sec = r.tv_sec;
|
||||||
|
t.tv_nsec = r.tv_nsec;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const luaL_Reg oslib[] = {
|
||||||
|
{"hostname", os_hostname},
|
||||||
|
{"sleep", os_sleep},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
callistoopen_os(lua_State *L)
|
||||||
|
{
|
||||||
|
newoverride(L, oslib, CALLISTO_OSLIBNAME);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
9
los.h
Normal file
9
los.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _LOS_H_
|
||||||
|
|
||||||
|
#define _LOS_H_
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
int callistoopen_os(lua_State *);
|
||||||
|
|
||||||
|
#endif
|
||||||
286
lprocess.c
Normal file
286
lprocess.c
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
/***
|
||||||
|
* Processes, signals, and
|
||||||
|
* signal handlers.
|
||||||
|
* @module process
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#ifdef __linux__
|
||||||
|
# include <bsd/string.h>
|
||||||
|
#else
|
||||||
|
/* assume BSD */
|
||||||
|
# include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include "lprocess.h"
|
||||||
|
#include "lcallisto.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define PID_MAX 8 /* rounded to the nearest even number */
|
||||||
|
#define PROCESS_MAX 256
|
||||||
|
|
||||||
|
|
||||||
|
/* signals and signal count */
|
||||||
|
#define SIGC 36
|
||||||
|
static const char *signals[] = {
|
||||||
|
[SIGHUP] = "SIGHUP",
|
||||||
|
[SIGINT] = "SIGINT",
|
||||||
|
[SIGQUIT] = "SIGQUIT",
|
||||||
|
[SIGILL] = "SIGILL",
|
||||||
|
[SIGTRAP] = "SIGTRAP",
|
||||||
|
[SIGABRT] = "SIGABRT",
|
||||||
|
[SIGIOT] = "SIGIOT",
|
||||||
|
[SIGFPE] = "SIGFPE",
|
||||||
|
[SIGKILL] = "SIGKILL",
|
||||||
|
[SIGBUS] = "SIGBUS",
|
||||||
|
[SIGSEGV] = "SIGSEGV",
|
||||||
|
[SIGSYS] = "SIGSYS",
|
||||||
|
[SIGPIPE] = "SIGPIPE",
|
||||||
|
[SIGALRM] = "SIGALRM",
|
||||||
|
[SIGTERM] = "SIGTERM",
|
||||||
|
[SIGURG] = "SIGURG",
|
||||||
|
[SIGSTOP] = "SIGSTOP",
|
||||||
|
[SIGTSTP] = "SIGTSTP",
|
||||||
|
[SIGCONT] = "SIGCONT",
|
||||||
|
[SIGCHLD] = "SIGCHLD",
|
||||||
|
[SIGTTIN] = "SIGTTIN",
|
||||||
|
[SIGTTOU] = "SIGTTOU",
|
||||||
|
#ifdef SIGSTKFL
|
||||||
|
[SIGSTKFL] = "SIGSTKFL",
|
||||||
|
#endif
|
||||||
|
[SIGIO] = "SIGIO",
|
||||||
|
[SIGXCPU] = "SIGXCPU",
|
||||||
|
[SIGXFSZ] = "SIGXFSZ",
|
||||||
|
#ifdef SIGVTALR
|
||||||
|
[SIGVTALR] = "SIGVTALR",
|
||||||
|
#elif defined(SIGVTALRM)
|
||||||
|
[SIGVTALRM] = "SIGVTALRM",
|
||||||
|
#endif
|
||||||
|
[SIGPROF] = "SIGPROF",
|
||||||
|
#ifdef SIGWINC
|
||||||
|
[SIGWINC] = "SIGWINC",
|
||||||
|
#elif defined (SIGWINCH)
|
||||||
|
[SIGWINCH] = "SIGWINCH",
|
||||||
|
#endif
|
||||||
|
#ifdef SIGINFO
|
||||||
|
[SIGINFO] = "SIGINFO",
|
||||||
|
#endif
|
||||||
|
#ifdef SIGPOLL
|
||||||
|
[SIGPOLL] = "SIGPOLL",
|
||||||
|
#endif
|
||||||
|
#ifdef SIGPWR
|
||||||
|
[SIGPWR] = "SIGPWR",
|
||||||
|
#endif
|
||||||
|
[SIGUSR1] = "SIGUSR1",
|
||||||
|
[SIGUSR2] = "SIGUSR2"
|
||||||
|
};
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Returns the ID of the given process.
|
||||||
|
*
|
||||||
|
* Returns nil if the process was not found.
|
||||||
|
*
|
||||||
|
* If the first parameter is nil,
|
||||||
|
* returns the ID of the current running process.
|
||||||
|
*
|
||||||
|
* @function pid
|
||||||
|
* @usage process.pid("init")
|
||||||
|
* @tparam[opt] string process The process to look up.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
process_pid(lua_State *L)
|
||||||
|
{
|
||||||
|
char *process; /* parameter 1 (string) */
|
||||||
|
char *command; /* pidof command buffer */
|
||||||
|
char *buffer; /* pidof reading buffer */
|
||||||
|
long pid; /* pid to return to Lua */
|
||||||
|
size_t pidmax; /* length passed to getline */
|
||||||
|
ssize_t ret; /* return value of getline */
|
||||||
|
FILE *p;
|
||||||
|
|
||||||
|
if (lua_isnoneornil(L, 1)) { /* check if first parameter wasn't given */
|
||||||
|
lua_pushinteger(L, getpid()); /* push current process pid */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
process = (char *)luaL_checkstring(L, 1);
|
||||||
|
command = calloc(1, PROCESS_MAX * sizeof(char *));
|
||||||
|
|
||||||
|
/* construct pidof command */
|
||||||
|
strlcat(command, "pidof -s '", (size_t)PROCESS_MAX);
|
||||||
|
strlcat(command, process, (size_t)PROCESS_MAX);
|
||||||
|
strlcat(command, "'", (size_t)PROCESS_MAX);
|
||||||
|
|
||||||
|
p = popen(command, "r");
|
||||||
|
buffer = malloc(PID_MAX * sizeof(char *));
|
||||||
|
pidmax = (size_t)PID_MAX;
|
||||||
|
ret = getline(&buffer, &pidmax, p); /* get line from pidof */
|
||||||
|
if (ret == -1 || pclose(p) != 0) { /* did getline or pidof fail? */
|
||||||
|
luaL_pushfail(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
pid = strtol(buffer, NULL, 10); /* convert it to an integer */
|
||||||
|
lua_pushinteger(L, pid); /* push pid */
|
||||||
|
free(command);
|
||||||
|
free(buffer);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
strtosig(const char *sig)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 1; i <= SIGC; i++) {
|
||||||
|
if (strcmp(signals[i], sig) == 0) {
|
||||||
|
return i; /* valid signal found */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1; /* invalid signal */
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Returns the given signal as an integer.
|
||||||
|
* This function may return different values
|
||||||
|
* across different operating systems, as
|
||||||
|
* signal constants vary across different Unixes.
|
||||||
|
*
|
||||||
|
* @function signum
|
||||||
|
* @usage local sigkill = process.signum("SIGKILL")
|
||||||
|
* @tparam string signal The signal to look up.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
process_signum(lua_State *L)
|
||||||
|
{
|
||||||
|
int sig;
|
||||||
|
|
||||||
|
if ((sig = strtosig(luaL_checkstring(L, 1))) != -1) /* valid signal? */
|
||||||
|
lua_pushinteger(L, sig); /* return signal */
|
||||||
|
else
|
||||||
|
return lfail(L, "no such signal");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sigsend(lua_State *L, pid_t pid, const char *sigstr)
|
||||||
|
{
|
||||||
|
int ret, sig;
|
||||||
|
|
||||||
|
sig = strtosig(sigstr);
|
||||||
|
if (sig != -1) {
|
||||||
|
ret = kill(pid, sig);
|
||||||
|
} else {
|
||||||
|
lfail(L, "no such signal");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == 0) { /* check for success */
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (errno) {
|
||||||
|
case ESRCH:
|
||||||
|
lfail(L, "no such process");
|
||||||
|
break;
|
||||||
|
case EPERM:
|
||||||
|
lfail(L, "permission denied");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Sends the given signal to the
|
||||||
|
* process with the given PID.
|
||||||
|
*
|
||||||
|
* The *signal* parameter is a string
|
||||||
|
* containing the name of the desired
|
||||||
|
* signal to send..
|
||||||
|
*
|
||||||
|
* @function send
|
||||||
|
* @usage
|
||||||
|
local pid = process.pid("sh")
|
||||||
|
process.send(pid, process.SIGTERM)
|
||||||
|
* @tparam integer pid The PID of the process.
|
||||||
|
* @tparam string signal The signal to send.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
process_send(lua_State *L)
|
||||||
|
{
|
||||||
|
pid_t pid; /* parameter 1 (integer) */
|
||||||
|
const char *sig; /* parameter 2 (string) */
|
||||||
|
|
||||||
|
pid = luaL_checkinteger(L, 1);
|
||||||
|
sig = luaL_checkstring(L, 2);
|
||||||
|
|
||||||
|
if (sigsend(L, pid, sig))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return LFAIL_RET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Kills the process with the given PID.
|
||||||
|
*
|
||||||
|
* Equivalent to `process.send(pid, "SIGKILL")`.
|
||||||
|
*
|
||||||
|
* @function kill
|
||||||
|
* @tparam integer pid The PID of the process.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
process_kill(lua_State *L)
|
||||||
|
{
|
||||||
|
pid_t pid; /* parameter 1 (integer) */
|
||||||
|
|
||||||
|
pid = luaL_checkinteger(L, 1);
|
||||||
|
|
||||||
|
if (sigsend(L, pid, "SIGKILL"))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return LFAIL_RET;
|
||||||
|
}
|
||||||
|
/***
|
||||||
|
* Terminates the process with the given PID.
|
||||||
|
*
|
||||||
|
* Equivalent to `process.send(pid, "SIGTERM")`.
|
||||||
|
*
|
||||||
|
* @function terminate
|
||||||
|
* @tparam integer pid The PID of the process.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
process_terminate(lua_State *L)
|
||||||
|
{
|
||||||
|
pid_t pid; /* parameter 1 (integer) */
|
||||||
|
|
||||||
|
pid = luaL_checkinteger(L, 1);
|
||||||
|
|
||||||
|
if (sigsend(L, pid, "SIGTERM"))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return LFAIL_RET;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const luaL_Reg proclib[] = {
|
||||||
|
{"kill", process_kill},
|
||||||
|
{"pid", process_pid},
|
||||||
|
{"send", process_send},
|
||||||
|
{"signum", process_signum},
|
||||||
|
{"terminate", process_terminate},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
callistoopen_process(lua_State *L)
|
||||||
|
{
|
||||||
|
luaL_newlib(L, proclib);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
9
lprocess.h
Normal file
9
lprocess.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _LPROCESS_H_
|
||||||
|
|
||||||
|
#define _LPROCESS_H_
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
int callistoopen_process(lua_State *);
|
||||||
|
|
||||||
|
#endif
|
||||||
25
lsocket.c
Normal file
25
lsocket.c
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
/***
|
||||||
|
* Networking and socket facilities, using LuaSocket.
|
||||||
|
*
|
||||||
|
* This module only provides the core, which
|
||||||
|
* provides support for the low-level TCP and UDP
|
||||||
|
* transport layers.
|
||||||
|
*
|
||||||
|
* @module socket
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include "lsocket.h"
|
||||||
|
#include "lcallisto.h"
|
||||||
|
|
||||||
|
|
||||||
|
int luaopen_socket_core(lua_State *L);
|
||||||
|
|
||||||
|
int
|
||||||
|
callistoopen_socket(lua_State *L)
|
||||||
|
{
|
||||||
|
luaopen_socket_core(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
9
lsocket.h
Normal file
9
lsocket.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _LSOCKET_H_
|
||||||
|
|
||||||
|
#define _LSOCKET_H_
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
int callistoopen_socket(lua_State *);
|
||||||
|
|
||||||
|
#endif
|
||||||
226
lua-5.4/Makefile
Normal file
226
lua-5.4/Makefile
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
# Makefile for building Lua
|
||||||
|
# See ../doc/readme.html for installation and customization instructions.
|
||||||
|
|
||||||
|
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
|
||||||
|
|
||||||
|
# Your platform. See PLATS for possible values.
|
||||||
|
PLAT= guess
|
||||||
|
|
||||||
|
CC= cc -std=gnu99
|
||||||
|
CFLAGS= -O2 -fpic -Wall -Wextra -DLUA_COMPAT_5_3 $(SYSCFLAGS) $(MYCFLAGS)
|
||||||
|
LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS)
|
||||||
|
LIBS= -lm $(SYSLIBS) $(MYLIBS)
|
||||||
|
|
||||||
|
AR= ar rcu
|
||||||
|
RANLIB= ranlib
|
||||||
|
RM= rm -f
|
||||||
|
UNAME= uname
|
||||||
|
|
||||||
|
SYSCFLAGS=
|
||||||
|
SYSLDFLAGS=
|
||||||
|
SYSLIBS=
|
||||||
|
|
||||||
|
MYCFLAGS=
|
||||||
|
MYLDFLAGS=
|
||||||
|
MYLIBS=
|
||||||
|
MYOBJS=
|
||||||
|
|
||||||
|
# Special flags for compiler modules; -Os reduces code size.
|
||||||
|
CMCFLAGS=
|
||||||
|
|
||||||
|
# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======
|
||||||
|
|
||||||
|
PLATS= guess aix bsd c89 freebsd generic linux linux-readline macosx mingw posix solaris
|
||||||
|
|
||||||
|
LUA_A= liblua.a
|
||||||
|
LUA_SO= liblua.so
|
||||||
|
CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o
|
||||||
|
LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
|
||||||
|
BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)
|
||||||
|
|
||||||
|
LUA_T= lua
|
||||||
|
LUA_O= lua.o
|
||||||
|
|
||||||
|
LUAC_T= luac
|
||||||
|
LUAC_O= luac.o
|
||||||
|
|
||||||
|
ALL_O= $(BASE_O) $(LUA_O) $(LUAC_O)
|
||||||
|
ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T) $(LUA_SO)
|
||||||
|
ALL_A= $(LUA_A)
|
||||||
|
|
||||||
|
# Targets start here.
|
||||||
|
default: $(PLAT)
|
||||||
|
|
||||||
|
all: $(ALL_T)
|
||||||
|
|
||||||
|
o: $(ALL_O)
|
||||||
|
|
||||||
|
a: $(ALL_A)
|
||||||
|
|
||||||
|
$(LUA_A): $(BASE_O)
|
||||||
|
$(AR) $@ $(BASE_O)
|
||||||
|
$(RANLIB) $@
|
||||||
|
|
||||||
|
$(LUA_SO): $(CORE_O) $(LIB_O)
|
||||||
|
$(CC) -shared -ldl -Wl,-soname,$(LUA_SO) -o $@ $? -lm $(MYLDFLAGS)
|
||||||
|
|
||||||
|
$(LUA_T): $(LUA_O) $(LUA_A)
|
||||||
|
$(CC) -o $@ $(LDFLAGS) $(LUA_O) $(LUA_A) $(LIBS)
|
||||||
|
|
||||||
|
$(LUAC_T): $(LUAC_O) $(LUA_A)
|
||||||
|
$(CC) -o $@ $(LDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS)
|
||||||
|
|
||||||
|
test:
|
||||||
|
./$(LUA_T) -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) $(ALL_T) $(ALL_O)
|
||||||
|
|
||||||
|
depend:
|
||||||
|
@$(CC) $(CFLAGS) -MM l*.c
|
||||||
|
|
||||||
|
echo:
|
||||||
|
@echo "PLAT= $(PLAT)"
|
||||||
|
@echo "CC= $(CC)"
|
||||||
|
@echo "CFLAGS= $(CFLAGS)"
|
||||||
|
@echo "LDFLAGS= $(LDFLAGS)"
|
||||||
|
@echo "LIBS= $(LIBS)"
|
||||||
|
@echo "AR= $(AR)"
|
||||||
|
@echo "RANLIB= $(RANLIB)"
|
||||||
|
@echo "RM= $(RM)"
|
||||||
|
@echo "UNAME= $(UNAME)"
|
||||||
|
|
||||||
|
# Convenience targets for popular platforms.
|
||||||
|
ALL= all
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Do 'make PLATFORM' where PLATFORM is one of these:"
|
||||||
|
@echo " $(PLATS)"
|
||||||
|
@echo "See doc/readme.html for complete instructions."
|
||||||
|
|
||||||
|
guess:
|
||||||
|
@echo Guessing `$(UNAME)`
|
||||||
|
@$(MAKE) `$(UNAME)`
|
||||||
|
|
||||||
|
AIX aix:
|
||||||
|
$(MAKE) $(ALL) CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-ldl" SYSLDFLAGS="-brtl -bexpall"
|
||||||
|
|
||||||
|
bsd:
|
||||||
|
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-Wl,-E"
|
||||||
|
|
||||||
|
c89:
|
||||||
|
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_C89" CC="gcc -std=c89"
|
||||||
|
@echo ''
|
||||||
|
@echo '*** C89 does not guarantee 64-bit integers for Lua.'
|
||||||
|
@echo '*** Make sure to compile all external Lua libraries'
|
||||||
|
@echo '*** with LUA_USE_C89 to ensure consistency'
|
||||||
|
@echo ''
|
||||||
|
|
||||||
|
FreeBSD NetBSD OpenBSD freebsd:
|
||||||
|
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE -I/usr/include/edit" SYSLIBS="-Wl,-E -ledit" CC="cc"
|
||||||
|
|
||||||
|
generic: $(ALL)
|
||||||
|
|
||||||
|
Linux linux: linux-noreadline
|
||||||
|
|
||||||
|
linux-noreadline:
|
||||||
|
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -ldl"
|
||||||
|
|
||||||
|
linux-readline:
|
||||||
|
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE" SYSLIBS="-Wl,-E -ldl -lreadline"
|
||||||
|
|
||||||
|
Darwin macos macosx:
|
||||||
|
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_MACOSX -DLUA_USE_READLINE" SYSLIBS="-lreadline"
|
||||||
|
|
||||||
|
mingw:
|
||||||
|
$(MAKE) "LUA_A=lua54.dll" "LUA_T=lua.exe" \
|
||||||
|
"AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \
|
||||||
|
"SYSCFLAGS=-DLUA_BUILD_AS_DLL" "SYSLIBS=" "SYSLDFLAGS=-s" lua.exe
|
||||||
|
$(MAKE) "LUAC_T=luac.exe" luac.exe
|
||||||
|
|
||||||
|
posix:
|
||||||
|
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX"
|
||||||
|
|
||||||
|
SunOS solaris:
|
||||||
|
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN -D_REENTRANT" SYSLIBS="-ldl"
|
||||||
|
|
||||||
|
# Targets that do not create files (not all makes understand .PHONY).
|
||||||
|
.PHONY: all $(PLATS) help test clean default o a depend echo
|
||||||
|
|
||||||
|
# Compiler modules may use special flags.
|
||||||
|
llex.o:
|
||||||
|
$(CC) $(CFLAGS) $(CMCFLAGS) -c llex.c
|
||||||
|
|
||||||
|
lparser.o:
|
||||||
|
$(CC) $(CFLAGS) $(CMCFLAGS) -c lparser.c
|
||||||
|
|
||||||
|
lcode.o:
|
||||||
|
$(CC) $(CFLAGS) $(CMCFLAGS) -c lcode.c
|
||||||
|
|
||||||
|
# DO NOT DELETE
|
||||||
|
|
||||||
|
lapi.o: lapi.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||||
|
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h \
|
||||||
|
ltable.h lundump.h lvm.h
|
||||||
|
lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h
|
||||||
|
lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||||
|
lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
|
||||||
|
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
|
||||||
|
ldo.h lgc.h lstring.h ltable.h lvm.h
|
||||||
|
lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||||
|
lctype.o: lctype.c lprefix.h lctype.h lua.h luaconf.h llimits.h
|
||||||
|
ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||||
|
ldebug.o: ldebug.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||||
|
lobject.h ltm.h lzio.h lmem.h lcode.h llex.h lopcodes.h lparser.h \
|
||||||
|
ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h lvm.h
|
||||||
|
ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||||
|
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \
|
||||||
|
lparser.h lstring.h ltable.h lundump.h lvm.h
|
||||||
|
ldump.o: ldump.c lprefix.h lua.h luaconf.h lobject.h llimits.h lstate.h \
|
||||||
|
ltm.h lzio.h lmem.h lundump.h
|
||||||
|
lfunc.o: lfunc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||||
|
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h
|
||||||
|
lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||||
|
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
|
||||||
|
linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h
|
||||||
|
liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||||
|
llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \
|
||||||
|
lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lgc.h llex.h lparser.h \
|
||||||
|
lstring.h ltable.h
|
||||||
|
lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||||
|
lmem.o: lmem.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||||
|
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h
|
||||||
|
loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||||
|
lobject.o: lobject.c lprefix.h lua.h luaconf.h lctype.h llimits.h \
|
||||||
|
ldebug.h lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h \
|
||||||
|
lvm.h
|
||||||
|
lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h
|
||||||
|
loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||||
|
lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
|
||||||
|
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
|
||||||
|
ldo.h lfunc.h lstring.h lgc.h ltable.h
|
||||||
|
lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
||||||
|
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h \
|
||||||
|
lstring.h ltable.h
|
||||||
|
lstring.o: lstring.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
|
||||||
|
lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h
|
||||||
|
lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||||
|
ltable.o: ltable.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||||
|
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
|
||||||
|
ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||||
|
ltm.o: ltm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||||
|
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
|
||||||
|
lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||||
|
luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h ldebug.h lstate.h \
|
||||||
|
lobject.h llimits.h ltm.h lzio.h lmem.h lopcodes.h lopnames.h lundump.h
|
||||||
|
lundump.o: lundump.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
|
||||||
|
lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h \
|
||||||
|
lundump.h
|
||||||
|
lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
||||||
|
lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
||||||
|
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h \
|
||||||
|
ltable.h lvm.h ljumptab.h
|
||||||
|
lzio.o: lzio.c lprefix.h lua.h luaconf.h llimits.h lmem.h lstate.h \
|
||||||
|
lobject.h ltm.h lzio.h
|
||||||
|
|
||||||
|
# (end of Makefile)
|
||||||
1466
lua-5.4/lapi.c
Normal file
1466
lua-5.4/lapi.c
Normal file
File diff suppressed because it is too large
Load diff
49
lua-5.4/lapi.h
Normal file
49
lua-5.4/lapi.h
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
** $Id: lapi.h $
|
||||||
|
** Auxiliary functions from Lua API
|
||||||
|
** See Copyright Notice in lua.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef lapi_h
|
||||||
|
#define lapi_h
|
||||||
|
|
||||||
|
|
||||||
|
#include "llimits.h"
|
||||||
|
#include "lstate.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Increments 'L->top', checking for stack overflows */
|
||||||
|
#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \
|
||||||
|
"stack overflow");}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** If a call returns too many multiple returns, the callee may not have
|
||||||
|
** stack space to accommodate all results. In this case, this macro
|
||||||
|
** increases its stack space ('L->ci->top').
|
||||||
|
*/
|
||||||
|
#define adjustresults(L,nres) \
|
||||||
|
{ if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; }
|
||||||
|
|
||||||
|
|
||||||
|
/* Ensure the stack has at least 'n' elements */
|
||||||
|
#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \
|
||||||
|
"not enough elements in the stack")
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** To reduce the overhead of returning from C functions, the presence of
|
||||||
|
** to-be-closed variables in these functions is coded in the CallInfo's
|
||||||
|
** field 'nresults', in a way that functions with no to-be-closed variables
|
||||||
|
** with zero, one, or "all" wanted results have no overhead. Functions
|
||||||
|
** with other number of wanted results, as well as functions with
|
||||||
|
** variables to be closed, have an extra check.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
|
||||||
|
|
||||||
|
/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
|
||||||
|
#define codeNresults(n) (-(n) - 3)
|
||||||
|
#define decodeNresults(n) (-(n) - 3)
|
||||||
|
|
||||||
|
#endif
|
||||||
1106
lua-5.4/lauxlib.c
Normal file
1106
lua-5.4/lauxlib.c
Normal file
File diff suppressed because it is too large
Load diff
301
lua-5.4/lauxlib.h
Normal file
301
lua-5.4/lauxlib.h
Normal file
|
|
@ -0,0 +1,301 @@
|
||||||
|
/*
|
||||||
|
** $Id: lauxlib.h $
|
||||||
|
** Auxiliary functions for building Lua libraries
|
||||||
|
** See Copyright Notice in lua.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef lauxlib_h
|
||||||
|
#define lauxlib_h
|
||||||
|
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "luaconf.h"
|
||||||
|
#include "lua.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* global table */
|
||||||
|
#define LUA_GNAME "_G"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct luaL_Buffer luaL_Buffer;
|
||||||
|
|
||||||
|
|
||||||
|
/* extra error code for 'luaL_loadfilex' */
|
||||||
|
#define LUA_ERRFILE (LUA_ERRERR+1)
|
||||||
|
|
||||||
|
|
||||||
|
/* key, in the registry, for table of loaded modules */
|
||||||
|
#define LUA_LOADED_TABLE "_LOADED"
|
||||||
|
|
||||||
|
|
||||||
|
/* key, in the registry, for table of preloaded loaders */
|
||||||
|
#define LUA_PRELOAD_TABLE "_PRELOAD"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct luaL_Reg {
|
||||||
|
const char *name;
|
||||||
|
lua_CFunction func;
|
||||||
|
} luaL_Reg;
|
||||||
|
|
||||||
|
|
||||||
|
#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number))
|
||||||
|
|
||||||
|
LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz);
|
||||||
|
#define luaL_checkversion(L) \
|
||||||
|
luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES)
|
||||||
|
|
||||||
|
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
|
||||||
|
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
|
||||||
|
LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
|
||||||
|
LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
|
||||||
|
LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname);
|
||||||
|
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
|
||||||
|
size_t *l);
|
||||||
|
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
|
||||||
|
const char *def, size_t *l);
|
||||||
|
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg);
|
||||||
|
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def);
|
||||||
|
|
||||||
|
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg);
|
||||||
|
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg,
|
||||||
|
lua_Integer def);
|
||||||
|
|
||||||
|
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
|
||||||
|
LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t);
|
||||||
|
LUALIB_API void (luaL_checkany) (lua_State *L, int arg);
|
||||||
|
|
||||||
|
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
|
||||||
|
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
|
||||||
|
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
|
||||||
|
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
|
||||||
|
|
||||||
|
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
|
||||||
|
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
|
||||||
|
|
||||||
|
LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
|
||||||
|
const char *const lst[]);
|
||||||
|
|
||||||
|
LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
|
||||||
|
LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
|
||||||
|
|
||||||
|
|
||||||
|
/* predefined references */
|
||||||
|
#define LUA_NOREF (-2)
|
||||||
|
#define LUA_REFNIL (-1)
|
||||||
|
|
||||||
|
LUALIB_API int (luaL_ref) (lua_State *L, int t);
|
||||||
|
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
|
||||||
|
|
||||||
|
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
|
||||||
|
const char *mode);
|
||||||
|
|
||||||
|
#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
|
||||||
|
|
||||||
|
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
|
||||||
|
const char *name, const char *mode);
|
||||||
|
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
|
||||||
|
|
||||||
|
LUALIB_API lua_State *(luaL_newstate) (void);
|
||||||
|
|
||||||
|
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
|
||||||
|
|
||||||
|
LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s,
|
||||||
|
const char *p, const char *r);
|
||||||
|
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s,
|
||||||
|
const char *p, const char *r);
|
||||||
|
|
||||||
|
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
|
||||||
|
|
||||||
|
LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname);
|
||||||
|
|
||||||
|
LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1,
|
||||||
|
const char *msg, int level);
|
||||||
|
|
||||||
|
LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
|
||||||
|
lua_CFunction openf, int glb);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** ===============================================================
|
||||||
|
** some useful macros
|
||||||
|
** ===============================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#define luaL_newlibtable(L,l) \
|
||||||
|
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
|
||||||
|
|
||||||
|
#define luaL_newlib(L,l) \
|
||||||
|
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
|
||||||
|
|
||||||
|
#define luaL_argcheck(L, cond,arg,extramsg) \
|
||||||
|
((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg))))
|
||||||
|
|
||||||
|
#define luaL_argexpected(L,cond,arg,tname) \
|
||||||
|
((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname))))
|
||||||
|
|
||||||
|
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
|
||||||
|
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
||||||
|
|
||||||
|
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
|
||||||
|
|
||||||
|
#define luaL_dofile(L, fn) \
|
||||||
|
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||||
|
|
||||||
|
#define luaL_dostring(L, s) \
|
||||||
|
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||||
|
|
||||||
|
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
|
||||||
|
|
||||||
|
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
|
||||||
|
|
||||||
|
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Perform arithmetic operations on lua_Integer values with wrap-around
|
||||||
|
** semantics, as the Lua core does.
|
||||||
|
*/
|
||||||
|
#define luaL_intop(op,v1,v2) \
|
||||||
|
((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2)))
|
||||||
|
|
||||||
|
|
||||||
|
/* push the value used to represent failure/error */
|
||||||
|
#define luaL_pushfail(L) lua_pushnil(L)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Internal assertions for in-house debugging
|
||||||
|
*/
|
||||||
|
#if !defined(lua_assert)
|
||||||
|
|
||||||
|
#if defined LUAI_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define lua_assert(c) assert(c)
|
||||||
|
#else
|
||||||
|
#define lua_assert(c) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** {======================================================
|
||||||
|
** Generic Buffer manipulation
|
||||||
|
** =======================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct luaL_Buffer {
|
||||||
|
char *b; /* buffer address */
|
||||||
|
size_t size; /* buffer size */
|
||||||
|
size_t n; /* number of characters in buffer */
|
||||||
|
lua_State *L;
|
||||||
|
union {
|
||||||
|
LUAI_MAXALIGN; /* ensure maximum alignment for buffer */
|
||||||
|
char b[LUAL_BUFFERSIZE]; /* initial buffer */
|
||||||
|
} init;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define luaL_bufflen(bf) ((bf)->n)
|
||||||
|
#define luaL_buffaddr(bf) ((bf)->b)
|
||||||
|
|
||||||
|
|
||||||
|
#define luaL_addchar(B,c) \
|
||||||
|
((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \
|
||||||
|
((B)->b[(B)->n++] = (c)))
|
||||||
|
|
||||||
|
#define luaL_addsize(B,s) ((B)->n += (s))
|
||||||
|
|
||||||
|
#define luaL_buffsub(B,s) ((B)->n -= (s))
|
||||||
|
|
||||||
|
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
|
||||||
|
LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
|
||||||
|
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
|
||||||
|
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
|
||||||
|
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
|
||||||
|
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
|
||||||
|
LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
|
||||||
|
LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
|
||||||
|
|
||||||
|
#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
|
||||||
|
|
||||||
|
/* }====================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** {======================================================
|
||||||
|
** File handles for IO library
|
||||||
|
** =======================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and
|
||||||
|
** initial structure 'luaL_Stream' (it may contain other fields
|
||||||
|
** after that initial structure).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LUA_FILEHANDLE "FILE*"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct luaL_Stream {
|
||||||
|
FILE *f; /* stream (NULL for incompletely created streams) */
|
||||||
|
lua_CFunction closef; /* to close stream (NULL for closed streams) */
|
||||||
|
} luaL_Stream;
|
||||||
|
|
||||||
|
/* }====================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** {==================================================================
|
||||||
|
** "Abstraction Layer" for basic report of messages and errors
|
||||||
|
** ===================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* print a string */
|
||||||
|
#if !defined(lua_writestring)
|
||||||
|
#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* print a newline and flush the output */
|
||||||
|
#if !defined(lua_writeline)
|
||||||
|
#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* print an error message */
|
||||||
|
#if !defined(lua_writestringerror)
|
||||||
|
#define lua_writestringerror(s,p) \
|
||||||
|
(fprintf(stderr, (s), (p)), fflush(stderr))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* }================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** {============================================================
|
||||||
|
** Compatibility with deprecated conversions
|
||||||
|
** =============================================================
|
||||||
|
*/
|
||||||
|
#if defined(LUA_COMPAT_APIINTCASTS)
|
||||||
|
|
||||||
|
#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a))
|
||||||
|
#define luaL_optunsigned(L,a,d) \
|
||||||
|
((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d)))
|
||||||
|
|
||||||
|
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
|
||||||
|
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
|
||||||
|
|
||||||
|
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
|
||||||
|
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
|
||||||
|
|
||||||
|
#endif
|
||||||
|
/* }============================================================ */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
549
lua-5.4/lbaselib.c
Normal file
549
lua-5.4/lbaselib.c
Normal file
|
|
@ -0,0 +1,549 @@
|
||||||
|
/*
|
||||||
|
** $Id: lbaselib.c $
|
||||||
|
** Basic library
|
||||||
|
** See Copyright Notice in lua.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define lbaselib_c
|
||||||
|
#define LUA_LIB
|
||||||
|
|
||||||
|
#include "lprefix.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "lua.h"
|
||||||
|
|
||||||
|
#include "lauxlib.h"
|
||||||
|
#include "lualib.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_print (lua_State *L) {
|
||||||
|
int n = lua_gettop(L); /* number of arguments */
|
||||||
|
int i;
|
||||||
|
for (i = 1; i <= n; i++) { /* for each argument */
|
||||||
|
size_t l;
|
||||||
|
const char *s = luaL_tolstring(L, i, &l); /* convert it to string */
|
||||||
|
if (i > 1) /* not the first element? */
|
||||||
|
lua_writestring("\t", 1); /* add a tab before it */
|
||||||
|
lua_writestring(s, l); /* print it */
|
||||||
|
lua_pop(L, 1); /* pop result */
|
||||||
|
}
|
||||||
|
lua_writeline();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Creates a warning with all given arguments.
|
||||||
|
** Check first for errors; otherwise an error may interrupt
|
||||||
|
** the composition of a warning, leaving it unfinished.
|
||||||
|
*/
|
||||||
|
static int luaB_warn (lua_State *L) {
|
||||||
|
int n = lua_gettop(L); /* number of arguments */
|
||||||
|
int i;
|
||||||
|
luaL_checkstring(L, 1); /* at least one argument */
|
||||||
|
for (i = 2; i <= n; i++)
|
||||||
|
luaL_checkstring(L, i); /* make sure all arguments are strings */
|
||||||
|
for (i = 1; i < n; i++) /* compose warning */
|
||||||
|
lua_warning(L, lua_tostring(L, i), 1);
|
||||||
|
lua_warning(L, lua_tostring(L, n), 0); /* close warning */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define SPACECHARS " \f\n\r\t\v"
|
||||||
|
|
||||||
|
static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
|
||||||
|
lua_Unsigned n = 0;
|
||||||
|
int neg = 0;
|
||||||
|
s += strspn(s, SPACECHARS); /* skip initial spaces */
|
||||||
|
if (*s == '-') { s++; neg = 1; } /* handle sign */
|
||||||
|
else if (*s == '+') s++;
|
||||||
|
if (!isalnum((unsigned char)*s)) /* no digit? */
|
||||||
|
return NULL;
|
||||||
|
do {
|
||||||
|
int digit = (isdigit((unsigned char)*s)) ? *s - '0'
|
||||||
|
: (toupper((unsigned char)*s) - 'A') + 10;
|
||||||
|
if (digit >= base) return NULL; /* invalid numeral */
|
||||||
|
n = n * base + digit;
|
||||||
|
s++;
|
||||||
|
} while (isalnum((unsigned char)*s));
|
||||||
|
s += strspn(s, SPACECHARS); /* skip trailing spaces */
|
||||||
|
*pn = (lua_Integer)((neg) ? (0u - n) : n);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_tonumber (lua_State *L) {
|
||||||
|
if (lua_isnoneornil(L, 2)) { /* standard conversion? */
|
||||||
|
if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */
|
||||||
|
lua_settop(L, 1); /* yes; return it */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
size_t l;
|
||||||
|
const char *s = lua_tolstring(L, 1, &l);
|
||||||
|
if (s != NULL && lua_stringtonumber(L, s) == l + 1)
|
||||||
|
return 1; /* successful conversion to number */
|
||||||
|
/* else not a number */
|
||||||
|
luaL_checkany(L, 1); /* (but there must be some parameter) */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
size_t l;
|
||||||
|
const char *s;
|
||||||
|
lua_Integer n = 0; /* to avoid warnings */
|
||||||
|
lua_Integer base = luaL_checkinteger(L, 2);
|
||||||
|
luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */
|
||||||
|
s = lua_tolstring(L, 1, &l);
|
||||||
|
luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
|
||||||
|
if (b_str2int(s, (int)base, &n) == s + l) {
|
||||||
|
lua_pushinteger(L, n);
|
||||||
|
return 1;
|
||||||
|
} /* else not a number */
|
||||||
|
} /* else not a number */
|
||||||
|
luaL_pushfail(L); /* not a number */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_error (lua_State *L) {
|
||||||
|
int level = (int)luaL_optinteger(L, 2, 1);
|
||||||
|
lua_settop(L, 1);
|
||||||
|
if (lua_type(L, 1) == LUA_TSTRING && level > 0) {
|
||||||
|
luaL_where(L, level); /* add extra information */
|
||||||
|
lua_pushvalue(L, 1);
|
||||||
|
lua_concat(L, 2);
|
||||||
|
}
|
||||||
|
return lua_error(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_getmetatable (lua_State *L) {
|
||||||
|
luaL_checkany(L, 1);
|
||||||
|
if (!lua_getmetatable(L, 1)) {
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1; /* no metatable */
|
||||||
|
}
|
||||||
|
luaL_getmetafield(L, 1, "__metatable");
|
||||||
|
return 1; /* returns either __metatable field (if present) or metatable */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_setmetatable (lua_State *L) {
|
||||||
|
int t = lua_type(L, 2);
|
||||||
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
|
||||||
|
if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL))
|
||||||
|
return luaL_error(L, "cannot change a protected metatable");
|
||||||
|
lua_settop(L, 2);
|
||||||
|
lua_setmetatable(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_rawequal (lua_State *L) {
|
||||||
|
luaL_checkany(L, 1);
|
||||||
|
luaL_checkany(L, 2);
|
||||||
|
lua_pushboolean(L, lua_rawequal(L, 1, 2));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_rawlen (lua_State *L) {
|
||||||
|
int t = lua_type(L, 1);
|
||||||
|
luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1,
|
||||||
|
"table or string");
|
||||||
|
lua_pushinteger(L, lua_rawlen(L, 1));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_rawget (lua_State *L) {
|
||||||
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
luaL_checkany(L, 2);
|
||||||
|
lua_settop(L, 2);
|
||||||
|
lua_rawget(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int luaB_rawset (lua_State *L) {
|
||||||
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
luaL_checkany(L, 2);
|
||||||
|
luaL_checkany(L, 3);
|
||||||
|
lua_settop(L, 3);
|
||||||
|
lua_rawset(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int pushmode (lua_State *L, int oldmode) {
|
||||||
|
if (oldmode == -1)
|
||||||
|
luaL_pushfail(L); /* invalid call to 'lua_gc' */
|
||||||
|
else
|
||||||
|
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
|
||||||
|
: "generational");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** check whether call to 'lua_gc' was valid (not inside a finalizer)
|
||||||
|
*/
|
||||||
|
#define checkvalres(res) { if (res == -1) break; }
|
||||||
|
|
||||||
|
static int luaB_collectgarbage (lua_State *L) {
|
||||||
|
static const char *const opts[] = {"stop", "restart", "collect",
|
||||||
|
"count", "step", "setpause", "setstepmul",
|
||||||
|
"isrunning", "generational", "incremental", NULL};
|
||||||
|
static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
|
||||||
|
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
|
||||||
|
LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC};
|
||||||
|
int o = optsnum[luaL_checkoption(L, 1, "collect", opts)];
|
||||||
|
switch (o) {
|
||||||
|
case LUA_GCCOUNT: {
|
||||||
|
int k = lua_gc(L, o);
|
||||||
|
int b = lua_gc(L, LUA_GCCOUNTB);
|
||||||
|
checkvalres(k);
|
||||||
|
lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case LUA_GCSTEP: {
|
||||||
|
int step = (int)luaL_optinteger(L, 2, 0);
|
||||||
|
int res = lua_gc(L, o, step);
|
||||||
|
checkvalres(res);
|
||||||
|
lua_pushboolean(L, res);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case LUA_GCSETPAUSE:
|
||||||
|
case LUA_GCSETSTEPMUL: {
|
||||||
|
int p = (int)luaL_optinteger(L, 2, 0);
|
||||||
|
int previous = lua_gc(L, o, p);
|
||||||
|
checkvalres(previous);
|
||||||
|
lua_pushinteger(L, previous);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case LUA_GCISRUNNING: {
|
||||||
|
int res = lua_gc(L, o);
|
||||||
|
checkvalres(res);
|
||||||
|
lua_pushboolean(L, res);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case LUA_GCGEN: {
|
||||||
|
int minormul = (int)luaL_optinteger(L, 2, 0);
|
||||||
|
int majormul = (int)luaL_optinteger(L, 3, 0);
|
||||||
|
return pushmode(L, lua_gc(L, o, minormul, majormul));
|
||||||
|
}
|
||||||
|
case LUA_GCINC: {
|
||||||
|
int pause = (int)luaL_optinteger(L, 2, 0);
|
||||||
|
int stepmul = (int)luaL_optinteger(L, 3, 0);
|
||||||
|
int stepsize = (int)luaL_optinteger(L, 4, 0);
|
||||||
|
return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize));
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
int res = lua_gc(L, o);
|
||||||
|
checkvalres(res);
|
||||||
|
lua_pushinteger(L, res);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
luaL_pushfail(L); /* invalid call (inside a finalizer) */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_type (lua_State *L) {
|
||||||
|
int t = lua_type(L, 1);
|
||||||
|
luaL_argcheck(L, t != LUA_TNONE, 1, "value expected");
|
||||||
|
lua_pushstring(L, lua_typename(L, t));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_next (lua_State *L) {
|
||||||
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
|
||||||
|
if (lua_next(L, 1))
|
||||||
|
return 2;
|
||||||
|
else {
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int pairscont (lua_State *L, int status, lua_KContext k) {
|
||||||
|
(void)L; (void)status; (void)k; /* unused */
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int luaB_pairs (lua_State *L) {
|
||||||
|
luaL_checkany(L, 1);
|
||||||
|
if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
|
||||||
|
lua_pushcfunction(L, luaB_next); /* will return generator, */
|
||||||
|
lua_pushvalue(L, 1); /* state, */
|
||||||
|
lua_pushnil(L); /* and initial value */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lua_pushvalue(L, 1); /* argument 'self' to metamethod */
|
||||||
|
lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */
|
||||||
|
}
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Traversal function for 'ipairs'
|
||||||
|
*/
|
||||||
|
static int ipairsaux (lua_State *L) {
|
||||||
|
lua_Integer i = luaL_checkinteger(L, 2);
|
||||||
|
i = luaL_intop(+, i, 1);
|
||||||
|
lua_pushinteger(L, i);
|
||||||
|
return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** 'ipairs' function. Returns 'ipairsaux', given "table", 0.
|
||||||
|
** (The given "table" may not be a table.)
|
||||||
|
*/
|
||||||
|
static int luaB_ipairs (lua_State *L) {
|
||||||
|
luaL_checkany(L, 1);
|
||||||
|
lua_pushcfunction(L, ipairsaux); /* iteration function */
|
||||||
|
lua_pushvalue(L, 1); /* state */
|
||||||
|
lua_pushinteger(L, 0); /* initial value */
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int load_aux (lua_State *L, int status, int envidx) {
|
||||||
|
if (l_likely(status == LUA_OK)) {
|
||||||
|
if (envidx != 0) { /* 'env' parameter? */
|
||||||
|
lua_pushvalue(L, envidx); /* environment for loaded function */
|
||||||
|
if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
|
||||||
|
lua_pop(L, 1); /* remove 'env' if not used by previous call */
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else { /* error (message is on top of the stack) */
|
||||||
|
luaL_pushfail(L);
|
||||||
|
lua_insert(L, -2); /* put before error message */
|
||||||
|
return 2; /* return fail plus error message */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_loadfile (lua_State *L) {
|
||||||
|
const char *fname = luaL_optstring(L, 1, NULL);
|
||||||
|
const char *mode = luaL_optstring(L, 2, NULL);
|
||||||
|
int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */
|
||||||
|
int status = luaL_loadfilex(L, fname, mode);
|
||||||
|
return load_aux(L, status, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** {======================================================
|
||||||
|
** Generic Read function
|
||||||
|
** =======================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** reserved slot, above all arguments, to hold a copy of the returned
|
||||||
|
** string to avoid it being collected while parsed. 'load' has four
|
||||||
|
** optional arguments (chunk, source name, mode, and environment).
|
||||||
|
*/
|
||||||
|
#define RESERVEDSLOT 5
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Reader for generic 'load' function: 'lua_load' uses the
|
||||||
|
** stack for internal stuff, so the reader cannot change the
|
||||||
|
** stack top. Instead, it keeps its resulting string in a
|
||||||
|
** reserved slot inside the stack.
|
||||||
|
*/
|
||||||
|
static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
|
||||||
|
(void)(ud); /* not used */
|
||||||
|
luaL_checkstack(L, 2, "too many nested functions");
|
||||||
|
lua_pushvalue(L, 1); /* get function */
|
||||||
|
lua_call(L, 0, 1); /* call it */
|
||||||
|
if (lua_isnil(L, -1)) {
|
||||||
|
lua_pop(L, 1); /* pop result */
|
||||||
|
*size = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else if (l_unlikely(!lua_isstring(L, -1)))
|
||||||
|
luaL_error(L, "reader function must return a string");
|
||||||
|
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
|
||||||
|
return lua_tolstring(L, RESERVEDSLOT, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_load (lua_State *L) {
|
||||||
|
int status;
|
||||||
|
size_t l;
|
||||||
|
const char *s = lua_tolstring(L, 1, &l);
|
||||||
|
const char *mode = luaL_optstring(L, 3, "bt");
|
||||||
|
int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */
|
||||||
|
if (s != NULL) { /* loading a string? */
|
||||||
|
const char *chunkname = luaL_optstring(L, 2, s);
|
||||||
|
status = luaL_loadbufferx(L, s, l, chunkname, mode);
|
||||||
|
}
|
||||||
|
else { /* loading from a reader function */
|
||||||
|
const char *chunkname = luaL_optstring(L, 2, "=(load)");
|
||||||
|
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||||
|
lua_settop(L, RESERVEDSLOT); /* create reserved slot */
|
||||||
|
status = lua_load(L, generic_reader, NULL, chunkname, mode);
|
||||||
|
}
|
||||||
|
return load_aux(L, status, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* }====================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
|
||||||
|
(void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */
|
||||||
|
return lua_gettop(L) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_dofile (lua_State *L) {
|
||||||
|
const char *fname = luaL_optstring(L, 1, NULL);
|
||||||
|
lua_settop(L, 1);
|
||||||
|
if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK))
|
||||||
|
return lua_error(L);
|
||||||
|
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
|
||||||
|
return dofilecont(L, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_assert (lua_State *L) {
|
||||||
|
if (l_likely(lua_toboolean(L, 1))) /* condition is true? */
|
||||||
|
return lua_gettop(L); /* return all arguments */
|
||||||
|
else { /* error */
|
||||||
|
luaL_checkany(L, 1); /* there must be a condition */
|
||||||
|
lua_remove(L, 1); /* remove it */
|
||||||
|
lua_pushliteral(L, "assertion failed!"); /* default message */
|
||||||
|
lua_settop(L, 1); /* leave only message (default if no other one) */
|
||||||
|
return luaB_error(L); /* call 'error' */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_select (lua_State *L) {
|
||||||
|
int n = lua_gettop(L);
|
||||||
|
if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {
|
||||||
|
lua_pushinteger(L, n-1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lua_Integer i = luaL_checkinteger(L, 1);
|
||||||
|
if (i < 0) i = n + i;
|
||||||
|
else if (i > n) i = n;
|
||||||
|
luaL_argcheck(L, 1 <= i, 1, "index out of range");
|
||||||
|
return n - (int)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Continuation function for 'pcall' and 'xpcall'. Both functions
|
||||||
|
** already pushed a 'true' before doing the call, so in case of success
|
||||||
|
** 'finishpcall' only has to return everything in the stack minus
|
||||||
|
** 'extra' values (where 'extra' is exactly the number of items to be
|
||||||
|
** ignored).
|
||||||
|
*/
|
||||||
|
static int finishpcall (lua_State *L, int status, lua_KContext extra) {
|
||||||
|
if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */
|
||||||
|
lua_pushboolean(L, 0); /* first result (false) */
|
||||||
|
lua_pushvalue(L, -2); /* error message */
|
||||||
|
return 2; /* return false, msg */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return lua_gettop(L) - (int)extra; /* return all results */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_pcall (lua_State *L) {
|
||||||
|
int status;
|
||||||
|
luaL_checkany(L, 1);
|
||||||
|
lua_pushboolean(L, 1); /* first result if no errors */
|
||||||
|
lua_insert(L, 1); /* put it in place */
|
||||||
|
status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall);
|
||||||
|
return finishpcall(L, status, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Do a protected call with error handling. After 'lua_rotate', the
|
||||||
|
** stack will have <f, err, true, f, [args...]>; so, the function passes
|
||||||
|
** 2 to 'finishpcall' to skip the 2 first values when returning results.
|
||||||
|
*/
|
||||||
|
static int luaB_xpcall (lua_State *L) {
|
||||||
|
int status;
|
||||||
|
int n = lua_gettop(L);
|
||||||
|
luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */
|
||||||
|
lua_pushboolean(L, 1); /* first result */
|
||||||
|
lua_pushvalue(L, 1); /* function */
|
||||||
|
lua_rotate(L, 3, 2); /* move them below function's arguments */
|
||||||
|
status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall);
|
||||||
|
return finishpcall(L, status, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_tostring (lua_State *L) {
|
||||||
|
luaL_checkany(L, 1);
|
||||||
|
luaL_tolstring(L, 1, NULL);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const luaL_Reg base_funcs[] = {
|
||||||
|
{"assert", luaB_assert},
|
||||||
|
{"collectgarbage", luaB_collectgarbage},
|
||||||
|
{"dofile", luaB_dofile},
|
||||||
|
{"error", luaB_error},
|
||||||
|
{"getmetatable", luaB_getmetatable},
|
||||||
|
{"ipairs", luaB_ipairs},
|
||||||
|
{"loadfile", luaB_loadfile},
|
||||||
|
{"load", luaB_load},
|
||||||
|
{"next", luaB_next},
|
||||||
|
{"pairs", luaB_pairs},
|
||||||
|
{"pcall", luaB_pcall},
|
||||||
|
{"print", luaB_print},
|
||||||
|
{"warn", luaB_warn},
|
||||||
|
{"rawequal", luaB_rawequal},
|
||||||
|
{"rawlen", luaB_rawlen},
|
||||||
|
{"rawget", luaB_rawget},
|
||||||
|
{"rawset", luaB_rawset},
|
||||||
|
{"select", luaB_select},
|
||||||
|
{"setmetatable", luaB_setmetatable},
|
||||||
|
{"tonumber", luaB_tonumber},
|
||||||
|
{"tostring", luaB_tostring},
|
||||||
|
{"type", luaB_type},
|
||||||
|
{"xpcall", luaB_xpcall},
|
||||||
|
/* placeholders */
|
||||||
|
{LUA_GNAME, NULL},
|
||||||
|
{"_VERSION", NULL},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
LUAMOD_API int luaopen_base (lua_State *L) {
|
||||||
|
/* open lib into global table */
|
||||||
|
lua_pushglobaltable(L);
|
||||||
|
luaL_setfuncs(L, base_funcs, 0);
|
||||||
|
/* set global _G */
|
||||||
|
lua_pushvalue(L, -1);
|
||||||
|
lua_setfield(L, -2, LUA_GNAME);
|
||||||
|
/* set global _VERSION */
|
||||||
|
lua_pushliteral(L, LUA_VERSION);
|
||||||
|
lua_setfield(L, -2, "_VERSION");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
1832
lua-5.4/lcode.c
Normal file
1832
lua-5.4/lcode.c
Normal file
File diff suppressed because it is too large
Load diff
104
lua-5.4/lcode.h
Normal file
104
lua-5.4/lcode.h
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
** $Id: lcode.h $
|
||||||
|
** Code generator for Lua
|
||||||
|
** See Copyright Notice in lua.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef lcode_h
|
||||||
|
#define lcode_h
|
||||||
|
|
||||||
|
#include "llex.h"
|
||||||
|
#include "lobject.h"
|
||||||
|
#include "lopcodes.h"
|
||||||
|
#include "lparser.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Marks the end of a patch list. It is an invalid value both as an absolute
|
||||||
|
** address, and as a list link (would link an element to itself).
|
||||||
|
*/
|
||||||
|
#define NO_JUMP (-1)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** grep "ORDER OPR" if you change these enums (ORDER OP)
|
||||||
|
*/
|
||||||
|
typedef enum BinOpr {
|
||||||
|
/* arithmetic operators */
|
||||||
|
OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW,
|
||||||
|
OPR_DIV, OPR_IDIV,
|
||||||
|
/* bitwise operators */
|
||||||
|
OPR_BAND, OPR_BOR, OPR_BXOR,
|
||||||
|
OPR_SHL, OPR_SHR,
|
||||||
|
/* string operator */
|
||||||
|
OPR_CONCAT,
|
||||||
|
/* comparison operators */
|
||||||
|
OPR_EQ, OPR_LT, OPR_LE,
|
||||||
|
OPR_NE, OPR_GT, OPR_GE,
|
||||||
|
/* logical operators */
|
||||||
|
OPR_AND, OPR_OR,
|
||||||
|
OPR_NOBINOPR
|
||||||
|
} BinOpr;
|
||||||
|
|
||||||
|
|
||||||
|
/* true if operation is foldable (that is, it is arithmetic or bitwise) */
|
||||||
|
#define foldbinop(op) ((op) <= OPR_SHR)
|
||||||
|
|
||||||
|
|
||||||
|
#define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0)
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
|
||||||
|
|
||||||
|
|
||||||
|
/* get (pointer to) instruction of given 'expdesc' */
|
||||||
|
#define getinstruction(fs,e) ((fs)->f->code[(e)->u.info])
|
||||||
|
|
||||||
|
|
||||||
|
#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET)
|
||||||
|
|
||||||
|
#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t)
|
||||||
|
|
||||||
|
LUAI_FUNC int luaK_code (FuncState *fs, Instruction i);
|
||||||
|
LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
|
||||||
|
LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx);
|
||||||
|
LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
|
||||||
|
int B, int C, int k);
|
||||||
|
LUAI_FUNC int luaK_isKint (expdesc *e);
|
||||||
|
LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
|
||||||
|
LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
|
||||||
|
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
|
||||||
|
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
|
||||||
|
LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
|
||||||
|
LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n);
|
||||||
|
LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
|
||||||
|
LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
|
||||||
|
LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
|
||||||
|
LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
|
||||||
|
LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
|
||||||
|
LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);
|
||||||
|
LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
|
||||||
|
LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
|
||||||
|
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
|
||||||
|
LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e);
|
||||||
|
LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
|
||||||
|
LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
|
||||||
|
LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
|
||||||
|
LUAI_FUNC int luaK_jump (FuncState *fs);
|
||||||
|
LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
|
||||||
|
LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
|
||||||
|
LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
|
||||||
|
LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
|
||||||
|
LUAI_FUNC int luaK_getlabel (FuncState *fs);
|
||||||
|
LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);
|
||||||
|
LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
|
||||||
|
LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1,
|
||||||
|
expdesc *v2, int line);
|
||||||
|
LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc,
|
||||||
|
int ra, int asize, int hsize);
|
||||||
|
LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
|
||||||
|
LUAI_FUNC void luaK_finish (FuncState *fs);
|
||||||
|
LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
210
lua-5.4/lcorolib.c
Normal file
210
lua-5.4/lcorolib.c
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
/*
|
||||||
|
** $Id: lcorolib.c $
|
||||||
|
** Coroutine Library
|
||||||
|
** See Copyright Notice in lua.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define lcorolib_c
|
||||||
|
#define LUA_LIB
|
||||||
|
|
||||||
|
#include "lprefix.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "lua.h"
|
||||||
|
|
||||||
|
#include "lauxlib.h"
|
||||||
|
#include "lualib.h"
|
||||||
|
|
||||||
|
|
||||||
|
static lua_State *getco (lua_State *L) {
|
||||||
|
lua_State *co = lua_tothread(L, 1);
|
||||||
|
luaL_argexpected(L, co, 1, "thread");
|
||||||
|
return co;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Resumes a coroutine. Returns the number of results for non-error
|
||||||
|
** cases or -1 for errors.
|
||||||
|
*/
|
||||||
|
static int auxresume (lua_State *L, lua_State *co, int narg) {
|
||||||
|
int status, nres;
|
||||||
|
if (l_unlikely(!lua_checkstack(co, narg))) {
|
||||||
|
lua_pushliteral(L, "too many arguments to resume");
|
||||||
|
return -1; /* error flag */
|
||||||
|
}
|
||||||
|
lua_xmove(L, co, narg);
|
||||||
|
status = lua_resume(co, L, narg, &nres);
|
||||||
|
if (l_likely(status == LUA_OK || status == LUA_YIELD)) {
|
||||||
|
if (l_unlikely(!lua_checkstack(L, nres + 1))) {
|
||||||
|
lua_pop(co, nres); /* remove results anyway */
|
||||||
|
lua_pushliteral(L, "too many results to resume");
|
||||||
|
return -1; /* error flag */
|
||||||
|
}
|
||||||
|
lua_xmove(co, L, nres); /* move yielded values */
|
||||||
|
return nres;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lua_xmove(co, L, 1); /* move error message */
|
||||||
|
return -1; /* error flag */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_coresume (lua_State *L) {
|
||||||
|
lua_State *co = getco(L);
|
||||||
|
int r;
|
||||||
|
r = auxresume(L, co, lua_gettop(L) - 1);
|
||||||
|
if (l_unlikely(r < 0)) {
|
||||||
|
lua_pushboolean(L, 0);
|
||||||
|
lua_insert(L, -2);
|
||||||
|
return 2; /* return false + error message */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
lua_insert(L, -(r + 1));
|
||||||
|
return r + 1; /* return true + 'resume' returns */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_auxwrap (lua_State *L) {
|
||||||
|
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
|
||||||
|
int r = auxresume(L, co, lua_gettop(L));
|
||||||
|
if (l_unlikely(r < 0)) { /* error? */
|
||||||
|
int stat = lua_status(co);
|
||||||
|
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
|
||||||
|
stat = lua_resetthread(co); /* close its tbc variables */
|
||||||
|
lua_assert(stat != LUA_OK);
|
||||||
|
lua_xmove(co, L, 1); /* move error message to the caller */
|
||||||
|
}
|
||||||
|
if (stat != LUA_ERRMEM && /* not a memory error and ... */
|
||||||
|
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
|
||||||
|
luaL_where(L, 1); /* add extra info, if available */
|
||||||
|
lua_insert(L, -2);
|
||||||
|
lua_concat(L, 2);
|
||||||
|
}
|
||||||
|
return lua_error(L); /* propagate error */
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_cocreate (lua_State *L) {
|
||||||
|
lua_State *NL;
|
||||||
|
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||||
|
NL = lua_newthread(L);
|
||||||
|
lua_pushvalue(L, 1); /* move function to top */
|
||||||
|
lua_xmove(L, NL, 1); /* move function from L to NL */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_cowrap (lua_State *L) {
|
||||||
|
luaB_cocreate(L);
|
||||||
|
lua_pushcclosure(L, luaB_auxwrap, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_yield (lua_State *L) {
|
||||||
|
return lua_yield(L, lua_gettop(L));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define COS_RUN 0
|
||||||
|
#define COS_DEAD 1
|
||||||
|
#define COS_YIELD 2
|
||||||
|
#define COS_NORM 3
|
||||||
|
|
||||||
|
|
||||||
|
static const char *const statname[] =
|
||||||
|
{"running", "dead", "suspended", "normal"};
|
||||||
|
|
||||||
|
|
||||||
|
static int auxstatus (lua_State *L, lua_State *co) {
|
||||||
|
if (L == co) return COS_RUN;
|
||||||
|
else {
|
||||||
|
switch (lua_status(co)) {
|
||||||
|
case LUA_YIELD:
|
||||||
|
return COS_YIELD;
|
||||||
|
case LUA_OK: {
|
||||||
|
lua_Debug ar;
|
||||||
|
if (lua_getstack(co, 0, &ar)) /* does it have frames? */
|
||||||
|
return COS_NORM; /* it is running */
|
||||||
|
else if (lua_gettop(co) == 0)
|
||||||
|
return COS_DEAD;
|
||||||
|
else
|
||||||
|
return COS_YIELD; /* initial state */
|
||||||
|
}
|
||||||
|
default: /* some error occurred */
|
||||||
|
return COS_DEAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_costatus (lua_State *L) {
|
||||||
|
lua_State *co = getco(L);
|
||||||
|
lua_pushstring(L, statname[auxstatus(L, co)]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_yieldable (lua_State *L) {
|
||||||
|
lua_State *co = lua_isnone(L, 1) ? L : getco(L);
|
||||||
|
lua_pushboolean(L, lua_isyieldable(co));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_corunning (lua_State *L) {
|
||||||
|
int ismain = lua_pushthread(L);
|
||||||
|
lua_pushboolean(L, ismain);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_close (lua_State *L) {
|
||||||
|
lua_State *co = getco(L);
|
||||||
|
int status = auxstatus(L, co);
|
||||||
|
switch (status) {
|
||||||
|
case COS_DEAD: case COS_YIELD: {
|
||||||
|
status = lua_resetthread(co);
|
||||||
|
if (status == LUA_OK) {
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lua_pushboolean(L, 0);
|
||||||
|
lua_xmove(co, L, 1); /* move error message */
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: /* normal or running coroutine */
|
||||||
|
return luaL_error(L, "cannot close a %s coroutine", statname[status]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const luaL_Reg co_funcs[] = {
|
||||||
|
{"create", luaB_cocreate},
|
||||||
|
{"resume", luaB_coresume},
|
||||||
|
{"running", luaB_corunning},
|
||||||
|
{"status", luaB_costatus},
|
||||||
|
{"wrap", luaB_cowrap},
|
||||||
|
{"yield", luaB_yield},
|
||||||
|
{"isyieldable", luaB_yieldable},
|
||||||
|
{"close", luaB_close},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LUAMOD_API int luaopen_coroutine (lua_State *L) {
|
||||||
|
luaL_newlib(L, co_funcs);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
64
lua-5.4/lctype.c
Normal file
64
lua-5.4/lctype.c
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
** $Id: lctype.c $
|
||||||
|
** 'ctype' functions for Lua
|
||||||
|
** See Copyright Notice in lua.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define lctype_c
|
||||||
|
#define LUA_CORE
|
||||||
|
|
||||||
|
#include "lprefix.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "lctype.h"
|
||||||
|
|
||||||
|
#if !LUA_USE_CTYPE /* { */
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
|
||||||
|
#if defined (LUA_UCID) /* accept UniCode IDentifiers? */
|
||||||
|
/* consider all non-ascii codepoints to be alphabetic */
|
||||||
|
#define NONA 0x01
|
||||||
|
#else
|
||||||
|
#define NONA 0x00 /* default */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = {
|
||||||
|
0x00, /* EOZ */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */
|
||||||
|
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */
|
||||||
|
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||||
|
0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */
|
||||||
|
0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||||
|
0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */
|
||||||
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||||
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */
|
||||||
|
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05,
|
||||||
|
0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */
|
||||||
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||||
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */
|
||||||
|
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00,
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||||
|
0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */
|
||||||
|
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||||
|
NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* } */
|
||||||
101
lua-5.4/lctype.h
Normal file
101
lua-5.4/lctype.h
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
** $Id: lctype.h $
|
||||||
|
** 'ctype' functions for Lua
|
||||||
|
** See Copyright Notice in lua.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef lctype_h
|
||||||
|
#define lctype_h
|
||||||
|
|
||||||
|
#include "lua.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** WARNING: the functions defined here do not necessarily correspond
|
||||||
|
** to the similar functions in the standard C ctype.h. They are
|
||||||
|
** optimized for the specific needs of Lua.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(LUA_USE_CTYPE)
|
||||||
|
|
||||||
|
#if 'A' == 65 && '0' == 48
|
||||||
|
/* ASCII case: can use its own tables; faster and fixed */
|
||||||
|
#define LUA_USE_CTYPE 0
|
||||||
|
#else
|
||||||
|
/* must use standard C ctype */
|
||||||
|
#define LUA_USE_CTYPE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if !LUA_USE_CTYPE /* { */
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "llimits.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define ALPHABIT 0
|
||||||
|
#define DIGITBIT 1
|
||||||
|
#define PRINTBIT 2
|
||||||
|
#define SPACEBIT 3
|
||||||
|
#define XDIGITBIT 4
|
||||||
|
|
||||||
|
|
||||||
|
#define MASK(B) (1 << (B))
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** add 1 to char to allow index -1 (EOZ)
|
||||||
|
*/
|
||||||
|
#define testprop(c,p) (luai_ctype_[(c)+1] & (p))
|
||||||
|
|
||||||
|
/*
|
||||||
|
** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_'
|
||||||
|
*/
|
||||||
|
#define lislalpha(c) testprop(c, MASK(ALPHABIT))
|
||||||
|
#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT)))
|
||||||
|
#define lisdigit(c) testprop(c, MASK(DIGITBIT))
|
||||||
|
#define lisspace(c) testprop(c, MASK(SPACEBIT))
|
||||||
|
#define lisprint(c) testprop(c, MASK(PRINTBIT))
|
||||||
|
#define lisxdigit(c) testprop(c, MASK(XDIGITBIT))
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** In ASCII, this 'ltolower' is correct for alphabetic characters and
|
||||||
|
** for '.'. That is enough for Lua needs. ('check_exp' ensures that
|
||||||
|
** the character either is an upper-case letter or is unchanged by
|
||||||
|
** the transformation, which holds for lower-case letters and '.'.)
|
||||||
|
*/
|
||||||
|
#define ltolower(c) \
|
||||||
|
check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \
|
||||||
|
(c) | ('A' ^ 'a'))
|
||||||
|
|
||||||
|
|
||||||
|
/* one entry for each character and for -1 (EOZ) */
|
||||||
|
LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];)
|
||||||
|
|
||||||
|
|
||||||
|
#else /* }{ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** use standard C ctypes
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define lislalpha(c) (isalpha(c) || (c) == '_')
|
||||||
|
#define lislalnum(c) (isalnum(c) || (c) == '_')
|
||||||
|
#define lisdigit(c) (isdigit(c))
|
||||||
|
#define lisspace(c) (isspace(c))
|
||||||
|
#define lisprint(c) (isprint(c))
|
||||||
|
#define lisxdigit(c) (isxdigit(c))
|
||||||
|
|
||||||
|
#define ltolower(c) (tolower(c))
|
||||||
|
|
||||||
|
#endif /* } */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
483
lua-5.4/ldblib.c
Normal file
483
lua-5.4/ldblib.c
Normal file
|
|
@ -0,0 +1,483 @@
|
||||||
|
/*
|
||||||
|
** $Id: ldblib.c $
|
||||||
|
** Interface from Lua to its debug API
|
||||||
|
** See Copyright Notice in lua.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ldblib_c
|
||||||
|
#define LUA_LIB
|
||||||
|
|
||||||
|
#include "lprefix.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "lua.h"
|
||||||
|
|
||||||
|
#include "lauxlib.h"
|
||||||
|
#include "lualib.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The hook table at registry[HOOKKEY] maps threads to their current
|
||||||
|
** hook function.
|
||||||
|
*/
|
||||||
|
static const char *const HOOKKEY = "_HOOKKEY";
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** If L1 != L, L1 can be in any state, and therefore there are no
|
||||||
|
** guarantees about its stack space; any push in L1 must be
|
||||||
|
** checked.
|
||||||
|
*/
|
||||||
|
static void checkstack (lua_State *L, lua_State *L1, int n) {
|
||||||
|
if (l_unlikely(L != L1 && !lua_checkstack(L1, n)))
|
||||||
|
luaL_error(L, "stack overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_getregistry (lua_State *L) {
|
||||||
|
lua_pushvalue(L, LUA_REGISTRYINDEX);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_getmetatable (lua_State *L) {
|
||||||
|
luaL_checkany(L, 1);
|
||||||
|
if (!lua_getmetatable(L, 1)) {
|
||||||
|
lua_pushnil(L); /* no metatable */
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_setmetatable (lua_State *L) {
|
||||||
|
int t = lua_type(L, 2);
|
||||||
|
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
|
||||||
|
lua_settop(L, 2);
|
||||||
|
lua_setmetatable(L, 1);
|
||||||
|
return 1; /* return 1st argument */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_getuservalue (lua_State *L) {
|
||||||
|
int n = (int)luaL_optinteger(L, 2, 1);
|
||||||
|
if (lua_type(L, 1) != LUA_TUSERDATA)
|
||||||
|
luaL_pushfail(L);
|
||||||
|
else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) {
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_setuservalue (lua_State *L) {
|
||||||
|
int n = (int)luaL_optinteger(L, 3, 1);
|
||||||
|
luaL_checktype(L, 1, LUA_TUSERDATA);
|
||||||
|
luaL_checkany(L, 2);
|
||||||
|
lua_settop(L, 2);
|
||||||
|
if (!lua_setiuservalue(L, 1, n))
|
||||||
|
luaL_pushfail(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Auxiliary function used by several library functions: check for
|
||||||
|
** an optional thread as function's first argument and set 'arg' with
|
||||||
|
** 1 if this argument is present (so that functions can skip it to
|
||||||
|
** access their other arguments)
|
||||||
|
*/
|
||||||
|
static lua_State *getthread (lua_State *L, int *arg) {
|
||||||
|
if (lua_isthread(L, 1)) {
|
||||||
|
*arg = 1;
|
||||||
|
return lua_tothread(L, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*arg = 0;
|
||||||
|
return L; /* function will operate over current thread */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Variations of 'lua_settable', used by 'db_getinfo' to put results
|
||||||
|
** from 'lua_getinfo' into result table. Key is always a string;
|
||||||
|
** value can be a string, an int, or a boolean.
|
||||||
|
*/
|
||||||
|
static void settabss (lua_State *L, const char *k, const char *v) {
|
||||||
|
lua_pushstring(L, v);
|
||||||
|
lua_setfield(L, -2, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void settabsi (lua_State *L, const char *k, int v) {
|
||||||
|
lua_pushinteger(L, v);
|
||||||
|
lua_setfield(L, -2, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void settabsb (lua_State *L, const char *k, int v) {
|
||||||
|
lua_pushboolean(L, v);
|
||||||
|
lua_setfield(L, -2, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** In function 'db_getinfo', the call to 'lua_getinfo' may push
|
||||||
|
** results on the stack; later it creates the result table to put
|
||||||
|
** these objects. Function 'treatstackoption' puts the result from
|
||||||
|
** 'lua_getinfo' on top of the result table so that it can call
|
||||||
|
** 'lua_setfield'.
|
||||||
|
*/
|
||||||
|
static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
|
||||||
|
if (L == L1)
|
||||||
|
lua_rotate(L, -2, 1); /* exchange object and table */
|
||||||
|
else
|
||||||
|
lua_xmove(L1, L, 1); /* move object to the "main" stack */
|
||||||
|
lua_setfield(L, -2, fname); /* put object into table */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Calls 'lua_getinfo' and collects all results in a new table.
|
||||||
|
** L1 needs stack space for an optional input (function) plus
|
||||||
|
** two optional outputs (function and line table) from function
|
||||||
|
** 'lua_getinfo'.
|
||||||
|
*/
|
||||||
|
static int db_getinfo (lua_State *L) {
|
||||||
|
lua_Debug ar;
|
||||||
|
int arg;
|
||||||
|
lua_State *L1 = getthread(L, &arg);
|
||||||
|
const char *options = luaL_optstring(L, arg+2, "flnSrtu");
|
||||||
|
checkstack(L, L1, 3);
|
||||||
|
luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'");
|
||||||
|
if (lua_isfunction(L, arg + 1)) { /* info about a function? */
|
||||||
|
options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
|
||||||
|
lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
|
||||||
|
lua_xmove(L, L1, 1);
|
||||||
|
}
|
||||||
|
else { /* stack level */
|
||||||
|
if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) {
|
||||||
|
luaL_pushfail(L); /* level out of range */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!lua_getinfo(L1, options, &ar))
|
||||||
|
return luaL_argerror(L, arg+2, "invalid option");
|
||||||
|
lua_newtable(L); /* table to collect results */
|
||||||
|
if (strchr(options, 'S')) {
|
||||||
|
lua_pushlstring(L, ar.source, ar.srclen);
|
||||||
|
lua_setfield(L, -2, "source");
|
||||||
|
settabss(L, "short_src", ar.short_src);
|
||||||
|
settabsi(L, "linedefined", ar.linedefined);
|
||||||
|
settabsi(L, "lastlinedefined", ar.lastlinedefined);
|
||||||
|
settabss(L, "what", ar.what);
|
||||||
|
}
|
||||||
|
if (strchr(options, 'l'))
|
||||||
|
settabsi(L, "currentline", ar.currentline);
|
||||||
|
if (strchr(options, 'u')) {
|
||||||
|
settabsi(L, "nups", ar.nups);
|
||||||
|
settabsi(L, "nparams", ar.nparams);
|
||||||
|
settabsb(L, "isvararg", ar.isvararg);
|
||||||
|
}
|
||||||
|
if (strchr(options, 'n')) {
|
||||||
|
settabss(L, "name", ar.name);
|
||||||
|
settabss(L, "namewhat", ar.namewhat);
|
||||||
|
}
|
||||||
|
if (strchr(options, 'r')) {
|
||||||
|
settabsi(L, "ftransfer", ar.ftransfer);
|
||||||
|
settabsi(L, "ntransfer", ar.ntransfer);
|
||||||
|
}
|
||||||
|
if (strchr(options, 't'))
|
||||||
|
settabsb(L, "istailcall", ar.istailcall);
|
||||||
|
if (strchr(options, 'L'))
|
||||||
|
treatstackoption(L, L1, "activelines");
|
||||||
|
if (strchr(options, 'f'))
|
||||||
|
treatstackoption(L, L1, "func");
|
||||||
|
return 1; /* return table */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_getlocal (lua_State *L) {
|
||||||
|
int arg;
|
||||||
|
lua_State *L1 = getthread(L, &arg);
|
||||||
|
int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */
|
||||||
|
if (lua_isfunction(L, arg + 1)) { /* function argument? */
|
||||||
|
lua_pushvalue(L, arg + 1); /* push function */
|
||||||
|
lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */
|
||||||
|
return 1; /* return only name (there is no value) */
|
||||||
|
}
|
||||||
|
else { /* stack-level argument */
|
||||||
|
lua_Debug ar;
|
||||||
|
const char *name;
|
||||||
|
int level = (int)luaL_checkinteger(L, arg + 1);
|
||||||
|
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
|
||||||
|
return luaL_argerror(L, arg+1, "level out of range");
|
||||||
|
checkstack(L, L1, 1);
|
||||||
|
name = lua_getlocal(L1, &ar, nvar);
|
||||||
|
if (name) {
|
||||||
|
lua_xmove(L1, L, 1); /* move local value */
|
||||||
|
lua_pushstring(L, name); /* push name */
|
||||||
|
lua_rotate(L, -2, 1); /* re-order */
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
luaL_pushfail(L); /* no name (nor value) */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_setlocal (lua_State *L) {
|
||||||
|
int arg;
|
||||||
|
const char *name;
|
||||||
|
lua_State *L1 = getthread(L, &arg);
|
||||||
|
lua_Debug ar;
|
||||||
|
int level = (int)luaL_checkinteger(L, arg + 1);
|
||||||
|
int nvar = (int)luaL_checkinteger(L, arg + 2);
|
||||||
|
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
|
||||||
|
return luaL_argerror(L, arg+1, "level out of range");
|
||||||
|
luaL_checkany(L, arg+3);
|
||||||
|
lua_settop(L, arg+3);
|
||||||
|
checkstack(L, L1, 1);
|
||||||
|
lua_xmove(L, L1, 1);
|
||||||
|
name = lua_setlocal(L1, &ar, nvar);
|
||||||
|
if (name == NULL)
|
||||||
|
lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */
|
||||||
|
lua_pushstring(L, name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** get (if 'get' is true) or set an upvalue from a closure
|
||||||
|
*/
|
||||||
|
static int auxupvalue (lua_State *L, int get) {
|
||||||
|
const char *name;
|
||||||
|
int n = (int)luaL_checkinteger(L, 2); /* upvalue index */
|
||||||
|
luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */
|
||||||
|
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
|
||||||
|
if (name == NULL) return 0;
|
||||||
|
lua_pushstring(L, name);
|
||||||
|
lua_insert(L, -(get+1)); /* no-op if get is false */
|
||||||
|
return get + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_getupvalue (lua_State *L) {
|
||||||
|
return auxupvalue(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_setupvalue (lua_State *L) {
|
||||||
|
luaL_checkany(L, 3);
|
||||||
|
return auxupvalue(L, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Check whether a given upvalue from a given closure exists and
|
||||||
|
** returns its index
|
||||||
|
*/
|
||||||
|
static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) {
|
||||||
|
void *id;
|
||||||
|
int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */
|
||||||
|
luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */
|
||||||
|
id = lua_upvalueid(L, argf, nup);
|
||||||
|
if (pnup) {
|
||||||
|
luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index");
|
||||||
|
*pnup = nup;
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_upvalueid (lua_State *L) {
|
||||||
|
void *id = checkupval(L, 1, 2, NULL);
|
||||||
|
if (id != NULL)
|
||||||
|
lua_pushlightuserdata(L, id);
|
||||||
|
else
|
||||||
|
luaL_pushfail(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_upvaluejoin (lua_State *L) {
|
||||||
|
int n1, n2;
|
||||||
|
checkupval(L, 1, 2, &n1);
|
||||||
|
checkupval(L, 3, 4, &n2);
|
||||||
|
luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
|
||||||
|
luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
|
||||||
|
lua_upvaluejoin(L, 1, n1, 3, n2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Call hook function registered at hook table for the current
|
||||||
|
** thread (if there is one)
|
||||||
|
*/
|
||||||
|
static void hookf (lua_State *L, lua_Debug *ar) {
|
||||||
|
static const char *const hooknames[] =
|
||||||
|
{"call", "return", "line", "count", "tail call"};
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
|
||||||
|
lua_pushthread(L);
|
||||||
|
if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */
|
||||||
|
lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */
|
||||||
|
if (ar->currentline >= 0)
|
||||||
|
lua_pushinteger(L, ar->currentline); /* push current line */
|
||||||
|
else lua_pushnil(L);
|
||||||
|
lua_assert(lua_getinfo(L, "lS", ar));
|
||||||
|
lua_call(L, 2, 0); /* call hook function */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Convert a string mask (for 'sethook') into a bit mask
|
||||||
|
*/
|
||||||
|
static int makemask (const char *smask, int count) {
|
||||||
|
int mask = 0;
|
||||||
|
if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
|
||||||
|
if (strchr(smask, 'r')) mask |= LUA_MASKRET;
|
||||||
|
if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
|
||||||
|
if (count > 0) mask |= LUA_MASKCOUNT;
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Convert a bit mask (for 'gethook') into a string mask
|
||||||
|
*/
|
||||||
|
static char *unmakemask (int mask, char *smask) {
|
||||||
|
int i = 0;
|
||||||
|
if (mask & LUA_MASKCALL) smask[i++] = 'c';
|
||||||
|
if (mask & LUA_MASKRET) smask[i++] = 'r';
|
||||||
|
if (mask & LUA_MASKLINE) smask[i++] = 'l';
|
||||||
|
smask[i] = '\0';
|
||||||
|
return smask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_sethook (lua_State *L) {
|
||||||
|
int arg, mask, count;
|
||||||
|
lua_Hook func;
|
||||||
|
lua_State *L1 = getthread(L, &arg);
|
||||||
|
if (lua_isnoneornil(L, arg+1)) { /* no hook? */
|
||||||
|
lua_settop(L, arg+1);
|
||||||
|
func = NULL; mask = 0; count = 0; /* turn off hooks */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const char *smask = luaL_checkstring(L, arg+2);
|
||||||
|
luaL_checktype(L, arg+1, LUA_TFUNCTION);
|
||||||
|
count = (int)luaL_optinteger(L, arg + 3, 0);
|
||||||
|
func = hookf; mask = makemask(smask, count);
|
||||||
|
}
|
||||||
|
if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
|
||||||
|
/* table just created; initialize it */
|
||||||
|
lua_pushliteral(L, "k");
|
||||||
|
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
|
||||||
|
lua_pushvalue(L, -1);
|
||||||
|
lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
|
||||||
|
}
|
||||||
|
checkstack(L, L1, 1);
|
||||||
|
lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */
|
||||||
|
lua_pushvalue(L, arg + 1); /* value (hook function) */
|
||||||
|
lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */
|
||||||
|
lua_sethook(L1, func, mask, count);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_gethook (lua_State *L) {
|
||||||
|
int arg;
|
||||||
|
lua_State *L1 = getthread(L, &arg);
|
||||||
|
char buff[5];
|
||||||
|
int mask = lua_gethookmask(L1);
|
||||||
|
lua_Hook hook = lua_gethook(L1);
|
||||||
|
if (hook == NULL) { /* no hook? */
|
||||||
|
luaL_pushfail(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (hook != hookf) /* external hook? */
|
||||||
|
lua_pushliteral(L, "external hook");
|
||||||
|
else { /* hook table must exist */
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
|
||||||
|
checkstack(L, L1, 1);
|
||||||
|
lua_pushthread(L1); lua_xmove(L1, L, 1);
|
||||||
|
lua_rawget(L, -2); /* 1st result = hooktable[L1] */
|
||||||
|
lua_remove(L, -2); /* remove hook table */
|
||||||
|
}
|
||||||
|
lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */
|
||||||
|
lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_debug (lua_State *L) {
|
||||||
|
for (;;) {
|
||||||
|
char buffer[250];
|
||||||
|
lua_writestringerror("%s", "lua_debug> ");
|
||||||
|
if (fgets(buffer, sizeof(buffer), stdin) == NULL ||
|
||||||
|
strcmp(buffer, "cont\n") == 0)
|
||||||
|
return 0;
|
||||||
|
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
|
||||||
|
lua_pcall(L, 0, 0, 0))
|
||||||
|
lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL));
|
||||||
|
lua_settop(L, 0); /* remove eventual returns */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_traceback (lua_State *L) {
|
||||||
|
int arg;
|
||||||
|
lua_State *L1 = getthread(L, &arg);
|
||||||
|
const char *msg = lua_tostring(L, arg + 1);
|
||||||
|
if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */
|
||||||
|
lua_pushvalue(L, arg + 1); /* return it untouched */
|
||||||
|
else {
|
||||||
|
int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0);
|
||||||
|
luaL_traceback(L, L1, msg, level);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int db_setcstacklimit (lua_State *L) {
|
||||||
|
int limit = (int)luaL_checkinteger(L, 1);
|
||||||
|
int res = lua_setcstacklimit(L, limit);
|
||||||
|
lua_pushinteger(L, res);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const luaL_Reg dblib[] = {
|
||||||
|
{"debug", db_debug},
|
||||||
|
{"getuservalue", db_getuservalue},
|
||||||
|
{"gethook", db_gethook},
|
||||||
|
{"getinfo", db_getinfo},
|
||||||
|
{"getlocal", db_getlocal},
|
||||||
|
{"getregistry", db_getregistry},
|
||||||
|
{"getmetatable", db_getmetatable},
|
||||||
|
{"getupvalue", db_getupvalue},
|
||||||
|
{"upvaluejoin", db_upvaluejoin},
|
||||||
|
{"upvalueid", db_upvalueid},
|
||||||
|
{"setuservalue", db_setuservalue},
|
||||||
|
{"sethook", db_sethook},
|
||||||
|
{"setlocal", db_setlocal},
|
||||||
|
{"setmetatable", db_setmetatable},
|
||||||
|
{"setupvalue", db_setupvalue},
|
||||||
|
{"traceback", db_traceback},
|
||||||
|
{"setcstacklimit", db_setcstacklimit},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
LUAMOD_API int luaopen_debug (lua_State *L) {
|
||||||
|
luaL_newlib(L, dblib);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
918
lua-5.4/ldebug.c
Normal file
918
lua-5.4/ldebug.c
Normal file
|
|
@ -0,0 +1,918 @@
|
||||||
|
/*
|
||||||
|
** $Id: ldebug.c $
|
||||||
|
** Debug Interface
|
||||||
|
** See Copyright Notice in lua.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ldebug_c
|
||||||
|
#define LUA_CORE
|
||||||
|
|
||||||
|
#include "lprefix.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "lua.h"
|
||||||
|
|
||||||
|
#include "lapi.h"
|
||||||
|
#include "lcode.h"
|
||||||
|
#include "ldebug.h"
|
||||||
|
#include "ldo.h"
|
||||||
|
#include "lfunc.h"
|
||||||
|
#include "lobject.h"
|
||||||
|
#include "lopcodes.h"
|
||||||
|
#include "lstate.h"
|
||||||
|
#include "lstring.h"
|
||||||
|
#include "ltable.h"
|
||||||
|
#include "ltm.h"
|
||||||
|
#include "lvm.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
|
||||||
|
|
||||||
|
|
||||||
|
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
|
||||||
|
const char **name);
|
||||||
|
|
||||||
|
|
||||||
|
static int currentpc (CallInfo *ci) {
|
||||||
|
lua_assert(isLua(ci));
|
||||||
|
return pcRel(ci->u.l.savedpc, ci_func(ci)->p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Get a "base line" to find the line corresponding to an instruction.
|
||||||
|
** Base lines are regularly placed at MAXIWTHABS intervals, so usually
|
||||||
|
** an integer division gets the right place. When the source file has
|
||||||
|
** large sequences of empty/comment lines, it may need extra entries,
|
||||||
|
** so the original estimate needs a correction.
|
||||||
|
** If the original estimate is -1, the initial 'if' ensures that the
|
||||||
|
** 'while' will run at least once.
|
||||||
|
** The assertion that the estimate is a lower bound for the correct base
|
||||||
|
** is valid as long as the debug info has been generated with the same
|
||||||
|
** value for MAXIWTHABS or smaller. (Previous releases use a little
|
||||||
|
** smaller value.)
|
||||||
|
*/
|
||||||
|
static int getbaseline (const Proto *f, int pc, int *basepc) {
|
||||||
|
if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) {
|
||||||
|
*basepc = -1; /* start from the beginning */
|
||||||
|
return f->linedefined;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */
|
||||||
|
/* estimate must be a lower bound of the correct base */
|
||||||
|
lua_assert(i < 0 ||
|
||||||
|
(i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc));
|
||||||
|
while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc)
|
||||||
|
i++; /* low estimate; adjust it */
|
||||||
|
*basepc = f->abslineinfo[i].pc;
|
||||||
|
return f->abslineinfo[i].line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Get the line corresponding to instruction 'pc' in function 'f';
|
||||||
|
** first gets a base line and from there does the increments until
|
||||||
|
** the desired instruction.
|
||||||
|
*/
|
||||||
|
int luaG_getfuncline (const Proto *f, int pc) {
|
||||||
|
if (f->lineinfo == NULL) /* no debug information? */
|
||||||
|
return -1;
|
||||||
|
else {
|
||||||
|
int basepc;
|
||||||
|
int baseline = getbaseline(f, pc, &basepc);
|
||||||
|
while (basepc++ < pc) { /* walk until given instruction */
|
||||||
|
lua_assert(f->lineinfo[basepc] != ABSLINEINFO);
|
||||||
|
baseline += f->lineinfo[basepc]; /* correct line */
|
||||||
|
}
|
||||||
|
return baseline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int getcurrentline (CallInfo *ci) {
|
||||||
|
return luaG_getfuncline(ci_func(ci)->p, currentpc(ci));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Set 'trap' for all active Lua frames.
|
||||||
|
** This function can be called during a signal, under "reasonable"
|
||||||
|
** assumptions. A new 'ci' is completely linked in the list before it
|
||||||
|
** becomes part of the "active" list, and we assume that pointers are
|
||||||
|
** atomic; see comment in next function.
|
||||||
|
** (A compiler doing interprocedural optimizations could, theoretically,
|
||||||
|
** reorder memory writes in such a way that the list could be
|
||||||
|
** temporarily broken while inserting a new element. We simply assume it
|
||||||
|
** has no good reasons to do that.)
|
||||||
|
*/
|
||||||
|
static void settraps (CallInfo *ci) {
|
||||||
|
for (; ci != NULL; ci = ci->previous)
|
||||||
|
if (isLua(ci))
|
||||||
|
ci->u.l.trap = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function can be called during a signal, under "reasonable"
|
||||||
|
** assumptions.
|
||||||
|
** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount')
|
||||||
|
** are for debug only, and it is no problem if they get arbitrary
|
||||||
|
** values (causes at most one wrong hook call). 'hookmask' is an atomic
|
||||||
|
** value. We assume that pointers are atomic too (e.g., gcc ensures that
|
||||||
|
** for all platforms where it runs). Moreover, 'hook' is always checked
|
||||||
|
** before being called (see 'luaD_hook').
|
||||||
|
*/
|
||||||
|
LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
|
||||||
|
if (func == NULL || mask == 0) { /* turn off hooks? */
|
||||||
|
mask = 0;
|
||||||
|
func = NULL;
|
||||||
|
}
|
||||||
|
L->hook = func;
|
||||||
|
L->basehookcount = count;
|
||||||
|
resethookcount(L);
|
||||||
|
L->hookmask = cast_byte(mask);
|
||||||
|
if (mask)
|
||||||
|
settraps(L->ci); /* to trace inside 'luaV_execute' */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LUA_API lua_Hook lua_gethook (lua_State *L) {
|
||||||
|
return L->hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LUA_API int lua_gethookmask (lua_State *L) {
|
||||||
|
return L->hookmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LUA_API int lua_gethookcount (lua_State *L) {
|
||||||
|
return L->basehookcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
|
||||||
|
int status;
|
||||||
|
CallInfo *ci;
|
||||||
|
if (level < 0) return 0; /* invalid (negative) level */
|
||||||
|
lua_lock(L);
|
||||||
|
for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous)
|
||||||
|
level--;
|
||||||
|
if (level == 0 && ci != &L->base_ci) { /* level found? */
|
||||||
|
status = 1;
|
||||||
|
ar->i_ci = ci;
|
||||||
|
}
|
||||||
|
else status = 0; /* no such level */
|
||||||
|
lua_unlock(L);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char *upvalname (const Proto *p, int uv) {
|
||||||
|
TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name);
|
||||||
|
if (s == NULL) return "?";
|
||||||
|
else return getstr(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
|
||||||
|
if (clLvalue(s2v(ci->func))->p->is_vararg) {
|
||||||
|
int nextra = ci->u.l.nextraargs;
|
||||||
|
if (n >= -nextra) { /* 'n' is negative */
|
||||||
|
*pos = ci->func - nextra - (n + 1);
|
||||||
|
return "(vararg)"; /* generic name for any vararg */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL; /* no such vararg */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
|
||||||
|
StkId base = ci->func + 1;
|
||||||
|
const char *name = NULL;
|
||||||
|
if (isLua(ci)) {
|
||||||
|
if (n < 0) /* access to vararg values? */
|
||||||
|
return findvararg(ci, n, pos);
|
||||||
|
else
|
||||||
|
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
|
||||||
|
}
|
||||||
|
if (name == NULL) { /* no 'standard' name? */
|
||||||
|
StkId limit = (ci == L->ci) ? L->top : ci->next->func;
|
||||||
|
if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */
|
||||||
|
/* generic name for any valid slot */
|
||||||
|
name = isLua(ci) ? "(temporary)" : "(C temporary)";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return NULL; /* no name */
|
||||||
|
}
|
||||||
|
if (pos)
|
||||||
|
*pos = base + (n - 1);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
|
||||||
|
const char *name;
|
||||||
|
lua_lock(L);
|
||||||
|
if (ar == NULL) { /* information about non-active function? */
|
||||||
|
if (!isLfunction(s2v(L->top - 1))) /* not a Lua function? */
|
||||||
|
name = NULL;
|
||||||
|
else /* consider live variables at function start (parameters) */
|
||||||
|
name = luaF_getlocalname(clLvalue(s2v(L->top - 1))->p, n, 0);
|
||||||
|
}
|
||||||
|
else { /* active function; get information through 'ar' */
|
||||||
|
StkId pos = NULL; /* to avoid warnings */
|
||||||
|
name = luaG_findlocal(L, ar->i_ci, n, &pos);
|
||||||
|
if (name) {
|
||||||
|
setobjs2s(L, L->top, pos);
|
||||||
|
api_incr_top(L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_unlock(L);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
|
||||||
|
StkId pos = NULL; /* to avoid warnings */
|
||||||
|
const char *name;
|
||||||
|
lua_lock(L);
|
||||||
|
name = luaG_findlocal(L, ar->i_ci, n, &pos);
|
||||||
|
if (name) {
|
||||||
|
setobjs2s(L, pos, L->top - 1);
|
||||||
|
L->top--; /* pop value */
|
||||||
|
}
|
||||||
|
lua_unlock(L);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void funcinfo (lua_Debug *ar, Closure *cl) {
|
||||||
|
if (noLuaClosure(cl)) {
|
||||||
|
ar->source = "=[C]";
|
||||||
|
ar->srclen = LL("=[C]");
|
||||||
|
ar->linedefined = -1;
|
||||||
|
ar->lastlinedefined = -1;
|
||||||
|
ar->what = "C";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const Proto *p = cl->l.p;
|
||||||
|
if (p->source) {
|
||||||
|
ar->source = getstr(p->source);
|
||||||
|
ar->srclen = tsslen(p->source);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ar->source = "=?";
|
||||||
|
ar->srclen = LL("=?");
|
||||||
|
}
|
||||||
|
ar->linedefined = p->linedefined;
|
||||||
|
ar->lastlinedefined = p->lastlinedefined;
|
||||||
|
ar->what = (ar->linedefined == 0) ? "main" : "Lua";
|
||||||
|
}
|
||||||
|
luaO_chunkid(ar->short_src, ar->source, ar->srclen);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int nextline (const Proto *p, int currentline, int pc) {
|
||||||
|
if (p->lineinfo[pc] != ABSLINEINFO)
|
||||||
|
return currentline + p->lineinfo[pc];
|
||||||
|
else
|
||||||
|
return luaG_getfuncline(p, pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void collectvalidlines (lua_State *L, Closure *f) {
|
||||||
|
if (noLuaClosure(f)) {
|
||||||
|
setnilvalue(s2v(L->top));
|
||||||
|
api_incr_top(L);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int i;
|
||||||
|
TValue v;
|
||||||
|
const Proto *p = f->l.p;
|
||||||
|
int currentline = p->linedefined;
|
||||||
|
Table *t = luaH_new(L); /* new table to store active lines */
|
||||||
|
sethvalue2s(L, L->top, t); /* push it on stack */
|
||||||
|
api_incr_top(L);
|
||||||
|
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
|
||||||
|
if (!p->is_vararg) /* regular function? */
|
||||||
|
i = 0; /* consider all instructions */
|
||||||
|
else { /* vararg function */
|
||||||
|
lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
|
||||||
|
currentline = nextline(p, currentline, 0);
|
||||||
|
i = 1; /* skip first instruction (OP_VARARGPREP) */
|
||||||
|
}
|
||||||
|
for (; i < p->sizelineinfo; i++) { /* for each instruction */
|
||||||
|
currentline = nextline(p, currentline, i); /* get its line */
|
||||||
|
luaH_setint(L, t, currentline, &v); /* table[line] = true */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
|
||||||
|
/* calling function is a known function? */
|
||||||
|
if (ci != NULL && !(ci->callstatus & CIST_TAIL))
|
||||||
|
return funcnamefromcall(L, ci->previous, name);
|
||||||
|
else return NULL; /* no way to find a name */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
|
||||||
|
Closure *f, CallInfo *ci) {
|
||||||
|
int status = 1;
|
||||||
|
for (; *what; what++) {
|
||||||
|
switch (*what) {
|
||||||
|
case 'S': {
|
||||||
|
funcinfo(ar, f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'l': {
|
||||||
|
ar->currentline = (ci && isLua(ci)) ? getcurrentline(ci) : -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'u': {
|
||||||
|
ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
|
||||||
|
if (noLuaClosure(f)) {
|
||||||
|
ar->isvararg = 1;
|
||||||
|
ar->nparams = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ar->isvararg = f->l.p->is_vararg;
|
||||||
|
ar->nparams = f->l.p->numparams;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 't': {
|
||||||
|
ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'n': {
|
||||||
|
ar->namewhat = getfuncname(L, ci, &ar->name);
|
||||||
|
if (ar->namewhat == NULL) {
|
||||||
|
ar->namewhat = ""; /* not found */
|
||||||
|
ar->name = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'r': {
|
||||||
|
if (ci == NULL || !(ci->callstatus & CIST_TRAN))
|
||||||
|
ar->ftransfer = ar->ntransfer = 0;
|
||||||
|
else {
|
||||||
|
ar->ftransfer = ci->u2.transferinfo.ftransfer;
|
||||||
|
ar->ntransfer = ci->u2.transferinfo.ntransfer;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'L':
|
||||||
|
case 'f': /* handled by lua_getinfo */
|
||||||
|
break;
|
||||||
|
default: status = 0; /* invalid option */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
|
||||||
|
int status;
|
||||||
|
Closure *cl;
|
||||||
|
CallInfo *ci;
|
||||||
|
TValue *func;
|
||||||
|
lua_lock(L);
|
||||||
|
if (*what == '>') {
|
||||||
|
ci = NULL;
|
||||||
|
func = s2v(L->top - 1);
|
||||||
|
api_check(L, ttisfunction(func), "function expected");
|
||||||
|
what++; /* skip the '>' */
|
||||||
|
L->top--; /* pop function */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ci = ar->i_ci;
|
||||||
|
func = s2v(ci->func);
|
||||||
|
lua_assert(ttisfunction(func));
|
||||||
|
}
|
||||||
|
cl = ttisclosure(func) ? clvalue(func) : NULL;
|
||||||
|
status = auxgetinfo(L, what, ar, cl, ci);
|
||||||
|
if (strchr(what, 'f')) {
|
||||||
|
setobj2s(L, L->top, func);
|
||||||
|
api_incr_top(L);
|
||||||
|
}
|
||||||
|
if (strchr(what, 'L'))
|
||||||
|
collectvalidlines(L, cl);
|
||||||
|
lua_unlock(L);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** {======================================================
|
||||||
|
** Symbolic Execution
|
||||||
|
** =======================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char *getobjname (const Proto *p, int lastpc, int reg,
|
||||||
|
const char **name);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Find a "name" for the constant 'c'.
|
||||||
|
*/
|
||||||
|
static void kname (const Proto *p, int c, const char **name) {
|
||||||
|
TValue *kvalue = &p->k[c];
|
||||||
|
*name = (ttisstring(kvalue)) ? svalue(kvalue) : "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Find a "name" for the register 'c'.
|
||||||
|
*/
|
||||||
|
static void rname (const Proto *p, int pc, int c, const char **name) {
|
||||||
|
const char *what = getobjname(p, pc, c, name); /* search for 'c' */
|
||||||
|
if (!(what && *what == 'c')) /* did not find a constant name? */
|
||||||
|
*name = "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Find a "name" for a 'C' value in an RK instruction.
|
||||||
|
*/
|
||||||
|
static void rkname (const Proto *p, int pc, Instruction i, const char **name) {
|
||||||
|
int c = GETARG_C(i); /* key index */
|
||||||
|
if (GETARG_k(i)) /* is 'c' a constant? */
|
||||||
|
kname(p, c, name);
|
||||||
|
else /* 'c' is a register */
|
||||||
|
rname(p, pc, c, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int filterpc (int pc, int jmptarget) {
|
||||||
|
if (pc < jmptarget) /* is code conditional (inside a jump)? */
|
||||||
|
return -1; /* cannot know who sets that register */
|
||||||
|
else return pc; /* current position sets that register */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Try to find last instruction before 'lastpc' that modified register 'reg'.
|
||||||
|
*/
|
||||||
|
static int findsetreg (const Proto *p, int lastpc, int reg) {
|
||||||
|
int pc;
|
||||||
|
int setreg = -1; /* keep last instruction that changed 'reg' */
|
||||||
|
int jmptarget = 0; /* any code before this address is conditional */
|
||||||
|
if (testMMMode(GET_OPCODE(p->code[lastpc])))
|
||||||
|
lastpc--; /* previous instruction was not actually executed */
|
||||||
|
for (pc = 0; pc < lastpc; pc++) {
|
||||||
|
Instruction i = p->code[pc];
|
||||||
|
OpCode op = GET_OPCODE(i);
|
||||||
|
int a = GETARG_A(i);
|
||||||
|
int change; /* true if current instruction changed 'reg' */
|
||||||
|
switch (op) {
|
||||||
|
case OP_LOADNIL: { /* set registers from 'a' to 'a+b' */
|
||||||
|
int b = GETARG_B(i);
|
||||||
|
change = (a <= reg && reg <= a + b);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_TFORCALL: { /* affect all regs above its base */
|
||||||
|
change = (reg >= a + 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_CALL:
|
||||||
|
case OP_TAILCALL: { /* affect all registers above base */
|
||||||
|
change = (reg >= a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_JMP: { /* doesn't change registers, but changes 'jmptarget' */
|
||||||
|
int b = GETARG_sJ(i);
|
||||||
|
int dest = pc + 1 + b;
|
||||||
|
/* jump does not skip 'lastpc' and is larger than current one? */
|
||||||
|
if (dest <= lastpc && dest > jmptarget)
|
||||||
|
jmptarget = dest; /* update 'jmptarget' */
|
||||||
|
change = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: /* any instruction that sets A */
|
||||||
|
change = (testAMode(op) && reg == a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (change)
|
||||||
|
setreg = filterpc(pc, jmptarget);
|
||||||
|
}
|
||||||
|
return setreg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Check whether table being indexed by instruction 'i' is the
|
||||||
|
** environment '_ENV'
|
||||||
|
*/
|
||||||
|
static const char *gxf (const Proto *p, int pc, Instruction i, int isup) {
|
||||||
|
int t = GETARG_B(i); /* table index */
|
||||||
|
const char *name; /* name of indexed variable */
|
||||||
|
if (isup) /* is an upvalue? */
|
||||||
|
name = upvalname(p, t);
|
||||||
|
else
|
||||||
|
getobjname(p, pc, t, &name);
|
||||||
|
return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char *getobjname (const Proto *p, int lastpc, int reg,
|
||||||
|
const char **name) {
|
||||||
|
int pc;
|
||||||
|
*name = luaF_getlocalname(p, reg + 1, lastpc);
|
||||||
|
if (*name) /* is a local? */
|
||||||
|
return "local";
|
||||||
|
/* else try symbolic execution */
|
||||||
|
pc = findsetreg(p, lastpc, reg);
|
||||||
|
if (pc != -1) { /* could find instruction? */
|
||||||
|
Instruction i = p->code[pc];
|
||||||
|
OpCode op = GET_OPCODE(i);
|
||||||
|
switch (op) {
|
||||||
|
case OP_MOVE: {
|
||||||
|
int b = GETARG_B(i); /* move from 'b' to 'a' */
|
||||||
|
if (b < GETARG_A(i))
|
||||||
|
return getobjname(p, pc, b, name); /* get name for 'b' */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_GETTABUP: {
|
||||||
|
int k = GETARG_C(i); /* key index */
|
||||||
|
kname(p, k, name);
|
||||||
|
return gxf(p, pc, i, 1);
|
||||||
|
}
|
||||||
|
case OP_GETTABLE: {
|
||||||
|
int k = GETARG_C(i); /* key index */
|
||||||
|
rname(p, pc, k, name);
|
||||||
|
return gxf(p, pc, i, 0);
|
||||||
|
}
|
||||||
|
case OP_GETI: {
|
||||||
|
*name = "integer index";
|
||||||
|
return "field";
|
||||||
|
}
|
||||||
|
case OP_GETFIELD: {
|
||||||
|
int k = GETARG_C(i); /* key index */
|
||||||
|
kname(p, k, name);
|
||||||
|
return gxf(p, pc, i, 0);
|
||||||
|
}
|
||||||
|
case OP_GETUPVAL: {
|
||||||
|
*name = upvalname(p, GETARG_B(i));
|
||||||
|
return "upvalue";
|
||||||
|
}
|
||||||
|
case OP_LOADK:
|
||||||
|
case OP_LOADKX: {
|
||||||
|
int b = (op == OP_LOADK) ? GETARG_Bx(i)
|
||||||
|
: GETARG_Ax(p->code[pc + 1]);
|
||||||
|
if (ttisstring(&p->k[b])) {
|
||||||
|
*name = svalue(&p->k[b]);
|
||||||
|
return "constant";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_SELF: {
|
||||||
|
rkname(p, pc, i, name);
|
||||||
|
return "method";
|
||||||
|
}
|
||||||
|
default: break; /* go through to return NULL */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL; /* could not find reasonable name */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Try to find a name for a function based on the code that called it.
|
||||||
|
** (Only works when function was called by a Lua function.)
|
||||||
|
** Returns what the name is (e.g., "for iterator", "method",
|
||||||
|
** "metamethod") and sets '*name' to point to the name.
|
||||||
|
*/
|
||||||
|
static const char *funcnamefromcode (lua_State *L, const Proto *p,
|
||||||
|
int pc, const char **name) {
|
||||||
|
TMS tm = (TMS)0; /* (initial value avoids warnings) */
|
||||||
|
Instruction i = p->code[pc]; /* calling instruction */
|
||||||
|
switch (GET_OPCODE(i)) {
|
||||||
|
case OP_CALL:
|
||||||
|
case OP_TAILCALL:
|
||||||
|
return getobjname(p, pc, GETARG_A(i), name); /* get function name */
|
||||||
|
case OP_TFORCALL: { /* for iterator */
|
||||||
|
*name = "for iterator";
|
||||||
|
return "for iterator";
|
||||||
|
}
|
||||||
|
/* other instructions can do calls through metamethods */
|
||||||
|
case OP_SELF: case OP_GETTABUP: case OP_GETTABLE:
|
||||||
|
case OP_GETI: case OP_GETFIELD:
|
||||||
|
tm = TM_INDEX;
|
||||||
|
break;
|
||||||
|
case OP_SETTABUP: case OP_SETTABLE: case OP_SETI: case OP_SETFIELD:
|
||||||
|
tm = TM_NEWINDEX;
|
||||||
|
break;
|
||||||
|
case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
|
||||||
|
tm = cast(TMS, GETARG_C(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_UNM: tm = TM_UNM; break;
|
||||||
|
case OP_BNOT: tm = TM_BNOT; break;
|
||||||
|
case OP_LEN: tm = TM_LEN; break;
|
||||||
|
case OP_CONCAT: tm = TM_CONCAT; break;
|
||||||
|
case OP_EQ: tm = TM_EQ; break;
|
||||||
|
/* no cases for OP_EQI and OP_EQK, as they don't call metamethods */
|
||||||
|
case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break;
|
||||||
|
case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break;
|
||||||
|
case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break;
|
||||||
|
default:
|
||||||
|
return NULL; /* cannot find a reasonable name */
|
||||||
|
}
|
||||||
|
*name = getstr(G(L)->tmname[tm]) + 2;
|
||||||
|
return "metamethod";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Try to find a name for a function based on how it was called.
|
||||||
|
*/
|
||||||
|
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
|
||||||
|
const char **name) {
|
||||||
|
if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */
|
||||||
|
*name = "?";
|
||||||
|
return "hook";
|
||||||
|
}
|
||||||
|
else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */
|
||||||
|
*name = "__gc";
|
||||||
|
return "metamethod"; /* report it as such */
|
||||||
|
}
|
||||||
|
else if (isLua(ci))
|
||||||
|
return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* }====================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Check whether pointer 'o' points to some value in the stack
|
||||||
|
** frame of the current function. Because 'o' may not point to a
|
||||||
|
** value in this stack, we cannot compare it with the region
|
||||||
|
** boundaries (undefined behaviour in ISO C).
|
||||||
|
*/
|
||||||
|
static int isinstack (CallInfo *ci, const TValue *o) {
|
||||||
|
StkId pos;
|
||||||
|
for (pos = ci->func + 1; pos < ci->top; pos++) {
|
||||||
|
if (o == s2v(pos))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0; /* not found */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Checks whether value 'o' came from an upvalue. (That can only happen
|
||||||
|
** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on
|
||||||
|
** upvalues.)
|
||||||
|
*/
|
||||||
|
static const char *getupvalname (CallInfo *ci, const TValue *o,
|
||||||
|
const char **name) {
|
||||||
|
LClosure *c = ci_func(ci);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < c->nupvalues; i++) {
|
||||||
|
if (c->upvals[i]->v == o) {
|
||||||
|
*name = upvalname(c->p, i);
|
||||||
|
return "upvalue";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char *formatvarinfo (lua_State *L, const char *kind,
|
||||||
|
const char *name) {
|
||||||
|
if (kind == NULL)
|
||||||
|
return ""; /* no information */
|
||||||
|
else
|
||||||
|
return luaO_pushfstring(L, " (%s '%s')", kind, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Build a string with a "description" for the value 'o', such as
|
||||||
|
** "variable 'x'" or "upvalue 'y'".
|
||||||
|
*/
|
||||||
|
static const char *varinfo (lua_State *L, const TValue *o) {
|
||||||
|
CallInfo *ci = L->ci;
|
||||||
|
const char *name = NULL; /* to avoid warnings */
|
||||||
|
const char *kind = NULL;
|
||||||
|
if (isLua(ci)) {
|
||||||
|
kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
|
||||||
|
if (!kind && isinstack(ci, o)) /* no? try a register */
|
||||||
|
kind = getobjname(ci_func(ci)->p, currentpc(ci),
|
||||||
|
cast_int(cast(StkId, o) - (ci->func + 1)), &name);
|
||||||
|
}
|
||||||
|
return formatvarinfo(L, kind, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Raise a type error
|
||||||
|
*/
|
||||||
|
static l_noret typeerror (lua_State *L, const TValue *o, const char *op,
|
||||||
|
const char *extra) {
|
||||||
|
const char *t = luaT_objtypename(L, o);
|
||||||
|
luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Raise a type error with "standard" information about the faulty
|
||||||
|
** object 'o' (using 'varinfo').
|
||||||
|
*/
|
||||||
|
l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
|
||||||
|
typeerror(L, o, op, varinfo(L, o));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Raise an error for calling a non-callable object. Try to find a name
|
||||||
|
** for the object based on how it was called ('funcnamefromcall'); if it
|
||||||
|
** cannot get a name there, try 'varinfo'.
|
||||||
|
*/
|
||||||
|
l_noret luaG_callerror (lua_State *L, const TValue *o) {
|
||||||
|
CallInfo *ci = L->ci;
|
||||||
|
const char *name = NULL; /* to avoid warnings */
|
||||||
|
const char *kind = funcnamefromcall(L, ci, &name);
|
||||||
|
const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o);
|
||||||
|
typeerror(L, o, "call", extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
|
||||||
|
luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
|
||||||
|
what, luaT_objtypename(L, o));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) {
|
||||||
|
if (ttisstring(p1) || cvt2str(p1)) p1 = p2;
|
||||||
|
luaG_typeerror(L, p1, "concatenate");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
l_noret luaG_opinterror (lua_State *L, const TValue *p1,
|
||||||
|
const TValue *p2, const char *msg) {
|
||||||
|
if (!ttisnumber(p1)) /* first operand is wrong? */
|
||||||
|
p2 = p1; /* now second is wrong */
|
||||||
|
luaG_typeerror(L, p2, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Error when both values are convertible to numbers, but not to integers
|
||||||
|
*/
|
||||||
|
l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
|
||||||
|
lua_Integer temp;
|
||||||
|
if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I))
|
||||||
|
p2 = p1;
|
||||||
|
luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
|
||||||
|
const char *t1 = luaT_objtypename(L, p1);
|
||||||
|
const char *t2 = luaT_objtypename(L, p2);
|
||||||
|
if (strcmp(t1, t2) == 0)
|
||||||
|
luaG_runerror(L, "attempt to compare two %s values", t1);
|
||||||
|
else
|
||||||
|
luaG_runerror(L, "attempt to compare %s with %s", t1, t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* add src:line information to 'msg' */
|
||||||
|
const char *luaG_addinfo (lua_State *L, const char *msg, TString *src,
|
||||||
|
int line) {
|
||||||
|
char buff[LUA_IDSIZE];
|
||||||
|
if (src)
|
||||||
|
luaO_chunkid(buff, getstr(src), tsslen(src));
|
||||||
|
else { /* no source available; use "?" instead */
|
||||||
|
buff[0] = '?'; buff[1] = '\0';
|
||||||
|
}
|
||||||
|
return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
l_noret luaG_errormsg (lua_State *L) {
|
||||||
|
if (L->errfunc != 0) { /* is there an error handling function? */
|
||||||
|
StkId errfunc = restorestack(L, L->errfunc);
|
||||||
|
lua_assert(ttisfunction(s2v(errfunc)));
|
||||||
|
setobjs2s(L, L->top, L->top - 1); /* move argument */
|
||||||
|
setobjs2s(L, L->top - 1, errfunc); /* push function */
|
||||||
|
L->top++; /* assume EXTRA_STACK */
|
||||||
|
luaD_callnoyield(L, L->top - 2, 1); /* call it */
|
||||||
|
}
|
||||||
|
luaD_throw(L, LUA_ERRRUN);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
|
||||||
|
CallInfo *ci = L->ci;
|
||||||
|
const char *msg;
|
||||||
|
va_list argp;
|
||||||
|
luaC_checkGC(L); /* error message uses memory */
|
||||||
|
va_start(argp, fmt);
|
||||||
|
msg = luaO_pushvfstring(L, fmt, argp); /* format message */
|
||||||
|
va_end(argp);
|
||||||
|
if (isLua(ci)) /* if Lua function, add source:line information */
|
||||||
|
luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci));
|
||||||
|
luaG_errormsg(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Check whether new instruction 'newpc' is in a different line from
|
||||||
|
** previous instruction 'oldpc'. More often than not, 'newpc' is only
|
||||||
|
** one or a few instructions after 'oldpc' (it must be after, see
|
||||||
|
** caller), so try to avoid calling 'luaG_getfuncline'. If they are
|
||||||
|
** too far apart, there is a good chance of a ABSLINEINFO in the way,
|
||||||
|
** so it goes directly to 'luaG_getfuncline'.
|
||||||
|
*/
|
||||||
|
static int changedline (const Proto *p, int oldpc, int newpc) {
|
||||||
|
if (p->lineinfo == NULL) /* no debug information? */
|
||||||
|
return 0;
|
||||||
|
if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
|
||||||
|
int delta = 0; /* line diference */
|
||||||
|
int pc = oldpc;
|
||||||
|
for (;;) {
|
||||||
|
int lineinfo = p->lineinfo[++pc];
|
||||||
|
if (lineinfo == ABSLINEINFO)
|
||||||
|
break; /* cannot compute delta; fall through */
|
||||||
|
delta += lineinfo;
|
||||||
|
if (pc == newpc)
|
||||||
|
return (delta != 0); /* delta computed successfully */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* either instructions are too far apart or there is an absolute line
|
||||||
|
info in the way; compute line difference explicitly */
|
||||||
|
return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Traces the execution of a Lua function. Called before the execution
|
||||||
|
** of each opcode, when debug is on. 'L->oldpc' stores the last
|
||||||
|
** instruction traced, to detect line changes. When entering a new
|
||||||
|
** function, 'npci' will be zero and will test as a new line whatever
|
||||||
|
** the value of 'oldpc'. Some exceptional conditions may return to
|
||||||
|
** a function without setting 'oldpc'. In that case, 'oldpc' may be
|
||||||
|
** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
|
||||||
|
** at most causes an extra call to a line hook.)
|
||||||
|
** This function is not "Protected" when called, so it should correct
|
||||||
|
** 'L->top' before calling anything that can run the GC.
|
||||||
|
*/
|
||||||
|
int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||||
|
CallInfo *ci = L->ci;
|
||||||
|
lu_byte mask = L->hookmask;
|
||||||
|
const Proto *p = ci_func(ci)->p;
|
||||||
|
int counthook;
|
||||||
|
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
|
||||||
|
ci->u.l.trap = 0; /* don't need to stop again */
|
||||||
|
return 0; /* turn off 'trap' */
|
||||||
|
}
|
||||||
|
pc++; /* reference is always next instruction */
|
||||||
|
ci->u.l.savedpc = pc; /* save 'pc' */
|
||||||
|
counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT));
|
||||||
|
if (counthook)
|
||||||
|
resethookcount(L); /* reset count */
|
||||||
|
else if (!(mask & LUA_MASKLINE))
|
||||||
|
return 1; /* no line hook and count != 0; nothing to be done now */
|
||||||
|
if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */
|
||||||
|
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
|
||||||
|
return 1; /* do not call hook again (VM yielded, so it did not move) */
|
||||||
|
}
|
||||||
|
if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
|
||||||
|
L->top = ci->top; /* correct top */
|
||||||
|
if (counthook)
|
||||||
|
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
|
||||||
|
if (mask & LUA_MASKLINE) {
|
||||||
|
/* 'L->oldpc' may be invalid; use zero in this case */
|
||||||
|
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
|
||||||
|
int npci = pcRel(pc, p);
|
||||||
|
if (npci <= oldpc || /* call hook when jump back (loop), */
|
||||||
|
changedline(p, oldpc, npci)) { /* or when enter new line */
|
||||||
|
int newline = luaG_getfuncline(p, npci);
|
||||||
|
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
|
||||||
|
}
|
||||||
|
L->oldpc = npci; /* 'pc' of last call to line hook */
|
||||||
|
}
|
||||||
|
if (L->status == LUA_YIELD) { /* did hook yield? */
|
||||||
|
if (counthook)
|
||||||
|
L->hookcount = 1; /* undo decrement to zero */
|
||||||
|
ci->u.l.savedpc--; /* undo increment (resume will increment it again) */
|
||||||
|
ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */
|
||||||
|
luaD_throw(L, LUA_YIELD);
|
||||||
|
}
|
||||||
|
return 1; /* keep 'trap' on */
|
||||||
|
}
|
||||||
|
|
||||||
63
lua-5.4/ldebug.h
Normal file
63
lua-5.4/ldebug.h
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
** $Id: ldebug.h $
|
||||||
|
** Auxiliary functions from Debug Interface module
|
||||||
|
** See Copyright Notice in lua.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ldebug_h
|
||||||
|
#define ldebug_h
|
||||||
|
|
||||||
|
|
||||||
|
#include "lstate.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
|
||||||
|
|
||||||
|
|
||||||
|
/* Active Lua function (given call info) */
|
||||||
|
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
|
||||||
|
|
||||||
|
|
||||||
|
#define resethookcount(L) (L->hookcount = L->basehookcount)
|
||||||
|
|
||||||
|
/*
|
||||||
|
** mark for entries in 'lineinfo' array that has absolute information in
|
||||||
|
** 'abslineinfo' array
|
||||||
|
*/
|
||||||
|
#define ABSLINEINFO (-0x80)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** MAXimum number of successive Instructions WiTHout ABSolute line
|
||||||
|
** information. (A power of two allows fast divisions.)
|
||||||
|
*/
|
||||||
|
#if !defined(MAXIWTHABS)
|
||||||
|
#define MAXIWTHABS 128
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
|
||||||
|
LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
|
||||||
|
StkId *pos);
|
||||||
|
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
|
||||||
|
const char *opname);
|
||||||
|
LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o);
|
||||||
|
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
|
||||||
|
const char *what);
|
||||||
|
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
|
||||||
|
const TValue *p2);
|
||||||
|
LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1,
|
||||||
|
const TValue *p2,
|
||||||
|
const char *msg);
|
||||||
|
LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1,
|
||||||
|
const TValue *p2);
|
||||||
|
LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1,
|
||||||
|
const TValue *p2);
|
||||||
|
LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...);
|
||||||
|
LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
|
||||||
|
TString *src, int line);
|
||||||
|
LUAI_FUNC l_noret luaG_errormsg (lua_State *L);
|
||||||
|
LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue