aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatarLibravatar daurnimator <quae@daurnimator.com> 2017-08-30 13:55:59 +1000
committerLibravatarLibravatar daurnimator <quae@daurnimator.com> 2017-08-30 13:55:59 +1000
commit7333333568b13db56136e2354c55556adc7714ed (patch)
tree028cec7c885ebbbdd404d9db7aba8510f87681a4
downloadluaossl-7333333568b13db56136e2354c55556adc7714ed.tar.gz
luaossl-7333333568b13db56136e2354c55556adc7714ed.tar.bz2
luaossl-7333333568b13db56136e2354c55556adc7714ed.zip
Squashed 'vendor/compat53/' content from commit 6f3deea
git-subtree-dir: vendor/compat53 git-subtree-split: 6f3deeaa6a4743e1f5148c613addb3f94a22d2df
-rw-r--r--.gitignore10
-rw-r--r--LICENSE20
-rw-r--r--README.md229
-rw-r--r--c-api/compat-5.3.c617
-rw-r--r--c-api/compat-5.3.h388
-rw-r--r--compat53/init.lua373
-rw-r--r--compat53/module.lua827
-rw-r--r--lprefix.h175
-rw-r--r--lstrlib.c1584
-rw-r--r--ltablib.c450
-rw-r--r--lutf8lib.c256
-rw-r--r--rockspecs/compat53-0.1-1.rockspec31
-rw-r--r--rockspecs/compat53-0.2-1.rockspec32
-rw-r--r--rockspecs/compat53-0.3-1.rockspec32
-rw-r--r--rockspecs/compat53-0.4-1.rockspec32
-rw-r--r--rockspecs/compat53-0.5-1.rockspec32
-rw-r--r--rockspecs/compat53-scm-0.rockspec32
-rwxr-xr-xtests/test.lua789
-rw-r--r--tests/testmod.c318
19 files changed, 6227 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..67c1b76
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+# generated files
+*.so
+*.dll
+*.o
+*.obj
+HISTO
+
+# vim temporaries
+.*.swp
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..511db15
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Kepler Project.
+
+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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..08614a1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,229 @@
+# lua-compat-5.3
+
+Lua-5.3-style APIs for Lua 5.2 and 5.1.
+
+## What is it
+
+This is a small module that aims to make it easier to write code
+in a Lua-5.3-style that is compatible with Lua 5.1, Lua 5.2, and Lua
+5.3. This does *not* make Lua 5.2 (or even Lua 5.1) entirely
+compatible with Lua 5.3, but it brings the API closer to that of Lua
+5.3.
+
+It includes:
+
+* _For writing Lua_: The Lua module `compat53`, which can be require'd
+ from Lua scripts and run in Lua 5.1, 5.2, and 5.3, including a
+ backport of the `utf8` module, the 5.3 `table` module, and the
+ string packing functions straight from the Lua 5.3 sources.
+* _For writing C_: A C header and file which can be linked to your
+ Lua module written in C, providing some functions from the C API
+ of Lua 5.3 that do not exist in Lua 5.2 or 5.1, making it easier to
+ write C code that compiles with all three versions of liblua.
+
+## How to use it
+
+### Lua module
+
+```lua
+require("compat53")
+```
+
+`compat53` makes changes to your global environment and does not return
+a meaningful return value, so the usual idiom of storing the return of
+`require` in a local variable makes no sense.
+
+When run under Lua 5.3, this module does nothing.
+
+When run under Lua 5.2 or 5.1, it replaces some of your standard
+functions and adds new ones to bring your environment closer to that
+of Lua 5.3. It also tries to load the backported `utf8`, `table`, and
+string packing modules automatically. If unsuccessful, pure Lua
+versions of the new `table` functions are used as a fallback, and
+[Roberto's struct library][1] is tried for string packing.
+
+#### Lua submodules
+
+```lua
+local _ENV = require("compat53.module")
+if setfenv then setfenv(1, _ENV) end
+```
+
+The `compat53.module` module does not modify the global environment,
+and so it is safe to use in modules without affecting other Lua files.
+It is supposed to be set as the current environment (see above), i.e.
+cherry picking individual functions from this module is expressly
+*not* supported!). Not all features are available when using this
+module (e.g. yieldable (x)pcall support, string/file methods, etc.),
+so it is recommended to use plain `require("compat53")` whenever
+possible.
+
+### C code
+
+There are two ways of adding the C API compatibility functions/macros to
+your project:
+* If `COMPAT53_PREFIX` is *not* `#define`d, `compat-5.3.h` `#include`s
+ `compat-5.3.c`, and all functions are made `static`. You don't have to
+ compile/link/add `compat-5.3.c` yourself. This is useful for one-file
+ projects.
+* If `COMPAT53_PREFIX` is `#define`d, all exported functions are renamed
+ behind the scenes using this prefix to avoid linker conflicts with other
+ code using this package. This doesn't change the way you call the
+ compatibility functions in your code. You have to compile and link
+ `compat-5.3.c` to your project yourself. You can change the way the
+ functions are exported using the `COMPAT53_API` macro (e.g. if you need
+ some `__declspec` magic). While it is technically possible to use
+ the "lua" prefix (and it looks better in the debugger), this is
+ discouraged because LuaJIT has started to implement its own Lua 5.2+
+ C API functions, and with the "lua" prefix you'd violate the
+ one-definition rule with recent LuaJIT versions.
+
+## What's implemented
+
+### Lua
+
+* the `utf8` module backported from the Lua 5.3 sources
+* `string.pack`, `string.packsize`, and `string.unpack` from the Lua
+ 5.3 sources or from the `struct` module. (`struct` is not 100%
+ compatible to Lua 5.3's string packing!) (See [here][4])
+* `math.maxinteger` and `math.mininteger`, `math.tointeger`, `math.type`,
+ and `math.ult` (see [here][5])
+* `assert` accepts non-string error messages
+* `ipairs` respects `__index` metamethod
+* `table.move`
+* `table` library respects metamethods
+
+For Lua 5.1 additionally:
+* `load` and `loadfile` accept `mode` and `env` parameters
+* `table.pack` and `table.unpack`
+* string patterns may contain embedded zeros (but see [here][6])
+* `string.rep` accepts `sep` argument
+* `string.format` calls `tostring` on arguments for `%s`
+* `math.log` accepts base argument
+* `xpcall` takes additional arguments
+* `pcall` and `xpcall` can execute functions that yield (see
+ [here][22] for a possible problem with `coroutine.running`)
+* `pairs` respects `__pairs` metamethod (see [here][7])
+* `rawlen` (but `#` still doesn't respect `__len` for tables)
+* `package.searchers` as alias for `package.loaders`
+* `package.searchpath` (see [here][8])
+* `coroutine` functions dealing with the main coroutine (see
+ [here][22] for a possible problem with `coroutine.running`)
+* `coroutine.create` accepts functions written in C
+* return code of `os.execute` (see [here][9])
+* `io.write` and `file:write` return file handle
+* `io.lines` and `file:lines` accept format arguments (like `io.read`)
+ (see [here][10] and [here][11])
+* `debug.setmetatable` returns object
+* `debug.getuservalue` (see [here][12])
+* `debug.setuservalue` (see [here][13])
+
+### C
+
+* `lua_KContext` (see [here][14])
+* `lua_KFunction` (see [here][14])
+* `lua_dump` (extra `strip` parameter, ignored, see [here][15])
+* `lua_getfield` (return value)
+* `lua_geti` and `lua_seti`
+* `lua_getglobal` (return value)
+* `lua_getmetafield` (return value)
+* `lua_gettable` (return value)
+* `lua_getuservalue` (limited compatibility, see [here][16])
+* `lua_setuservalue` (limited compatibility, see [here][17])
+* `lua_isinteger`
+* `lua_numbertointeger`
+* `lua_callk` and `lua_pcallk` (limited compatibility, see [here][14])
+* `lua_rawget` and `lua_rawgeti` (return values)
+* `lua_rawgetp` and `lua_rawsetp`
+* `luaL_requiref` (now checks `package.loaded` first)
+* `lua_rotate`
+* `lua_stringtonumber` (see [here][18])
+
+For Lua 5.1 additionally:
+* `LUA_OK`
+* `LUA_OP*` macros for `lua_arith` and `lua_compare`
+* `lua_Unsigned`
+* `lua_absindex`
+* `lua_arith` (see [here][19])
+* `lua_compare`
+* `lua_len`, `lua_rawlen`, and `luaL_len`
+* `lua_pushstring`, `lua_pushlstring` (return value)
+* `lua_copy`
+* `lua_pushglobaltable`
+* `luaL_testudata`
+* `luaL_setfuncs`, `luaL_newlibtable`, and `luaL_newlib`
+* `luaL_setmetatable`
+* `luaL_getsubtable`
+* `luaL_traceback`
+* `luaL_execresult`
+* `luaL_fileresult`
+* `luaL_checkversion` (with empty body, only to avoid compile errors,
+ see [here][20])
+* `luaL_tolstring`
+* `luaL_buffinitsize`, `luaL_prepbuffsize`, and `luaL_pushresultsize`
+ (see [here][21])
+* `lua_pushunsigned`, `lua_tounsignedx`, `lua_tounsigned`,
+ `luaL_checkunsigned`, `luaL_optunsigned`, if
+ `LUA_COMPAT_APIINTCASTS` is defined.
+
+## What's not implemented
+
+* bit operators
+* integer division operator
+* utf8 escape sequences
+* 64 bit integers
+* `coroutine.isyieldable`
+* Lua 5.1: `_ENV`, `goto`, labels, ephemeron tables, etc. See
+ [`lua-compat-5.2`][2] for a detailed list.
+* the following C API functions/macros:
+ * `lua_isyieldable`
+ * `lua_getextraspace`
+ * `lua_arith` (new operators missing)
+ * `lua_push(v)fstring` (new formats missing)
+ * `lua_upvalueid` (5.1)
+ * `lua_upvaluejoin` (5.1)
+ * `lua_version` (5.1)
+ * `lua_yieldk` (5.1)
+ * `luaL_loadbufferx` (5.1)
+ * `luaL_loadfilex` (5.1)
+
+## See also
+
+* For Lua-5.2-style APIs under Lua 5.1, see [lua-compat-5.2][2],
+ which also is the basis for most of the code in this project.
+* For Lua-5.1-style APIs under Lua 5.0, see [Compat-5.1][3]
+
+## Credits
+
+This package contains code written by:
+
+* [The Lua Team](http://www.lua.org)
+* Philipp Janda ([@siffiejoe](http://github.com/siffiejoe))
+* Tomás Guisasola Gorham ([@tomasguisasola](http://github.com/tomasguisasola))
+* Hisham Muhammad ([@hishamhm](http://github.com/hishamhm))
+* Renato Maia ([@renatomaia](http://github.com/renatomaia))
+
+
+ [1]: http://www.inf.puc-rio.br/~roberto/struct/
+ [2]: http://github.com/keplerproject/lua-compat-5.2/
+ [3]: http://keplerproject.org/compat/
+ [4]: https://github.com/keplerproject/lua-compat-5.3/wiki/string_packing
+ [5]: https://github.com/keplerproject/lua-compat-5.3/wiki/math.type
+ [6]: https://github.com/keplerproject/lua-compat-5.3/wiki/pattern_matching
+ [7]: https://github.com/keplerproject/lua-compat-5.3/wiki/pairs
+ [8]: https://github.com/keplerproject/lua-compat-5.3/wiki/package.searchpath
+ [9]: https://github.com/keplerproject/lua-compat-5.3/wiki/os.execute
+ [10]: https://github.com/keplerproject/lua-compat-5.3/wiki/io.lines
+ [11]: https://github.com/keplerproject/lua-compat-5.3/wiki/file.lines
+ [12]: https://github.com/keplerproject/lua-compat-5.3/wiki/debug.getuservalue
+ [13]: https://github.com/keplerproject/lua-compat-5.3/wiki/debug.setuservalue
+ [14]: https://github.com/keplerproject/lua-compat-5.3/wiki/yieldable_c_functions
+ [15]: https://github.com/keplerproject/lua-compat-5.3/wiki/lua_dump
+ [16]: https://github.com/keplerproject/lua-compat-5.3/wiki/lua_getuservalue
+ [17]: https://github.com/keplerproject/lua-compat-5.3/wiki/lua_setuservalue
+ [18]: https://github.com/keplerproject/lua-compat-5.3/wiki/lua_stringtonumber
+ [19]: https://github.com/keplerproject/lua-compat-5.3/wiki/lua_arith
+ [20]: https://github.com/keplerproject/lua-compat-5.3/wiki/luaL_checkversion
+ [21]: https://github.com/keplerproject/lua-compat-5.3/wiki/luaL_Buffer
+ [22]: https://github.com/keplerproject/lua-compat-5.3/wiki/coroutine.running
+
diff --git a/c-api/compat-5.3.c b/c-api/compat-5.3.c
new file mode 100644
index 0000000..883efb8
--- /dev/null
+++ b/c-api/compat-5.3.c
@@ -0,0 +1,617 @@
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include "compat-5.3.h"
+
+/* don't compile it again if it already is included via compat53.h */
+#ifndef COMPAT53_C_
+#define COMPAT53_C_
+
+
+
+/* definitions for Lua 5.1 only */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
+
+
+COMPAT53_API int lua_absindex (lua_State *L, int i) {
+ if (i < 0 && i > LUA_REGISTRYINDEX)
+ i += lua_gettop(L) + 1;
+ return i;
+}
+
+
+static void compat53_call_lua (lua_State *L, char const code[], size_t len,
+ int nargs, int nret) {
+ lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code);
+ if (lua_type(L, -1) != LUA_TFUNCTION) {
+ lua_pop(L, 1);
+ if (luaL_loadbuffer(L, code, len, "=none"))
+ lua_error(L);
+ lua_pushvalue(L, -1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code);
+ }
+ lua_insert(L, -nargs-1);
+ lua_call(L, nargs, nret);
+}
+
+
+static const char compat53_arith_code[] =
+ "local op,a,b=...\n"
+ "if op==0 then return a+b\n"
+ "elseif op==1 then return a-b\n"
+ "elseif op==2 then return a*b\n"
+ "elseif op==3 then return a/b\n"
+ "elseif op==4 then return a%b\n"
+ "elseif op==5 then return a^b\n"
+ "elseif op==6 then return -a\n"
+ "end\n";
+
+COMPAT53_API void lua_arith (lua_State *L, int op) {
+ if (op < LUA_OPADD || op > LUA_OPUNM)
+ luaL_error(L, "invalid 'op' argument for lua_arith");
+ luaL_checkstack(L, 5, "not enough stack slots");
+ if (op == LUA_OPUNM)
+ lua_pushvalue(L, -1);
+ lua_pushnumber(L, op);
+ lua_insert(L, -3);
+ compat53_call_lua(L, compat53_arith_code,
+ sizeof(compat53_arith_code)-1, 3, 1);
+}
+
+
+static const char compat53_compare_code[] =
+ "local a,b=...\n"
+ "return a<=b\n";
+
+COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op) {
+ int result = 0;
+ switch (op) {
+ case LUA_OPEQ:
+ return lua_equal(L, idx1, idx2);
+ case LUA_OPLT:
+ return lua_lessthan(L, idx1, idx2);
+ case LUA_OPLE:
+ luaL_checkstack(L, 5, "not enough stack slots");
+ idx1 = lua_absindex(L, idx1);
+ idx2 = lua_absindex(L, idx2);
+ lua_pushvalue(L, idx1);
+ lua_pushvalue(L, idx2);
+ compat53_call_lua(L, compat53_compare_code,
+ sizeof(compat53_compare_code)-1, 2, 1);
+ result = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ return result;
+ default:
+ luaL_error(L, "invalid 'op' argument for lua_compare");
+ }
+ return 0;
+}
+
+
+COMPAT53_API void lua_copy (lua_State *L, int from, int to) {
+ int abs_to = lua_absindex(L, to);
+ luaL_checkstack(L, 1, "not enough stack slots");
+ lua_pushvalue(L, from);
+ lua_replace(L, abs_to);
+}
+
+
+COMPAT53_API void lua_len (lua_State *L, int i) {
+ switch (lua_type(L, i)) {
+ case LUA_TSTRING:
+ lua_pushnumber(L, (lua_Integer)lua_objlen(L, i));
+ break;
+ case LUA_TTABLE:
+ if (!luaL_callmeta(L, i, "__len"))
+ lua_pushnumber(L, (lua_Integer)lua_objlen(L, i));
+ break;
+ case LUA_TUSERDATA:
+ if (luaL_callmeta(L, i, "__len"))
+ break;
+ /* maybe fall through */
+ default:
+ luaL_error(L, "attempt to get length of a %s value",
+ lua_typename(L, lua_type(L, i)));
+ }
+}
+
+
+COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p) {
+ int abs_i = lua_absindex(L, i);
+ lua_pushlightuserdata(L, (void*)p);
+ lua_rawget(L, abs_i);
+ return lua_type(L, -1);
+}
+
+COMPAT53_API void lua_rawsetp (lua_State *L, int i, const void *p) {
+ int abs_i = lua_absindex(L, i);
+ luaL_checkstack(L, 1, "not enough stack slots");
+ lua_pushlightuserdata(L, (void*)p);
+ lua_insert(L, -2);
+ lua_rawset(L, abs_i);
+}
+
+
+COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum) {
+ lua_Integer n = lua_tointeger(L, i);
+ if (isnum != NULL) {
+ *isnum = (n != 0 || lua_isnumber(L, i));
+ }
+ return n;
+}
+
+
+COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum) {
+ lua_Number n = lua_tonumber(L, i);
+ if (isnum != NULL) {
+ *isnum = (n != 0 || lua_isnumber(L, i));
+ }
+ return n;
+}
+
+
+COMPAT53_API void luaL_checkversion (lua_State *L) {
+ (void)L;
+}
+
+
+COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg) {
+ if (!lua_checkstack(L, sp+LUA_MINSTACK)) {
+ if (msg != NULL)
+ luaL_error(L, "stack overflow (%s)", msg);
+ else {
+ lua_pushliteral(L, "stack overflow");
+ lua_error(L);
+ }
+ }
+}
+
+
+COMPAT53_API int luaL_getsubtable (lua_State *L, int i, const char *name) {
+ int abs_i = lua_absindex(L, i);
+ luaL_checkstack(L, 3, "not enough stack slots");
+ lua_pushstring(L, name);
+ lua_gettable(L, abs_i);
+ if (lua_istable(L, -1))
+ return 1;
+ lua_pop(L, 1);
+ lua_newtable(L);
+ lua_pushstring(L, name);
+ lua_pushvalue(L, -2);
+ lua_settable(L, abs_i);
+ return 0;
+}
+
+
+COMPAT53_API lua_Integer luaL_len (lua_State *L, int i) {
+ lua_Integer res = 0;
+ int isnum = 0;
+ luaL_checkstack(L, 1, "not enough stack slots");
+ lua_len(L, i);
+ res = lua_tointegerx(L, -1, &isnum);
+ lua_pop(L, 1);
+ if (!isnum)
+ luaL_error(L, "object length is not an integer");
+ return res;
+}
+
+
+COMPAT53_API void luaL_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)); /* table must be below the upvalues, the name and the closure */
+ }
+ lua_pop(L, nup); /* remove upvalues */
+}
+
+
+COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname) {
+ luaL_checkstack(L, 1, "not enough stack slots");
+ luaL_getmetatable(L, tname);
+ lua_setmetatable(L, -2);
+}
+
+
+COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname) {
+ void *p = lua_touserdata(L, i);
+ luaL_checkstack(L, 2, "not enough stack slots");
+ if (p == NULL || !lua_getmetatable(L, i))
+ return NULL;
+ else {
+ int res = 0;
+ luaL_getmetatable(L, tname);
+ res = lua_rawequal(L, -1, -2);
+ lua_pop(L, 2);
+ if (!res)
+ p = NULL;
+ }
+ return p;
+}
+
+
+static int compat53_countlevels (lua_State *L) {
+ lua_Debug ar;
+ int li = 1, le = 1;
+ /* find an upper bound */
+ while (lua_getstack(L, le, &ar)) { li = le; le *= 2; }
+ /* do a binary search */
+ while (li < le) {
+ int m = (li + le)/2;
+ if (lua_getstack(L, m, &ar)) li = m + 1;
+ else le = m;
+ }
+ return le - 1;
+}
+
+static int compat53_findfield (lua_State *L, int objidx, int level) {
+ if (level == 0 || !lua_istable(L, -1))
+ return 0; /* not found */
+ lua_pushnil(L); /* start 'next' loop */
+ while (lua_next(L, -2)) { /* for each pair in table */
+ if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */
+ if (lua_rawequal(L, objidx, -1)) { /* found object? */
+ lua_pop(L, 1); /* remove value (but keep name) */
+ return 1;
+ }
+ else if (compat53_findfield(L, objidx, level - 1)) { /* try recursively */
+ lua_remove(L, -2); /* remove table (but keep name) */
+ lua_pushliteral(L, ".");
+ lua_insert(L, -2); /* place '.' between the two names */
+ lua_concat(L, 3);
+ return 1;
+ }
+ }
+ lua_pop(L, 1); /* remove value */
+ }
+ return 0; /* not found */
+}
+
+static int compat53_pushglobalfuncname (lua_State *L, lua_Debug *ar) {
+ int top = lua_gettop(L);
+ lua_getinfo(L, "f", ar); /* push function */
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ if (compat53_findfield(L, top + 1, 2)) {
+ lua_copy(L, -1, top + 1); /* move name to proper place */
+ lua_pop(L, 2); /* remove pushed values */
+ return 1;
+ }
+ else {
+ lua_settop(L, top); /* remove function and global table */
+ return 0;
+ }
+}
+
+static void compat53_pushfuncname (lua_State *L, lua_Debug *ar) {
+ if (*ar->namewhat != '\0') /* is there a name? */
+ lua_pushfstring(L, "function " LUA_QS, ar->name);
+ else if (*ar->what == 'm') /* main? */
+ lua_pushliteral(L, "main chunk");
+ else if (*ar->what == 'C') {
+ if (compat53_pushglobalfuncname(L, ar)) {
+ lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1));
+ lua_remove(L, -2); /* remove name */
+ }
+ else
+ lua_pushliteral(L, "?");
+ }
+ else
+ lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined);
+}
+
+#define COMPAT53_LEVELS1 12 /* size of the first part of the stack */
+#define COMPAT53_LEVELS2 10 /* size of the second part of the stack */
+
+COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1,
+ const char *msg, int level) {
+ lua_Debug ar;
+ int top = lua_gettop(L);
+ int numlevels = compat53_countlevels(L1);
+ int mark = (numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2) ? COMPAT53_LEVELS1 : 0;
+ if (msg) lua_pushfstring(L, "%s\n", msg);
+ lua_pushliteral(L, "stack traceback:");
+ while (lua_getstack(L1, level++, &ar)) {
+ if (level == mark) { /* too many levels? */
+ lua_pushliteral(L, "\n\t..."); /* add a '...' */
+ level = numlevels - COMPAT53_LEVELS2; /* and skip to last ones */
+ }
+ else {
+ lua_getinfo(L1, "Slnt", &ar);
+ lua_pushfstring(L, "\n\t%s:", ar.short_src);
+ if (ar.currentline > 0)
+ lua_pushfstring(L, "%d:", ar.currentline);
+ lua_pushliteral(L, " in ");
+ compat53_pushfuncname(L, &ar);
+ lua_concat(L, lua_gettop(L) - top);
+ }
+ }
+ lua_concat(L, lua_gettop(L) - top);
+}
+
+
+COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
+ int en = errno; /* calls to Lua API may change this value */
+ if (stat) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ lua_pushnil(L);
+ if (fname)
+ lua_pushfstring(L, "%s: %s", fname, strerror(en));
+ else
+ lua_pushstring(L, strerror(en));
+ lua_pushnumber(L, (lua_Number)en);
+ return 3;
+ }
+}
+
+
+#if !defined(l_inspectstat) && \
+ (defined(unix) || defined(__unix) || defined(__unix__) || \
+ defined(__TOS_AIX__) || defined(_SYSTYPE_BSD) || \
+ (defined(__APPLE__) && defined(__MACH__)))
+/* some form of unix; check feature macros in unistd.h for details */
+# include <unistd.h>
+/* check posix version; the relevant include files and macros probably
+ * were available before 2001, but I'm not sure */
+# if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L
+# include <sys/wait.h>
+# define l_inspectstat(stat,what) \
+ if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \
+ else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; }
+# endif
+#endif
+
+/* provide default (no-op) version */
+#if !defined(l_inspectstat)
+# define l_inspectstat(stat,what) ((void)0)
+#endif
+
+
+COMPAT53_API int luaL_execresult (lua_State *L, int stat) {
+ const char *what = "exit";
+ if (stat == -1)
+ return luaL_fileresult(L, 0, NULL);
+ else {
+ l_inspectstat(stat, what);
+ if (*what == 'e' && stat == 0)
+ lua_pushboolean(L, 1);
+ else
+ lua_pushnil(L);
+ lua_pushstring(L, what);
+ lua_pushinteger(L, stat);
+ return 3;
+ }
+}
+
+
+COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B) {
+ /* make it crash if used via pointer to a 5.1-style luaL_Buffer */
+ B->b.p = NULL;
+ B->b.L = NULL;
+ B->b.lvl = 0;
+ /* reuse the buffer from the 5.1-style luaL_Buffer though! */
+ B->ptr = B->b.buffer;
+ B->capacity = LUAL_BUFFERSIZE;
+ B->nelems = 0;
+ B->L2 = L;
+}
+
+
+COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s) {
+ if (B->capacity - B->nelems < s) { /* needs to grow */
+ char* newptr = NULL;
+ size_t newcap = B->capacity * 2;
+ if (newcap - B->nelems < s)
+ newcap = B->nelems + s;
+ if (newcap < B->capacity) /* overflow */
+ luaL_error(B->L2, "buffer too large");
+ newptr = (char*)lua_newuserdata(B->L2, newcap);
+ memcpy(newptr, B->ptr, B->nelems);
+ if (B->ptr != B->b.buffer)
+ lua_replace(B->L2, -2); /* remove old buffer */
+ B->ptr = newptr;
+ B->capacity = newcap;
+ }
+ return B->ptr+B->nelems;
+}
+
+
+COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l) {
+ memcpy(luaL_prepbuffsize(B, l), s, l);
+ luaL_addsize(B, l);
+}
+
+
+COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B) {
+ size_t len = 0;
+ const char *s = lua_tolstring(B->L2, -1, &len);
+ if (!s)
+ luaL_error(B->L2, "cannot convert value to string");
+ if (B->ptr != B->b.buffer)
+ lua_insert(B->L2, -2); /* userdata buffer must be at stack top */
+ luaL_addlstring(B, s, len);
+ lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1);
+}
+
+
+void luaL_pushresult (luaL_Buffer_53 *B) {
+ lua_pushlstring(B->L2, B->ptr, B->nelems);
+ if (B->ptr != B->b.buffer)
+ lua_replace(B->L2, -2); /* remove userdata buffer */
+}
+
+
+#endif /* Lua 5.1 */
+
+
+
+/* definitions for Lua 5.1 and Lua 5.2 */
+#if defined( LUA_VERSION_NUM ) && LUA_VERSION_NUM <= 502
+
+
+COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i) {
+ index = lua_absindex(L, index);
+ lua_pushinteger(L, i);
+ lua_gettable(L, index);
+ return lua_type(L, -1);
+}
+
+
+COMPAT53_API int lua_isinteger (lua_State *L, int index) {
+ if (lua_type(L, index) == LUA_TNUMBER) {
+ lua_Number n = lua_tonumber(L, index);
+ lua_Integer i = lua_tointeger(L, index);
+ if (i == n)
+ return 1;
+ }
+ return 0;
+}
+
+
+static void compat53_reverse (lua_State *L, int a, int b) {
+ for (; a < b; ++a, --b) {
+ lua_pushvalue(L, a);
+ lua_pushvalue(L, b);
+ lua_replace(L, a);
+ lua_replace(L, b);
+ }
+}
+
+
+COMPAT53_API void lua_rotate (lua_State *L, int idx, int n) {
+ int n_elems = 0;
+ idx = lua_absindex(L, idx);
+ n_elems = lua_gettop(L)-idx+1;
+ if (n < 0)
+ n += n_elems;
+ if ( n > 0 && n < n_elems) {
+ luaL_checkstack(L, 2, "not enough stack slots available");
+ n = n_elems - n;
+ compat53_reverse(L, idx, idx+n-1);
+ compat53_reverse(L, idx+n, idx+n_elems-1);
+ compat53_reverse(L, idx, idx+n_elems-1);
+ }
+}
+
+
+COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i) {
+ luaL_checkstack(L, 1, "not enough stack slots available");
+ index = lua_absindex(L, index);
+ lua_pushinteger(L, i);
+ lua_insert(L, -2);
+ lua_settable(L, index);
+}
+
+
+#if !defined(lua_str2number)
+# define lua_str2number(s, p) strtod((s), (p))
+#endif
+
+COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s) {
+ char* endptr;
+ lua_Number n = lua_str2number(s, &endptr);
+ if (endptr != s) {
+ while (*endptr != '\0' && isspace((unsigned char)*endptr))
+ ++endptr;
+ if (*endptr == '\0') {
+ lua_pushnumber(L, n);
+ return endptr - s + 1;
+ }
+ }
+ return 0;
+}
+
+
+COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
+ if (!luaL_callmeta(L, idx, "__tostring")) {
+ int t = lua_type(L, idx), tt = 0;
+ char const* name = NULL;
+ switch (t) {
+ case LUA_TNIL:
+ lua_pushliteral(L, "nil");
+ break;
+ case LUA_TSTRING:
+ case LUA_TNUMBER:
+ lua_pushvalue(L, idx);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, idx))
+ lua_pushliteral(L, "true");
+ else
+ lua_pushliteral(L, "false");
+ break;
+ default:
+ tt = luaL_getmetafield(L, idx, "__name");
+ name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t);
+ lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx));
+ if (tt != LUA_TNIL)
+ lua_replace(L, -2);
+ break;
+ }
+ } else {
+ if (!lua_isstring(L, -1))
+ luaL_error(L, "'__tostring' must return a string");
+ }
+ return lua_tolstring(L, -1, len);
+}
+
+
+COMPAT53_API void luaL_requiref (lua_State *L, const char *modname,
+ lua_CFunction openf, int glb) {
+ luaL_checkstack(L, 3, "not enough stack slots available");
+ luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED");
+ if (lua_getfield(L, -1, modname) == LUA_TNIL) {
+ lua_pop(L, 1);
+ lua_pushcfunction(L, openf);
+ lua_pushstring(L, modname);
+ lua_call(L, 1, 1);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, modname);
+ }
+ if (glb) {
+ lua_pushvalue(L, -1);
+ lua_setglobal(L, modname);
+ }
+ lua_replace(L, -2);
+}
+
+
+#endif /* Lua 5.1 and 5.2 */
+
+
+#endif /* COMPAT53_C_ */
+
+
+/*********************************************************************
+* This file contains parts of Lua 5.2's and Lua 5.3's source code:
+*
+* Copyright (C) 1994-2014 Lua.org, PUC-Rio.
+*
+* 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.
+*********************************************************************/
+
diff --git a/c-api/compat-5.3.h b/c-api/compat-5.3.h
new file mode 100644
index 0000000..bee77a1
--- /dev/null
+++ b/c-api/compat-5.3.h
@@ -0,0 +1,388 @@
+#ifndef COMPAT53_H_
+#define COMPAT53_H_
+
+#include <stddef.h>
+#include <limits.h>
+#include <string.h>
+#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
+extern "C" {
+#endif
+#include <lua.h>
+#include <lauxlib.h>
+#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
+}
+#endif
+
+
+#if defined(COMPAT53_PREFIX)
+/* - change the symbol names of functions to avoid linker conflicts
+ * - compat-5.3.c needs to be compiled (and linked) separately
+ */
+# if !defined(COMPAT53_API)
+# define COMPAT53_API extern
+# endif
+# undef COMPAT53_INCLUDE_SOURCE
+#else /* COMPAT53_PREFIX */
+/* - make all functions static and include the source.
+ * - compat-5.3.c doesn't need to be compiled (and linked) separately
+ */
+# define COMPAT53_PREFIX compat53
+# undef COMPAT53_API
+# if defined(__GNUC__) || defined(__clang__)
+# define COMPAT53_API __attribute__((__unused__)) static
+# else
+# define COMPAT53_API static
+# endif
+# define COMPAT53_INCLUDE_SOURCE
+#endif /* COMPAT53_PREFIX */
+
+#define COMPAT53_CONCAT_HELPER(a, b) a##b
+#define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b)
+
+
+
+/* declarations for Lua 5.1 */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
+
+/* XXX not implemented:
+ * lua_arith (new operators)
+ * lua_upvalueid
+ * lua_upvaluejoin
+ * lua_version
+ * lua_yieldk
+ * luaL_loadbufferx
+ * luaL_loadfilex
+ */
+
+#ifndef LUA_OK
+# define LUA_OK 0
+#endif
+#ifndef LUA_OPADD
+# define LUA_OPADD 0
+#endif
+#ifndef LUA_OPSUB
+# define LUA_OPSUB 1
+#endif
+#ifndef LUA_OPMUL
+# define LUA_OPMUL 2
+#endif
+#ifndef LUA_OPDIV
+# define LUA_OPDIV 3
+#endif
+#ifndef LUA_OPMOD
+# define LUA_OPMOD 4
+#endif
+#ifndef LUA_OPPOW
+# define LUA_OPPOW 5
+#endif
+#ifndef LUA_OPUNM
+# define LUA_OPUNM 6
+#endif
+#ifndef LUA_OPEQ
+# define LUA_OPEQ 0
+#endif
+#ifndef LUA_OPLT
+# define LUA_OPLT 1
+#endif
+#ifndef LUA_OPLE
+# define LUA_OPLE 2
+#endif
+
+typedef size_t lua_Unsigned;
+
+typedef struct luaL_Buffer_53 {
+ luaL_Buffer b; /* make incorrect code crash! */
+ char *ptr;
+ size_t nelems;
+ size_t capacity;
+ lua_State *L2;
+} luaL_Buffer_53;
+#define luaL_Buffer luaL_Buffer_53
+
+#define lua_absindex COMPAT53_CONCAT(COMPAT53_PREFIX, _absindex)
+COMPAT53_API int lua_absindex (lua_State *L, int i);
+
+#define lua_arith COMPAT53_CONCAT(COMPAT53_PREFIX, _arith)
+COMPAT53_API void lua_arith (lua_State *L, int op);
+
+#define lua_compare COMPAT53_CONCAT(COMPAT53_PREFIX, _compare)
+COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op);
+
+#define lua_copy COMPAT53_CONCAT(COMPAT53_PREFIX, _copy)
+COMPAT53_API void lua_copy (lua_State *L, int from, int to);
+
+#define lua_getuservalue(L, i) \
+ (lua_getfenv((L), (i)), lua_type((L), -1))
+#define lua_setuservalue(L, i) \
+ (luaL_checktype((L), -1, LUA_TTABLE), lua_setfenv((L), (i)))
+
+#define lua_len COMPAT53_CONCAT(COMPAT53_PREFIX, _len)
+COMPAT53_API void lua_len (lua_State *L, int i);
+
+#define lua_pushstring(L, s) \
+ (lua_pushstring((L), (s)), lua_tostring((L), -1))
+
+#define lua_pushlstring(L, s, len) \
+ ((((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len))), lua_tostring((L), -1))
+
+#ifndef luaL_newlibtable
+# define luaL_newlibtable(L, l) \
+ (lua_createtable((L), 0, sizeof((l))/sizeof(*(l))-1))
+#endif
+#ifndef luaL_newlib
+# define luaL_newlib(L, l) \
+ (luaL_newlibtable((L), (l)), luaL_register((L), NULL, (l)))
+#endif
+
+#define lua_pushglobaltable(L) \
+ lua_pushvalue((L), LUA_GLOBALSINDEX)
+
+#define lua_rawgetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawgetp)
+COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p);
+
+#define lua_rawsetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawsetp)
+COMPAT53_API void lua_rawsetp(lua_State *L, int i, const void *p);
+
+#define lua_rawlen(L, i) lua_objlen((L), (i))
+
+#define lua_tointegerx COMPAT53_CONCAT(COMPAT53_PREFIX, _tointegerx)
+COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum);
+
+#define lua_tonumberx COMPAT53_CONCAT(COMPAT53_PREFIX, _tonumberx)
+COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum);
+
+#define luaL_checkversion COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkversion)
+COMPAT53_API void luaL_checkversion (lua_State *L);
+
+#define luaL_checkstack COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkstack_53)
+COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg);
+
+#define luaL_getsubtable COMPAT53_CONCAT(COMPAT53_PREFIX, L_getsubtable)
+COMPAT53_API int luaL_getsubtable (lua_State* L, int i, const char *name);
+
+#define luaL_len COMPAT53_CONCAT(COMPAT53_PREFIX, L_len)
+COMPAT53_API lua_Integer luaL_len (lua_State *L, int i);
+
+#define luaL_setfuncs COMPAT53_CONCAT(COMPAT53_PREFIX, L_setfuncs)
+COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);
+
+#define luaL_setmetatable COMPAT53_CONCAT(COMPAT53_PREFIX, L_setmetatable)
+COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname);
+
+#define luaL_testudata COMPAT53_CONCAT(COMPAT53_PREFIX, L_testudata)
+COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname);
+
+#define luaL_traceback COMPAT53_CONCAT(COMPAT53_PREFIX, L_traceback)
+COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level);
+
+#define luaL_fileresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_fileresult)
+COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname);
+
+#define luaL_execresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_execresult)
+COMPAT53_API int luaL_execresult (lua_State *L, int stat);
+
+#define lua_callk(L, na, nr, ctx, cont) \
+ ((void)(ctx), (void)(cont), lua_call((L), (na), (nr)))
+#define lua_pcallk(L, na, nr, err, ctx, cont) \
+ ((void)(ctx), (void)(cont), lua_pcall((L), (na), (nr), (err)))
+
+#define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53)
+COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B);
+
+#define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53)
+COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s);
+
+#define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53)
+COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l);
+
+#define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53)
+COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B);
+
+#define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53)
+COMPAT53_API void luaL_pushresult (luaL_Buffer_53 *B);
+
+#undef luaL_buffinitsize
+#define luaL_buffinitsize(L, B, s) \
+ (luaL_buffinit((L), (B)), luaL_prepbuffsize((B), (s)))
+
+#undef luaL_prepbuffer
+#define luaL_prepbuffer(B) \
+ luaL_prepbuffsize((B), LUAL_BUFFERSIZE)
+
+#undef luaL_addchar
+#define luaL_addchar(B, c) \
+ ((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize((B), 1)), \
+ ((B)->ptr[(B)->nelems++] = (c)))
+
+#undef luaL_addsize
+#define luaL_addsize(B, s) \
+ ((B)->nelems += (s))
+
+#undef luaL_addstring
+#define luaL_addstring(B, s) \
+ luaL_addlstring((B), (s), strlen((s)))
+
+#undef luaL_pushresultsize
+#define luaL_pushresultsize(B, s) \
+ (luaL_addsize((B), (s)), luaL_pushresult((B)))
+
+#if defined(LUA_COMPAT_APIINTCASTS)
+#define lua_pushunsigned(L, n) \
+ lua_pushinteger((L), (lua_Integer)(n))
+#define lua_tounsignedx(L, i, is) \
+ ((lua_Unsigned)lua_tointegerx((L), (i), (is)))
+#define lua_tounsigned(L, i) \
+ lua_tounsignedx((L), (i), NULL)
+#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)))
+#endif
+
+#endif /* Lua 5.1 only */
+
+
+
+/* declarations for Lua 5.1 and 5.2 */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502
+
+typedef int lua_KContext;
+
+typedef int (*lua_KFunction)(lua_State *L, int status, lua_KContext ctx);
+
+#define lua_dump(L, w, d, s) \
+ ((void)(s), lua_dump((L), (w), (d)))
+
+#define lua_getfield(L, i, k) \
+ (lua_getfield((L), (i), (k)), lua_type((L), -1))
+
+#define lua_gettable(L, i) \
+ (lua_gettable((L), (i)), lua_type((L), -1))
+
+#define lua_geti COMPAT53_CONCAT(COMPAT53_PREFIX, _geti)
+COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i);
+
+#define lua_isinteger COMPAT53_CONCAT(COMPAT53_PREFIX, _isinteger)
+COMPAT53_API int lua_isinteger (lua_State *L, int index);
+
+#define lua_numbertointeger(n, p) \
+ ((*(p) = (lua_Integer)(n)), 1)
+
+#define lua_rawget(L, i) \
+ (lua_rawget((L), (i)), lua_type((L), -1))
+
+#define lua_rawgeti(L, i, n) \
+ (lua_rawgeti((L), (i), (n)), lua_type((L), -1))
+
+#define lua_rotate COMPAT53_CONCAT(COMPAT53_PREFIX, _rotate)
+COMPAT53_API void lua_rotate (lua_State *L, int idx, int n);
+
+#define lua_seti COMPAT53_CONCAT(COMPAT53_PREFIX, _seti)
+COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i);
+
+#define lua_stringtonumber COMPAT53_CONCAT(COMPAT53_PREFIX, _stringtonumber)
+COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s);
+
+#define luaL_tolstring COMPAT53_CONCAT(COMPAT53_PREFIX, L_tolstring)
+COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len);
+
+#define luaL_getmetafield(L, o, e) \
+ (luaL_getmetafield((L), (o), (e)) ? lua_type((L), -1) : LUA_TNIL)
+
+#define luaL_newmetatable(L, tn) \
+ (luaL_newmetatable((L), (tn)) ? (lua_pushstring((L), (tn)), lua_setfield((L), -2, "__name"), 1) : 0)
+
+#define luaL_requiref COMPAT53_CONCAT(COMPAT53_PREFIX, L_requiref_53)
+COMPAT53_API void luaL_requiref (lua_State *L, const char *modname,
+ lua_CFunction openf, int glb );
+
+#endif /* Lua 5.1 and Lua 5.2 */
+
+
+
+/* declarations for Lua 5.2 */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 502
+
+/* XXX not implemented:
+ * lua_isyieldable
+ * lua_getextraspace
+ * lua_arith (new operators)
+ * lua_pushfstring (new formats)
+ */
+
+#define lua_getglobal(L, n) \
+ (lua_getglobal((L), (n)), lua_type((L), -1))
+
+#define lua_getuservalue(L, i) \
+ (lua_getuservalue((L), (i)), lua_type((L), -1))
+
+#define lua_pushlstring(L, s, len) \
+ (((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len)))
+
+#define lua_rawgetp(L, i, p) \
+ (lua_rawgetp((L), (i), (p)), lua_type((L), -1))
+
+#define LUA_KFUNCTION(_name) \
+ static int (_name)(lua_State *L, int status, lua_KContext ctx); \
+ static int (_name ## _52)(lua_State *L) { \
+ lua_KContext ctx; \
+ int status = lua_getctx(L, &ctx); \
+ return (_name)(L, status, ctx); \
+ } \
+ static int (_name)(lua_State *L, int status, lua_KContext ctx)
+
+#define lua_pcallk(L, na, nr, err, ctx, cont) \
+ lua_pcallk((L), (na), (nr), (err), (ctx), cont ## _52)
+
+#define lua_callk(L, na, nr, ctx, cont) \
+ lua_callk((L), (na), (nr), (ctx), cont ## _52)
+
+#define lua_yieldk(L, nr, ctx, cont) \
+ lua_yieldk((L), (nr), (ctx), cont ## _52)
+
+#ifdef lua_call
+# undef lua_call
+# define lua_call(L, na, nr) \
+ (lua_callk)((L), (na), (nr), 0, NULL)
+#endif
+
+#ifdef lua_pcall
+# undef lua_pcall
+# define lua_pcall(L, na, nr, err) \
+ (lua_pcallk)((L), (na), (nr), (err), 0, NULL)
+#endif
+
+#ifdef lua_yield
+# undef lua_yield
+# define lua_yield(L, nr) \
+ (lua_yieldk)((L), (nr), 0, NULL)
+#endif
+
+#endif /* Lua 5.2 only */
+
+
+
+/* other Lua versions */
+#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 || LUA_VERSION_NUM > 503
+
+# error "unsupported Lua version (i.e. not Lua 5.1, 5.2, or 5.3)"
+
+#endif /* other Lua versions except 5.1, 5.2, and 5.3 */
+
+
+
+/* helper macro for defining continuation functions (for every version
+ * *except* Lua 5.2) */
+#ifndef LUA_KFUNCTION
+#define LUA_KFUNCTION(_name) \
+ static int (_name)(lua_State *L, int status, lua_KContext ctx)
+#endif
+
+
+#if defined(COMPAT53_INCLUDE_SOURCE)
+# include "compat-5.3.c"
+#endif
+
+
+#endif /* COMPAT53_H_ */
+
diff --git a/compat53/init.lua b/compat53/init.lua
new file mode 100644
index 0000000..a7f0c80
--- /dev/null
+++ b/compat53/init.lua
@@ -0,0 +1,373 @@
+local lua_version = _VERSION:sub(-3)
+
+
+if lua_version < "5.3" then
+
+ local _G, pairs, require, select, type =
+ _G, pairs, require, select, type
+ local debug, io = debug, io
+ local unpack = lua_version == "5.1" and unpack or table.unpack
+
+ local M = require("compat53.module")
+
+ -- select the most powerful getmetatable function available
+ local gmt = type(debug) == "table" and debug.getmetatable or
+ getmetatable or function() return false end
+ -- metatable for file objects from Lua's standard io library
+ local file_meta = gmt(io.stdout)
+
+
+ -- make '*' optional for file:read and file:lines
+ if type(file_meta) == "table" and type(file_meta.__index) == "table" then
+
+ local function addasterisk(fmt)
+ if type(fmt) == "string" and fmt:sub(1, 1) ~= "*" then
+ return "*"..fmt
+ else
+ return fmt
+ end
+ end
+
+ local file_lines = file_meta.__index.lines
+ file_meta.__index.lines = function(self, ...)
+ local n = select('#', ...)
+ for i = 1, n do
+ local a = select(i, ...)
+ local b = addasterisk(a)
+ -- as an optimization we only allocate a table for the
+ -- modified format arguments when we have a '*' somewhere
+ if a ~= b then
+ local args = { ... }
+ args[i] = b
+ for j = i+1, n do
+ args[j] = addasterisk(args[j])
+ end
+ return file_lines(self, unpack(args, 1, n))
+ end
+ end
+ return file_lines(self, ...)
+ end
+
+ local file_read = file_meta.__index.read
+ file_meta.__index.read = function(self, ...)
+ local n = select('#', ...)
+ for i = 1, n do
+ local a = select(i, ...)
+ local b = addasterisk(a)
+ -- as an optimization we only allocate a table for the
+ -- modified format arguments when we have a '*' somewhere
+ if a ~= b then
+ local args = { ... }
+ args[i] = b
+ for j = i+1, n do
+ args[j] = addasterisk(args[j])
+ end
+ return file_read(self, unpack(args, 1, n))
+ end
+ end
+ return file_read(self, ...)
+ end
+
+ end -- got a valid metatable for file objects
+
+
+ -- changes for Lua 5.1 only
+ if lua_version == "5.1" then
+
+ -- cache globals
+ local error, pcall, rawset, setmetatable, tostring, xpcall =
+ error, pcall, rawset, setmetatable, tostring, xpcall
+ local coroutine, package, string = coroutine, package, string
+ local coroutine_resume = coroutine.resume
+ local coroutine_running = coroutine.running
+ local coroutine_status = coroutine.status
+ local coroutine_yield = coroutine.yield
+ local io_type = io.type
+
+
+ -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag)
+ local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ"
+ local is_luajit52 = is_luajit and
+ #setmetatable({}, { __len = function() return 1 end }) == 1
+
+
+ -- make package.searchers available as an alias for package.loaders
+ local p_index = { searchers = package.loaders }
+ setmetatable(package, {
+ __index = p_index,
+ __newindex = function(p, k, v)
+ if k == "searchers" then
+ rawset(p, "loaders", v)
+ p_index.searchers = v
+ else
+ rawset(p, k, v)
+ end
+ end
+ })
+
+
+ if type(file_meta) == "table" and type(file_meta.__index) == "table" then
+ if not is_luajit then
+ local function helper(_, var_1, ...)
+ if var_1 == nil then
+ if (...) ~= nil then
+ error((...), 2)
+ end
+ end
+ return var_1, ...
+ end
+
+ local function lines_iterator(st)
+ return helper(st, st.f:read(unpack(st, 1, st.n)))
+ end
+
+ local file_write = file_meta.__index.write
+ file_meta.__index.write = function(self, ...)
+ local res, msg, errno = file_write(self, ...)
+ if res then
+ return self
+ else
+ return nil, msg, errno
+ end
+ end
+
+ file_meta.__index.lines = function(self, ...)
+ if io_type(self) == "closed file" then
+ error("attempt to use a closed file", 2)
+ end
+ local st = { f=self, n=select('#', ...), ... }
+ for i = 1, st.n do
+ local t = type(st[i])
+ if t == "string" then
+ local fmt = st[i]:match("^*?([aln])")
+ if not fmt then
+ error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
+ end
+ st[i] = "*"..fmt
+ elseif t ~= "number" then
+ error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
+ end
+ end
+ return lines_iterator, st
+ end
+ end -- not luajit
+ end -- file_meta valid
+
+
+ -- the (x)pcall implementations start a new coroutine internally
+ -- to allow yielding even in Lua 5.1. to allow for accurate
+ -- stack traces we keep track of the nested coroutine activations
+ -- in the weak tables below:
+ local weak_meta = { __mode = "kv" }
+ -- maps the internal pcall coroutines to the user coroutine that
+ -- *should* be running if pcall didn't use coroutines internally
+ local pcall_mainOf = setmetatable({}, weak_meta)
+ -- table that maps each running coroutine started by pcall to
+ -- the coroutine that resumed it (user coroutine *or* pcall
+ -- coroutine!)
+ local pcall_previous = setmetatable({}, weak_meta)
+ -- reverse of `pcall_mainOf`. maps a user coroutine to the
+ -- currently active pcall coroutine started within it
+ local pcall_callOf = setmetatable({}, weak_meta)
+ -- similar to `pcall_mainOf` but is used only while executing
+ -- the error handler of xpcall (thus no nesting is necessary!)
+ local xpcall_running = setmetatable({}, weak_meta)
+
+ -- handle debug functions
+ if type(debug) == "table" then
+ local debug_getinfo = debug.getinfo
+ local debug_traceback = debug.traceback
+
+ if not is_luajit then
+ local function calculate_trace_level(co, level)
+ if level ~= nil then
+ for out = 1, 1/0 do
+ local info = (co==nil) and debug_getinfo(out, "") or debug_getinfo(co, out, "")
+ if info == nil then
+ local max = out-1
+ if level <= max then
+ return level
+ end
+ return nil, level-max
+ end
+ end
+ end
+ return 1
+ end
+
+ local stack_pattern = "\nstack traceback:"
+ local stack_replace = ""
+ function debug.traceback(co, msg, level)
+ local lvl
+ local nilmsg
+ if type(co) ~= "thread" then
+ co, msg, level = coroutine_running(), co, msg
+ end
+ if msg == nil then
+ msg = ""
+ nilmsg = true
+ elseif type(msg) ~= "string" then
+ return msg
+ end
+ if co == nil then
+ msg = debug_traceback(msg, level or 1)
+ else
+ local xpco = xpcall_running[co]
+ if xpco ~= nil then
+ lvl, level = calculate_trace_level(xpco, level)
+ if lvl then
+ msg = debug_traceback(xpco, msg, lvl)
+ else
+ msg = msg..stack_pattern
+ end
+ lvl, level = calculate_trace_level(co, level)
+ if lvl then
+ local trace = debug_traceback(co, "", lvl)
+ msg = msg..trace:gsub(stack_pattern, stack_replace)
+ end
+ else
+ co = pcall_callOf[co] or co
+ lvl, level = calculate_trace_level(co, level)
+ if lvl then
+ msg = debug_traceback(co, msg, lvl)
+ else
+ msg = msg..stack_pattern
+ end
+ end
+ co = pcall_previous[co]
+ while co ~= nil do
+ lvl, level = calculate_trace_level(co, level)
+ if lvl then
+ local trace = debug_traceback(co, "", lvl)
+ msg = msg..trace:gsub(stack_pattern, stack_replace)
+ end
+ co = pcall_previous[co]
+ end
+ end
+ if nilmsg then
+ msg = msg:gsub("^\n", "")
+ end
+ msg = msg:gsub("\n\t%(tail call%): %?", "\000")
+ msg = msg:gsub("\n\t%.%.%.\n", "\001\n")
+ msg = msg:gsub("\n\t%.%.%.$", "\001")
+ msg = msg:gsub("(%z+)\001(%z+)", function(some, other)
+ return "\n\t(..."..#some+#other.."+ tail call(s)...)"
+ end)
+ msg = msg:gsub("\001(%z+)", function(zeros)
+ return "\n\t(..."..#zeros.."+ tail call(s)...)"
+ end)
+ msg = msg:gsub("(%z+)\001", function(zeros)
+ return "\n\t(..."..#zeros.."+ tail call(s)...)"
+ end)
+ msg = msg:gsub("%z+", function(zeros)
+ return "\n\t(..."..#zeros.." tail call(s)...)"
+ end)
+ msg = msg:gsub("\001", function()
+ return "\n\t..."
+ end)
+ return msg
+ end
+ end -- is not luajit
+ end -- debug table available
+
+
+ if not is_luajit52 then
+ local coroutine_running52 = M.coroutine.running
+ function M.coroutine.running()
+ local co, ismain = coroutine_running52()
+ if ismain then
+ return co, true
+ else
+ return pcall_mainOf[co] or co, false
+ end
+ end
+ end
+
+ if not is_luajit then
+ local function pcall_results(current, call, success, ...)
+ if coroutine_status(call) == "suspended" then
+ return pcall_results(current, call, coroutine_resume(call, coroutine_yield(...)))
+ end
+ if pcall_previous then
+ pcall_previous[call] = nil
+ local main = pcall_mainOf[call]
+ if main == current then current = nil end
+ pcall_callOf[main] = current
+ end
+ pcall_mainOf[call] = nil
+ return success, ...
+ end
+
+ local function pcall_exec(current, call, ...)
+ local main = pcall_mainOf[current] or current
+ pcall_mainOf[call] = main
+ if pcall_previous then
+ pcall_previous[call] = current
+ pcall_callOf[main] = call
+ end
+ return pcall_results(current, call, coroutine_resume(call, ...))
+ end
+
+ local coroutine_create52 = M.coroutine.create
+
+ local function pcall_coroutine(func)
+ if type(func) ~= "function" then
+ local callable = func
+ func = function (...) return callable(...) end
+ end
+ return coroutine_create52(func)
+ end
+
+ function M.pcall(func, ...)
+ local current = coroutine_running()
+ if not current then return pcall(func, ...) end
+ return pcall_exec(current, pcall_coroutine(func), ...)
+ end
+
+ local function xpcall_catch(current, call, msgh, success, ...)
+ if not success then
+ xpcall_running[current] = call
+ local ok, result = pcall(msgh, ...)
+ xpcall_running[current] = nil
+ if not ok then
+ return false, "error in error handling ("..tostring(result)..")"
+ end
+ return false, result
+ end
+ return true, ...
+ end
+
+ function M.xpcall(f, msgh, ...)
+ local current = coroutine_running()
+ if not current then
+ local args, n = { ... }, select('#', ...)
+ return xpcall(function() return f(unpack(args, 1, n)) end, msgh)
+ end
+ local call = pcall_coroutine(f)
+ return xpcall_catch(current, call, msgh, pcall_exec(current, call, ...))
+ end
+ end -- not luajit
+
+ end -- lua 5.1
+
+
+ -- handle exporting to global scope
+ local function extend_table(from, to)
+ if from ~= to then
+ for k,v in pairs(from) do
+ if type(v) == "table" and
+ type(to[k]) == "table" and
+ v ~= to[k] then
+ extend_table(v, to[k])
+ else
+ to[k] = v
+ end
+ end
+ end
+ end
+
+ extend_table(M, _G)
+
+end -- lua < 5.3
+
+-- vi: set expandtab softtabstop=3 shiftwidth=3 :
diff --git a/compat53/module.lua b/compat53/module.lua
new file mode 100644
index 0000000..30be6b5
--- /dev/null
+++ b/compat53/module.lua
@@ -0,0 +1,827 @@
+local _G, _VERSION = _G, _VERSION
+local lua_version = _VERSION:sub(-3)
+
+
+local M = _G
+
+if lua_version < "5.3" then
+
+ -- cache globals in upvalues
+ local error, ipairs, pairs, pcall, require, select, setmetatable, type =
+ error, ipairs, pairs, pcall, require, select, setmetatable, type
+ local debug, io, math, package, string, table =
+ debug, io, math, package, string, table
+ local io_lines = io.lines
+ local io_read = io.read
+ local unpack = lua_version == "5.1" and unpack or table.unpack
+
+ -- create module table
+ M = {}
+ local M_meta = {
+ __index = _G,
+ -- __newindex is set at the end
+ }
+ setmetatable(M, M_meta)
+
+ -- create subtables
+ M.io = setmetatable({}, { __index = io })
+ M.math = setmetatable({}, { __index = math })
+ M.string = setmetatable({}, { __index = string })
+ M.table = setmetatable({}, { __index = table })
+ M.utf8 = {}
+
+
+ -- select the most powerful getmetatable function available
+ local gmt = type(debug) == "table" and debug.getmetatable or
+ getmetatable or function() return false end
+
+ -- type checking functions
+ local checkinteger -- forward declararation
+
+ local function argcheck(cond, i, f, extra)
+ if not cond then
+ error("bad argument #"..i.." to '"..f.."' ("..extra..")", 0)
+ end
+ end
+
+
+ -- load utf8 library
+ local utf8_ok, utf8lib = pcall(require, "compat53.utf8")
+ if utf8_ok then
+ if lua_version == "5.1" then
+ utf8lib.charpattern = "[%z\1-\127\194-\244][\128-\191]*"
+ end
+ for k,v in pairs(utf8lib) do
+ M.utf8[k] = v
+ end
+ package.loaded["utf8"] = M.utf8
+ end
+
+
+ -- load table library
+ local table_ok, tablib = pcall(require, "compat53.table")
+ if table_ok then
+ for k,v in pairs(tablib) do
+ M.table[k] = v
+ end
+ end
+
+
+ -- load string packing functions
+ local str_ok, strlib = pcall(require, "compat53.string")
+ if str_ok then
+ for k,v in pairs(strlib) do
+ M.string[k] = v
+ end
+ end
+
+
+ -- try Roberto's struct module for string packing/unpacking if
+ -- compat53.string is unavailable
+ if not str_ok then
+ local struct_ok, struct = pcall(require, "struct")
+ if struct_ok then
+ M.string.pack = struct.pack
+ M.string.packsize = struct.size
+ M.string.unpack = struct.unpack
+ end
+ end
+
+
+ -- update math library
+ do
+ local maxint, minint = 1
+
+ while maxint+1 > maxint and 2*maxint > maxint do
+ maxint = maxint * 2
+ end
+ if 2*maxint <= maxint then
+ maxint = 2*maxint-1
+ minint = -maxint-1
+ else
+ maxint = maxint
+ minint = -maxint
+ end
+ M.math.maxinteger = maxint
+ M.math.mininteger = minint
+
+ function M.math.tointeger(n)
+ if type(n) == "number" and n <= maxint and n >= minint and n % 1 == 0 then
+ return n
+ end
+ return nil
+ end
+
+ function M.math.type(n)
+ if type(n) == "number" then
+ if n <= maxint and n >= minint and n % 1 == 0 then
+ return "integer"
+ else
+ return "float"
+ end
+ else
+ return nil
+ end
+ end
+
+ function checkinteger(x, i, f)
+ local t = type(x)
+ if t ~= "number" then
+ error("bad argument #"..i.." to '"..f..
+ "' (number expected, got "..t..")", 0)
+ elseif x > maxint or x < minint or x % 1 ~= 0 then
+ error("bad argument #"..i.." to '"..f..
+ "' (number has no integer representation)", 0)
+ else
+ return x
+ end
+ end
+
+ function M.math.ult(m, n)
+ m = checkinteger(m, "1", "math.ult")
+ n = checkinteger(n, "2", "math.ult")
+ if m >= 0 and n < 0 then
+ return true
+ elseif m < 0 and n >= 0 then
+ return false
+ else
+ return m < n
+ end
+ end
+ end
+
+
+ -- assert should allow non-string error objects
+ function M.assert(cond, ...)
+ if cond then
+ return cond, ...
+ elseif select('#', ...) > 0 then
+ error((...), 0)
+ else
+ error("assertion failed!", 0)
+ end
+ end
+
+
+ -- ipairs should respect __index metamethod
+ do
+ local function ipairs_iterator(st, var)
+ var = var + 1
+ local val = st[var]
+ if val ~= nil then
+ return var, st[var]
+ end
+ end
+ function M.ipairs(t)
+ if gmt(t) ~= nil then -- t has metatable
+ return ipairs_iterator, t, 0
+ else
+ return ipairs(t)
+ end
+ end
+ end
+
+
+ -- make '*' optional for io.read and io.lines
+ do
+ local function addasterisk(fmt)
+ if type(fmt) == "string" and fmt:sub(1, 1) ~= "*" then
+ return "*"..fmt
+ else
+ return fmt
+ end
+ end
+
+ function M.io.read(...)
+ local n = select('#', ...)
+ for i = 1, n do
+ local a = select(i, ...)
+ local b = addasterisk(a)
+ -- as an optimization we only allocate a table for the
+ -- modified format arguments when we have a '*' somewhere.
+ if a ~= b then
+ local args = { ... }
+ args[i] = b
+ for j = i+1, n do
+ args[j] = addasterisk(args[j])
+ end
+ return io_read(unpack(args, 1, n))
+ end
+ end
+ return io_read(...)
+ end
+
+ -- PUC-Rio Lua 5.1 uses a different implementation for io.lines!
+ function M.io.lines(...)
+ local n = select('#', ...)
+ for i = 2, n do
+ local a = select(i, ...)
+ local b = addasterisk(a)
+ -- as an optimization we only allocate a table for the
+ -- modified format arguments when we have a '*' somewhere.
+ if a ~= b then
+ local args = { ... }
+ args[i] = b
+ for j = i+1, n do
+ args[j] = addasterisk(args[j])
+ end
+ return io_lines(unpack(args, 1, n))
+ end
+ end
+ return io_lines(...)
+ end
+ end
+
+
+ -- update table library (if C module not available)
+ if not table_ok then
+ local table_concat = table.concat
+ local table_insert = table.insert
+ local table_remove = table.remove
+ local table_sort = table.sort
+
+ function M.table.concat(list, sep, i, j)
+ local mt = gmt(list)
+ if type(mt) == "table" and type(mt.__len) == "function" then
+ local src = list
+ list, i, j = {}, i or 1, j or mt.__len(src)
+ for k = i, j do
+ list[k] = src[k]
+ end
+ end
+ return table_concat(list, sep, i, j)
+ end
+
+ function M.table.insert(list, ...)
+ local mt = gmt(list)
+ local has_mt = type(mt) == "table"
+ local has_len = has_mt and type(mt.__len) == "function"
+ if has_mt and (has_len or mt.__index or mt.__newindex) then
+ local e = (has_len and mt.__len(list) or #list)+1
+ local nargs, pos, value = select('#', ...), ...
+ if nargs == 1 then
+ pos, value = e, pos
+ elseif nargs == 2 then
+ pos = checkinteger(pos, "2", "table.insert")
+ argcheck(1 <= pos and pos <= e, "2", "table.insert",
+ "position out of bounds" )
+ else
+ error("wrong number of arguments to 'insert'", 0)
+ end
+ for i = e-1, pos, -1 do
+ list[i+1] = list[i]
+ end
+ list[pos] = value
+ else
+ return table_insert(list, ...)
+ end
+ end
+
+ function M.table.move(a1, f, e, t, a2)
+ a2 = a2 or a1
+ f = checkinteger(f, "2", "table.move")
+ argcheck(f > 0, "2", "table.move",
+ "initial position must be positive")
+ e = checkinteger(e, "3", "table.move")
+ t = checkinteger(t, "4", "table.move")
+ if e >= f then
+ local m, n, d = 0, e-f, 1
+ if t > f then m, n, d = n, m, -1 end
+ for i = m, n, d do
+ a2[t+i] = a1[f+i]
+ end
+ end
+ return a2
+ end
+
+ function M.table.remove(list, pos)
+ local mt = gmt(list)
+ local has_mt = type(mt) == "table"
+ local has_len = has_mt and type(mt.__len) == "function"
+ if has_mt and (has_len or mt.__index or mt.__newindex) then
+ local e = (has_len and mt.__len(list) or #list)
+ pos = pos ~= nil and checkinteger(pos, "2", "table.remove") or e
+ if pos ~= e then
+ argcheck(1 <= pos and pos <= e+1, "2", "table.remove",
+ "position out of bounds" )
+ end
+ local result = list[pos]
+ while pos < e do
+ list[pos] = list[pos+1]
+ pos = pos + 1
+ end
+ list[pos] = nil
+ return result
+ else
+ return table_remove(list, pos)
+ end
+ end
+
+ do
+ local function pivot(list, cmp, a, b)
+ local m = b - a
+ if m > 2 then
+ local c = a + (m-m%2)/2
+ local x, y, z = list[a], list[b], list[c]
+ if not cmp(x, y) then
+ x, y, a, b = y, x, b, a
+ end
+ if not cmp(y, z) then
+ y, b = z, c
+ end
+ if not cmp(x, y) then
+ y, b = x, a
+ end
+ return b, y
+ else
+ return b, list[b]
+ end
+ end
+
+ local function lt_cmp(a, b)
+ return a < b
+ end
+
+ local function qsort(list, cmp, b, e)
+ if b < e then
+ local i, j, k, val = b, e, pivot(list, cmp, b, e)
+ while i < j do
+ while i < j and cmp(list[i], val) do
+ i = i + 1
+ end
+ while i < j and not cmp(list[j], val) do
+ j = j - 1
+ end
+ if i < j then
+ list[i], list[j] = list[j], list[i]
+ if i == k then k = j end -- update pivot position
+ i, j = i+1, j-1
+ end
+ end
+ if i ~= k and not cmp(list[i], val) then
+ list[i], list[k] = val, list[i]
+ k = i -- update pivot position
+ end
+ qsort(list, cmp, b, i == k and i-1 or i)
+ return qsort(list, cmp, i+1, e)
+ end
+ end
+
+ function M.table.sort(list, cmp)
+ local mt = gmt(list)
+ local has_mt = type(mt) == "table"
+ local has_len = has_mt and type(mt.__len) == "function"
+ if has_len then
+ cmp = cmp or lt_cmp
+ local len = mt.__len(list)
+ return qsort(list, cmp, 1, len)
+ else
+ return table_sort(list, cmp)
+ end
+ end
+ end
+
+ local function unpack_helper(list, i, j, ...)
+ if j < i then
+ return ...
+ else
+ return unpack_helper(list, i, j-1, list[j], ...)
+ end
+ end
+ function M.table.unpack(list, i, j)
+ local mt = gmt(list)
+ local has_mt = type(mt) == "table"
+ local has_len = has_mt and type(mt.__len) == "function"
+ if has_mt and (has_len or mt.__index) then
+ i, j = i or 1, j or (has_len and mt.__len(list)) or #list
+ return unpack_helper(list, i, j)
+ else
+ return unpack(list, i, j)
+ end
+ end
+ end -- update table library
+
+
+ -- bring Lua 5.1 (and LuaJIT) up to speed with Lua 5.2
+ if lua_version == "5.1" then
+ -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag)
+ local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ"
+ local is_luajit52 = is_luajit and
+ #setmetatable({}, { __len = function() return 1 end }) == 1
+
+ -- cache globals in upvalues
+ local load, loadfile, loadstring, setfenv, xpcall =
+ load, loadfile, loadstring, setfenv, xpcall
+ local coroutine, os = coroutine, os
+ local coroutine_create = coroutine.create
+ local coroutine_resume = coroutine.resume
+ local coroutine_running = coroutine.running
+ local coroutine_status = coroutine.status
+ local coroutine_yield = coroutine.yield
+ local io_input = io.input
+ local io_open = io.open
+ local io_output = io.output
+ local io_write = io.write
+ local math_log = math.log
+ local os_execute = os.execute
+ local string_find = string.find
+ local string_format = string.format
+ local string_gmatch = string.gmatch
+ local string_gsub = string.gsub
+ local string_match = string.match
+ local string_rep = string.rep
+ local table_concat = table.concat
+
+ -- create subtables
+ M.coroutine = setmetatable({}, { __index = coroutine })
+ M.os = setmetatable({}, { __index = os })
+ M.package = setmetatable({}, { __index = package })
+
+ -- handle debug functions
+ if type(debug) == "table" then
+ local debug_setfenv = debug.setfenv
+ local debug_getfenv = debug.getfenv
+ local debug_setmetatable = debug.setmetatable
+
+ M.debug = setmetatable({}, { __index = debug })
+
+ if not is_luajit52 then
+ function M.debug.setuservalue(obj, value)
+ if type(obj) ~= "userdata" then
+ error("bad argument #1 to 'setuservalue' (userdata expected, got "..
+ type(obj)..")", 2)
+ end
+ if value == nil then value = _G end
+ if type(value) ~= "table" then
+ error("bad argument #2 to 'setuservalue' (table expected, got "..
+ type(value)..")", 2)
+ end
+ return debug_setfenv(obj, value)
+ end
+
+ function M.debug.getuservalue(obj)
+ if type(obj) ~= "userdata" then
+ return nil
+ else
+ local v = debug_getfenv(obj)
+ if v == _G or v == package then
+ return nil
+ end
+ return v
+ end
+ end
+
+ function M.debug.setmetatable(value, tab)
+ debug_setmetatable(value, tab)
+ return value
+ end
+ end -- not luajit with compat52 enabled
+ end -- debug table available
+
+
+ if not is_luajit52 then
+ function M.pairs(t)
+ local mt = gmt(t)
+ if type(mt) == "table" and type(mt.__pairs) == "function" then
+ return mt.__pairs(t)
+ else
+ return pairs(t)
+ end
+ end
+ end
+
+
+ if not is_luajit then
+ local function check_mode(mode, prefix)
+ local has = { text = false, binary = false }
+ for i = 1,#mode do
+ local c = mode:sub(i, i)
+ if c == "t" then has.text = true end
+ if c == "b" then has.binary = true end
+ end
+ local t = prefix:sub(1, 1) == "\27" and "binary" or "text"
+ if not has[t] then
+ return "attempt to load a "..t.." chunk (mode is '"..mode.."')"
+ end
+ end
+
+ function M.load(ld, source, mode, env)
+ mode = mode or "bt"
+ local chunk, msg
+ if type( ld ) == "string" then
+ if mode ~= "bt" then
+ local merr = check_mode(mode, ld)
+ if merr then return nil, merr end
+ end
+ chunk, msg = loadstring(ld, source)
+ else
+ local ld_type = type(ld)
+ if ld_type ~= "function" then
+ error("bad argument #1 to 'load' (function expected, got "..
+ ld_type..")", 2)
+ end
+ if mode ~= "bt" then
+ local checked, merr = false, nil
+ local function checked_ld()
+ if checked then
+ return ld()
+ else
+ checked = true
+ local v = ld()
+ merr = check_mode(mode, v or "")
+ if merr then return nil end
+ return v
+ end
+ end
+ chunk, msg = load(checked_ld, source)
+ if merr then return nil, merr end
+ else
+ chunk, msg = load(ld, source)
+ end
+ end
+ if not chunk then
+ return chunk, msg
+ end
+ if env ~= nil then
+ setfenv(chunk, env)
+ end
+ return chunk
+ end
+
+ M.loadstring = M.load
+
+ function M.loadfile(file, mode, env)
+ mode = mode or "bt"
+ if mode ~= "bt" then
+ local f = io_open(file, "rb")
+ if f then
+ local prefix = f:read(1)
+ f:close()
+ if prefix then
+ local merr = check_mode(mode, prefix)
+ if merr then return nil, merr end
+ end
+ end
+ end
+ local chunk, msg = loadfile(file)
+ if not chunk then
+ return chunk, msg
+ end
+ if env ~= nil then
+ setfenv(chunk, env)
+ end
+ return chunk
+ end
+ end -- not luajit
+
+
+ if not is_luajit52 then
+ function M.rawlen(v)
+ local t = type(v)
+ if t ~= "string" and t ~= "table" then
+ error("bad argument #1 to 'rawlen' (table or string expected)", 2)
+ end
+ return #v
+ end
+ end
+
+
+ if not is_luajit then
+ function M.xpcall(f, msgh, ...)
+ local args, n = { ... }, select('#', ...)
+ return xpcall(function() return f(unpack(args, 1, n)) end, msgh)
+ end
+ end
+
+
+ if not is_luajit52 then
+ function M.os.execute(cmd)
+ local code = os_execute(cmd)
+ -- Lua 5.1 does not report exit by signal.
+ if code == 0 then
+ return true, "exit", code
+ else
+ if package.config:sub(1, 1) == '/' then
+ code = code/256 -- only correct on Linux!
+ end
+ return nil, "exit", code
+ end
+ end
+ end
+
+
+ if not table_ok and not is_luajit52 then
+ M.table.pack = function(...)
+ return { n = select('#', ...), ... }
+ end
+ end
+
+
+ local main_coroutine = coroutine_create(function() end)
+
+ function M.coroutine.create(func)
+ local success, result = pcall(coroutine_create, func)
+ if not success then
+ if type(func) ~= "function" then
+ error("bad argument #1 (function expected)", 0)
+ end
+ result = coroutine_create(function(...) return func(...) end)
+ end
+ return result
+ end
+
+ if not is_luajit52 then
+ function M.coroutine.running()
+ local co = coroutine_running()
+ if co then
+ return co, false
+ else
+ return main_coroutine, true
+ end
+ end
+ end
+
+ function M.coroutine.yield(...)
+ local co, flag = coroutine_running()
+ if co and not flag then
+ return coroutine_yield(...)
+ else
+ error("attempt to yield from outside a coroutine", 0)
+ end
+ end
+
+ if not is_luajit then
+ function M.coroutine.resume(co, ...)
+ if co == main_coroutine then
+ return false, "cannot resume non-suspended coroutine"
+ else
+ return coroutine_resume(co, ...)
+ end
+ end
+
+ function M.coroutine.status(co)
+ local notmain = coroutine_running()
+ if co == main_coroutine then
+ return notmain and "normal" or "running"
+ else
+ return coroutine_status(co)
+ end
+ end
+ end -- not luajit
+
+
+ if not is_luajit then
+ M.math.log = function(x, base)
+ if base ~= nil then
+ return math_log(x)/math_log(base)
+ else
+ return math_log(x)
+ end
+ end
+ end
+
+
+ if not is_luajit then
+ function M.package.searchpath(name, path, sep, rep)
+ sep = (sep or "."):gsub("(%p)", "%%%1")
+ rep = (rep or package.config:sub(1, 1)):gsub("(%%)", "%%%1")
+ local pname = name:gsub(sep, rep):gsub("(%%)", "%%%1")
+ local msg = {}
+ for subpath in path:gmatch("[^;]+") do
+ local fpath = subpath:gsub("%?", pname)
+ local f = io_open(fpath, "r")
+ if f then
+ f:close()
+ return fpath
+ end
+ msg[#msg+1] = "\n\tno file '" .. fpath .. "'"
+ end
+ return nil, table_concat(msg)
+ end
+ end
+
+
+ local function fix_pattern(pattern)
+ return (string_gsub(pattern, "%z", "%%z"))
+ end
+
+ function M.string.find(s, pattern, ...)
+ return string_find(s, fix_pattern(pattern), ...)
+ end
+
+ function M.string.gmatch(s, pattern)
+ return string_gmatch(s, fix_pattern(pattern))
+ end
+
+ function M.string.gsub(s, pattern, ...)
+ return string_gsub(s, fix_pattern(pattern), ...)
+ end
+
+ function M.string.match(s, pattern, ...)
+ return string_match(s, fix_pattern(pattern), ...)
+ end
+
+ if not is_luajit then
+ function M.string.rep(s, n, sep)
+ if sep ~= nil and sep ~= "" and n >= 2 then
+ return s .. string_rep(sep..s, n-1)
+ else
+ return string_rep(s, n)
+ end
+ end
+ end
+
+ if not is_luajit then
+ do
+ local addqt = {
+ ["\n"] = "\\\n",
+ ["\\"] = "\\\\",
+ ["\""] = "\\\""
+ }
+
+ local function addquoted(c, d)
+ return (addqt[c] or string_format(d~="" and "\\%03d" or "\\%d", c:byte()))..d
+ end
+
+ function M.string.format(fmt, ...)
+ local args, n = { ... }, select('#', ...)
+ local i = 0
+ local function adjust_fmt(lead, mods, kind)
+ if #lead % 2 == 0 then
+ i = i + 1
+ if kind == "s" then
+ args[i] = _G.tostring(args[i])
+ elseif kind == "q" then
+ args[i] = '"'..string_gsub(args[i], "([%z%c\\\"\n])(%d?)", addquoted)..'"'
+ return lead.."%"..mods.."s"
+ end
+ end
+ end
+ fmt = string_gsub(fmt, "(%%*)%%([%d%.%-%+%# ]*)(%a)", adjust_fmt)
+ return string_format(fmt, unpack(args, 1, n))
+ end
+ end
+ end
+
+
+ function M.io.write(...)
+ local res, msg, errno = io_write(...)
+ if res then
+ return io_output()
+ else
+ return nil, msg, errno
+ end
+ end
+
+ if not is_luajit then
+ local function helper(st, var_1, ...)
+ if var_1 == nil then
+ if st.doclose then st.f:close() end
+ if (...) ~= nil then
+ error((...), 2)
+ end
+ end
+ return var_1, ...
+ end
+
+ local function lines_iterator(st)
+ return helper(st, st.f:read(unpack(st, 1, st.n)))
+ end
+
+ function M.io.lines(fname, ...)
+ local doclose, file, msg
+ if fname ~= nil then
+ doclose, file, msg = true, io_open(fname, "r")
+ if not file then error(msg, 2) end
+ else
+ doclose, file = false, io_input()
+ end
+ local st = { f=file, doclose=doclose, n=select('#', ...), ... }
+ for i = 1, st.n do
+ local t = type(st[i])
+ if t == "string" then
+ local fmt = st[i]:match("^%*?([aln])")
+ if not fmt then
+ error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
+ end
+ st[i] = "*"..fmt
+ elseif t ~= "number" then
+ error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
+ end
+ end
+ return lines_iterator, st
+ end
+ end -- not luajit
+
+ end -- lua 5.1
+
+ -- further write should be forwarded to _G
+ M_meta.__newindex = _G
+
+end -- lua < 5.3
+
+
+-- return module table
+return M
+
+-- vi: set expandtab softtabstop=3 shiftwidth=3 :
diff --git a/lprefix.h b/lprefix.h
new file mode 100644
index 0000000..0eb149f
--- /dev/null
+++ b/lprefix.h
@@ -0,0 +1,175 @@
+/*
+** $Id: lprefix.h,v 1.2 2014/12/29 16:54:13 roberto Exp $
+** Definitions for Lua code that must come before any other header file
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lprefix_h
+#define lprefix_h
+
+
+/*
+** Allows POSIX/XSI stuff
+*/
+#if !defined(LUA_USE_C89) /* { */
+
+#if !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 600
+#elif _XOPEN_SOURCE == 0
+#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */
+#endif
+
+/*
+** Allows manipulation of large files in gcc and some other compilers
+*/
+#if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS)
+#define _LARGEFILE_SOURCE 1
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#endif /* } */
+
+
+/*
+** Windows stuff
+*/
+#if defined(_WIN32) /* { */
+
+#if !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */
+#endif
+
+#endif /* } */
+
+
+/* COMPAT53 adaptation */
+#include "c-api/compat-5.3.h"
+
+#undef LUAMOD_API
+#define LUAMOD_API extern
+
+
+#ifdef lutf8lib_c
+# define luaopen_utf8 luaopen_compat53_utf8
+/* we don't support the %U format string of lua_pushfstring!
+ * code below adapted from the Lua 5.3 sources:
+ */
+static const char *compat53_utf8_escape (lua_State* L, long x) {
+ if (x < 0x80) { /* ASCII */
+ char c = (char)x;
+ lua_pushlstring(L, &c, 1);
+ } else {
+ char buff[8] = { 0 };
+ unsigned int mfb = 0x3f;
+ int n = 1;
+ do {
+ buff[8 - (n++)] = (char)(0x80|(x & 0x3f));
+ x >>= 6;
+ mfb >>= 1;
+ } while (x > mfb);
+ buff[8-n] = (char)((~mfb << 1) | x);
+ lua_pushlstring(L, buff+8-n, n);
+ }
+ return lua_tostring(L, -1);
+}
+# define lua_pushfstring(L, fmt, l) \
+ compat53_utf8_escape(L, l)
+#endif
+
+
+#ifdef ltablib_c
+# define luaopen_table luaopen_compat53_table
+# ifndef LUA_MAXINTEGER
+/* conservative estimate: */
+# define LUA_MAXINTEGER INT_MAX
+# endif
+#endif /* ltablib_c */
+
+
+#ifdef lstrlib_c
+#include <locale.h>
+#include <lualib.h>
+/* move the string library open function out of the way (we only take
+ * the string packing functions)!
+ */
+# define luaopen_string luaopen_string_XXX
+/* used in string.format implementation, which we don't use: */
+# ifndef LUA_INTEGER_FRMLEN
+# define LUA_INTEGER_FRMLEN ""
+# define LUA_NUMBER_FRMLEN ""
+# endif
+# ifndef LUA_MININTEGER
+# define LUA_MININTEGER 0
+# endif
+# ifndef LUA_INTEGER_FMT
+# define LUA_INTEGER_FMT "%d"
+# endif
+# ifndef LUAI_UACINT
+# define LUAI_UACINT lua_Integer
+# endif
+/* different Lua 5.3 versions have conflicting variants of this macro
+ * in luaconf.h, there's a fallback implementation in lstrlib.c, and
+ * the macro isn't used for string (un)packing anyway!
+ * */
+# undef lua_number2strx
+# if LUA_VERSION_NUM < 503
+/* lstrlib assumes that lua_Integer and lua_Unsigned have the same
+ * size, so we use the unsigned equivalent of ptrdiff_t! */
+# define lua_Unsigned size_t
+# endif
+# ifndef l_mathlim
+# ifdef LUA_NUMBER_DOUBLE
+# define l_mathlim(n) (DBL_##n)
+# else
+# define l_mathlim(n) (FLT_##n)
+# endif
+# endif
+# ifndef l_mathop
+# ifdef LUA_NUMBER_DOUBLE
+# define l_mathop(op) op
+# else
+# define l_mathop(op) op##f
+# endif
+# endif
+# ifndef lua_getlocaledecpoint
+# define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
+# endif
+# ifndef l_sprintf
+# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+# define l_sprintf(s,sz,f,i) (snprintf(s, sz, f, i))
+# else
+# define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s, f, i))
+# endif
+# endif
+
+static int str_pack (lua_State *L);
+static int str_packsize (lua_State *L);
+static int str_unpack (lua_State *L);
+LUAMOD_API int luaopen_compat53_string (lua_State *L) {
+ luaL_Reg const funcs[] = {
+ { "pack", str_pack },
+ { "packsize", str_packsize },
+ { "unpack", str_unpack },
+ { NULL, NULL }
+ };
+ luaL_newlib(L, funcs);
+ return 1;
+}
+/* fake CLANG feature detection on other compilers */
+# ifndef __has_attribute
+# define __has_attribute(x) 0
+# endif
+/* make luaopen_string(_XXX) static, so it (and all other referenced
+ * string functions) won't be included in the resulting dll
+ * (hopefully).
+ */
+# undef LUAMOD_API
+# if defined(__GNUC__) || __has_attribute(__unused__)
+# define LUAMOD_API __attribute__((__unused__)) static
+# else
+# define LUAMOD_API static
+# endif
+#endif /* lstrlib.c */
+
+#endif
+
diff --git a/lstrlib.c b/lstrlib.c
new file mode 100644
index 0000000..c7aa755
--- /dev/null
+++ b/lstrlib.c
@@ -0,0 +1,1584 @@
+/*
+** $Id: lstrlib.c,v 1.254 2016/12/22 13:08:50 roberto Exp $
+** Standard library for string operations and pattern-matching
+** See Copyright Notice in lua.h
+*/
+
+#define lstrlib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <ctype.h>
+#include <float.h>
+#include <limits.h>
+#include <locale.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/*
+** maximum number of captures that a pattern can do during
+** pattern-matching. This limit is arbitrary, but must fit in
+** an unsigned char.
+*/
+#if !defined(LUA_MAXCAPTURES)
+#define LUA_MAXCAPTURES 32
+#endif
+
+
+/* macro to 'unsign' a character */
+#define uchar(c) ((unsigned char)(c))
+
+
+/*
+** Some sizes are better limited to fit in 'int', but must also fit in
+** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.)
+*/
+#define MAX_SIZET ((size_t)(~(size_t)0))
+
+#define MAXSIZE \
+ (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX))
+
+
+
+
+static int str_len (lua_State *L) {
+ size_t l;
+ luaL_checklstring(L, 1, &l);
+ lua_pushinteger(L, (lua_Integer)l);
+ return 1;
+}
+
+
+/* translate a relative string position: negative means back from end */
+static lua_Integer posrelat (lua_Integer pos, size_t len) {
+ if (pos >= 0) return pos;
+ else if (0u - (size_t)pos > len) return 0;
+ else return (lua_Integer)len + pos + 1;
+}
+
+
+static int str_sub (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ lua_Integer start = posrelat(luaL_checkinteger(L, 2), l);
+ lua_Integer end = posrelat(luaL_optinteger(L, 3, -1), l);
+ if (start < 1) start = 1;
+ if (end > (lua_Integer)l) end = l;
+ if (start <= end)
+ lua_pushlstring(L, s + start - 1, (size_t)(end - start) + 1);
+ else lua_pushliteral(L, "");
+ return 1;
+}
+
+
+static int str_reverse (lua_State *L) {
+ size_t l, i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ char *p = luaL_buffinitsize(L, &b, l);
+ for (i = 0; i < l; i++)
+ p[i] = s[l - i - 1];
+ luaL_pushresultsize(&b, l);
+ return 1;
+}
+
+
+static int str_lower (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ char *p = luaL_buffinitsize(L, &b, l);
+ for (i=0; i<l; i++)
+ p[i] = tolower(uchar(s[i]));
+ luaL_pushresultsize(&b, l);
+ return 1;
+}
+
+
+static int str_upper (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ char *p = luaL_buffinitsize(L, &b, l);
+ for (i=0; i<l; i++)
+ p[i] = toupper(uchar(s[i]));
+ luaL_pushresultsize(&b, l);
+ return 1;
+}
+
+
+static int str_rep (lua_State *L) {
+ size_t l, lsep;
+ const char *s = luaL_checklstring(L, 1, &l);
+ lua_Integer n = luaL_checkinteger(L, 2);
+ const char *sep = luaL_optlstring(L, 3, "", &lsep);
+ if (n <= 0) lua_pushliteral(L, "");
+ else if (l + lsep < l || l + lsep > MAXSIZE / n) /* may overflow? */
+ return luaL_error(L, "resulting string too large");
+ else {
+ size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep;
+ luaL_Buffer b;
+ char *p = luaL_buffinitsize(L, &b, totallen);
+ while (n-- > 1) { /* first n-1 copies (followed by separator) */
+ memcpy(p, s, l * sizeof(char)); p += l;
+ if (lsep > 0) { /* empty 'memcpy' is not that cheap */
+ memcpy(p, sep, lsep * sizeof(char));
+ p += lsep;
+ }
+ }
+ memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */
+ luaL_pushresultsize(&b, totallen);
+ }
+ return 1;
+}
+
+
+static int str_byte (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ lua_Integer posi = posrelat(luaL_optinteger(L, 2, 1), l);
+ lua_Integer pose = posrelat(luaL_optinteger(L, 3, posi), l);
+ int n, i;
+ if (posi < 1) posi = 1;
+ if (pose > (lua_Integer)l) pose = l;
+ if (posi > pose) return 0; /* empty interval; return no values */
+ if (pose - posi >= INT_MAX) /* arithmetic overflow? */
+ return luaL_error(L, "string slice too long");
+ n = (int)(pose - posi) + 1;
+ luaL_checkstack(L, n, "string slice too long");
+ for (i=0; i<n; i++)
+ lua_pushinteger(L, uchar(s[posi+i-1]));
+ return n;
+}
+
+
+static int str_char (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ luaL_Buffer b;
+ char *p = luaL_buffinitsize(L, &b, n);
+ for (i=1; i<=n; i++) {
+ lua_Integer c = luaL_checkinteger(L, i);
+ luaL_argcheck(L, uchar(c) == c, i, "value out of range");
+ p[i - 1] = uchar(c);
+ }
+ luaL_pushresultsize(&b, n);
+ return 1;
+}
+
+
+static int writer (lua_State *L, const void *b, size_t size, void *B) {
+ (void)L;
+ luaL_addlstring((luaL_Buffer *) B, (const char *)b, size);
+ return 0;
+}
+
+
+static int str_dump (lua_State *L) {
+ luaL_Buffer b;
+ int strip = lua_toboolean(L, 2);
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ lua_settop(L, 1);
+ luaL_buffinit(L,&b);
+ if (lua_dump(L, writer, &b, strip) != 0)
+ return luaL_error(L, "unable to dump given function");
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+
+/*
+** {======================================================
+** PATTERN MATCHING
+** =======================================================
+*/
+
+
+#define CAP_UNFINISHED (-1)
+#define CAP_POSITION (-2)
+
+
+typedef struct MatchState {
+ const char *src_init; /* init of source string */
+ const char *src_end; /* end ('\0') of source string */
+ const char *p_end; /* end ('\0') of pattern */
+ lua_State *L;
+ int matchdepth; /* control for recursive depth (to avoid C stack overflow) */
+ unsigned char level; /* total number of captures (finished or unfinished) */
+ struct {
+ const char *init;
+ ptrdiff_t len;
+ } capture[LUA_MAXCAPTURES];
+} MatchState;
+
+
+/* recursive function */
+static const char *match (MatchState *ms, const char *s, const char *p);
+
+
+/* maximum recursion depth for 'match' */
+#if !defined(MAXCCALLS)
+#define MAXCCALLS 200
+#endif
+
+
+#define L_ESC '%'
+#define SPECIALS "^$*+?.([%-"
+
+
+static int check_capture (MatchState *ms, int l) {
+ l -= '1';
+ if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
+ return luaL_error(ms->L, "invalid capture index %%%d", l + 1);
+ return l;
+}
+
+
+static int capture_to_close (MatchState *ms) {
+ int level = ms->level;
+ for (level--; level>=0; level--)
+ if (ms->capture[level].len == CAP_UNFINISHED) return level;
+ return luaL_error(ms->L, "invalid pattern capture");
+}
+
+
+static const char *classend (MatchState *ms, const char *p) {
+ switch (*p++) {
+ case L_ESC: {
+ if (p == ms->p_end)
+ luaL_error(ms->L, "malformed pattern (ends with '%%')");
+ return p+1;
+ }
+ case '[': {
+ if (*p == '^') p++;
+ do { /* look for a ']' */
+ if (p == ms->p_end)
+ luaL_error(ms->L, "malformed pattern (missing ']')");
+ if (*(p++) == L_ESC && p < ms->p_end)
+ p++; /* skip escapes (e.g. '%]') */
+ } while (*p != ']');
+ return p+1;
+ }
+ default: {
+ return p;
+ }
+ }
+}
+
+
+static int match_class (int c, int cl) {
+ int res;
+ switch (tolower(cl)) {
+ case 'a' : res = isalpha(c); break;
+ case 'c' : res = iscntrl(c); break;
+ case 'd' : res = isdigit(c); break;
+ case 'g' : res = isgraph(c); break;
+ case 'l' : res = islower(c); break;
+ case 'p' : res = ispunct(c); break;
+ case 's' : res = isspace(c); break;
+ case 'u' : res = isupper(c); break;
+ case 'w' : res = isalnum(c); break;
+ case 'x' : res = isxdigit(c); break;
+ case 'z' : res = (c == 0); break; /* deprecated option */
+ default: return (cl == c);
+ }
+ return (islower(cl) ? res : !res);
+}
+
+
+static int matchbracketclass (int c, const char *p, const char *ec) {
+ int sig = 1;
+ if (*(p+1) == '^') {
+ sig = 0;
+ p++; /* skip the '^' */
+ }
+ while (++p < ec) {
+ if (*p == L_ESC) {
+ p++;
+ if (match_class(c, uchar(*p)))
+ return sig;
+ }
+ else if ((*(p+1) == '-') && (p+2 < ec)) {
+ p+=2;
+ if (uchar(*(p-2)) <= c && c <= uchar(*p))
+ return sig;
+ }
+ else if (uchar(*p) == c) return sig;
+ }
+ return !sig;
+}
+
+
+static int singlematch (MatchState *ms, const char *s, const char *p,
+ const char *ep) {
+ if (s >= ms->src_end)
+ return 0;
+ else {
+ int c = uchar(*s);
+ switch (*p) {
+ case '.': return 1; /* matches any char */
+ case L_ESC: return match_class(c, uchar(*(p+1)));
+ case '[': return matchbracketclass(c, p, ep-1);
+ default: return (uchar(*p) == c);
+ }
+ }
+}
+
+
+static const char *matchbalance (MatchState *ms, const char *s,
+ const char *p) {
+ if (p >= ms->p_end - 1)
+ luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')");
+ if (*s != *p) return NULL;
+ else {
+ int b = *p;
+ int e = *(p+1);
+ int cont = 1;
+ while (++s < ms->src_end) {
+ if (*s == e) {
+ if (--cont == 0) return s+1;
+ }
+ else if (*s == b) cont++;
+ }
+ }
+ return NULL; /* string ends out of balance */
+}
+
+
+static const char *max_expand (MatchState *ms, const char *s,
+ const char *p, const char *ep) {
+ ptrdiff_t i = 0; /* counts maximum expand for item */
+ while (singlematch(ms, s + i, p, ep))
+ i++;
+ /* keeps trying to match with the maximum repetitions */
+ while (i>=0) {
+ const char *res = match(ms, (s+i), ep+1);
+ if (res) return res;
+ i--; /* else didn't match; reduce 1 repetition to try again */
+ }
+ return NULL;
+}
+
+
+static const char *min_expand (MatchState *ms, const char *s,
+ const char *p, const char *ep) {
+ for (;;) {
+ const char *res = match(ms, s, ep+1);
+ if (res != NULL)
+ return res;
+ else if (singlematch(ms, s, p, ep))
+ s++; /* try with one more repetition */
+ else return NULL;
+ }
+}
+
+
+static const char *start_capture (MatchState *ms, const char *s,
+ const char *p, int what) {
+ const char *res;
+ int level = ms->level;
+ if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures");
+ ms->capture[level].init = s;
+ ms->capture[level].len = what;
+ ms->level = level+1;
+ if ((res=match(ms, s, p)) == NULL) /* match failed? */
+ ms->level--; /* undo capture */
+ return res;
+}
+
+
+static const char *end_capture (MatchState *ms, const char *s,
+ const char *p) {
+ int l = capture_to_close(ms);
+ const char *res;
+ ms->capture[l].len = s - ms->capture[l].init; /* close capture */
+ if ((res = match(ms, s, p)) == NULL) /* match failed? */
+ ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
+ return res;
+}
+
+
+static const char *match_capture (MatchState *ms, const char *s, int l) {
+ size_t len;
+ l = check_capture(ms, l);
+ len = ms->capture[l].len;
+ if ((size_t)(ms->src_end-s) >= len &&
+ memcmp(ms->capture[l].init, s, len) == 0)
+ return s+len;
+ else return NULL;
+}
+
+
+static const char *match (MatchState *ms, const char *s, const char *p) {
+ if (ms->matchdepth-- == 0)
+ luaL_error(ms->L, "pattern too complex");
+ init: /* using goto's to optimize tail recursion */
+ if (p != ms->p_end) { /* end of pattern? */
+ switch (*p) {
+ case '(': { /* start capture */
+ if (*(p + 1) == ')') /* position capture? */
+ s = start_capture(ms, s, p + 2, CAP_POSITION);
+ else
+ s = start_capture(ms, s, p + 1, CAP_UNFINISHED);
+ break;
+ }
+ case ')': { /* end capture */
+ s = end_capture(ms, s, p + 1);
+ break;
+ }
+ case '$': {
+ if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */
+ goto dflt; /* no; go to default */
+ s = (s == ms->src_end) ? s : NULL; /* check end of string */
+ break;
+ }
+ case L_ESC: { /* escaped sequences not in the format class[*+?-]? */
+ switch (*(p + 1)) {
+ case 'b': { /* balanced string? */
+ s = matchbalance(ms, s, p + 2);
+ if (s != NULL) {
+ p += 4; goto init; /* return match(ms, s, p + 4); */
+ } /* else fail (s == NULL) */
+ break;
+ }
+ case 'f': { /* frontier? */
+ const char *ep; char previous;
+ p += 2;
+ if (*p != '[')
+ luaL_error(ms->L, "missing '[' after '%%f' in pattern");
+ ep = classend(ms, p); /* points to what is next */
+ previous = (s == ms->src_init) ? '\0' : *(s - 1);
+ if (!matchbracketclass(uchar(previous), p, ep - 1) &&
+ matchbracketclass(uchar(*s), p, ep - 1)) {
+ p = ep; goto init; /* return match(ms, s, ep); */
+ }
+ s = NULL; /* match failed */
+ break;
+ }
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9': { /* capture results (%0-%9)? */
+ s = match_capture(ms, s, uchar(*(p + 1)));
+ if (s != NULL) {
+ p += 2; goto init; /* return match(ms, s, p + 2) */
+ }
+ break;
+ }
+ default: goto dflt;
+ }
+ break;
+ }
+ default: dflt: { /* pattern class plus optional suffix */
+ const char *ep = classend(ms, p); /* points to optional suffix */
+ /* does not match at least once? */
+ if (!singlematch(ms, s, p, ep)) {
+ if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */
+ p = ep + 1; goto init; /* return match(ms, s, ep + 1); */
+ }
+ else /* '+' or no suffix */
+ s = NULL; /* fail */
+ }
+ else { /* matched once */
+ switch (*ep) { /* handle optional suffix */
+ case '?': { /* optional */
+ const char *res;
+ if ((res = match(ms, s + 1, ep + 1)) != NULL)
+ s = res;
+ else {
+ p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */
+ }
+ break;
+ }
+ case '+': /* 1 or more repetitions */
+ s++; /* 1 match already done */
+ /* FALLTHROUGH */
+ case '*': /* 0 or more repetitions */
+ s = max_expand(ms, s, p, ep);
+ break;
+ case '-': /* 0 or more repetitions (minimum) */
+ s = min_expand(ms, s, p, ep);
+ break;
+ default: /* no suffix */
+ s++; p = ep; goto init; /* return match(ms, s + 1, ep); */
+ }
+ }
+ break;
+ }
+ }
+ }
+ ms->matchdepth++;
+ return s;
+}
+
+
+
+static const char *lmemfind (const char *s1, size_t l1,
+ const char *s2, size_t l2) {
+ if (l2 == 0) return s1; /* empty strings are everywhere */
+ else if (l2 > l1) return NULL; /* avoids a negative 'l1' */
+ else {
+ const char *init; /* to search for a '*s2' inside 's1' */
+ l2--; /* 1st char will be checked by 'memchr' */
+ l1 = l1-l2; /* 's2' cannot be found after that */
+ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
+ init++; /* 1st char is already checked */
+ if (memcmp(init, s2+1, l2) == 0)
+ return init-1;
+ else { /* correct 'l1' and 's1' to try again */
+ l1 -= init-s1;
+ s1 = init;
+ }
+ }
+ return NULL; /* not found */
+ }
+}
+
+
+static void push_onecapture (MatchState *ms, int i, const char *s,
+ const char *e) {
+ if (i >= ms->level) {
+ if (i == 0) /* ms->level == 0, too */
+ lua_pushlstring(ms->L, s, e - s); /* add whole match */
+ else
+ luaL_error(ms->L, "invalid capture index %%%d", i + 1);
+ }
+ else {
+ ptrdiff_t l = ms->capture[i].len;
+ if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture");
+ if (l == CAP_POSITION)
+ lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1);
+ else
+ lua_pushlstring(ms->L, ms->capture[i].init, l);
+ }
+}
+
+
+static int push_captures (MatchState *ms, const char *s, const char *e) {
+ int i;
+ int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
+ luaL_checkstack(ms->L, nlevels, "too many captures");
+ for (i = 0; i < nlevels; i++)
+ push_onecapture(ms, i, s, e);
+ return nlevels; /* number of strings pushed */
+}
+
+
+/* check whether pattern has no special characters */
+static int nospecials (const char *p, size_t l) {
+ size_t upto = 0;
+ do {
+ if (strpbrk(p + upto, SPECIALS))
+ return 0; /* pattern has a special character */
+ upto += strlen(p + upto) + 1; /* may have more after \0 */
+ } while (upto <= l);
+ return 1; /* no special chars found */
+}
+
+
+static void prepstate (MatchState *ms, lua_State *L,
+ const char *s, size_t ls, const char *p, size_t lp) {
+ ms->L = L;
+ ms->matchdepth = MAXCCALLS;
+ ms->src_init = s;
+ ms->src_end = s + ls;
+ ms->p_end = p + lp;
+}
+
+
+static void reprepstate (MatchState *ms) {
+ ms->level = 0;
+ lua_assert(ms->matchdepth == MAXCCALLS);
+}
+
+
+static int str_find_aux (lua_State *L, int find) {
+ size_t ls, lp;
+ const char *s = luaL_checklstring(L, 1, &ls);
+ const char *p = luaL_checklstring(L, 2, &lp);
+ lua_Integer init = posrelat(luaL_optinteger(L, 3, 1), ls);
+ if (init < 1) init = 1;
+ else if (init > (lua_Integer)ls + 1) { /* start after string's end? */
+ lua_pushnil(L); /* cannot find anything */
+ return 1;
+ }
+ /* explicit request or no special characters? */
+ if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) {
+ /* do a plain search */
+ const char *s2 = lmemfind(s + init - 1, ls - (size_t)init + 1, p, lp);
+ if (s2) {
+ lua_pushinteger(L, (s2 - s) + 1);
+ lua_pushinteger(L, (s2 - s) + lp);
+ return 2;
+ }
+ }
+ else {
+ MatchState ms;
+ const char *s1 = s + init - 1;
+ int anchor = (*p == '^');
+ if (anchor) {
+ p++; lp--; /* skip anchor character */
+ }
+ prepstate(&ms, L, s, ls, p, lp);
+ do {
+ const char *res;
+ reprepstate(&ms);
+ if ((res=match(&ms, s1, p)) != NULL) {
+ if (find) {
+ lua_pushinteger(L, (s1 - s) + 1); /* start */
+ lua_pushinteger(L, res - s); /* end */
+ return push_captures(&ms, NULL, 0) + 2;
+ }
+ else
+ return push_captures(&ms, s1, res);
+ }
+ } while (s1++ < ms.src_end && !anchor);
+ }
+ lua_pushnil(L); /* not found */
+ return 1;
+}
+
+
+static int str_find (lua_State *L) {
+ return str_find_aux(L, 1);
+}
+
+
+static int str_match (lua_State *L) {
+ return str_find_aux(L, 0);
+}
+
+
+/* state for 'gmatch' */
+typedef struct GMatchState {
+ const char *src; /* current position */
+ const char *p; /* pattern */
+ const char *lastmatch; /* end of last match */
+ MatchState ms; /* match state */
+} GMatchState;
+
+
+static int gmatch_aux (lua_State *L) {
+ GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3));
+ const char *src;
+ gm->ms.L = L;
+ for (src = gm->src; src <= gm->ms.src_end; src++) {
+ const char *e;
+ reprepstate(&gm->ms);
+ if ((e = match(&gm->ms, src, gm->p)) != NULL && e != gm->lastmatch) {
+ gm->src = gm->lastmatch = e;
+ return push_captures(&gm->ms, src, e);
+ }
+ }
+ return 0; /* not found */
+}
+
+
+static int gmatch (lua_State *L) {
+ size_t ls, lp;
+ const char *s = luaL_checklstring(L, 1, &ls);
+ const char *p = luaL_checklstring(L, 2, &lp);
+ GMatchState *gm;
+ lua_settop(L, 2); /* keep them on closure to avoid being collected */
+ gm = (GMatchState *)lua_newuserdata(L, sizeof(GMatchState));
+ prepstate(&gm->ms, L, s, ls, p, lp);
+ gm->src = s; gm->p = p; gm->lastmatch = NULL;
+ lua_pushcclosure(L, gmatch_aux, 3);
+ return 1;
+}
+
+
+static void add_s (MatchState *ms, luaL_Buffer *b, const char *s,
+ const char *e) {
+ size_t l, i;
+ lua_State *L = ms->L;
+ const char *news = lua_tolstring(L, 3, &l);
+ for (i = 0; i < l; i++) {
+ if (news[i] != L_ESC)
+ luaL_addchar(b, news[i]);
+ else {
+ i++; /* skip ESC */
+ if (!isdigit(uchar(news[i]))) {
+ if (news[i] != L_ESC)
+ luaL_error(L, "invalid use of '%c' in replacement string", L_ESC);
+ luaL_addchar(b, news[i]);
+ }
+ else if (news[i] == '0')
+ luaL_addlstring(b, s, e - s);
+ else {
+ push_onecapture(ms, news[i] - '1', s, e);
+ luaL_tolstring(L, -1, NULL); /* if number, convert it to string */
+ lua_remove(L, -2); /* remove original value */
+ luaL_addvalue(b); /* add capture to accumulated result */
+ }
+ }
+ }
+}
+
+
+static void add_value (MatchState *ms, luaL_Buffer *b, const char *s,
+ const char *e, int tr) {
+ lua_State *L = ms->L;
+ switch (tr) {
+ case LUA_TFUNCTION: {
+ int n;
+ lua_pushvalue(L, 3);
+ n = push_captures(ms, s, e);
+ lua_call(L, n, 1);
+ break;
+ }
+ case LUA_TTABLE: {
+ push_onecapture(ms, 0, s, e);
+ lua_gettable(L, 3);
+ break;
+ }
+ default: { /* LUA_TNUMBER or LUA_TSTRING */
+ add_s(ms, b, s, e);
+ return;
+ }
+ }
+ if (!lua_toboolean(L, -1)) { /* nil or false? */
+ lua_pop(L, 1);
+ lua_pushlstring(L, s, e - s); /* keep original text */
+ }
+ else if (!lua_isstring(L, -1))
+ luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1));
+ luaL_addvalue(b); /* add result to accumulator */
+}
+
+
+static int str_gsub (lua_State *L) {
+ size_t srcl, lp;
+ const char *src = luaL_checklstring(L, 1, &srcl); /* subject */
+ const char *p = luaL_checklstring(L, 2, &lp); /* pattern */
+ const char *lastmatch = NULL; /* end of last match */
+ int tr = lua_type(L, 3); /* replacement type */
+ lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */
+ int anchor = (*p == '^');
+ lua_Integer n = 0; /* replacement count */
+ MatchState ms;
+ luaL_Buffer b;
+ luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
+ tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
+ "string/function/table expected");
+ luaL_buffinit(L, &b);
+ if (anchor) {
+ p++; lp--; /* skip anchor character */
+ }
+ prepstate(&ms, L, src, srcl, p, lp);
+ while (n < max_s) {
+ const char *e;
+ reprepstate(&ms); /* (re)prepare state for new match */
+ if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */
+ n++;
+ add_value(&ms, &b, src, e, tr); /* add replacement to buffer */
+ src = lastmatch = e;
+ }
+ else if (src < ms.src_end) /* otherwise, skip one character */
+ luaL_addchar(&b, *src++);
+ else break; /* end of subject */
+ if (anchor) break;
+ }
+ luaL_addlstring(&b, src, ms.src_end-src);
+ luaL_pushresult(&b);
+ lua_pushinteger(L, n); /* number of substitutions */
+ return 2;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** STRING FORMAT
+** =======================================================
+*/
+
+#if !defined(lua_number2strx) /* { */
+
+/*
+** Hexadecimal floating-point formatter
+*/
+
+#include <math.h>
+
+#define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char))
+
+
+/*
+** Number of bits that goes into the first digit. It can be any value
+** between 1 and 4; the following definition tries to align the number
+** to nibble boundaries by making what is left after that first digit a
+** multiple of 4.
+*/
+#define L_NBFD ((l_mathlim(MANT_DIG) - 1)%4 + 1)
+
+
+/*
+** Add integer part of 'x' to buffer and return new 'x'
+*/
+static lua_Number adddigit (char *buff, int n, lua_Number x) {
+ lua_Number dd = l_mathop(floor)(x); /* get integer part from 'x' */
+ int d = (int)dd;
+ buff[n] = (d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */
+ return x - dd; /* return what is left */
+}
+
+
+static int num2straux (char *buff, int sz, lua_Number x) {
+ /* if 'inf' or 'NaN', format it like '%g' */
+ if (x != x || x == (lua_Number)HUGE_VAL || x == -(lua_Number)HUGE_VAL)
+ return l_sprintf(buff, sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)x);
+ else if (x == 0) { /* can be -0... */
+ /* create "0" or "-0" followed by exponent */
+ return l_sprintf(buff, sz, LUA_NUMBER_FMT "x0p+0", (LUAI_UACNUMBER)x);
+ }
+ else {
+ int e;
+ lua_Number m = l_mathop(frexp)(x, &e); /* 'x' fraction and exponent */
+ int n = 0; /* character count */
+ if (m < 0) { /* is number negative? */
+ buff[n++] = '-'; /* add signal */
+ m = -m; /* make it positive */
+ }
+ buff[n++] = '0'; buff[n++] = 'x'; /* add "0x" */
+ m = adddigit(buff, n++, m * (1 << L_NBFD)); /* add first digit */
+ e -= L_NBFD; /* this digit goes before the radix point */
+ if (m > 0) { /* more digits? */
+ buff[n++] = lua_getlocaledecpoint(); /* add radix point */
+ do { /* add as many digits as needed */
+ m = adddigit(buff, n++, m * 16);
+ } while (m > 0);
+ }
+ n += l_sprintf(buff + n, sz - n, "p%+d", e); /* add exponent */
+ lua_assert(n < sz);
+ return n;
+ }
+}
+
+
+static int lua_number2strx (lua_State *L, char *buff, int sz,
+ const char *fmt, lua_Number x) {
+ int n = num2straux(buff, sz, x);
+ if (fmt[SIZELENMOD] == 'A') {
+ int i;
+ for (i = 0; i < n; i++)
+ buff[i] = toupper(uchar(buff[i]));
+ }
+ else if (fmt[SIZELENMOD] != 'a')
+ luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented");
+ return n;
+}
+
+#endif /* } */
+
+
+/*
+** Maximum size of each formatted item. This maximum size is produced
+** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.',
+** and '\0') + number of decimal digits to represent maxfloat (which
+** is maximum exponent + 1). (99+3+1 then rounded to 120 for "extra
+** expenses", such as locale-dependent stuff)
+*/
+#define MAX_ITEM (120 + l_mathlim(MAX_10_EXP))
+
+
+/* valid flags in a format specification */
+#define FLAGS "-+ #0"
+
+/*
+** maximum size of each format specification (such as "%-099.99d")
+*/
+#define MAX_FORMAT 32
+
+
+static void addquoted (luaL_Buffer *b, const char *s, size_t len) {
+ luaL_addchar(b, '"');
+ while (len--) {
+ if (*s == '"' || *s == '\\' || *s == '\n') {
+ luaL_addchar(b, '\\');
+ luaL_addchar(b, *s);
+ }
+ else if (iscntrl(uchar(*s))) {
+ char buff[10];
+ if (!isdigit(uchar(*(s+1))))
+ l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s));
+ else
+ l_sprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s));
+ luaL_addstring(b, buff);
+ }
+ else
+ luaL_addchar(b, *s);
+ s++;
+ }
+ luaL_addchar(b, '"');
+}
+
+
+/*
+** Ensures the 'buff' string uses a dot as the radix character.
+*/
+static void checkdp (char *buff, int nb) {
+ if (memchr(buff, '.', nb) == NULL) { /* no dot? */
+ char point = lua_getlocaledecpoint(); /* try locale point */
+ char *ppoint = (char *)memchr(buff, point, nb);
+ if (ppoint) *ppoint = '.'; /* change it to a dot */
+ }
+}
+
+
+static void addliteral (lua_State *L, luaL_Buffer *b, int arg) {
+ switch (lua_type(L, arg)) {
+ case LUA_TSTRING: {
+ size_t len;
+ const char *s = lua_tolstring(L, arg, &len);
+ addquoted(b, s, len);
+ break;
+ }
+ case LUA_TNUMBER: {
+ char *buff = luaL_prepbuffsize(b, MAX_ITEM);
+ int nb;
+ if (!lua_isinteger(L, arg)) { /* float? */
+ lua_Number n = lua_tonumber(L, arg); /* write as hexa ('%a') */
+ nb = lua_number2strx(L, buff, MAX_ITEM, "%" LUA_NUMBER_FRMLEN "a", n);
+ checkdp(buff, nb); /* ensure it uses a dot */
+ }
+ else { /* integers */
+ lua_Integer n = lua_tointeger(L, arg);
+ const char *format = (n == LUA_MININTEGER) /* corner case? */
+ ? "0x%" LUA_INTEGER_FRMLEN "x" /* use hexa */
+ : LUA_INTEGER_FMT; /* else use default format */
+ nb = l_sprintf(buff, MAX_ITEM, format, (LUAI_UACINT)n);
+ }
+ luaL_addsize(b, nb);
+ break;
+ }
+ case LUA_TNIL: case LUA_TBOOLEAN: {
+ luaL_tolstring(L, arg, NULL);
+ luaL_addvalue(b);
+ break;
+ }
+ default: {
+ luaL_argerror(L, arg, "value has no literal form");
+ }
+ }
+}
+
+
+static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
+ const char *p = strfrmt;
+ while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */
+ if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char))
+ luaL_error(L, "invalid format (repeated flags)");
+ if (isdigit(uchar(*p))) p++; /* skip width */
+ if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
+ if (*p == '.') {
+ p++;
+ if (isdigit(uchar(*p))) p++; /* skip precision */
+ if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
+ }
+ if (isdigit(uchar(*p)))
+ luaL_error(L, "invalid format (width or precision too long)");
+ *(form++) = '%';
+ memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char));
+ form += (p - strfrmt) + 1;
+ *form = '\0';
+ return p;
+}
+
+
+/*
+** add length modifier into formats
+*/
+static void addlenmod (char *form, const char *lenmod) {
+ size_t l = strlen(form);
+ size_t lm = strlen(lenmod);
+ char spec = form[l - 1];
+ strcpy(form + l - 1, lenmod);
+ form[l + lm - 1] = spec;
+ form[l + lm] = '\0';
+}
+
+
+static int str_format (lua_State *L) {
+ int top = lua_gettop(L);
+ int arg = 1;
+ size_t sfl;
+ const char *strfrmt = luaL_checklstring(L, arg, &sfl);
+ const char *strfrmt_end = strfrmt+sfl;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (strfrmt < strfrmt_end) {
+ if (*strfrmt != L_ESC)
+ luaL_addchar(&b, *strfrmt++);
+ else if (*++strfrmt == L_ESC)
+ luaL_addchar(&b, *strfrmt++); /* %% */
+ else { /* format item */
+ char form[MAX_FORMAT]; /* to store the format ('%...') */
+ char *buff = luaL_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */
+ int nb = 0; /* number of bytes in added item */
+ if (++arg > top)
+ luaL_argerror(L, arg, "no value");
+ strfrmt = scanformat(L, strfrmt, form);
+ switch (*strfrmt++) {
+ case 'c': {
+ nb = l_sprintf(buff, MAX_ITEM, form, (int)luaL_checkinteger(L, arg));
+ break;
+ }
+ case 'd': case 'i':
+ case 'o': case 'u': case 'x': case 'X': {
+ lua_Integer n = luaL_checkinteger(L, arg);
+ addlenmod(form, LUA_INTEGER_FRMLEN);
+ nb = l_sprintf(buff, MAX_ITEM, form, (LUAI_UACINT)n);
+ break;
+ }
+ case 'a': case 'A':
+ addlenmod(form, LUA_NUMBER_FRMLEN);
+ nb = lua_number2strx(L, buff, MAX_ITEM, form,
+ luaL_checknumber(L, arg));
+ break;
+ case 'e': case 'E': case 'f':
+ case 'g': case 'G': {
+ lua_Number n = luaL_checknumber(L, arg);
+ addlenmod(form, LUA_NUMBER_FRMLEN);
+ nb = l_sprintf(buff, MAX_ITEM, form, (LUAI_UACNUMBER)n);
+ break;
+ }
+ case 'q': {
+ addliteral(L, &b, arg);
+ break;
+ }
+ case 's': {
+ size_t l;
+ const char *s = luaL_tolstring(L, arg, &l);
+ if (form[2] == '\0') /* no modifiers? */
+ luaL_addvalue(&b); /* keep entire string */
+ else {
+ luaL_argcheck(L, l == strlen(s), arg, "string contains zeros");
+ if (!strchr(form, '.') && l >= 100) {
+ /* no precision and string is too long to be formatted */
+ luaL_addvalue(&b); /* keep entire string */
+ }
+ else { /* format the string into 'buff' */
+ nb = l_sprintf(buff, MAX_ITEM, form, s);
+ lua_pop(L, 1); /* remove result from 'luaL_tolstring' */
+ }
+ }
+ break;
+ }
+ default: { /* also treat cases 'pnLlh' */
+ return luaL_error(L, "invalid option '%%%c' to 'format'",
+ *(strfrmt - 1));
+ }
+ }
+ lua_assert(nb < MAX_ITEM);
+ luaL_addsize(&b, nb);
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** PACK/UNPACK
+** =======================================================
+*/
+
+
+/* value used for padding */
+#if !defined(LUAL_PACKPADBYTE)
+#define LUAL_PACKPADBYTE 0x00
+#endif
+
+/* maximum size for the binary representation of an integer */
+#define MAXINTSIZE 16
+
+/* number of bits in a character */
+#define NB CHAR_BIT
+
+/* mask for one character (NB 1's) */
+#define MC ((1 << NB) - 1)
+
+/* size of a lua_Integer */
+#define SZINT ((int)sizeof(lua_Integer))
+
+
+/* dummy union to get native endianness */
+static const union {
+ int dummy;
+ char little; /* true iff machine is little endian */
+} nativeendian = {1};
+
+
+/* dummy structure to get native alignment requirements */
+struct cD {
+ char c;
+ union { double d; void *p; lua_Integer i; lua_Number n; } u;
+};
+
+#define MAXALIGN (offsetof(struct cD, u))
+
+
+/*
+** Union for serializing floats
+*/
+typedef union Ftypes {
+ float f;
+ double d;
+ lua_Number n;
+ char buff[5 * sizeof(lua_Number)]; /* enough for any float type */
+} Ftypes;
+
+
+/*
+** information to pack/unpack stuff
+*/
+typedef struct Header {
+ lua_State *L;
+ int islittle;
+ int maxalign;
+} Header;
+
+
+/*
+** options for pack/unpack
+*/
+typedef enum KOption {
+ Kint, /* signed integers */
+ Kuint, /* unsigned integers */
+ Kfloat, /* floating-point numbers */
+ Kchar, /* fixed-length strings */
+ Kstring, /* strings with prefixed length */
+ Kzstr, /* zero-terminated strings */
+ Kpadding, /* padding */
+ Kpaddalign, /* padding for alignment */
+ Knop /* no-op (configuration or spaces) */
+} KOption;
+
+
+/*
+** Read an integer numeral from string 'fmt' or return 'df' if
+** there is no numeral
+*/
+static int digit (int c) { return '0' <= c && c <= '9'; }
+
+static int getnum (const char **fmt, int df) {
+ if (!digit(**fmt)) /* no number? */
+ return df; /* return default value */
+ else {
+ int a = 0;
+ do {
+ a = a*10 + (*((*fmt)++) - '0');
+ } while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10);
+ return a;
+ }
+}
+
+
+/*
+** Read an integer numeral and raises an error if it is larger
+** than the maximum size for integers.
+*/
+static int getnumlimit (Header *h, const char **fmt, int df) {
+ int sz = getnum(fmt, df);
+ if (sz > MAXINTSIZE || sz <= 0)
+ luaL_error(h->L, "integral size (%d) out of limits [1,%d]",
+ sz, MAXINTSIZE);
+ return sz;
+}
+
+
+/*
+** Initialize Header
+*/
+static void initheader (lua_State *L, Header *h) {
+ h->L = L;
+ h->islittle = nativeendian.little;
+ h->maxalign = 1;
+}
+
+
+/*
+** Read and classify next option. 'size' is filled with option's size.
+*/
+static KOption getoption (Header *h, const char **fmt, int *size) {
+ int opt = *((*fmt)++);
+ *size = 0; /* default */
+ switch (opt) {
+ case 'b': *size = sizeof(char); return Kint;
+ case 'B': *size = sizeof(char); return Kuint;
+ case 'h': *size = sizeof(short); return Kint;
+ case 'H': *size = sizeof(short); return Kuint;
+ case 'l': *size = sizeof(long); return Kint;
+ case 'L': *size = sizeof(long); return Kuint;
+ case 'j': *size = sizeof(lua_Integer); return Kint;
+ case 'J': *size = sizeof(lua_Integer); return Kuint;
+ case 'T': *size = sizeof(size_t); return Kuint;
+ case 'f': *size = sizeof(float); return Kfloat;
+ case 'd': *size = sizeof(double); return Kfloat;
+ case 'n': *size = sizeof(lua_Number); return Kfloat;
+ case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint;
+ case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint;
+ case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring;
+ case 'c':
+ *size = getnum(fmt, -1);
+ if (*size == -1)
+ luaL_error(h->L, "missing size for format option 'c'");
+ return Kchar;
+ case 'z': return Kzstr;
+ case 'x': *size = 1; return Kpadding;
+ case 'X': return Kpaddalign;
+ case ' ': break;
+ case '<': h->islittle = 1; break;
+ case '>': h->islittle = 0; break;
+ case '=': h->islittle = nativeendian.little; break;
+ case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break;
+ default: luaL_error(h->L, "invalid format option '%c'", opt);
+ }
+ return Knop;
+}
+
+
+/*
+** Read, classify, and fill other details about the next option.
+** 'psize' is filled with option's size, 'notoalign' with its
+** alignment requirements.
+** Local variable 'size' gets the size to be aligned. (Kpadal option
+** always gets its full alignment, other options are limited by
+** the maximum alignment ('maxalign'). Kchar option needs no alignment
+** despite its size.
+*/
+static KOption getdetails (Header *h, size_t totalsize,
+ const char **fmt, int *psize, int *ntoalign) {
+ KOption opt = getoption(h, fmt, psize);
+ int align = *psize; /* usually, alignment follows size */
+ if (opt == Kpaddalign) { /* 'X' gets alignment from following option */
+ if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0)
+ luaL_argerror(h->L, 1, "invalid next option for option 'X'");
+ }
+ if (align <= 1 || opt == Kchar) /* need no alignment? */
+ *ntoalign = 0;
+ else {
+ if (align > h->maxalign) /* enforce maximum alignment */
+ align = h->maxalign;
+ if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */
+ luaL_argerror(h->L, 1, "format asks for alignment not power of 2");
+ *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
+ }
+ return opt;
+}
+
+
+/*
+** Pack integer 'n' with 'size' bytes and 'islittle' endianness.
+** The final 'if' handles the case when 'size' is larger than
+** the size of a Lua integer, correcting the extra sign-extension
+** bytes if necessary (by default they would be zeros).
+*/
+static void packint (luaL_Buffer *b, lua_Unsigned n,
+ int islittle, int size, int neg) {
+ char *buff = luaL_prepbuffsize(b, size);
+ int i;
+ buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */
+ for (i = 1; i < size; i++) {
+ n >>= NB;
+ buff[islittle ? i : size - 1 - i] = (char)(n & MC);
+ }
+ if (neg && size > SZINT) { /* negative number need sign extension? */
+ for (i = SZINT; i < size; i++) /* correct extra bytes */
+ buff[islittle ? i : size - 1 - i] = (char)MC;
+ }
+ luaL_addsize(b, size); /* add result to buffer */
+}
+
+
+/*
+** Copy 'size' bytes from 'src' to 'dest', correcting endianness if
+** given 'islittle' is different from native endianness.
+*/
+static void copywithendian (volatile char *dest, volatile const char *src,
+ int size, int islittle) {
+ if (islittle == nativeendian.little) {
+ while (size-- != 0)
+ *(dest++) = *(src++);
+ }
+ else {
+ dest += size - 1;
+ while (size-- != 0)
+ *(dest--) = *(src++);
+ }
+}
+
+
+static int str_pack (lua_State *L) {
+ luaL_Buffer b;
+ Header h;
+ const char *fmt = luaL_checkstring(L, 1); /* format string */
+ int arg = 1; /* current argument to pack */
+ size_t totalsize = 0; /* accumulate total size of result */
+ initheader(L, &h);
+ lua_pushnil(L); /* mark to separate arguments from string buffer */
+ luaL_buffinit(L, &b);
+ while (*fmt != '\0') {
+ int size, ntoalign;
+ KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
+ totalsize += ntoalign + size;
+ while (ntoalign-- > 0)
+ luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */
+ arg++;
+ switch (opt) {
+ case Kint: { /* signed integers */
+ lua_Integer n = luaL_checkinteger(L, arg);
+ if (size < SZINT) { /* need overflow check? */
+ lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1);
+ luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow");
+ }
+ packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0));
+ break;
+ }
+ case Kuint: { /* unsigned integers */
+ lua_Integer n = luaL_checkinteger(L, arg);
+ if (size < SZINT) /* need overflow check? */
+ luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)),
+ arg, "unsigned overflow");
+ packint(&b, (lua_Unsigned)n, h.islittle, size, 0);
+ break;
+ }
+ case Kfloat: { /* floating-point options */
+ volatile Ftypes u;
+ char *buff = luaL_prepbuffsize(&b, size);
+ lua_Number n = luaL_checknumber(L, arg); /* get argument */
+ if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */
+ else if (size == sizeof(u.d)) u.d = (double)n;
+ else u.n = n;
+ /* move 'u' to final result, correcting endianness if needed */
+ copywithendian(buff, u.buff, size, h.islittle);
+ luaL_addsize(&b, size);
+ break;
+ }
+ case Kchar: { /* fixed-size string */
+ size_t len;
+ const char *s = luaL_checklstring(L, arg, &len);
+ luaL_argcheck(L, len <= (size_t)size, arg,
+ "string longer than given size");
+ luaL_addlstring(&b, s, len); /* add string */
+ while (len++ < (size_t)size) /* pad extra space */
+ luaL_addchar(&b, LUAL_PACKPADBYTE);
+ break;
+ }
+ case Kstring: { /* strings with length count */
+ size_t len;
+ const char *s = luaL_checklstring(L, arg, &len);
+ luaL_argcheck(L, size >= (int)sizeof(size_t) ||
+ len < ((size_t)1 << (size * NB)),
+ arg, "string length does not fit in given size");
+ packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */
+ luaL_addlstring(&b, s, len);
+ totalsize += len;
+ break;
+ }
+ case Kzstr: { /* zero-terminated string */
+ size_t len;
+ const char *s = luaL_checklstring(L, arg, &len);
+ luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros");
+ luaL_addlstring(&b, s, len);
+ luaL_addchar(&b, '\0'); /* add zero at the end */
+ totalsize += len + 1;
+ break;
+ }
+ case Kpadding: luaL_addchar(&b, LUAL_PACKPADBYTE); /* FALLTHROUGH */
+ case Kpaddalign: case Knop:
+ arg--; /* undo increment */
+ break;
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_packsize (lua_State *L) {
+ Header h;
+ const char *fmt = luaL_checkstring(L, 1); /* format string */
+ size_t totalsize = 0; /* accumulate total size of result */
+ initheader(L, &h);
+ while (*fmt != '\0') {
+ int size, ntoalign;
+ KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
+ size += ntoalign; /* total space used by option */
+ luaL_argcheck(L, totalsize <= MAXSIZE - size, 1,
+ "format result too large");
+ totalsize += size;
+ switch (opt) {
+ case Kstring: /* strings with length count */
+ case Kzstr: /* zero-terminated string */
+ luaL_argerror(L, 1, "variable-length format");
+ /* call never return, but to avoid warnings: *//* FALLTHROUGH */
+ default: break;
+ }
+ }
+ lua_pushinteger(L, (lua_Integer)totalsize);
+ return 1;
+}
+
+
+/*
+** Unpack an integer with 'size' bytes and 'islittle' endianness.
+** If size is smaller than the size of a Lua integer and integer
+** is signed, must do sign extension (propagating the sign to the
+** higher bits); if size is larger than the size of a Lua integer,
+** it must check the unread bytes to see whether they do not cause an
+** overflow.
+*/
+static lua_Integer unpackint (lua_State *L, const char *str,
+ int islittle, int size, int issigned) {
+ lua_Unsigned res = 0;
+ int i;
+ int limit = (size <= SZINT) ? size : SZINT;
+ for (i = limit - 1; i >= 0; i--) {
+ res <<= NB;
+ res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i];
+ }
+ if (size < SZINT) { /* real size smaller than lua_Integer? */
+ if (issigned) { /* needs sign extension? */
+ lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1);
+ res = ((res ^ mask) - mask); /* do sign extension */
+ }
+ }
+ else if (size > SZINT) { /* must check unread bytes */
+ int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC;
+ for (i = limit; i < size; i++) {
+ if ((unsigned char)str[islittle ? i : size - 1 - i] != mask)
+ luaL_error(L, "%d-byte integer does not fit into Lua Integer", size);
+ }
+ }
+ return (lua_Integer)res;
+}
+
+
+static int str_unpack (lua_State *L) {
+ Header h;
+ const char *fmt = luaL_checkstring(L, 1);
+ size_t ld;
+ const char *data = luaL_checklstring(L, 2, &ld);
+ size_t pos = (size_t)posrelat(luaL_optinteger(L, 3, 1), ld) - 1;
+ int n = 0; /* number of results */
+ luaL_argcheck(L, pos <= ld, 3, "initial position out of string");
+ initheader(L, &h);
+ while (*fmt != '\0') {
+ int size, ntoalign;
+ KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign);
+ if ((size_t)ntoalign + size > ~pos || pos + ntoalign + size > ld)
+ luaL_argerror(L, 2, "data string too short");
+ pos += ntoalign; /* skip alignment */
+ /* stack space for item + next position */
+ luaL_checkstack(L, 2, "too many results");
+ n++;
+ switch (opt) {
+ case Kint:
+ case Kuint: {
+ lua_Integer res = unpackint(L, data + pos, h.islittle, size,
+ (opt == Kint));
+ lua_pushinteger(L, res);
+ break;
+ }
+ case Kfloat: {
+ volatile Ftypes u;
+ lua_Number num;
+ copywithendian(u.buff, data + pos, size, h.islittle);
+ if (size == sizeof(u.f)) num = (lua_Number)u.f;
+ else if (size == sizeof(u.d)) num = (lua_Number)u.d;
+ else num = u.n;
+ lua_pushnumber(L, num);
+ break;
+ }
+ case Kchar: {
+ lua_pushlstring(L, data + pos, size);
+ break;
+ }
+ case Kstring: {
+ size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0);
+ luaL_argcheck(L, pos + len + size <= ld, 2, "data string too short");
+ lua_pushlstring(L, data + pos + size, len);
+ pos += len; /* skip string */
+ break;
+ }
+ case Kzstr: {
+ size_t len = (int)strlen(data + pos);
+ lua_pushlstring(L, data + pos, len);
+ pos += len + 1; /* skip string plus final '\0' */
+ break;
+ }
+ case Kpaddalign: case Kpadding: case Knop:
+ n--; /* undo increment */
+ break;
+ }
+ pos += size;
+ }
+ lua_pushinteger(L, pos + 1); /* next position */
+ return n + 1;
+}
+
+/* }====================================================== */
+
+
+static const luaL_Reg strlib[] = {
+ {"byte", str_byte},
+ {"char", str_char},
+ {"dump", str_dump},
+ {"find", str_find},
+ {"format", str_format},
+ {"gmatch", gmatch},
+ {"gsub", str_gsub},
+ {"len", str_len},
+ {"lower", str_lower},
+ {"match", str_match},
+ {"rep", str_rep},
+ {"reverse", str_reverse},
+ {"sub", str_sub},
+ {"upper", str_upper},
+ {"pack", str_pack},
+ {"packsize", str_packsize},
+ {"unpack", str_unpack},
+ {NULL, NULL}
+};
+
+
+static void createmetatable (lua_State *L) {
+ lua_createtable(L, 0, 1); /* table to be metatable for strings */
+ lua_pushliteral(L, ""); /* dummy string */
+ lua_pushvalue(L, -2); /* copy table */
+ lua_setmetatable(L, -2); /* set table as metatable for strings */
+ lua_pop(L, 1); /* pop dummy string */
+ lua_pushvalue(L, -2); /* get string library */
+ lua_setfield(L, -2, "__index"); /* metatable.__index = string */
+ lua_pop(L, 1); /* pop metatable */
+}
+
+
+/*
+** Open string library
+*/
+LUAMOD_API int luaopen_string (lua_State *L) {
+ luaL_newlib(L, strlib);
+ createmetatable(L);
+ return 1;
+}
+
diff --git a/ltablib.c b/ltablib.c
new file mode 100644
index 0000000..98b2f87
--- /dev/null
+++ b/ltablib.c
@@ -0,0 +1,450 @@
+/*
+** $Id: ltablib.c,v 1.93 2016/02/25 19:41:54 roberto Exp $
+** Library for Table Manipulation
+** See Copyright Notice in lua.h
+*/
+
+#define ltablib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/*
+** Operations that an object must define to mimic a table
+** (some functions only need some of them)
+*/
+#define TAB_R 1 /* read */
+#define TAB_W 2 /* write */
+#define TAB_L 4 /* length */
+#define TAB_RW (TAB_R | TAB_W) /* read/write */
+
+
+#define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n))
+
+
+static int checkfield (lua_State *L, const char *key, int n) {
+ lua_pushstring(L, key);
+ return (lua_rawget(L, -n) != LUA_TNIL);
+}
+
+
+/*
+** Check that 'arg' either is a table or can behave like one (that is,
+** has a metatable with the required metamethods)
+*/
+static void checktab (lua_State *L, int arg, int what) {
+ if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */
+ int n = 1; /* number of elements to pop */
+ if (lua_getmetatable(L, arg) && /* must have metatable */
+ (!(what & TAB_R) || checkfield(L, "__index", ++n)) &&
+ (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) &&
+ (!(what & TAB_L) || checkfield(L, "__len", ++n))) {
+ lua_pop(L, n); /* pop metatable and tested metamethods */
+ }
+ else
+ luaL_checktype(L, arg, LUA_TTABLE); /* force an error */
+ }
+}
+
+
+#if defined(LUA_COMPAT_MAXN)
+static int maxn (lua_State *L) {
+ lua_Number max = 0;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, 1)) {
+ lua_pop(L, 1); /* remove value */
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ lua_Number v = lua_tonumber(L, -1);
+ if (v > max) max = v;
+ }
+ }
+ lua_pushnumber(L, max);
+ return 1;
+}
+#endif
+
+
+static int tinsert (lua_State *L) {
+ lua_Integer e = aux_getn(L, 1, TAB_RW) + 1; /* first empty element */
+ lua_Integer pos; /* where to insert new element */
+ switch (lua_gettop(L)) {
+ case 2: { /* called with only 2 arguments */
+ pos = e; /* insert new element at the end */
+ break;
+ }
+ case 3: {
+ lua_Integer i;
+ pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */
+ luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds");
+ for (i = e; i > pos; i--) { /* move up elements */
+ lua_geti(L, 1, i - 1);
+ lua_seti(L, 1, i); /* t[i] = t[i - 1] */
+ }
+ break;
+ }
+ default: {
+ return luaL_error(L, "wrong number of arguments to 'insert'");
+ }
+ }
+ lua_seti(L, 1, pos); /* t[pos] = v */
+ return 0;
+}
+
+
+static int tremove (lua_State *L) {
+ lua_Integer size = aux_getn(L, 1, TAB_RW);
+ lua_Integer pos = luaL_optinteger(L, 2, size);
+ if (pos != size) /* validate 'pos' if given */
+ luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds");
+ lua_geti(L, 1, pos); /* result = t[pos] */
+ for ( ; pos < size; pos++) {
+ lua_geti(L, 1, pos + 1);
+ lua_seti(L, 1, pos); /* t[pos] = t[pos + 1] */
+ }
+ lua_pushnil(L);
+ lua_seti(L, 1, pos); /* t[pos] = nil */
+ return 1;
+}
+
+
+/*
+** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever
+** possible, copy in increasing order, which is better for rehashing.
+** "possible" means destination after original range, or smaller
+** than origin, or copying to another table.
+*/
+static int tmove (lua_State *L) {
+ lua_Integer f = luaL_checkinteger(L, 2);
+ lua_Integer e = luaL_checkinteger(L, 3);
+ lua_Integer t = luaL_checkinteger(L, 4);
+ int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */
+ checktab(L, 1, TAB_R);
+ checktab(L, tt, TAB_W);
+ if (e >= f) { /* otherwise, nothing to move */
+ lua_Integer n, i;
+ luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3,
+ "too many elements to move");
+ n = e - f + 1; /* number of elements to move */
+ luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4,
+ "destination wrap around");
+ if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) {
+ for (i = 0; i < n; i++) {
+ lua_geti(L, 1, f + i);
+ lua_seti(L, tt, t + i);
+ }
+ }
+ else {
+ for (i = n - 1; i >= 0; i--) {
+ lua_geti(L, 1, f + i);
+ lua_seti(L, tt, t + i);
+ }
+ }
+ }
+ lua_pushvalue(L, tt); /* return destination table */
+ return 1;
+}
+
+
+static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) {
+ lua_geti(L, 1, i);
+ if (!lua_isstring(L, -1))
+ luaL_error(L, "invalid value (%s) at index %d in table for 'concat'",
+ luaL_typename(L, -1), i);
+ luaL_addvalue(b);
+}
+
+
+static int tconcat (lua_State *L) {
+ luaL_Buffer b;
+ lua_Integer last = aux_getn(L, 1, TAB_R);
+ size_t lsep;
+ const char *sep = luaL_optlstring(L, 2, "", &lsep);
+ lua_Integer i = luaL_optinteger(L, 3, 1);
+ last = luaL_optinteger(L, 4, last);
+ luaL_buffinit(L, &b);
+ for (; i < last; i++) {
+ addfield(L, &b, i);
+ luaL_addlstring(&b, sep, lsep);
+ }
+ if (i == last) /* add last value (if interval was not empty) */
+ addfield(L, &b, i);
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+/*
+** {======================================================
+** Pack/unpack
+** =======================================================
+*/
+
+static int pack (lua_State *L) {
+ int i;
+ int n = lua_gettop(L); /* number of elements to pack */
+ lua_createtable(L, n, 1); /* create result table */
+ lua_insert(L, 1); /* put it at index 1 */
+ for (i = n; i >= 1; i--) /* assign elements */
+ lua_seti(L, 1, i);
+ lua_pushinteger(L, n);
+ lua_setfield(L, 1, "n"); /* t.n = number of elements */
+ return 1; /* return table */
+}
+
+
+static int unpack (lua_State *L) {
+ lua_Unsigned n;
+ lua_Integer i = luaL_optinteger(L, 2, 1);
+ lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1));
+ if (i > e) return 0; /* empty range */
+ n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */
+ if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n)))
+ return luaL_error(L, "too many results to unpack");
+ for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */
+ lua_geti(L, 1, i);
+ }
+ lua_geti(L, 1, e); /* push last element */
+ return (int)n;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** Quicksort
+** (based on 'Algorithms in MODULA-3', Robert Sedgewick;
+** Addison-Wesley, 1993.)
+** =======================================================
+*/
+
+
+/* type for array indices */
+typedef unsigned int IdxT;
+
+
+/*
+** Produce a "random" 'unsigned int' to randomize pivot choice. This
+** macro is used only when 'sort' detects a big imbalance in the result
+** of a partition. (If you don't want/need this "randomness", ~0 is a
+** good choice.)
+*/
+#if !defined(l_randomizePivot) /* { */
+
+#include <time.h>
+
+/* size of 'e' measured in number of 'unsigned int's */
+#define sof(e) (sizeof(e) / sizeof(unsigned int))
+
+/*
+** Use 'time' and 'clock' as sources of "randomness". Because we don't
+** know the types 'clock_t' and 'time_t', we cannot cast them to
+** anything without risking overflows. A safe way to use their values
+** is to copy them to an array of a known type and use the array values.
+*/
+static unsigned int l_randomizePivot (void) {
+ clock_t c = clock();
+ time_t t = time(NULL);
+ unsigned int buff[sof(c) + sof(t)];
+ unsigned int i, rnd = 0;
+ memcpy(buff, &c, sof(c) * sizeof(unsigned int));
+ memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int));
+ for (i = 0; i < sof(buff); i++)
+ rnd += buff[i];
+ return rnd;
+}
+
+#endif /* } */
+
+
+/* arrays larger than 'RANLIMIT' may use randomized pivots */
+#define RANLIMIT 100u
+
+
+static void set2 (lua_State *L, IdxT i, IdxT j) {
+ lua_seti(L, 1, i);
+ lua_seti(L, 1, j);
+}
+
+
+/*
+** Return true iff value at stack index 'a' is less than the value at
+** index 'b' (according to the order of the sort).
+*/
+static int sort_comp (lua_State *L, int a, int b) {
+ if (lua_isnil(L, 2)) /* no function? */
+ return lua_compare(L, a, b, LUA_OPLT); /* a < b */
+ else { /* function */
+ int res;
+ lua_pushvalue(L, 2); /* push function */
+ lua_pushvalue(L, a-1); /* -1 to compensate function */
+ lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */
+ lua_call(L, 2, 1); /* call function */
+ res = lua_toboolean(L, -1); /* get result */
+ lua_pop(L, 1); /* pop result */
+ return res;
+ }
+}
+
+
+/*
+** Does the partition: Pivot P is at the top of the stack.
+** precondition: a[lo] <= P == a[up-1] <= a[up],
+** so it only needs to do the partition from lo + 1 to up - 2.
+** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up]
+** returns 'i'.
+*/
+static IdxT partition (lua_State *L, IdxT lo, IdxT up) {
+ IdxT i = lo; /* will be incremented before first use */
+ IdxT j = up - 1; /* will be decremented before first use */
+ /* loop invariant: a[lo .. i] <= P <= a[j .. up] */
+ for (;;) {
+ /* next loop: repeat ++i while a[i] < P */
+ while (lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) {
+ if (i == up - 1) /* a[i] < P but a[up - 1] == P ?? */
+ luaL_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[i] */
+ }
+ /* after the loop, a[i] >= P and a[lo .. i - 1] < P */
+ /* next loop: repeat --j while P < a[j] */
+ while (lua_geti(L, 1, --j), sort_comp(L, -3, -1)) {
+ if (j < i) /* j < i but a[j] > P ?? */
+ luaL_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[j] */
+ }
+ /* after the loop, a[j] <= P and a[j + 1 .. up] >= P */
+ if (j < i) { /* no elements out of place? */
+ /* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */
+ lua_pop(L, 1); /* pop a[j] */
+ /* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */
+ set2(L, up - 1, i);
+ return i;
+ }
+ /* otherwise, swap a[i] - a[j] to restore invariant and repeat */
+ set2(L, i, j);
+ }
+}
+
+
+/*
+** Choose an element in the middle (2nd-3th quarters) of [lo,up]
+** "randomized" by 'rnd'
+*/
+static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) {
+ IdxT r4 = (up - lo) / 4; /* range/4 */
+ IdxT p = rnd % (r4 * 2) + (lo + r4);
+ lua_assert(lo + r4 <= p && p <= up - r4);
+ return p;
+}
+
+
+/*
+** QuickSort algorithm (recursive function)
+*/
+static void auxsort (lua_State *L, IdxT lo, IdxT up,
+ unsigned int rnd) {
+ while (lo < up) { /* loop for tail recursion */
+ IdxT p; /* Pivot index */
+ IdxT n; /* to be used later */
+ /* sort elements 'lo', 'p', and 'up' */
+ lua_geti(L, 1, lo);
+ lua_geti(L, 1, up);
+ if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */
+ set2(L, lo, up); /* swap a[lo] - a[up] */
+ else
+ lua_pop(L, 2); /* remove both values */
+ if (up - lo == 1) /* only 2 elements? */
+ return; /* already sorted */
+ if (up - lo < RANLIMIT || rnd == 0) /* small interval or no randomize? */
+ p = (lo + up)/2; /* middle element is a good pivot */
+ else /* for larger intervals, it is worth a random pivot */
+ p = choosePivot(lo, up, rnd);
+ lua_geti(L, 1, p);
+ lua_geti(L, 1, lo);
+ if (sort_comp(L, -2, -1)) /* a[p] < a[lo]? */
+ set2(L, p, lo); /* swap a[p] - a[lo] */
+ else {
+ lua_pop(L, 1); /* remove a[lo] */
+ lua_geti(L, 1, up);
+ if (sort_comp(L, -1, -2)) /* a[up] < a[p]? */
+ set2(L, p, up); /* swap a[up] - a[p] */
+ else
+ lua_pop(L, 2);
+ }
+ if (up - lo == 2) /* only 3 elements? */
+ return; /* already sorted */
+ lua_geti(L, 1, p); /* get middle element (Pivot) */
+ lua_pushvalue(L, -1); /* push Pivot */
+ lua_geti(L, 1, up - 1); /* push a[up - 1] */
+ set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */
+ p = partition(L, lo, up);
+ /* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */
+ if (p - lo < up - p) { /* lower interval is smaller? */
+ auxsort(L, lo, p - 1, rnd); /* call recursively for lower interval */
+ n = p - lo; /* size of smaller interval */
+ lo = p + 1; /* tail call for [p + 1 .. up] (upper interval) */
+ }
+ else {
+ auxsort(L, p + 1, up, rnd); /* call recursively for upper interval */
+ n = up - p; /* size of smaller interval */
+ up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */
+ }
+ if ((up - lo) / 128 > n) /* partition too imbalanced? */
+ rnd = l_randomizePivot(); /* try a new randomization */
+ } /* tail call auxsort(L, lo, up, rnd) */
+}
+
+
+static int sort (lua_State *L) {
+ lua_Integer n = aux_getn(L, 1, TAB_RW);
+ if (n > 1) { /* non-trivial interval? */
+ luaL_argcheck(L, n < INT_MAX, 1, "array too big");
+ if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */
+ luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */
+ lua_settop(L, 2); /* make sure there are two arguments */
+ auxsort(L, 1, (IdxT)n, 0);
+ }
+ return 0;
+}
+
+/* }====================================================== */
+
+
+static const luaL_Reg tab_funcs[] = {
+ {"concat", tconcat},
+#if defined(LUA_COMPAT_MAXN)
+ {"maxn", maxn},
+#endif
+ {"insert", tinsert},
+ {"pack", pack},
+ {"unpack", unpack},
+ {"remove", tremove},
+ {"move", tmove},
+ {"sort", sort},
+ {NULL, NULL}
+};
+
+
+LUAMOD_API int luaopen_table (lua_State *L) {
+ luaL_newlib(L, tab_funcs);
+#if defined(LUA_COMPAT_UNPACK)
+ /* _G.unpack = table.unpack */
+ lua_getfield(L, -1, "unpack");
+ lua_setglobal(L, "unpack");
+#endif
+ return 1;
+}
+
diff --git a/lutf8lib.c b/lutf8lib.c
new file mode 100644
index 0000000..de9e3dc
--- /dev/null
+++ b/lutf8lib.c
@@ -0,0 +1,256 @@
+/*
+** $Id: lutf8lib.c,v 1.16 2016/12/22 13:08:50 roberto Exp $
+** Standard library for UTF-8 manipulation
+** See Copyright Notice in lua.h
+*/
+
+#define lutf8lib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+#define MAXUNICODE 0x10FFFF
+
+#define iscont(p) ((*(p) & 0xC0) == 0x80)
+
+
+/* from strlib */
+/* translate a relative string position: negative means back from end */
+static lua_Integer u_posrelat (lua_Integer pos, size_t len) {
+ if (pos >= 0) return pos;
+ else if (0u - (size_t)pos > len) return 0;
+ else return (lua_Integer)len + pos + 1;
+}
+
+
+/*
+** Decode one UTF-8 sequence, returning NULL if byte sequence is invalid.
+*/
+static const char *utf8_decode (const char *o, int *val) {
+ static const unsigned int limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFF};
+ const unsigned char *s = (const unsigned char *)o;
+ unsigned int c = s[0];
+ unsigned int res = 0; /* final result */
+ if (c < 0x80) /* ascii? */
+ res = c;
+ else {
+ int count = 0; /* to count number of continuation bytes */
+ while (c & 0x40) { /* still have continuation bytes? */
+ int cc = s[++count]; /* read next byte */
+ if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
+ return NULL; /* invalid byte sequence */
+ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
+ c <<= 1; /* to test next bit */
+ }
+ res |= ((c & 0x7F) << (count * 5)); /* add first byte */
+ if (count > 3 || res > MAXUNICODE || res <= limits[count])
+ return NULL; /* invalid byte sequence */
+ s += count; /* skip continuation bytes read */
+ }
+ if (val) *val = res;
+ return (const char *)s + 1; /* +1 to include first byte */
+}
+
+
+/*
+** utf8len(s [, i [, j]]) --> number of characters that start in the
+** range [i,j], or nil + current position if 's' is not well formed in
+** that interval
+*/
+static int utflen (lua_State *L) {
+ int n = 0;
+ size_t len;
+ const char *s = luaL_checklstring(L, 1, &len);
+ lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
+ lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len);
+ luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2,
+ "initial position out of string");
+ luaL_argcheck(L, --posj < (lua_Integer)len, 3,
+ "final position out of string");
+ while (posi <= posj) {
+ const char *s1 = utf8_decode(s + posi, NULL);
+ if (s1 == NULL) { /* conversion error? */
+ lua_pushnil(L); /* return nil ... */
+ lua_pushinteger(L, posi + 1); /* ... and current position */
+ return 2;
+ }
+ posi = s1 - s;
+ n++;
+ }
+ lua_pushinteger(L, n);
+ return 1;
+}
+
+
+/*
+** codepoint(s, [i, [j]]) -> returns codepoints for all characters
+** that start in the range [i,j]
+*/
+static int codepoint (lua_State *L) {
+ size_t len;
+ const char *s = luaL_checklstring(L, 1, &len);
+ lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
+ lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len);
+ int n;
+ const char *se;
+ luaL_argcheck(L, posi >= 1, 2, "out of range");
+ luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of range");
+ if (posi > pose) return 0; /* empty interval; return no values */
+ if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */
+ return luaL_error(L, "string slice too long");
+ n = (int)(pose - posi) + 1;
+ luaL_checkstack(L, n, "string slice too long");
+ n = 0;
+ se = s + pose;
+ for (s += posi - 1; s < se;) {
+ int code;
+ s = utf8_decode(s, &code);
+ if (s == NULL)
+ return luaL_error(L, "invalid UTF-8 code");
+ lua_pushinteger(L, code);
+ n++;
+ }
+ return n;
+}
+
+
+static void pushutfchar (lua_State *L, int arg) {
+ lua_Integer code = luaL_checkinteger(L, arg);
+ luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, "value out of range");
+ lua_pushfstring(L, "%U", (long)code);
+}
+
+
+/*
+** utfchar(n1, n2, ...) -> char(n1)..char(n2)...
+*/
+static int utfchar (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ if (n == 1) /* optimize common case of single char */
+ pushutfchar(L, 1);
+ else {
+ int i;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ for (i = 1; i <= n; i++) {
+ pushutfchar(L, i);
+ luaL_addvalue(&b);
+ }
+ luaL_pushresult(&b);
+ }
+ return 1;
+}
+
+
+/*
+** offset(s, n, [i]) -> index where n-th character counting from
+** position 'i' starts; 0 means character at 'i'.
+*/
+static int byteoffset (lua_State *L) {
+ size_t len;
+ const char *s = luaL_checklstring(L, 1, &len);
+ lua_Integer n = luaL_checkinteger(L, 2);
+ lua_Integer posi = (n >= 0) ? 1 : len + 1;
+ posi = u_posrelat(luaL_optinteger(L, 3, posi), len);
+ luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3,
+ "position out of range");
+ if (n == 0) {
+ /* find beginning of current byte sequence */
+ while (posi > 0 && iscont(s + posi)) posi--;
+ }
+ else {
+ if (iscont(s + posi))
+ luaL_error(L, "initial position is a continuation byte");
+ if (n < 0) {
+ while (n < 0 && posi > 0) { /* move back */
+ do { /* find beginning of previous character */
+ posi--;
+ } while (posi > 0 && iscont(s + posi));
+ n++;
+ }
+ }
+ else {
+ n--; /* do not move for 1st character */
+ while (n > 0 && posi < (lua_Integer)len) {
+ do { /* find beginning of next character */
+ posi++;
+ } while (iscont(s + posi)); /* (cannot pass final '\0') */
+ n--;
+ }
+ }
+ }
+ if (n == 0) /* did it find given character? */
+ lua_pushinteger(L, posi + 1);
+ else /* no such character */
+ lua_pushnil(L);
+ return 1;
+}
+
+
+static int iter_aux (lua_State *L) {
+ size_t len;
+ const char *s = luaL_checklstring(L, 1, &len);
+ lua_Integer n = lua_tointeger(L, 2) - 1;
+ if (n < 0) /* first iteration? */
+ n = 0; /* start from here */
+ else if (n < (lua_Integer)len) {
+ n++; /* skip current byte */
+ while (iscont(s + n)) n++; /* and its continuations */
+ }
+ if (n >= (lua_Integer)len)
+ return 0; /* no more codepoints */
+ else {
+ int code;
+ const char *next = utf8_decode(s + n, &code);
+ if (next == NULL || iscont(next))
+ return luaL_error(L, "invalid UTF-8 code");
+ lua_pushinteger(L, n + 1);
+ lua_pushinteger(L, code);
+ return 2;
+ }
+}
+
+
+static int iter_codes (lua_State *L) {
+ luaL_checkstring(L, 1);
+ lua_pushcfunction(L, iter_aux);
+ lua_pushvalue(L, 1);
+ lua_pushinteger(L, 0);
+ return 3;
+}
+
+
+/* pattern to match a single UTF-8 character */
+#define UTF8PATT "[\0-\x7F\xC2-\xF4][\x80-\xBF]*"
+
+
+static const luaL_Reg funcs[] = {
+ {"offset", byteoffset},
+ {"codepoint", codepoint},
+ {"char", utfchar},
+ {"len", utflen},
+ {"codes", iter_codes},
+ /* placeholders */
+ {"charpattern", NULL},
+ {NULL, NULL}
+};
+
+
+LUAMOD_API int luaopen_utf8 (lua_State *L) {
+ luaL_newlib(L, funcs);
+ lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)/sizeof(char) - 1);
+ lua_setfield(L, -2, "charpattern");
+ return 1;
+}
+
diff --git a/rockspecs/compat53-0.1-1.rockspec b/rockspecs/compat53-0.1-1.rockspec
new file mode 100644
index 0000000..0ff56b0
--- /dev/null
+++ b/rockspecs/compat53-0.1-1.rockspec
@@ -0,0 +1,31 @@
+package = "compat53"
+version = "0.1-1"
+source = {
+ url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.1.zip",
+ dir = "lua-compat-5.3-0.1",
+}
+description = {
+ summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1",
+ detailed = [[
+ This is a small module that aims to make it easier to write Lua
+ code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1.
+ It does *not* make Lua 5.2 (or even 5.1) entirely compatible
+ with Lua 5.3, but it brings the API closer to that of Lua 5.3.
+ ]],
+ homepage = "https://github.com/keplerproject/lua-compat-5.3",
+ license = "MIT"
+}
+dependencies = {
+ "lua >= 5.1, < 5.4",
+ --"struct" -- make Roberto's struct module optional
+}
+build = {
+ type = "builtin",
+ modules = {
+ ["compat53"] = "compat53.lua",
+ ["compat53.utf8"] = "lutf8lib.c",
+ ["compat53.table"] = "ltablib.c",
+ ["compat53.string"] = "lstrlib.c",
+ }
+}
+
diff --git a/rockspecs/compat53-0.2-1.rockspec b/rockspecs/compat53-0.2-1.rockspec
new file mode 100644
index 0000000..1b3c80e
--- /dev/null
+++ b/rockspecs/compat53-0.2-1.rockspec
@@ -0,0 +1,32 @@
+package = "compat53"
+version = "0.2-1"
+source = {
+ url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.2.zip",
+ dir = "lua-compat-5.3-0.2",
+}
+description = {
+ summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1",
+ detailed = [[
+ This is a small module that aims to make it easier to write Lua
+ code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1.
+ It does *not* make Lua 5.2 (or even 5.1) entirely compatible
+ with Lua 5.3, but it brings the API closer to that of Lua 5.3.
+ ]],
+ homepage = "https://github.com/keplerproject/lua-compat-5.3",
+ license = "MIT"
+}
+dependencies = {
+ "lua >= 5.1, < 5.4",
+ --"struct" -- make Roberto's struct module optional
+}
+build = {
+ type = "builtin",
+ modules = {
+ ["compat53.init"] = "compat53/init.lua",
+ ["compat53.module"] = "compat53/module.lua",
+ ["compat53.utf8"] = "lutf8lib.c",
+ ["compat53.table"] = "ltablib.c",
+ ["compat53.string"] = "lstrlib.c",
+ }
+}
+
diff --git a/rockspecs/compat53-0.3-1.rockspec b/rockspecs/compat53-0.3-1.rockspec
new file mode 100644
index 0000000..43f53d2
--- /dev/null
+++ b/rockspecs/compat53-0.3-1.rockspec
@@ -0,0 +1,32 @@
+package = "compat53"
+version = "0.3-1"
+source = {
+ url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.3.zip",
+ dir = "lua-compat-5.3-0.3",
+}
+description = {
+ summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1",
+ detailed = [[
+ This is a small module that aims to make it easier to write Lua
+ code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1.
+ It does *not* make Lua 5.2 (or even 5.1) entirely compatible
+ with Lua 5.3, but it brings the API closer to that of Lua 5.3.
+ ]],
+ homepage = "https://github.com/keplerproject/lua-compat-5.3",
+ license = "MIT"
+}
+dependencies = {
+ "lua >= 5.1, < 5.4",
+ --"struct" -- make Roberto's struct module optional
+}
+build = {
+ type = "builtin",
+ modules = {
+ ["compat53.init"] = "compat53/init.lua",
+ ["compat53.module"] = "compat53/module.lua",
+ ["compat53.utf8"] = "lutf8lib.c",
+ ["compat53.table"] = "ltablib.c",
+ ["compat53.string"] = "lstrlib.c",
+ }
+}
+
diff --git a/rockspecs/compat53-0.4-1.rockspec b/rockspecs/compat53-0.4-1.rockspec
new file mode 100644
index 0000000..9331e19
--- /dev/null
+++ b/rockspecs/compat53-0.4-1.rockspec
@@ -0,0 +1,32 @@
+package = "compat53"
+version = "0.4-1"
+source = {
+ url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.4.zip",
+ dir = "lua-compat-5.3-0.4",
+}
+description = {
+ summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1",
+ detailed = [[
+ This is a small module that aims to make it easier to write Lua
+ code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1.
+ It does *not* make Lua 5.2 (or even 5.1) entirely compatible
+ with Lua 5.3, but it brings the API closer to that of Lua 5.3.
+ ]],
+ homepage = "https://github.com/keplerproject/lua-compat-5.3",
+ license = "MIT"
+}
+dependencies = {
+ "lua >= 5.1, < 5.4",
+ --"struct" -- make Roberto's struct module optional
+}
+build = {
+ type = "builtin",
+ modules = {
+ ["compat53.init"] = "compat53/init.lua",
+ ["compat53.module"] = "compat53/module.lua",
+ ["compat53.utf8"] = "lutf8lib.c",
+ ["compat53.table"] = "ltablib.c",
+ ["compat53.string"] = "lstrlib.c",
+ }
+}
+
diff --git a/rockspecs/compat53-0.5-1.rockspec b/rockspecs/compat53-0.5-1.rockspec
new file mode 100644
index 0000000..3cceccd
--- /dev/null
+++ b/rockspecs/compat53-0.5-1.rockspec
@@ -0,0 +1,32 @@
+package = "compat53"
+version = "0.5-1"
+source = {
+ url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.5.zip",
+ dir = "lua-compat-5.3-0.5",
+}
+description = {
+ summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1",
+ detailed = [[
+ This is a small module that aims to make it easier to write Lua
+ code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1.
+ It does *not* make Lua 5.2 (or even 5.1) entirely compatible
+ with Lua 5.3, but it brings the API closer to that of Lua 5.3.
+ ]],
+ homepage = "https://github.com/keplerproject/lua-compat-5.3",
+ license = "MIT"
+}
+dependencies = {
+ "lua >= 5.1, < 5.4",
+ --"struct" -- make Roberto's struct module optional
+}
+build = {
+ type = "builtin",
+ modules = {
+ ["compat53.init"] = "compat53/init.lua",
+ ["compat53.module"] = "compat53/module.lua",
+ ["compat53.utf8"] = "lutf8lib.c",
+ ["compat53.table"] = "ltablib.c",
+ ["compat53.string"] = "lstrlib.c",
+ }
+}
+
diff --git a/rockspecs/compat53-scm-0.rockspec b/rockspecs/compat53-scm-0.rockspec
new file mode 100644
index 0000000..317e18c
--- /dev/null
+++ b/rockspecs/compat53-scm-0.rockspec
@@ -0,0 +1,32 @@
+package = "compat53"
+version = "scm-0"
+source = {
+ url = "https://github.com/keplerproject/lua-compat-5.3/archive/master.zip",
+ dir = "lua-compat-5.3-master",
+}
+description = {
+ summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1",
+ detailed = [[
+ This is a small module that aims to make it easier to write Lua
+ code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1.
+ It does *not* make Lua 5.2 (or even 5.1) entirely compatible
+ with Lua 5.3, but it brings the API closer to that of Lua 5.3.
+ ]],
+ homepage = "https://github.com/keplerproject/lua-compat-5.3",
+ license = "MIT"
+}
+dependencies = {
+ "lua >= 5.1, < 5.4",
+ --"struct" -- make Roberto's struct module optional
+}
+build = {
+ type = "builtin",
+ modules = {
+ ["compat53.init"] = "compat53/init.lua",
+ ["compat53.module"] = "compat53/module.lua",
+ ["compat53.utf8"] = "lutf8lib.c",
+ ["compat53.table"] = "ltablib.c",
+ ["compat53.string"] = "lstrlib.c",
+ }
+}
+
diff --git a/tests/test.lua b/tests/test.lua
new file mode 100755
index 0000000..582f55e
--- /dev/null
+++ b/tests/test.lua
@@ -0,0 +1,789 @@
+#!/usr/bin/env lua
+
+local F, tproxy, writefile, noprint, ___
+do
+ local type, unpack = type, table.unpack or unpack
+ local assert, io = assert, io
+ function F(...)
+ local args, n = { ... }, select('#', ...)
+ for i = 1, n do
+ local t = type(args[i])
+ if t ~= "string" and t ~= "number" and t ~= "boolean" then
+ args[i] = t
+ end
+ end
+ return unpack(args, 1, n)
+ end
+ function tproxy(t)
+ return setmetatable({}, {
+ __index = t,
+ __newindex = t,
+ __len = function() return #t end,
+ }), t
+ end
+ function writefile(name, contents, bin)
+ local f = assert(io.open(name, bin and "wb" or "w"))
+ f:write(contents)
+ f:close()
+ end
+ function noprint() end
+ local sep = ("="):rep(70)
+ function ___()
+ print(sep)
+ end
+end
+
+local V = _VERSION:gsub("^.*(%d+)%.(%d+)$", "%1%2")
+if jit then V = "jit" end
+
+local mode = "global"
+if arg[1] == "module" then
+ mode = "module"
+end
+
+
+package.path = "../?.lua;../?/init.lua"
+package.cpath = "./?-"..V..".so;./?-"..V..".dll;./?.so;./?.dll"
+if mode == "module" then
+ print("testing Lua API using `compat53.module` ...")
+ _ENV = require("compat53.module")
+ if setfenv then setfenv(1, _ENV) end
+else
+ print("testing Lua API using `compat53` ...")
+ require("compat53")
+end
+
+
+___''
+do
+ print("assert", F(pcall(assert, false)))
+ print("assert", F(pcall(assert, false, nil)))
+ print("assert", F(pcall(assert, false, "error msg")))
+ print("assert", F(pcall(assert, nil, {})))
+ print("assert", F(pcall(assert, 1, 2, 3)))
+end
+
+
+___''
+do
+ local t = setmetatable({}, { __index = { 1, false, "three" } })
+ for i,v in ipairs(t) do
+ print("ipairs", i, v)
+ end
+end
+
+
+___''
+do
+ local p, t = tproxy{ "a", "b", "c" }
+ print("table.concat", table.concat(p))
+ print("table.concat", table.concat(p, ",", 2))
+ print("table.concat", table.concat(p, ".", 1, 2))
+ print("table.concat", table.concat(t))
+ print("table.concat", table.concat(t, ",", 2))
+ print("table.concat", table.concat(t, ".", 1, 2))
+end
+
+
+___''
+do
+ local p, t = tproxy{ "a", "b", "c" }
+ table.insert(p, "d")
+ print("table.insert", next(p), t[4])
+ table.insert(p, 1, "z")
+ print("table.insert", next(p), t[1], t[2])
+ table.insert(p, 2, "y")
+ print("table.insert", next(p), t[1], t[2], p[3])
+ t = { "a", "b", "c" }
+ table.insert(t, "d")
+ print("table.insert", t[1], t[2], t[3], t[4])
+ table.insert(t, 1, "z")
+ print("table.insert", t[1], t[2], t[3], t[4], t[5])
+ table.insert(t, 2, "y")
+ print("table.insert", t[1], t[2], t[3], t[4], t[5])
+end
+
+
+___''
+do
+ local ps, s = tproxy{ "a", "b", "c", "d" }
+ local pd, d = tproxy{ "A", "B", "C", "D" }
+ table.move(ps, 1, 4, 1, pd)
+ print("table.move", next(pd), d[1], d[2], d[3], d[4])
+ pd, d = tproxy{ "A", "B", "C", "D" }
+ table.move(ps, 2, 4, 1, pd)
+ print("table.move", next(pd), d[1], d[2], d[3], d[4])
+ pd, d = tproxy{ "A", "B", "C", "D" }
+ table.move(ps, 2, 3, 4, pd)
+ print("table.move", next(pd), d[1], d[2], d[3], d[4], d[5])
+ table.move(ps, 2, 4, 1)
+ print("table.move", next(ps), s[1], s[2], s[3], s[4])
+ ps, s = tproxy{ "a", "b", "c", "d" }
+ table.move(ps, 2, 3, 4)
+ print("table.move", next(ps), s[1], s[2], s[3], s[4], s[5])
+ s = { "a", "b", "c", "d" }
+ d = { "A", "B", "C", "D" }
+ table.move(s, 1, 4, 1, d)
+ print("table.move", d[1], d[2], d[3], d[4])
+ d = { "A", "B", "C", "D" }
+ table.move(s, 2, 4, 1, d)
+ print("table.move", d[1], d[2], d[3], d[4])
+ d = { "A", "B", "C", "D" }
+ table.move(s, 2, 3, 4, d)
+ print("table.move", d[1], d[2], d[3], d[4], d[5])
+ table.move(s, 2, 4, 1)
+ print("table.move", s[1], s[2], s[3], s[4])
+ s = { "a", "b", "c", "d" }
+ table.move(s, 2, 3, 4)
+ print("table.move", s[1], s[2], s[3], s[4], s[5])
+end
+
+
+___''
+do
+ local p, t = tproxy{ "a", "b", "c", "d", "e" }
+ print("table.remove", table.remove(p))
+ print("table.remove", next(p), t[1], t[2], t[3], t[4], t[5])
+ print("table.remove", table.remove(p, 1))
+ print("table.remove", next(p), t[1], t[2], t[3], t[4])
+ print("table.remove", table.remove(p, 2))
+ print("table.remove", next(p), t[1], t[2], t[3])
+ print("table.remove", table.remove(p, 3))
+ print("table.remove", next(p), t[1], t[2], t[3])
+ p, t = tproxy{}
+ print("table.remove", table.remove(p))
+ print("table.remove", next(p), next(t))
+ t = { "a", "b", "c", "d", "e" }
+ print("table.remove", table.remove(t))
+ print("table.remove", t[1], t[2], t[3], t[4], t[5])
+ print("table.remove", table.remove(t, 1))
+ print("table.remove", t[1], t[2], t[3], t[4])
+ print("table.remove", table.remove(t, 2))
+ print("table.remove", t[1], t[2], t[3])
+ print("table.remove", table.remove(t, 3))
+ print("table.remove", t[1], t[2], t[3])
+ t = {}
+ print("table.remove", table.remove(t))
+ print("table.remove", next(t))
+end
+
+___''
+do
+ local p, t = tproxy{ 3, 1, 5, 2, 8, 5, 2, 9, 7, 4 }
+ table.sort(p)
+ print("table.sort", next(p))
+ for i,v in ipairs(t) do
+ print("table.sort", i, v)
+ end
+ table.sort(p)
+ print("table.sort", next(p))
+ for i,v in ipairs(t) do
+ print("table.sort", i, v)
+ end
+ p, t = tproxy{ 9, 8, 7, 6, 5, 4, 3, 2, 1 }
+ table.sort(p)
+ print("table.sort", next(p))
+ for i,v in ipairs(t) do
+ print("table.sort", i, v)
+ end
+ table.sort(p, function(a, b) return a > b end)
+ print("table.sort", next(p))
+ for i,v in ipairs(t) do
+ print("table.sort", i, v)
+ end
+ p, t = tproxy{ 1, 1, 1, 1, 1 }
+ print("table.sort", next(p))
+ for i,v in ipairs(t) do
+ print("table.sort", i, v)
+ end
+ t = { 3, 1, 5, 2, 8, 5, 2, 9, 7, 4 }
+ table.sort(t)
+ for i,v in ipairs(t) do
+ print("table.sort", i, v)
+ end
+ table.sort(t, function(a, b) return a > b end)
+ for i,v in ipairs(t) do
+ print("table.sort", i, v)
+ end
+end
+
+
+___''
+do
+ local p, t = tproxy{ "a", "b", "c" }
+ print("table.unpack", table.unpack(p))
+ print("table.unpack", table.unpack(p, 2))
+ print("table.unpack", table.unpack(p, 1, 2))
+ print("table.unpack", table.unpack(t))
+ print("table.unpack", table.unpack(t, 2))
+ print("table.unpack", table.unpack(t, 1, 2))
+end
+
+
+___''
+print("math.maxinteger", math.maxinteger+1 > math.maxinteger)
+print("math.mininteger", math.mininteger-1 < math.mininteger)
+
+
+___''
+print("math.tointeger", math.tointeger(0))
+print("math.tointeger", math.tointeger(math.pi))
+print("math.tointeger", math.tointeger("hello"))
+print("math.tointeger", math.tointeger(math.maxinteger+2.0))
+print("math.tointeger", math.tointeger(math.mininteger*2.0))
+
+
+___''
+print("math.type", math.type(0))
+print("math.type", math.type(math.pi))
+print("math.type", math.type("hello"))
+
+
+___''
+print("math.ult", math.ult(1, 2), math.ult(2, 1))
+print("math.ult", math.ult(-1, 2), math.ult(2, -1))
+print("math.ult", math.ult(-1, -2), math.ult(-2, -1))
+print("math.ult", pcall(math.ult, "x", 2))
+print("math.ult", pcall(math.ult, 1, 2.1))
+___''
+
+
+if utf8.len then
+ local unpack = table.unpack or unpack
+ local function utf8rt(s)
+ local t = { utf8.codepoint(s, 1, #s) }
+ local ps, cs = {}, {}
+ for p,c in utf8.codes(s) do
+ ps[#ps+1], cs[#cs+1] = p, c
+ end
+ print("utf8.codes", unpack(ps))
+ print("utf8.codes", unpack(cs))
+ print("utf8.codepoint", unpack(t))
+ print("utf8.len", utf8.len(s), #t, #s)
+ print("utf8.char", utf8.char(unpack(t)))
+ end
+ utf8rt("äöüßÄÖÜ")
+ utf8rt("abcdefg")
+ ___''
+ local s = "äöüßÄÖÜ"
+ print("utf8.offset", utf8.offset(s, 1, 1))
+ print("utf8.offset", utf8.offset(s, 2, 1))
+ print("utf8.offset", utf8.offset(s, 3, 1))
+ print("utf8.offset", pcall(utf8.offset, s, 3, 2))
+ print("utf8.offset", utf8.offset(s, 3, 3))
+ print("utf8.offset", utf8.offset(s, -1, 7))
+ print("utf8.offset", utf8.offset(s, -2, 7))
+ print("utf8.offset", utf8.offset(s, -3, 7))
+ print("utf8.offset", utf8.offset(s, -1))
+ ___''
+else
+ print("XXX: utf8 module not available")
+end
+
+
+if string.pack then
+ local format = "bBhHlLjJdc3z"
+ local s = string.pack(format, -128, 255, -32768, 65535, -2147483648, 4294967295, -32768, 65536, 1.25, "abc", "defgh")
+ print("string.unpack", string.unpack(format, s))
+ ___''
+else
+ print("XXX: string packing not available")
+end
+
+
+print("testing Lua API for Lua 5.1 ...")
+
+___''
+print("debug.getuservalue()", F(debug.getuservalue(false)))
+print("debug.setuservalue()", pcall(function()
+ debug.setuservalue(false, {})
+end))
+print("debug.setmetatable()", F(debug.setmetatable({}, {})))
+
+
+___''
+do
+ local t = setmetatable({}, {
+ __pairs = function() return pairs({ a = "a" }) end,
+ })
+ for k,v in pairs(t) do
+ print("pairs()", k, v)
+ end
+end
+
+
+___''
+do
+ local code = "print('hello world')\n"
+ local badcode = "print('blub\n"
+ print("load()", pcall(function() load(true) end))
+ print("load()", F(load(badcode)))
+ print("load()", F(load(code)))
+ print("load()", F(load(code, "[L]")))
+ print("load()", F(load(code, "[L]", "b")))
+ print("load()", F(load(code, "[L]", "t")))
+ print("load()", F(load(code, "[L]", "bt")))
+ local f = load(code, "[L]", "bt", {})
+ print("load()", pcall(f))
+ f = load(code, "[L]", "bt", { print = noprint })
+ print("load()", pcall(f))
+ local bytecode = string.dump(f)
+ print("load()", F(load(bytecode)))
+ print("load()", F(load(bytecode, "[L]")))
+ print("load()", F(load(bytecode, "[L]", "b")))
+ print("load()", F(load(bytecode, "[L]", "t")))
+ print("load()", F(load(bytecode, "[L]", "bt")))
+ f = load(bytecode, "[L]", "bt", {})
+ print("load()", pcall(f))
+ f = load(bytecode, "[L]", "bt", { print = noprint })
+ print("load()", pcall(f))
+ local function make_loader(code)
+ local mid = math.floor( #code/2 )
+ local array = { code:sub(1, mid), code:sub(mid+1) }
+ local i = 0
+ return function()
+ i = i + 1
+ return array[i]
+ end
+ end
+ print("load()", F(load(make_loader(badcode))))
+ print("load()", F(load(make_loader(code))))
+ print("load()", F(load(make_loader(code), "[L]")))
+ print("load()", F(load(make_loader(code), "[L]", "b")))
+ print("load()", F(load(make_loader(code), "[L]", "t")))
+ print("load()", F(load(make_loader(code), "[L]", "bt")))
+ f = load(make_loader(code), "[L]", "bt", {})
+ print("load()", pcall(f))
+ f = load(make_loader(code), "[L]", "bt", { print = noprint })
+ print("load()", pcall(f))
+ print("load()", F(load(make_loader(bytecode))))
+ print("load()", F(load(make_loader(bytecode), "[L]")))
+ print("load()", F(load(make_loader(bytecode), "[L]", "b")))
+ print("load()", F(load(make_loader(bytecode), "[L]", "t")))
+ print("load()", F(load(make_loader(bytecode), "[L]", "bt")))
+ f = load(make_loader(bytecode), "[L]", "bt", {})
+ print("load()", pcall(f))
+ f = load(make_loader(bytecode), "[L]", "bt", { print = noprint })
+ print("load()", pcall(f))
+ writefile("good.lua", code)
+ writefile("bad.lua", badcode)
+ writefile("good.luac", bytecode, true)
+ print("loadfile()", F(loadfile("bad.lua")))
+ print("loadfile()", F(loadfile("good.lua")))
+ print("loadfile()", F(loadfile("good.lua", "b")))
+ print("loadfile()", F(loadfile("good.lua", "t")))
+ print("loadfile()", F(loadfile("good.lua", "bt")))
+ f = loadfile("good.lua", "bt", {})
+ print("loadfile()", pcall(f))
+ f = loadfile("good.lua", "bt", { print = noprint })
+ print("loadfile()", pcall(f))
+ print("loadfile()", F(loadfile("good.luac")))
+ print("loadfile()", F(loadfile("good.luac", "b")))
+ print("loadfile()", F(loadfile("good.luac", "t")))
+ print("loadfile()", F(loadfile("good.luac", "bt")))
+ f = loadfile("good.luac", "bt", {})
+ print("loadfile()", pcall(f))
+ f = loadfile("good.luac", "bt", { print = noprint })
+ print("loadfile()", pcall(f))
+ os.remove("good.lua")
+ os.remove("bad.lua")
+ os.remove("good.luac")
+end
+
+
+___''
+do
+ local function func(throw)
+ if throw then
+ error("argh")
+ else
+ return 1, 2, 3
+ end
+ end
+ local function tb(err) return "|"..err.."|" end
+ print("xpcall()", xpcall(func, debug.traceback, false))
+ print("xpcall()", xpcall(func, debug.traceback, true))
+ print("xpcall()", xpcall(func, tb, true))
+ if mode ~= "module" then
+ local function func2(cb)
+ print("xpcall()", xpcall(cb, debug.traceback, "str"))
+ end
+ local function func3(cb)
+ print("pcall()", pcall(cb, "str"))
+ end
+ local function cb(arg)
+ coroutine.yield(2)
+ return arg
+ end
+ local c = coroutine.wrap(func2)
+ print("xpcall()", c(cb))
+ print("xpcall()", c())
+ local c = coroutine.wrap(func3)
+ print("pcall()", c(cb))
+ print("pcall()", c())
+ end
+end
+
+
+___''
+do
+ local t = setmetatable({ 1 }, { __len = function() return 5 end })
+ print("rawlen()", rawlen(t), rawlen("123"))
+end
+
+
+___''
+print("os.execute()", os.execute("exit 1"))
+io.flush()
+print("os.execute()", os.execute("echo 'hello world!'"))
+io.flush()
+print("os.execute()", os.execute("no_such_file"))
+
+
+___''
+do
+ local t = table.pack("a", nil, "b", nil)
+ print("table.(un)pack()", t.n, table.unpack(t, 1, t.n))
+end
+
+
+___''
+do
+ print("coroutine.running()", F(coroutine.wrap(function()
+ return coroutine.running()
+ end)()))
+ print("coroutine.running()", F(coroutine.running()))
+ local main_co, co1, co2 = coroutine.running()
+ -- coroutine.yield
+ if mode ~= "module" then
+ print("coroutine.yield()", pcall(function()
+ coroutine.yield(1, 2, 3)
+ end))
+ end
+ print("coroutine.yield()", coroutine.wrap(function()
+ coroutine.yield(1, 2, 3)
+ end)())
+ print("coroutine.resume()", coroutine.resume(main_co, 1, 2, 3))
+ co1 = coroutine.create(function(a, b, c)
+ print("coroutine.resume()", a, b, c)
+ return a, b, c
+ end)
+ print("coroutine.resume()", coroutine.resume(co1, 1, 2, 3))
+ co1 = coroutine.create(function()
+ print("coroutine.status()", "[co1] main is", coroutine.status(main_co))
+ print("coroutine.status()", "[co1] co2 is", coroutine.status(co2))
+ end)
+ co2 = coroutine.create(function()
+ print("coroutine.status()", "[co2] main is", coroutine.status(main_co))
+ print("coroutine.status()", "[co2] co2 is", coroutine.status(co2))
+ coroutine.yield()
+ coroutine.resume(co1)
+ end)
+ print("coroutine.status()", coroutine.status(main_co))
+ print("coroutine.status()", coroutine.status(co2))
+ coroutine.resume(co2)
+ print("coroutine.status()", F(coroutine.status(co2)))
+ coroutine.resume(co2)
+ print("coroutine.status()", F(coroutine.status(co2)))
+end
+
+
+___''
+print("math.log()", math.log(1000))
+print("math.log()", math.log(1000, 10))
+
+
+___''
+do
+ local path, prefix = "./?.lua;?/init.lua;../?.lua", "package.searchpath()"
+ print(prefix, package.searchpath("no.such.module", path))
+ print(prefix, package.searchpath("no.such.module", ""))
+ print(prefix, package.searchpath("compat53", path))
+ print(prefix, package.searchpath("no:such:module", path, ":", "|"))
+end
+
+
+___''
+if mode ~= "module" then
+ local function mod_func() return {} end
+ local function my_searcher(name)
+ if name == "my.module" then
+ print("package.searchers", "my.module found")
+ return mod_func
+ end
+ end
+ local function my_searcher2(name)
+ if name == "my.module" then
+ print("package.searchers", "my.module found 2")
+ return mod_func
+ end
+ end
+ table.insert(package.searchers, my_searcher)
+ require("my.module")
+ package.loaded["my.module"] = nil
+ local new_s = { my_searcher2 }
+ for i,f in ipairs(package.searchers) do
+ new_s[i+1] = f
+ end
+ package.searchers = new_s
+ require("my.module")
+end
+
+
+___''
+do
+ print("string.find()", string.find("abc\0abc\0abc", "[^a\0]+"))
+ print("string.find()", string.find("abc\0abc\0abc", "%w+\0", 5))
+ for x in string.gmatch("abc\0def\0ghi", "[^\0]+") do
+ print("string.gmatch()", x)
+ end
+ for x in string.gmatch("abc\0def\0ghi", "%w*\0") do
+ print("string.gmatch()", #x)
+ end
+ print("string.gsub()", string.gsub("abc\0def\0ghi", "[\0]", "X"))
+ print("string.gsub()", string.gsub("abc\0def\0ghi", "%w*\0", "X"))
+ print("string.gsub()", string.gsub("abc\0def\0ghi", "%A", "X"))
+ print("string.match()", string.match("abc\0abc\0abc", "([^\0a]+)"))
+ print("string.match()", #string.match("abc\0abc\0abc", ".*\0"))
+ print("string.rep()", string.rep("a", 0))
+ print("string.rep()", string.rep("b", 1))
+ print("string.rep()", string.rep("c", 4))
+ print("string.rep()", string.rep("a", 0, "|"))
+ print("string.rep()", string.rep("b", 1, "|"))
+ print("string.rep()", string.rep("c", 4, "|"))
+ local _tostring = tostring
+ function tostring(v)
+ if type(v) == "number" then
+ return "(".._tostring(v)..")"
+ else
+ return _tostring(v)
+ end
+ end
+ print("string.format()", string.format("%q", "\"\\\0000\0010\002\r\n0\t0\""))
+ print("string.format()", string.format("%12.3fx%%sxx%.6s", 3.1, {}))
+ print("string.format()", string.format("%-3f %%%s %%s", 3.1, true))
+ print("string.format()", string.format("% 3.2g %%d %%%s", 3.1, nil))
+ print("string.format()", string.format("%+3d %%d %%%%%10.6s", 3, io.stdout))
+ print("string.format()", pcall(function()
+ print("string.format()", string.format("%d %%s", {}))
+ end))
+ tostring = _tostring
+end
+
+
+___''
+do
+ print("io.write()", io.type(io.write("hello world\n")))
+ local f = assert(io.tmpfile())
+ print("file:write()", io.type(f:write("hello world\n")))
+ f:close()
+end
+
+
+___''
+do
+ writefile("data.txt", "123 18.8 hello world\ni'm here\n")
+ io.input("data.txt")
+ print("io.read()", io.read("*n", "*number", "*l", "*a"))
+ io.input("data.txt")
+ print("io.read()", io.read("n", "number", "l", "a"))
+ io.input(io.stdin)
+ if mode ~= "module" then
+ local f = assert(io.open("data.txt", "r"))
+ print("file:read()", f:read("*n", "*number", "*l", "*a"))
+ f:close()
+ f = assert(io.open("data.txt", "r"))
+ print("file:read()", f:read("n", "number", "l", "a"))
+ f:close()
+ end
+ os.remove("data.txt")
+end
+
+
+___''
+do
+ writefile("data.txt", "123 18.8 hello world\ni'm here\n")
+ for a,b in io.lines("test.lua", 2, "*l") do
+ print("io.lines()", a, b)
+ break
+ end
+ for l in io.lines("test.lua") do
+ print("io.lines()", l)
+ break
+ end
+ for n1,n2,rest in io.lines("data.txt", "*n", "n", "*a") do
+ print("io.lines()", n1, n2, rest)
+ end
+ for l in io.lines("data.txt") do
+ print("io.lines()", l)
+ end
+ print("io.lines()", pcall(function()
+ for l in io.lines("data.txt", "*x") do print(l) end
+ end))
+ print("io.lines()", pcall(function()
+ for l in io.lines("no_such_file.txt") do print(l) end
+ end))
+ if mode ~= "module" then
+ local f = assert(io.open("test.lua", "r"))
+ for a,b in f:lines(2, "*l") do
+ print("file:lines()", a, b)
+ break
+ end
+ f:close()
+ f = assert(io.open("data.txt", "r"))
+ for n1,n2,rest in f:lines("*n", "n", "*a") do
+ print("file:lines()", n1, n2, rest)
+ end
+ f:close()
+ f = assert(io.open("data.txt", "r"))
+ for l in f:lines() do
+ print("file:lines()", l)
+ end
+ f:close()
+ print("file:lines()", pcall(function()
+ for l in f:lines() do print(l) end
+ end))
+ print("file:lines()", pcall(function()
+ local f = assert(io.open("data.txt", "r"))
+ for l in f:lines("*l", "*x") do print(l) end
+ f:close()
+ end))
+ end
+ os.remove("data.txt")
+end
+___''
+
+
+print("testing C API ...")
+local mod = require("testmod")
+___''
+print(mod.isinteger(1))
+print(mod.isinteger(0))
+print(mod.isinteger(1234567))
+print(mod.isinteger(12.3))
+print(mod.isinteger(math.huge))
+print(mod.isinteger(math.sqrt(-1)))
+
+
+___''
+print(mod.rotate(1, 1, 2, 3, 4, 5, 6))
+print(mod.rotate(-1, 1, 2, 3, 4, 5, 6))
+print(mod.rotate(4, 1, 2, 3, 4, 5, 6))
+print(mod.rotate(-4, 1, 2, 3, 4, 5, 6))
+
+
+___''
+print(mod.strtonum("+123"))
+print(mod.strtonum(" 123 "))
+print(mod.strtonum("-1.23"))
+print(mod.strtonum(" 123 abc"))
+print(mod.strtonum("jkl"))
+
+
+___''
+local a, b, c = mod.requiref()
+print( type(a), type(b), type(c),
+ a.boolean, b.boolean, c.boolean,
+ type(requiref1), type(requiref2), type(requiref3))
+
+___''
+local proxy, backend = {}, {}
+setmetatable(proxy, { __index = backend, __newindex = backend })
+print(rawget(proxy, 1), rawget(backend, 1))
+print(mod.getseti(proxy, 1))
+print(rawget(proxy, 1), rawget(backend, 1))
+print(mod.getseti(proxy, 1))
+print(rawget(proxy, 1), rawget(backend, 1))
+
+-- tests for Lua 5.1
+___''
+print(mod.tonumber(12))
+print(mod.tonumber("12"))
+print(mod.tonumber("0"))
+print(mod.tonumber(false))
+print(mod.tonumber("error"))
+
+___''
+print(mod.tointeger(12))
+print(mod.tointeger("12"))
+print(mod.tointeger("0"))
+print( "aaa" )
+print(mod.tointeger(math.pi))
+print( "bbb" )
+print(mod.tointeger(false))
+print(mod.tointeger("error"))
+
+___''
+print(mod.len("123"))
+print(mod.len({ 1, 2, 3}))
+print(pcall(mod.len, true))
+local ud, meta = mod.newproxy()
+meta.__len = function() return 5 end
+print(mod.len(ud))
+meta.__len = function() return true end
+print(pcall(mod.len, ud))
+
+___''
+print(mod.copy(true, "string", {}, 1))
+
+___''
+print(mod.rawxetp())
+print(mod.rawxetp("I'm back"))
+
+___''
+print(F(mod.globals()), mod.globals() == _G)
+
+___''
+local t = {}
+print(F(mod.subtable(t)))
+local x, msg = mod.subtable(t)
+print(F(x, msg, x == t.xxx))
+
+___''
+print(F(mod.udata()))
+print(mod.udata("nosuchtype"))
+
+___''
+print(F(mod.uservalue()))
+
+___''
+print(mod.getupvalues())
+
+___''
+print(mod.absindex("hi", true))
+
+___''
+print(mod.arith(2, 1))
+print(mod.arith(3, 5))
+
+___''
+print(mod.compare(1, 1))
+print(mod.compare(2, 1))
+print(mod.compare(1, 2))
+
+___''
+print(mod.tolstring("string"))
+local t = setmetatable({}, {
+ __tostring = function(v) return "mytable" end
+})
+print(mod.tolstring(t))
+local t = setmetatable({}, {
+ __tostring = function(v) return nil end
+})
+print(pcall(mod.tolstring, t))
+local ud, meta = mod.newproxy()
+meta.__name = "XXX"
+print(mod.tolstring(ud):gsub(":.*$", ": yyy"))
+
+___''
+print(mod.pushstring())
+
+___''
+print(mod.buffer())
+
+___''
+print(mod.exec("exit 0"))
+print(mod.exec("exit 1"))
+print(mod.exec("exit 25"))
+___''
+
diff --git a/tests/testmod.c b/tests/testmod.c
new file mode 100644
index 0000000..868136b
--- /dev/null
+++ b/tests/testmod.c
@@ -0,0 +1,318 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <lua.h>
+#include <lauxlib.h>
+#include "compat-5.3.h"
+
+
+static int test_isinteger (lua_State *L) {
+ lua_pushboolean(L, lua_isinteger(L, 1));
+ return 1;
+}
+
+
+static int test_rotate (lua_State *L) {
+ int r = luaL_checkint(L, 1);
+ int n = lua_gettop(L)-1;
+ luaL_argcheck(L, (r < 0 ? -r : r) <= n, 1, "not enough arguments");
+ lua_rotate(L, 2, r);
+ return n;
+}
+
+
+static int test_str2num (lua_State *L) {
+ const char *s = luaL_checkstring(L, 1);
+ size_t len = lua_stringtonumber(L, s);
+ if (len == 0)
+ lua_pushnumber(L, 0);
+ lua_pushinteger(L, (lua_Integer)len);
+ return 2;
+}
+
+
+static int my_mod (lua_State *L ) {
+ lua_newtable(L);
+ lua_pushboolean(L, 1);
+ lua_setfield(L, -2, "boolean");
+ return 1;
+}
+
+static int test_requiref (lua_State *L) {
+ lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+ lua_newtable(L);
+ lua_pushboolean(L, 0);
+ lua_setfield(L, -2, "boolean");
+ lua_setfield(L, -2, "requiref3");
+ lua_pop(L, 1);
+ luaL_requiref(L, "requiref1", my_mod, 0);
+ luaL_requiref(L, "requiref2", my_mod, 1);
+ luaL_requiref(L, "requiref3", my_mod, 1);
+ return 3;
+}
+
+static int test_getseti (lua_State *L) {
+ lua_Integer k = luaL_checkinteger(L, 2);
+ lua_Integer n = 0;
+ if (lua_geti(L, 1, k) == LUA_TNUMBER) {
+ n = lua_tointeger(L, -1);
+ } else {
+ lua_pop(L, 1);
+ lua_pushinteger(L, n);
+ }
+ lua_pushinteger(L, n+1);
+ lua_seti(L, 1, k);
+ return 1;
+}
+
+
+/* additional tests for Lua5.1 */
+#define NUP 3
+
+static int test_newproxy (lua_State *L) {
+ lua_settop(L, 0);
+ lua_newuserdata(L, 0);
+ lua_newtable(L);
+ lua_pushvalue(L, -1);
+ lua_pushboolean(L, 1);
+ lua_setfield(L, -2, "__gc");
+ lua_setmetatable(L, -3);
+ return 2;
+}
+
+static int test_absindex (lua_State *L) {
+ int i = 1;
+ for (i = 1; i <= NUP; ++i)
+ lua_pushvalue(L, lua_absindex(L, lua_upvalueindex(i)));
+ lua_pushvalue(L, lua_absindex(L, LUA_REGISTRYINDEX));
+ lua_pushstring(L, lua_typename(L, lua_type(L, lua_absindex(L, -1))));
+ lua_replace(L, lua_absindex(L, -2));
+ lua_pushvalue(L, lua_absindex(L, -2));
+ lua_pushvalue(L, lua_absindex(L, -4));
+ lua_pushvalue(L, lua_absindex(L, -6));
+ i += 3;
+ lua_pushvalue(L, lua_absindex(L, 1));
+ lua_pushvalue(L, lua_absindex(L, 2));
+ lua_pushvalue(L, lua_absindex(L, 3));
+ i += 3;
+ return i;
+}
+
+static int test_arith (lua_State *L) {
+ lua_settop(L, 2);
+ lua_pushvalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_arith(L, LUA_OPADD);
+ lua_pushvalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_arith(L, LUA_OPSUB);
+ lua_pushvalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_arith(L, LUA_OPMUL);
+ lua_pushvalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_arith(L, LUA_OPDIV);
+ lua_pushvalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_arith(L, LUA_OPMOD);
+ lua_pushvalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_arith(L, LUA_OPPOW);
+ lua_pushvalue(L, 1);
+ lua_arith(L, LUA_OPUNM);
+ return lua_gettop(L)-2;
+}
+
+static int test_compare (lua_State *L) {
+ luaL_checknumber(L, 1);
+ luaL_checknumber(L, 2);
+ lua_settop(L, 2);
+ lua_pushboolean(L, lua_compare(L, 1, 2, LUA_OPEQ));
+ lua_pushboolean(L, lua_compare(L, 1, 2, LUA_OPLT));
+ lua_pushboolean(L, lua_compare(L, 1, 2, LUA_OPLE));
+ return 3;
+}
+
+static int test_globals (lua_State *L) {
+ lua_pushglobaltable(L);
+ return 1;
+}
+
+static int test_tonumber (lua_State *L) {
+ int isnum = 0;
+ lua_Number n = lua_tonumberx(L, 1, &isnum);
+ if (!isnum)
+ lua_pushnil(L);
+ else
+ lua_pushnumber(L, n);
+ return 1;
+}
+
+static int test_tointeger (lua_State *L) {
+ int isnum = 0;
+ lua_Integer n = lua_tointegerx(L, 1, &isnum);
+ if (!isnum)
+ lua_pushnil(L);
+ else
+ lua_pushinteger(L, n);
+ return 1;
+}
+
+static int test_len (lua_State *L) {
+ luaL_checkany(L, 1);
+ lua_len(L, 1);
+ lua_pushinteger(L, luaL_len(L, 1));
+ return 2;
+}
+
+static int test_copy (lua_State *L) {
+ int args = lua_gettop(L);
+ if (args >= 2) {
+ int i = 0;
+ for (i = args-1; i > 0; --i)
+ lua_copy(L, args, i);
+ }
+ return args;
+}
+
+/* need an address */
+static char const dummy = 0;
+
+static int test_rawxetp (lua_State *L) {
+ if (lua_gettop(L) > 0)
+ lua_pushvalue(L, 1);
+ else
+ lua_pushliteral(L, "hello again");
+ lua_rawsetp(L, LUA_REGISTRYINDEX, &dummy);
+ lua_settop(L, 0);
+ lua_rawgetp(L, LUA_REGISTRYINDEX, &dummy);
+ return 1;
+}
+
+static int test_udata (lua_State *L) {
+ const char *tname = luaL_optstring(L, 1, "utype1");
+ void *u1 = lua_newuserdata(L, 1);
+ int u1pos = lua_gettop(L);
+ void *u2 = lua_newuserdata(L, 1);
+ int u2pos = lua_gettop(L);
+ luaL_newmetatable(L, "utype1");
+ luaL_newmetatable(L, "utype2");
+ lua_pop(L, 2);
+ luaL_setmetatable(L, "utype2");
+ lua_pushvalue(L, u1pos);
+ luaL_setmetatable(L, "utype1");
+ lua_pop(L, 1);
+ (void)u1;
+ (void)u2;
+ lua_pushlightuserdata(L, luaL_testudata(L, u1pos, tname));
+ lua_pushlightuserdata(L, luaL_testudata(L, u2pos, tname));
+ luaL_getmetatable(L, "utype1");
+ lua_getfield(L, -1, "__name");
+ lua_replace(L, -2);
+ return 3;
+}
+
+static int test_subtable (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 1);
+ if (luaL_getsubtable(L, 1, "xxx")) {
+ lua_pushliteral(L, "oldtable");
+ } else {
+ lua_pushliteral(L, "newtable");
+ }
+ return 2;
+}
+
+static int test_uservalue (lua_State *L) {
+ void *udata = lua_newuserdata(L, 1);
+ int ui = lua_gettop(L);
+ lua_newtable(L);
+ lua_setuservalue(L, ui);
+ lua_getuservalue(L, ui);
+ (void)udata;
+ return 1;
+}
+
+static int test_upvalues (lua_State *L) {
+ int i = 1;
+ for (i = 1; i <= NUP; ++i)
+ lua_pushvalue(L, lua_upvalueindex(i));
+ return NUP;
+}
+
+static int test_tolstring (lua_State *L) {
+ size_t len = 0;
+ luaL_tolstring(L, 1, &len);
+ lua_pushinteger(L, (int)len);
+ return 2;
+}
+
+static int test_pushstring (lua_State *L) {
+ lua_pushstring(L, lua_pushliteral(L, "abc"));
+ lua_pushstring(L, lua_pushlstring(L, "abc", 2));
+ lua_pushstring(L, lua_pushlstring(L, NULL, 0));
+ lua_pushstring(L, lua_pushstring(L, "abc"));
+ lua_pushboolean(L, NULL == lua_pushstring(L, NULL));
+ return 10;
+}
+
+static int test_buffer (lua_State *L) {
+ luaL_Buffer b;
+ char *p = luaL_buffinitsize(L, &b, LUAL_BUFFERSIZE+1);
+ p[0] = 'a';
+ p[1] = 'b';
+ luaL_addsize(&b, 2);
+ luaL_addstring(&b, "c");
+ lua_pushliteral(L, "d");
+ luaL_addvalue(&b);
+ luaL_addchar(&b, 'e');
+ luaL_pushresult(&b);
+ return 1;
+}
+
+static int test_exec (lua_State *L) {
+ const char *cmd = luaL_checkstring(L, 1);
+ return luaL_execresult(L, system(cmd));
+}
+
+
+static const luaL_Reg funcs[] = {
+ { "isinteger", test_isinteger },
+ { "rotate", test_rotate },
+ { "strtonum", test_str2num },
+ { "requiref", test_requiref },
+ { "getseti", test_getseti },
+ { "newproxy", test_newproxy },
+ { "arith", test_arith },
+ { "compare", test_compare },
+ { "tonumber", test_tonumber },
+ { "tointeger", test_tointeger },
+ { "len", test_len },
+ { "copy", test_copy },
+ { "rawxetp", test_rawxetp },
+ { "subtable", test_subtable },
+ { "udata", test_udata },
+ { "uservalue", test_uservalue },
+ { "globals", test_globals },
+ { "tolstring", test_tolstring },
+ { "pushstring", test_pushstring },
+ { "buffer", test_buffer },
+ { "exec", test_exec },
+ { NULL, NULL }
+};
+
+static const luaL_Reg more_funcs[] = {
+ { "getupvalues", test_upvalues },
+ { "absindex", test_absindex },
+ { NULL, NULL }
+};
+
+
+int luaopen_testmod (lua_State *L) {
+ int i = 1;
+ luaL_newlib(L, funcs);
+ for (i = 1; i <= NUP; ++i)
+ lua_pushnumber(L, i);
+ luaL_setfuncs(L, more_funcs, NUP);
+ return 1;
+}
+