Add plist_*_val_compare, plist_*_val_contains, etc. for the respective node types

... except container node types like PLIST_ARRAY or PLIST_DICT.
diff --git a/configure.ac b/configure.ac
index 084095c..663aaef 100644
--- a/configure.ac
+++ b/configure.ac
@@ -54,7 +54,7 @@
 AC_TYPE_UINT8_T
 
 # Checks for library functions.
-AC_CHECK_FUNCS([asprintf strcasecmp strdup strerror strndup stpcpy vasprintf gmtime_r localtime_r timegm strptime])
+AC_CHECK_FUNCS([asprintf strcasecmp strdup strerror strndup stpcpy vasprintf gmtime_r localtime_r timegm strptime memmem])
 
 # Checking endianness
 AC_C_BIGENDIAN([AC_DEFINE([__BIG_ENDIAN__], [1], [big endian])],
diff --git a/include/plist/plist.h b/include/plist/plist.h
index 29b1fce..ab91612 100644
--- a/include/plist/plist.h
+++ b/include/plist/plist.h
@@ -762,6 +762,189 @@
     #define PLIST_IS_KEY(__plist)     _PLIST_IS_TYPE(__plist, KEY)
     #define PLIST_IS_UID(__plist)     _PLIST_IS_TYPE(__plist, UID)
 
+    /**
+     * Helper function to check the value of a PLIST_BOOL node.
+     *
+     * @param boolnode node of type PLIST_BOOL
+     * @return 1 if the boolean node has a value of TRUE, 0 if FALSE,
+     *   or -1 if the node is not of type PLIST_BOOL
+     */
+    int plist_bool_val_is_true(plist_t boolnode);
+
+    /**
+     * Helper function to compare the value of a PLIST_UINT node against
+     * a given value.
+     *
+     * @param uintnode node of type PLIST_UINT
+     * @param cmpval value to compare against
+     * @return 0 if the node's value and cmpval are equal,
+     *         1 if the node's value is greater than cmpval,
+     *         or -1 if the node's value is less than cmpval.
+     */
+    int plist_uint_val_compare(plist_t uintnode, uint64_t cmpval);
+
+    /**
+     * Helper function to compare the value of a PLIST_UID node against
+     * a given value.
+     *
+     * @param uidnode node of type PLIST_UID
+     * @param cmpval value to compare against
+     * @return 0 if the node's value and cmpval are equal,
+     *         1 if the node's value is greater than cmpval,
+     *         or -1 if the node's value is less than cmpval.
+     */
+    int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval);
+
+    /**
+     * Helper function to compare the value of a PLIST_REAL node against
+     * a given value.
+     *
+     * @note WARNING: Comparing floating point values can give inaccurate
+     *     results because of the nature of floating point values on computer
+     *     systems. While this function is designed to be as accurate as
+     *     possible, please don't rely on it too much.
+     *
+     * @param realnode node of type PLIST_REAL
+     * @param cmpval value to compare against
+     * @return 0 if the node's value and cmpval are (almost) equal,
+     *         1 if the node's value is greater than cmpval,
+     *         or -1 if the node's value is less than cmpval.
+     */
+    int plist_real_val_compare(plist_t realnode, double cmpval);
+
+    /**
+     * Helper function to compare the value of a PLIST_DATE node against
+     * a given set of seconds and fraction of a second since epoch.
+     *
+     * @param datenode node of type PLIST_DATE
+     * @param cmpsec number of seconds since epoch to compare against
+     * @param cmpusec fraction of a second in microseconds to compare against
+     * @return 0 if the node's date is equal to the supplied values,
+     *         1 if the node's date is greater than the supplied values,
+     *         or -1 if the node's date is less than the supplied values.
+     */
+    int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec);
+
+    /**
+     * Helper function to compare the value of a PLIST_STRING node against
+     * a given value.
+     * This function basically behaves like strcmp.
+     *
+     * @param strnode node of type PLIST_STRING
+     * @param cmpval value to compare against
+     * @return 0 if the node's value and cmpval are equal,
+     *     > 0 if the node's value is lexicographically greater than cmpval,
+     *     or < 0 if the node's value is lexicographically less than cmpval.
+     */
+    int plist_string_val_compare(plist_t strnode, const char* cmpval);
+
+    /**
+     * Helper function to compare the value of a PLIST_STRING node against
+     * a given value, while not comparing more than n characters.
+     * This function basically behaves like strncmp.
+     *
+     * @param strnode node of type PLIST_STRING
+     * @param cmpval value to compare against
+     * @param n maximum number of characters to compare
+     * @return 0 if the node's value and cmpval are equal,
+     *     > 0 if the node's value is lexicographically greater than cmpval,
+     *     or < 0 if the node's value is lexicographically less than cmpval.
+     */
+    int plist_string_val_compare_with_size(plist_t strnode, const char* cmpval, size_t n);
+
+    /**
+     * Helper function to match a given substring in the value of a
+     * PLIST_STRING node.
+     *
+     * @param strnode node of type PLIST_STRING
+     * @param substr value to match
+     * @return 1 if the node's value contains the given substring,
+     *     or 0 if not.
+     */
+    int plist_string_val_contains(plist_t strnode, const char* substr);
+
+    /**
+     * Helper function to compare the value of a PLIST_KEY node against
+     * a given value.
+     * This function basically behaves like strcmp.
+     *
+     * @param keynode node of type PLIST_KEY
+     * @param cmpval value to compare against
+     * @return 0 if the node's value and cmpval are equal,
+     *     > 0 if the node's value is lexicographically greater than cmpval,
+     *     or < 0 if the node's value is lexicographically less than cmpval.
+     */
+    int plist_key_val_compare(plist_t keynode, const char* cmpval);
+
+    /**
+     * Helper function to compare the value of a PLIST_KEY node against
+     * a given value, while not comparing more than n characters.
+     * This function basically behaves like strncmp.
+     *
+     * @param keynode node of type PLIST_KEY
+     * @param cmpval value to compare against
+     * @param n maximum number of characters to compare
+     * @return 0 if the node's value and cmpval are equal,
+     *     > 0 if the node's value is lexicographically greater than cmpval,
+     *     or < 0 if the node's value is lexicographically less than cmpval.
+     */
+    int plist_key_val_compare_with_size(plist_t keynode, const char* cmpval, size_t n);
+
+    /**
+     * Helper function to match a given substring in the value of a
+     * PLIST_KEY node.
+     *
+     * @param keynode node of type PLIST_KEY
+     * @param substr value to match
+     * @return 1 if the node's value contains the given substring,
+     *     or 0 if not.
+     */
+    int plist_key_val_contains(plist_t keynode, const char* substr);
+
+    /**
+     * Helper function to compare the data of a PLIST_DATA node against
+     * a given blob and size.
+     * This function basically behaves like memcmp after making sure the
+     * size of the node's data value is equal to the size of cmpval (n),
+     * making this a "full match" comparison.
+     *
+     * @param datanode node of type PLIST_DATA
+     * @param cmpval data blob to compare against
+     * @param n size of data blob passed in cmpval
+     * @return 0 if the node's data blob and cmpval are equal,
+     *     > 0 if the node's value is lexicographically greater than cmpval,
+     *     or < 0 if the node's value is lexicographically less than cmpval.
+     */
+    int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, size_t n);
+
+    /**
+     * Helper function to compare the data of a PLIST_DATA node against
+     * a given blob and size, while no more than n bytes are compared.
+     * This function basically behaves like memcmp after making sure the
+     * size of the node's data value is at least n, making this a
+     * "starts with" comparison.
+     *
+     * @param datanode node of type PLIST_DATA
+     * @param cmpval data blob to compare against
+     * @param n size of data blob passed in cmpval
+     * @return 0 if the node's value and cmpval are equal,
+     *     > 0 if the node's value is lexicographically greater than cmpval,
+     *     or < 0 if the node's value is lexicographically less than cmpval.
+     */
+    int plist_data_val_compare_with_size(plist_t datanode, const uint8_t* cmpval, size_t n);
+
+    /**
+     * Helper function to match a given data blob within the value of a
+     * PLIST_DATA node.
+     *
+     * @param datanode node of type PLIST_KEY
+     * @param cmpval data blob to match
+     * @param n size of data blob passed in cmpval
+     * @return 1 if the node's value contains the given data blob
+     *     or 0 if not.
+     */
+    int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, size_t n);
+
     /*@}*/
 
 #ifdef __cplusplus
