• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lunarmodules / Penlight / 21340975460

25 Jan 2026 10:51PM UTC coverage: 90.15%. Remained the same
21340975460

push

github

web-flow
Merge 04b8b46aa into 678de0ebb

5537 of 6142 relevant lines covered (90.15%)

727.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.17
/lua/pl/compat.lua
1
----------------
2
--- Lua 5.1/5.2/5.3 compatibility.
3
-- Injects `table.pack`, `table.unpack`, and `package.searchpath` in the global
4
-- environment, to make sure they are available for Lua 5.1 and LuaJIT.
5
--
6
-- All other functions are exported as usual in the returned module table.
7
--
8
-- NOTE: everything in this module is also available in `pl.utils`.
9
-- @module pl.compat
10
local compat = {}
748✔
11

12
--- boolean flag this is Lua 5.1 (or LuaJIT).
13
-- @field lua51
14
compat.lua51 = _VERSION == 'Lua 5.1'
748✔
15

16
--- boolean flag this is LuaJIT.
17
-- @field jit
18
compat.jit = (tostring(assert):match('builtin') ~= nil)
748✔
19

20
--- boolean flag this is LuaJIT with 5.2 compatibility compiled in.
21
-- @field jit52
22
if compat.jit then
748✔
23
    -- 'goto' is a keyword when 52 compatibility is enabled in LuaJit
24
    compat.jit52 = not loadstring("local goto = 1")
220✔
25
end
26

27
--- the directory separator character for the current platform.
28
-- @field dir_separator
29
compat.dir_separator = _G.package.config:sub(1,1)
968✔
30

31
--- boolean flag this is a Windows platform.
32
-- @field is_windows
33
compat.is_windows = compat.dir_separator == '\\'
748✔
34

35
--- execute a shell command, in a compatible and platform independent way.
36
-- This is a compatibility function that returns the same for Lua 5.1 and
37
-- Lua 5.2+.
38
--
39
-- NOTE: Windows systems can use signed 32bit integer exitcodes. Posix systems
40
-- only use exitcodes 0-255, anything else is undefined.
41
--
42
-- NOTE2: In Lua 5.2 and 5.3 a Windows exitcode of -1 would not properly be
43
-- returned, this function will return it properly for all versions.
44
-- @param cmd a shell command
45
-- @return true if successful
46
-- @return actual return code
47
function compat.execute(cmd)
748✔
48
    local res1,res2,res3 = os.execute(cmd)
692✔
49
    if res2 == "No error" and res3 == 0 and compat.is_windows then
692✔
50
      -- os.execute bug in Lua 5.2/5.3 not reporting -1 properly on Windows
51
      -- this was fixed in 5.4
52
      res3 = -1
8✔
53
    end
54
    if compat.lua51 and not compat.jit52 then
692✔
55
        if compat.is_windows then
320✔
56
            return res1==0,res1
86✔
57
        else
58
            res1 = res1 > 255 and res1 / 256 or res1
234✔
59
            return res1==0,res1
234✔
60
        end
61
    else
62
        if compat.is_windows then
372✔
63
            return res3==0,res3
138✔
64
        else
65
            return not not res1,res3
234✔
66
        end
67
    end
68
end
69

70
----------------
71
-- Load Lua code as a text or binary chunk (in a Lua 5.2 compatible way).
72
-- @param ld code string or loader
73
-- @param[opt] source name of chunk for errors
74
-- @param[opt] mode 'b', 't' or 'bt'
75
-- @param[opt] env environment to load the chunk in
76
-- @function compat.load
77

