diff --git a/doc/18-library-reference.md b/doc/18-library-reference.md
index 0a3f91225..4c82ab4c1 100644
--- a/doc/18-library-reference.md
+++ b/doc/18-library-reference.md
@@ -16,7 +16,10 @@ Signature:
function regex(pattern, text)
-Returns true if the regular expression matches the text, false otherwise.
+Returns true if the regular expression matches the text, false otherwise. The mode argument is
+optional and can be either MatchAll (in which case all elements for an array have to match) or MatchAny
+(in which case at least one element has to match). The default mode is MatchAll.
+
**Tip**: In case you are looking for regular expression tests try [regex101](https://regex101.com).
Example:
@@ -34,9 +37,11 @@ Example:
Signature:
- function match(pattern, text)
+ function match(pattern, text, mode)
-Returns true if the wildcard (`?*`) pattern matches the text, false otherwise.
+Returns true if the wildcard (`?*`) pattern matches the text, false otherwise. The mode argument is
+optional and can be either MatchAll (in which case all elements for an array have to match) or MatchAny
+(in which case at least one element has to match). The default mode is MatchAll.
Example:
@@ -59,7 +64,10 @@ Signature:
Returns true if the CIDR pattern matches the IP address, false otherwise.
IPv4 addresses are converted to IPv4-mapped IPv6 addresses before being
-matched against the pattern.
+matched against the pattern. The mode argument is optional and can be
+either MatchAll (in which case all elements for an array have to match) or MatchAny
+(in which case at least one element has to match). The default mode is MatchAll.
+
Example:
@@ -1148,6 +1156,24 @@ Signature:
Returns a copy of the array containing only the elements for which `func(element)`
is true.
+### Array#any
+
+Signature:
+
+ function any(func);
+
+Returns true if the array contains at least one element for which `func(element)`
+is true, false otherwise.
+
+### Array#all
+
+Signature:
+
+ function all(func);
+
+Returns true if the array contains only elements for which `func(element)`
+is true, false otherwise.
+
### Array#unique
Signature:
@@ -1220,6 +1246,14 @@ Signature:
Returns a list of keys for all items that are currently in the dictionary.
+### Dictionary#values
+
+Signature:
+
+ function values();
+
+Returns a list of values for all items that are currently in the dictionary.
+
## Function type
Inherits methods from the [Object type](18-library-reference.md#object-type).
diff --git a/lib/base/array-script.cpp b/lib/base/array-script.cpp
index 03ae2cae8..568037d9e 100644
--- a/lib/base/array-script.cpp
+++ b/lib/base/array-script.cpp
@@ -207,6 +207,43 @@ static Array::Ptr ArrayFilter(const Function::Ptr& function)
return result;
}
+static bool ArrayAny(const Function::Ptr& function)
+{
+ ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
+ Array::Ptr self = static_cast(vframe->Self);
+
+ if (vframe->Sandboxed && !function->IsSideEffectFree())
+ BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free."));
+
+ ObjectLock olock(self);
+ for (const Value& item : self) {
+ std::vector args;
+ args.push_back(item);
+ if (function->Invoke(args))
+ return true;
+ }
+
+ return false;
+}
+
+static bool ArrayAll(const Function::Ptr& function)
+{
+ ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
+ Array::Ptr self = static_cast(vframe->Self);
+
+ if (vframe->Sandboxed && !function->IsSideEffectFree())
+ BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free."));
+
+ ObjectLock olock(self);
+ for (const Value& item : self) {
+ std::vector args;
+ args.push_back(item);
+ if (!function->Invoke(args))
+ return false;
+ }
+
+ return true;
+}
static Array::Ptr ArrayUnique(void)
{
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
@@ -242,6 +279,8 @@ Object::Ptr Array::GetPrototype(void)
prototype->Set("map", new Function("Array#map", WrapFunction(ArrayMap), { "func" }, true));
prototype->Set("reduce", new Function("Array#reduce", WrapFunction(ArrayReduce), { "reduce" }, true));
prototype->Set("filter", new Function("Array#filter", WrapFunction(ArrayFilter), { "func" }, true));
+ prototype->Set("any", new Function("Array#any", WrapFunction(ArrayAny), { "func" }, true));
+ prototype->Set("all", new Function("Array#all", WrapFunction(ArrayAll), { "func" }, true));
prototype->Set("unique", new Function("Array#unique", WrapFunction(ArrayUnique), {}, true));
}
diff --git a/lib/base/dictionary-script.cpp b/lib/base/dictionary-script.cpp
index 908b719b2..66aa26cbd 100644
--- a/lib/base/dictionary-script.cpp
+++ b/lib/base/dictionary-script.cpp
@@ -79,6 +79,18 @@ static Array::Ptr DictionaryKeys(void)
return keys;
}
+static Array::Ptr DictionaryValues(void)
+{
+ ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
+ Dictionary::Ptr self = static_cast(vframe->Self);
+ Array::Ptr keys = new Array();
+ ObjectLock olock(self);
+ for (const Dictionary::Pair& kv : self) {
+ keys->Add(kv.second);
+ }
+ return keys;
+}
+
Object::Ptr Dictionary::GetPrototype(void)
{
static Dictionary::Ptr prototype;
@@ -92,6 +104,7 @@ Object::Ptr Dictionary::GetPrototype(void)
prototype->Set("contains", new Function("Dictionary#contains", WrapFunction(DictionaryContains), { "key" }, true));
prototype->Set("shallow_clone", new Function("Dictionary#shallow_clone", WrapFunction(DictionaryShallowClone), {}, true));
prototype->Set("keys", new Function("Dictionary#keys", WrapFunction(DictionaryKeys), {}, true));
+ prototype->Set("values", new Function("Dictionary#values", WrapFunction(DictionaryValues), {}, true));
}
return prototype;
diff --git a/lib/base/scriptutils.cpp b/lib/base/scriptutils.cpp
index 9cbdc1c5f..ef70eccbf 100644
--- a/lib/base/scriptutils.cpp
+++ b/lib/base/scriptutils.cpp
@@ -27,6 +27,7 @@
#include "base/configtype.hpp"
#include "base/application.hpp"
#include "base/dependencygraph.hpp"
+#include "base/initialize.hpp"
#include
#include
#include
@@ -36,9 +37,9 @@
using namespace icinga;
-REGISTER_SAFE_SCRIPTFUNCTION_NS(System, regex, &ScriptUtils::Regex, "pattern:text");
-REGISTER_SAFE_SCRIPTFUNCTION_NS(System, match, &Utility::Match, "pattern:text");
-REGISTER_SAFE_SCRIPTFUNCTION_NS(System, cidr_match, &Utility::CidrMatch, "pattern:ip");
+REGISTER_SAFE_SCRIPTFUNCTION_NS(System, regex, &ScriptUtils::Regex, "pattern:text:mode");
+REGISTER_SAFE_SCRIPTFUNCTION_NS(System, match, &ScriptUtils::Match, "pattern:text:mode");
+REGISTER_SAFE_SCRIPTFUNCTION_NS(System, cidr_match, &ScriptUtils::CidrMatch, "pattern:ip:mode");
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, len, &ScriptUtils::Len, "value");
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, union, &ScriptUtils::Union, "");
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, intersection, &ScriptUtils::Intersection, "");
@@ -67,6 +68,20 @@ REGISTER_SAFE_SCRIPTFUNCTION_NS(System, escape_create_process_arg, &Utility::Esc
REGISTER_SCRIPTFUNCTION_NS(System, ptr, &ScriptUtils::Ptr, "object");
REGISTER_SCRIPTFUNCTION_NS(System, sleep, &Utility::Sleep, "interval");
+INITIALIZE_ONCE(&ScriptUtils::StaticInitialize);
+
+enum MatchType
+{
+ MatchAll,
+ MatchAny
+};
+
+void ScriptUtils::StaticInitialize(void)
+{
+ ScriptGlobal::Set("MatchAll", MatchAll);
+ ScriptGlobal::Set("MatchAny", MatchAny);
+}
+
String ScriptUtils::CastString(const Value& value)
{
return value;
@@ -81,18 +96,120 @@ bool ScriptUtils::CastBool(const Value& value)
{
return value.ToBool();
}
-bool ScriptUtils::Regex(const String& pattern, const String& text)
+
+bool ScriptUtils::Regex(const std::vector& args)
{
- bool res = false;
- try {
- boost::regex expr(pattern.GetData());
- boost::smatch what;
- res = boost::regex_search(text.GetData(), what, expr);
- } catch (boost::exception&) {
- res = false; /* exception means something went terribly wrong */
+ if (args.size() < 2)
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Regular expression and text must be specified."));
+
+ Array::Ptr texts = new Array();
+
+ String pattern = args[0];
+ Value argTexts = args[1];
+ MatchType mode;
+
+ if (args.size() > 2)
+ mode = static_cast(static_cast(args[2]));
+ else
+ mode = MatchAll;
+
+ if (argTexts.IsObjectType())
+ texts = argTexts;
+ else {
+ texts = new Array();
+ texts->Add(argTexts);
}
- return res;
+ ObjectLock olock(texts);
+ for (const String& text : texts) {
+ bool res = false;
+ try {
+ boost::regex expr(pattern.GetData());
+ boost::smatch what;
+ res = boost::regex_search(text.GetData(), what, expr);
+ } catch (boost::exception&) {
+ res = false; /* exception means something went terribly wrong */
+ }
+
+ if (mode == MatchAny && res)
+ return true;
+ else if (mode == MatchAll && !res)
+ return false;
+ }
+
+ return mode == MatchAll;
+}
+
+bool ScriptUtils::Match(const std::vector& args)
+{
+ if (args.size() < 2)
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Pattern and text must be specified."));
+
+ Array::Ptr texts = new Array();
+
+ String pattern = args[0];
+ Value argTexts = args[1];
+ MatchType mode;
+
+ if (args.size() > 2)
+ mode = static_cast(static_cast(args[2]));
+ else
+ mode = MatchAll;
+
+ if (argTexts.IsObjectType())
+ texts = argTexts;
+ else {
+ texts = new Array();
+ texts->Add(argTexts);
+ }
+
+ ObjectLock olock(texts);
+ for (const String& text : texts) {
+ bool res = Utility::Match(pattern, text);
+
+ if (mode == MatchAny && res)
+ return true;
+ else if (mode == MatchAll && !res)
+ return false;
+ }
+
+ return mode == MatchAll;
+}
+
+bool ScriptUtils::CidrMatch(const std::vector& args)
+{
+ if (args.size() < 2)
+ BOOST_THROW_EXCEPTION(std::invalid_argument("CIDR and IP address must be specified."));
+
+ Array::Ptr ips = new Array();
+
+ String pattern = args[0];
+ Value argIps = args[1];
+ MatchType mode;
+
+ if (args.size() > 2)
+ mode = static_cast(static_cast(args[2]));
+ else
+ mode = MatchAll;
+
+ if (argIps.IsObjectType())
+ ips = argIps;
+ else {
+ ips = new Array();
+ ips->Add(argIps);
+ }
+
+ ObjectLock olock(ips);
+ for (const String& ip : ips) {
+ bool res = Utility::CidrMatch(pattern, ip);
+
+ if (mode == MatchAny && res)
+ return true;
+ else if (mode == MatchAll && !res)
+ return false;
+ }
+
+ return mode == MatchAll;
}
double ScriptUtils::Len(const Value& value)
diff --git a/lib/base/scriptutils.hpp b/lib/base/scriptutils.hpp
index fd4333241..e605be5fa 100644
--- a/lib/base/scriptutils.hpp
+++ b/lib/base/scriptutils.hpp
@@ -36,10 +36,13 @@ namespace icinga
class I2_BASE_API ScriptUtils
{
public:
+ static void StaticInitialize(void);
static String CastString(const Value& value);
static double CastNumber(const Value& value);
static bool CastBool(const Value& value);
- static bool Regex(const String& pattern, const String& text);
+ static bool Regex(const std::vector& args);
+ static bool Match(const std::vector& args);
+ static bool CidrMatch(const std::vector& args);
static double Len(const Value& value);
static Array::Ptr Union(const std::vector& arguments);
static Array::Ptr Intersection(const std::vector& arguments);