diff options
| author | tobil4sk <tobil4sk@outlook.com> | 2026-02-03 22:47:50 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-03 19:47:50 -0300 |
| commit | 47301d83aba58925e1b9594023621ebb27070cdb (patch) | |
| tree | 73021b5366687ec1683b9e66505e74f22f71d31b /vendor/compat53 | |
| parent | acf1f47e7f1b1ecbc147e41cae51ddfd06ad898d (diff) | |
| download | luarocks-main.tar.gz luarocks-main.tar.bz2 luarocks-main.zip | |
Improve flexibility around vendored librariesmain
compat53 is vendored since #1757 as it is required to run luarocks with lua 5.1 or 5.2.
However, this introduced some issues as the GNUmakefile install rule places these in the same place where `luarocks install compat53` would install them. This means you get conflicts if you install the actual package:
```
Warning: /.../prefix/share/lua/5.1/compat53/init.lua is not tracked by this installation of LuaRocks. Moving it to /.../prefix/share/lua/5.1/compat53/init.lua~
Warning: /.../prefix/share/lua/5.1/compat53/module.lua is not tracked by this installation of LuaRocks. Moving it to /.../prefix/share/lua/5.1/compat53/module.lua~
Warning: /.../prefix/share/lua/5.1/compat53/file_mt.lua is not tracked by this installation of LuaRocks. Moving it to /.../prefix/share/lua/5.1/compat53/file_mt.lua~
```
It is also not ideal for linux package maintainers to include a vendored package, see: https://github.com/luarocks/luarocks/pull/1757#issuecomment-3409873412.
To solve these issues, this patchset makes the following changes:
- GNUmakefile now places the compat53 files under `luarocks/vendor/compat53` (which is added internally to the luarocks script's `package.path`). This way a user's installation of compat53 does not interfere at all with luarocks one.
- Added `--with-system-compat53` option to configure script for external packaging systems.
- Fixed install.bat's logic for deciding whether to vendor compat53, as the current script includes it for every version.
install.bat already places luarocks sources outside of LUAPATH, so that part can stay as is.
I've also inverted the version check to avoid the need for future patches like: #1850.
Diffstat (limited to 'vendor/compat53')
| -rw-r--r-- | vendor/compat53/file_mt.lua | 71 | ||||
| -rw-r--r-- | vendor/compat53/init.lua | 325 | ||||
| -rw-r--r-- | vendor/compat53/module.lua | 894 |
3 files changed, 1290 insertions, 0 deletions
diff --git a/vendor/compat53/file_mt.lua b/vendor/compat53/file_mt.lua new file mode 100644 index 00000000..6433619d --- /dev/null +++ b/vendor/compat53/file_mt.lua | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | local lua_version = _VERSION:sub(-3) | ||
| 2 | |||
| 3 | local M = {} | ||
| 4 | |||
| 5 | local unpack = lua_version == "5.1" and unpack or table.unpack | ||
| 6 | |||
| 7 | local function addasterisk(fmt) | ||
| 8 | if type(fmt) == "string" and fmt:sub(1, 1) ~= "*" then | ||
| 9 | return "*"..fmt | ||
| 10 | else | ||
| 11 | return fmt | ||
| 12 | end | ||
| 13 | end | ||
| 14 | |||
| 15 | function M.update_file_meta(file_meta, is_luajit52) | ||
| 16 | |||
| 17 | -- make '*' optional for file:read and file:lines | ||
| 18 | |||
| 19 | local file_lines = file_meta.__index.lines | ||
| 20 | file_meta.__index.lines = function(self, ...) | ||
| 21 | local n = select('#', ...) | ||
| 22 | for i = 1, n do | ||
| 23 | local a = select(i, ...) | ||
| 24 | local b = addasterisk(a) | ||
| 25 | -- as an optimization we only allocate a table for the | ||
| 26 | -- modified format arguments when we have a '*' somewhere | ||
| 27 | if a ~= b then | ||
| 28 | local args = { ... } | ||
| 29 | args[i] = b | ||
| 30 | for j = i+1, n do | ||
| 31 | args[j] = addasterisk(args[j]) | ||
| 32 | end | ||
| 33 | return file_lines(self, unpack(args, 1, n)) | ||
| 34 | end | ||
| 35 | end | ||
| 36 | return file_lines(self, ...) | ||
| 37 | end | ||
| 38 | |||
| 39 | local file_read = file_meta.__index.read | ||
| 40 | file_meta.__index.read = function(self, ...) | ||
| 41 | local n = select('#', ...) | ||
| 42 | for i = 1, n do | ||
| 43 | local a = select(i, ...) | ||
| 44 | local b = addasterisk(a) | ||
| 45 | -- as an optimization we only allocate a table for the | ||
| 46 | -- modified format arguments when we have a '*' somewhere | ||
| 47 | if a ~= b then | ||
| 48 | local args = { ... } | ||
| 49 | args[i] = b | ||
| 50 | for j = i+1, n do | ||
| 51 | args[j] = addasterisk(args[j]) | ||
| 52 | end | ||
| 53 | return file_read(self, unpack(args, 1, n)) | ||
| 54 | end | ||
| 55 | end | ||
| 56 | return file_read(self, ...) | ||
| 57 | end | ||
| 58 | |||
| 59 | if not is_luajit52 then | ||
| 60 | local file_write = file_meta.__index.write | ||
| 61 | file_meta.__index.write = function(self, ...) | ||
| 62 | local ret, err = file_write(self, ...) | ||
| 63 | if ret then | ||
| 64 | return self | ||
| 65 | end | ||
| 66 | return ret, err | ||
| 67 | end | ||
| 68 | end | ||
| 69 | end | ||
| 70 | |||
| 71 | return M | ||
diff --git a/vendor/compat53/init.lua b/vendor/compat53/init.lua new file mode 100644 index 00000000..b5075713 --- /dev/null +++ b/vendor/compat53/init.lua | |||
| @@ -0,0 +1,325 @@ | |||
| 1 | local lua_version = _VERSION:sub(-3) | ||
| 2 | |||
| 3 | |||
| 4 | if lua_version < "5.3" then | ||
| 5 | |||
| 6 | local _G, pairs, require, select, type = | ||
| 7 | _G, pairs, require, select, type | ||
| 8 | local debug, io = debug, io | ||
| 9 | local unpack = lua_version == "5.1" and unpack or table.unpack | ||
| 10 | |||
| 11 | local M = require("compat53.module") | ||
| 12 | |||
| 13 | -- select the most powerful getmetatable function available | ||
| 14 | local gmt = type(debug) == "table" and debug.getmetatable or | ||
| 15 | getmetatable or function() return false end | ||
| 16 | -- metatable for file objects from Lua's standard io library | ||
| 17 | local file_meta = gmt(io.stdout) | ||
| 18 | |||
| 19 | |||
| 20 | -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag) | ||
| 21 | local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ" | ||
| 22 | local is_luajit52 = is_luajit and | ||
| 23 | #setmetatable({}, { __len = function() return 1 end }) == 1 | ||
| 24 | |||
| 25 | |||
| 26 | if type(file_meta) == "table" and type(file_meta.__index) == "table" then | ||
| 27 | local file_mt = require("compat53.file_mt") | ||
| 28 | file_mt.update_file_meta(file_meta, is_luajit52) | ||
| 29 | end -- got a valid metatable for file objects | ||
| 30 | |||
| 31 | |||
| 32 | -- changes for Lua 5.1 only | ||
| 33 | if lua_version == "5.1" then | ||
| 34 | |||
| 35 | -- cache globals | ||
| 36 | local error, pcall, rawset, setmetatable, tostring, xpcall = | ||
| 37 | error, pcall, rawset, setmetatable, tostring, xpcall | ||
| 38 | local coroutine, package, string = coroutine, package, string | ||
| 39 | local coroutine_resume = coroutine.resume | ||
| 40 | local coroutine_running = coroutine.running | ||
| 41 | local coroutine_status = coroutine.status | ||
| 42 | local coroutine_yield = coroutine.yield | ||
| 43 | local io_type = io.type | ||
| 44 | |||
| 45 | |||
| 46 | -- make package.searchers available as an alias for package.loaders | ||
| 47 | local p_index = { searchers = package.loaders } | ||
| 48 | setmetatable(package, { | ||
| 49 | __index = p_index, | ||
| 50 | __newindex = function(p, k, v) | ||
| 51 | if k == "searchers" then | ||
| 52 | rawset(p, "loaders", v) | ||
| 53 | p_index.searchers = v | ||
| 54 | else | ||
| 55 | rawset(p, k, v) | ||
| 56 | end | ||
| 57 | end | ||
| 58 | }) | ||
| 59 | |||
| 60 | |||
| 61 | if type(file_meta) == "table" and type(file_meta.__index) == "table" then | ||
| 62 | if not is_luajit then | ||
| 63 | local function helper(_, var_1, ...) | ||
| 64 | if var_1 == nil then | ||
| 65 | if (...) ~= nil then | ||
| 66 | error((...), 2) | ||
| 67 | end | ||
| 68 | end | ||
| 69 | return var_1, ... | ||
| 70 | end | ||
| 71 | |||
| 72 | local function lines_iterator(st) | ||
| 73 | return helper(st, st.f:read(unpack(st, 1, st.n))) | ||
| 74 | end | ||
| 75 | |||
| 76 | local file_write = file_meta.__index.write | ||
| 77 | file_meta.__index.write = function(self, ...) | ||
| 78 | local res, msg, errno = file_write(self, ...) | ||
| 79 | if res then | ||
| 80 | return self | ||
| 81 | else | ||
| 82 | return nil, msg, errno | ||
| 83 | end | ||
| 84 | end | ||
| 85 | |||
| 86 | file_meta.__index.lines = function(self, ...) | ||
| 87 | if io_type(self) == "closed file" then | ||
| 88 | error("attempt to use a closed file", 2) | ||
| 89 | end | ||
| 90 | local st = { f=self, n=select('#', ...), ... } | ||
| 91 | for i = 1, st.n do | ||
| 92 | local t = type(st[i]) | ||
| 93 | if t == "string" then | ||
| 94 | local fmt = st[i]:match("^*?([aln])") | ||
| 95 | if not fmt then | ||
| 96 | error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2) | ||
| 97 | end | ||
| 98 | st[i] = "*"..fmt | ||
| 99 | elseif t ~= "number" then | ||
| 100 | error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2) | ||
| 101 | end | ||
| 102 | end | ||
| 103 | return lines_iterator, st | ||
| 104 | end | ||
| 105 | end -- not luajit | ||
| 106 | end -- file_meta valid | ||
| 107 | |||
| 108 | |||
| 109 | -- the (x)pcall implementations start a new coroutine internally | ||
| 110 | -- to allow yielding even in Lua 5.1. to allow for accurate | ||
| 111 | -- stack traces we keep track of the nested coroutine activations | ||
| 112 | -- in the weak tables below: | ||
| 113 | local weak_meta = { __mode = "kv" } | ||
| 114 | -- maps the internal pcall coroutines to the user coroutine that | ||
| 115 | -- *should* be running if pcall didn't use coroutines internally | ||
| 116 | local pcall_mainOf = setmetatable({}, weak_meta) | ||
| 117 | -- table that maps each running coroutine started by pcall to | ||
| 118 | -- the coroutine that resumed it (user coroutine *or* pcall | ||
| 119 | -- coroutine!) | ||
| 120 | local pcall_previous = setmetatable({}, weak_meta) | ||
| 121 | -- reverse of `pcall_mainOf`. maps a user coroutine to the | ||
| 122 | -- currently active pcall coroutine started within it | ||
| 123 | local pcall_callOf = setmetatable({}, weak_meta) | ||
| 124 | -- similar to `pcall_mainOf` but is used only while executing | ||
| 125 | -- the error handler of xpcall (thus no nesting is necessary!) | ||
| 126 | local xpcall_running = setmetatable({}, weak_meta) | ||
| 127 | |||
| 128 | -- handle debug functions | ||
| 129 | if type(debug) == "table" then | ||
| 130 | local debug_getinfo = debug.getinfo | ||
| 131 | local debug_traceback = debug.traceback | ||
| 132 | |||
| 133 | if not is_luajit then | ||
| 134 | local function calculate_trace_level(co, level) | ||
| 135 | if level ~= nil then | ||
| 136 | for out = 1, 1/0 do | ||
| 137 | local info = (co==nil) and debug_getinfo(out, "") or debug_getinfo(co, out, "") | ||
| 138 | if info == nil then | ||
| 139 | local max = out-1 | ||
| 140 | if level <= max then | ||
| 141 | return level | ||
| 142 | end | ||
| 143 | return nil, level-max | ||
| 144 | end | ||
| 145 | end | ||
| 146 | end | ||
| 147 | return 1 | ||
| 148 | end | ||
| 149 | |||
| 150 | local stack_pattern = "\nstack traceback:" | ||
| 151 | local stack_replace = "" | ||
| 152 | function debug.traceback(co, msg, level) | ||
| 153 | local lvl | ||
| 154 | local nilmsg | ||
| 155 | if type(co) ~= "thread" then | ||
| 156 | co, msg, level = coroutine_running(), co, msg | ||
| 157 | end | ||
| 158 | if msg == nil then | ||
| 159 | msg = "" | ||
| 160 | nilmsg = true | ||
| 161 | elseif type(msg) ~= "string" then | ||
| 162 | return msg | ||
| 163 | end | ||
| 164 | if co == nil then | ||
| 165 | msg = debug_traceback(msg, level or 1) | ||
| 166 | else | ||
| 167 | local xpco = xpcall_running[co] | ||
| 168 | if xpco ~= nil then | ||
| 169 | lvl, level = calculate_trace_level(xpco, level) | ||
| 170 | if lvl then | ||
| 171 | msg = debug_traceback(xpco, msg, lvl) | ||
| 172 | else | ||
| 173 | msg = msg..stack_pattern | ||
| 174 | end | ||
| 175 | lvl, level = calculate_trace_level(co, level) | ||
| 176 | if lvl then | ||
| 177 | local trace = debug_traceback(co, "", lvl) | ||
| 178 | msg = msg..trace:gsub(stack_pattern, stack_replace) | ||
| 179 | end | ||
| 180 | else | ||
| 181 | co = pcall_callOf[co] or co | ||
| 182 | lvl, level = calculate_trace_level(co, level) | ||
| 183 | if lvl then | ||
| 184 | msg = debug_traceback(co, msg, lvl) | ||
| 185 | else | ||
| 186 | msg = msg..stack_pattern | ||
| 187 | end | ||
| 188 | end | ||
| 189 | co = pcall_previous[co] | ||
| 190 | while co ~= nil do | ||
| 191 | lvl, level = calculate_trace_level(co, level) | ||
| 192 | if lvl then | ||
| 193 | local trace = debug_traceback(co, "", lvl) | ||
| 194 | msg = msg..trace:gsub(stack_pattern, stack_replace) | ||
| 195 | end | ||
| 196 | co = pcall_previous[co] | ||
| 197 | end | ||
| 198 | end | ||
| 199 | if nilmsg then | ||
| 200 | msg = msg:gsub("^\n", "") | ||
| 201 | end | ||
| 202 | msg = msg:gsub("\n\t%(tail call%): %?", "\000") | ||
| 203 | msg = msg:gsub("\n\t%.%.%.\n", "\001\n") | ||
| 204 | msg = msg:gsub("\n\t%.%.%.$", "\001") | ||
| 205 | msg = msg:gsub("(%z+)\001(%z+)", function(some, other) | ||
| 206 | return "\n\t(..."..#some+#other.."+ tail call(s)...)" | ||
| 207 | end) | ||
| 208 | msg = msg:gsub("\001(%z+)", function(zeros) | ||
| 209 | return "\n\t(..."..#zeros.."+ tail call(s)...)" | ||
| 210 | end) | ||
| 211 | msg = msg:gsub("(%z+)\001", function(zeros) | ||
| 212 | return "\n\t(..."..#zeros.."+ tail call(s)...)" | ||
| 213 | end) | ||
| 214 | msg = msg:gsub("%z+", function(zeros) | ||
| 215 | return "\n\t(..."..#zeros.." tail call(s)...)" | ||
| 216 | end) | ||
| 217 | msg = msg:gsub("\001", function() | ||
| 218 | return "\n\t..." | ||
| 219 | end) | ||
| 220 | return msg | ||
| 221 | end | ||
| 222 | end -- is not luajit | ||
| 223 | end -- debug table available | ||
| 224 | |||
| 225 | |||
| 226 | if not is_luajit52 then | ||
| 227 | local coroutine_running52 = M.coroutine.running | ||
| 228 | function M.coroutine.running() | ||
| 229 | local co, ismain = coroutine_running52() | ||
| 230 | if ismain then | ||
| 231 | return co, true | ||
| 232 | else | ||
| 233 | return pcall_mainOf[co] or co, false | ||
| 234 | end | ||
| 235 | end | ||
| 236 | end | ||
| 237 | |||
| 238 | if not is_luajit then | ||
| 239 | local function pcall_results(current, call, success, ...) | ||
| 240 | if coroutine_status(call) == "suspended" then | ||
| 241 | return pcall_results(current, call, coroutine_resume(call, coroutine_yield(...))) | ||
| 242 | end | ||
| 243 | if pcall_previous then | ||
| 244 | pcall_previous[call] = nil | ||
| 245 | local main = pcall_mainOf[call] | ||
| 246 | if main == current then current = nil end | ||
| 247 | pcall_callOf[main] = current | ||
| 248 | end | ||
| 249 | pcall_mainOf[call] = nil | ||
| 250 | return success, ... | ||
| 251 | end | ||
| 252 | |||
| 253 | local function pcall_exec(current, call, ...) | ||
| 254 | local main = pcall_mainOf[current] or current | ||
| 255 | pcall_mainOf[call] = main | ||
| 256 | if pcall_previous then | ||
| 257 | pcall_previous[call] = current | ||
| 258 | pcall_callOf[main] = call | ||
| 259 | end | ||
| 260 | return pcall_results(current, call, coroutine_resume(call, ...)) | ||
| 261 | end | ||
| 262 | |||
| 263 | local coroutine_create52 = M.coroutine.create | ||
| 264 | |||
| 265 | local function pcall_coroutine(func) | ||
| 266 | if type(func) ~= "function" then | ||
| 267 | local callable = func | ||
| 268 | func = function (...) return callable(...) end | ||
| 269 | end | ||
| 270 | return coroutine_create52(func) | ||
| 271 | end | ||
| 272 | |||
| 273 | function M.pcall(func, ...) | ||
| 274 | local current = coroutine_running() | ||
| 275 | if not current then return pcall(func, ...) end | ||
| 276 | return pcall_exec(current, pcall_coroutine(func), ...) | ||
| 277 | end | ||
| 278 | |||
| 279 | local function xpcall_catch(current, call, msgh, success, ...) | ||
| 280 | if not success then | ||
| 281 | xpcall_running[current] = call | ||
| 282 | local ok, result = pcall(msgh, ...) | ||
| 283 | xpcall_running[current] = nil | ||
| 284 | if not ok then | ||
| 285 | return false, "error in error handling ("..tostring(result)..")" | ||
| 286 | end | ||
| 287 | return false, result | ||
| 288 | end | ||
| 289 | return true, ... | ||
| 290 | end | ||
| 291 | |||
| 292 | function M.xpcall(f, msgh, ...) | ||
| 293 | local current = coroutine_running() | ||
| 294 | if not current then | ||
| 295 | local args, n = { ... }, select('#', ...) | ||
| 296 | return xpcall(function() return f(unpack(args, 1, n)) end, msgh) | ||
| 297 | end | ||
| 298 | local call = pcall_coroutine(f) | ||
| 299 | return xpcall_catch(current, call, msgh, pcall_exec(current, call, ...)) | ||
| 300 | end | ||
| 301 | end -- not luajit | ||
| 302 | |||
| 303 | end -- lua 5.1 | ||
| 304 | |||
| 305 | |||
| 306 | -- handle exporting to global scope | ||
| 307 | local function extend_table(from, to) | ||
| 308 | if from ~= to then | ||
| 309 | for k,v in pairs(from) do | ||
| 310 | if type(v) == "table" and | ||
| 311 | type(to[k]) == "table" and | ||
| 312 | v ~= to[k] then | ||
| 313 | extend_table(v, to[k]) | ||
| 314 | else | ||
| 315 | to[k] = v | ||
| 316 | end | ||
| 317 | end | ||
| 318 | end | ||
| 319 | end | ||
| 320 | |||
| 321 | extend_table(M, _G) | ||
| 322 | |||
| 323 | end -- lua < 5.3 | ||
| 324 | |||
| 325 | -- vi: set expandtab softtabstop=3 shiftwidth=3 : | ||
diff --git a/vendor/compat53/module.lua b/vendor/compat53/module.lua new file mode 100644 index 00000000..b580e536 --- /dev/null +++ b/vendor/compat53/module.lua | |||
| @@ -0,0 +1,894 @@ | |||
| 1 | local _G, _VERSION = _G, _VERSION | ||
| 2 | local lua_version = _VERSION:sub(-3) | ||
| 3 | |||
| 4 | |||
| 5 | local M = _G | ||
| 6 | |||
| 7 | if lua_version < "5.3" then | ||
| 8 | |||
| 9 | -- cache globals in upvalues | ||
| 10 | local error, ipairs, pairs, pcall, require, select, setmetatable, type = | ||
| 11 | error, ipairs, pairs, pcall, require, select, setmetatable, type | ||
| 12 | local debug, io, math, package, string, table = | ||
| 13 | debug, io, math, package, string, table | ||
| 14 | local io_lines = io.lines | ||
| 15 | local io_read = io.read | ||
| 16 | local io_open = io.open | ||
| 17 | local io_popen = io.popen | ||
| 18 | local io_tmpfile = io.tmpfile | ||
| 19 | local unpack = lua_version == "5.1" and unpack or table.unpack | ||
| 20 | local debug_setmetatable = type(debug) == "table" and debug.setmetatable | ||
| 21 | |||
| 22 | -- create module table | ||
| 23 | M = {} | ||
| 24 | local M_meta = { | ||
| 25 | __index = _G, | ||
| 26 | -- __newindex is set at the end | ||
| 27 | } | ||
| 28 | setmetatable(M, M_meta) | ||
| 29 | |||
| 30 | -- create subtables | ||
| 31 | M.io = setmetatable({}, { __index = io }) | ||
| 32 | M.math = setmetatable({}, { __index = math }) | ||
| 33 | M.string = setmetatable({}, { __index = string }) | ||
| 34 | M.table = setmetatable({}, { __index = table }) | ||
| 35 | M.utf8 = {} | ||
| 36 | |||
| 37 | |||
| 38 | -- select the most powerful getmetatable function available | ||
| 39 | local gmt = type(debug) == "table" and debug.getmetatable or | ||
| 40 | getmetatable or function() return false end | ||
| 41 | |||
| 42 | -- type checking functions | ||
| 43 | local checkinteger -- forward declararation | ||
| 44 | |||
| 45 | local function argcheck(cond, i, f, extra) | ||
| 46 | if not cond then | ||
| 47 | error("bad argument #"..i.." to '"..f.."' ("..extra..")", 0) | ||
| 48 | end | ||
| 49 | end | ||
| 50 | |||
| 51 | |||
| 52 | -- load utf8 library | ||
| 53 | local utf8_ok, utf8lib = pcall(require, "compat53.utf8") | ||
| 54 | if utf8_ok then | ||
| 55 | if lua_version == "5.1" then | ||
| 56 | utf8lib.charpattern = "[%z\1-\127\194-\244][\128-\191]*" | ||
| 57 | end | ||
| 58 | for k,v in pairs(utf8lib) do | ||
| 59 | M.utf8[k] = v | ||
| 60 | end | ||
| 61 | package.loaded["utf8"] = M.utf8 | ||
| 62 | end | ||
| 63 | |||
| 64 | |||
| 65 | -- load table library | ||
| 66 | local table_ok, tablib = pcall(require, "compat53.table") | ||
| 67 | if table_ok then | ||
| 68 | for k,v in pairs(tablib) do | ||
| 69 | M.table[k] = v | ||
| 70 | end | ||
| 71 | end | ||
| 72 | |||
| 73 | |||
| 74 | -- load io functions | ||
| 75 | local io_ok, iolib = pcall(require, "compat53.io") | ||
| 76 | if io_ok then | ||
| 77 | for k,v in pairs(iolib) do | ||
| 78 | M.io[k] = v | ||
| 79 | end | ||
| 80 | end | ||
| 81 | |||
| 82 | |||
| 83 | -- load string packing functions | ||
| 84 | local str_ok, strlib = pcall(require, "compat53.string") | ||
| 85 | if str_ok then | ||
| 86 | for k,v in pairs(strlib) do | ||
| 87 | M.string[k] = v | ||
| 88 | end | ||
| 89 | end | ||
| 90 | |||
| 91 | |||
| 92 | -- try Roberto's struct module for string packing/unpacking if | ||
| 93 | -- compat53.string is unavailable | ||
| 94 | if not str_ok then | ||
| 95 | local struct_ok, struct = pcall(require, "struct") | ||
| 96 | if struct_ok then | ||
| 97 | M.string.pack = struct.pack | ||
| 98 | M.string.packsize = struct.size | ||
| 99 | M.string.unpack = struct.unpack | ||
| 100 | end | ||
| 101 | end | ||
| 102 | |||
| 103 | |||
| 104 | -- update math library | ||
| 105 | do | ||
| 106 | local maxint, minint = 1 | ||
| 107 | |||
| 108 | while maxint+1 > maxint and 2*maxint > maxint do | ||
| 109 | maxint = maxint * 2 | ||
| 110 | end | ||
| 111 | if 2*maxint <= maxint then | ||
| 112 | maxint = 2*maxint-1 | ||
| 113 | minint = -maxint-1 | ||
| 114 | else | ||
| 115 | maxint = maxint | ||
| 116 | minint = -maxint | ||
| 117 | end | ||
| 118 | M.math.maxinteger = maxint | ||
| 119 | M.math.mininteger = minint | ||
| 120 | |||
| 121 | function M.math.tointeger(n) | ||
| 122 | n = tonumber(n) | ||
| 123 | if type(n) == "number" and n <= maxint and n >= minint and n % 1 == 0 then | ||
| 124 | return n | ||
| 125 | end | ||
| 126 | return nil | ||
| 127 | end | ||
| 128 | |||
| 129 | function M.math.type(n) | ||
| 130 | if type(n) == "number" then | ||
| 131 | if n <= maxint and n >= minint and n % 1 == 0 then | ||
| 132 | return "integer" | ||
| 133 | else | ||
| 134 | return "float" | ||
| 135 | end | ||
| 136 | else | ||
| 137 | return nil | ||
| 138 | end | ||
| 139 | end | ||
| 140 | |||
| 141 | function checkinteger(x, i, f) | ||
| 142 | local t = type(x) | ||
| 143 | if t ~= "number" then | ||
| 144 | error("bad argument #"..i.." to '"..f.. | ||
| 145 | "' (number expected, got "..t..")", 0) | ||
| 146 | elseif x > maxint or x < minint or x % 1 ~= 0 then | ||
| 147 | error("bad argument #"..i.." to '"..f.. | ||
| 148 | "' (number has no integer representation)", 0) | ||
| 149 | else | ||
| 150 | return x | ||
| 151 | end | ||
| 152 | end | ||
| 153 | |||
| 154 | function M.math.ult(m, n) | ||
| 155 | m = checkinteger(m, "1", "math.ult") | ||
| 156 | n = checkinteger(n, "2", "math.ult") | ||
| 157 | if m >= 0 and n < 0 then | ||
| 158 | return true | ||
| 159 | elseif m < 0 and n >= 0 then | ||
| 160 | return false | ||
| 161 | else | ||
| 162 | return m < n | ||
| 163 | end | ||
| 164 | end | ||
| 165 | end | ||
| 166 | |||
| 167 | |||
| 168 | -- assert should allow non-string error objects | ||
| 169 | function M.assert(cond, ...) | ||
| 170 | if cond then | ||
| 171 | return cond, ... | ||
| 172 | elseif select('#', ...) > 0 then | ||
| 173 | error((...), 2) | ||
| 174 | else | ||
| 175 | error("assertion failed!", 2) | ||
| 176 | end | ||
| 177 | end | ||
| 178 | |||
| 179 | |||
| 180 | -- ipairs should respect __index metamethod | ||
| 181 | do | ||
| 182 | local function ipairs_iterator(st, var) | ||
| 183 | var = var + 1 | ||
| 184 | local val = st[var] | ||
| 185 | if val ~= nil then | ||
| 186 | return var, st[var] | ||
| 187 | end | ||
| 188 | end | ||
| 189 | function M.ipairs(t) | ||
| 190 | if gmt(t) ~= nil then -- t has metatable | ||
| 191 | return ipairs_iterator, t, 0 | ||
| 192 | else | ||
| 193 | return ipairs(t) | ||
| 194 | end | ||
| 195 | end | ||
| 196 | end | ||
| 197 | |||
| 198 | |||
| 199 | -- make '*' optional for io.read and io.lines | ||
| 200 | do | ||
| 201 | local function addasterisk(fmt) | ||
| 202 | if type(fmt) == "string" and fmt:sub(1, 1) ~= "*" then | ||
| 203 | return "*"..fmt | ||
| 204 | else | ||
| 205 | return fmt | ||
| 206 | end | ||
| 207 | end | ||
| 208 | |||
| 209 | function M.io.read(...) | ||
| 210 | local n = select('#', ...) | ||
| 211 | for i = 1, n do | ||
| 212 | local a = select(i, ...) | ||
| 213 | local b = addasterisk(a) | ||
| 214 | -- as an optimization we only allocate a table for the | ||
| 215 | -- modified format arguments when we have a '*' somewhere. | ||
| 216 | if a ~= b then | ||
| 217 | local args = { ... } | ||
| 218 | args[i] = b | ||
| 219 | for j = i+1, n do | ||
| 220 | args[j] = addasterisk(args[j]) | ||
| 221 | end | ||
| 222 | return io_read(unpack(args, 1, n)) | ||
| 223 | end | ||
| 224 | end | ||
| 225 | return io_read(...) | ||
| 226 | end | ||
| 227 | |||
| 228 | -- PUC-Rio Lua 5.1 uses a different implementation for io.lines! | ||
| 229 | function M.io.lines(...) | ||
| 230 | local n = select('#', ...) | ||
| 231 | for i = 2, n do | ||
| 232 | local a = select(i, ...) | ||
| 233 | local b = addasterisk(a) | ||
| 234 | -- as an optimization we only allocate a table for the | ||
| 235 | -- modified format arguments when we have a '*' somewhere. | ||
| 236 | if a ~= b then | ||
| 237 | local args = { ... } | ||
| 238 | args[i] = b | ||
| 239 | for j = i+1, n do | ||
| 240 | args[j] = addasterisk(args[j]) | ||
| 241 | end | ||
| 242 | return io_lines(unpack(args, 1, n)) | ||
| 243 | end | ||
| 244 | end | ||
| 245 | return io_lines(...) | ||
| 246 | end | ||
| 247 | end | ||
| 248 | |||
| 249 | |||
| 250 | -- update table library (if C module not available) | ||
| 251 | if not table_ok then | ||
| 252 | local table_concat = table.concat | ||
| 253 | local table_insert = table.insert | ||
| 254 | local table_remove = table.remove | ||
| 255 | local table_sort = table.sort | ||
| 256 | |||
| 257 | function M.table.concat(list, sep, i, j) | ||
| 258 | local mt = gmt(list) | ||
| 259 | if type(mt) == "table" and type(mt.__len) == "function" then | ||
| 260 | local src = list | ||
| 261 | list, i, j = {}, i or 1, j or mt.__len(src) | ||
| 262 | for k = i, j do | ||
| 263 | list[k] = src[k] | ||
| 264 | end | ||
| 265 | end | ||
| 266 | return table_concat(list, sep, i, j) | ||
| 267 | end | ||
| 268 | |||
| 269 | function M.table.insert(list, ...) | ||
| 270 | local mt = gmt(list) | ||
| 271 | local has_mt = type(mt) == "table" | ||
| 272 | local has_len = has_mt and type(mt.__len) == "function" | ||
| 273 | if has_mt and (has_len or mt.__index or mt.__newindex) then | ||
| 274 | local e = (has_len and mt.__len(list) or #list)+1 | ||
| 275 | local nargs, pos, value = select('#', ...), ... | ||
| 276 | if nargs == 1 then | ||
| 277 | pos, value = e, pos | ||
| 278 | elseif nargs == 2 then | ||
| 279 | pos = checkinteger(pos, "2", "table.insert") | ||
| 280 | argcheck(1 <= pos and pos <= e, "2", "table.insert", | ||
| 281 | "position out of bounds" ) | ||
| 282 | else | ||
| 283 | error("wrong number of arguments to 'insert'", 0) | ||
| 284 | end | ||
| 285 | for i = e-1, pos, -1 do | ||
| 286 | list[i+1] = list[i] | ||
| 287 | end | ||
| 288 | list[pos] = value | ||
| 289 | else | ||
| 290 | return table_insert(list, ...) | ||
| 291 | end | ||
| 292 | end | ||
| 293 | |||
| 294 | function M.table.move(a1, f, e, t, a2) | ||
| 295 | a2 = a2 or a1 | ||
| 296 | f = checkinteger(f, "2", "table.move") | ||
| 297 | argcheck(f > 0, "2", "table.move", | ||
| 298 | "initial position must be positive") | ||
| 299 | e = checkinteger(e, "3", "table.move") | ||
| 300 | t = checkinteger(t, "4", "table.move") | ||
| 301 | if e >= f then | ||
| 302 | local m, n, d = 0, e-f, 1 | ||
| 303 | if t > f then m, n, d = n, m, -1 end | ||
| 304 | for i = m, n, d do | ||
| 305 | a2[t+i] = a1[f+i] | ||
| 306 | end | ||
| 307 | end | ||
| 308 | return a2 | ||
| 309 | end | ||
| 310 | |||
| 311 | function M.table.remove(list, pos) | ||
| 312 | local mt = gmt(list) | ||
| 313 | local has_mt = type(mt) == "table" | ||
| 314 | local has_len = has_mt and type(mt.__len) == "function" | ||
| 315 | if has_mt and (has_len or mt.__index or mt.__newindex) then | ||
| 316 | local e = (has_len and mt.__len(list) or #list) | ||
| 317 | pos = pos ~= nil and checkinteger(pos, "2", "table.remove") or e | ||
| 318 | if pos ~= e then | ||
| 319 | argcheck(1 <= pos and pos <= e+1, "2", "table.remove", | ||
| 320 | "position out of bounds" ) | ||
| 321 | end | ||
| 322 | local result = list[pos] | ||
| 323 | while pos < e do | ||
| 324 | list[pos] = list[pos+1] | ||
| 325 | pos = pos + 1 | ||
| 326 | end | ||
| 327 | list[pos] = nil | ||
| 328 | return result | ||
| 329 | else | ||
| 330 | return table_remove(list, pos) | ||
| 331 | end | ||
| 332 | end | ||
| 333 | |||
| 334 | do | ||
| 335 | local function pivot(list, cmp, a, b) | ||
| 336 | local m = b - a | ||
| 337 | if m > 2 then | ||
| 338 | local c = a + (m-m%2)/2 | ||
| 339 | local x, y, z = list[a], list[b], list[c] | ||
| 340 | if not cmp(x, y) then | ||
| 341 | x, y, a, b = y, x, b, a | ||
| 342 | end | ||
| 343 | if not cmp(y, z) then | ||
| 344 | y, b = z, c | ||
| 345 | end | ||
| 346 | if not cmp(x, y) then | ||
| 347 | y, b = x, a | ||
| 348 | end | ||
| 349 | return b, y | ||
| 350 | else | ||
| 351 | return b, list[b] | ||
| 352 | end | ||
| 353 | end | ||
| 354 | |||
| 355 | local function lt_cmp(a, b) | ||
| 356 | return a < b | ||
| 357 | end | ||
| 358 | |||
| 359 | local function qsort(list, cmp, b, e) | ||
| 360 | if b < e then | ||
| 361 | local i, j, k, val = b, e, pivot(list, cmp, b, e) | ||
| 362 | while i < j do | ||
| 363 | while i < j and cmp(list[i], val) do | ||
| 364 | i = i + 1 | ||
| 365 | end | ||
| 366 | while i < j and not cmp(list[j], val) do | ||
| 367 | j = j - 1 | ||
| 368 | end | ||
| 369 | if i < j then | ||
| 370 | list[i], list[j] = list[j], list[i] | ||
| 371 | if i == k then k = j end -- update pivot position | ||
| 372 | i, j = i+1, j-1 | ||
| 373 | end | ||
| 374 | end | ||
| 375 | if i ~= k and not cmp(list[i], val) then | ||
| 376 | list[i], list[k] = val, list[i] | ||
| 377 | k = i -- update pivot position | ||
| 378 | end | ||
| 379 | qsort(list, cmp, b, i == k and i-1 or i) | ||
| 380 | return qsort(list, cmp, i+1, e) | ||
| 381 | end | ||
| 382 | end | ||
| 383 | |||
| 384 | function M.table.sort(list, cmp) | ||
| 385 | local mt = gmt(list) | ||
| 386 | local has_mt = type(mt) == "table" | ||
| 387 | local has_len = has_mt and type(mt.__len) == "function" | ||
| 388 | if has_len then | ||
| 389 | cmp = cmp or lt_cmp | ||
| 390 | local len = mt.__len(list) | ||
| 391 | return qsort(list, cmp, 1, len) | ||
| 392 | else | ||
| 393 | return table_sort(list, cmp) | ||
| 394 | end | ||
| 395 | end | ||
| 396 | end | ||
| 397 | |||
| 398 | local function unpack_helper(list, i, j, ...) | ||
| 399 | if j < i then | ||
| 400 | return ... | ||
| 401 | else | ||
| 402 | return unpack_helper(list, i, j-1, list[j], ...) | ||
| 403 | end | ||
| 404 | end | ||
| 405 | function M.table.unpack(list, i, j) | ||
| 406 | local mt = gmt(list) | ||
| 407 | local has_mt = type(mt) == "table" | ||
| 408 | local has_len = has_mt and type(mt.__len) == "function" | ||
| 409 | if has_mt and (has_len or mt.__index) then | ||
| 410 | i, j = i or 1, j or (has_len and mt.__len(list)) or #list | ||
| 411 | return unpack_helper(list, i, j) | ||
| 412 | else | ||
| 413 | return unpack(list, i, j) | ||
| 414 | end | ||
| 415 | end | ||
| 416 | end -- update table library | ||
| 417 | |||
| 418 | |||
| 419 | -- bring Lua 5.1 (and LuaJIT) up to speed with Lua 5.2 | ||
| 420 | if lua_version == "5.1" then | ||
| 421 | -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag) | ||
| 422 | local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ" | ||
| 423 | local is_luajit52 = is_luajit and | ||
| 424 | #setmetatable({}, { __len = function() return 1 end }) == 1 | ||
| 425 | |||
| 426 | -- cache globals in upvalues | ||
| 427 | local load, loadfile, loadstring, setfenv, xpcall = | ||
| 428 | load, loadfile, loadstring, setfenv, xpcall | ||
| 429 | local coroutine, os = coroutine, os | ||
| 430 | local coroutine_create = coroutine.create | ||
| 431 | local coroutine_resume = coroutine.resume | ||
| 432 | local coroutine_running = coroutine.running | ||
| 433 | local coroutine_status = coroutine.status | ||
| 434 | local coroutine_yield = coroutine.yield | ||
| 435 | local io_input = io.input | ||
| 436 | local io_open = io.open | ||
| 437 | local io_output = io.output | ||
| 438 | local io_write = io.write | ||
| 439 | local math_log = math.log | ||
| 440 | local os_execute = os.execute | ||
| 441 | local string_find = string.find | ||
| 442 | local string_format = string.format | ||
| 443 | local string_gmatch = string.gmatch | ||
| 444 | local string_gsub = string.gsub | ||
| 445 | local string_match = string.match | ||
| 446 | local string_rep = string.rep | ||
| 447 | local table_concat = table.concat | ||
| 448 | |||
| 449 | -- create subtables | ||
| 450 | M.coroutine = setmetatable({}, { __index = coroutine }) | ||
| 451 | M.os = setmetatable({}, { __index = os }) | ||
| 452 | M.package = setmetatable({}, { __index = package }) | ||
| 453 | |||
| 454 | -- handle debug functions | ||
| 455 | if type(debug) == "table" then | ||
| 456 | local debug_setfenv = debug.setfenv | ||
| 457 | local debug_getfenv = debug.getfenv | ||
| 458 | |||
| 459 | M.debug = setmetatable({}, { __index = debug }) | ||
| 460 | |||
| 461 | if not is_luajit52 then | ||
| 462 | function M.debug.setuservalue(obj, value) | ||
| 463 | if type(obj) ~= "userdata" then | ||
| 464 | error("bad argument #1 to 'setuservalue' (userdata expected, got ".. | ||
| 465 | type(obj)..")", 2) | ||
| 466 | end | ||
| 467 | if value == nil then value = _G end | ||
| 468 | if type(value) ~= "table" then | ||
| 469 | error("bad argument #2 to 'setuservalue' (table expected, got ".. | ||
| 470 | type(value)..")", 2) | ||
| 471 | end | ||
| 472 | return debug_setfenv(obj, value) | ||
| 473 | end | ||
| 474 | |||
| 475 | function M.debug.getuservalue(obj) | ||
| 476 | if type(obj) ~= "userdata" then | ||
| 477 | return nil | ||
| 478 | else | ||
| 479 | local v = debug_getfenv(obj) | ||
| 480 | if v == _G or v == package then | ||
| 481 | return nil | ||
| 482 | end | ||
| 483 | return v | ||
| 484 | end | ||
| 485 | end | ||
| 486 | |||
| 487 | function M.debug.setmetatable(value, tab) | ||
| 488 | debug_setmetatable(value, tab) | ||
| 489 | return value | ||
| 490 | end | ||
| 491 | end -- not luajit with compat52 enabled | ||
| 492 | end -- debug table available | ||
| 493 | |||
| 494 | |||
| 495 | if not is_luajit52 then | ||
| 496 | function M.pairs(t) | ||
| 497 | local mt = gmt(t) | ||
| 498 | if type(mt) == "table" and type(mt.__pairs) == "function" then | ||
| 499 | return mt.__pairs(t) | ||
| 500 | else | ||
| 501 | return pairs(t) | ||
| 502 | end | ||
| 503 | end | ||
| 504 | end | ||
| 505 | |||
| 506 | |||
| 507 | if not is_luajit then | ||
| 508 | local function check_mode(mode, prefix) | ||
| 509 | local has = { text = false, binary = false } | ||
| 510 | for i = 1,#mode do | ||
| 511 | local c = mode:sub(i, i) | ||
| 512 | if c == "t" then has.text = true end | ||
| 513 | if c == "b" then has.binary = true end | ||
| 514 | end | ||
| 515 | local t = prefix:sub(1, 1) == "\27" and "binary" or "text" | ||
| 516 | if not has[t] then | ||
| 517 | return "attempt to load a "..t.." chunk (mode is '"..mode.."')" | ||
| 518 | end | ||
| 519 | end | ||
| 520 | |||
| 521 | function M.load(ld, source, mode, env) | ||
| 522 | mode = mode or "bt" | ||
| 523 | local chunk, msg | ||
| 524 | if type( ld ) == "string" then | ||
| 525 | if mode ~= "bt" then | ||
| 526 | local merr = check_mode(mode, ld) | ||
| 527 | if merr then return nil, merr end | ||
| 528 | end | ||
| 529 | chunk, msg = loadstring(ld, source) | ||
| 530 | else | ||
| 531 | local ld_type = type(ld) | ||
| 532 | if ld_type ~= "function" then | ||
| 533 | error("bad argument #1 to 'load' (function expected, got ".. | ||
| 534 | ld_type..")", 2) | ||
| 535 | end | ||
| 536 | if mode ~= "bt" then | ||
| 537 | local checked, merr = false, nil | ||
| 538 | local function checked_ld() | ||
| 539 | if checked then | ||
| 540 | return ld() | ||
| 541 | else | ||
| 542 | checked = true | ||
| 543 | local v = ld() | ||
| 544 | merr = check_mode(mode, v or "") | ||
| 545 | if merr then return nil end | ||
| 546 | return v | ||
| 547 | end | ||
| 548 | end | ||
| 549 | chunk, msg = load(checked_ld, source) | ||
| 550 | if merr then return nil, merr end | ||
| 551 | else | ||
| 552 | chunk, msg = load(ld, source) | ||
| 553 | end | ||
| 554 | end | ||
| 555 | if not chunk then | ||
| 556 | return chunk, msg | ||
| 557 | end | ||
| 558 | if env ~= nil then | ||
| 559 | setfenv(chunk, env) | ||
| 560 | end | ||
| 561 | return chunk | ||
| 562 | end | ||
| 563 | |||
| 564 | M.loadstring = M.load | ||
| 565 | |||
| 566 | function M.loadfile(file, mode, env) | ||
| 567 | mode = mode or "bt" | ||
| 568 | if mode ~= "bt" then | ||
| 569 | local f = io_open(file, "rb") | ||
| 570 | if f then | ||
| 571 | local prefix = f:read(1) | ||
| 572 | f:close() | ||
| 573 | if prefix then | ||
| 574 | local merr = check_mode(mode, prefix) | ||
| 575 | if merr then return nil, merr end | ||
| 576 | end | ||
| 577 | end | ||
| 578 | end | ||
| 579 | local chunk, msg = loadfile(file) | ||
| 580 | if not chunk then | ||
| 581 | return chunk, msg | ||
| 582 | end | ||
| 583 | if env ~= nil then | ||
| 584 | setfenv(chunk, env) | ||
| 585 | end | ||
| 586 | return chunk | ||
| 587 | end | ||
| 588 | end -- not luajit | ||
| 589 | |||
| 590 | |||
| 591 | if not is_luajit52 then | ||
| 592 | function M.rawlen(v) | ||
| 593 | local t = type(v) | ||
| 594 | if t ~= "string" and t ~= "table" then | ||
| 595 | error("bad argument #1 to 'rawlen' (table or string expected)", 2) | ||
| 596 | end | ||
| 597 | return #v | ||
| 598 | end | ||
| 599 | end | ||
| 600 | |||
| 601 | |||
| 602 | if not is_luajit then | ||
| 603 | function M.xpcall(f, msgh, ...) | ||
| 604 | local args, n = { ... }, select('#', ...) | ||
| 605 | return xpcall(function() return f(unpack(args, 1, n)) end, msgh) | ||
| 606 | end | ||
| 607 | end | ||
| 608 | |||
| 609 | |||
| 610 | if not is_luajit52 then | ||
| 611 | function M.os.execute(cmd) | ||
| 612 | local code = os_execute(cmd) | ||
| 613 | -- Lua 5.1 does not report exit by signal. | ||
| 614 | if code == 0 then | ||
| 615 | return true, "exit", code | ||
| 616 | else | ||
| 617 | if package.config:sub(1, 1) == '/' then | ||
| 618 | code = code/256 -- only correct on Linux! | ||
| 619 | end | ||
| 620 | return nil, "exit", code | ||
| 621 | end | ||
| 622 | end | ||
| 623 | end | ||
| 624 | |||
| 625 | |||
| 626 | if not table_ok and not is_luajit52 then | ||
| 627 | M.table.pack = function(...) | ||
| 628 | return { n = select('#', ...), ... } | ||
| 629 | end | ||
| 630 | end | ||
| 631 | |||
| 632 | |||
| 633 | local main_coroutine = coroutine_create(function() end) | ||
| 634 | |||
| 635 | function M.coroutine.create(func) | ||
| 636 | local success, result = pcall(coroutine_create, func) | ||
| 637 | if not success then | ||
| 638 | if type(func) ~= "function" then | ||
| 639 | error("bad argument #1 (function expected)", 0) | ||
| 640 | end | ||
| 641 | result = coroutine_create(function(...) return func(...) end) | ||
| 642 | end | ||
| 643 | return result | ||
| 644 | end | ||
| 645 | |||
| 646 | if not is_luajit52 then | ||
| 647 | function M.coroutine.running() | ||
| 648 | local co = coroutine_running() | ||
| 649 | if co then | ||
| 650 | return co, false | ||
| 651 | else | ||
| 652 | return main_coroutine, true | ||
| 653 | end | ||
| 654 | end | ||
| 655 | end | ||
| 656 | |||
| 657 | function M.coroutine.yield(...) | ||
| 658 | local co, flag = coroutine_running() | ||
| 659 | if co and not flag then | ||
| 660 | return coroutine_yield(...) | ||
| 661 | else | ||
| 662 | error("attempt to yield from outside a coroutine", 0) | ||
| 663 | end | ||
| 664 | end | ||
| 665 | |||
| 666 | if not is_luajit then | ||
| 667 | function M.coroutine.resume(co, ...) | ||
| 668 | if co == main_coroutine then | ||
| 669 | return false, "cannot resume non-suspended coroutine" | ||
| 670 | else | ||
| 671 | return coroutine_resume(co, ...) | ||
| 672 | end | ||
| 673 | end | ||
| 674 | |||
| 675 | function M.coroutine.status(co) | ||
| 676 | local notmain = coroutine_running() | ||
| 677 | if co == main_coroutine then | ||
| 678 | return notmain and "normal" or "running" | ||
| 679 | else | ||
| 680 | return coroutine_status(co) | ||
| 681 | end | ||
| 682 | end | ||
| 683 | end -- not luajit | ||
| 684 | |||
| 685 | |||
| 686 | if not is_luajit then | ||
| 687 | M.math.log = function(x, base) | ||
| 688 | if base ~= nil then | ||
| 689 | return math_log(x)/math_log(base) | ||
| 690 | else | ||
| 691 | return math_log(x) | ||
| 692 | end | ||
| 693 | end | ||
| 694 | end | ||
| 695 | |||
| 696 | |||
| 697 | if not is_luajit then | ||
| 698 | function M.package.searchpath(name, path, sep, rep) | ||
| 699 | sep = (sep or "."):gsub("(%p)", "%%%1") | ||
| 700 | rep = (rep or package.config:sub(1, 1)):gsub("(%%)", "%%%1") | ||
| 701 | local pname = name:gsub(sep, rep):gsub("(%%)", "%%%1") | ||
| 702 | local msg = {} | ||
| 703 | for subpath in path:gmatch("[^;]+") do | ||
| 704 | local fpath = subpath:gsub("%?", pname) | ||
| 705 | local f = io_open(fpath, "r") | ||
| 706 | if f then | ||
| 707 | f:close() | ||
| 708 | return fpath | ||
| 709 | end | ||
| 710 | msg[#msg+1] = "\n\tno file '" .. fpath .. "'" | ||
| 711 | end | ||
| 712 | return nil, table_concat(msg) | ||
| 713 | end | ||
| 714 | end | ||
| 715 | |||
| 716 | |||
| 717 | local function fix_pattern(pattern) | ||
| 718 | return (string_gsub(pattern, "%z", "%%z")) | ||
| 719 | end | ||
| 720 | |||
| 721 | function M.string.find(s, pattern, ...) | ||
| 722 | return string_find(s, fix_pattern(pattern), ...) | ||
| 723 | end | ||
| 724 | |||
| 725 | function M.string.gmatch(s, pattern) | ||
| 726 | return string_gmatch(s, fix_pattern(pattern)) | ||
| 727 | end | ||
| 728 | |||
| 729 | function M.string.gsub(s, pattern, ...) | ||
| 730 | return string_gsub(s, fix_pattern(pattern), ...) | ||
| 731 | end | ||
| 732 | |||
| 733 | function M.string.match(s, pattern, ...) | ||
| 734 | return string_match(s, fix_pattern(pattern), ...) | ||
| 735 | end | ||
| 736 | |||
| 737 | if not is_luajit then | ||
| 738 | function M.string.rep(s, n, sep) | ||
| 739 | if sep ~= nil and sep ~= "" and n >= 2 then | ||
| 740 | return s .. string_rep(sep..s, n-1) | ||
| 741 | else | ||
| 742 | return string_rep(s, n) | ||
| 743 | end | ||
| 744 | end | ||
| 745 | end | ||
| 746 | |||
| 747 | if not is_luajit then | ||
| 748 | do | ||
| 749 | local addqt = { | ||
| 750 | ["\n"] = "\\\n", | ||
| 751 | ["\\"] = "\\\\", | ||
| 752 | ["\""] = "\\\"" | ||
| 753 | } | ||
| 754 | |||
| 755 | local function addquoted(c, d) | ||
| 756 | return (addqt[c] or string_format(d~="" and "\\%03d" or "\\%d", c:byte()))..d | ||
| 757 | end | ||
| 758 | |||
| 759 | function M.string.format(fmt, ...) | ||
| 760 | local args, n = { ... }, select('#', ...) | ||
| 761 | local i = 0 | ||
| 762 | local function adjust_fmt(lead, mods, kind) | ||
| 763 | if #lead % 2 == 0 then | ||
| 764 | i = i + 1 | ||
| 765 | if kind == "s" then | ||
| 766 | args[i] = _G.tostring(args[i]) | ||
| 767 | elseif kind == "q" then | ||
| 768 | args[i] = '"'..string_gsub(args[i], "([%z%c\\\"\n])(%d?)", addquoted)..'"' | ||
| 769 | return lead.."%"..mods.."s" | ||
| 770 | end | ||
| 771 | end | ||
| 772 | end | ||
| 773 | fmt = string_gsub(fmt, "(%%*)%%([%d%.%-%+%# ]*)(%a)", adjust_fmt) | ||
| 774 | return string_format(fmt, unpack(args, 1, n)) | ||
| 775 | end | ||
| 776 | end | ||
| 777 | end | ||
| 778 | |||
| 779 | |||
| 780 | function M.io.write(...) | ||
| 781 | local res, msg, errno = io_write(...) | ||
| 782 | if res then | ||
| 783 | return io_output() | ||
| 784 | else | ||
| 785 | return nil, msg, errno | ||
| 786 | end | ||
| 787 | end | ||
| 788 | |||
| 789 | if not is_luajit then | ||
| 790 | local function helper(st, var_1, ...) | ||
| 791 | if var_1 == nil then | ||
| 792 | if st.doclose then st.f:close() end | ||
| 793 | if (...) ~= nil then | ||
| 794 | error((...), 2) | ||
| 795 | end | ||
| 796 | end | ||
| 797 | return var_1, ... | ||
| 798 | end | ||
| 799 | |||
| 800 | local function lines_iterator(st) | ||
| 801 | return helper(st, st.f:read(unpack(st, 1, st.n))) | ||
| 802 | end | ||
| 803 | |||
| 804 | function M.io.lines(fname, ...) | ||
| 805 | local doclose, file, msg | ||
| 806 | if fname ~= nil then | ||
| 807 | doclose, file, msg = true, io_open(fname, "r") | ||
| 808 | if not file then error(msg, 2) end | ||
| 809 | else | ||
| 810 | doclose, file = false, io_input() | ||
| 811 | end | ||
| 812 | local st = { f=file, doclose=doclose, n=select('#', ...), ... } | ||
| 813 | for i = 1, st.n do | ||
| 814 | local t = type(st[i]) | ||
| 815 | if t == "string" then | ||
| 816 | local fmt = st[i]:match("^%*?([aln])") | ||
| 817 | if not fmt then | ||
| 818 | error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2) | ||
| 819 | end | ||
| 820 | st[i] = "*"..fmt | ||
| 821 | elseif t ~= "number" then | ||
| 822 | error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2) | ||
| 823 | end | ||
| 824 | end | ||
| 825 | return lines_iterator, st | ||
| 826 | end | ||
| 827 | end -- not luajit | ||
| 828 | |||
| 829 | if is_luajit then | ||
| 830 | local compat_file_meta = {} | ||
| 831 | local compat_file_meta_loaded = 0 | ||
| 832 | |||
| 833 | local function load_compat_file_meta(file_meta) | ||
| 834 | -- fill compat_file_meta with original entries | ||
| 835 | for k, v in pairs(file_meta) do | ||
| 836 | compat_file_meta[k] = v | ||
| 837 | end | ||
| 838 | compat_file_meta.__index = {} | ||
| 839 | for k, v in pairs(file_meta.__index) do | ||
| 840 | compat_file_meta.__index[k] = v | ||
| 841 | end | ||
| 842 | |||
| 843 | compat_file_meta_loaded = 1 | ||
| 844 | |||
| 845 | -- update it with compatibility functions | ||
| 846 | local file_mt_ok, file_mt = pcall(require, "compat53.file_mt") | ||
| 847 | if file_mt_ok then | ||
| 848 | file_mt.update_file_meta(compat_file_meta, is_luajit52) | ||
| 849 | |||
| 850 | compat_file_meta_loaded = 2 | ||
| 851 | end | ||
| 852 | end | ||
| 853 | |||
| 854 | local function return_fd(fd, err, code) | ||
| 855 | if not fd then | ||
| 856 | return fd, err, code | ||
| 857 | end | ||
| 858 | if fd and debug_setmetatable then | ||
| 859 | if compat_file_meta_loaded == 0 then | ||
| 860 | local file_meta = gmt(fd) | ||
| 861 | load_compat_file_meta(file_meta) | ||
| 862 | end | ||
| 863 | if compat_file_meta_loaded == 2 then | ||
| 864 | debug_setmetatable(fd, compat_file_meta) | ||
| 865 | end | ||
| 866 | end | ||
| 867 | return fd | ||
| 868 | end | ||
| 869 | |||
| 870 | function M.io.open(...) | ||
| 871 | return return_fd(io_open(...)) | ||
| 872 | end | ||
| 873 | |||
| 874 | function M.io.popen(...) | ||
| 875 | return return_fd(io_popen(...)) | ||
| 876 | end | ||
| 877 | |||
| 878 | function M.io.tmpfile(...) | ||
| 879 | return return_fd(io_tmpfile(...)) | ||
| 880 | end | ||
| 881 | end | ||
| 882 | |||
| 883 | end -- lua 5.1 | ||
| 884 | |||
| 885 | -- further write should be forwarded to _G | ||
| 886 | M_meta.__newindex = _G | ||
| 887 | |||
| 888 | end -- lua < 5.3 | ||
| 889 | |||
| 890 | |||
| 891 | -- return module table | ||
| 892 | return M | ||
| 893 | |||
| 894 | -- vi: set expandtab softtabstop=3 shiftwidth=3 : | ||
