5 Several common useful Lua functions
11 Copyright 2008 Steven Barth <steven@midlink.org>
13 Licensed under the Apache License, Version 2.0 (the "License");
14 you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at
17 http://www.apache.org/licenses/LICENSE-2.0
19 Unless required by applicable law or agreed to in writing, software
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
27 module("luci.util", package.seeall)
30 -- Lua simplified Python-style OO class support emulation
34 local create = function(class, ...)
36 setmetatable(inst, {__index = class})
39 local stat, err = copcall(inst.__init__, inst, ...)
48 local classmeta = {__call = create}
51 classmeta.__index = base
54 setmetatable(class, classmeta)
59 -- Clones an object (deep on-demand)
60 function clone(object, deep)
63 for k, v in pairs(object) do
64 if deep and type(v) == "table" then
70 setmetatable(copy, getmetatable(object))
76 -- Combines two or more numerically indexed tables into one
79 for i, a in ipairs(arg) do
80 for j, v in ipairs(a) do
81 table.insert(result, v)
88 -- Checks whether a table has an object "value" in it
89 function contains(table, value)
90 for k, v in pairs(table) do
99 -- Dumps and strips a Lua-Function
101 local d = string.dump(f)
102 return d and strip_bytecode(d)
106 -- Dumps a table to stdout (useful for testing and debugging)
107 function dumptable(t, i)
109 for k,v in pairs(t) do
110 print(string.rep("\t", i) .. tostring(k), tostring(v))
111 if type(v) == "table" then
118 -- Escapes all occurences of c in s
119 function escape(s, c)
121 return s:gsub(c, "\\" .. c)
125 -- Populate obj in the scope of f as key
126 function extfenv(f, key, obj)
127 local scope = getfenv(f)
132 -- Checks whether an object is an instanceof class
133 function instanceof(object, class)
134 local meta = getmetatable(object)
135 while meta and meta.__index do
136 if meta.__index == class then
139 meta = getmetatable(meta.__index)
145 -- Creates valid XML PCDATA from a string
146 function pcdata(value)
147 value = value:gsub("&", "&")
148 value = value:gsub('"', """)
149 value = value:gsub("'", "'")
150 value = value:gsub("<", "<")
151 return value:gsub(">", ">")
155 -- Returns an error message to stdout
157 io.stderr:write(tostring(obj) .. "\n")
161 -- Resets the scope of f doing a shallow copy of its scope into a new table
163 setfenv(f, clone(getfenv(f)))
167 -- Splits a string into an array
168 function split(str, pat, max, regex)
188 local s, e = str:find(pat, c, not regex)
190 if s and max < 0 then
191 table.insert(t, str:sub(c))
193 table.insert(t, str:sub(c, s and s - 1))
195 c = e and e + 1 or #str + 1
196 until not s or max < 0
202 -- Strips lua bytecode
203 -- Original version by Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
204 function strip_bytecode(dump)
205 local version, format, endian, int, size, ins, num, lnum = dump:byte(5, 12)
208 subint = function(dump, i, l)
211 val = val * 256 + dump:byte(i + n - 1)
216 subint = function(dump, i, l)
219 val = val * 256 + dump:byte(i + n - 1)
226 strip_function = function(dump)
227 local count, offset = subint(dump, 1, size)
228 local stripped, dirty = string.rep("\0", size), offset + count
229 offset = offset + count + int * 2 + 4
230 offset = offset + int + subint(dump, offset, int) * ins
231 count, offset = subint(dump, offset, int)
234 t, offset = subint(dump, offset, 1)
238 offset = offset + size + subint(dump, offset, size)
240 offset = offset + num
241 elseif t == 254 or t == 9 then
242 offset = offset + lnum
245 count, offset = subint(dump, offset, int)
246 stripped = stripped .. dump:sub(dirty, offset - 1)
248 local proto, off = strip_function(dump:sub(offset, -1))
249 stripped, offset = stripped .. proto, offset + off - 1
251 offset = offset + subint(dump, offset, int) * int + int
252 count, offset = subint(dump, offset, int)
254 offset = offset + subint(dump, offset, size) + size + int * 2
256 count, offset = subint(dump, offset, int)
258 offset = offset + subint(dump, offset, size) + size
260 stripped = stripped .. string.rep("\0", int * 3)
261 return stripped, offset
264 return dump:sub(1,12) .. strip_function(dump:sub(13,-1))
268 -- Creates a new threadlocal store
269 function threadlocal()
272 local function get(self, key)
273 local c = coroutine.running()
274 local thread = coxpt[c] or c or 0
275 if not rawget(self, thread) then
278 return rawget(self, thread)[key]
281 local function set(self, key, value)
282 local c = coroutine.running()
283 local thread = coxpt[c] or c or 0
284 if not rawget(self, thread) then
285 rawset(self, thread, {})
287 rawget(self, thread)[key] = value
290 setmetatable(tbl, {__index = get, __newindex = set, __mode = "k"})
296 -- Removes whitespace from beginning and end of a string
298 local s = str:gsub("^%s*(.-)%s*$", "%1")
303 -- Updates given table with new values
304 function update(t, updates)
305 for k, v in pairs(updates) do
311 -- Updates the scope of f with "extscope"
312 function updfenv(f, extscope)
313 update(getfenv(f), extscope)
317 -- Parse units from a string and return integer value
318 function parse_units(ustr)
325 y = 60 * 60 * 24 * 366,
326 m = 60 * 60 * 24 * 31,
327 w = 60 * 60 * 24 * 7,
335 gb = 1024 * 1024 * 1024,
337 -- storage sizes (si)
340 gib = 1000 * 1000 * 1000
343 -- parse input string
344 for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
346 local num = spec:gsub("[^0-9%.]+$","")
347 local spn = spec:gsub("^[0-9%.]+", "")
349 if map[spn] or map[spn:sub(1,1)] then
350 val = val + num * ( map[spn] or map[spn:sub(1,1)] )
361 -- Provide various sorting iterators
362 function _sortiter( t, f )
365 for k, v in pairs(t) do
366 table.insert( keys, k )
370 local _len = table.getn( keys )
372 table.sort( keys, f )
377 return keys[_pos], t[keys[_pos]]
382 -- Return key, value pairs sorted by provided callback function
384 return _sortiter( t, f )
387 -- Return key, value pairs sorted by keys
389 return _sortiter( t )
392 -- Return key, value pairs sorted by values
394 return _sortiter( t, function (a,b) return t[a] < t[b] end )
398 -- Coroutine safe xpcall and pcall versions modified for Luci
400 -- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
401 local performResume, handleReturnValue
402 local oldpcall, oldxpcall = pcall, xpcall
404 setmetatable(coxpt, {__mode = "kv"})
406 function handleReturnValue(err, co, status, ...)
408 return false, err(debug.traceback(co, (...)), ...)
410 if coroutine.status(co) == 'suspended' then
411 return performResume(err, co, coroutine.yield(...))
417 function performResume(err, co, ...)
418 return handleReturnValue(err, co, coroutine.resume(co, ...))
421 function coxpcall(f, err, ...)
422 local res, co = oldpcall(coroutine.create, f)
425 local newf = function() return f(unpack(params)) end
426 co = coroutine.create(newf)
428 local c = coroutine.running()
429 coxpt[co] = coxpt[c] or c or 0
431 return performResume(err, co, ...)
434 local function id(trace, ...)
438 function copcall(f, ...)
439 return coxpcall(f, id, ...)