---[[
-LuCI - Utility library
-
-Description:
-Several common useful Lua functions
-
-License:
-Copyright 2008 Steven Barth <steven@midlink.org>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-]]--
+-- Copyright 2008 Steven Barth <steven@midlink.org>
+-- Licensed to the public under the Apache License 2.0.
local io = require "io"
local math = require "math"
local string = require "string"
local coroutine = require "coroutine"
local tparser = require "luci.template.parser"
+local json = require "luci.jsonc"
local _ubus = require "ubus"
local _ubus_connection = nil
local getmetatable, setmetatable = getmetatable, setmetatable
local rawget, rawset, unpack = rawget, rawset, unpack
-local tostring, type, assert = tostring, type, assert
+local tostring, type, assert, error = tostring, type, assert, error
local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring
local require, pcall, xpcall = require, pcall, xpcall
local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit
---- LuCI utility functions.
module "luci.util"
--
-- Pythonic string formatting extension
--
getmetatable("").__mod = function(a, b)
+ local ok, res
+
if not b then
return a
elseif type(b) == "table" then
+ local k, _
for k, _ in pairs(b) do if type(b[k]) == "userdata" then b[k] = tostring(b[k]) end end
- return a:format(unpack(b))
+
+ ok, res = pcall(a.format, a, unpack(b))
+ if not ok then
+ error(res, 2)
+ end
+ return res
else
if type(b) == "userdata" then b = tostring(b) end
- return a:format(b)
+
+ ok, res = pcall(a.format, a, b)
+ if not ok then
+ error(res, 2)
+ end
+ return res
end
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.
-- Attaching a table to the class object makes this table shared between
-- to the __init__ function of this class - if such a function exists.
-- The __init__ function must be used to set any object parameters that are not shared
-- with other objects of this class. Any return values will be ignored.
--- @param base The base class to inherit from (optional)
--- @return A class object
--- @see instanceof
--- @see clone
function class(base)
return setmetatable({}, {
__call = _instantiate,
})
end
---- 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 whether the object is an instance
--- @see class
--- @see clone
function instanceof(object, class)
local meta = getmetatable(object)
while meta and meta.__index do
end
}
---- 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
-- Debugging routines
--
---- Write given object to stderr.
--- @param obj Value to write to stderr
--- @return Boolean indicating whether the write operation was successful
function perror(obj)
return io.stderr:write(tostring(obj) .. "\n")
end
---- Recursively dumps a table to stdout, useful for testing and debugging.
--- @param t Table value to dump
--- @param maxdepth Maximum depth
--- @return Always nil
function dumptable(t, maxdepth, i, seen)
i = i or 0
seen = seen or setmetatable({}, {__mode="k"})
-- String and data manipulation routines
--
---- Create valid XML PCDATA from given string.
--- @param value String value containing the data to escape
--- @return String value containing the escaped data
function pcdata(value)
return value and tparser.pcdata(tostring(value))
end
---- Strip HTML tags from given string.
--- @param value String containing the HTML text
--- @return String with HTML tags stripped of
function striptags(value)
return value and tparser.striptags(tostring(value))
end
---- Splits given string on a defined separator sequence and return a table
+-- for bash, ash and similar shells single-quoted strings are taken
+-- literally except for single quotes (which terminate the string)
+-- (and the exception noted below for dash (-) at the start of a
+-- command line parameter).
+function shellsqescape(value)
+ local res
+ res, _ = string.gsub(value, "'", "'\\''")
+ return res
+end
+
+-- bash, ash and other similar shells interpret a dash (-) at the start
+-- of a command-line parameters as an option indicator regardless of
+-- whether it is inside a single-quoted string. It must be backlash
+-- escaped to resolve this. This requires in some funky special-case
+-- handling. It may actually be a property of the getopt function
+-- rather than the shell proper.
+function shellstartsqescape(value)
+ res, _ = string.gsub(value, "^\-", "\\-")
+ res, _ = string.gsub(res, "^-", "\-")
+ return shellsqescape(value)
+end
+
-- 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, 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 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)
pat = pat or "\n"
max = max or #str
return t
end
---- Remove leading and trailing whitespace from given string value.
--- @param str String value containing whitespace padded data
--- @return String value with leading and trailing space removed
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 type(v) == "table" then
local k = nil
return function() end
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:
-- o "y" - one year (60*60*24*366)
-- o "kib" - one si kilobyte (1000)
-- o "mib" - one si megabyte (1000*1000)
-- o "gib" - one si gigabyte (1000*1000*1000)
--- @param ustr String containing a numerical value with trailing unit
--- @return Number containing the canonical value
function parse_units(ustr)
local val = 0
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
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(...)
return append({}, ...)
end
---- 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 whether the given value occurs within table
function contains(table, value)
for k, v in pairs(table) do
if value == v then
return false
end
---- Update values in given table with the values from the second given table.
-- Both table are - in fact - merged together.
--- @param t Table which should be updated
--- @param updates Table containing the values to update
--- @return Always nil
function update(t, updates)
for k, v in pairs(updates) do
t[k] = v
end
end
---- Retrieve all keys of given associative table.
--- @param t Table to extract keys from
--- @return Sorted table containing the keys
function keys(t)
local keys = { }
if t then
return keys
end
---- Clones the given object and return it's copy.
--- @param object Table value to clone
--- @param deep Boolean indicating whether to do recursive cloning
--- @return Cloned table value
function clone(object, deep)
local copy = {}
end
---- Create a dynamic table which automatically creates subtables.
--- @return Dynamic Table
function dtable()
return setmetatable({}, { __index =
function(tbl, key)
return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. 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, seen)
seen = seen or setmetatable({}, {__mode="k"})
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 data. The byte code
-- will be stripped before it is returned.
--- @param val Value to return as bytecode
--- @return String value containing the bytecode of the given data
function get_bytecode(val)
local code
return code -- and strip_bytecode(code)
end
---- Strips unnescessary lua bytecode from given string. Information like line
-- numbers and debugging numbers will be discarded. Original version by
-- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
--- @param code String value containing the original lua byte code
--- @return String value containing the stripped lua byte code
function strip_bytecode(code)
local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12)
local subint
end
end
---- Return a key, value iterator which returns the values sorted according to
-- the provided callback function.
--- @param t The table to iterate
--- @param f A callback function to decide the order of elements
--- @return Function value containing the corresponding iterator
function spairs(t,f)
return _sortiter( t, f )
end
---- Return a key, value iterator for the given table.
-- The table pairs are sorted by key.
--- @param t The table to iterate
--- @return Function value containing the corresponding iterator
function kspairs(t)
return _sortiter( t )
end
---- Return a key, value iterator for the given table.
-- The table pairs are sorted by value.
--- @param t The table to iterate
--- @return Function value containing the corresponding iterator
function vspairs(t)
return _sortiter( t, function (a,b) return t[a] < t[b] end )
end
-- System utility functions
--
---- Test whether the current system is operating in big endian mode.
--- @return Boolean value indicating whether system is big endian
function bigendian()
return string.byte(string.dump(function() end), 7) == 0
end
---- Execute given commandline and gather stdout.
--- @param command String containing command to execute
--- @return String containing the command's stdout
function exec(command)
local pp = io.popen(command)
local data = pp:read("*a")
return data
end
---- Return a line-buffered iterator over the output of given command.
--- @param command String containing the command to execute
--- @return Iterator
function execi(command)
local pp = io.popen(command)
return data
end
---- Issue an ubus call.
--- @param object String containing the ubus object to call
--- @param method String containing the ubus method to call
--- @param values Table containing the values to pass
--- @return Table containin the ubus result
function ubus(object, method, data)
if not _ubus_connection then
_ubus_connection = _ubus.connect()
end
end
---- Returns the absolute path to LuCI base directory.
--- @return String containing the directory path
+function serialize_json(x, cb)
+ local js = json.stringify(x)
+ if type(cb) == "function" then
+ cb(js)
+ else
+ return js
+ end
+end
+
+
function libpath()
return require "nixio.fs".dirname(ldebug.__file__)
end
+function checklib(fullpathexe, wantedlib)
+ local fs = require "nixio.fs"
+ local haveldd = fs.access('/usr/bin/ldd')
+ if not haveldd then
+ return false
+ end
+ local libs = exec("/usr/bin/ldd " .. fullpathexe)
+ if not libs then
+ return false
+ end
+ for k, v in ipairs(split(libs)) do
+ if v:find(wantedlib) then
+ return true
+ end
+ end
+ return false
+end
--
-- Coroutine safe xpcall and pcall versions modified for Luci
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)
return performResume(err, co, ...)
end
---- 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, copcall_id, ...)