diff --git a/src/plist.c b/src/plist.c
index 87be488..f58148b 100644
--- a/src/plist.c
+++ b/src/plist.c
@@ -21,6 +21,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 
 #include <string.h>
 #include "plist.h"
@@ -29,6 +32,7 @@
 #include <math.h>
 #include <assert.h>
 #include <limits.h>
+#include <float.h>
 
 #ifdef WIN32
 #include <windows.h>
@@ -111,6 +115,55 @@
 
 #endif
 
+#ifndef HAVE_MEMMEM
+// see https://sourceware.org/legacy-ml/libc-alpha/2007-12/msg00000.html
+
+#ifndef _LIBC
+# define __builtin_expect(expr, val)   (expr)
+#endif
+
+#undef memmem
+
+/* Return the first occurrence of NEEDLE in HAYSTACK. */
+void* memmem(const void* haystack, size_t haystack_len, const void* needle, size_t needle_len)
+{
+    /* not really Rabin-Karp, just using additive hashing */
+    char* haystack_ = (char*)haystack;
+    char* needle_ = (char*)needle;
+    int hash = 0;  /* this is the static hash value of the needle */
+    int hay_hash = 0;  /* rolling hash over the haystack */
+    char* last;
+    size_t i;
+
+    if (haystack_len < needle_len)
+        return NULL;
+
+    if (!needle_len)
+        return haystack_;
+
+    /* initialize hashes */
+    for (i = needle_len; i; --i) {
+        hash += *needle_++;
+        hay_hash += *haystack_++;
+    }
+
+    /* iterate over the haystack */
+    haystack_ = (char*)haystack;
+    needle_ = (char*)needle;
+    last = haystack_+(haystack_len - needle_len + 1);
+    for (; haystack_ < last; ++haystack_) {
+        if (__builtin_expect(hash == hay_hash, 0)
+              && *haystack_ == *needle_ /* prevent calling memcmp, was a optimization from existing glibc */
+              && !memcmp (haystack_, needle_, needle_len)) {
+            return haystack_;
+        }
+        /* roll the hash */
+        hay_hash -= *haystack_;
+        hay_hash += *(haystack_+needle_len);
+    }
+    return NULL;
+}
+#endif
 
 PLIST_API int plist_is_binary(const char *plist_data, uint32_t length)
 {
@@ -1132,3 +1185,181 @@
     plist_set_element_val(node, PLIST_DATE, &val, sizeof(struct timeval));
 }
 