78
---------------
79
-- Get environment of a function (in a Lua 5.1 compatible way).
80
-- Not 100% compatible, so with Lua 5.2 it may return nil for a function with no
81
-- global references!
82
-- Based on code by [Sergey Rozhenko](http://lua-users.org/lists/lua-l/2010-06/msg00313.html)
83
-- @param f a function or a call stack reference
84
-- @function compat.getfenv
85

86
---------------
87
-- Set environment of a function (in a Lua 5.1 compatible way).
88
-- @param f a function or a call stack reference
89
-- @param env a table that becomes the new environment of `f`
90
-- @function compat.setfenv
91

92
if compat.lua51 then -- define Lua 5.2 style load()
748✔
93
    if not compat.jit then -- but LuaJIT's load _is_ compatible
352✔
94
        local lua51_load = load
132✔
95
        function compat.load(str,src,mode,env)
132✔
96
            local chunk,err
97
            if type(str) == 'string' then
405✔
98
                if str:byte(1) == 27 and not (mode or 'bt'):find 'b' then
402✔
99
                    return nil,"attempt to load a binary chunk"
3✔
100
                end
101
                chunk,err = loadstring(str,src)
399✔
102
            else
103
                chunk,err = lua51_load(str,src)
3✔
104
            end
105
            if chunk and env then setfenv(chunk,env) end
402✔
106
            return chunk,err
402✔
107
        end
108
    else
109
        compat.load = load
220✔
110
    end
111
    compat.setfenv, compat.getfenv = setfenv, getfenv
352✔
112
else
113
    compat.load = load
396✔
114
    -- setfenv/getfenv replacements for Lua 5.2
115
    -- by Sergey Rozhenko
116
    -- http://lua-users.org/lists/lua-l/2010-06/msg00313.html
117
    -- Roberto Ierusalimschy notes that it is possible for getfenv to return nil
118
    -- in the case of a function with no globals:
119
    -- http://lua-users.org/lists/lua-l/2010-06/msg00315.html
120
    function compat.setfenv(f, t)
396✔
121
        f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
18✔
122
        local name
123
        local up = 0
18✔
124
        repeat
125
            up = up + 1
45✔
126
            name = debug.getupvalue(f, up)
45✔
127
        until name == '_ENV' or name == nil
45✔
128
        if name then
18✔
129
            debug.upvaluejoin(f, up, function() return name end, 1) -- use unique upvalue
18✔
130
            debug.setupvalue(f, up, t)
18✔
131
        end
132
        if f ~= 0 then return f end
18✔
133
    end
134

135
    function compat.getfenv(f)
396✔
136
        local f = f or 0
81✔
137
        f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
81✔
138
        local name, val
139
        local up = 0
81✔
140
        repeat
141
            up = up + 1
108✔
142
            name, val = debug.getupvalue(f, up)
108✔
143
        until name == '_ENV' or name == nil
108✔
144
        return val
81✔
145
    end
146
end
147

148

149
--- Global exported functions (for Lua 5.1 & LuaJIT)
150
-- @section lua52
151

152
--- pack an argument list into a table.
153
-- @param ... any arguments
154
-- @return a table with field n set to the length
155
-- @function table.pack
156
if not table.pack then
748✔
157
    function table.pack (...)       -- luacheck: ignore
704✔
158
        return {n=select('#',...); ...}
632✔
159
    end
160
end
161

162
--- unpack a table and return the elements.
163
--
164
-- NOTE: this version does NOT honor the n field, and hence it is not nil-safe.
165
-- See `utils.unpack` for a version that is nil-safe.
166
-- @param t table to unpack
167
-- @param[opt] i index from which to start unpacking, defaults to 1
168
-- @param[opt] j index of the last element to unpack, defaults to #t
169
-- @return multiple return values from the table
170
-- @function table.unpack
171
-- @see utils.unpack
172
if not table.unpack then
748✔
173
    table.unpack = unpack           -- luacheck: ignore
352✔
174
end
175

176
--- return the full path where a file name would be matched.
177
-- This function was introduced in Lua 5.2, so this compatibility version
178
-- will be injected in Lua 5.1 engines.
179
-- @string name file name, possibly dotted
180
-- @string path a path-template in the same form as package.path or package.cpath
181
-- @string[opt] sep template separate character to be replaced by path separator. Default: "."
182
-- @string[opt] rep the path separator to use, defaults to system separator. Default; "/" on Unixes, "\" on Windows.
183
-- @see path.package_path
184
-- @function package.searchpath
185
-- @return on success: path of the file
186
-- @return on failure: nil, error string listing paths tried
187
if not package.searchpath then
748✔
188
    function package.searchpath (name,path,sep,rep)    -- luacheck: ignore
264✔
189
        if type(name) ~= "string" then
12✔
190
            error(("bad argument #1 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
×
191
        end
192
        if type(path) ~= "string" then
12✔
193
            error(("bad argument #2 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
×
194
        end
195
        if sep ~= nil and type(sep) ~= "string" then
12✔
196
            error(("bad argument #3 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
×
197
        end
198
        if rep ~= nil and type(rep) ~= "string" then
12✔
199
            error(("bad argument #4 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
×
200
        end
201
        sep = sep or "."
12✔
202
        rep = rep or compat.dir_separator
12✔
203
        do
204
          local s, e = name:find(sep, nil, true)
12✔
205
          while s do
30✔
206
            name = name:sub(1, s-1) .. rep .. name:sub(e+1, -1)
18✔
207
            s, e = name:find(sep, s + #rep + 1, true)
18✔
208
          end
209
        end
210
        local tried = {}
12✔
211
        for m in path:gmatch('[^;]+') do
30✔
212
            local nm = m:gsub('?', name)
24✔
213
            tried[#tried+1] = nm
24✔
214
            local f = io.open(nm,'r')
24✔
215
            if f then f:close(); return nm end
24✔
216
        end
217
        return nil, "\tno file '" .. table.concat(tried, "'\n\tno file '") .. "'"
6✔
218
    end
219
end
220

221
--- Global exported functions (for Lua < 5.4)
222
-- @section lua54
223

224
--- raise a warning message.
225
-- This functions mimics the `warn` function added in Lua 5.4.
226
-- @function warn
227
-- @param ... any arguments
228
if not rawget(_G, "warn") then
748✔
229
    local enabled = false
616✔
230
    local function warn(arg1, ...)
231
        if type(arg1) == "string" and arg1:sub(1, 1) == "@" then
152✔
232
            -- control message
233
            if arg1 == "@on" then
×
234
                enabled = true
×
235
                return
×
236
            end
237
            if arg1 == "@off" then
×
238
                enabled = false
×
239
                return
×
240
            end
241
            return -- ignore unknown control messages
×
242
        end
243
        if enabled then
112✔
244
          io.stderr:write("Lua warning: ", arg1, ...)
×
245
          io.stderr:write("\n")
×
246
        end
247
    end
248
    -- use rawset to bypass OpenResty's protection of global scope
249
    rawset(_G, "warn", warn)
616✔
250
end
251

252
return compat
748✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc