/usr/share/lua/5.1/syscall/helpers.lua is in lua-ljsyscall 0.12-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 | -- misc helper functions that we use across the board
local require, error, assert, tonumber, tostring,
setmetatable, pairs, ipairs, unpack, rawget, rawset,
pcall, type, table, string, math =
require, error, assert, tonumber, tostring,
setmetatable, pairs, ipairs, unpack, rawget, rawset,
pcall, type, table, string, math
local debug, collectgarbage = require "debug", collectgarbage
local abi = require "syscall.abi"
local ffi = require "ffi"
local bit = require "syscall.bit"
local h = {}
-- generic assert helper, mainly for tests
function h.assert(cond, err, ...)
if not cond then
error(tostring(err or "unspecified error")) -- annoyingly, assert does not call tostring!
end
collectgarbage("collect") -- force gc, to test for bugs
if type(cond) == "function" then return cond, err, ... end
if cond == true then return ... end
return cond, ...
end
local voidp = ffi.typeof("void *")
local function ptvoid(x)
return ffi.cast(voidp, x)
end
local function ptt(tp)
local ptp = ffi.typeof(tp .. " *")
return function(x) return ffi.cast(ptp, x) end
end
h.ptt = ptt
-- constants
h.uint64_max = ffi.cast("uint64_t", 0) - ffi.cast("uint64_t", 1)
h.err64 = ffi.cast("int64_t", -1)
if abi.abi64 then h.errpointer = ptvoid(h.err64) else h.errpointer = ptvoid(0xffffffff) end
h.uint32_max = ffi.cast("uint32_t", 0xffffffff)
h.int32_max = 0x7fffffff
if abi.abi64 then h.longmax = bit.rshift64(h.err64, 1) else h.longmax = h.int32_max end
-- generic iterator that counts down so needs no closure to hold state
function h.reviter(array, i)
i = i - 1
if i >= 0 then return i, array[i] end
end
function h.mktype(tp, x) if ffi.istype(tp, x) then return x else return tp(x) end end
function h.istype(tp, x) if ffi.istype(tp, x) then return x else return false end end
local function lenfn(tp) return ffi.sizeof(tp) end
h.lenfn = lenfn
h.lenmt = {__len = lenfn}
local tint = ffi.typeof("int")
local function getfd(fd)
if type(fd) == "number" or ffi.istype(tint, fd) then return fd end
return fd:getfd()
end
h.getfd = getfd
-- generic function for __new
function h.newfn(tp, tab)
local obj = ffi.new(tp)
if not tab then return obj end
-- these are split out so __newindex is called, not just initialisers luajit understands
for k, v in pairs(tab) do if type(k) == "string" then obj[k] = v end end -- set string indexes
return obj
end
-- generic function for __tostring
local function simpleprint(pt, x)
local out = {}
for _, v in ipairs(pt) do out[#out + 1] = v .. " = " .. tostring(x[v]) end
return "{ " .. table.concat(out, ", ") .. " }"
end
-- type initialisation helpers
function h.addtype(types, name, tp, mt)
if abi.rumpfn then tp = abi.rumpfn(tp) end
if mt then
if mt.index and not mt.__index then -- generic index method
local index = mt.index
mt.index = nil
mt.__index = function(tp, k) if index[k] then return index[k](tp) else error("invalid index " .. k) end end
end
if mt.newindex and not mt.__newindex then -- generic newindex method
local newindex = mt.newindex
mt.newindex = nil
mt.__newindex = function(tp, k, v) if newindex[k] then newindex[k](tp, v) else error("invalid index " .. k) end end
end
if not mt.__len then mt.__len = lenfn end -- default length function is just sizeof
if not mt.__tostring and mt.print then mt.__tostring = function(x) return simpleprint(mt.print, x) end end
types.t[name] = ffi.metatype(tp, mt)
else
types.t[name] = ffi.typeof(tp)
end
types.ctypes[tp] = types.t[name]
types.pt[name] = ptt(tp)
types.s[name] = ffi.sizeof(types.t[name])
end
-- for variables length types, ie those with arrays
function h.addtype_var(types, name, tp, mt)
if abi.rumpfn then tp = abi.rumpfn(tp) end
if not mt.__len then mt.__len = lenfn end -- default length function is just sizeof, gives instance size for var lngth
types.t[name] = ffi.metatype(tp, mt)
types.pt[name] = ptt(tp)
end
function h.addtype_fn(types, name, tp)
if abi.rumpfn then tp = abi.rumpfn(tp) end
types.t[name] = ffi.typeof(tp)
types.s[name] = ffi.sizeof(types.t[name])
end
function h.addraw2(types, name, tp)
if abi.rumpfn then tp = abi.rumpfn(tp) end
types.t[name] = ffi.typeof(tp .. "[2]")
end
function h.addtype1(types, name, tp)
types.t[name] = ffi.typeof(tp .. "[1]")
types.s[name] = ffi.sizeof(types.t[name])
end
function h.addtype2(types, name, tp)
types.t[name] = ffi.typeof(tp .. "[2]")
types.s[name] = ffi.sizeof(types.t[name])
end
function h.addptrtype(types, name, tp)
local ptr = ffi.typeof(tp)
types.t[name] = function(v) return ffi.cast(ptr, v) end
types.s[name] = ffi.sizeof(ptr)
end
-- endian conversion
-- TODO add tests eg for signs.
if abi.be then -- nothing to do
function h.htonl(b) return b end
function h.htons(b) return b end
function h.convle32(b) return bit.bswap(b) end -- used by file system capabilities, always stored as le
else
function h.htonl(b) return bit.bswap(b) end
function h.htons(b) return bit.rshift(bit.bswap(b), 16) end
function h.convle32(b) return b end -- used by file system capabilities, always stored as le
end
h.ntohl = h.htonl -- reverse is the same
h.ntohs = h.htons -- reverse is the same
function h.octal(s) return tonumber(s, 8) end
local octal = h.octal
function h.split(delimiter, text)
if delimiter == "" then return {text} end
if #text == 0 then return {} end
local list = {}
local pos = 1
while true do
local first, last = text:find(delimiter, pos)
if first then
list[#list + 1] = text:sub(pos, first - 1)
pos = last + 1
else
list[#list + 1] = text:sub(pos)
break
end
end
return list
end
function h.trim(s) -- TODO should replace underscore with space
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
local split, trim = h.split, h.trim
-- for AT_FDCWD
function h.atflag(tab)
local function flag(cache, str)
if not str then return tab.FDCWD end
if type(str) == "number" then return str end
if type(str) ~= "string" then return getfd(str) end
if #str == 0 then return 0 end
local s = trim(str):upper()
if #s == 0 then return 0 end
local val = rawget(tab, s)
if not val then error("invalid flag " .. s) end
cache[str] = val
return val
end
return setmetatable(tab, {__index = setmetatable({}, {__index = flag}), __call = function(t, a) return t[a] end})
end
-- for single valued flags
function h.strflag(tab)
local function flag(cache, str)
if type(str) ~= "string" then return str end
if #str == 0 then return 0 end
local s = trim(str):upper()
if #s == 0 then return 0 end
local val = rawget(tab, s)
if not val then return nil end
cache[str] = val
return val
end
return setmetatable(tab, {__index = setmetatable({}, {__index = flag}), __call = function(t, a) return t[a] end})
end
-- take a bunch of flags in a string and return a number
-- allows multiple comma sep flags that are ORed
function h.multiflags(tab)
local function flag(cache, str)
if not str then return 0 end
if type(str) ~= "string" then return str end
if #str == 0 then return 0 end
local f = 0
local a = split(",", str)
if #a == 1 and str == str:upper() then return nil end -- this is to allow testing for presense, while catching errors
for _, v in ipairs(a) do
local s = trim(v):upper()
if #s == 0 then error("empty flag") end
local val = rawget(tab, s)
if not val then error("invalid flag " .. s) end
f = bit.bor(f, val)
end
cache[str] = f
return f
end
return setmetatable(tab, {
__index = setmetatable({}, {__index = flag}),
__call = function(tab, x, ...) -- this allows easily adding or removing a flag
local a = tab[x]
for _, v in ipairs{...} do
if type(v) == "string" and v:find("~") then -- allow negation eg c.IFF(old, "~UP")
local sa = split(",", v)
for _, vv in ipairs(sa) do
local s = trim(vv):upper()
if #s == 0 then error("empty flag") end
local negate = false
if s:sub(1, 1) == "~" then
negate = true
s = trim(s:sub(2))
if #s == 0 then error("empty flag") end
end
local val = rawget(tab, s)
if not val then error("invalid flag " .. s) end
if negate then a = bit.band(a, bit.bnot(val)) else a = bit.bor(a, val) end
end
else
a = bit.bor(a, tab[v])
end
end
return a
end,
})
end
-- like multiflags but also allow octal values in string
function h.modeflags(tab)
local function flag(cache, str)
if not str then return 0 end
if type(str) ~= "string" then return str end
if #str == 0 then return 0 end
local f = 0
local a = split(",", str)
if #a == 1 and str == str:upper() and str:sub(1,1) ~= "0" then return nil end -- this is to allow testing for presense, while catching errors
for i, v in ipairs(a) do
local s = trim(v):upper()
if #s == 0 then error("empty flag") end
local val
if s:sub(1, 1) == "0" then
val = octal(s)
else
val = rawget(tab, s)
if not val then error("invalid flag " .. s) end
end
f = bit.bor(f, val)
end
cache[str] = f
return f
end
return setmetatable(tab, {__index = setmetatable({}, {__index = flag}), __call = function(t, a) return t[a] end})
end
function h.swapflags(tab)
local function flag(cache, str)
if not str then return 0 end
if type(str) ~= "string" then return str end
if #str == 0 then return 0 end
local f = 0
local a = split(",", str)
if #a == 1 and str == str:upper() then return nil end -- this is to allow testing for presense, while catching errors
for i, v in ipairs(a) do
local s = trim(v):upper()
if #s == 0 then error("empty flag") end
if tonumber(s) then
local val = tonumber(s)
f = bit.bor(f, rawget(tab, "PREFER"), bit.lshift(bit.band(rawget(tab, "PRIO_MASK"), val), rawget(tab, "PRIO_SHIFT")))
else
local val = rawget(tab, s)
if not val then error("invalid flag " .. s) end
f = bit.bor(f, val)
end
end
cache[str] = f
return f
end
return setmetatable(tab, {__index = setmetatable({}, {__index = flag}), __call = function(t, a) return t[a] end})
end
-- single char flags, eg used for access which allows "rwx"
function h.charflags(tab)
local function flag(cache, str)
if not str then return 0 end
if type(str) ~= "string" then return str end
str = trim(str:upper())
local flag = 0
for i = 1, #str do
local c = str:sub(i, i)
local val = rawget(tab, c)
if not val then error("invalid flag " .. c) end
flag = bit.bor(flag, val)
end
cache[str] = flag
return flag
end
return setmetatable(tab, {__index = setmetatable({}, {__index = flag}), __call = function(t, a) return t[a] end})
end
h.divmod = function(a, b)
return math.floor(a / b), a % b
end
h.booltoc = setmetatable({
[0] = 0,
[1] = 1,
[false] = 0,
[true] = 1,
}, {__call = function(tb, arg) return tb[arg or 0] end}) -- allow nil as false
function h.ctobool(i) return tonumber(i) ~= 0 end
local function align(len, a) return bit.band(tonumber(len) + a - 1, bit.bnot(a - 1)) end
h.align = align
return h
|