mirror of
				https://github.com/Ysurac/openmptcprouter-feeds.git
				synced 2025-03-09 15:40:03 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			776 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			776 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
-- 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 table = require "table"
 | 
						|
local debug = require "debug"
 | 
						|
local ldebug = require "luci.debug"
 | 
						|
local string = require "string"
 | 
						|
local coroutine = require "coroutine"
 | 
						|
local tparser = require "luci.template.parser"
 | 
						|
local json = require "luci.jsonc"
 | 
						|
local lhttp = require "lucihttp"
 | 
						|
 | 
						|
local _ubus = require "ubus"
 | 
						|
local _ubus_connection = nil
 | 
						|
 | 
						|
local getmetatable, setmetatable = getmetatable, setmetatable
 | 
						|
local rawget, rawset, unpack, select = rawget, rawset, unpack, select
 | 
						|
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
 | 
						|
 | 
						|
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
 | 
						|
 | 
						|
		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
 | 
						|
 | 
						|
		ok, res = pcall(a.format, a, b)
 | 
						|
		if not ok then
 | 
						|
			error(res, 2)
 | 
						|
		end
 | 
						|
		return res
 | 
						|
	end
 | 
						|
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
 | 
						|
 | 
						|
-- 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 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.
 | 
						|
-- 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.
 | 
						|
function class(base)
 | 
						|
	return setmetatable({}, {
 | 
						|
		__call  = _instantiate,
 | 
						|
		__index = base
 | 
						|
	})
 | 
						|
end
 | 
						|
 | 
						|
function instanceof(object, class)
 | 
						|
	local meta = getmetatable(object)
 | 
						|
	while meta and meta.__index do
 | 
						|
		if meta.__index == class then
 | 
						|
			return true
 | 
						|
		end
 | 
						|
		meta = getmetatable(meta.__index)
 | 
						|
	end
 | 
						|
	return false
 | 
						|
end
 | 
						|
 | 
						|
 | 
						|
--
 | 
						|
-- Scope manipulation routines
 | 
						|
--
 | 
						|
 | 
						|
coxpt = setmetatable({}, { __mode = "kv" })
 | 
						|
 | 
						|
local tl_meta = {
 | 
						|
	__mode = "k",
 | 
						|
 | 
						|
	__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
 | 
						|
		local r = rawget(self, c)
 | 
						|
		if not r then
 | 
						|
			rawset(self, c, { [key] = value })
 | 
						|
		else
 | 
						|
			r[key] = value
 | 
						|
		end
 | 
						|
	end
 | 
						|
}
 | 
						|
 | 
						|
-- 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.
 | 
						|
function threadlocal(tbl)
 | 
						|
	return setmetatable(tbl or {}, tl_meta)
 | 
						|
end
 | 
						|
 | 
						|
 | 
						|
--
 | 
						|
-- Debugging routines
 | 
						|
--
 | 
						|
 | 
						|
function perror(obj)
 | 
						|
	return io.stderr:write(tostring(obj) .. "\n")
 | 
						|
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
 | 
						|
			if not seen[v] then
 | 
						|
				seen[v] = true
 | 
						|
				dumptable(v, maxdepth, i+1, seen)
 | 
						|
			else
 | 
						|
				perror(string.rep("\t", i) .. "*** RECURSION ***")
 | 
						|
			end
 | 
						|
		end
 | 
						|
	end
 | 
						|
end
 | 
						|
 | 
						|
 | 
						|
--
 | 
						|
-- String and data manipulation routines
 | 
						|
--
 | 
						|
 | 
						|
function pcdata(value)
 | 
						|
	return value and tparser.pcdata(tostring(value))
 | 
						|
end
 | 
						|
 | 
						|
function urlencode(value)
 | 
						|
	if value ~= nil then
 | 
						|
		local str = tostring(value)
 | 
						|
		return lhttp.urlencode(str, lhttp.ENCODE_IF_NEEDED + lhttp.ENCODE_FULL)
 | 
						|
			or str
 | 
						|
	end
 | 
						|
	return nil
 | 
						|
end
 | 
						|
 | 
						|
