ntopng/scripts/lua/modules/lua_utils_generic.lua
Alfredo Cardigliano 15509c3468 Improve cli hint
2023-06-20 10:45:07 +02:00

496 lines
12 KiB
Lua

--
-- (C) 2014-22 - ntop.org
--
local clock_start = os.clock()
-- GENERIC UTILS
-- split
function split(s, delimiter)
result = {};
if(s ~= nil) then
if delimiter == nil then
-- No delimiter, split all characters
for match in s:gmatch"." do
table.insert(result, match);
end
else
-- Split by delimiter
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
end
end
end
return result;
end
-- startswith
function startswith(s, char)
return string.sub(s, 1, string.len(s)) == char
end
-- endswith
function endswith(s, char)
return string.sub(s, -#char) == char
end
-- strsplit
function strsplit(s, delimiter)
result = {};
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
if(match ~= "") then result[match] = true end
end
return result;
end
-- isempty
function isempty(array)
local count = 0
for _,__ in pairs(array) do
count = count + 1
end
return (count == 0)
end
-- isin
function isin(s, array)
if (s == nil or s == "" or array == nil or isempty(array)) then return false end
for _, v in pairs(array) do
if (s == v) then return true end
end
return false
end
-- hasKey
function hasKey(key, theTable)
if((theTable == nil) or (theTable[key] == nil)) then
return(false)
else
return(true)
end
end
-- ###############################################
-- removes trailing/leading spaces
function trimString(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
-- ###############################################
-- removes all spaces
function trimSpace(what)
if(what == nil) then return("") end
return(string.gsub(string.gsub(what, "%s+", ""), "+%s", ""))
end
-- ###############################################
-- TODO: improve this function
function jsonencode(what)
what = string.gsub(what, '"', "'")
-- everything but all ASCII characters from the space to the tilde
what = string.gsub(what, "[^ -~]", " ")
-- cleanup line feeds and carriage returns
what = string.gsub(what, "\n", " ")
what = string.gsub(what, "\r", " ")
-- escape all the remaining backslashes
what = string.gsub(what, "\\", "\\\\")
-- max 1 sequential whitespace
what = string.gsub(what, " +"," ")
return(what)
end
-- ###########################################
-- Merges table a and table b into a new table. If some elements are presents in
-- both a and b, b elements will have precedence.
-- NOTE: this does *not* perform a deep merge. Only first level is merged.
function table.merge(a, b)
local merged = {}
a = a or {}
b = b or {}
if((a[1] ~= nil) and (b[1] ~= nil)) then
-- index based tables
for _, t in ipairs({a, b}) do
for _,v in pairs(t) do
merged[#merged + 1] = v
end
end
else
-- key based tables
for _, t in ipairs({a, b}) do
for k,v in pairs(t) do
merged[k] = v
end
end
end
return merged
end
-- ###########################################
-- Performs a deep copy of the table.
function table.clone(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[table.clone(orig_key)] = table.clone(orig_value)
end
setmetatable(copy, table.clone(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
-- ###########################################
-- From http://lua-users.org/lists/lua-l/2014-09/msg00421.html
-- Returns true if tables are equal
function table.compare(t1, t2, ignore_mt)
local ty1 = type(t1)
local ty2 = type(t2)
if ty1 ~= ty2 then return false end
if ty1 ~= 'table' and ty2 ~= 'table' then return t1 == t2 end
local mt = getmetatable(t1)
if not ignore_mt and mt and mt.__eq then return t1 == t2 end
for k1,v1 in pairs(t1) do
local v2 = t2[k1]
if v2 == nil or not table.compare(v1, v2) then return false end
end
for k2,v2 in pairs(t2) do
local v1 = t1[k2]
if v1 == nil or not table.compare(v1, v2) then return false end
end
return true
end
-- ##############################################
-- returns the MAXIMUM value found in a table t, together with the corresponding
-- index argmax. a pair argmax, max is returned.
function tmax(t)
local argmx, mx = nil, nil
if (type(t) ~= "table") then return nil, nil end
for k, v in pairs(t) do
-- first iteration
if mx == nil and argmx == nil then
mx = v
argmx = k
elseif (v == mx and k > argmx) or v > mx then
-- if there is a tie, prefer the greatest argument
-- otherwise grab the maximum
argmx = k
mx = v
end
end
return argmx, mx
end
-- ##############################################
-- returns the MINIMUM value found in a table t, together with the corresponding
-- index argmin. a pair argmin, min is returned.
function tmin(t)
local argmn, mn = nil, nil
if (type(t) ~= "table") then return nil, nil end
for k, v in pairs(t) do
-- first iteration
if mn == nil and argmn == nil then
mn = v
argmn = k
elseif (v == mn and k > argmn) or v < mn then
-- if there is a tie, prefer the greatest argument
-- otherwise grab the minimum
argmn = k
mn = v
end
end
return argmn, mn
end
-- ###########################################
function toboolean(s)
if not isEmptyString(s) and (
s == "true" or
s == true or
s == "1" or
s == 1) then
return true
else
return false
end
end
-- ###########################################
--
-- Find the highest divisor which divides input value.
-- val_idx can be used to index divisors values.
-- Returns the highest_idx
--
function highestDivisor(divisors, value, val_idx, iterator_fn)
local highest_idx = nil
local highest_val = nil
iterator_fn = iterator_fn or ipairs
for i, v in iterator_fn(divisors) do
local cmp_v
if val_idx ~= nil then
v = v[val_idx]
end
if((highest_val == nil) or ((v > highest_val) and (value % v == 0))) then
highest_val = v
highest_idx = i
end
end
return highest_idx
end
-- ###########################################
--- Test if each element inside the table t satisfies the predicate function
--- @param t table The table containing values to test
--- @param predicate function The function that return a boolean value (true|false)
--- @return boolean
function table.all(t, predicate)
if type(t) ~= 'table' then
traceError(TRACE_DEBUG, TRACE_CONSOLE, "the first paramater is not a table!")
return false
end
if type(predicate) ~= 'function' then
traceError(TRACE_DEBUG, TRACE_CONSOLE, "the passed predicate is not a function!")
return false
end
if t == nil then return false end
for _, value in pairs(t) do
-- check if the value satisfies the boolean predicate
local term = predicate(value)
-- if the return value is valid and true then do nothing
-- otherwise stop the loop and return false
if term == nil then
-- inform the client about the nil value
traceError(TRACE_DEBUG, TRACE_CONSOLE, "a null term has been returned from the predicate function!")
return false
elseif not term then
return false
end
end
-- each entry satisfies the predicate
return true
end
-- ###########################################
--- Perform a linear search to check if an element is inside a table
--- @param t table The table to scan
--- @param needle any The element to search
--- @param comp function The compare function used to compare the searched element with others
--- @return boolean True if the element is insie the table, False otherwise
function table.contains(t, needle, comp)
if (t == nil) then return false end
if (type(t) ~= "table") then return false end
if (#t == 0) then return false end
local default_compare = (function(e) return e == needle end)
comp = comp or default_compare
for _, element in ipairs(t) do
if comp(element) then return true end
end
return false
end
-- ###########################################
--- Insert an element inside the table if is not present
function table.insertIfNotPresent(t, element, comp)
if table.contains(t, element, comp) then return end
t[#t+1] = element
end
-- ###########################################
--- Fold right table with a custom function
--- @param t table Table to fold
--- @param func function Function to execute on table values
--- @param val any The returned default value
function table.foldr(t, func, val)
for i,v in pairs(t) do
val = func(val, v)
end
return val
end
-- ###########################################
function table.has_key(table, key)
return table[key] ~= nil
end
function table.slice(t, start_table, end_table)
if t == nil then
error("The array to slice cannot be nil!")
end
if end_table > #t then
end_table = #t
end
if start_table < 1 then
error("Invalid bounds!")
end
local res = {}
for i = start_table, end_table, 1 do
res[#res + 1] = t[i]
end
return res
end
if(trace_script_duration ~= nil) then
io.write(debug.getinfo(1,'S').source .." executed in ".. (os.clock()-clock_start)*1000 .. " ms\n")
end
-- ##############################################
-- Note: Regexs are applied by default. Pass plain=true to disable them.
function string.contains(str, start, is_plain)
if type(str) ~= 'string' or type(start) ~= 'string' or isEmptyString(str) or isEmptyString(start) then
return false
end
local i, _ = string.find(str, start, 1, is_plain)
return(i ~= nil)
end
-- ##############################################
function string.containsIgnoreCase(str, start, is_plain)
return string.contains(string.lower(str), string.lower(start), is_plain)
end
-- ##############################################
function shortenString(name, max_len)
local ellipsis = "\u{2026}" -- The unicode ellipsis (takes less space than three separate dots)
if(name == nil) then return("") end
if max_len == nil then
max_len = ntop.getPref("ntopng.prefs.max_ui_strlen")
max_len = tonumber(max_len)
if(max_len == nil) then max_len = 24 end
end
-- Error, max_len is not a number, print an error and return the name
if not tonumber(max_len) then
traceError(TRACE_DEBUG, TRACE_CONSOLE, "Length parameter is not a number.")
tprint(debug.traceback())
return name
end
if(string.len(name) < max_len + 1 --[[ The space taken by the ellipsis --]]) then
return(name)
else
return(string.sub(name, 1, max_len)..ellipsis)
end
end
-- ##############################################
function convertDate(vardate)
local m,d,y,h,i,s = string.match(vardate, '(%d+)/(%d+)/(%d+) (%d+):(%d+):(%d+)')
local key = ntop.getPref('ntopng.user.' .. _SESSION["user"] .. '.date_format')
if(key == "little_endian") then
return string.format('%s/%s/%s %s:%s:%s', d,m,y,h,i,s)
elseif( key == "middle_endian") then
return string.format('%s/%s/%s %s:%s:%s', m,d,y,h,i,s)
else
return string.format('%s/%s/%s %s:%s:%s', y,m,d,h,i,s)
end
end
-- ##############################################
-- compare two tables for equality
function table.is_equal(t1, t2, ignore_keys)
local ty1 = type(t1)
local ty2 = type(t2)
if ty1 ~= ty2 then
return false
end
-- non-table types can be directly compared
if ty1 ~= 'table' and ty2 ~= 'table' then
return t1 == t2
end
-- as well as tables which have the metamethod __eq
local mt = getmetatable(t1)
if mt and mt.__eq then return t1 == t2 end
for k1,v1 in pairs(t1) do
if((ignore_keys == nil) or (ignore_keys[k1] == nil)) then
local v2 = t2[k1]
if v2 == nil or not table.is_equal(v1,v2) then
--tprint("Change in key (1) "..k1)
if(k1 == "ntopng.prefs.snmp_configuration") then
-- tprint(v1)
-- tprint(v2)
end
return false
end
end
end
for k2,v2 in pairs(t2) do
if((ignore_keys == nil) or (ignore_keys[k2] == nil)) then
local v1 = t1[k2]
if v1 == nil or not table.is_equal(v1,v2) then
-- tprint("Change in key (2) "..k2)
return false
end
end
end
return true
end
-- ##############################################
if(trace_script_duration ~= nil) then
io.write(debug.getinfo(1,'S').source .." executed in ".. (os.clock()-clock_start)*1000 .. " ms\n")
end