diff options
author | daurnimator <quae@daurnimator.com> | 2017-08-30 13:55:59 +1000 |
---|---|---|
committer | daurnimator <quae@daurnimator.com> | 2017-08-30 13:55:59 +1000 |
commit | 7333333568b13db56136e2354c55556adc7714ed (patch) | |
tree | 028cec7c885ebbbdd404d9db7aba8510f87681a4 | |
download | luaossl-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-- | .gitignore | 10 | ||||
-rw-r--r-- | LICENSE | 20 | ||||
-rw-r--r-- | README.md | 229 | ||||
-rw-r--r-- | c-api/compat-5.3.c | 617 | ||||
-rw-r--r-- | c-api/compat-5.3.h | 388 | ||||
-rw-r--r-- | compat53/init.lua | 373 | ||||
-rw-r--r-- | compat53/module.lua | 827 | ||||
-rw-r--r-- | lprefix.h | 175 | ||||
-rw-r--r-- | lstrlib.c | 1584 | ||||
-rw-r--r-- | ltablib.c | 450 | ||||
-rw-r--r-- | lutf8lib.c | 256 | ||||
-rw-r--r-- | rockspecs/compat53-0.1-1.rockspec | 31 | ||||
-rw-r--r-- | rockspecs/compat53-0.2-1.rockspec | 32 | ||||
-rw-r--r-- | rockspecs/compat53-0.3-1.rockspec | 32 | ||||
-rw-r--r-- | rockspecs/compat53-0.4-1.rockspec | 32 | ||||
-rw-r--r-- | rockspecs/compat53-0.5-1.rockspec | 32 | ||||
-rw-r--r-- | rockspecs/compat53-scm-0.rockspec | 32 | ||||
-rwxr-xr-x | tests/test.lua | 789 | ||||
-rw-r--r-- | tests/testmod.c | 318 |
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 + @@ -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; +} + |