X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=libs%2Fcore%2Fluasrc%2Futil.lua;h=a248026f10357d54f04ab4ff0969787ba90dc2df;hp=5f9c609f65c8da89b462dbb7326ceea9259faaa9;hb=ddd1ba088ed3b93b3485e34d05c427d43306c744;hpb=8dbf29e86e984e9de87ad0724a99cab89f2d8ea6 diff --git a/libs/core/luasrc/util.lua b/libs/core/luasrc/util.lua index 5f9c609f6..a248026f1 100644 --- a/libs/core/luasrc/util.lua +++ b/libs/core/luasrc/util.lua @@ -34,9 +34,10 @@ local coroutine = require "coroutine" local getmetatable, setmetatable = getmetatable, setmetatable local rawget, rawset, unpack = rawget, rawset, unpack -local tostring, type, assert = tostring, type, assert +local tostring, type, assert = tostring, type, assert local ipairs, pairs, loadstring = ipairs, pairs, loadstring local require, pcall, xpcall = require, pcall, xpcall +local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit --- LuCI utility functions. module "luci.util" @@ -48,8 +49,10 @@ getmetatable("").__mod = function(a, b) if not b then return a elseif type(b) == "table" then + for k, _ in pairs(b) do if type(b[k]) == "userdata" then b[k] = tostring(b[k]) end end return a:format(unpack(b)) else + if type(b) == "userdata" then b = tostring(b) end return a:format(b) end end @@ -59,6 +62,17 @@ end -- Class helper routines -- +-- Instantiates a class +local function _instantiate(class, ...) + local inst = setmetatable({}, {__index = class}) + + if inst.__init__ then + inst:__init__(...) + end + + return inst +end + --- 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. @@ -74,26 +88,10 @@ end -- @see instanceof -- @see clone function class(base) - local class = {} - - local create = function(class, ...) - local inst = setmetatable({}, {__index = class}) - - if inst.__init__ then - inst:__init__(...) - end - - return inst - end - - local classmeta = {__call = create} - - if base then - classmeta.__index = base - end - - setmetatable(class, classmeta) - return class + return setmetatable({}, { + __call = _instantiate, + __index = base + }) end --- Test whether the given object is an instance of the given class. @@ -118,34 +116,31 @@ end -- Scope manipulation routines -- ---- Create a new or get an already existing thread local store associated with --- the current active coroutine. A thread local store is private a table object --- whose values can't be accessed from outside of the running coroutine. --- @return Table value representing the corresponding thread local store -function threadlocal() - local tbl = {} - - local function get(self, key) - local c = coroutine.running() - local thread = coxpt[c] or c or 0 - if not rawget(self, thread) then - return nil - end - return rawget(self, thread)[key] - end +local tl_meta = { + __mode = "k", - local function set(self, key, value) - local c = coroutine.running() - local thread = coxpt[c] or c or 0 - if not rawget(self, thread) then - rawset(self, thread, {}) + __index = function(self, key) + local t = rawget(self, coxpt[coroutine.running()] + or coroutine.running() or 0) + return t and t[key] + end, + + __newindex = function(self, key, value) + local c = coxpt[coroutine.running()] or coroutine.running() or 0 + if not rawget(self, c) then + rawset(self, c, { [key] = value }) + else + rawget(self, c)[key] = value end - rawget(self, thread)[key] = value end +} - setmetatable(tbl, {__index = get, __newindex = set, __mode = "k"}) - - return tbl +--- Create a new or get an already existing thread local store associated with +-- the current active coroutine. A thread local store is private a table object +-- whose values can't be accessed from outside of the running coroutine. +-- @return Table value representing the corresponding thread local store +function threadlocal(tbl) + return setmetatable(tbl or {}, tl_meta) end @@ -167,7 +162,7 @@ end function dumptable(t, maxdepth, i, seen) i = i or 0 seen = seen or setmetatable({}, {__mode="k"}) - + for k,v in pairs(t) do perror(string.rep("\t", i) .. tostring(k) .. "\t" .. tostring(v)) if type(v) == "table" and (not maxdepth or i < maxdepth) then @@ -198,21 +193,32 @@ end --- Create valid XML PCDATA from given string. -- @param value String value containing the data to escape -- @return String value containing the escaped data +local function _pcdata_repl(c) + local i = string.byte(c) + + if ( i >= 0x00 and i <= 0x08 ) or ( i >= 0x0B and i <= 0x0C ) or + ( i >= 0x0E and i <= 0x1F ) or ( i == 0x7F ) + then + return "" + + elseif ( i == 0x26 ) or ( i == 0x27 ) or ( i == 0x22 ) or + ( i == 0x3C ) or ( i == 0x3E ) + then + return string.format("&#%i;", i) + end + + return c +end + function pcdata(value) - if not value then return end - value = tostring(value) - value = value:gsub("&", "&") - value = value:gsub('"', """) - value = value:gsub("'", "'") - value = value:gsub("<", "<") - return value:gsub(">", ">") + return value and tostring(value):gsub("[&\"'<>%c]", _pcdata_repl) end --- Strip HTML tags from given string. -- @param value String containing the HTML text -- @return String with HTML tags stripped of function striptags(s) - return pcdata(s:gsub("]*>", " "):gsub("%s+", " ")) + return pcdata(tostring(s):gsub("]*>", " "):gsub("%s+", " ")) end --- Splits given string on a defined separator sequence and return a table @@ -266,6 +272,32 @@ function trim(str) return (str:gsub("^%s*(.-)%s*$", "%1")) end +--- Count the occurences of given substring in given string. +-- @param str String to search in +-- @param pattern String containing pattern to find +-- @return Number of found occurences +function cmatch(str, pat) + local count = 0 + for _ in str:gmatch(pat) do count = count + 1 end + return count +end + +--- Return a matching iterator for the given value. The iterator will return +-- one token per invocation, the tokens are separated by whitespace. If the +-- input value is a table, it is transformed into a string first. A nil value +-- will result in a valid interator which aborts with the first invocation. +-- @param val The value to scan (table, string or nil) +-- @return Iterator which returns one token per call +function imatch(v) + if v == nil then + v = "" + elseif type(v) == "table" then + v = table.concat(v, " ") + end + + return v:gmatch("%S+") +end + --- Parse certain units from the given string and return the canonical integer -- value or 0 if the unit is unknown. Upper- or lower case is irrelevant. -- Recognized units are: @@ -325,19 +357,40 @@ function parse_units(ustr) return val end ---- Combines two or more numerically indexed tables into one. +-- also register functions above in the central string class for convenience +string.escape = escape +string.pcdata = pcdata +string.striptags = striptags +string.split = split +string.trim = trim +string.cmatch = cmatch +string.parse_units = parse_units + + +--- Appends numerically indexed tables or single objects to a given table. +-- @param src Target table +-- @param ... Objects to insert +-- @return Target table +function append(src, ...) + for i, a in ipairs({...}) do + if type(a) == "table" then + for j, v in ipairs(a) do + src[#src+1] = v + end + else + src[#src+1] = a + end + end + return src +end + +--- Combines two or more numerically indexed tables and single objects into one table. -- @param tbl1 Table value to combine -- @param tbl2 Table value to combine -- @param ... More tables to combine -- @return Table value containing all values of given tables function combine(...) - local result = {} - for i, a in ipairs(arg) do - for j, v in ipairs(a) do - result[#result+1] = v - end - end - return result + return append({}, ...) end --- Checks whether the given table contains the given value. @@ -411,7 +464,7 @@ end function _serialize_table(t, seen) assert(not seen[t], "Recursion detected.") seen[t] = true - + local data = "" local idata = "" local ilen = 0 @@ -430,7 +483,7 @@ function _serialize_table(t, seen) for i = 1, ilen do local v = serialize_data(t[i], seen) idata = idata .. ( #idata > 0 and ", " or "" ) .. v - end + end return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data end @@ -443,7 +496,7 @@ end -- @see get_bytecode function serialize_data(val, seen) seen = seen or setmetatable({}, {__mode="k"}) - + if val == nil then return "nil" elseif type(val) == "number" then @@ -488,7 +541,7 @@ function get_bytecode(val) code = string.dump( loadstring( "return " .. serialize_data(val) ) ) end - return code and strip_bytecode(code) + return code -- and strip_bytecode(code) end --- Strips unnescessary lua bytecode from given string. Information like line @@ -517,10 +570,10 @@ function strip_bytecode(code) end end - local strip_function - strip_function = function(code) + local function strip_function(code) local count, offset = subint(code, 1, size) - local stripped, dirty = string.rep("\0", size), offset + count + local stripped = { string.rep("\0", size) } + local dirty = offset + count offset = offset + count + int * 2 + 4 offset = offset + int + subint(code, offset, int) * ins count, offset = subint(code, offset, int) @@ -538,10 +591,11 @@ function strip_bytecode(code) end end count, offset = subint(code, offset, int) - stripped = stripped .. code:sub(dirty, offset - 1) + stripped[#stripped+1] = code:sub(dirty, offset - 1) for n = 1, count do local proto, off = strip_function(code:sub(offset, -1)) - stripped, offset = stripped .. proto, offset + off - 1 + stripped[#stripped+1] = proto + offset = offset + off - 1 end offset = offset + subint(code, offset, int) * int + int count, offset = subint(code, offset, int) @@ -552,8 +606,8 @@ function strip_bytecode(code) for n = 1, count do offset = offset + subint(code, offset, size) + size end - stripped = stripped .. string.rep("\0", int * 3) - return stripped, offset + stripped[#stripped+1] = string.rep("\0", int * 3) + return table.concat(stripped), offset end return code:sub(1,12) .. strip_function(code:sub(13,-1)) @@ -572,13 +626,12 @@ function _sortiter( t, f ) end local _pos = 0 - local _len = table.getn( keys ) table.sort( keys, f ) return function() _pos = _pos + 1 - if _pos <= _len then + if _pos <= #keys then return keys[_pos], t[keys[_pos]] end end @@ -639,11 +692,11 @@ function execi(command) return pp and function() local line = pp:read() - + if not line then pp:close() end - + return line end end @@ -667,7 +720,7 @@ end --- Returns the absolute path to LuCI base directory. -- @return String containing the directory path function libpath() - return require "luci.fs".dirname(ldebug.__file__) + return require "nixio.fs".dirname(ldebug.__file__) end @@ -734,18 +787,19 @@ function copcall(f, ...) end -- Handle return value of protected call -function handleReturnValue(err, co, status, ...) +function handleReturnValue(err, co, status, arg1, arg2, arg3, arg4, arg5) if not status then - return false, err(debug.traceback(co, (...)), ...) + return false, err(debug.traceback(co, arg1), arg1, arg2, arg3, arg4, arg5) end - if coroutine.status(co) == 'suspended' then - return performResume(err, co, coroutine.yield(...)) - else - return true, ... + + if coroutine.status(co) ~= 'suspended' then + return true, arg1, arg2, arg3, arg4, arg5 end + + return performResume(err, co, coroutine.yield(arg1, arg2, arg3, arg4, arg5)) end -- Resume execution of protected function call -function performResume(err, co, ...) - return handleReturnValue(err, co, coroutine.resume(co, ...)) +function performResume(err, co, arg1, arg2, arg3, arg4, arg5) + return handleReturnValue(err, co, coroutine.resume(co, arg1, arg2, arg3, arg4, arg5)) end