+PLIST_API int plist_boolean_is_true(plist_t boolnode)
+{
+    if (!PLIST_IS_BOOLEAN(boolnode)) {
+        return -1;
+    }
+    uint8_t bv = 0;
+    plist_get_bool_val(boolnode, &bv);
+    return (bv == 1);
+}
+
+PLIST_API int plist_uint_val_compare(plist_t uintnode, uint64_t cmpval)
+{
+    if (!PLIST_IS_UINT(uintnode)) {
+        return -1;
+    }
+    uint64_t uintval = 0;
+    plist_get_uint_val(uintnode, &uintval);
+    if (uintval == cmpval) {
+        return 0;
+    } else if (uintval < cmpval) {
+        return -1;
+    } else {
+        return 1;
+    }
+}
+
+PLIST_API int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval)
+{
+    if (!PLIST_IS_UID(uidnode)) {
+        return -1;
+    }
+    uint64_t uidval = 0;
+    plist_get_uid_val(uidnode, &uidval);
+    if (uidval == cmpval) {
+        return 0;
+    } else if (uidval < cmpval) {
+        return -1;
+    } else {
+        return 1;
+    }
+}
+
+PLIST_API int plist_real_val_compare(plist_t realnode, double cmpval)
+{
+    if (!PLIST_IS_REAL(realnode)) {
+        return -1;
+    }
+    double a = 0;
+    double b = cmpval;
+    plist_get_real_val(realnode, &a);
+    double abs_a = fabs(a);
+    double abs_b = fabs(b);
+    double diff = fabs(a - b);
+    if (a == b) {
+        return 0;
+    } else if (a == 0 || b == 0 || (abs_a + abs_b < DBL_MIN)) {
+        if (diff < (DBL_EPSILON * DBL_MIN)) {
+            return 0;
+        } else if (a < b) {
+            return -1;
+        }
+    } else {
+        if ((diff / fmin(abs_a + abs_b, DBL_MAX)) < DBL_EPSILON) {
+            return 0;
+        } else if (a < b) {
+            return -1;
+        }
+    }
+    return 1;
+}
+
+PLIST_API int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec)
+{
+    if (!PLIST_IS_DATE(datenode)) {
+        return -1;
+    }
+    int32_t sec = 0;
+    int32_t usec = 0;
+    plist_get_date_val(datenode, &sec, &usec);
+    uint64_t dateval = ((int64_t)sec << 32) | usec;
+    uint64_t cmpval = ((int64_t)cmpsec << 32) | cmpusec;
+    if (dateval == cmpval) {
+        return 0;
+    } else if (dateval < cmpval) {
+        return -1;
+    } else {
+        return 1;
+    }
+}
+
+PLIST_API int plist_string_val_compare(plist_t strnode, const char* cmpval)
+{
+    if (!PLIST_IS_STRING(strnode)) {
+        return -1;
+    }
+    plist_data_t data = plist_get_data(strnode);
+    return strcmp(data->strval, cmpval);
+}
+
+PLIST_API int plist_string_val_compare_with_size(plist_t strnode, const char* cmpval, size_t n)
+{
+    if (!PLIST_IS_STRING(strnode)) {
+        return -1;
+    }
+    plist_data_t data = plist_get_data(strnode);
+    return strncmp(data->strval, cmpval, n);
+}
+
+PLIST_API int plist_string_val_contains(plist_t strnode, const char* substr)
+{
+    if (!PLIST_IS_STRING(strnode)) {
+        return 0;
+    }
+    plist_data_t data = plist_get_data(strnode);
+    return (strstr(data->strval, substr) != NULL);
+}
+
+PLIST_API int plist_key_val_compare(plist_t keynode, const char* cmpval)
+{
+    if (!PLIST_IS_KEY(keynode)) {
+        return -1;
+    }
+    plist_data_t data = plist_get_data(keynode);
+    return strcmp(data->strval, cmpval);
+}
+
+PLIST_API int plist_key_val_compare_with_size(plist_t keynode, const char* cmpval, size_t n)
+{
+    if (!PLIST_IS_KEY(keynode)) {
+        return -1;
+    }
+    plist_data_t data = plist_get_data(keynode);
+    return strncmp(data->strval, cmpval, n);
+}
+
+PLIST_API int plist_key_val_contains(plist_t keynode, const char* substr)
+{
+    if (!PLIST_IS_KEY(keynode)) {
+        return 0;
+    }
+    plist_data_t data = plist_get_data(keynode);
+    return (strstr(data->strval, substr) != NULL);
+}
+
+PLIST_API int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, size_t n)
+{
+    if (!PLIST_IS_DATA(datanode)) {
+        return -1;
+    }
+    plist_data_t data = plist_get_data(datanode);
+    if (data->length < n) {
+        return -1;
+    } else if (data->length > n) {
+        return 1;
+    }
+    return memcmp(data->buff, cmpval, n);
+}
+
+PLIST_API int plist_data_val_compare_with_size(plist_t datanode, const uint8_t* cmpval, size_t n)
+{
+    if (!PLIST_IS_DATA(datanode)) {
+        return -1;
+    }
+    plist_data_t data = plist_get_data(datanode);
+    if (data->length < n) {
+        return -1;
+    }
+    return memcmp(data->buff, cmpval, n);
+}
+
+PLIST_API int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, size_t n)
+{
+    if (!PLIST_IS_DATA(datanode)) {
+        return -1;
+    }
+    plist_data_t data = plist_get_data(datanode);
+    return (memmem(data->buff, data->length, cmpval, n) != NULL);
+}