diff options
34 files changed, 1155 insertions, 348 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..e995b30 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +github: nikias +patreon: nikias +custom: ["https://www.paypal.me/NikiasBassen"] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68948af..233a584 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,7 @@ name: build on: push: + pull_request: schedule: - cron: '0 0 1 * *' @@ -50,7 +51,7 @@ jobs: else brew install libtool autoconf automake fi - pip3 install cython + pip3 install --break-system-packages cython shell: bash - uses: actions/checkout@v4 with: @@ -74,6 +75,7 @@ jobs: PYTHON_EXEC_PREFIX=`$PYTHON3_BIN -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('exec_prefix'))"` PYTHON_LIBS_PATH=$PYTHON_EXEC_PREFIX/lib PYTHON_FRAMEWORK_PATH=$PYTHON_EXEC_PREFIX/Python3 + export PYTHON_CPPFLAGS="-I$PYTHON_EXEC_PREFIX/Headers" export PYTHON_LIBS="-L$PYTHON_LIBS_PATH -lpython$PYTHON_VER" export PYTHON_EXTRA_LDFLAGS="-Wl,-stack_size,1000000 -framework CoreFoundation $PYTHON_FRAMEWORK_PATH" fi @@ -95,7 +97,7 @@ jobs: name: libplist-latest_macOS path: libplist.tar build-windows: - runs-on: windows-2019 + runs-on: windows-latest defaults: run: shell: msys2 {0} @@ -116,6 +118,8 @@ jobs: base-devel git mingw-w64-${{ matrix.arch }}-gcc + mingw-w64-${{ matrix.arch }}-pkg-config + mingw-w64-${{ matrix.arch }}-python make libtool autoconf diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 1a69794..c5d6ec0 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -19,7 +19,7 @@ jobs: dry-run: false language: c++ - name: Upload Crash - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() && steps.build.outcome == 'success' with: name: artifacts diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9e02074..8f2384a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -28,7 +28,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. @@ -36,7 +36,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -50,4 +50,4 @@ jobs: make - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/Makefile.am b/Makefile.am index f44a6bc..e6923a4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,6 +19,7 @@ EXTRA_DIST = \ git-version-gen dist-hook: + @if ! git diff --quiet; then echo "Uncommitted changes present; not releasing"; exit 1; fi echo $(VERSION) > $(distdir)/.tarball-version docs/html: $(top_builddir)/doxygen.cfg $(top_srcdir)/include/plist/*.h @@ -1,3 +1,38 @@ +Version 2.7.0 +~~~~~~~~~~~~~ + +- Changes: + * Add plist_new_unix_date, plist_get_unix_date_val, plist_set_unix_date_val functions + that work with int64_t values representing a UNIX timestamp instead of + using the 'MAC epoch'. + These new functions should be used instead of plist_new_date, + plist_get_date_val, and plist_set_date_val, which are now marked deprecated + and might be removed in a future version of libplist. + * Allow building the library without tool(s) + * Switch to more generic global initializer method + * json: Allow e+/E+ in exponent as per RFC 8259 + * C++: Add more convenience functions to the interface + * C++: Add more type variants to different constructors and operators +- Bugfixes: + * Fix segmentation fault when calling plist_sort() on an empty dictionary + * Fix compilation on MSVC + * C++: Fix bug in internal helper function of Array class + * C++: Fix String::GetValue memory leaking and support assignment of const char* + +Version 2.6.0 +~~~~~~~~~~~~~ + +- Changes: + * Revert back API change around PLIST_DATA to use char* again + +Version 2.5.0 +~~~~~~~~~~~~~ + +- Changes: + * Change API around PLIST_DATA to use uint8_t* instead of char* + * Add PLIST_DICT helper functions for different operations + * Require Cython 3.0 for python bindings + Version 2.4.0 ~~~~~~~~~~~~~ @@ -260,8 +260,8 @@ Please make sure your contribution adheres to: ## Links * Homepage: https://libimobiledevice.org/ -* Repository: https://git.libimobiledevice.org/libplist.git -* Repository (Mirror): https://github.com/libimobiledevice/libplist.git +* Repository: https://github.com/libimobiledevice/libplist.git +* Repository (Mirror): https://git.libimobiledevice.org/libplist.git * Issue Tracker: https://github.com/libimobiledevice/libplist/issues * Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel * Twitter: https://twitter.com/libimobiledev @@ -279,5 +279,5 @@ iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc. This project is an independent software library and has not been authorized, sponsored, or otherwise approved by Apple Inc. -README Updated on: 2024-02-21 +README Updated on: 2024-10-22 diff --git a/configure.ac b/configure.ac index 757ea95..6f574d9 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ dnl libtool versioning # changes to the signature and the semantic) # ? :+1 : ? == just internal changes # CURRENT : REVISION : AGE -LIBPLIST_SO_VERSION=8:0:4 +LIBPLIST_SO_VERSION=11:0:7 AC_SUBST(LIBPLIST_SO_VERSION) @@ -71,41 +71,12 @@ case ${host_os} in ;; *) AC_MSG_RESULT([${host_os}]) - AX_PTHREAD([], [AC_MSG_ERROR([pthread is required to build libplist])]) - AC_CHECK_LIB(pthread, [pthread_once], [], [AC_MSG_ERROR([pthread with pthread_once required to build libplist])]) ;; esac AM_CONDITIONAL(WIN32, test x$win32 = xtrue) AC_SEARCH_LIBS([fmin],[m]) -# Check if the C compiler supports __attribute__((constructor)) -AC_CACHE_CHECK([wether the C compiler supports constructor/destructor attributes], - ac_cv_attribute_constructor, [ - ac_cv_attribute_constructor=no - AC_COMPILE_IFELSE([AC_LANG_PROGRAM( - [[ - #ifndef __has_attribute - #define __has_attribute(x) 0 - #endif - #if !__has_attribute(constructor) - #error No constructor attribute - #endif - #if !__has_attribute(destructor) - #error No destructor attribute - #endif - static void __attribute__((constructor)) test_constructor(void) { - } - static void __attribute__((destructor)) test_destructor(void) { - } - ]], [])], - [ac_cv_attribute_constructor=yes] - )] -) -if test "$ac_cv_attribute_constructor" = "yes"; then - AC_DEFINE(HAVE_ATTRIBUTE_CONSTRUCTOR, 1, [Define if the C compiler supports constructor/destructor attributes]) -fi - # Check if struct tm has a tm_gmtoff member AC_CACHE_CHECK(for tm_gmtoff in struct tm, ac_cv_struct_tm_gmtoff, AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ @@ -147,9 +118,9 @@ AC_ARG_WITH([cython], [build_cython=false], [build_cython=true]) if test "$build_cython" = "true"; then - AC_PROG_CYTHON([0.17.0]) + AC_PROG_CYTHON([3.0.0]) if [test "x$CYTHON" != "xfalse"]; then - AM_PATH_PYTHON([2.3], [CYTHON_PYTHON]) + AM_PATH_PYTHON([3.0], [CYTHON_PYTHON]) fi else CYTHON=false @@ -174,8 +145,16 @@ else fi AM_CONDITIONAL([HAVE_CYTHON],[test "x$cython_python_bindings" = "xyes"]) -AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter -Wno-strict-aliasing $PTHREAD_CFLAGS") -GLOBAL_LDFLAGS="$PTHREAD_LIBS" +AC_ARG_WITH([tools], + [AS_HELP_STRING([--with-tools], [Build libplist tools. [default=yes]])], + [build_tools=${withval}], + [build_tools=yes] +) +AS_IF([test "x$build_tools" = "xyes"], [AC_DEFINE(BUILD_TOOLS, 1, [Define if we are building plist tools])]) +AM_CONDITIONAL(BUILD_TOOLS, test "x$build_tools" = "xyes") + +AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter -Wno-strict-aliasing") +GLOBAL_LDFLAGS="" AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], @@ -226,7 +205,11 @@ AC_ARG_WITH([tests], [AS_HELP_STRING([--without-tests], [Do not build libplist test suite (default is yes)])], [build_tests=${withval}], - [build_tests=yes]) + [build_tests=${build_tools}]) + +if test "x$build_tests" = "xyes" -a "x$build_tools" != "xyes"; then + AC_MSG_ERROR([Building the test suite requires plistutil. Reconfigure with --with-tools=yes to enable.]) +fi if test "x$build_fuzzers" = "xyes"; then if test "x$build_sanitizers" = "xno"; then @@ -345,6 +328,8 @@ Configuration for $PACKAGE $VERSION: Install prefix ..........: $prefix Debug code ..............: $debug Python bindings .........: $cython_python_bindings + Build tools .............: $build_tools + Build test suite ........: $build_tests $EXTRA_CONF Now type 'make' to build $PACKAGE $VERSION, and then 'make install' for installation. diff --git a/cython/plist.pyx b/cython/plist.pyx index b5f4ef6..29c1181 100644 --- a/cython/plist.pyx +++ b/cython/plist.pyx @@ -33,9 +33,9 @@ cdef extern from *: void plist_get_real_val(plist_t node, double *val) void plist_set_real_val(plist_t node, double val) - plist_t plist_new_date(int32_t sec, int32_t usec) - void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec) - void plist_set_date_val(plist_t node, int32_t sec, int32_t usec) + plist_t plist_new_unix_date(int64_t sec) + void plist_get_unix_date_val(plist_t node, int64_t *sec) + void plist_set_unix_date_val(plist_t node, int64_t sec) void plist_get_key_val(plist_t node, char **val) void plist_set_key_val(plist_t node, char *val) @@ -488,23 +488,21 @@ cdef String String_factory(plist_t c_node, bint managed=True): instance._c_node = c_node return instance -MAC_EPOCH = 978307200 - cdef extern from "plist_util.h": - void datetime_to_ints(object obj, int32_t* sec, int32_t* usec) - object ints_to_datetime(int32_t sec, int32_t usec) + int64_t datetime_to_timestamp(object obj) + object timestamp_to_datetime(int64_t sec) int check_datetime(object obj) cdef plist_t create_date_plist(value=None): cdef plist_t node = NULL - cdef int32_t secs - cdef int32_t usecs if value is None: - node = plist_new_date(0, 0) + node = plist_new_unix_date(0) + elif isinstance(value, int): + node = plist_new_unix_date(value) + elif isinstance(value, float): + node = plist_new_unix_date(int(value)) elif check_datetime(value): - datetime_to_ints(value, &secs, &usecs) - secs -= MAC_EPOCH - node = plist_new_date(secs, usecs) + node = plist_new_unix_date(datetime_to_timestamp(value)) return node cdef class Date(Node): @@ -531,19 +529,21 @@ cdef class Date(Node): return d >= other cpdef object get_value(self): - cdef int32_t secs = 0 - cdef int32_t usecs = 0 - plist_get_date_val(self._c_node, &secs, &usecs) - secs += MAC_EPOCH - return ints_to_datetime(secs, usecs) + cdef int64_t secs = 0 + plist_get_unix_date_val(self._c_node, &secs) + return timestamp_to_datetime(secs) cpdef set_value(self, object value): - cdef int32_t secs - cdef int32_t usecs - if not check_datetime(value): - raise ValueError("Expected a datetime") - datetime_to_ints(value, &secs, &usecs) - plist_set_date_val(self._c_node, secs, usecs) + cdef int64_t secs = 0 + if isinstance(value, int): + secs = value + elif isinstance(value, float): + secs = int(value) + elif check_datetime(value): + secs = datetime_to_timestamp(value) + else: + raise ValueError("Expected int or datetime") + plist_set_unix_date_val(self._c_node, secs) cdef Date Date_factory(plist_t c_node, bint managed=True): cdef Date instance = Date.__new__(Date) @@ -851,7 +851,7 @@ cdef plist_t native_to_plist_t(object native): return plist_new_string(native) if isinstance(native, bool): return plist_new_bool(<bint>native) - if isinstance(native, int) or isinstance(native, long): + if isinstance(native, int): return plist_new_uint(native) if isinstance(native, float): return plist_new_real(native) diff --git a/cython/plist_util.c b/cython/plist_util.c index 4f922e5..27084fa 100644 --- a/cython/plist_util.c +++ b/cython/plist_util.c @@ -3,13 +3,11 @@ #include <time.h> #include <datetime.h> -void datetime_to_ints(PyObject* obj, int32_t* sec, int32_t* usec) { +int64_t datetime_to_timestamp(PyObject* obj) { PyDateTime_IMPORT; if (!PyDateTime_Check(obj)) { - PyErr_SetString(PyExc_ValueError,"Expected a datetime"); - sec = NULL; - usec = NULL; - return; + PyErr_SetString(PyExc_ValueError,"Expected a datetime"); + return 0; } struct tm t; memset(&t, 0, sizeof(t)); @@ -19,22 +17,21 @@ void datetime_to_ints(PyObject* obj, int32_t* sec, int32_t* usec) { t.tm_mday = PyDateTime_GET_DAY(obj); t.tm_mon = PyDateTime_GET_MONTH(obj)-1; t.tm_year = PyDateTime_GET_YEAR(obj)-1900; - *sec = (int32_t)mktime(&t); - *usec = PyDateTime_DATE_GET_MICROSECOND(obj); + return mktime(&t); } -PyObject* ints_to_datetime(int32_t sec, int32_t usec) { - time_t sec_tt = sec; +PyObject* timestamp_to_datetime(int64_t sec) { + time_t sec_tt = sec; struct tm* t = gmtime(&sec_tt); if(t){ - PyDateTime_IMPORT; - return PyDateTime_FromDateAndTime(t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, usec); + PyDateTime_IMPORT; + return PyDateTime_FromDateAndTime(t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, 0); } - return NULL; + return NULL; } int check_datetime(PyObject* ob) { - if(ob){ - PyDateTime_IMPORT; - return PyDateTime_Check(ob); - } - return 0; + if(ob){ + PyDateTime_IMPORT; + return PyDateTime_Check(ob); + } + return 0; } diff --git a/cython/plist_util.h b/cython/plist_util.h index fbf56b6..e0a84b5 100644 --- a/cython/plist_util.h +++ b/cython/plist_util.h @@ -1,5 +1,5 @@ #include <Python.h> -void datetime_to_ints(PyObject* obj, int32_t* sec, int32_t* usec); -PyObject* ints_to_datetime(int32_t sec, int32_t usec); +int64_t datetime_to_timestamp(PyObject* obj); +PyObject* timestamp_to_datetime(int64_t sec); int check_datetime(PyObject* obj); diff --git a/include/plist/Array.h b/include/plist/Array.h index 0239c78..f4360c5 100644 --- a/include/plist/Array.h +++ b/include/plist/Array.h @@ -43,6 +43,10 @@ public : typedef std::vector<Node*>::const_iterator const_iterator; Node* operator[](unsigned int index); + Node* Back(); + Node* back(); + Node* Front(); + Node* front(); iterator Begin(); iterator begin(); iterator End(); @@ -52,11 +56,20 @@ public : const_iterator End() const; const_iterator end() const; size_t size() const; - void Append(Node* node); - void Insert(Node* node, unsigned int pos); + void Append(const Node& node); + void Append(const Node* node); + void Insert(const Node& node, unsigned int pos); + void Insert(const Node* node, unsigned int pos); void Remove(Node* node); void Remove(unsigned int pos); - unsigned int GetNodeIndex(Node* node) const; + unsigned int GetNodeIndex(const Node& node) const; + unsigned int GetNodeIndex(const Node* node) const; + template <typename T> T* at(unsigned int index) { + return (T*)(_array.at(index)); + } + template <typename T> T* At(unsigned int index) { + return (T*)(_array.at(index)); + } private : std::vector<Node*> _array; diff --git a/include/plist/Data.h b/include/plist/Data.h index b566a6c..1de88aa 100644 --- a/include/plist/Data.h +++ b/include/plist/Data.h @@ -36,6 +36,7 @@ public : Data(const Data& d); Data& operator=(const Data& b); Data(const std::vector<char>& buff); + Data(const char* buff, uint64_t size); virtual ~Data(); Node* Clone() const; diff --git a/include/plist/Date.h b/include/plist/Date.h index 5113cf3..7026699 100644 --- a/include/plist/Date.h +++ b/include/plist/Date.h @@ -40,13 +40,13 @@ public : Date(plist_t node, Node* parent = NULL); Date(const Date& d); Date& operator=(const Date& d); - Date(timeval t); + Date(int64_t t); virtual ~Date(); Node* Clone() const; - void SetValue(timeval t); - timeval GetValue() const; + void SetValue(int64_t t); + int64_t GetValue() const; }; }; diff --git a/include/plist/Dictionary.h b/include/plist/Dictionary.h index 583a430..37ff1c9 100644 --- a/include/plist/Dictionary.h +++ b/include/plist/Dictionary.h @@ -60,11 +60,12 @@ public : void Remove(Node* node); void Remove(const std::string& key); std::string GetNodeKey(Node* node); + template <typename T> T* Get(const std::string& key) { + return (T*)(_map[key]); + } private : std::map<std::string,Node*> _map; - - }; }; diff --git a/include/plist/String.h b/include/plist/String.h index 9aba16b..95f8dd1 100644 --- a/include/plist/String.h +++ b/include/plist/String.h @@ -35,7 +35,10 @@ public : String(plist_t node, Node* parent = NULL); String(const String& s); String& operator=(const String& s); + String& operator=(const std::string& s); + String& operator=(const char* s); String(const std::string& s); + String(const char *s); virtual ~String(); Node* Clone() const; diff --git a/include/plist/Structure.h b/include/plist/Structure.h index eded8b2..b7fbdb5 100644 --- a/include/plist/Structure.h +++ b/include/plist/Structure.h @@ -43,6 +43,9 @@ public : static Structure* FromXml(const std::string& xml); static Structure* FromBin(const std::vector<char>& bin); + static Structure* FromBin(const char* bin, uint64_t size); + static Structure* FromMemory(const std::vector<char>& buf, plist_format_t* format = NULL); + static Structure* FromMemory(const char* buf, uint64_t size, plist_format_t* format = NULL); protected: Structure(Node* parent = NULL); diff --git a/include/plist/plist.h b/include/plist/plist.h index 46aca16..41af8ce 100644 --- a/include/plist/plist.h +++ b/include/plist/plist.h @@ -263,12 +263,11 @@ extern "C" /** * Create a new plist_t type #PLIST_DATE * - * @param sec the number of seconds since 01/01/2001 - * @param usec the number of microseconds + * @param sec The number of seconds since 01/01/1970 (UNIX timestamp) * @return the created item * @sa #plist_type */ - PLIST_API plist_t plist_new_date(int32_t sec, int32_t usec); + PLIST_API plist_t plist_new_unix_date(int64_t sec); /** * Create a new plist_t type #PLIST_UID @@ -492,6 +491,166 @@ extern "C" */ PLIST_API void plist_dict_merge(plist_t *target, plist_t source); + /** + * Get a boolean value from a given #PLIST_DICT entry. + * + * The value node can be of type #PLIST_BOOLEAN, but also + * #PLIST_STRING (either 'true' or 'false'), + * #PLIST_INT with a numerical value of 0 or >= 1, + * or #PLIST_DATA with a single byte with a value of 0 or >= 1. + * + * @note This function returns 0 if the dictionary does not contain an + * entry for the given key, if the value node is of any other than + * the above mentioned type, or has any mismatching value. + * + * @param dict A node of type #PLIST_DICT + * @param key The key to look for in dict + * @return 0 or 1 depending on the value of the node. + */ + PLIST_API uint8_t plist_dict_get_bool(plist_t dict, const char *key); + + /** + * Get a signed integer value from a given #PLIST_DICT entry. + * The value node can be of type #PLIST_INT, but also + * #PLIST_STRING with a numerical value as string (decimal or hexadecimal), + * or #PLIST_DATA with a size of 1, 2, 4, or 8 bytes in little endian byte order. + * + * @note This function returns 0 if the dictionary does not contain an + * entry for the given key, if the value node is of any other than + * the above mentioned type, or has any mismatching value. + * + * @param dict A node of type #PLIST_DICT + * @param key The key to look for in dict + * @return Signed integer value depending on the value of the node. + */ + PLIST_API int64_t plist_dict_get_int(plist_t dict, const char *key); + + /** + * Get an unsigned integer value from a given #PLIST_DICT entry. + * The value node can be of type #PLIST_INT, but also + * #PLIST_STRING with a numerical value as string (decimal or hexadecimal), + * or #PLIST_DATA with a size of 1, 2, 4, or 8 bytes in little endian byte order. + * + * @note This function returns 0 if the dictionary does not contain an + * entry for the given key, if the value node is of any other than + * the above mentioned type, or has any mismatching value. + * + * @param dict A node of type #PLIST_DICT + * @param key The key to look for in dict + * @return Signed integer value depending on the value of the node. + */ + PLIST_API uint64_t plist_dict_get_uint(plist_t dict, const char *key); + + /** + * Copy a node from *source_dict* to *target_dict*. + * The node is looked up in *source_dict* with given *key*, unless *alt_source_key* + * is non-NULL, in which case it is looked up with *alt_source_key*. + * The entry in *target_dict* is **always** created with *key*. + * + * @param target_dict The target dictionary to copy to. + * @param source_dict The source dictionary to copy from. + * @param key The key for the node to copy. + * @param alt_source_key The alternative source key for lookup in *source_dict* or NULL. + * + * @result PLIST_ERR_SUCCESS on success or PLIST_ERR_INVALID_ARG if the source dictionary does not contain + * any entry with given key or alt_source_key. + */ + PLIST_API plist_err_t plist_dict_copy_item(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); + + /** + * Copy a boolean value from *source_dict* to *target_dict*. + * The node is looked up in *source_dict* with given *key*, unless *alt_source_key* + * is non-NULL, in which case it is looked up with *alt_source_key*. + * The entry in *target_dict* is **always** created with *key*. + * + * @note The boolean value from *source_dict* is retrieved with #plist_dict_get_bool, + * but is **always** created as #PLIST_BOOLEAN in *target_dict*. + * + * @param target_dict The target dictionary to copy to. + * @param source_dict The source dictionary to copy from. + * @param key The key for the node to copy. + * @param alt_source_key The alternative source key for lookup in *source_dict* or NULL. + * + * @result PLIST_ERR_SUCCESS on success or PLIST_ERR_INVALID_ARG if the source dictionary does not contain + * any entry with given key or alt_source_key. + */ + PLIST_API plist_err_t plist_dict_copy_bool(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); + + /** + * Copy a signed integer value from *source_dict* to *target_dict*. + * The node is looked up in *source_dict* with given *key*, unless *alt_source_key* + * is non-NULL, in which case it is looked up with *alt_source_key*. + * The entry in *target_dict* is **always** created with *key*. + * + * @note The signed integer value from *source_dict* is retrieved with #plist_dict_get_int, + * but is **always** created as #PLIST_INT. + * + * @param target_dict The target dictionary to copy to. + * @param source_dict The source dictionary to copy from. + * @param key The key for the node value to copy. + * @param alt_source_key The alternative source key for lookup in *source_dict* or NULL. + * + * @result PLIST_ERR_SUCCESS on success or PLIST_ERR_INVALID_ARG if the source dictionary does not contain + * any entry with given key or alt_source_key. + */ + PLIST_API plist_err_t plist_dict_copy_int(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); + + /** + * Copy an unsigned integer value from *source_dict* to *target_dict*. + * The node is looked up in *source_dict* with given *key*, unless *alt_source_key* + * is non-NULL, in which case it is looked up with *alt_source_key*. + * The entry in *target_dict* is **always** created with *key*. + * + * @note The unsigned integer value from *source_dict* is retrieved with #plist_dict_get_uint, + * but is **always** created as #PLIST_INT. + * + * @param target_dict The target dictionary to copy to. + * @param source_dict The source dictionary to copy from. + * @param key The key for the node value to copy. + * @param alt_source_key The alternative source key for lookup in *source_dict* or NULL. + * + * @result PLIST_ERR_SUCCESS on success or PLIST_ERR_INVALID_ARG if the source dictionary does not contain + * any entry with given key or alt_source_key. + */ + PLIST_API plist_err_t plist_dict_copy_uint(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); + + /** + * Copy a #PLIST_DATA node from *source_dict* to *target_dict*. + * The node is looked up in *source_dict* with given *key*, unless *alt_source_key* + * is non-NULL, in which case it is looked up with *alt_source_key*. + * The entry in *target_dict* is **always** created with *key*. + * + * @note This function is like #plist_dict_copy_item, except that it fails + * if the source node is not of type #PLIST_DATA. + * + * @param target_dict The target dictionary to copy to. + * @param source_dict The source dictionary to copy from. + * @param key The key for the node value to copy. + * @param alt_source_key The alternative source key for lookup in *source_dict* or NULL. + * + * @result PLIST_ERR_SUCCESS on success or PLIST_ERR_INVALID_ARG if the source dictionary does not contain + * any entry with given key or alt_source_key, or if it is not of type #PLIST_DATA. + */ + PLIST_API plist_err_t plist_dict_copy_data(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); + + /** + * Copy a #PLIST_STRING node from *source_dict* to *target_dict*. + * The node is looked up in *source_dict* with given *key*, unless *alt_source_key* + * is non-NULL, in which case it is looked up with *alt_source_key*. + * The entry in *target_dict* is **always** created with *key*. + * + * @note This function is like #plist_dict_copy_item, except that it fails + * if the source node is not of type #PLIST_STRING. + * + * @param target_dict The target dictionary to copy to. + * @param source_dict The source dictionary to copy from. + * @param key The key for the node value to copy. + * @param alt_source_key The alternative source key for lookup in *source_dict* or NULL. + * + * @result PLIST_ERR_SUCCESS on success or PLIST_ERR_INVALID_ARG if the source dictionary does not contain + * any entry with given key or alt_source_key, or if it is not of type #PLIST_STRING. + */ + PLIST_API plist_err_t plist_dict_copy_string(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); /******************************************** * * @@ -615,10 +774,9 @@ extern "C" * This function does nothing if node is not of type #PLIST_DATE * * @param node the node - * @param sec a pointer to an int32_t variable. Represents the number of seconds since 01/01/2001. - * @param usec a pointer to an int32_t variable. Represents the number of microseconds + * @param sec a pointer to an int64_t variable. Represents the number of seconds since 01/01/1970 (UNIX timestamp). */ - PLIST_API void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec); + PLIST_API void plist_get_unix_date_val(plist_t node, int64_t *sec); /** * Get the value of a #PLIST_UID node. @@ -707,10 +865,9 @@ extern "C" * Forces type of node to #PLIST_DATE * * @param node the node - * @param sec the number of seconds since 01/01/2001 - * @param usec the number of microseconds + * @param sec the number of seconds since 01/01/1970 (UNIX timestamp) */ - PLIST_API void plist_set_date_val(plist_t node, int32_t sec, int32_t usec); + PLIST_API void plist_set_unix_date_val(plist_t node, int64_t sec); /** * Set the value of a node. @@ -1053,16 +1210,15 @@ extern "C" /** * Helper function to compare the value of a PLIST_DATE node against - * a given set of seconds and fraction of a second since epoch. + * a given number of seconds since epoch (UNIX timestamp). * * @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 + * @param cmpval Number of seconds to compare against (UNIX timestamp) * @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. */ - PLIST_API int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec); + PLIST_API int plist_unix_date_val_compare(plist_t datenode, int64_t cmpval); /** * Helper function to compare the value of a PLIST_STRING node against @@ -1222,6 +1378,68 @@ extern "C" */ PLIST_API const char* libplist_version(); + + /******************************************** + * * + * Deprecated API * + * * + ********************************************/ + + /** + * Create a new plist_t type #PLIST_DATE + * + * @deprecated Deprecated. Use plist_new_unix_date instead. + * + * @param sec the number of seconds since 01/01/2001 + * @param usec the number of microseconds + * @return the created item + * @sa #plist_type + */ + PLIST_WARN_DEPRECATED("use plist_new_unix_date instead") + PLIST_API plist_t plist_new_date(int32_t sec, int32_t usec); + + /** + * Get the value of a #PLIST_DATE node. + * This function does nothing if node is not of type #PLIST_DATE + * + * @deprecated Deprecated. Use plist_get_unix_date_val instead. + * + * @param node the node + * @param sec a pointer to an int32_t variable. Represents the number of seconds since 01/01/2001. + * @param usec a pointer to an int32_t variable. Represents the number of microseconds + */ + PLIST_WARN_DEPRECATED("use plist_get_unix_date_val instead") + PLIST_API void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec); + + /** + * Set the value of a node. + * Forces type of node to #PLIST_DATE + * + * @deprecated Deprecated. Use plist_set_unix_date_val instead. + * + * @param node the node + * @param sec the number of seconds since 01/01/2001 + * @param usec the number of microseconds + */ + PLIST_WARN_DEPRECATED("use plist_set_unix_date_val instead") + PLIST_API void plist_set_date_val(plist_t node, int32_t sec, int32_t usec); + + /** + * Helper function to compare the value of a PLIST_DATE node against + * a given set of seconds and fraction of a second since epoch. + * + * @deprecated Deprecated. Use plist_unix_date_val_compare instead. + * + * @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. + */ + PLIST_WARN_DEPRECATED("use plist_unix_date_val_compare instead") + PLIST_API int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec); + /*@}*/ #ifdef __cplusplus diff --git a/m4/ax_python_devel.m4 b/m4/ax_python_devel.m4 index 44dbd83..935056c 100644 --- a/m4/ax_python_devel.m4 +++ b/m4/ax_python_devel.m4 @@ -4,7 +4,7 @@ # # SYNOPSIS # -# AX_PYTHON_DEVEL([version]) +# AX_PYTHON_DEVEL([version[,optional]]) # # DESCRIPTION # @@ -23,6 +23,11 @@ # version number. Don't use "PYTHON_VERSION" for this: that environment # variable is declared as precious and thus reserved for the end-user. # +# By default this will fail if it does not detect a development version of +# python. If you want it to continue, set optional to true, like +# AX_PYTHON_DEVEL([], [true]). The ax_python_devel_found variable will be +# "no" if it fails. +# # This macro should work for all versions of Python >= 2.1.0. As an end # user, you can disable the check for the python version by setting the # PYTHON_NOVERSIONCHECK environment variable to something else than the @@ -67,10 +72,18 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 21 +#serial 37 AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) AC_DEFUN([AX_PYTHON_DEVEL],[ + # Get whether it's optional + if test -z "$2"; then + ax_python_devel_optional=false + else + ax_python_devel_optional=$2 + fi + ax_python_devel_found=yes + # # Allow the use of a (user set) custom python version # @@ -81,21 +94,26 @@ AC_DEFUN([AX_PYTHON_DEVEL],[ AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) if test -z "$PYTHON"; then - AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path]) + AC_MSG_WARN([Cannot find python$PYTHON_VERSION in your system path]) + if ! $ax_python_devel_optional; then + AC_MSG_ERROR([Giving up, python development not available]) + fi + ax_python_devel_found=no PYTHON_VERSION="" fi - # - # Check for a version of Python >= 2.1.0 - # - AC_MSG_CHECKING([for a version of Python >= '2.1.0']) - ac_supports_python_ver=`$PYTHON -c "import sys; \ + if test $ax_python_devel_found = yes; then + # + # Check for a version of Python >= 2.1.0 + # + AC_MSG_CHECKING([for a version of Python >= '2.1.0']) + ac_supports_python_ver=`$PYTHON -c "import sys; \ ver = sys.version.split ()[[0]]; \ print (ver >= '2.1.0')"` - if test "$ac_supports_python_ver" != "True"; then + if test "$ac_supports_python_ver" != "True"; then if test -z "$PYTHON_NOVERSIONCHECK"; then AC_MSG_RESULT([no]) - AC_MSG_FAILURE([ + AC_MSG_WARN([ This version of the AC@&t@_PYTHON_DEVEL macro doesn't work properly with versions of Python before 2.1.0. You may need to re-run configure, setting the @@ -104,58 +122,119 @@ PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. Moreover, to disable this check, set PYTHON_NOVERSIONCHECK to something else than an empty string. ]) + if ! $ax_python_devel_optional; then + AC_MSG_FAILURE([Giving up]) + fi + ax_python_devel_found=no + PYTHON_VERSION="" else AC_MSG_RESULT([skip at user request]) fi - else + else AC_MSG_RESULT([yes]) + fi fi - # - # if the macro parameter ``version'' is set, honour it - # - if test -n "$1"; then + if test $ax_python_devel_found = yes; then + # + # If the macro parameter ``version'' is set, honour it. + # A Python shim class, VPy, is used to implement correct version comparisons via + # string expressions, since e.g. a naive textual ">= 2.7.3" won't work for + # Python 2.7.10 (the ".1" being evaluated as less than ".3"). + # + if test -n "$1"; then AC_MSG_CHECKING([for a version of Python $1]) - ac_supports_python_ver=`$PYTHON -c "import sys; \ - ver = sys.version.split ()[[0]]; \ + cat << EOF > ax_python_devel_vpy.py +class VPy: + def vtup(self, s): + return tuple(map(int, s.strip().replace("rc", ".").split("."))) + def __init__(self): + import sys + self.vpy = tuple(sys.version_info)[[:3]] + def __eq__(self, s): + return self.vpy == self.vtup(s) + def __ne__(self, s): + return self.vpy != self.vtup(s) + def __lt__(self, s): + return self.vpy < self.vtup(s) + def __gt__(self, s): + return self.vpy > self.vtup(s) + def __le__(self, s): + return self.vpy <= self.vtup(s) + def __ge__(self, s): + return self.vpy >= self.vtup(s) +EOF + ac_supports_python_ver=`$PYTHON -c "import ax_python_devel_vpy; \ + ver = ax_python_devel_vpy.VPy(); \ print (ver $1)"` + rm -rf ax_python_devel_vpy*.py* __pycache__/ax_python_devel_vpy*.py* if test "$ac_supports_python_ver" = "True"; then - AC_MSG_RESULT([yes]) + AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) - AC_MSG_ERROR([this package requires Python $1. + AC_MSG_WARN([this package requires Python $1. If you have it installed, but it isn't the default Python interpreter in your system path, please pass the PYTHON_VERSION variable to configure. See ``configure --help'' for reference. ]) + if ! $ax_python_devel_optional; then + AC_MSG_ERROR([Giving up]) + fi + ax_python_devel_found=no PYTHON_VERSION="" fi + fi fi - # - # Check if you have distutils, else fail - # - AC_MSG_CHECKING([for the distutils Python package]) - ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` - if test $? -eq 0; then + if test $ax_python_devel_found = yes; then + # + # Check if you have distutils, else fail + # + AC_MSG_CHECKING([for the sysconfig Python package]) + ac_sysconfig_result=`$PYTHON -c "import sysconfig" 2>&1` + if test $? -eq 0; then AC_MSG_RESULT([yes]) - else + IMPORT_SYSCONFIG="import sysconfig" + else AC_MSG_RESULT([no]) - AC_MSG_ERROR([cannot import Python module "distutils". + + AC_MSG_CHECKING([for the distutils Python package]) + ac_sysconfig_result=`$PYTHON -c "from distutils import sysconfig" 2>&1` + if test $? -eq 0; then + AC_MSG_RESULT([yes]) + IMPORT_SYSCONFIG="from distutils import sysconfig" + else + AC_MSG_WARN([cannot import Python module "distutils". Please check your Python installation. The error was: -$ac_distutils_result]) - PYTHON_VERSION="" +$ac_sysconfig_result]) + if ! $ax_python_devel_optional; then + AC_MSG_ERROR([Giving up]) + fi + ax_python_devel_found=no + PYTHON_VERSION="" + fi + fi fi - # - # Check for Python include path - # - AC_MSG_CHECKING([for Python include path]) - if test -z "$PYTHON_CPPFLAGS"; then - python_path=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_inc ());"` - plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_inc (plat_specific=1));"` + if test $ax_python_devel_found = yes; then + # + # Check for Python include path + # + AC_MSG_CHECKING([for Python include path]) + if test -z "$PYTHON_CPPFLAGS"; then + if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then + # sysconfig module has different functions + python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + print (sysconfig.get_path ('include'));"` + plat_python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + print (sysconfig.get_path ('platinclude'));"` + else + # old distutils way + python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + print (sysconfig.get_python_inc ());"` + plat_python_path=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + print (sysconfig.get_python_inc (plat_specific=1));"` + fi if test -n "${python_path}"; then if test "${plat_python_path}" != "${python_path}"; then python_path="-I$python_path -I$plat_python_path" @@ -164,22 +243,22 @@ $ac_distutils_result]) fi fi PYTHON_CPPFLAGS=$python_path - fi - AC_MSG_RESULT([$PYTHON_CPPFLAGS]) - AC_SUBST([PYTHON_CPPFLAGS]) + fi + AC_MSG_RESULT([$PYTHON_CPPFLAGS]) + AC_SUBST([PYTHON_CPPFLAGS]) - # - # Check for Python library path - # - AC_MSG_CHECKING([for Python library path]) - if test -z "$PYTHON_LIBS"; then + # + # Check for Python library path + # + AC_MSG_CHECKING([for Python library path]) + if test -z "$PYTHON_LIBS"; then # (makes two attempts to ensure we've got a version number # from the interpreter) ac_python_version=`cat<<EOD | $PYTHON - # join all versioning strings, on some systems # major/minor numbers could be in different list elements -from distutils.sysconfig import * +from sysconfig import * e = get_config_var('VERSION') if e is not None: print(e) @@ -190,7 +269,7 @@ EOD` ac_python_version=$PYTHON_VERSION else ac_python_version=`$PYTHON -c "import sys; \ - print (sys.version[[:3]])"` + print ("%d.%d" % sys.version_info[[:2]])"` fi fi @@ -202,8 +281,8 @@ EOD` ac_python_libdir=`cat<<EOD | $PYTHON - # There should be only one -import distutils.sysconfig -e = distutils.sysconfig.get_config_var('LIBDIR') +$IMPORT_SYSCONFIG +e = sysconfig.get_config_var('LIBDIR') if e is not None: print (e) EOD` @@ -211,8 +290,8 @@ EOD` # Now, for the library: ac_python_library=`cat<<EOD | $PYTHON - -import distutils.sysconfig -c = distutils.sysconfig.get_config_vars() +$IMPORT_SYSCONFIG +c = sysconfig.get_config_vars() if 'LDVERSION' in c: print ('python'+c[['LDVERSION']]) else: @@ -231,83 +310,140 @@ EOD` else # old way: use libpython from python_configdir ac_python_libdir=`$PYTHON -c \ - "from distutils.sysconfig import get_python_lib as f; \ + "from sysconfig import get_python_lib as f; \ import os; \ print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"` PYTHON_LIBS="-L$ac_python_libdir -lpython$ac_python_version" fi - if test -z "PYTHON_LIBS"; then - AC_MSG_ERROR([ + if test -z "$PYTHON_LIBS"; then + AC_MSG_WARN([ Cannot determine location of your Python DSO. Please check it was installed with dynamic libraries enabled, or try setting PYTHON_LIBS by hand. ]) + if ! $ax_python_devel_optional; then + AC_MSG_ERROR([Giving up]) + fi + ax_python_devel_found=no + PYTHON_VERSION="" fi + fi fi - AC_MSG_RESULT([$PYTHON_LIBS]) - AC_SUBST([PYTHON_LIBS]) - # - # Check for site packages - # - AC_MSG_CHECKING([for Python site-packages path]) - if test -z "$PYTHON_SITE_PKG"; then - PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_lib(0,0));"` - fi - AC_MSG_RESULT([$PYTHON_SITE_PKG]) - AC_SUBST([PYTHON_SITE_PKG]) + if test $ax_python_devel_found = yes; then + AC_MSG_RESULT([$PYTHON_LIBS]) + AC_SUBST([PYTHON_LIBS]) - # - # libraries which must be linked in when embedding - # - AC_MSG_CHECKING(python extra libraries) - if test -z "$PYTHON_EXTRA_LIBS"; then - PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \ - conf = distutils.sysconfig.get_config_var; \ + # + # Check for site packages + # + AC_MSG_CHECKING([for Python site-packages path]) + if test -z "$PYTHON_SITE_PKG"; then + if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then + PYTHON_SITE_PKG=`$PYTHON -c " +$IMPORT_SYSCONFIG; +if hasattr(sysconfig, 'get_default_scheme'): + scheme = sysconfig.get_default_scheme() +else: + scheme = sysconfig._get_default_scheme() +if scheme == 'posix_local': + # Debian's default scheme installs to /usr/local/ but we want to find headers in /usr/ + scheme = 'posix_prefix' +prefix = '$prefix' +if prefix == 'NONE': + prefix = '$ac_default_prefix' +sitedir = sysconfig.get_path('purelib', scheme, vars={'base': prefix}) +print(sitedir)"` + else + # distutils.sysconfig way + PYTHON_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + print (sysconfig.get_python_lib(0,0));"` + fi + fi + AC_MSG_RESULT([$PYTHON_SITE_PKG]) + AC_SUBST([PYTHON_SITE_PKG]) + + # + # Check for platform-specific site packages + # + AC_MSG_CHECKING([for Python platform specific site-packages path]) + if test -z "$PYTHON_PLATFORM_SITE_PKG"; then + if test "$IMPORT_SYSCONFIG" = "import sysconfig"; then + PYTHON_PLATFORM_SITE_PKG=`$PYTHON -c " +$IMPORT_SYSCONFIG; +if hasattr(sysconfig, 'get_default_scheme'): + scheme = sysconfig.get_default_scheme() +else: + scheme = sysconfig._get_default_scheme() +if scheme == 'posix_local': + # Debian's default scheme installs to /usr/local/ but we want to find headers in /usr/ + scheme = 'posix_prefix' +prefix = '$prefix' +if prefix == 'NONE': + prefix = '$ac_default_prefix' +sitedir = sysconfig.get_path('platlib', scheme, vars={'platbase': prefix}) +print(sitedir)"` + else + # distutils.sysconfig way + PYTHON_PLATFORM_SITE_PKG=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + print (sysconfig.get_python_lib(1,0));"` + fi + fi + AC_MSG_RESULT([$PYTHON_PLATFORM_SITE_PKG]) + AC_SUBST([PYTHON_PLATFORM_SITE_PKG]) + + # + # libraries which must be linked in when embedding + # + AC_MSG_CHECKING(python extra libraries) + if test -z "$PYTHON_EXTRA_LIBS"; then + PYTHON_EXTRA_LIBS=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + conf = sysconfig.get_config_var; \ print (conf('LIBS') + ' ' + conf('SYSLIBS'))"` - fi - AC_MSG_RESULT([$PYTHON_EXTRA_LIBS]) - AC_SUBST(PYTHON_EXTRA_LIBS) + fi + AC_MSG_RESULT([$PYTHON_EXTRA_LIBS]) + AC_SUBST(PYTHON_EXTRA_LIBS) - # - # linking flags needed when embedding - # - AC_MSG_CHECKING(python extra linking flags) - if test -z "$PYTHON_EXTRA_LDFLAGS"; then - PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \ - conf = distutils.sysconfig.get_config_var; \ + # + # linking flags needed when embedding + # + AC_MSG_CHECKING(python extra linking flags) + if test -z "$PYTHON_EXTRA_LDFLAGS"; then + PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "$IMPORT_SYSCONFIG; \ + conf = sysconfig.get_config_var; \ print (conf('LINKFORSHARED'))"` - fi - AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS]) - AC_SUBST(PYTHON_EXTRA_LDFLAGS) + # Hack for macos, it sticks this in here. + PYTHON_EXTRA_LDFLAGS=`echo $PYTHON_EXTRA_LDFLAGS | sed 's/CoreFoundation.*$/CoreFoundation/'` + fi + AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS]) + AC_SUBST(PYTHON_EXTRA_LDFLAGS) - # - # final check to see if everything compiles alright - # - AC_MSG_CHECKING([consistency of all components of python development environment]) - # save current global flags - ac_save_LIBS="$LIBS" - ac_save_LDFLAGS="$LDFLAGS" - ac_save_CPPFLAGS="$CPPFLAGS" - LIBS="$ac_save_LIBS $PYTHON_LIBS $PYTHON_EXTRA_LIBS $PYTHON_EXTRA_LIBS" - LDFLAGS="$ac_save_LDFLAGS $PYTHON_EXTRA_LDFLAGS" - CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS" - AC_LANG_PUSH([C]) - AC_LINK_IFELSE([ + # + # final check to see if everything compiles alright + # + AC_MSG_CHECKING([consistency of all components of python development environment]) + # save current global flags + ac_save_LIBS="$LIBS" + ac_save_LDFLAGS="$LDFLAGS" + ac_save_CPPFLAGS="$CPPFLAGS" + LIBS="$ac_save_LIBS $PYTHON_LIBS $PYTHON_EXTRA_LIBS" + LDFLAGS="$ac_save_LDFLAGS $PYTHON_EXTRA_LDFLAGS" + CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS" + AC_LANG_PUSH([C]) + AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include <Python.h>]], [[Py_Initialize();]]) ],[pythonexists=yes],[pythonexists=no]) - AC_LANG_POP([C]) - # turn back to default flags - CPPFLAGS="$ac_save_CPPFLAGS" - LIBS="$ac_save_LIBS" - LDFLAGS="$ac_save_LDFLAGS" + AC_LANG_POP([C]) + # turn back to default flags + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + LDFLAGS="$ac_save_LDFLAGS" - AC_MSG_RESULT([$pythonexists]) + AC_MSG_RESULT([$pythonexists]) - if test ! "x$pythonexists" = "xyes"; then - AC_MSG_FAILURE([ + if test ! "x$pythonexists" = "xyes"; then + AC_MSG_WARN([ Could not link test program to Python. Maybe the main Python library has been installed in some non-standard library path. If so, pass it to configure, via the LIBS environment variable. @@ -317,8 +453,13 @@ EOD` You probably have to install the development version of the Python package for your distribution. The exact name of this package varies among them. ============================================================================ - ]) - PYTHON_VERSION="" + ]) + if ! $ax_python_devel_optional; then + AC_MSG_ERROR([Giving up]) + fi + ax_python_devel_found=no + PYTHON_VERSION="" + fi fi # diff --git a/src/Array.cpp b/src/Array.cpp index bc448d3..49b8924 100644 --- a/src/Array.cpp +++ b/src/Array.cpp @@ -40,7 +40,9 @@ static void array_fill(Array *_this, std::vector<Node*> &array, plist_t node) do { subnode = NULL; plist_array_next_item(node, iter, &subnode); - array.push_back( Node::FromPlist(subnode, _this) ); + if (subnode) { + array.push_back( Node::FromPlist(subnode, _this) ); + } } while (subnode); free(iter); } @@ -51,7 +53,7 @@ Array::Array(plist_t node, Node* parent) : Structure(parent) array_fill(this, _array, _node); } -Array::Array(const PList::Array& a) +Array::Array(const PList::Array& a) : Structure(a.GetParent()) { _array.clear(); _node = plist_copy(a.GetPlist()); @@ -88,6 +90,26 @@ Node* Array::operator[](unsigned int array_index) return _array.at(array_index); } +Node* Array::Back() +{ + return _array.back(); +} + +Node* Array::back() +{ + return _array.back(); +} + +Node* Array::Front() +{ + return _array.front(); +} + +Node* Array::front() +{ + return _array.front(); +} + Array::iterator Array::Begin() { return _array.begin(); @@ -132,7 +154,7 @@ size_t Array::size() const { return _array.size(); } -void Array::Append(Node* node) +void Array::Append(const Node* node) { if (node) { @@ -143,7 +165,12 @@ void Array::Append(Node* node) } } -void Array::Insert(Node* node, unsigned int pos) +void Array::Append(const Node& node) +{ + Append(&node); +} + +void Array::Insert(const Node* node, unsigned int pos) { if (node) { @@ -156,6 +183,11 @@ void Array::Insert(Node* node, unsigned int pos) } } +void Array::Insert(const Node &node, unsigned int pos) +{ + Insert(&node, pos); +} + void Array::Remove(Node* node) { if (node) @@ -168,7 +200,7 @@ void Array::Remove(Node* node) std::vector<Node*>::iterator it = _array.begin(); it += pos; _array.erase(it); - delete node; + free(node); } } @@ -181,10 +213,15 @@ void Array::Remove(unsigned int pos) _array.erase(it); } -unsigned int Array::GetNodeIndex(Node* node) const +unsigned int Array::GetNodeIndex(const Node* node) const { std::vector<Node*>::const_iterator it = std::find(_array.begin(), _array.end(), node); return std::distance (_array.begin(), it); } +unsigned int Array::GetNodeIndex(const Node& node) const +{ + return GetNodeIndex(&node); +} + } // namespace PList diff --git a/src/Data.cpp b/src/Data.cpp index a96fc50..56b2d6b 100644 --- a/src/Data.cpp +++ b/src/Data.cpp @@ -50,6 +50,11 @@ Data::Data(const std::vector<char>& buff) : Node(PLIST_DATA) plist_set_data_val(_node, &buff[0], buff.size()); } +Data::Data(const char* buff, uint64_t size) : Node(PLIST_DATA) +{ + plist_set_data_val(_node, buff, size); +} + Data::~Data() { } @@ -66,14 +71,10 @@ void Data::SetValue(const std::vector<char>& buff) std::vector<char> Data::GetValue() const { - char* buff = NULL; uint64_t length = 0; - plist_get_data_val(_node, &buff, &length); + const char* buff = plist_get_data_ptr(_node, &length); std::vector<char> ret(buff, buff + length); - delete buff; return ret; } - - } // namespace PList diff --git a/src/Date.cpp b/src/Date.cpp index 8b8e650..cbfa123 100644 --- a/src/Date.cpp +++ b/src/Date.cpp @@ -34,8 +34,8 @@ Date::Date(plist_t node, Node* parent) : Node(node, parent) Date::Date(const PList::Date& d) : Node(PLIST_DATE) { - timeval t = d.GetValue(); - plist_set_date_val(_node, t.tv_sec, t.tv_usec); + int64_t t = d.GetValue(); + plist_set_unix_date_val(_node, t); } Date& Date::operator=(const PList::Date& d) @@ -45,9 +45,9 @@ Date& Date::operator=(const PList::Date& d) return *this; } -Date::Date(timeval t) : Node(PLIST_DATE) +Date::Date(int64_t t) : Node(PLIST_DATE) { - plist_set_date_val(_node, t.tv_sec, t.tv_usec); + plist_set_unix_date_val(_node, t); } Date::~Date() @@ -59,18 +59,16 @@ Node* Date::Clone() const return new Date(*this); } -void Date::SetValue(timeval t) +void Date::SetValue(int64_t t) { - plist_set_date_val(_node, t.tv_sec, t.tv_usec); + plist_set_unix_date_val(_node, t); } -timeval Date::GetValue() const +int64_t Date::GetValue() const { - int32_t tv_sec = 0; - int32_t tv_usec = 0; - plist_get_date_val(_node, &tv_sec, &tv_usec); - timeval t = {tv_sec, tv_usec}; - return t; + int64_t sec = 0; + plist_get_unix_date_val(_node, &sec); + return sec; } } // namespace PList diff --git a/src/Dictionary.cpp b/src/Dictionary.cpp index 30c20b6..0a909e5 100644 --- a/src/Dictionary.cpp +++ b/src/Dictionary.cpp @@ -40,7 +40,7 @@ static void dictionary_fill(Dictionary *_this, std::map<std::string,Node*> &map, plist_dict_next_item(node, it, &key, &subnode); if (key && subnode) map[std::string(key)] = Node::FromPlist(subnode, _this); - delete key; + free(key); } while (subnode); free(it); } @@ -51,7 +51,7 @@ Dictionary::Dictionary(plist_t node, Node* parent) : Structure(parent) dictionary_fill(this, _map, _node); } -Dictionary::Dictionary(const PList::Dictionary& d) +Dictionary::Dictionary(const PList::Dictionary& d) : Structure(d.GetParent()) { for (Dictionary::iterator it = _map.begin(); it != _map.end(); it++) { @@ -176,9 +176,9 @@ void Dictionary::Remove(Node* node) plist_dict_get_item_key(node->GetPlist(), &key); plist_dict_remove_item(_node, key); std::string skey = key; - delete key; + free(key); _map.erase(skey); - delete node; + free(node); } } diff --git a/src/Integer.cpp b/src/Integer.cpp index 30a5405..653455d 100644 --- a/src/Integer.cpp +++ b/src/Integer.cpp @@ -35,7 +35,8 @@ Integer::Integer(plist_t node, Node* parent) : Node(node, parent) Integer::Integer(const PList::Integer& i) : Node(PLIST_INT) { - plist_set_uint_val(_node, i.GetValue()); + plist_free(_node); + _node = plist_copy(i.GetPlist()); } Integer& Integer::operator=(const PList::Integer& i) diff --git a/src/Key.cpp b/src/Key.cpp index 79265d5..86a0bf8 100644 --- a/src/Key.cpp +++ b/src/Key.cpp @@ -69,7 +69,7 @@ std::string Key::GetValue() const char* s = NULL; plist_get_key_val(_node, &s); std::string ret = s ? s : ""; - delete s; + free(s); return ret; } diff --git a/src/Node.cpp b/src/Node.cpp index 0bd428a..1043b31 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -73,7 +73,7 @@ Node::Node(plist_type type, Node* parent) : _parent(parent) _node = plist_new_data(NULL,0); break; case PLIST_DATE: - _node = plist_new_date(0,0); + _node = plist_new_unix_date(0); break; case PLIST_ARRAY: _node = plist_new_array(); diff --git a/src/String.cpp b/src/String.cpp index 2ddc28b..8daec0f 100644 --- a/src/String.cpp +++ b/src/String.cpp @@ -45,11 +45,30 @@ String& String::operator=(const PList::String& s) return *this; } +String& String::operator=(const std::string& s) +{ + plist_free(_node); + _node = plist_new_string(s.c_str()); + return *this; +} + +String& String::operator=(const char* s) +{ + plist_free(_node); + _node = plist_new_string(s); + return *this; +} + String::String(const std::string& s) : Node(PLIST_STRING) { plist_set_string_val(_node, s.c_str()); } +String::String(const char *s) : Node(PLIST_STRING) +{ + plist_set_string_val(_node, s); +} + String::~String() { } @@ -66,10 +85,8 @@ void String::SetValue(const std::string& s) std::string String::GetValue() const { - char* s = NULL; - plist_get_string_val(_node, &s); + const char* s = plist_get_string_ptr(_node, NULL); std::string ret = s ? s : ""; - delete s; return ret; } diff --git a/src/Structure.cpp b/src/Structure.cpp index 670cce6..65e5ca8 100644 --- a/src/Structure.cpp +++ b/src/Structure.cpp @@ -57,7 +57,7 @@ std::string Structure::ToXml() const uint32_t length = 0; plist_to_xml(_node, &xml, &length); std::string ret(xml, xml+length); - delete xml; + free(xml); return ret; } @@ -67,7 +67,7 @@ std::vector<char> Structure::ToBin() const uint32_t length = 0; plist_to_bin(_node, &bin, &length); std::vector<char> ret(bin, bin+length); - delete bin; + free(bin); return ret; } @@ -77,7 +77,7 @@ void Structure::UpdateNodeParent(Node* node) if ( NULL != node->_parent ) { plist_type type = plist_get_node_type(node->_parent); - if (PLIST_ARRAY ==type || PLIST_DICT == type ) + if (PLIST_ARRAY == type || PLIST_DICT == type) { Structure* s = static_cast<Structure*>(node->_parent); s->Remove(node); @@ -117,8 +117,27 @@ Structure* Structure::FromBin(const std::vector<char>& bin) plist_from_bin(&bin[0], bin.size(), &root); return ImportStruct(root); +} + +Structure* Structure::FromBin(const char* bin, uint64_t size) +{ + plist_t root = NULL; + plist_from_bin(bin, size, &root); + return ImportStruct(root); } -} // namespace PList +Structure* Structure::FromMemory(const std::vector<char>& buf, plist_format_t *format) +{ + return Structure::FromMemory(&buf[0], buf.size(), format); +} +Structure* Structure::FromMemory(const char* buf, uint64_t size, plist_format_t *format) +{ + plist_t root = NULL; + plist_from_memory(buf, size, &root, format); + return ImportStruct(root); +} + + +} // namespace PList diff --git a/src/bplist.c b/src/bplist.c index 93f0bc6..85e55e2 100644 --- a/src/bplist.c +++ b/src/bplist.c @@ -87,6 +87,28 @@ union plist_uint_ptr uint64_t *u64ptr; }; +#ifdef _MSC_VER +uint64_t get_unaligned_64(uint64_t *ptr) +{ + uint64_t temp; + memcpy(&temp, ptr, sizeof(temp)); + return temp; +} + +uint32_t get_unaligned_32(uint32_t *ptr) +{ + uint32_t temp; + memcpy(&temp, ptr, sizeof(temp)); + return temp; +} + +uint16_t get_unaligned_16(uint16_t *ptr) +{ + uint16_t temp; + memcpy(&temp, ptr, sizeof(temp)); + return temp; +} +#else #define get_unaligned(ptr) \ ({ \ struct __attribute__((packed)) { \ @@ -94,6 +116,7 @@ union plist_uint_ptr } *__p = (void *) (ptr); \ __p->__v; \ }) +#endif #ifndef bswap16 @@ -148,17 +171,31 @@ union plist_uint_ptr #define beNtoh(x,n) be64toh((x) << ((8-(n)) << 3)) #endif +#ifdef _MSC_VER +static uint64_t UINT_TO_HOST(const void* x, uint8_t n) +{ + union plist_uint_ptr __up; + __up.src = (n > 8) ? (const char*)x + (n - 8) : (const char*)x; + return (n >= 8 ? be64toh( get_unaligned_64(__up.u64ptr) ) : + (n == 4 ? be32toh( get_unaligned_32(__up.u32ptr) ) : + (n == 2 ? be16toh( get_unaligned_16(__up.u16ptr) ) : + (n == 1 ? *__up.u8ptr : + beNtoh( get_unaligned_64(__up.u64ptr), n) + )))); +} +#else #define UINT_TO_HOST(x, n) \ ({ \ union plist_uint_ptr __up; \ - __up.src = ((n) > 8) ? (x) + ((n) - 8) : (x); \ + __up.src = ((n) > 8) ? (const char*)(x) + ((n) - 8) : (const char*)(x); \ ((n) >= 8 ? be64toh( get_unaligned(__up.u64ptr) ) : \ ((n) == 4 ? be32toh( get_unaligned(__up.u32ptr) ) : \ ((n) == 2 ? be16toh( get_unaligned(__up.u16ptr) ) : \ - ((n) == 1 ? *__up.u8ptr : \ + ((n) == 1 ? *__up.u8ptr : \ beNtoh( get_unaligned(__up.u64ptr), n) \ )))); \ }) +#endif #define get_needed_bytes(x) \ ( ((uint64_t)(x)) < (1ULL << 8) ? 1 : \ @@ -168,15 +205,13 @@ union plist_uint_ptr #define get_real_bytes(x) ((x) == (float) (x) ? sizeof(float) : sizeof(double)) -#if (defined(__LITTLE_ENDIAN__) \ - && !defined(__FLOAT_WORD_ORDER__)) \ - || (defined(__FLOAT_WORD_ORDER__) \ - && __FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__) -#define float_bswap64(x) bswap64(x) -#define float_bswap32(x) bswap32(x) -#else +#if (defined(__BIG_ENDIAN__) && !defined(__FLOAT_WORD_ORDER__)) \ + || (defined(__FLOAT_WORD_ORDER__) && __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__) #define float_bswap64(x) (x) #define float_bswap32(x) (x) +#else +#define float_bswap64(x) bswap64(x) +#define float_bswap32(x) bswap32(x) #endif #ifndef __has_builtin @@ -271,19 +306,30 @@ static plist_t parse_int_node(const char **bnode, uint8_t size) static plist_t parse_real_node(const char **bnode, uint8_t size) { plist_data_t data = plist_new_plist_data(); - uint8_t buf[8]; size = 1 << size; // make length less misleading switch (size) { case sizeof(uint32_t): - *(uint32_t*)buf = float_bswap32(get_unaligned((uint32_t*)*bnode)); - data->realval = *(float *) buf; - break; + { + uint32_t ival; + memcpy(&ival, *bnode, sizeof(uint32_t)); + ival = float_bswap32(ival); + float fval; + memcpy(&fval, &ival, sizeof(float)); + data->realval = fval; + } + break; + case sizeof(uint64_t): - *(uint64_t*)buf = float_bswap64(get_unaligned((uint64_t*)*bnode)); - data->realval = *(double *) buf; + { + uint64_t ival; + memcpy(&ival, *bnode, sizeof(uint64_t)); + ival = float_bswap64(ival); + memcpy(&data->realval, &ival, sizeof(double)); break; + } + default: free(data); PLIST_BIN_ERR("%s: Invalid byte size for real node\n", __func__); @@ -343,7 +389,7 @@ static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, } while (i < len) { - wc = be16toh(get_unaligned(unistr + i)); + wc = UINT_TO_HOST(unistr + i, sizeof(wc)); i++; if (wc >= 0xD800 && wc <= 0xDBFF) { if (!read_lead_surrogate) { diff --git a/src/jplist.c b/src/jplist.c index 782d2b3..1c7a932 100644 --- a/src/jplist.c +++ b/src/jplist.c @@ -72,8 +72,10 @@ void plist_json_set_debug(int debug) } #ifndef HAVE_STRNDUP +#ifndef _MSC_VER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" +#endif static char* strndup(const char* str, size_t len) { char *newstr = (char *)malloc(len+1); @@ -83,8 +85,10 @@ static char* strndup(const char* str, size_t len) } return newstr; } +#ifndef _MSC_VER #pragma GCC diagnostic pop #endif +#endif static size_t dtostr(char *buf, size_t bufsize, double realval) { @@ -460,6 +464,8 @@ static int64_t parse_decimal(const char* str, const char* str_end, char** endp) if (str[0] == '-') { is_neg = 1; (*endp)++; + } else if (str[0] == '+') { + (*endp)++; } if (is_neg) { MAX++; @@ -522,7 +528,7 @@ static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index) } else { val = plist_new_uint((uint64_t)intpart); } - } else if ((*endp == '.' && endp+1 < str_end && isdigit(*(endp+1))) || ((*endp == 'e' || *endp == 'E') && endp+1 < str_end && (isdigit(*(endp+1)) || ((*(endp+1) == '-') && endp+2 < str_end && isdigit(*(endp+2)))))) { + } else if ((*endp == '.' && endp+1 < str_end && isdigit(*(endp+1))) || ((*endp == 'e' || *endp == 'E') && endp+1 < str_end && (isdigit(*(endp+1)) || (((*(endp+1) == '-') || (*(endp+1) == '+')) && endp+2 < str_end && isdigit(*(endp+2)))))) { /* floating point */ double dval = (double)intpart; char* fendp = endp; @@ -546,7 +552,7 @@ static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index) if (fendp >= str_end) { break; } - if (fendp+1 < str_end && (*fendp == 'e' || *fendp == 'E') && (isdigit(*(fendp+1)) || ((*(fendp+1) == '-') && fendp+2 < str_end && isdigit(*(fendp+2))))) { + if (fendp+1 < str_end && (*fendp == 'e' || *fendp == 'E') && (isdigit(*(fendp+1)) || (((*(fendp+1) == '-') || (*(fendp+1) == '+')) && fendp+2 < str_end && isdigit(*(fendp+2))))) { int64_t exp = parse_decimal(fendp+1, str_end, &fendp); dval = dval * pow(10, (double)exp); } else { diff --git a/src/oplist.c b/src/oplist.c index 6ab6603..33896f9 100644 --- a/src/oplist.c +++ b/src/oplist.c @@ -71,8 +71,10 @@ void plist_ostep_set_debug(int debug) } #ifndef HAVE_STRNDUP +#ifndef _MSC_VER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" +#endif static char* strndup(const char* str, size_t len) { char *newstr = (char *)malloc(len+1); @@ -82,8 +84,10 @@ static char* strndup(const char* str, size_t len) } return newstr; } +#ifndef _MSC_VER #pragma GCC diagnostic pop #endif +#endif static size_t dtostr(char *buf, size_t bufsize, double realval) { diff --git a/src/plist.c b/src/plist.c index 2078520..fe98f34 100644 --- a/src/plist.c +++ b/src/plist.c @@ -35,11 +35,10 @@ #include <limits.h> #include <float.h> #include <ctype.h> +#include <inttypes.h> #ifdef WIN32 #include <windows.h> -#else -#include <pthread.h> #endif #include <node.h> @@ -47,10 +46,92 @@ #include <hashtable.h> #include <ptrarray.h> +#define MAC_EPOCH 978307200 + #ifdef _MSC_VER typedef SSIZE_T ssize_t; #endif +#ifdef DEBUG +static int plist_debug = 0; +#define PLIST_ERR(...) if (plist_debug > 0) { fprintf(stderr, "libplist ERROR: " __VA_ARGS__); } +#else +#define PLIST_ERR(...) +#endif + +#ifndef bswap16 +#define bswap16(x) ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8)) +#endif + +#ifndef bswap32 +#define bswap32(x) ((((x) & 0xFF000000) >> 24) \ + | (((x) & 0x00FF0000) >> 8) \ + | (((x) & 0x0000FF00) << 8) \ + | (((x) & 0x000000FF) << 24)) +#endif + +#ifndef bswap64 +#define bswap64(x) ((((x) & 0xFF00000000000000ull) >> 56) \ + | (((x) & 0x00FF000000000000ull) >> 40) \ + | (((x) & 0x0000FF0000000000ull) >> 24) \ + | (((x) & 0x000000FF00000000ull) >> 8) \ + | (((x) & 0x00000000FF000000ull) << 8) \ + | (((x) & 0x0000000000FF0000ull) << 24) \ + | (((x) & 0x000000000000FF00ull) << 40) \ + | (((x) & 0x00000000000000FFull) << 56)) +#endif + +#ifndef le16toh +#ifdef __BIG_ENDIAN__ +#define le16toh(x) bswap16(x) +#else +#define le16toh(x) (x) +#endif +#endif + +#ifndef le32toh +#ifdef __BIG_ENDIAN__ +#define le32toh(x) bswap32(x) +#else +#define le32toh(x) (x) +#endif +#endif + +#ifndef le64toh +#ifdef __BIG_ENDIAN__ +#define le64toh(x) bswap64(x) +#else +#define le64toh(x) (x) +#endif +#endif + +// Reference: https://stackoverflow.com/a/2390626/1806760 +// Initializer/finalizer sample for MSVC and GCC/Clang. +// 2010-2016 Joe Lowe. Released into the public domain. + +#ifdef __cplusplus + #define INITIALIZER(f) \ + static void f(void); \ + struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \ + static void f(void) +#elif defined(_MSC_VER) + #pragma section(".CRT$XCU",read) + #define INITIALIZER2_(f,p) \ + static void f(void); \ + __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ + __pragma(comment(linker,"/include:" p #f "_")) \ + static void f(void) + #ifdef _WIN64 + #define INITIALIZER(f) INITIALIZER2_(f,"") + #else + #define INITIALIZER(f) INITIALIZER2_(f,"_") + #endif +#else + #define INITIALIZER(f) \ + static void f(void) __attribute__((__constructor__)); \ + static void f(void) +#endif + extern void plist_xml_init(void); extern void plist_xml_deinit(void); extern void plist_bin_init(void); @@ -60,14 +141,6 @@ extern void plist_json_deinit(void); extern void plist_ostep_init(void); extern void plist_ostep_deinit(void); -static void internal_plist_init(void) -{ - plist_bin_init(); - plist_xml_init(); - plist_json_init(); - plist_ostep_init(); -} - static void internal_plist_deinit(void) { plist_bin_deinit(); @@ -76,66 +149,14 @@ static void internal_plist_deinit(void) plist_ostep_deinit(); } -#ifdef WIN32 -typedef volatile struct { - LONG lock; - int state; -} thread_once_t; - -static thread_once_t init_once = {0, 0}; -static thread_once_t deinit_once = {0, 0}; - -static void thread_once(thread_once_t *once_control, void (*init_routine)(void)) +INITIALIZER(internal_plist_init) { - while (InterlockedExchange(&(once_control->lock), 1) != 0) { - Sleep(1); - } - if (!once_control->state) { - once_control->state = 1; - init_routine(); - } - InterlockedExchange(&(once_control->lock), 0); -} -#else -static pthread_once_t init_once = PTHREAD_ONCE_INIT; -static pthread_once_t deinit_once = PTHREAD_ONCE_INIT; -#define thread_once pthread_once -#endif - -#ifndef HAVE_ATTRIBUTE_CONSTRUCTOR - #if defined(__llvm__) || defined(__GNUC__) - #define HAVE_ATTRIBUTE_CONSTRUCTOR - #endif -#endif - -#ifdef HAVE_ATTRIBUTE_CONSTRUCTOR -static void __attribute__((constructor)) libplist_initialize(void) -{ - thread_once(&init_once, internal_plist_init); -} - -static void __attribute__((destructor)) libplist_deinitialize(void) -{ - thread_once(&deinit_once, internal_plist_deinit); -} -#elif defined(WIN32) -BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) -{ - switch (dwReason) { - case DLL_PROCESS_ATTACH: - thread_once(&init_once, internal_plist_init); - break; - case DLL_PROCESS_DETACH: - thread_once(&deinit_once, internal_plist_deinit); - break; - default: - break; - } - return 1; + plist_bin_init(); + plist_xml_init(); + plist_json_init(); + plist_ostep_init(); + atexit(internal_plist_deinit); } -#else -#warning No compiler support for constructor/destructor attributes, some features might not be available. -#endif #ifndef HAVE_MEMMEM // see https://sourceware.org/legacy-ml/libc-alpha/2007-12/msg00000.html @@ -337,7 +358,7 @@ plist_data_t plist_get_data(plist_t node) plist_data_t plist_new_plist_data(void) { - plist_data_t data = (plist_data_t) calloc(sizeof(struct plist_data_s), 1); + plist_data_t data = (plist_data_t) calloc(1, sizeof(struct plist_data_s)); return data; } @@ -509,6 +530,15 @@ plist_t plist_new_date(int32_t sec, int32_t usec) return plist_new_node(data); } +plist_t plist_new_unix_date(int64_t sec) +{ + plist_data_t data = plist_new_plist_data(); + data->type = PLIST_DATE; + data->realval = (double)sec - MAC_EPOCH; + data->length = sizeof(double); + return plist_new_node(data); +} + plist_t plist_new_null(void) { plist_data_t data = plist_new_plist_data(); @@ -957,6 +987,195 @@ void plist_dict_merge(plist_t *target, plist_t source) free(it); } +uint8_t plist_dict_get_bool(plist_t dict, const char *key) +{ + uint8_t bval = 0; + uint64_t uintval = 0; + const char *strval = NULL; + uint64_t strsz = 0; + plist_t node = plist_dict_get_item(dict, key); + if (!node) { + return 0; + } + switch (plist_get_node_type(node)) { + case PLIST_BOOLEAN: + plist_get_bool_val(node, &bval); + break; + case PLIST_INT: + plist_get_uint_val(node, &uintval); + bval = (uintval) ? 1 : 0; + break; + case PLIST_STRING: + strval = plist_get_string_ptr(node, NULL); + if (strval) { + if (strcmp(strval, "true")) { + bval = 1; + } else if (strcmp(strval, "false")) { + bval = 0; + } else { + PLIST_ERR("%s: invalid string '%s' for string to boolean conversion\n", __func__, strval); + } + } + break; + case PLIST_DATA: + strval = (const char*)plist_get_data_ptr(node, &strsz); + if (strval) { + if (strsz == 1) { + bval = (strval[0]) ? 1 : 0; + } else { + PLIST_ERR("%s: invalid size %" PRIu64 " for data to boolean conversion\n", __func__, strsz); + } + } + break; + default: + break; + } + return bval; +} + +int64_t plist_dict_get_int(plist_t dict, const char *key) +{ + int64_t intval = 0; + const char *strval = NULL; + uint64_t strsz = 0; + plist_t node = plist_dict_get_item(dict, key); + if (!node) { + return intval; + } + switch (plist_get_node_type(node)) { + case PLIST_INT: + plist_get_int_val(node, &intval); + break; + case PLIST_STRING: + strval = plist_get_string_ptr(node, NULL); + if (strval) { + intval = strtoll(strval, NULL, 0); + } + break; + case PLIST_DATA: + strval = (const char*)plist_get_data_ptr(node, &strsz); + if (strval) { + if (strsz == 8) { + intval = le64toh(*(int64_t*)strval); + } else if (strsz == 4) { + intval = le32toh(*(int32_t*)strval); + } else if (strsz == 2) { + intval = le16toh(*(int16_t*)strval); + } else if (strsz == 1) { + intval = strval[0]; + } else { + PLIST_ERR("%s: invalid size %" PRIu64 " for data to integer conversion\n", __func__, strsz); + } + } + break; + default: + break; + } + return intval; +} + + +uint64_t plist_dict_get_uint(plist_t dict, const char *key) +{ + uint64_t uintval = 0; + const char *strval = NULL; + uint64_t strsz = 0; + plist_t node = plist_dict_get_item(dict, key); + if (!node) { + return uintval; + } + switch (plist_get_node_type(node)) { + case PLIST_INT: + plist_get_uint_val(node, &uintval); + break; + case PLIST_STRING: + strval = plist_get_string_ptr(node, NULL); + if (strval) { + uintval = strtoull(strval, NULL, 0); + } + break; + case PLIST_DATA: + strval = (const char*)plist_get_data_ptr(node, &strsz); + if (strval) { + if (strsz == 8) { + uintval = le64toh(*(uint64_t*)strval); + } else if (strsz == 4) { + uintval = le32toh(*(uint32_t*)strval); + } else if (strsz == 2) { + uintval = le16toh(*(uint16_t*)strval); + } else if (strsz == 1) { + uintval = strval[0]; + } else { + PLIST_ERR("%s: invalid size %" PRIu64 " for data to integer conversion\n", __func__, strsz); + } + } + break; + default: + break; + } + return uintval; +} + +plist_err_t plist_dict_copy_item(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key); + if (!node) { + return PLIST_ERR_INVALID_ARG; + } + plist_dict_set_item(target_dict, key, plist_copy(node)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_bool(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) { + return PLIST_ERR_INVALID_ARG; + } + uint8_t bval = plist_dict_get_bool(source_dict, (alt_source_key) ? alt_source_key : key); + plist_dict_set_item(target_dict, key, plist_new_bool(bval)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_int(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) { + return PLIST_ERR_INVALID_ARG; + } + int64_t i64val = plist_dict_get_int(source_dict, (alt_source_key) ? alt_source_key : key); + plist_dict_set_item(target_dict, key, plist_new_int(i64val)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_uint(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) { + return PLIST_ERR_INVALID_ARG; + } + uint64_t u64val = plist_dict_get_uint(source_dict, (alt_source_key) ? alt_source_key : key); + plist_dict_set_item(target_dict, key, plist_new_uint(u64val)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_data(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key); + if (!PLIST_IS_DATA(node)) { + return PLIST_ERR_INVALID_ARG; + } + plist_dict_set_item(target_dict, key, plist_copy(node)); + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_dict_copy_string(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) +{ + plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key); + if (!PLIST_IS_STRING(node)) { + return PLIST_ERR_INVALID_ARG; + } + plist_dict_set_item(target_dict, key, plist_copy(node)); + return PLIST_ERR_SUCCESS; +} + plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v) { plist_t current = plist; @@ -1184,6 +1403,20 @@ void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec) } } +void plist_get_unix_date_val(plist_t node, int64_t *sec) +{ + if (!node || !sec) + return; + plist_type type = plist_get_node_type(node); + uint64_t length = 0; + double val = 0; + if (PLIST_DATE != type) + return; + plist_get_type_and_value(node, &type, (void *) &val, &length); + assert(length == sizeof(double)); + *sec = (int64_t)val + MAC_EPOCH; +} + int plist_data_compare(const void *a, const void *b) { plist_data_t val_a = NULL; @@ -1340,7 +1573,13 @@ void plist_set_data_val(plist_t node, const char *val, uint64_t length) void plist_set_date_val(plist_t node, int32_t sec, int32_t usec) { double val = (double)sec + (double)usec / 1000000; - plist_set_element_val(node, PLIST_DATE, &val, sizeof(struct timeval)); + plist_set_element_val(node, PLIST_DATE, &val, sizeof(double)); +} + +void plist_set_unix_date_val(plist_t node, int64_t sec) +{ + double val = (double)(sec - MAC_EPOCH); + plist_set_element_val(node, PLIST_DATE, &val, sizeof(double)); } int plist_bool_val_is_true(plist_t boolnode) @@ -1462,9 +1701,12 @@ 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); + plist_data_t data = plist_get_data(datenode); + assert(data->length == sizeof(double)); + double val = data->realval; + int32_t sec = (int32_t)val; + val = fabs((val - (int64_t)val) * 1000000); + int32_t usec = (int32_t)val; uint64_t dateval = ((int64_t)sec << 32) | usec; uint64_t cmpval = ((int64_t)cmpsec << 32) | cmpusec; if (dateval == cmpval) { @@ -1478,6 +1720,24 @@ int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec) return 1; } +int plist_unix_date_val_compare(plist_t datenode, int64_t cmpval) +{ + if (!PLIST_IS_DATE(datenode)) { + return -1; + } + int64_t dateval = 0; + plist_get_unix_date_val(datenode, &dateval); + if (dateval == cmpval) { + return 0; + } + + if (dateval < cmpval) { + return -1; + } + + return 1; +} + int plist_string_val_compare(plist_t strnode, const char* cmpval) { if (!PLIST_IS_STRING(strnode)) { @@ -1577,6 +1837,9 @@ extern void plist_ostep_set_debug(int debug); void plist_set_debug(int debug) { +#if DEBUG + plist_debug = debug; +#endif plist_xml_set_debug(debug); plist_bin_set_debug(debug); plist_json_set_debug(debug); @@ -1597,6 +1860,9 @@ void plist_sort(plist_t plist) } else if (PLIST_IS_DICT(plist)) { node_t node = (node_t)plist; node_t ch; + if (!node_first_child(node)) { + return; + } for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { ch = node_next_sibling(ch); plist_sort((plist_t)ch); diff --git a/tools/Makefile.am b/tools/Makefile.am index 9f3214d..5883286 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,3 +1,5 @@ +if BUILD_TOOLS + AM_CFLAGS = \ $(GLOBAL_CFLAGS) \ -I$(top_srcdir)/include @@ -8,3 +10,5 @@ bin_PROGRAMS = plistutil plistutil_SOURCES = plistutil.c plistutil_LDADD = $(top_builddir)/src/libplist-2.0.la + +endif diff --git a/tools/plistutil.c b/tools/plistutil.c index 8121a7d..b6b64d3 100644 --- a/tools/plistutil.c +++ b/tools/plistutil.c @@ -32,10 +32,13 @@ #include <string.h> #include <sys/stat.h> #include <errno.h> +#ifndef _MSC_VER #include <unistd.h> +#endif #ifdef _MSC_VER #pragma warning(disable:4996) +#define STDIN_FILENO _fileno(stdin) #endif typedef struct _options @@ -170,7 +173,7 @@ static options_t *parse_arguments(int argc, char *argv[]) } else if (!strcmp(argv[i], "--version") || !strcmp(argv[i], "-v")) { - printf("plistutil %s\n", PACKAGE_VERSION); + printf("plistutil %s\n", libplist_version()); exit(EXIT_SUCCESS); } else |