diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | include/plist/plist.h | 183 | ||||
-rw-r--r-- | src/plist.c | 231 |
3 files changed, 415 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac index 084095c..663aaef 100644 --- a/configure.ac +++ b/configure.ac @@ -54,7 +54,7 @@ AC_TYPE_UINT32_T 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 @@ extern "C" #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 @@ static void __attribute__((destructor)) libplist_deinitialize(void) #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_API void plist_set_date_val(plist_t node, int32_t sec, int32_t usec) 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); +} |