function urldecode(value, decode_plus)
 | 
						|
	if value ~= nil then
 | 
						|
		local flag = decode_plus and lhttp.DECODE_PLUS or 0
 | 
						|
		local str = tostring(value)
 | 
						|
		return lhttp.urldecode(str, lhttp.DECODE_IF_NEEDED + flag)
 | 
						|
			or str
 | 
						|
	end
 | 
						|
	return nil
 | 
						|
end
 | 
						|
 | 
						|
function striptags(value)
 | 
						|
	return value and tparser.striptags(tostring(value))
 | 
						|
end
 | 
						|
 | 
						|
function shellquote(value)
 | 
						|
	return string.format("'%s'", string.gsub(value or "", "'", "'\\''"))
 | 
						|
end
 | 
						|
 | 
						|
-- 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, "^%-", "\\-")
 | 
						|
   return shellsqescape(res)
 | 
						|
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.
 | 
						|
--					pattern as regular expression (optional, default is false)
 | 
						|
function split(str, pat, max, regex)
 | 
						|
	pat = pat or "\n"
 | 
						|
	max = max or #str
 | 
						|
 | 
						|
	local t = {}
 | 
						|
	local c = 1
 | 
						|
 | 
						|
	if #str == 0 then
 | 
						|
		return {""}
 | 
						|
	end
 | 
						|
 | 
						|
	if #pat == 0 then
 | 
						|
		return nil
 | 
						|
	end
 | 
						|
 | 
						|
	if max == 0 then
 | 
						|
		return str
 | 
						|
	end
 | 
						|
 | 
						|
	repeat
 | 
						|
		local s, e = str:find(pat, c, not regex)
 | 
						|
		max = max - 1
 | 
						|
		if s and max < 0 then
 | 
						|
			t[#t+1] = str:sub(c)
 | 
						|
		else
 | 
						|
			t[#t+1] = str:sub(c, s and s - 1)
 | 
						|
		end
 | 
						|
		c = e and e + 1 or #str + 1
 | 
						|
	until not s or max < 0
 | 
						|
 | 
						|
	return t
 | 
						|
end
 | 
						|
 | 
						|
function trim(str)
 | 
						|
	return (str:gsub("^%s*(.-)%s*$", "%1"))
 | 
						|
end
 | 
						|
 | 
						|
function cmatch(str, pat)
 | 
						|
	local count = 0
 | 
						|
	for _ in str:gmatch(pat) do count = count + 1 end
 | 
						|
	return count
 | 
						|
end
 | 
						|
 | 
						|
-- 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 iterator which aborts with the first invocation.
 | 
						|
function imatch(v)
 | 
						|
	if type(v) == "table" then
 | 
						|
		local k = nil
 | 
						|
		return function()
 | 
						|
			k = next(v, k)
 | 
						|
			return v[k]
 | 
						|
		end
 | 
						|
 | 
						|
	elseif type(v) == "number" or type(v) == "boolean" then
 | 
						|
		local x = true
 | 
						|
		return function()
 | 
						|
			if x then
 | 
						|
				x = false
 | 
						|
				return tostring(v)
 | 
						|
			end
 | 
						|
		end
 | 
						|
 | 
						|
	elseif type(v) == "userdata" or type(v) == "string" then
 | 
						|
		return tostring(v):gmatch("%S+")
 | 
						|
	end
 | 
						|
 | 
						|
	return function() end
 | 
						|
end
 | 
						|
 | 
						|
-- 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)
 | 
						|
--  o "w"	- one week   (60*60*24*7)
 | 
						|
--  o "d"	- one day    (60*60*24)
 | 
						|
--  o "h"	- one hour	 (60*60)
 | 
						|
--  o "min"	- one minute (60)
 | 
						|
--  o "kb"  - one kilobyte (1024)
 | 
						|
--  o "mb"	- one megabyte (1024*1024)
 | 
						|
--  o "gb"	- one gigabyte (1024*1024*1024)
 | 
						|
--  o "kib" - one si kilobyte (1000)
 | 
						|
--  o "mib"	- one si megabyte (1000*1000)
 | 
						|
--  o "gib"	- one si gigabyte (1000*1000*1000)
 | 
						|
function parse_units(ustr)
 | 
						|
 | 
						|
	local val = 0
 | 
						|
 | 
						|
	-- unit map
 | 
						|
	local map = {
 | 
						|
		-- date stuff
 | 
						|
		y   = 60 * 60 * 24 * 366,
 | 
						|
		m   = 60 * 60 * 24 * 31,
 | 
						|
		w   = 60 * 60 * 24 * 7,
 | 
						|
		d   = 60 * 60 * 24,
 | 
						|
		h   = 60 * 60,
 | 
						|
		min = 60,
 | 
						|
 | 
						|
		-- storage sizes
 | 
						|
		kb  = 1024,
 | 
						|
		mb  = 1024 * 1024,
 | 
						|
		gb  = 1024 * 1024 * 1024,
 | 
						|
 | 
						|
		-- storage sizes (si)
 | 
						|
		kib = 1000,
 | 
						|
		mib = 1000 * 1000,
 | 
						|
		gib = 1000 * 1000 * 1000
 | 
						|
	}
 | 
						|
 | 
						|
	-- parse input string
 | 
						|
	for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
 | 
						|
 | 
						|
		local num = spec:gsub("[^0-9%.]+$","")
 | 
						|
		local spn = spec:gsub("^[0-9%.]+", "")
 | 
						|
 | 
						|
		if map[spn] or map[spn:sub(1,1)] then
 | 
						|
			val = val + num * ( map[spn] or map[spn:sub(1,1)] )
 | 
						|
		else
 | 
						|
			val = val + num
 | 
						|
		end
 | 
						|
	end
 | 
						|
 | 
						|
 | 
						|
	return val
 | 
						|
end
 | 
						|
 | 
						|
-- also register functions above in the central string class for convenience
 | 
						|
string.pcdata      = pcdata
 | 
						|
string.striptags   = striptags
 | 
						|
string.split       = split
 | 
						|
string.trim        = trim
 | 
						|
string.cmatch      = cmatch
 | 
						|
string.parse_units = parse_units
 | 
						|
 | 
						|
 | 
						|
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
 | 
						|
 | 
						|
function combine(...)
 | 
						|
	return append({}, ...)
 | 
						|
end
 | 
						|
 | 
						|
function contains(table, value)
 | 
						|
	for k, v in pairs(table) do
 | 
						|
		if value == v then
 | 
						|
			return k
 | 
						|
		end
 | 
						|
	end
 | 
						|
	return false
 | 
						|
end
 | 
						|
 | 
						|
-- Both table are - in fact - merged together.
 | 
						|
function update(t, updates)
 | 
						|
	for k, v in pairs(updates) do
 | 
						|
		t[k] = v
 | 
						|
	end
 | 
						|
end
 | 
						|
 | 
						|
function keys(t)
 | 
						|
	local keys = { }
 | 
						|
	if t then
 | 
						|
		for k, _ in kspairs(t) do
 | 
						|
			keys[#keys+1] = k
 | 
						|
		end
 | 
						|
	end
 | 
						|
	return keys
 | 
						|
end
 | 
						|
 | 
						|
function clone(object, deep)
 | 
						|
	local copy = {}
 | 
						|
 | 
						|
	for k, v in pairs(object) do
 | 
						|
		if deep and type(v) == "table" then
 | 
						|
			v = clone(v, deep)
 | 
						|
		end
 | 
						|
		copy[k] = v
 | 
						|
	end
 | 
						|
 | 
						|
	return setmetatable(copy, getmetatable(object))
 | 
						|
end
 | 
						|
 | 
						|
 | 
						|
-- Serialize the contents of a table value.
 | 
						|
function _serialize_table(t, seen)
 | 
						|
	assert(not seen[t], "Recursion detected.")
 | 
						|
	seen[t] = true
 | 
						|
 | 
						|
	local data  = ""
 | 
						|
	local idata = ""
 | 
						|
	local ilen  = 0
 | 
						|
 | 
						|
	for k, v in pairs(t) do
 | 
						|
		if type(k) ~= "number" or k < 1 or math.floor(k) ~= k or ( k - #t ) > 3 then
 | 
						|
			k = serialize_data(k, seen)
 | 
						|
			v = serialize_data(v, seen)
 | 
						|
			data = data .. ( #data > 0 and ", " or "" ) ..
 | 
						|
				'[' .. k .. '] = ' .. v
 | 
						|
		elseif k > ilen then
 | 
						|
			ilen = k
 | 
						|
		end
 | 
						|
	end
 | 
						|
 | 
						|
	for i = 1, ilen do
 | 
						|
		local v = serialize_data(t[i], seen)
 | 
						|
		idata = idata .. ( #idata > 0 and ", " or "" ) .. v
 | 
						|
	end
 | 
						|
 | 
						|
	return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data
 | 
						|
end
 | 
						|
 | 
						|
-- with loadstring().
 | 
						|
function serialize_data(val, seen)
 | 
						|
	seen = seen or setmetatable({}, {__mode="k"})
 | 
						|
 | 
						|
	if val == nil then
 | 
						|
		return "nil"
 | 
						|
	elseif type(val) == "number" then
 | 
						|
		return val
 | 
						|
	elseif type(val) == "string" then
 | 
						|
		return "%q" % val
 | 
						|
	elseif type(val) == "boolean" then
 | 
						|
		return val and "true" or "false"
 | 
						|
	elseif type(val) == "function" then
 | 
						|
		return "loadstring(%q)" % get_bytecode(val)
 | 
						|
	elseif type(val) == "table" then
 | 
						|
		return "{ " .. _serialize_table(val, seen) .. " }"
 | 
						|
	else
 | 
						|
		return '"[unhandled data type:' .. type(val) .. ']"'
 | 
						|
	end
 | 
						|
end
 | 
						|
 | 
						|
function restore_data(str)
 | 
						|
	return loadstring("return " .. str)()
 | 
						|
end
 | 
						|
 | 
						|
 | 
						|
--
 | 
						|
-- Byte code manipulation routines
 | 
						|
--
 | 
						|
 | 
						|
-- will be stripped before it is returned.
 | 
						|
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
 | 
						|
 | 
						|
-- numbers and debugging numbers will be discarded. Original version by
 | 
						|
-- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
 | 
						|
function strip_bytecode(code)
 | 
						|
	local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12)
 | 
						|
	local subint
 | 
						|
	if endian == 1 then
 | 
						|
		subint = function(code, i, l)
 | 
						|
			local val = 0
 | 
						|
			for n = l, 1, -1 do
 | 
						|
				val = val * 256 + code:byte(i + n - 1)
 | 
						|
			end
 | 
						|
			return val, i + l
 | 
						|
		end
 | 
						|
	else
 | 
						|
		subint = function(code, i, l)
 | 
						|
			local val = 0
 | 
						|
			for n = 1, l, 1 do
 | 
						|
				val = val * 256 + code:byte(i + n - 1)
 | 
						|
			end
 | 
						|
			return val, i + l
 | 
						|
		end
 | 
						|
	end
 | 
						|
 | 
						|
	local function strip_function(code)
 | 
						|
		local count, offset = subint(code, 1, size)
 | 
						|
		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)
 | 
						|
		for n = 1, count do
 | 
						|
			local t
 | 
						|
			t, offset = subint(code, offset, 1)
 | 
						|
			if t == 1 then
 | 
						|
				offset = offset + 1
 | 
						|
			elseif t == 4 then
 | 
						|
				offset = offset + size + subint(code, offset, size)
 | 
						|
			elseif t == 3 then
 | 
						|
				offset = offset + num
 | 
						|
			elseif t == 254 or t == 9 then
 | 
						|
				offset = offset + lnum
 | 
						|
			end
 | 
						|
		end
 | 
						|
		count, offset = subint(code, offset, int)
 | 
						|
		stripped[#stripped+1] = code:sub(dirty, offset - 1)
 | 
						|
		for n = 1, count do
 | 
						|
			local proto, off = strip_function(code:sub(offset, -1))
 | 
						|
			stripped[#stripped+1] = proto
 | 
						|
			offset = offset + off - 1
 | 
						|
		end
 | 
						|
		offset = offset + subint(code, offset, int) * int + int
 | 
						|
		count, offset = subint(code, offset, int)
 | 
						|
		for n = 1, count do
 | 
						|
			offset = offset + subint(code, offset, size) + size + int * 2
 | 
						|
		end
 | 
						|
		count, offset = subint(code, offset, int)
 | 
						|
		for n = 1, count do
 | 
						|
			offset = offset + subint(code, offset, size) + size
 | 
						|
		end
 | 
						|
		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))
 | 
						|
end
 | 
						|
 | 
						|
 | 
						|
--
 | 
						|
-- Sorting iterator functions
 | 
						|
--
 | 
						|
 | 
						|
function _sortiter( t, f )
 | 
						|
	local keys = { }
 | 
						|
 | 
						|
	local k, v
 | 
						|
	for k, v in pairs(t) do
 | 
						|
		keys[#keys+1] = k
 | 
						|
	end
 | 
						|
 | 
						|
	local _pos = 0
 | 
						|
 | 
						|
	table.sort( keys, f )
 | 
						|
 | 
						|
	return function()
 | 
						|
		_pos = _pos + 1
 | 
						|
		if _pos <= #keys then
 | 
						|
			return keys[_pos], t[keys[_pos]], _pos
 | 
						|
		end
 | 
						|
	end
 | 
						|
end
 | 
						|
 | 
						|
-- the provided callback function.
 | 
						|
function spairs(t,f)
 | 
						|
	return _sortiter( t, f )
 | 
						|
end
 | 
						|
 | 
						|
-- The table pairs are sorted by key.
 | 
						|
function kspairs(t)
 | 
						|
	return _sortiter( t )
 | 
						|
end
 | 
						|
 | 
						|
-- The table pairs are sorted by value.
 | 
						|
function vspairs(t)
 | 
						|
	return _sortiter( t, function (a,b) return t[a] < t[b] end )
 | 
						|
end
 | 
						|
 | 
						|
 | 
						|
--
 | 
						|
-- System utility functions
 | 
						|
--
 | 
						|
 | 
						|
function bigendian()
 | 
						|
	return string.byte(string.dump(function() end), 7) == 0
 | 
						|
end
 | 
						|
 | 
						|
function exec(command)
 | 
						|
	local pp   = io.popen(command)
 | 
						|
	local data = pp:read("*a")
 | 
						|
	pp:close()
 | 
						|
 | 
						|
	return data
 | 
						|
end
 | 
						|
 | 
						|
function execi(command)
 | 
						|
	local pp = io.popen(command)
 | 
						|
 | 
						|
	return pp and function()
 | 
						|
		local line = pp:read()
 | 
						|
 | 
						|
		if not line then
 | 
						|
			pp:close()
 | 
						|
		end
 | 
						|
 | 
						|
		return line
 | 
						|
	end
 | 
						|
end
 | 
						|
 | 
						|
-- Deprecated
 | 
						|
function execl(command)
 | 
						|
	local pp   = io.popen(command)
 | 
						|
	local line = ""
 | 
						|
	local data = {}
 | 
						|
 | 
						|
	while true do
 | 
						|
		line = pp:read()
 | 
						|
		if (line == nil) then break end
 | 
						|
		data[#data+1] = line
 | 
						|
	end
 | 
						|
	pp:close()
 | 
						|
 | 
						|
	return data
 | 
						|
end
 | 
						|
 | 
						|
 | 
						|
local ubus_codes = {
 | 
						|
	"INVALID_COMMAND",
 | 
						|
	"INVALID_ARGUMENT",
 | 
						|
	"METHOD_NOT_FOUND",
 | 
						|
	"NOT_FOUND",
 | 
						|
	"NO_DATA",
 | 
						|
	"PERMISSION_DENIED",
 | 
						|
	"TIMEOUT",
 | 
						|
	"NOT_SUPPORTED",
 | 
						|
	"UNKNOWN_ERROR",
 | 
						|
	"CONNECTION_FAILED"
 | 
						|
}
 | 
						|
 | 
						|
local function ubus_return(...)
 | 
						|
	if select('#', ...) == 2 then
 | 
						|
		local rv, err = select(1, ...), select(2, ...)
 | 
						|
		if rv == nil and type(err) == "number" then
 | 
						|
			return nil, err, ubus_codes[err]
 | 
						|
		end
 | 
						|
	end
 | 
						|
 | 
						|
	return ...
 | 
						|
end
 | 
						|
 | 
						|
function ubus(object, method, data)
 | 
						|
	if not _ubus_connection then
 | 
						|
		_ubus_connection = _ubus.connect()
 | 
						|
		assert(_ubus_connection, "Unable to establish ubus connection")
 | 
						|
	end
 | 
						|
 | 
						|
	if object and method then
 | 
						|
		if type(data) ~= "table" then
 | 
						|
			data = { }
 | 
						|
		end
 | 
						|
		return ubus_return(_ubus_connection:call(object, method, data))
 | 
						|
	elseif object then
 | 
						|
		return _ubus_connection:signatures(object)
 | 
						|
	else
 | 
						|
		return _ubus_connection:objects()
 | 
						|
	end
 | 
						|
end
 | 
						|
 | 
						|
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')
 | 
						|
	local haveexe = fs.access(fullpathexe)
 | 
						|
	if not haveldd or not haveexe then
 | 
						|
		return false
 | 
						|
	end
 | 
						|
	local libs = exec(string.format("/usr/bin/ldd %s", shellquote(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
 | 
						|
--
 | 
						|
-- Encapsulates the protected calls with a coroutine based loop, so errors can
 | 
						|
-- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines
 | 
						|
-- yielding inside the call to pcall or xpcall.
 | 
						|
--
 | 
						|
-- Authors: Roberto Ierusalimschy and Andre Carregal
 | 
						|
-- Contributors: Thomas Harning Jr., Ignacio Burgueño, Fabio Mascarenhas
 | 
						|
--
 | 
						|
-- Copyright 2005 - Kepler Project
 | 
						|
--
 | 
						|
-- $Id: coxpcall.lua,v 1.13 2008/05/19 19:20:02 mascarenhas Exp $
 | 
						|
-------------------------------------------------------------------------------
 | 
						|
 | 
						|
-------------------------------------------------------------------------------
 | 
						|
-- Implements xpcall with coroutines
 | 
						|
-------------------------------------------------------------------------------
 | 
						|
local coromap = setmetatable({}, { __mode = "k" })
 | 
						|
 | 
						|
local function handleReturnValue(err, co, status, ...)
 | 
						|
	if not status then
 | 
						|
		return false, err(debug.traceback(co, (...)), ...)
 | 
						|
	end
 | 
						|
	if coroutine.status(co) == 'suspended' then
 | 
						|
		return performResume(err, co, coroutine.yield(...))
 | 
						|
	else
 | 
						|
		return true, ...
 | 
						|
	end
 | 
						|
end
 | 
						|
 | 
						|
function performResume(err, co, ...)
 | 
						|
	return handleReturnValue(err, co, coroutine.resume(co, ...))
 | 
						|
end
 | 
						|
 | 
						|
local function id(trace, ...)
 | 
						|
	return trace
 | 
						|
end
 | 
						|
 | 
						|
function coxpcall(f, err, ...)
 | 
						|
	local current = coroutine.running()
 | 
						|
	if not current then
 | 
						|
		if err == id then
 | 
						|
			return pcall(f, ...)
 | 
						|
		else
 | 
						|
			if select("#", ...) > 0 then
 | 
						|
				local oldf, params = f, { ... }
 | 
						|
				f = function() return oldf(unpack(params)) end
 | 
						|
			end
 | 
						|
			return xpcall(f, err)
 | 
						|
		end
 | 
						|
	else
 | 
						|
		local res, co = pcall(coroutine.create, f)
 | 
						|
		if not res then
 | 
						|
			local newf = function(...) return f(...) end
 | 
						|
			co = coroutine.create(newf)
 | 
						|
		end
 | 
						|
		coromap[co] = current
 | 
						|
		coxpt[co] = coxpt[current] or current or 0
 | 
						|
		return performResume(err, co, ...)
 | 
						|
	end
 | 
						|
end
 | 
						|
 | 
						|
function copcall(f, ...)
 | 
						|
	return coxpcall(f, id, ...)
 | 
						|
end
 |