aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTobiasz Laskowski <tobil4sk@outlook.com>2025-02-24 21:20:56 +0000
committerHisham Muhammad <hisham@gobolinux.org>2025-02-24 20:11:55 -0300
commit71b7dd641892dd302cf37c270c472248b24ef8a4 (patch)
treebe59dcd6d92e0e3f8b354a17a6781ba03a295614 /src
parentc71174f1c20c2b57265a5cedfdccda559445e55c (diff)
downloadluarocks-71b7dd641892dd302cf37c270c472248b24ef8a4.tar.gz
luarocks-71b7dd641892dd302cf37c270c472248b24ef8a4.tar.bz2
luarocks-71b7dd641892dd302cf37c270c472248b24ef8a4.zip
Add vendored lua compat53 v0.14.4
Diffstat (limited to 'src')
-rw-r--r--src/compat53/file_mt.lua71
-rw-r--r--src/compat53/init.lua325
-rw-r--r--src/compat53/module.lua894
3 files changed, 1290 insertions, 0 deletions
diff --git a/src/compat53/file_mt.lua b/src/compat53/file_mt.lua
new file mode 100644
index 00000000..6433619d
--- /dev/null
+++ b/src/compat53/file_mt.lua
@@ -0,0 +1,71 @@
1local lua_version = _VERSION:sub(-3)
2
3local M = {}
4
5local unpack = lua_version == "5.1" and unpack or table.unpack
6
7local function addasterisk(fmt)
8 if type(fmt) == "string" and fmt:sub(1, 1) ~= "*" then
9 return "*"..fmt
10 else
11 return fmt
12 end
13end
14
15function 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
69end
70
71return M
diff --git a/src/compat53/init.lua b/src/compat53/init.lua
new file mode 100644
index 00000000..b5075713
--- /dev/null
+++ b/src/compat53/init.lua
@@ -0,0 +1,325 @@
1local lua_version = _VERSION:sub(-3)
2
3
4if 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
323end -- lua < 5.3
324
325-- vi: set expandtab softtabstop=3 shiftwidth=3 :
diff --git a/src/compat53/module.lua b/src/compat53/module.lua
new file mode 100644
index 00000000..52b1dd63
--- /dev/null
+++ b/src/compat53/module.lua
@@ -0,0 +1,894 @@
1local _G, _VERSION = _G, _VERSION
2local lua_version = _VERSION:sub(-3)
3
4
5local M = _G
6
7if 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((...), 0)
174 else
175 error("assertion failed!", 0)
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
888end -- lua < 5.3
889
890
891-- return module table
892return M
893
894-- vi: set expandtab softtabstop=3 shiftwidth=3 :