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 unpack = lua_version == "5.1" and unpack or table.unpack
17
18   -- create module table
19   M = {}
20   local M_meta = {
21      __index = _G,
22      -- __newindex is set at the end
23   }
24   setmetatable(M, M_meta)
25
26   -- create subtables
27   M.io = setmetatable({}, { __index = io })
28   M.math = setmetatable({}, { __index = math })
29   M.string = setmetatable({}, { __index = string })
30   M.table = setmetatable({}, { __index = table })
31   M.utf8 = {}
32
33
34   -- select the most powerful getmetatable function available
35   local gmt = type(debug) == "table" and debug.getmetatable or
36               getmetatable or function() return false end
37
38   -- type checking functions
39   local checkinteger -- forward declararation
40
41   local function argcheck(cond, i, f, extra)
42      if not cond then
43         error("bad argument #"..i.." to '"..f.."' ("..extra..")", 0)
44      end
45   end
46
47
48   -- load utf8 library
49   local utf8_ok, utf8lib = pcall(require, "compat53.utf8")
50   if utf8_ok then
51      if lua_version == "5.1" then
52         utf8lib.charpattern = "[%z\1-\127\194-\244][\128-\191]*"
53      end
54      for k,v in pairs(utf8lib) do
55         M.utf8[k] = v
56      end
57      package.loaded["utf8"] = M.utf8
58   end
59
60
61   -- load table library
62   local table_ok, tablib = pcall(require, "compat53.table")
63   if table_ok then
64      for k,v in pairs(tablib) do
65         M.table[k] = v
66      end
67   end
68
69
70   -- load string packing functions
71   local str_ok, strlib = pcall(require, "compat53.string")
72   if str_ok then
73      for k,v in pairs(strlib) do
74         M.string[k] = v
75      end
76   end
77
78
79   -- try Roberto's struct module for string packing/unpacking if
80   -- compat53.string is unavailable
81   if not str_ok then
82      local struct_ok, struct = pcall(require, "struct")
83      if struct_ok then
84         M.string.pack = struct.pack
85         M.string.packsize = struct.size
86         M.string.unpack = struct.unpack
87      end
88   end
89
90
91   -- update math library
92   do
93      local maxint, minint = 1
94
95      while maxint+1 > maxint and 2*maxint > maxint do
96         maxint = maxint * 2
97      end
98      if 2*maxint <= maxint then
99         maxint = 2*maxint-1
100         minint = -maxint-1
101      else
102         maxint = maxint
103         minint = -maxint
104      end
105      M.math.maxinteger = maxint
106      M.math.mininteger = minint
107
108      function M.math.tointeger(n)
109         if type(n) == "number" and n <= maxint and n >= minint and n % 1 == 0 then
110            return n
111         end
112         return nil
113      end
114
115      function M.math.type(n)
116         if type(n) == "number" then
117            if n <= maxint and n >= minint and n % 1 == 0 then
118               return "integer"
119            else
120               return "float"
121            end
122         else
123            return nil
124         end
125      end
126
127      function checkinteger(x, i, f)
128         local t = type(x)
129         if t ~= "number" then
130            error("bad argument #"..i.." to '"..f..
131                  "' (number expected, got "..t..")", 0)
132         elseif x > maxint or x < minint or x % 1 ~= 0 then
133            error("bad argument #"..i.." to '"..f..
134                  "' (number has no integer representation)", 0)
135         else
136            return x
137         end
138      end
139
140      function M.math.ult(m, n)
141         m = checkinteger(m, "1", "math.ult")
142         n = checkinteger(n, "2", "math.ult")
143         if m >= 0 and n < 0 then
144            return true
145         elseif m < 0 and n >= 0 then
146            return false
147         else
148            return m < n
149         end
150      end
151   end
152
153
154   -- assert should allow non-string error objects
155   function M.assert(cond, ...)
156      if cond then
157         return cond, ...
158      elseif select('#', ...) > 0 then
159         error((...), 0)
160      else
161         error("assertion failed!", 0)
162      end
163   end
164
165
166   -- ipairs should respect __index metamethod
167   do
168      local function ipairs_iterator(st, var)
169         var = var + 1
170         local val = st[var]
171         if val ~= nil then
172            return var, st[var]
173         end
174      end
175      function M.ipairs(t)
176         if gmt(t) ~= nil then -- t has metatable
177            return ipairs_iterator, t, 0
178         else
179            return ipairs(t)
180         end
181      end
182   end
183
184
185   -- make '*' optional for io.read and io.lines
186   do
187      local function addasterisk(fmt)
188         if type(fmt) == "string" and fmt:sub(1, 1) ~= "*" then
189            return "*"..fmt
190         else
191            return fmt
192         end
193      end
194
195      function M.io.read(...)
196         local n = select('#', ...)
197         for i = 1, n do
198            local a = select(i, ...)
199            local b = addasterisk(a)
200            -- as an optimization we only allocate a table for the
201            -- modified format arguments when we have a '*' somewhere.
202            if a ~= b then
203               local args = { ... }
204               args[i] = b
205               for j = i+1, n do
206                  args[j] = addasterisk(args[j])
207               end
208               return io_read(unpack(args, 1, n))
209            end
210         end
211         return io_read(...)
212      end
213
214      -- PUC-Rio Lua 5.1 uses a different implementation for io.lines!
215      function M.io.lines(...)
216         local n = select('#', ...)
217         for i = 2, n do
218            local a = select(i, ...)
219            local b = addasterisk(a)
220            -- as an optimization we only allocate a table for the
221            -- modified format arguments when we have a '*' somewhere.
222            if a ~= b then
223               local args = { ... }
224               args[i] = b
225               for j = i+1, n do
226                  args[j] = addasterisk(args[j])
227               end
228               return io_lines(unpack(args, 1, n))
229            end
230         end
231         return io_lines(...)
232      end
233   end
234
235
236   -- update table library (if C module not available)
237   if not table_ok then
238      local table_concat = table.concat
239      local table_insert = table.insert
240      local table_remove = table.remove
241      local table_sort = table.sort
242
243      function M.table.concat(list, sep, i, j)
244         local mt = gmt(list)
245         if type(mt) == "table" and type(mt.__len) == "function" then
246            local src = list
247            list, i, j  = {}, i or 1, j or mt.__len(src)
248            for k = i, j do
249               list[k] = src[k]
250            end
251         end
252         return table_concat(list, sep, i, j)
253      end
254
255      function M.table.insert(list, ...)
256         local mt = gmt(list)
257         local has_mt = type(mt) == "table"
258         local has_len = has_mt and type(mt.__len) == "function"
259         if has_mt and (has_len or mt.__index or mt.__newindex) then
260            local e = (has_len and mt.__len(list) or #list)+1
261            local nargs, pos, value = select('#', ...), ...
262            if nargs == 1 then
263               pos, value = e, pos
264            elseif nargs == 2 then
265               pos = checkinteger(pos, "2", "table.insert")
266               argcheck(1 <= pos and pos <= e, "2", "table.insert",
267                        "position out of bounds" )
268            else
269               error("wrong number of arguments to 'insert'", 0)
270            end
271            for i = e-1, pos, -1 do
272               list[i+1] = list[i]
273            end
274            list[pos] = value
275         else
276            return table_insert(list, ...)
277         end
278      end
279
280      function M.table.move(a1, f, e, t, a2)
281         a2 = a2 or a1
282         f = checkinteger(f, "2", "table.move")
283         argcheck(f > 0, "2", "table.move",
284                  "initial position must be positive")
285         e = checkinteger(e, "3", "table.move")
286         t = checkinteger(t, "4", "table.move")
287         if e >= f then
288            local m, n, d = 0, e-f, 1
289            if t > f then m, n, d = n, m, -1 end
290            for i = m, n, d do
291               a2[t+i] = a1[f+i]
292            end
293         end
294         return a2
295      end
296
297      function M.table.remove(list, pos)
298         local mt = gmt(list)
299         local has_mt = type(mt) == "table"
300         local has_len = has_mt and type(mt.__len) == "function"
301         if has_mt and (has_len or mt.__index or mt.__newindex) then
302            local e = (has_len and mt.__len(list) or #list)
303            pos = pos ~= nil and checkinteger(pos, "2", "table.remove") or e
304            if pos ~= e then
305               argcheck(1 <= pos and pos <= e+1, "2", "table.remove",
306                        "position out of bounds" )
307            end
308            local result = list[pos]
309            while pos < e do
310               list[pos] = list[pos+1]
311               pos = pos + 1
312            end
313            list[pos] = nil
314            return result
315         else
316            return table_remove(list, pos)
317         end
318      end
319
320      do
321         local function pivot(list, cmp, a, b)
322            local m = b - a
323            if m > 2 then
324               local c = a + (m-m%2)/2
325               local x, y, z = list[a], list[b], list[c]
326               if not cmp(x, y) then
327                  x, y, a, b = y, x, b, a
328               end
329               if not cmp(y, z) then
330                  y, b = z, c
331               end
332               if not cmp(x, y) then
333                  y, b = x, a
334               end
335               return b, y
336            else
337               return b, list[b]
338            end
339         end
340
341         local function lt_cmp(a, b)
342            return a < b
343         end
344
345         local function qsort(list, cmp, b, e)
346            if b < e then
347               local i, j, k, val = b, e, pivot(list, cmp, b, e)
348               while i < j do
349                  while i < j and cmp(list[i], val) do
350                     i = i + 1
351                  end
352                  while i < j and not cmp(list[j], val) do
353                     j = j - 1
354                  end
355                  if i < j then
356                     list[i], list[j] = list[j], list[i]
357                     if i == k then k = j end -- update pivot position
358                     i, j = i+1, j-1
359                  end
360               end
361               if i ~= k and not cmp(list[i], val) then
362                  list[i], list[k] = val, list[i]
363                  k = i -- update pivot position
364               end
365               qsort(list, cmp, b, i == k and i-1 or i)
366               return qsort(list, cmp, i+1, e)
367            end
368         end
369
370         function M.table.sort(list, cmp)
371            local mt = gmt(list)
372            local has_mt = type(mt) == "table"
373            local has_len = has_mt and type(mt.__len) == "function"
374            if has_len then
375               cmp = cmp or lt_cmp
376               local len = mt.__len(list)
377               return qsort(list, cmp, 1, len)
378            else
379               return table_sort(list, cmp)
380            end
381         end
382      end
383
384      local function unpack_helper(list, i, j, ...)
385         if j < i then
386            return ...
387         else
388            return unpack_helper(list, i, j-1, list[j], ...)
389         end
390      end
391      function M.table.unpack(list, i, j)
392         local mt = gmt(list)
393         local has_mt = type(mt) == "table"
394         local has_len = has_mt and type(mt.__len) == "function"
395         if has_mt and (has_len or mt.__index) then
396            i, j = i or 1, j or (has_len and mt.__len(list)) or #list
397            return unpack_helper(list, i, j)
398         else
399            return unpack(list, i, j)
400         end
401      end
402   end -- update table library
403
404
405   -- bring Lua 5.1 (and LuaJIT) up to speed with Lua 5.2
406   if lua_version == "5.1" then
407      -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag)
408      local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ"
409      local is_luajit52 = is_luajit and
410        #setmetatable({}, { __len = function() return 1 end }) == 1
411
412      -- cache globals in upvalues
413      local load, loadfile, loadstring, setfenv, xpcall =
414            load, loadfile, loadstring, setfenv, xpcall
415      local coroutine, os = coroutine, os
416      local coroutine_create = coroutine.create
417      local coroutine_resume = coroutine.resume
418      local coroutine_running = coroutine.running
419      local coroutine_status = coroutine.status
420      local coroutine_yield = coroutine.yield
421      local io_input = io.input
422      local io_open = io.open
423      local io_output = io.output
424      local io_write = io.write
425      local math_log = math.log
426      local os_execute = os.execute
427      local string_find = string.find
428      local string_format = string.format
429      local string_gmatch = string.gmatch
430      local string_gsub = string.gsub
431      local string_match = string.match
432      local string_rep = string.rep
433      local table_concat = table.concat
434
435      -- create subtables
436      M.coroutine = setmetatable({}, { __index = coroutine })
437      M.os = setmetatable({}, { __index = os })
438      M.package = setmetatable({}, { __index = package })
439
440      -- handle debug functions
441      if type(debug) == "table" then
442         local debug_setfenv = debug.setfenv
443         local debug_getfenv = debug.getfenv
444         local debug_setmetatable = debug.setmetatable
445
446         M.debug = setmetatable({}, { __index = debug })
447
448         if not is_luajit52 then
449            function M.debug.setuservalue(obj, value)
450               if type(obj) ~= "userdata" then
451                  error("bad argument #1 to 'setuservalue' (userdata expected, got "..
452                        type(obj)..")", 2)
453               end
454               if value == nil then value = _G end
455               if type(value) ~= "table" then
456                  error("bad argument #2 to 'setuservalue' (table expected, got "..
457                        type(value)..")", 2)
458               end
459               return debug_setfenv(obj, value)
460            end
461
462            function M.debug.getuservalue(obj)
463               if type(obj) ~= "userdata" then
464                  return nil
465               else
466                  local v = debug_getfenv(obj)
467                  if v == _G or v == package then
468                     return nil
469                  end
470                  return v
471               end
472            end
473
474            function M.debug.setmetatable(value, tab)
475               debug_setmetatable(value, tab)
476               return value
477            end
478         end -- not luajit with compat52 enabled
479      end -- debug table available
480
481
482      if not is_luajit52 then
483         function M.pairs(t)
484            local mt = gmt(t)
485            if type(mt) == "table" and type(mt.__pairs) == "function" then
486               return mt.__pairs(t)
487            else
488               return pairs(t)
489            end
490         end
491      end
492
493
494      if not is_luajit then
495         local function check_mode(mode, prefix)
496            local has = { text = false, binary = false }
497            for i = 1,#mode do
498               local c = mode:sub(i, i)
499               if c == "t" then has.text = true end
500               if c == "b" then has.binary = true end
501            end
502            local t = prefix:sub(1, 1) == "\27" and "binary" or "text"
503            if not has[t] then
504               return "attempt to load a "..t.." chunk (mode is '"..mode.."')"
505            end
506         end
507
508         function M.load(ld, source, mode, env)
509            mode = mode or "bt"
510            local chunk, msg
511            if type( ld ) == "string" then
512               if mode ~= "bt" then
513                  local merr = check_mode(mode, ld)
514                  if merr then return nil, merr end
515               end
516               chunk, msg = loadstring(ld, source)
517            else
518               local ld_type = type(ld)
519               if ld_type ~= "function" then
520                  error("bad argument #1 to 'load' (function expected, got "..
521                        ld_type..")", 2)
522               end
523               if mode ~= "bt" then
524                  local checked, merr = false, nil
525                  local function checked_ld()
526                     if checked then
527                        return ld()
528                     else
529                        checked = true
530                        local v = ld()
531                        merr = check_mode(mode, v or "")
532                        if merr then return nil end
533                        return v
534                     end
535                  end
536                  chunk, msg = load(checked_ld, source)
537                  if merr then return nil, merr end
538               else
539                  chunk, msg = load(ld, source)
540               end
541            end
542            if not chunk then
543               return chunk, msg
544            end
545            if env ~= nil then
546               setfenv(chunk, env)
547            end
548            return chunk
549         end
550
551         M.loadstring = M.load
552
553         function M.loadfile(file, mode, env)
554            mode = mode or "bt"
555            if mode ~= "bt" then
556               local f = io_open(file, "rb")
557               if f then
558                  local prefix = f:read(1)
559                  f:close()
560                  if prefix then
561                     local merr = check_mode(mode, prefix)
562                     if merr then return nil, merr end
563                  end
564               end
565            end
566            local chunk, msg = loadfile(file)
567            if not chunk then
568               return chunk, msg
569            end
570            if env ~= nil then
571               setfenv(chunk, env)
572            end
573            return chunk
574         end
575      end -- not luajit
576
577
578      if not is_luajit52 then
579         function M.rawlen(v)
580            local t = type(v)
581            if t ~= "string" and t ~= "table" then
582               error("bad argument #1 to 'rawlen' (table or string expected)", 2)
583            end
584            return #v
585         end
586      end
587
588
589      if not is_luajit then
590         function M.xpcall(f, msgh, ...)
591            local args, n = { ... }, select('#', ...)
592            return xpcall(function() return f(unpack(args, 1, n)) end, msgh)
593         end
594      end
595
596
597      if not is_luajit52 then
598         function M.os.execute(cmd)
599            local code = os_execute(cmd)
600            -- Lua 5.1 does not report exit by signal.
601            if code == 0 then
602               return true, "exit", code
603            else
604               if package.config:sub(1, 1) == '/' then
605                  code = code/256 -- only correct on Linux!
606               end
607               return nil, "exit", code
608            end
609         end
610      end
611
612
613      if not table_ok and not is_luajit52 then
614         M.table.pack = function(...)
615            return { n = select('#', ...), ... }
616         end
617      end
618
619
620      local main_coroutine = coroutine_create(function() end)
621
622      function M.coroutine.create(func)
623         local success, result = pcall(coroutine_create, func)
624         if not success then
625            if type(func) ~= "function" then
626               error("bad argument #1 (function expected)", 0)
627             end
628            result = coroutine_create(function(...) return func(...) end)
629         end
630         return result
631      end
632
633      if not is_luajit52 then
634         function M.coroutine.running()
635            local co = coroutine_running()
636            if co then
637               return co, false
638            else
639               return main_coroutine, true
640            end
641         end
642      end
643
644      function M.coroutine.yield(...)
645         local co, flag = coroutine_running()
646         if co and not flag then
647            return coroutine_yield(...)
648         else
649            error("attempt to yield from outside a coroutine", 0)
650         end
651      end
652
653      if not is_luajit then
654         function M.coroutine.resume(co, ...)
655            if co == main_coroutine then
656               return false, "cannot resume non-suspended coroutine"
657            else
658               return coroutine_resume(co, ...)
659            end
660         end
661
662         function M.coroutine.status(co)
663            local notmain = coroutine_running()
664            if co == main_coroutine then
665               return notmain and "normal" or "running"
666            else
667               return coroutine_status(co)
668            end
669         end
670      end -- not luajit
671
672
673      if not is_luajit then
674         M.math.log = function(x, base)
675            if base ~= nil then
676               return math_log(x)/math_log(base)
677            else
678               return math_log(x)
679            end
680         end
681      end
682
683
684      if not is_luajit then
685         function M.package.searchpath(name, path, sep, rep)
686            sep = (sep or "."):gsub("(%p)", "%%%1")
687            rep = (rep or package.config:sub(1, 1)):gsub("(%%)", "%%%1")
688            local pname = name:gsub(sep, rep):gsub("(%%)", "%%%1")
689            local msg = {}
690            for subpath in path:gmatch("[^;]+") do
691               local fpath = subpath:gsub("%?", pname)
692               local f = io_open(fpath, "r")
693               if f then
694                  f:close()
695                  return fpath
696               end
697               msg[#msg+1] = "\n\tno file '" .. fpath .. "'"
698            end
699            return nil, table_concat(msg)
700         end
701      end
702
703
704      local function fix_pattern(pattern)
705         return (string_gsub(pattern, "%z", "%%z"))
706      end
707
708      function M.string.find(s, pattern, ...)
709         return string_find(s, fix_pattern(pattern), ...)
710      end
711
712      function M.string.gmatch(s, pattern)
713         return string_gmatch(s, fix_pattern(pattern))
714      end
715
716      function M.string.gsub(s, pattern, ...)
717         return string_gsub(s, fix_pattern(pattern), ...)
718      end
719
720      function M.string.match(s, pattern, ...)
721         return string_match(s, fix_pattern(pattern), ...)
722      end
723
724      if not is_luajit then
725         function M.string.rep(s, n, sep)
726            if sep ~= nil and sep ~= "" and n >= 2 then
727               return s .. string_rep(sep..s, n-1)
728            else
729               return string_rep(s, n)
730            end
731         end
732      end
733
734      if not is_luajit then
735         do
736            local addqt = {
737               ["\n"] = "\\\n",
738               ["\\"] = "\\\\",
739               ["\""] = "\\\""
740            }
741
742            local function addquoted(c, d)
743               return (addqt[c] or string_format(d~="" and "\\%03d" or "\\%d", c:byte()))..d
744            end
745
746            function M.string.format(fmt, ...)
747               local args, n = { ... }, select('#', ...)
748               local i = 0
749               local function adjust_fmt(lead, mods, kind)
750                  if #lead % 2 == 0 then
751                     i = i + 1
752                     if kind == "s" then
753                        args[i] = _G.tostring(args[i])
754                     elseif kind == "q" then
755                        args[i] = '"'..string_gsub(args[i], "([%z%c\\\"\n])(%d?)", addquoted)..'"'
756                        return lead.."%"..mods.."s"
757                     end
758                  end
759               end
760               fmt = string_gsub(fmt, "(%%*)%%([%d%.%-%+%# ]*)(%a)", adjust_fmt)
761               return string_format(fmt, unpack(args, 1, n))
762            end
763         end
764      end
765
766
767      function M.io.write(...)
768         local res, msg, errno = io_write(...)
769         if res then
770            return io_output()
771         else
772            return nil, msg, errno
773         end
774      end
775
776      if not is_luajit then
777         local function helper(st, var_1, ...)
778            if var_1 == nil then
779               if st.doclose then st.f:close() end
780               if (...) ~= nil then
781                  error((...), 2)
782               end
783            end
784            return var_1, ...
785         end
786
787         local function lines_iterator(st)
788            return helper(st, st.f:read(unpack(st, 1, st.n)))
789         end
790
791         function M.io.lines(fname, ...)
792            local doclose, file, msg
793            if fname ~= nil then
794               doclose, file, msg = true, io_open(fname, "r")
795               if not file then error(msg, 2) end
796            else
797               doclose, file = false, io_input()
798            end
799            local st = { f=file, doclose=doclose, n=select('#', ...), ... }
800            for i = 1, st.n do
801               local t = type(st[i])
802               if t == "string" then
803                  local fmt = st[i]:match("^%*?([aln])")
804                  if not fmt then
805                     error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
806                  end
807                  st[i] = "*"..fmt
808               elseif t ~= "number" then
809                  error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
810               end
811            end
812            return lines_iterator, st
813         end
814      end -- not luajit
815
816   end -- lua 5.1
817
818   -- further write should be forwarded to _G
819   M_meta.__newindex = _G
820
821end -- lua < 5.3
822
823
824-- return module table
825return M
826
827-- vi: set expandtab softtabstop=3 shiftwidth=3 :
828