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)
189 table.insert(t, str:sub(c, s and s - 1))
191 c = e and e + 1 or #str + 1
192 until not s or max < 0
198 -- Strips lua bytecode
199 -- Original version by Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
200 function strip_bytecode(dump)
201 local version, format, endian, int, size, ins, num, lnum = dump:byte(5, 12)
204 subint = function(dump, i, l)
207 val = val * 256 + dump:byte(i + n - 1)
212 subint = function(dump, i, l)
215 val = val * 256 + dump:byte(i + n - 1)
222 strip_function = function(dump)
223 local count, offset = subint(dump, 1, size)
224 local stripped, dirty = string.rep("\0", size), offset + count
225 offset = offset + count + int * 2 + 4
226 offset = offset + int + subint(dump, offset, int) * ins
227 count, offset = subint(dump, offset, int)
230 t, offset = subint(dump, offset, 1)
234 offset = offset + size + subint(dump, offset, size)
236 offset = offset + num
238 offset = offset + lnum
241 count, offset = subint(dump, offset, int)
242 stripped = stripped .. dump:sub(dirty, offset - 1)
244 local proto, off = strip_function(dump:sub(offset, -1))
245 stripped, offset = stripped .. proto, offset + off - 1
247 offset = offset + subint(dump, offset, int) * int + int
248 count, offset = subint(dump, offset, int)
250 offset = offset + subint(dump, offset, size) + size + int * 2
252 count, offset = subint(dump, offset, int)
254 offset = offset + subint(dump, offset, size) + size
256 stripped = stripped .. string.rep("\0", int * 3)
257 return stripped, offset
260 return dump:sub(1,12) .. strip_function(dump:sub(13,-1))
264 -- Creates a new threadlocal store
265 function threadlocal()
268 local function get(self, key)
269 local c = coroutine.running()
270 local thread = coxpt[c] or c or 0
271 if not rawget(self, thread) then
274 return rawget(self, thread)[key]
277 local function set(self, key, value)
278 local c = coroutine.running()
279 local thread = coxpt[c] or c or 0
280 if not rawget(self, thread) then
281 rawset(self, thread, {})
283 rawget(self, thread)[key] = value
286 setmetatable(tbl, {__index = get, __newindex = set})
292 -- Removes whitespace from beginning and end of a string
294 local s = str:gsub("^%s*(.-)%s*$", "%1")
299 -- Updates given table with new values
300 function update(t, updates)
301 for k, v in pairs(updates) do
307 -- Updates the scope of f with "extscope"
308 function updfenv(f, extscope)
309 update(getfenv(f), extscope)
313 -- Parse units from a string and return integer value
314 function parse_units(ustr)
321 y = 60 * 60 * 24 * 366,
322 m = 60 * 60 * 24 * 31,
323 w = 60 * 60 * 24 * 7,
331 gb = 1024 * 1024 * 1024,
333 -- storage sizes (si)
336 gib = 1000 * 1000 * 1000
339 -- parse input string
340 for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
342 local num = spec:gsub("[^0-9%.]+$","")
343 local spn = spec:gsub("^[0-9%.]+", "")
345 if map[spn] or map[spn:sub(1,1)] then
346 val = val + num * ( map[spn] or map[spn:sub(1,1)] )
357 -- Provide various sorting iterators
358 function _sortiter( t, f )
361 for k, v in pairs(t) do
362 table.insert( keys, k )
366 local _len = table.getn( keys )
368 table.sort( keys, f )
373 return keys[_pos], t[keys[_pos]]
378 -- Return key, value pairs sorted by provided callback function
380 return _sortiter( t, f )
383 -- Return key, value pairs sorted by keys
385 return _sortiter( t )
388 -- Return key, value pairs sorted by values
390 return _sortiter( t, function (a,b) return t[a] < t[b] end )
394 -- Coroutine safe xpcall and pcall versions modified for Luci
396 -- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
397 local performResume, handleReturnValue
398 local oldpcall, oldxpcall = pcall, xpcall
401 function handleReturnValue(err, co, status, ...)
403 return false, err(debug.traceback(co, (...)), ...)
405 if coroutine.status(co) == 'suspended' then
406 return performResume(err, co, coroutine.yield(...))
412 function performResume(err, co, ...)
413 return handleReturnValue(err, co, coroutine.resume(co, ...))
416 function coxpcall(f, err, ...)
417 local res, co = oldpcall(coroutine.create, f)
420 local newf = function() return f(unpack(params)) end
421 co = coroutine.create(newf)
423 local c = coroutine.running()
424 coxpt[co] = coxpt[c] or c or 0
425 return performResume(err, co, ...)
428 local function id(trace, ...)
432 function copcall(f, ...)
433 return coxpcall(f, id, ...)