]]--
--- LuCI utility functions.
+--- LuCI utility functions.
module("luci.util", package.seeall)
--
-- Class helper routines
--
---- Creates a Class object (Python-style object model)
--- Creates a new class object which can be instantiated by calling itself.
+--- Create a Class object (Python-style object model).
+-- The class object can be instantiated by calling itself.
-- Any class functions or shared parameters can be attached to this object.
-- Attaching a table to the class object makes this table shared between
--- all instances of this class. For object paramters use the __init__ function.
+-- all instances of this class. For object parameters use the __init__ function.
-- Classes can inherit member functions and values from a base class.
-- Class can be instantiated by calling them. All parameters will be passed
-- to the __init__ function of this class - if such a function exists.
--- Test whether the given object is an instance of the given class.
-- @param object Object instance
-- @param class Class object to test against
--- @return Boolean indicating wheather the object is an instance
+-- @return Boolean indicating whether the object is an instance
-- @see class
-- @see clone
function instanceof(object, class)
-- Scope manipulation routines
--
---- Resets the scope of f doing a shallow copy of its scope into a new table
--- (ToDo: @param and @return)
+--- Replace a function scope with a shallow copy of itself.
+-- This is useful if you want to get rid of several unwanted side effects
+-- while changing the scope of a certain Lua function.
+-- @param f Lua function
function resfenv(f)
setfenv(f, clone(getfenv(f)))
end
---- Store given object associated with given key in the scope associated with
--- the given identifier.
--- @param f Value containing the scope identifier
+--- Store given object associated with given key in the scope of a function.
+-- @param f Lua function
-- @param key String value containg the key of the object to store
-- @param obj Object to store in the scope
-- @return Always nil
+-- @see updfenv
+-- @see resfenv
function extfenv(f, key, obj)
local scope = getfenv(f)
scope[key] = obj
end
---- Updates the scope of f with "extscope" (ToDo: docu)
+--- Extend the scope of a function with the contents of a table
+-- @param f Lua function
+-- @param key String value containg the key of the object to store
+-- @param obj Object to store in the scope
+-- @return Always nil
+-- @see extfenv
+-- @see resfenv
function updfenv(f, extscope)
update(getfenv(f), extscope)
end
--- Write given object to stderr.
-- @param obj Value to write to stderr
--- @return Boolean indicating wheather the write operation was successful
+-- @return Boolean indicating whether the write operation was successful
function perror(obj)
- io.stderr:write(tostring(obj) .. "\n")
+ return io.stderr:write(tostring(obj) .. "\n")
end
--- Recursively dumps a table to stdout, useful for testing and debugging.
-- String and data manipulation routines
--
---- Escapes all occurences of the given character in given string.
+--- Escapes all occurrences of the given character in given string.
-- @param s String value containing unescaped characters
-- @param c String value with character to escape (optional, defaults to "\")
--- @return String value with each occurence of character escaped with "\"
+-- @return String value with each occurrence of character escaped with "\"
function escape(s, c)
c = c or "\\"
return s:gsub(c, "\\" .. c)
return value:gsub(">", ">")
end
---- Splits given string on a defined seperator sequence and return a table
+--- Splits given string on a defined separator sequence and return a table
-- containing the resulting substrings. The optional max parameter specifies
-- the number of bytes to process, regardless of the actual length of the given
--- string. The optional last parameter, regex, sepcifies wheather the separator
+-- string. The optional last parameter, regex, specifies whether the separator
-- sequence is interpreted as regular expression.
-- @param str String value containing the data to split up
-- @param pat String with separator pattern (optional, defaults to "\n")
--- @param max Num of bytes to process (optional, default is string length)
--- @param regexp Boolean indicating wheather to interprete the separator
+-- @param max Maximum times to split (optional)
+-- @param regex Boolean indicating whether to interpret the separator
-- pattern as regular expression (optional, default is false)
-- @return Table containing the resulting substrings
function split(str, pat, max, regex)
end
--- Parse certain units from the given string and return the canonical integer
--- value or 0 if the unit is unknown. Upper- or lowercase is irrelevant.
+-- value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
-- Recognized units are:
-- o "y" - one year (60*60*24*366)
-- o "m" - one month (60*60*24*31)
--- Checks whether the given table contains the given value.
-- @param table Table value
-- @param value Value to search within the given table
--- @return Boolean indicating wheather the given value occurs within table
+-- @return Boolean indicating whether the given value occurs within table
function contains(table, value)
for k, v in pairs(table) do
if value == v then
--- Clones the given object and return it's copy.
-- @param object Table value to clone
--- @param deep Boolean indicating wheather to do recursive cloning
+-- @param deep Boolean indicating whether to do recursive cloning
-- @return Cloned table value
function clone(object, deep)
local copy = {}
return copy
end
+-- Test whether the given table value is a numerically indexed table.
+function _is_numeric_table(t)
+ local k = pairs(t)(t)
+ return ( tonumber(k) ~= nil )
+end
+
+-- Serialize the contents of a table value.
+function _serialize_table(t)
+ local data = ""
+ if _is_numeric_table(t) then
+ for i, v in ipairs(t) do
+ v = serialize_data(v)
+ data = data .. ( #data > 0 and ", " or "" ) .. v
+ end
+ else
+ for k, v in pairs(t) do
+ k = serialize_data(k)
+ v = serialize_data(v)
+ data = data .. ( #data > 0 and "; " or "" ) ..
+ '[' .. k .. '] = ' .. v
+ end
+ end
+ return data
+end
+
+--- Recursively serialize given data to lua code, suitable for restoring
+-- with loadstring().
+-- @param val Value containing the data to serialize
+-- @return String value containing the serialized code
+-- @see restore_data
+-- @see get_bytecode
+function serialize_data(val)
+ if val == nil then
+ return "nil"
+ elseif type(val) == "number" then
+ return tostring(val)
+ elseif type(val) == "string" then
+ val = val:gsub("\\", "\\\\")
+ :gsub("\r", "\\r")
+ :gsub("\n", "\\n")
+ :gsub('"','\\"')
+ return '"' .. val .. '"'
+ elseif type(val) == "boolean" then
+ return val and "true" or "false"
+ elseif type(val) == "table" then
+ return "{ " .. _serialize_table(val) .. " }"
+ else
+ return '"[unhandled data type:' .. type(val) .. ']"'
+ end
+end
+
+--- Restore data previously serialized with serialize_data().
+-- @param str String containing the data to restore
+-- @return Value containing the restored data structure
+-- @see serialize_data
+-- @see get_bytecode
+function restore_data(str)
+ return loadstring("return " .. str)()
+end
+
--
-- Byte code manipulation routines
--
---- Return the current runtime bytecode of the given function. The byte code
+--- Return the current runtime bytecode of the given data. The byte code
-- will be stripped before it is returned.
--- @param f Function value to return as bytecode
--- @return String value containing the bytecode of the given function
-function get_bytecode(f)
- local d = string.dump(f)
- return d and strip_bytecode(d)
+-- @param val Value to return as bytecode
+-- @return String value containing the bytecode of the given data
+function get_bytecode(val)
+ local code
+
+ if type(val) == "function" then
+ code = string.dump(val)
+ else
+ code = string.dump( loadstring( "return " .. serialize_data(val) ) )
+ end
+
+ return code and strip_bytecode(code)
end
--- Strips unnescessary lua bytecode from given string. Information like line
coxpt = {}
setmetatable(coxpt, {__mode = "kv"})
---- (ToDo: docu)
+-- Identity function for copcall
+local function copcall_id(trace, ...)
+ return ...
+end
+
+--- This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function
+-- @param f Lua function to be called protected
+-- @param err Custom error handler
+-- @param ... Parameters passed to the function
+-- @return A boolean whether the function call succeeded and the return
+-- values of either the function or the error handler
function coxpcall(f, err, ...)
local res, co = oldpcall(coroutine.create, f)
if not res then
return performResume(err, co, ...)
end
---- (ToDo: docu)
+--- This is a coroutine-safe drop-in replacement for Lua's "pcall"-function
+-- @param f Lua function to be called protected
+-- @param ... Parameters passed to the function
+-- @return A boolean whether the function call succeeded and the returns
+-- values of the function or the error object
function copcall(f, ...)
- return coxpcall(f, id, ...)
-end
-
---- (ToDo: docu)
-local function id(trace, ...)
- return ...
+ return coxpcall(f, copcall_id, ...)
end
---- (ToDo: docu)
+-- Handle return value of protected call
function handleReturnValue(err, co, status, ...)
if not status then
return false, err(debug.traceback(co, (...)), ...)
end
end
---- (ToDo: docu)
+-- Resume execution of protected function call
function performResume(err, co, ...)
return handleReturnValue(err, co, coroutine.resume(co, ...))
end