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 --- Creates a Class object (Python-style object model)
31 -- Creates a new class object which can be instantiated by calling itself.
32 -- Any class functions or shared parameters can be attached to this object.
33 -- Attaching a table to the class object makes this table shared between
34 -- all instances of this class. For object paramters use the __init__ function.
35 -- Classes can inherit member functions and values from a base class.
36 -- Class can be instantiated by calling them. All parameters will be passed
37 -- to the __init__ function of this class - if such a function exists.
38 -- The __init__ function must be used to set any object parameters that are not shared
39 -- with other objects of this class. Any return values will be ignored.
42 -- @param base the base class to inherit from (optional)
43 -- @return class object
47 local create = function(class, ...)
49 setmetatable(inst, {__index = class})
52 local stat, err = copcall(inst.__init__, inst, ...)
61 local classmeta = {__call = create}
64 classmeta.__index = base
67 setmetatable(class, classmeta)
72 -- Clones an object (deep on-demand)
73 function clone(object, deep)
76 for k, v in pairs(object) do
77 if deep and type(v) == "table" then
83 setmetatable(copy, getmetatable(object))
89 -- Combines two or more numerically indexed tables into one
92 for i, a in ipairs(arg) do
93 for j, v in ipairs(a) do
94 table.insert(result, v)
101 -- Checks whether a table has an object "value" in it
102 function contains(table, value)
103 for k, v in pairs(table) do
112 -- Dumps and strips a Lua-Function
114 local d = string.dump(f)
115 return d and strip_bytecode(d)
119 -- Dumps a table to stdout (useful for testing and debugging)
120 function dumptable(t, i)
122 for k,v in pairs(t) do
123 print(string.rep("\t", i) .. tostring(k), tostring(v))
124 if type(v) == "table" then
131 -- Escapes all occurences of c in s
132 function escape(s, c)
134 return s:gsub(c, "\\" .. c)
138 -- Populate obj in the scope of f as key
139 function extfenv(f, key, obj)
140 local scope = getfenv(f)
145 -- Checks whether an object is an instanceof class
146 function instanceof(object, class)
147 local meta = getmetatable(object)
148 while meta and meta.__index do
149 if meta.__index == class then
152 meta = getmetatable(meta.__index)
158 -- Creates valid XML PCDATA from a string
159 function pcdata(value)
160 value = value:gsub("&", "&")
161 value = value:gsub('"', """)
162 value = value:gsub("'", "'")
163 value = value:gsub("<", "<")
164 return value:gsub(">", ">")
168 -- Returns an error message to stdout
170 io.stderr:write(tostring(obj) .. "\n")
174 -- Resets the scope of f doing a shallow copy of its scope into a new table
176 setfenv(f, clone(getfenv(f)))
180 -- Splits a string into an array
181 function split(str, pat, max, regex)
201 local s, e = str:find(pat, c, not regex)
203 if s and max < 0 then
204 table.insert(t, str:sub(c))
206 table.insert(t, str:sub(c, s and s - 1))
208 c = e and e + 1 or #str + 1
209 until not s or max < 0
215 -- Strips lua bytecode
216 -- Original version by Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
217 function strip_bytecode(dump)
218 local version, format, endian, int, size, ins, num, lnum = dump:byte(5, 12)
221 subint = function(dump, i, l)
224 val = val * 256 + dump:byte(i + n - 1)
229 subint = function(dump, i, l)
232 val = val * 256 + dump:byte(i + n - 1)
239 strip_function = function(dump)
240 local count, offset = subint(dump, 1, size)
241 local stripped, dirty = string.rep("\0", size), offset + count
242 offset = offset + count + int * 2 + 4
243 offset = offset + int + subint(dump, offset, int) * ins
244 count, offset = subint(dump, offset, int)
247 t, offset = subint(dump, offset, 1)
251 offset = offset + size + subint(dump, offset, size)
253 offset = offset + num
254 elseif t == 254 or t == 9 then
255 offset = offset + lnum
258 count, offset = subint(dump, offset, int)
259 stripped = stripped .. dump:sub(dirty, offset - 1)
261 local proto, off = strip_function(dump:sub(offset, -1))
262 stripped, offset = stripped .. proto, offset + off - 1
264 offset = offset + subint(dump, offset, int) * int + int
265 count, offset = subint(dump, offset, int)
267 offset = offset + subint(dump, offset, size) + size + int * 2
269 count, offset = subint(dump, offset, int)
271 offset = offset + subint(dump, offset, size) + size
273 stripped = stripped .. string.rep("\0", int * 3)
274 return stripped, offset
277 return dump:sub(1,12) .. strip_function(dump:sub(13,-1))
281 -- Creates a new threadlocal store
282 function threadlocal()
285 local function get(self, key)
286 local c = coroutine.running()
287 local thread = coxpt[c] or c or 0
288 if not rawget(self, thread) then
291 return rawget(self, thread)[key]
294 local function set(self, key, value)
295 local c = coroutine.running()
296 local thread = coxpt[c] or c or 0
297 if not rawget(self, thread) then
298 rawset(self, thread, {})
300 rawget(self, thread)[key] = value
303 setmetatable(tbl, {__index = get, __newindex = set, __mode = "k"})
309 -- Removes whitespace from beginning and end of a string
311 local s = str:gsub("^%s*(.-)%s*$", "%1")
316 -- Updates given table with new values
317 function update(t, updates)
318 for k, v in pairs(updates) do
324 -- Updates the scope of f with "extscope"
325 function updfenv(f, extscope)
326 update(getfenv(f), extscope)
330 -- Parse units from a string and return integer value
331 function parse_units(ustr)
338 y = 60 * 60 * 24 * 366,
339 m = 60 * 60 * 24 * 31,
340 w = 60 * 60 * 24 * 7,
348 gb = 1024 * 1024 * 1024,
350 -- storage sizes (si)
353 gib = 1000 * 1000 * 1000
356 -- parse input string
357 for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
359 local num = spec:gsub("[^0-9%.]+$","")
360 local spn = spec:gsub("^[0-9%.]+", "")
362 if map[spn] or map[spn:sub(1,1)] then
363 val = val + num * ( map[spn] or map[spn:sub(1,1)] )
374 -- Provide various sorting iterators
375 function _sortiter( t, f )
378 for k, v in pairs(t) do
379 table.insert( keys, k )
383 local _len = table.getn( keys )
385 table.sort( keys, f )
390 return keys[_pos], t[keys[_pos]]
395 -- Return key, value pairs sorted by provided callback function
397 return _sortiter( t, f )
400 -- Return key, value pairs sorted by keys
402 return _sortiter( t )
405 -- Return key, value pairs sorted by values
407 return _sortiter( t, function (a,b) return t[a] < t[b] end )
411 -- Coroutine safe xpcall and pcall versions modified for Luci
413 -- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
414 local performResume, handleReturnValue
415 local oldpcall, oldxpcall = pcall, xpcall
417 setmetatable(coxpt, {__mode = "kv"})
419 function handleReturnValue(err, co, status, ...)
421 return false, err(debug.traceback(co, (...)), ...)
423 if coroutine.status(co) == 'suspended' then
424 return performResume(err, co, coroutine.yield(...))
430 function performResume(err, co, ...)
431 return handleReturnValue(err, co, coroutine.resume(co, ...))
434 function coxpcall(f, err, ...)
435 local res, co = oldpcall(coroutine.create, f)
438 local newf = function() return f(unpack(params)) end
439 co = coroutine.create(newf)
441 local c = coroutine.running()
442 coxpt[co] = coxpt[c] or c or 0
444 return performResume(err, co, ...)
447 local function id(trace, ...)
451 function copcall(f, ...)
452 return coxpcall(f, id, ...)