diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Array.cpp | 66 | ||||
-rw-r--r-- | src/Boolean.cpp | 7 | ||||
-rw-r--r-- | src/Data.cpp | 20 | ||||
-rw-r--r-- | src/Date.cpp | 6 | ||||
-rw-r--r-- | src/Dictionary.cpp | 42 | ||||
-rw-r--r-- | src/Integer.cpp | 42 | ||||
-rw-r--r-- | src/Key.cpp | 18 | ||||
-rw-r--r-- | src/Makefile.am | 102 | ||||
-rw-r--r-- | src/Node.cpp | 9 | ||||
-rw-r--r-- | src/Real.cpp | 8 | ||||
-rw-r--r-- | src/String.cpp | 18 | ||||
-rw-r--r-- | src/Structure.cpp | 9 | ||||
-rw-r--r-- | src/Uid.cpp | 7 | ||||
-rw-r--r-- | src/bplist.c | 216 | ||||
-rw-r--r-- | src/bytearray.c | 34 | ||||
-rw-r--r-- | src/bytearray.h | 3 | ||||
-rw-r--r-- | src/hashtable.c | 10 | ||||
-rw-r--r-- | src/jplist.c | 856 | ||||
-rw-r--r-- | src/jsmn.c | 289 | ||||
-rw-r--r-- | src/jsmn.h | 92 | ||||
-rw-r--r-- | src/libplist++-2.0.pc.in (renamed from src/libplist++.pc.in) | 4 | ||||
-rw-r--r-- | src/libplist-2.0.pc.in (renamed from src/libplist.pc.in) | 2 | ||||
-rw-r--r-- | src/oplist.c | 933 | ||||
-rw-r--r-- | src/out-default.c | 491 | ||||
-rw-r--r-- | src/out-limd.c | 449 | ||||
-rw-r--r-- | src/out-plutil.c | 465 | ||||
-rw-r--r-- | src/plist.c | 1061 | ||||
-rw-r--r-- | src/plist.h | 22 | ||||
-rw-r--r-- | src/ptrarray.c | 2 | ||||
-rw-r--r-- | src/strbuf.h | 1 | ||||
-rw-r--r-- | src/time64.c | 55 | ||||
-rw-r--r-- | src/time64.h | 2 | ||||
-rw-r--r-- | src/xplist.c | 190 |
33 files changed, 4983 insertions, 548 deletions
diff --git a/src/Array.cpp b/src/Array.cpp index d5d9d7c..bc448d3 100644 --- a/src/Array.cpp +++ b/src/Array.cpp @@ -18,11 +18,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> -#include <plist/Array.h> - +#include <cstdlib> #include <algorithm> -#include <limits.h> +#include <climits> +#include "plist.h" +#include <plist/Array.h> namespace PList { @@ -32,7 +32,7 @@ Array::Array(Node* parent) : Structure(PLIST_ARRAY, parent) _array.clear(); } -static void array_fill(Array *_this, std::vector<Node*> array, plist_t node) +static void array_fill(Array *_this, std::vector<Node*> &array, plist_t node) { plist_array_iter iter = NULL; plist_array_new_iter(node, &iter); @@ -51,18 +51,17 @@ Array::Array(plist_t node, Node* parent) : Structure(parent) array_fill(this, _array, _node); } -Array::Array(const PList::Array& a) : Structure() +Array::Array(const PList::Array& a) { _array.clear(); _node = plist_copy(a.GetPlist()); array_fill(this, _array, _node); } -Array& Array::operator=(PList::Array& a) +Array& Array::operator=(const PList::Array& a) { plist_free(_node); - for (unsigned int it = 0; it < _array.size(); it++) - { + for (size_t it = 0; it < _array.size(); it++) { delete _array.at(it); } _array.clear(); @@ -73,8 +72,7 @@ Array& Array::operator=(PList::Array& a) Array::~Array() { - for (unsigned int it = 0; it < _array.size(); it++) - { + for (size_t it = 0; it < _array.size(); it++) { delete (_array.at(it)); } _array.clear(); @@ -90,6 +88,50 @@ Node* Array::operator[](unsigned int array_index) return _array.at(array_index); } +Array::iterator Array::Begin() +{ + return _array.begin(); +} + +Array::iterator Array::begin() +{ + return _array.begin(); +} + +Array::iterator Array::End() +{ + return _array.end(); +} + +Array::iterator Array::end() +{ + return _array.end(); +} + +Array::const_iterator Array::Begin() const +{ + return _array.begin(); +} + +Array::const_iterator Array::begin() const +{ + return _array.begin(); +} + +Array::const_iterator Array::End() const +{ + return _array.end(); +} + +Array::const_iterator Array::end() const +{ + return _array.end(); +} + +size_t Array::size() const { + return _array.size(); +} + void Array::Append(Node* node) { if (node) @@ -145,4 +187,4 @@ unsigned int Array::GetNodeIndex(Node* node) const return std::distance (_array.begin(), it); } -}; +} // namespace PList diff --git a/src/Boolean.cpp b/src/Boolean.cpp index 4608eaf..9ec1a63 100644 --- a/src/Boolean.cpp +++ b/src/Boolean.cpp @@ -18,7 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> +#include <cstdlib> +#include "plist.h" #include <plist/Boolean.h> namespace PList @@ -37,7 +38,7 @@ Boolean::Boolean(const PList::Boolean& b) : Node(PLIST_BOOLEAN) plist_set_bool_val(_node, b.GetValue()); } -Boolean& Boolean::operator=(PList::Boolean& b) +Boolean& Boolean::operator=(const PList::Boolean& b) { plist_free(_node); _node = plist_copy(b.GetPlist()); @@ -70,4 +71,4 @@ bool Boolean::GetValue() const return b != 0 ; } -}; +} // namespace PList diff --git a/src/Data.cpp b/src/Data.cpp index 2e93007..c4709f7 100644 --- a/src/Data.cpp +++ b/src/Data.cpp @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> +#include <cstdlib> #include <plist/Data.h> namespace PList @@ -34,18 +34,18 @@ Data::Data(plist_t node, Node* parent) : Node(node, parent) Data::Data(const PList::Data& d) : Node(PLIST_DATA) { - std::vector<char> b = d.GetValue(); + std::vector<uint8_t> b = d.GetValue(); plist_set_data_val(_node, &b[0], b.size()); } -Data& Data::operator=(PList::Data& b) +Data& Data::operator=(const PList::Data& b) { plist_free(_node); _node = plist_copy(b.GetPlist()); return *this; } -Data::Data(const std::vector<char>& buff) : Node(PLIST_DATA) +Data::Data(const std::vector<uint8_t>& buff) : Node(PLIST_DATA) { plist_set_data_val(_node, &buff[0], buff.size()); } @@ -59,21 +59,21 @@ Node* Data::Clone() const return new Data(*this); } -void Data::SetValue(const std::vector<char>& buff) +void Data::SetValue(const std::vector<uint8_t>& buff) { plist_set_data_val(_node, &buff[0], buff.size()); } -std::vector<char> Data::GetValue() const +std::vector<uint8_t> Data::GetValue() const { - char* buff = NULL; + uint8_t* buff = NULL; uint64_t length = 0; plist_get_data_val(_node, &buff, &length); - std::vector<char> ret(buff, buff + length); - free(buff); + std::vector<uint8_t> ret(buff, buff + length); + delete buff; return ret; } -}; +} // namespace PList diff --git a/src/Date.cpp b/src/Date.cpp index 4b5e0a1..8b8e650 100644 --- a/src/Date.cpp +++ b/src/Date.cpp @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> +#include <cstdlib> #include <plist/Date.h> namespace PList @@ -38,7 +38,7 @@ Date::Date(const PList::Date& d) : Node(PLIST_DATE) plist_set_date_val(_node, t.tv_sec, t.tv_usec); } -Date& Date::operator=(PList::Date& d) +Date& Date::operator=(const PList::Date& d) { plist_free(_node); _node = plist_copy(d.GetPlist()); @@ -73,4 +73,4 @@ timeval Date::GetValue() const return t; } -}; +} // namespace PList diff --git a/src/Dictionary.cpp b/src/Dictionary.cpp index 59908c6..30c20b6 100644 --- a/src/Dictionary.cpp +++ b/src/Dictionary.cpp @@ -18,7 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> +#include <cstdlib> +#include "plist.h" #include <plist/Dictionary.h> namespace PList @@ -39,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); - free(key); + delete key; } while (subnode); free(it); } @@ -50,7 +51,7 @@ Dictionary::Dictionary(plist_t node, Node* parent) : Structure(parent) dictionary_fill(this, _map, _node); } -Dictionary::Dictionary(const PList::Dictionary& d) : Structure() +Dictionary::Dictionary(const PList::Dictionary& d) { for (Dictionary::iterator it = _map.begin(); it != _map.end(); it++) { @@ -62,7 +63,7 @@ Dictionary::Dictionary(const PList::Dictionary& d) : Structure() dictionary_fill(this, _map, _node); } -Dictionary& Dictionary::operator=(PList::Dictionary& d) +Dictionary& Dictionary::operator=(const PList::Dictionary& d) { for (Dictionary::iterator it = _map.begin(); it != _map.end(); it++) { @@ -99,21 +100,45 @@ Dictionary::iterator Dictionary::Begin() return _map.begin(); } +Dictionary::iterator Dictionary::begin() +{ + return _map.begin(); +} + Dictionary::iterator Dictionary::End() { return _map.end(); } +Dictionary::iterator Dictionary::end() +{ + return _map.end(); +} + Dictionary::const_iterator Dictionary::Begin() const { return _map.begin(); } +Dictionary::const_iterator Dictionary::begin() const +{ + return _map.begin(); +} + Dictionary::const_iterator Dictionary::End() const { return _map.end(); } +Dictionary::const_iterator Dictionary::end() const +{ + return _map.end(); +} + +size_t Dictionary::size() const { + return _map.size(); +} + Dictionary::iterator Dictionary::Find(const std::string& key) { return _map.find(key); @@ -143,11 +168,6 @@ Dictionary::iterator Dictionary::Set(const std::string& key, const Node& node) return Set(key, &node); } -Dictionary::iterator Dictionary::Insert(const std::string& key, Node* node) -{ - return this->Set(key, node); -} - void Dictionary::Remove(Node* node) { if (node) @@ -156,7 +176,7 @@ void Dictionary::Remove(Node* node) plist_dict_get_item_key(node->GetPlist(), &key); plist_dict_remove_item(_node, key); std::string skey = key; - free(key); + delete key; _map.erase(skey); delete node; } @@ -179,4 +199,4 @@ std::string Dictionary::GetNodeKey(Node* node) return ""; } -}; +} // namespace PList diff --git a/src/Integer.cpp b/src/Integer.cpp index 04315d7..30a5405 100644 --- a/src/Integer.cpp +++ b/src/Integer.cpp @@ -18,13 +18,14 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> +#include <cstdlib> +#include "plist.h" #include <plist/Integer.h> namespace PList { -Integer::Integer(Node* parent) : Node(PLIST_UINT, parent) +Integer::Integer(Node* parent) : Node(PLIST_INT, parent) { } @@ -32,23 +33,28 @@ Integer::Integer(plist_t node, Node* parent) : Node(node, parent) { } -Integer::Integer(const PList::Integer& i) : Node(PLIST_UINT) +Integer::Integer(const PList::Integer& i) : Node(PLIST_INT) { plist_set_uint_val(_node, i.GetValue()); } -Integer& Integer::operator=(PList::Integer& i) +Integer& Integer::operator=(const PList::Integer& i) { plist_free(_node); _node = plist_copy(i.GetPlist()); return *this; } -Integer::Integer(uint64_t i) : Node(PLIST_UINT) +Integer::Integer(uint64_t i) : Node(PLIST_INT) { plist_set_uint_val(_node, i); } +Integer::Integer(int64_t i) : Node(PLIST_INT) +{ + plist_set_int_val(_node, i); +} + Integer::~Integer() { } @@ -58,16 +64,38 @@ Node* Integer::Clone() const return new Integer(*this); } +void Integer::SetValue(int64_t i) +{ + plist_set_int_val(_node, i); +} + void Integer::SetValue(uint64_t i) { plist_set_uint_val(_node, i); } -uint64_t Integer::GetValue() const +void Integer::SetUnsignedValue(uint64_t i) +{ + plist_set_uint_val(_node, i); +} + +int64_t Integer::GetValue() const +{ + int64_t i = 0; + plist_get_int_val(_node, &i); + return i; +} + +uint64_t Integer::GetUnsignedValue() const { uint64_t i = 0; plist_get_uint_val(_node, &i); return i; } -}; +bool Integer::isNegative() const +{ + return plist_int_val_is_negative(_node); +} + +} // namespace PList diff --git a/src/Key.cpp b/src/Key.cpp index ed0c0c6..79265d5 100644 --- a/src/Key.cpp +++ b/src/Key.cpp @@ -18,7 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> +#include <cstdlib> +#include "plist.h" #include <plist/Key.h> namespace PList @@ -32,12 +33,12 @@ Key::Key(plist_t node, Node* parent) : Node(node, parent) { } -Key::Key(const PList::Key& k) : Node(PLIST_UINT) +Key::Key(const PList::Key& k) : Node(PLIST_INT) { plist_set_key_val(_node, k.GetValue().c_str()); } -Key& Key::operator=(PList::Key& k) +Key& Key::operator=(const PList::Key& k) { plist_free(_node); _node = plist_copy(k.GetPlist()); @@ -67,14 +68,9 @@ std::string Key::GetValue() const { char* s = NULL; plist_get_key_val(_node, &s); - std::string ret; - if (s) { - ret = s; - free(s); - } else { - ret = ""; - } + std::string ret = s ? s : ""; + delete s; return ret; } -}; +} // namespace PList diff --git a/src/Makefile.am b/src/Makefile.am index 995bddd..1a416ad 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,52 +1,70 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir) -I$(top_srcdir)/libcnary/include +AM_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir) \ + -I$(top_srcdir)/libcnary/include AM_CFLAGS = $(GLOBAL_CFLAGS) +AM_CXXFLAGS = $(GLOBAL_CXXFLAGS) AM_LDFLAGS = $(GLOBAL_LDFLAGS) -lib_LTLIBRARIES = libplist.la libplist++.la -libplist_la_LIBADD = $(top_builddir)/libcnary/libcnary.la -libplist_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPLIST_SO_VERSION) -no-undefined -libplist_la_SOURCES = base64.c base64.h \ - bytearray.c bytearray.h \ - strbuf.h \ - hashtable.c hashtable.h \ - ptrarray.c ptrarray.h \ - time64.c time64.h time64_limits.h \ - xplist.c \ - bplist.c \ - plist.c plist.h +lib_LTLIBRARIES = \ + libplist-2.0.la \ + libplist++-2.0.la -libplist___la_LIBADD = libplist.la -libplist___la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPLIST_SO_VERSION) -no-undefined -libplist___la_SOURCES = Node.cpp \ - Structure.cpp \ - Array.cpp \ - Boolean.cpp \ - Data.cpp \ - Date.cpp \ - Dictionary.cpp \ - Integer.cpp \ - Key.cpp \ - Real.cpp \ - String.cpp \ - Uid.cpp \ - $(top_srcdir)/include/plist/Node.h \ - $(top_srcdir)/include/plist/Structure.h \ - $(top_srcdir)/include/plist/Array.h \ - $(top_srcdir)/include/plist/Boolean.h \ - $(top_srcdir)/include/plist/Data.h \ - $(top_srcdir)/include/plist/Date.h \ - $(top_srcdir)/include/plist/Dictionary.h \ - $(top_srcdir)/include/plist/Integer.h \ - $(top_srcdir)/include/plist/Key.h \ - $(top_srcdir)/include/plist/Real.h \ - $(top_srcdir)/include/plist/String.h \ - $(top_srcdir)/include/plist/Uid.h +libplist_2_0_la_LIBADD = $(top_builddir)/libcnary/libcnary.la +libplist_2_0_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPLIST_SO_VERSION) -no-undefined +libplist_2_0_la_SOURCES = \ + base64.c base64.h \ + bytearray.c bytearray.h \ + strbuf.h \ + hashtable.c hashtable.h \ + ptrarray.c ptrarray.h \ + time64.c time64.h \ + time64_limits.h \ + xplist.c \ + bplist.c \ + jsmn.c jsmn.h \ + jplist.c \ + oplist.c \ + out-default.c \ + out-plutil.c \ + out-limd.c \ + plist.c plist.h + +libplist___2_0_la_LIBADD = libplist-2.0.la +libplist___2_0_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPLIST_SO_VERSION) -no-undefined +libplist___2_0_la_SOURCES = \ + Node.cpp \ + Structure.cpp \ + Array.cpp \ + Boolean.cpp \ + Data.cpp \ + Date.cpp \ + Dictionary.cpp \ + Integer.cpp \ + Key.cpp \ + Real.cpp \ + String.cpp \ + Uid.cpp \ + $(top_srcdir)/include/plist/Node.h \ + $(top_srcdir)/include/plist/Structure.h \ + $(top_srcdir)/include/plist/Array.h \ + $(top_srcdir)/include/plist/Boolean.h \ + $(top_srcdir)/include/plist/Data.h \ + $(top_srcdir)/include/plist/Date.h \ + $(top_srcdir)/include/plist/Dictionary.h \ + $(top_srcdir)/include/plist/Integer.h \ + $(top_srcdir)/include/plist/Key.h \ + $(top_srcdir)/include/plist/Real.h \ + $(top_srcdir)/include/plist/String.h \ + $(top_srcdir)/include/plist/Uid.h if WIN32 -libplist_la_LDFLAGS += -avoid-version -static-libgcc -libplist___la_LDFLAGS += -avoid-version -static-libgcc +libplist_2_0_la_LDFLAGS += -avoid-version -static-libgcc +libplist___2_0_la_LDFLAGS += -avoid-version -static-libgcc endif pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = libplist.pc libplist++.pc +pkgconfig_DATA = \ + libplist-2.0.pc \ + libplist++-2.0.pc diff --git a/src/Node.cpp b/src/Node.cpp index 3da401e..0bd428a 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -18,7 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> +#include <cstdlib> +#include "plist.h" #include <plist/Node.h> #include <plist/Structure.h> #include <plist/Dictionary.h> @@ -52,7 +53,7 @@ Node::Node(plist_type type, Node* parent) : _parent(parent) case PLIST_BOOLEAN: _node = plist_new_bool(0); break; - case PLIST_UINT: + case PLIST_INT: _node = plist_new_uint(0); break; case PLIST_REAL: @@ -134,7 +135,7 @@ Node* Node::FromPlist(plist_t node, Node* parent) case PLIST_BOOLEAN: ret = new Boolean(node, parent); break; - case PLIST_UINT: + case PLIST_INT: ret = new Integer(node, parent); break; case PLIST_REAL: @@ -163,4 +164,4 @@ Node* Node::FromPlist(plist_t node, Node* parent) return ret; } -}; +} // namespace PList diff --git a/src/Real.cpp b/src/Real.cpp index ec300c9..02d1d9b 100644 --- a/src/Real.cpp +++ b/src/Real.cpp @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> +#include <cstdlib> #include <plist/Real.h> namespace PList @@ -32,12 +32,12 @@ Real::Real(plist_t node, Node* parent) : Node(node, parent) { } -Real::Real(const PList::Real& d) : Node(PLIST_UINT) +Real::Real(const PList::Real& d) : Node(PLIST_INT) { plist_set_real_val(_node, d.GetValue()); } -Real& Real::operator=(PList::Real& d) +Real& Real::operator=(const PList::Real& d) { plist_free(_node); _node = plist_copy(d.GetPlist()); @@ -70,4 +70,4 @@ double Real::GetValue() const return d; } -}; +} // namespace PList diff --git a/src/String.cpp b/src/String.cpp index 0965349..2ddc28b 100644 --- a/src/String.cpp +++ b/src/String.cpp @@ -18,7 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> +#include <cstdlib> +#include "plist.h" #include <plist/String.h> namespace PList @@ -32,12 +33,12 @@ String::String(plist_t node, Node* parent) : Node(node, parent) { } -String::String(const PList::String& s) : Node(PLIST_UINT) +String::String(const PList::String& s) : Node(PLIST_INT) { plist_set_string_val(_node, s.GetValue().c_str()); } -String& String::operator=(PList::String& s) +String& String::operator=(const PList::String& s) { plist_free(_node); _node = plist_copy(s.GetPlist()); @@ -67,14 +68,9 @@ std::string String::GetValue() const { char* s = NULL; plist_get_string_val(_node, &s); - std::string ret; - if (s) { - ret = s; - free(s); - } else { - ret = ""; - } + std::string ret = s ? s : ""; + delete s; return ret; } -}; +} // namespace PList diff --git a/src/Structure.cpp b/src/Structure.cpp index 70150c2..670cce6 100644 --- a/src/Structure.cpp +++ b/src/Structure.cpp @@ -18,7 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> +#include <cstdlib> +#include "plist.h" #include <plist/Structure.h> namespace PList @@ -56,7 +57,7 @@ std::string Structure::ToXml() const uint32_t length = 0; plist_to_xml(_node, &xml, &length); std::string ret(xml, xml+length); - free(xml); + delete xml; return ret; } @@ -66,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); - free(bin); + delete bin; return ret; } @@ -119,5 +120,5 @@ Structure* Structure::FromBin(const std::vector<char>& bin) } -}; +} // namespace PList diff --git a/src/Uid.cpp b/src/Uid.cpp index 440dec4..8c73c80 100644 --- a/src/Uid.cpp +++ b/src/Uid.cpp @@ -18,7 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <stdlib.h> +#include <cstdlib> +#include "plist.h" #include <plist/Uid.h> namespace PList @@ -37,7 +38,7 @@ Uid::Uid(const PList::Uid& i) : Node(PLIST_UID) plist_set_uid_val(_node, i.GetValue()); } -Uid& Uid::operator=(PList::Uid& i) +Uid& Uid::operator=(const PList::Uid& i) { plist_free(_node); _node = plist_copy(i.GetPlist()); @@ -70,4 +71,4 @@ uint64_t Uid::GetValue() const return i; } -}; +} // namespace PList diff --git a/src/bplist.c b/src/bplist.c index 14db755..93f0bc6 100644 --- a/src/bplist.c +++ b/src/bplist.c @@ -32,11 +32,11 @@ #include <ctype.h> #include <inttypes.h> -#include <plist/plist.h> #include "plist.h" #include "hashtable.h" #include "bytearray.h" #include "ptrarray.h" +#include "plist/plist.h" #include <node.h> @@ -47,7 +47,8 @@ #define BPLIST_VERSION ((uint8_t*)"00") #define BPLIST_VERSION_SIZE 2 -typedef struct __attribute__((packed)) { +#pragma pack(push,1) +typedef struct { uint8_t unused[6]; uint8_t offset_size; uint8_t ref_size; @@ -55,6 +56,7 @@ typedef struct __attribute__((packed)) { uint64_t root_object_index; uint64_t offset_table_offset; } bplist_trailer_t; +#pragma pack(pop) enum { @@ -62,7 +64,7 @@ enum BPLIST_FALSE = 0x08, BPLIST_TRUE = 0x09, BPLIST_FILL = 0x0F, /* will be used for length grabbing */ - BPLIST_UINT = 0x10, + BPLIST_INT = 0x10, BPLIST_REAL = 0x20, BPLIST_DATE = 0x30, BPLIST_DATA = 0x40, @@ -143,28 +145,28 @@ union plist_uint_ptr #ifdef __BIG_ENDIAN__ #define beNtoh(x,n) (x >> ((8-n) << 3)) #else -#define beNtoh(x,n) be64toh(x << ((8-n) << 3)) +#define beNtoh(x,n) be64toh((x) << ((8-(n)) << 3)) #endif #define UINT_TO_HOST(x, n) \ ({ \ union plist_uint_ptr __up; \ - __up.src = (n > 8) ? x + (n - 8) : 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 : \ + __up.src = ((n) > 8) ? (x) + ((n) - 8) : (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 : \ beNtoh( get_unaligned(__up.u64ptr), n) \ )))); \ }) #define get_needed_bytes(x) \ - ( ((uint64_t)x) < (1ULL << 8) ? 1 : \ - ( ((uint64_t)x) < (1ULL << 16) ? 2 : \ - ( ((uint64_t)x) < (1ULL << 24) ? 3 : \ - ( ((uint64_t)x) < (1ULL << 32) ? 4 : 8)))) + ( ((uint64_t)(x)) < (1ULL << 8) ? 1 : \ + ( ((uint64_t)(x)) < (1ULL << 16) ? 2 : \ + ( ((uint64_t)(x)) < (1ULL << 24) ? 3 : \ + ( ((uint64_t)(x)) < (1ULL << 32) ? 4 : 8)))) -#define get_real_bytes(x) (x == (float) x ? sizeof(float) : sizeof(double)) +#define get_real_bytes(x) ((x) == (float) (x) ? sizeof(float) : sizeof(double)) #if (defined(__LITTLE_ENDIAN__) \ && !defined(__FLOAT_WORD_ORDER__)) \ @@ -182,7 +184,7 @@ union plist_uint_ptr #endif #if __has_builtin(__builtin_umulll_overflow) || __GNUC__ >= 5 -#define uint64_mul_overflow(a, b, r) __builtin_umulll_overflow(a, b, (unsigned long long*)r) +#define uint64_mul_overflow(a, b, r) __builtin_umulll_overflow(a, b, (unsigned long long*)(r)) #else static int uint64_mul_overflow(uint64_t a, uint64_t b, uint64_t *res) { @@ -191,7 +193,7 @@ static int uint64_mul_overflow(uint64_t a, uint64_t b, uint64_t *res) } #endif -#define NODE_IS_ROOT(x) (((node_t*)x)->isRoot) +#define NODE_IS_ROOT(x) (((node_t)(x))->isRoot) struct bplist_data { const char* data; @@ -227,9 +229,16 @@ void plist_bin_deinit(void) /* deinit binary plist stuff */ } +void plist_bin_set_debug(int debug) +{ +#if DEBUG + plist_bin_debug = debug; +#endif +} + static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node_index); -static plist_t parse_uint_node(const char **bnode, uint8_t size) +static plist_t parse_int_node(const char **bnode, uint8_t size) { plist_data_t data = plist_new_plist_data(); @@ -254,7 +263,7 @@ static plist_t parse_uint_node(const char **bnode, uint8_t size) data->intval = UINT_TO_HOST(*bnode, size); (*bnode) += size; - data->type = PLIST_UINT; + data->type = PLIST_INT; return node_create(NULL, data); } @@ -317,7 +326,8 @@ static plist_t parse_string_node(const char **bnode, uint64_t size) static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, long *items_written) { if (!unistr || (len <= 0)) return NULL; - char *outbuf; + char* outbuf; + char* outbuf_new; int p = 0; long i = 0; @@ -325,6 +335,7 @@ static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, uint32_t w; int read_lead_surrogate = 0; + /* allocate with enough space */ outbuf = (char*)malloc(4*(len+1)); if (!outbuf) { PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, (uint64_t)(4*(len+1))); @@ -339,7 +350,7 @@ static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, read_lead_surrogate = 1; w = 0x010000 + ((wc & 0x3FF) << 10); } else { - // This is invalid, the next 16 bit char should be a trail surrogate. + // This is invalid, the next 16 bit char should be a trail surrogate. // Handling error by skipping. read_lead_surrogate = 0; } @@ -374,30 +385,29 @@ static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, } outbuf[p] = 0; + /* reduce the size to the actual size */ + outbuf_new = (char*)realloc(outbuf, p+1); + if (outbuf_new) { + outbuf = outbuf_new; + } + return outbuf; } static plist_t parse_unicode_node(const char **bnode, uint64_t size) { plist_data_t data = plist_new_plist_data(); - char *tmpstr = NULL; long items_read = 0; long items_written = 0; data->type = PLIST_STRING; - - tmpstr = plist_utf16be_to_utf8((uint16_t*)(*bnode), size, &items_read, &items_written); - if (!tmpstr) { + data->strval = plist_utf16be_to_utf8((uint16_t*)(*bnode), size, &items_read, &items_written); + if (!data->strval) { plist_free_data(data); return NULL; } - tmpstr[items_written] = '\0'; - - data->type = PLIST_STRING; - data->strval = realloc(tmpstr, items_written+1); - if (!data->strval) - data->strval = tmpstr; data->length = items_written; + return node_create(NULL, data); } @@ -490,8 +500,8 @@ static plist_t parse_dict_node(struct bplist_data *bplist, const char** bnode, u return NULL; } - node_attach(node, key); - node_attach(node, val); + node_attach((node_t)node, (node_t)key); + node_attach((node_t)node, (node_t)val); } return node; @@ -535,7 +545,7 @@ static plist_t parse_array_node(struct bplist_data *bplist, const char** bnode, return NULL; } - node_attach(node, val); + node_attach((node_t)node, (node_t)val); } return node; @@ -583,8 +593,8 @@ static plist_t parse_bin_node(struct bplist_data *bplist, const char** object) case BPLIST_DICT: { uint16_t next_size = **object & BPLIST_FILL; - if ((**object & BPLIST_MASK) != BPLIST_UINT) { - PLIST_BIN_ERR("%s: invalid size node type for node type 0x%02x: found 0x%02x, expected 0x%02x\n", __func__, type, **object & BPLIST_MASK, BPLIST_UINT); + if ((**object & BPLIST_MASK) != BPLIST_INT) { + PLIST_BIN_ERR("%s: invalid size node type for node type 0x%02x: found 0x%02x, expected 0x%02x\n", __func__, type, **object & BPLIST_MASK, BPLIST_INT); return NULL; } (*object)++; @@ -630,16 +640,23 @@ static plist_t parse_bin_node(struct bplist_data *bplist, const char** object) } case BPLIST_NULL: + { + plist_data_t data = plist_new_plist_data(); + data->type = PLIST_NULL; + data->length = 0; + return node_create(NULL, data); + } + default: return NULL; } - case BPLIST_UINT: + case BPLIST_INT: if (pobject + (uint64_t)(1 << size) > poffset_table) { - PLIST_BIN_ERR("%s: BPLIST_UINT data bytes point outside of valid range\n", __func__); + PLIST_BIN_ERR("%s: BPLIST_INT data bytes point outside of valid range\n", __func__); return NULL; } - return parse_uint_node(object, size); + return parse_int_node(object, size); case BPLIST_REAL: if (pobject + (uint64_t)(1 << size) > poffset_table) { @@ -734,7 +751,7 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node ptr = bplist->data + UINT_TO_HOST(idx_ptr, bplist->offset_size); /* make sure the node offset is in a sane range */ - if ((ptr < bplist->data) || (ptr >= bplist->offset_table)) { + if ((ptr < bplist->data+BPLIST_MAGIC_SIZE+BPLIST_VERSION_SIZE) || (ptr >= bplist->offset_table)) { PLIST_BIN_ERR("offset for node index %u points outside of valid range\n", node_index); return NULL; } @@ -751,8 +768,8 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node /* recursion check */ if (bplist->level > 0) { for (i = bplist->level-1; i >= 0; i--) { - uint32_t node_i = (uint32_t)(uintptr_t)ptr_array_index(bplist->used_indexes, i); - uint32_t node_level = (uint32_t)(uintptr_t)ptr_array_index(bplist->used_indexes, bplist->level); + void *node_i = ptr_array_index(bplist->used_indexes, i); + void *node_level = ptr_array_index(bplist->used_indexes, bplist->level); if (node_i == node_level) { PLIST_BIN_ERR("recursion detected in binary plist\n"); return NULL; @@ -767,7 +784,7 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node return plist; } -PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist) +plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist) { bplist_trailer_t *trailer = NULL; uint8_t offset_size = 0; @@ -779,20 +796,28 @@ PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * const char *start_data = NULL; const char *end_data = NULL; + if (!plist) { + return PLIST_ERR_INVALID_ARG; + } + *plist = NULL; + if (!plist_bin || length == 0) { + return PLIST_ERR_INVALID_ARG; + } + //first check we have enough data if (!(length >= BPLIST_MAGIC_SIZE + BPLIST_VERSION_SIZE + sizeof(bplist_trailer_t))) { PLIST_BIN_ERR("plist data is to small to hold a binary plist\n"); - return; + return PLIST_ERR_PARSE; } //check that plist_bin in actually a plist if (memcmp(plist_bin, BPLIST_MAGIC, BPLIST_MAGIC_SIZE) != 0) { PLIST_BIN_ERR("bplist magic mismatch\n"); - return; + return PLIST_ERR_PARSE; } //check for known version if (memcmp(plist_bin + BPLIST_MAGIC_SIZE, BPLIST_VERSION, BPLIST_VERSION_SIZE) != 0) { PLIST_BIN_ERR("unsupported binary plist version '%.2s\n", plist_bin+BPLIST_MAGIC_SIZE); - return; + return PLIST_ERR_PARSE; } start_data = plist_bin + BPLIST_MAGIC_SIZE + BPLIST_VERSION_SIZE; @@ -809,37 +834,37 @@ PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * if (num_objects == 0) { PLIST_BIN_ERR("number of objects must be larger than 0\n"); - return; + return PLIST_ERR_PARSE; } if (offset_size == 0) { PLIST_BIN_ERR("offset size in trailer must be larger than 0\n"); - return; + return PLIST_ERR_PARSE; } if (ref_size == 0) { PLIST_BIN_ERR("object reference size in trailer must be larger than 0\n"); - return; + return PLIST_ERR_PARSE; } if (root_object >= num_objects) { PLIST_BIN_ERR("root object index (%" PRIu64 ") must be smaller than number of objects (%" PRIu64 ")\n", root_object, num_objects); - return; + return PLIST_ERR_PARSE; } if (offset_table < start_data || offset_table >= end_data) { PLIST_BIN_ERR("offset table offset points outside of valid range\n"); - return; + return PLIST_ERR_PARSE; } if (uint64_mul_overflow(num_objects, offset_size, &offset_table_size)) { PLIST_BIN_ERR("integer overflow when calculating offset table size\n"); - return; + return PLIST_ERR_PARSE; } - if ((offset_table + offset_table_size < offset_table) || (offset_table + offset_table_size > end_data)) { + if (offset_table_size > (uint64_t)(end_data - offset_table)) { PLIST_BIN_ERR("offset table points outside of valid range\n"); - return; + return PLIST_ERR_PARSE; } struct bplist_data bplist; @@ -854,12 +879,18 @@ PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * if (!bplist.used_indexes) { PLIST_BIN_ERR("failed to create array to hold used node indexes. Out of memory?\n"); - return; + return PLIST_ERR_NO_MEM; } *plist = parse_bin_node_at_index(&bplist, root_object); ptr_array_free(bplist.used_indexes); + + if (!*plist) { + return PLIST_ERR_PARSE; + } + + return PLIST_ERR_SUCCESS; } static unsigned int plist_data_hash(const void* key) @@ -875,7 +906,8 @@ static unsigned int plist_data_hash(const void* key) switch (data->type) { case PLIST_BOOLEAN: - case PLIST_UINT: + case PLIST_NULL: + case PLIST_INT: case PLIST_REAL: case PLIST_DATE: case PLIST_UID: @@ -914,7 +946,7 @@ struct serialize_s hashtable_t* ref_table; }; -static void serialize_plist(node_t* node, void* data) +static void serialize_plist(node_t node, void* data) { uint64_t *index_val = NULL; struct serialize_s *ser = (struct serialize_s *) data; @@ -937,15 +969,13 @@ static void serialize_plist(node_t* node, void* data) ptr_array_add(ser->objects, node); //now recurse on children - node_t *ch; + node_t ch; for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { serialize_plist(ch, data); } - - return; } -#define Log2(x) (x == 8 ? 3 : (x == 4 ? 2 : (x == 2 ? 1 : 0))) +#define Log2(x) ((x) == 8 ? 3 : ((x) == 4 ? 2 : ((x) == 2 ? 1 : 0))) static void write_int(bytearray_t * bplist, uint64_t val) { @@ -954,7 +984,7 @@ static void write_int(bytearray_t * bplist, uint64_t val) //do not write 3bytes int node if (size == 3) size++; - sz = BPLIST_UINT | Log2(size); + sz = BPLIST_INT | Log2(size); val = be64toh(val); byte_array_append(bplist, &sz, 1); @@ -963,7 +993,7 @@ static void write_int(bytearray_t * bplist, uint64_t val) static void write_uint(bytearray_t * bplist, uint64_t val) { - uint8_t sz = BPLIST_UINT | 4; + uint8_t sz = BPLIST_INT | 4; uint64_t zero = 0; val = be64toh(val); @@ -979,18 +1009,24 @@ static void write_real(bytearray_t * bplist, double val) buff[7] = BPLIST_REAL | Log2(size); if (size == sizeof(float)) { float floatval = (float)val; - *(uint32_t*)(buff+8) = float_bswap32(*(uint32_t*)&floatval); + uint32_t intval; + memcpy(&intval, &floatval, sizeof(float)); + *(uint32_t*)(buff+8) = float_bswap32(intval); } else { - *(uint64_t*)(buff+8) = float_bswap64(*(uint64_t*)&val); + uint64_t intval; + memcpy(&intval, &val, sizeof(double)); + *(uint64_t*)(buff+8) = float_bswap64(intval); } byte_array_append(bplist, buff+7, size+1); } static void write_date(bytearray_t * bplist, double val) { + uint64_t intval; + memcpy(&intval, &val, sizeof(double)); uint8_t buff[16]; buff[7] = BPLIST_DATE | 3; - *(uint64_t*)(buff+8) = float_bswap64(*(uint64_t*)&val); + *(uint64_t*)(buff+8) = float_bswap64(intval); byte_array_append(bplist, buff+7, 9); } @@ -1085,9 +1121,9 @@ static void write_unicode(bytearray_t * bplist, char *val, uint64_t size) free(unicodestr); } -static void write_array(bytearray_t * bplist, node_t* node, hashtable_t* ref_table, uint8_t ref_size) +static void write_array(bytearray_t * bplist, node_t node, hashtable_t* ref_table, uint8_t ref_size) { - node_t* cur = NULL; + node_t cur = NULL; uint64_t i = 0; uint64_t size = node_n_children(node); @@ -1104,9 +1140,9 @@ static void write_array(bytearray_t * bplist, node_t* node, hashtable_t* ref_tab } } -static void write_dict(bytearray_t * bplist, node_t* node, hashtable_t* ref_table, uint8_t ref_size) +static void write_dict(bytearray_t * bplist, node_t node, hashtable_t* ref_table, uint8_t ref_size) { - node_t* cur = NULL; + node_t cur = NULL; uint64_t i = 0; uint64_t size = node_n_children(node) / 2; @@ -1158,7 +1194,7 @@ static int is_ascii_string(char* s, int len) return ret; } -PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) +plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) { ptrarray_t* objects = NULL; hashtable_t* ref_table = NULL; @@ -1176,18 +1212,26 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) uint64_t objects_len = 0; //check for valid input - if (!plist || !plist_bin || *plist_bin || !length) - return; + if (!plist || !plist_bin || !length) { + return PLIST_ERR_INVALID_ARG; + } //list of objects objects = ptr_array_new(4096); + if (!objects) { + return PLIST_ERR_NO_MEM; + } //hashtable to write only once same nodes ref_table = hash_table_new(plist_data_hash, plist_data_compare, free); + if (!ref_table) { + ptr_array_free(objects); + return PLIST_ERR_NO_MEM; + } //serialize plist ser_s.objects = objects; ser_s.ref_table = ref_table; - serialize_plist(plist, &ser_s); + serialize_plist((node_t)plist, &ser_s); //now stream to output buffer offset_size = 0; //unknown yet @@ -1201,12 +1245,13 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) uint64_t req = 0; for (i = 0; i < num_objects; i++) { - node_t* node = ptr_array_index(objects, i); + node_t node = (node_t)ptr_array_index(objects, i); plist_data_t data = plist_get_data(node); uint64_t size; uint8_t bsize; switch (data->type) { + case PLIST_NULL: case PLIST_BOOLEAN: req += 1; break; @@ -1281,6 +1326,11 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) //setup a dynamic bytes array to store bplist in bplist_buff = byte_array_new(req); + if (!bplist_buff) { + ptr_array_free(objects); + hash_table_destroy(ref_table); + return PLIST_ERR_NO_MEM; + } //set magic number and version byte_array_append(bplist_buff, BPLIST_MAGIC, BPLIST_MAGIC_SIZE); @@ -1297,12 +1347,17 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) switch (data->type) { + case PLIST_NULL: { + uint8_t b = 0; + byte_array_append(bplist_buff, &b, 1); + break; + } case PLIST_BOOLEAN: { uint8_t b = data->boolval ? BPLIST_TRUE : BPLIST_FALSE; byte_array_append(bplist_buff, &b, 1); break; } - case PLIST_UINT: + case PLIST_INT: if (data->length == 16) { write_uint(bplist_buff, data->intval); } else { @@ -1329,10 +1384,10 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) write_data(bplist_buff, data->buff, data->length); break; case PLIST_ARRAY: - write_array(bplist_buff, ptr_array_index(objects, i), ref_table, ref_size); + write_array(bplist_buff, (node_t)ptr_array_index(objects, i), ref_table, ref_size); break; case PLIST_DICT: - write_dict(bplist_buff, ptr_array_index(objects, i), ref_table, ref_size); + write_dict(bplist_buff, (node_t)ptr_array_index(objects, i), ref_table, ref_size); break; case PLIST_DATE: write_date(bplist_buff, data->realval); @@ -1370,14 +1425,11 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) byte_array_append(bplist_buff, &trailer, sizeof(bplist_trailer_t)); //set output buffer and size - *plist_bin = bplist_buff->data; + *plist_bin = (char*)bplist_buff->data; *length = bplist_buff->len; bplist_buff->data = NULL; // make sure we don't free the output buffer byte_array_free(bplist_buff); -} -PLIST_API void plist_to_bin_free(char *plist_bin) -{ - free(plist_bin); + return PLIST_ERR_SUCCESS; } diff --git a/src/bytearray.c b/src/bytearray.c index 7d0549b..39fad5f 100644 --- a/src/bytearray.c +++ b/src/bytearray.c @@ -29,6 +29,17 @@ bytearray_t *byte_array_new(size_t initial) a->capacity = (initial > PAGE_SIZE) ? (initial+(PAGE_SIZE-1)) & (~(PAGE_SIZE-1)) : PAGE_SIZE; a->data = malloc(a->capacity); a->len = 0; + a->stream = NULL; + return a; +} + +bytearray_t *byte_array_new_for_stream(FILE *stream) +{ + bytearray_t *a = (bytearray_t*)malloc(sizeof(bytearray_t)); + a->capacity = (size_t)-1; + a->data = NULL; + a->len = 0; + a->stream = stream; return a; } @@ -43,6 +54,9 @@ void byte_array_free(bytearray_t *ba) void byte_array_grow(bytearray_t *ba, size_t amount) { + if (ba->stream) { + return; + } size_t increase = (amount > PAGE_SIZE) ? (amount+(PAGE_SIZE-1)) & (~(PAGE_SIZE-1)) : PAGE_SIZE; ba->data = realloc(ba->data, ba->capacity + increase); ba->capacity += increase; @@ -50,12 +64,20 @@ void byte_array_grow(bytearray_t *ba, size_t amount) void byte_array_append(bytearray_t *ba, void *buf, size_t len) { - if (!ba || !ba->data || (len <= 0)) return; - size_t remaining = ba->capacity-ba->len; - if (len > remaining) { - size_t needed = len - remaining; - byte_array_grow(ba, needed); + if (!ba || (!ba->stream && !ba->data) || (len <= 0)) return; + if (ba->stream) { + if (fwrite(buf, 1, len, ba->stream) < len) { +#if DEBUG + fprintf(stderr, "ERROR: Failed to write to stream.\n"); +#endif + } + } else { + size_t remaining = ba->capacity-ba->len; + if (len > remaining) { + size_t needed = len - remaining; + byte_array_grow(ba, needed); + } + memcpy(((char*)ba->data) + ba->len, buf, len); } - memcpy(((char*)ba->data) + ba->len, buf, len); ba->len += len; } diff --git a/src/bytearray.h b/src/bytearray.h index 312e2aa..b53e006 100644 --- a/src/bytearray.h +++ b/src/bytearray.h @@ -21,14 +21,17 @@ #ifndef BYTEARRAY_H #define BYTEARRAY_H #include <stdlib.h> +#include <stdio.h> typedef struct bytearray_t { void *data; size_t len; size_t capacity; + FILE *stream; } bytearray_t; bytearray_t *byte_array_new(size_t initial); +bytearray_t *byte_array_new_for_stream(FILE *stream); void byte_array_free(bytearray_t *ba); void byte_array_grow(bytearray_t *ba, size_t amount); void byte_array_append(bytearray_t *ba, void *buf, size_t len); diff --git a/src/hashtable.c b/src/hashtable.c index dd6dbfc..86dae82 100644 --- a/src/hashtable.c +++ b/src/hashtable.c @@ -47,7 +47,7 @@ void hash_table_destroy(hashtable_t *ht) ht->free_func(e->value); } hashentry_t* old = e; - e = e->next; + e = (hashentry_t*)e->next; free(old); } } @@ -71,7 +71,7 @@ void hash_table_insert(hashtable_t* ht, void *key, void *value) e->value = value; return; } - e = e->next; + e = (hashentry_t*)e->next; } // if we get here, the element is not yet in the list. @@ -103,7 +103,7 @@ void* hash_table_lookup(hashtable_t* ht, void *key) if (ht->compare_func(e->key, key)) { return e->value; } - e = e->next; + e = (hashentry_t*)e->next; } return NULL; } @@ -124,7 +124,7 @@ void hash_table_remove(hashtable_t* ht, void *key) // found element, remove it from the list hashentry_t* old = e; if (e == ht->entries[idx0]) { - ht->entries[idx0] = e->next; + ht->entries[idx0] = (hashentry_t*)e->next; } else { last->next = e->next; } @@ -135,6 +135,6 @@ void hash_table_remove(hashtable_t* ht, void *key) return; } last = e; - e = e->next; + e = (hashentry_t*)e->next; } } diff --git a/src/jplist.c b/src/jplist.c new file mode 100644 index 0000000..782d2b3 --- /dev/null +++ b/src/jplist.c @@ -0,0 +1,856 @@ +/* + * jplist.c + * JSON plist implementation + * + * Copyright (c) 2019-2021 Nikias Bassen All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +#include <inttypes.h> +#include <ctype.h> +#include <math.h> +#include <limits.h> + +#include <node.h> + +#include "plist.h" +#include "strbuf.h" +#include "jsmn.h" + +#ifdef DEBUG +static int plist_json_debug = 0; +#define PLIST_JSON_ERR(...) if (plist_json_debug) { fprintf(stderr, "libplist[jsonparser] ERROR: " __VA_ARGS__); } +#define PLIST_JSON_WRITE_ERR(...) if (plist_json_debug) { fprintf(stderr, "libplist[jsonwriter] ERROR: " __VA_ARGS__); } +#else +#define PLIST_JSON_ERR(...) +#define PLIST_JSON_WRITE_ERR(...) +#endif + +void plist_json_init(void) +{ + /* init JSON stuff */ +#ifdef DEBUG + char *env_debug = getenv("PLIST_JSON_DEBUG"); + if (env_debug && !strcmp(env_debug, "1")) { + plist_json_debug = 1; + } +#endif +} + +void plist_json_deinit(void) +{ + /* deinit JSON stuff */ +} + +void plist_json_set_debug(int debug) +{ +#ifdef DEBUG + plist_json_debug = debug; +#endif +} + +#ifndef HAVE_STRNDUP +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +static char* strndup(const char* str, size_t len) +{ + char *newstr = (char *)malloc(len+1); + if (newstr) { + strncpy(newstr, str, len); + newstr[len]= '\0'; + } + return newstr; +} +#pragma GCC diagnostic pop +#endif + +static size_t dtostr(char *buf, size_t bufsize, double realval) +{ + size_t len = 0; + if (isnan(realval)) { + len = snprintf(buf, bufsize, "nan"); + } else if (isinf(realval)) { + len = snprintf(buf, bufsize, "%cinfinity", (realval > 0.0) ? '+' : '-'); + } else if (realval == 0.0f) { + len = snprintf(buf, bufsize, "0.0"); + } else { + size_t i = 0; + len = snprintf(buf, bufsize, "%.*g", 17, realval); + for (i = 0; buf && i < len; i++) { + if (buf[i] == ',') { + buf[i] = '.'; + break; + } else if (buf[i] == '.') { + break; + } + } + } + return len; +} + +static plist_err_t node_to_json(node_t node, bytearray_t **outbuf, uint32_t depth, int prettify) +{ + plist_data_t node_data = NULL; + + char *val = NULL; + size_t val_len = 0; + + uint32_t i = 0; + + if (!node) + return PLIST_ERR_INVALID_ARG; + + node_data = plist_get_data(node); + + switch (node_data->type) + { + case PLIST_BOOLEAN: + { + if (node_data->boolval) { + str_buf_append(*outbuf, "true", 4); + } else { + str_buf_append(*outbuf, "false", 5); + } + } + break; + + case PLIST_NULL: + str_buf_append(*outbuf, "null", 4); + break; + + case PLIST_INT: + val = (char*)malloc(64); + if (node_data->length == 16) { + val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); + } else { + val_len = snprintf(val, 64, "%" PRIi64, node_data->intval); + } + str_buf_append(*outbuf, val, val_len); + free(val); + break; + + case PLIST_REAL: + val = (char*)malloc(64); + val_len = dtostr(val, 64, node_data->realval); + str_buf_append(*outbuf, val, val_len); + free(val); + break; + + case PLIST_STRING: + case PLIST_KEY: { + const char *charmap[32] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", + "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", "\\u000e", "\\u000f", + "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017", + "\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f", + }; + size_t j = 0; + size_t len = 0; + off_t start = 0; + off_t cur = 0; + + str_buf_append(*outbuf, "\"", 1); + + len = node_data->length; + for (j = 0; j < len; j++) { + unsigned char ch = (unsigned char)node_data->strval[j]; + if (ch < 0x20) { + str_buf_append(*outbuf, node_data->strval + start, cur - start); + str_buf_append(*outbuf, charmap[ch], (charmap[ch][1] == 'u') ? 6 : 2); + start = cur+1; + } else if (ch == '"') { + str_buf_append(*outbuf, node_data->strval + start, cur - start); + str_buf_append(*outbuf, "\\\"", 2); + start = cur+1; + } + cur++; + } + str_buf_append(*outbuf, node_data->strval + start, cur - start); + + str_buf_append(*outbuf, "\"", 1); + } break; + + case PLIST_ARRAY: { + str_buf_append(*outbuf, "[", 1); + node_t ch; + uint32_t cnt = 0; + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + if (cnt > 0) { + str_buf_append(*outbuf, ",", 1); + } + if (prettify) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i <= depth; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + plist_err_t res = node_to_json(ch, outbuf, depth+1, prettify); + if (res < 0) { + return res; + } + cnt++; + } + if (cnt > 0 && prettify) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i < depth; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + str_buf_append(*outbuf, "]", 1); + } break; + case PLIST_DICT: { + str_buf_append(*outbuf, "{", 1); + node_t ch; + uint32_t cnt = 0; + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + if (cnt > 0 && cnt % 2 == 0) { + str_buf_append(*outbuf, ",", 1); + } + if (cnt % 2 == 0 && prettify) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i <= depth; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + plist_err_t res = node_to_json(ch, outbuf, depth+1, prettify); + if (res < 0) { + return res; + } + if (cnt % 2 == 0) { + str_buf_append(*outbuf, ":", 1); + if (prettify) { + str_buf_append(*outbuf, " ", 1); + } + } + cnt++; + } + if (cnt > 0 && prettify) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i < depth; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + str_buf_append(*outbuf, "}", 1); + } break; + case PLIST_DATA: + // NOT VALID FOR JSON + PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n"); + return PLIST_ERR_FORMAT; + case PLIST_DATE: + // NOT VALID FOR JSON + PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n"); + return PLIST_ERR_FORMAT; + case PLIST_UID: + // NOT VALID FOR JSON + PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n"); + return PLIST_ERR_FORMAT; + default: + return PLIST_ERR_UNKNOWN; + } + + return PLIST_ERR_SUCCESS; +} + +#define PO10i_LIMIT (INT64_MAX/10) + +/* based on https://stackoverflow.com/a/4143288 */ +static int num_digits_i(int64_t i) +{ + int n; + int64_t po10; + n=1; + if (i < 0) { + i = (i == INT64_MIN) ? INT64_MAX : -i; + n++; + } + po10=10; + while (i>=po10) { + n++; + if (po10 > PO10i_LIMIT) break; + po10*=10; + } + return n; +} + +#define PO10u_LIMIT (UINT64_MAX/10) + +/* based on https://stackoverflow.com/a/4143288 */ +static int num_digits_u(uint64_t i) +{ + int n; + uint64_t po10; + n=1; + po10=10; + while (i>=po10) { + n++; + if (po10 > PO10u_LIMIT) break; + po10*=10; + } + return n; +} + +static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify) +{ + plist_data_t data; + if (!node) { + return PLIST_ERR_INVALID_ARG; + } + data = plist_get_data(node); + if (node->children) { + node_t ch; + unsigned int n_children = node_n_children(node); + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + plist_err_t res = node_estimate_size(ch, size, depth + 1, prettify); + if (res < 0) { + return res; + } + } + switch (data->type) { + case PLIST_DICT: + *size += 2; // '{' and '}' + *size += n_children-1; // number of ':' and ',' + if (prettify) { + *size += n_children; // number of '\n' and extra space + *size += (uint64_t)n_children * (depth+1); // indent for every 2nd child + *size += 1; // additional '\n' + } + break; + case PLIST_ARRAY: + *size += 2; // '[' and ']' + *size += n_children-1; // number of ',' + if (prettify) { + *size += n_children; // number of '\n' + *size += (uint64_t)n_children * ((depth+1)<<1); // indent for every child + *size += 1; // additional '\n' + } + break; + default: + break; + } + if (prettify) + *size += (depth << 1); // indent for {} and [] + } else { + switch (data->type) { + case PLIST_STRING: + case PLIST_KEY: + *size += data->length; + *size += 2; + break; + case PLIST_INT: + if (data->length == 16) { + *size += num_digits_u(data->intval); + } else { + *size += num_digits_i((int64_t)data->intval); + } + break; + case PLIST_REAL: + *size += dtostr(NULL, 0, data->realval); + break; + case PLIST_BOOLEAN: + *size += ((data->boolval) ? 4 : 5); + break; + case PLIST_NULL: + *size += 4; + break; + case PLIST_DICT: + case PLIST_ARRAY: + *size += 2; + break; + case PLIST_DATA: + // NOT VALID FOR JSON + PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n"); + return PLIST_ERR_FORMAT; + case PLIST_DATE: + // NOT VALID FOR JSON + PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n"); + return PLIST_ERR_FORMAT; + case PLIST_UID: + // NOT VALID FOR JSON + PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n"); + return PLIST_ERR_FORMAT; + default: + PLIST_JSON_WRITE_ERR("invalid node type encountered\n"); + return PLIST_ERR_UNKNOWN; + } + } + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, int prettify) +{ + uint64_t size = 0; + plist_err_t res; + + if (!plist || !plist_json || !length) { + return PLIST_ERR_INVALID_ARG; + } + + if (!PLIST_IS_DICT(plist) && !PLIST_IS_ARRAY(plist)) { + PLIST_JSON_WRITE_ERR("plist data is not valid for JSON format\n"); + return PLIST_ERR_FORMAT; + } + + res = node_estimate_size((node_t)plist, &size, 0, prettify); + if (res < 0) { + return res; + } + + strbuf_t *outbuf = str_buf_new(size); + if (!outbuf) { + PLIST_JSON_WRITE_ERR("Could not allocate output buffer\n"); + return PLIST_ERR_NO_MEM; + } + + res = node_to_json((node_t)plist, &outbuf, 0, prettify); + if (res < 0) { + str_buf_free(outbuf); + *plist_json = NULL; + *length = 0; + return res; + } + if (prettify) { + str_buf_append(outbuf, "\n", 1); + } + + str_buf_append(outbuf, "\0", 1); + + *plist_json = (char*)outbuf->data; + *length = outbuf->len - 1; + + outbuf->data = NULL; + str_buf_free(outbuf); + + return PLIST_ERR_SUCCESS; +} + +typedef struct { + jsmntok_t* tokens; + int count; +} jsmntok_info_t; + +static int64_t parse_decimal(const char* str, const char* str_end, char** endp) +{ + uint64_t MAX = INT64_MAX; + uint64_t x = 0; + int is_neg = 0; + *endp = (char*)str; + + if (str[0] == '-') { + is_neg = 1; + (*endp)++; + } + if (is_neg) { + MAX++; + } + while (*endp < str_end && isdigit(**endp)) { + if (x > PO10i_LIMIT) { + x = MAX; + break; + } + x = x * 10; + unsigned int add = (**endp - '0'); + if (x + add > MAX) { + x = MAX; + break; + } + x += add; + (*endp)++; + } + + // swallow the rest of the digits in case we dropped out early + while (*endp < str_end && isdigit(**endp)) (*endp)++; + + int64_t result = x; + if (is_neg) { + if (x == MAX) { + result = INT64_MIN; + } else { + result = -(int64_t)x; + } + } + return result; +} + +static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index) +{ + if (ti->tokens[*index].type != JSMN_PRIMITIVE) { + PLIST_JSON_ERR("%s: token type != JSMN_PRIMITIVE\n", __func__); + return NULL; + } + plist_t val = NULL; + const char* str_val = js + ti->tokens[*index].start; + const char* str_end = js + ti->tokens[*index].end; + size_t str_len = ti->tokens[*index].end - ti->tokens[*index].start; + if (!strncmp("false", str_val, str_len)) { + val = plist_new_bool(0); + } else if (!strncmp("true", str_val, str_len)) { + val = plist_new_bool(1); + } else if (!strncmp("null", str_val, str_len)) { + plist_data_t data = plist_new_plist_data(); + data->type = PLIST_NULL; + val = plist_new_node(data); + } else if (isdigit(str_val[0]) || (str_val[0] == '-' && str_val+1 < str_end && isdigit(str_val[1]))) { + char* endp = (char*)str_val; + int is_neg = (str_val[0] == '-'); + int64_t intpart = parse_decimal(str_val, str_end, &endp); + if (endp >= str_end) { + /* integer */ + if (is_neg || intpart <= INT64_MAX) { + val = plist_new_int(intpart); + } 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)))))) { + /* floating point */ + double dval = (double)intpart; + char* fendp = endp; + int err = 0; + do { + if (*endp == '.') { + fendp++; + double frac = 0; + double p = 0.1; + while (fendp < str_end && isdigit(*fendp)) { + frac = frac + (*fendp - '0') * p; + p *= 0.1; + fendp++; + } + if (is_neg) { + dval -= frac; + } else { + dval += frac; + } + } + 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))))) { + int64_t exp = parse_decimal(fendp+1, str_end, &fendp); + dval = dval * pow(10, (double)exp); + } else { + PLIST_JSON_ERR("%s: invalid character at offset %d when parsing floating point value\n", __func__, (int)(fendp - js)); + err++; + } + } while (0); + if (!err) { + if (isinf(dval) || isnan(dval)) { + PLIST_JSON_ERR("%s: unrepresentable floating point value at offset %d when parsing numerical value\n", __func__, (int)(str_val - js)); + } else { + val = plist_new_real(dval); + } + } + } else { + PLIST_JSON_ERR("%s: invalid character at offset %d when parsing numerical value\n", __func__, (int)(endp - js)); + } + } else { + PLIST_JSON_ERR("%s: invalid primitive value '%.*s' encountered\n", __func__, (int)str_len, str_val); + } + (*index)++; + return val; +} + +static char* unescape_string(const char* str_val, size_t str_len, size_t *new_len) +{ + char* strval = strndup(str_val, str_len); + size_t i = 0; + while (i < str_len) { + if (strval[i] == '\\' && i < str_len-1) { + switch (strval[i+1]) { + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + memmove(strval+i, strval+i+1, str_len - (i+1)); + str_len--; + switch (strval[i]) { + case 'b': + strval[i] = '\b'; + break; + case 'f': + strval[i] = '\f'; + break; + case 'r': + strval[i] = '\r'; + break; + case 'n': + strval[i] = '\n'; + break; + case 't': + strval[i] = '\t'; + break; + default: + break; + } + break; + case 'u': { + unsigned int val = 0; + if (str_len-(i+2) < 4) { + PLIST_JSON_ERR("%s: invalid escape sequence '%s' (too short)\n", __func__, strval+i); + free(strval); + return NULL; + } + if (!(isxdigit(strval[i+2]) && isxdigit(strval[i+3]) && isxdigit(strval[i+4]) && isxdigit(strval[i+5])) || sscanf(strval+i+2, "%04x", &val) != 1) { + PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 6, strval+i); + free(strval); + return NULL; + } + int bytelen = 0; + if (val >= 0x800) { + /* three bytes */ + strval[i] = (char)(0xE0 + ((val >> 12) & 0xF)); + strval[i+1] = (char)(0x80 + ((val >> 6) & 0x3F)); + strval[i+2] = (char)(0x80 + (val & 0x3F)); + bytelen = 3; + } else if (val >= 0x80) { + /* two bytes */ + strval[i] = (char)(0xC0 + ((val >> 6) & 0x1F)); + strval[i+1] = (char)(0x80 + (val & 0x3F)); + bytelen = 2; + } else { + /* one byte */ + strval[i] = (char)(val & 0x7F); + bytelen = 1; + } + memmove(strval+i+bytelen, strval+i+6, str_len - (i+5)); + str_len -= (6-bytelen); + } break; + default: + PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 2, strval+i); + free(strval); + return NULL; + } + } + i++; + } + strval[str_len] = '\0'; + if (new_len) { + *new_len = str_len; + } + return strval; +} + +static plist_t parse_string(const char* js, jsmntok_info_t* ti, int* index) +{ + if (ti->tokens[*index].type != JSMN_STRING) { + PLIST_JSON_ERR("%s: token type != JSMN_STRING\n", __func__); + return NULL; + } + + size_t str_len = 0; ; + char* strval = unescape_string(js + ti->tokens[*index].start, ti->tokens[*index].end - ti->tokens[*index].start, &str_len); + if (!strval) { + return NULL; + } + plist_t node; + + plist_data_t data = plist_new_plist_data(); + data->type = PLIST_STRING; + data->strval = strval; + data->length = str_len; + node = plist_new_node(data); + + (*index)++; + return node; +} + +static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index); + +static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index) +{ + if (ti->tokens[*index].type != JSMN_ARRAY) { + PLIST_JSON_ERR("%s: token type != JSMN_ARRAY\n", __func__); + return NULL; + } + plist_t arr = plist_new_array(); + int num_tokens = ti->tokens[*index].size; + int num; + int j = (*index)+1; + for (num = 0; num < num_tokens; num++) { + if (j >= ti->count) { + PLIST_JSON_ERR("%s: token index out of valid range\n", __func__); + plist_free(arr); + return NULL; + } + plist_t val = NULL; + switch (ti->tokens[j].type) { + case JSMN_OBJECT: + val = parse_object(js, ti, &j); + break; + case JSMN_ARRAY: + val = parse_array(js, ti, &j); + break; + case JSMN_STRING: + val = parse_string(js, ti, &j); + break; + case JSMN_PRIMITIVE: + val = parse_primitive(js, ti, &j); + break; + default: + break; + } + if (val) { + plist_array_append_item(arr, val); + } else { + plist_free(arr); + return NULL; + } + } + *(index) = j; + return arr; +} + +static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index) +{ + if (ti->tokens[*index].type != JSMN_OBJECT) { + PLIST_JSON_ERR("%s: token type != JSMN_OBJECT\n", __func__); + return NULL; + } + int num_tokens = ti->tokens[*index].size; + int num; + int j = (*index)+1; + if (num_tokens % 2 != 0) { + PLIST_JSON_ERR("%s: number of children must be even\n", __func__); + return NULL; + } + plist_t obj = plist_new_dict(); + for (num = 0; num < num_tokens; num++) { + if (j+1 >= ti->count) { + PLIST_JSON_ERR("%s: token index out of valid range\n", __func__); + plist_free(obj); + return NULL; + } + if (ti->tokens[j].type == JSMN_STRING) { + char* key = unescape_string(js + ti->tokens[j].start, ti->tokens[j].end - ti->tokens[j].start, NULL); + if (!key) { + plist_free(obj); + return NULL; + } + plist_t val = NULL; + j++; + num++; + switch (ti->tokens[j].type) { + case JSMN_OBJECT: + val = parse_object(js, ti, &j); + break; + case JSMN_ARRAY: + val = parse_array(js, ti, &j); + break; + case JSMN_STRING: + val = parse_string(js, ti, &j); + break; + case JSMN_PRIMITIVE: + val = parse_primitive(js, ti, &j); + break; + default: + break; + } + if (val) { + plist_dict_set_item(obj, key, val); + } else { + free(key); + plist_free(obj); + return NULL; + } + free(key); + } else { + PLIST_JSON_ERR("%s: keys must be of type STRING\n", __func__); + plist_free(obj); + return NULL; + } + } + (*index) = j; + return obj; +} + +plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist) +{ + if (!plist) { + return PLIST_ERR_INVALID_ARG; + } + *plist = NULL; + if (!json || (length == 0)) { + return PLIST_ERR_INVALID_ARG; + } + + jsmn_parser parser; + jsmn_init(&parser); + int maxtoks = 256; + int curtoks = 0; + int r = 0; + jsmntok_t *tokens = NULL; + + do { + jsmntok_t* newtokens = (jsmntok_t*)realloc(tokens, sizeof(jsmntok_t)*maxtoks); + if (!newtokens) { + PLIST_JSON_ERR("%s: Out of memory\n", __func__); + return PLIST_ERR_NO_MEM; + } + memset((unsigned char*)newtokens + sizeof(jsmntok_t)*curtoks, '\0', sizeof(jsmntok_t)*(maxtoks-curtoks)); + tokens = newtokens; + curtoks = maxtoks; + + r = jsmn_parse(&parser, json, length, tokens, maxtoks); + if (r == JSMN_ERROR_NOMEM) { + maxtoks+=16; + continue; + } + } while (r == JSMN_ERROR_NOMEM); + + switch(r) { + case JSMN_ERROR_NOMEM: + PLIST_JSON_ERR("%s: Out of memory...\n", __func__); + free(tokens); + return PLIST_ERR_NO_MEM; + case JSMN_ERROR_INVAL: + PLIST_JSON_ERR("%s: Invalid character inside JSON string\n", __func__); + free(tokens); + return PLIST_ERR_PARSE; + case JSMN_ERROR_PART: + PLIST_JSON_ERR("%s: Incomplete JSON, more bytes expected\n", __func__); + free(tokens); + return PLIST_ERR_PARSE; + default: + break; + } + + int startindex = 0; + jsmntok_info_t ti = { tokens, parser.toknext }; + switch (tokens[startindex].type) { + case JSMN_PRIMITIVE: + *plist = parse_primitive(json, &ti, &startindex); + break; + case JSMN_STRING: + *plist = parse_string(json, &ti, &startindex); + break; + case JSMN_ARRAY: + *plist = parse_array(json, &ti, &startindex); + break; + case JSMN_OBJECT: + *plist = parse_object(json, &ti, &startindex); + break; + default: + break; + } + free(tokens); + return PLIST_ERR_SUCCESS; +} diff --git a/src/jsmn.c b/src/jsmn.c new file mode 100644 index 0000000..f190312 --- /dev/null +++ b/src/jsmn.c @@ -0,0 +1,289 @@ +/* + * jsmn.c + * Simple JSON parser + * + * Copyright (c) 2010 Serge A. Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdlib.h> + +#include "jsmn.h" + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, int num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, + jsmntok_t *tokens, int num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; (parser->end > 0 && parser->pos < parser->end) && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + default: + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return JSMN_SUCCESS; +} + +/** + * Fills next token with JSON string. + */ +static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, + jsmntok_t *tokens, int num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; (parser->end > 0 && parser->pos < parser->end) && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return JSMN_SUCCESS; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\') { + parser->pos++; + if (parser->end > 0 && parser->pos >= parser->end) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + /* TODO */ + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, jsmntok_t *tokens, + unsigned int num_tokens) { + jsmnerr_t r; + int i; + jsmntok_t *token; + + parser->end = length; + + for (; (parser->end > 0 && parser->pos < parser->end) && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, tokens, num_tokens); + if (r < 0) return r; + if (parser->toksuper != -1) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, tokens, num_tokens); + if (r < 0) return r; + if (parser->toksuper != -1) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + + } + } + + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + + return JSMN_SUCCESS; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->end = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + diff --git a/src/jsmn.h b/src/jsmn.h new file mode 100644 index 0000000..380744d --- /dev/null +++ b/src/jsmn.h @@ -0,0 +1,92 @@ +/* + * jsmn.h + * Simple JSON parser (header file) + * + * Copyright (c) 2010 Serge A. Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef __JSMN_H_ +#define __JSMN_H_ + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_PRIMITIVE = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3 +} jsmntype_t; + +typedef enum { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3, + /* Everything was fine */ + JSMN_SUCCESS = 0 +} jsmnerr_t; + +/** + * JSON token description. + * @param type type (object, array, string etc.) + * @param start start position in JSON data string + * @param end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int end; /* offset after last character of JSON string */ + int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each describing + * a single JSON object. + */ +jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, + jsmntok_t *tokens, unsigned int num_tokens); + +#endif /* __JSMN_H_ */ diff --git a/src/libplist++.pc.in b/src/libplist++-2.0.pc.in index 0fce9d1..79dc315 100644 --- a/src/libplist++.pc.in +++ b/src/libplist++-2.0.pc.in @@ -6,6 +6,6 @@ includedir=@includedir@ Name: @PACKAGE_NAME@++ Description: C++ binding for @PACKAGE_NAME@ Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lplist++ +Libs: -L${libdir} -lplist++-2.0 Cflags: -I${includedir} -Requires.private: libplist >= @PACKAGE_VERSION@ +Requires.private: libplist-2.0 >= @PACKAGE_VERSION@ diff --git a/src/libplist.pc.in b/src/libplist-2.0.pc.in index 0bd6932..43d9f57 100644 --- a/src/libplist.pc.in +++ b/src/libplist-2.0.pc.in @@ -6,5 +6,5 @@ includedir=@includedir@ Name: @PACKAGE_NAME@ Description: A library to handle Apple Property Lists whereas they are binary or XML Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lplist +Libs: -L${libdir} -lplist-2.0 Cflags: -I${includedir} diff --git a/src/oplist.c b/src/oplist.c new file mode 100644 index 0000000..6ab6603 --- /dev/null +++ b/src/oplist.c @@ -0,0 +1,933 @@ +/* + * oplist.c + * OpenStep plist implementation + * + * Copyright (c) 2021-2022 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +#include <inttypes.h> +#include <ctype.h> +#include <math.h> +#include <limits.h> + +#include <node.h> + +#include "plist.h" +#include "strbuf.h" + +#ifdef DEBUG +static int plist_ostep_debug = 0; +#define PLIST_OSTEP_ERR(...) if (plist_ostep_debug) { fprintf(stderr, "libplist[ostepparser] ERROR: " __VA_ARGS__); } +#define PLIST_OSTEP_WRITE_ERR(...) if (plist_ostep_debug) { fprintf(stderr, "libplist[ostepwriter] ERROR: " __VA_ARGS__); } +#else +#define PLIST_OSTEP_ERR(...) +#define PLIST_OSTEP_WRITE_ERR(...) +#endif + +void plist_ostep_init(void) +{ + /* init OpenStep stuff */ +#ifdef DEBUG + char *env_debug = getenv("PLIST_OSTEP_DEBUG"); + if (env_debug && !strcmp(env_debug, "1")) { + plist_ostep_debug = 1; + } +#endif +} + +void plist_ostep_deinit(void) +{ + /* deinit OpenStep plist stuff */ +} + +void plist_ostep_set_debug(int debug) +{ +#if DEBUG + plist_ostep_debug = debug; +#endif +} + +#ifndef HAVE_STRNDUP +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +static char* strndup(const char* str, size_t len) +{ + char *newstr = (char *)malloc(len+1); + if (newstr) { + strncpy(newstr, str, len); + newstr[len]= '\0'; + } + return newstr; +} +#pragma GCC diagnostic pop +#endif + +static size_t dtostr(char *buf, size_t bufsize, double realval) +{ + size_t len = 0; + if (isnan(realval)) { + len = snprintf(buf, bufsize, "nan"); + } else if (isinf(realval)) { + len = snprintf(buf, bufsize, "%cinfinity", (realval > 0.0) ? '+' : '-'); + } else if (realval == 0.0f) { + len = snprintf(buf, bufsize, "0.0"); + } else { + size_t i = 0; + len = snprintf(buf, bufsize, "%.*g", 17, realval); + for (i = 0; buf && i < len; i++) { + if (buf[i] == ',') { + buf[i] = '.'; + break; + } else if (buf[i] == '.') { + break; + } + } + } + return len; +} + +static const char allowed_unquoted_chars[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static int str_needs_quotes(const char* str, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) { + if (!allowed_unquoted_chars[(unsigned char)str[i]]) { + return 1; + } + } + return 0; +} + +static plist_err_t node_to_openstep(node_t node, bytearray_t **outbuf, uint32_t depth, int prettify) +{ + plist_data_t node_data = NULL; + + char *val = NULL; + size_t val_len = 0; + + uint32_t i = 0; + + if (!node) + return PLIST_ERR_INVALID_ARG; + + node_data = plist_get_data(node); + + switch (node_data->type) + { + case PLIST_INT: + val = (char*)malloc(64); + if (node_data->length == 16) { + val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); + } else { + val_len = snprintf(val, 64, "%" PRIi64, node_data->intval); + } + str_buf_append(*outbuf, val, val_len); + free(val); + break; + + case PLIST_REAL: + val = (char*)malloc(64); + val_len = dtostr(val, 64, node_data->realval); + str_buf_append(*outbuf, val, val_len); + free(val); + break; + + case PLIST_STRING: + case PLIST_KEY: { + const char *charmap[32] = { + "\\U0000", "\\U0001", "\\U0002", "\\U0003", "\\U0004", "\\U0005", "\\U0006", "\\U0007", + "\\b", "\\t", "\\n", "\\U000b", "\\f", "\\r", "\\U000e", "\\U000f", + "\\U0010", "\\U0011", "\\U0012", "\\U0013", "\\U0014", "\\U0015", "\\U0016", "\\U0017", + "\\U0018", "\\U0019", "\\U001a", "\\U001b", "\\U001c", "\\U001d", "\\U001e", "\\U001f", + }; + size_t j = 0; + size_t len = 0; + off_t start = 0; + off_t cur = 0; + int needs_quotes; + + len = node_data->length; + + needs_quotes = str_needs_quotes(node_data->strval, len); + + if (needs_quotes) { + str_buf_append(*outbuf, "\"", 1); + } + + for (j = 0; j < len; j++) { + unsigned char ch = (unsigned char)node_data->strval[j]; + if (ch < 0x20) { + str_buf_append(*outbuf, node_data->strval + start, cur - start); + str_buf_append(*outbuf, charmap[ch], (charmap[ch][1] == 'u') ? 6 : 2); + start = cur+1; + } else if (ch == '"') { + str_buf_append(*outbuf, node_data->strval + start, cur - start); + str_buf_append(*outbuf, "\\\"", 2); + start = cur+1; + } + cur++; + } + str_buf_append(*outbuf, node_data->strval + start, cur - start); + + if (needs_quotes) { + str_buf_append(*outbuf, "\"", 1); + } + + } break; + + case PLIST_ARRAY: { + str_buf_append(*outbuf, "(", 1); + node_t ch; + uint32_t cnt = 0; + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + if (cnt > 0) { + str_buf_append(*outbuf, ",", 1); + } + if (prettify) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i <= depth; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + plist_err_t res = node_to_openstep(ch, outbuf, depth+1, prettify); + if (res < 0) { + return res; + } + cnt++; + } + if (cnt > 0 && prettify) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i < depth; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + str_buf_append(*outbuf, ")", 1); + } break; + case PLIST_DICT: { + str_buf_append(*outbuf, "{", 1); + node_t ch; + uint32_t cnt = 0; + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + if (cnt > 0 && cnt % 2 == 0) { + str_buf_append(*outbuf, ";", 1); + } + if (cnt % 2 == 0 && prettify) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i <= depth; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + plist_err_t res = node_to_openstep(ch, outbuf, depth+1, prettify); + if (res < 0) { + return res; + } + if (cnt % 2 == 0) { + if (prettify) { + str_buf_append(*outbuf, " = ", 3); + } else { + str_buf_append(*outbuf, "=", 1); + } + } + cnt++; + } + if (cnt > 0) { + str_buf_append(*outbuf, ";", 1); + } + if (cnt > 0 && prettify) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i < depth; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + str_buf_append(*outbuf, "}", 1); + } break; + case PLIST_DATA: { + size_t j = 0; + size_t len = 0; + str_buf_append(*outbuf, "<", 1); + len = node_data->length; + for (j = 0; j < len; j++) { + char charb[4]; + if (prettify && j > 0 && (j % 4 == 0)) + str_buf_append(*outbuf, " ", 1); + sprintf(charb, "%02x", (unsigned char)node_data->buff[j]); + str_buf_append(*outbuf, charb, 2); + } + str_buf_append(*outbuf, ">", 1); + } break; + case PLIST_BOOLEAN: + PLIST_OSTEP_WRITE_ERR("PLIST_BOOLEAN type is not valid for OpenStep format\n"); + return PLIST_ERR_FORMAT; + case PLIST_NULL: + PLIST_OSTEP_WRITE_ERR("PLIST_NULL type is not valid for OpenStep format\n"); + return PLIST_ERR_FORMAT; + case PLIST_DATE: + // NOT VALID FOR OPENSTEP + PLIST_OSTEP_WRITE_ERR("PLIST_DATE type is not valid for OpenStep format\n"); + return PLIST_ERR_FORMAT; + case PLIST_UID: + // NOT VALID FOR OPENSTEP + PLIST_OSTEP_WRITE_ERR("PLIST_UID type is not valid for OpenStep format\n"); + return PLIST_ERR_FORMAT; + default: + return PLIST_ERR_UNKNOWN; + } + + return PLIST_ERR_SUCCESS; +} + +#define PO10i_LIMIT (INT64_MAX/10) + +/* based on https://stackoverflow.com/a/4143288 */ +static int num_digits_i(int64_t i) +{ + int n; + int64_t po10; + n=1; + if (i < 0) { + i = (i == INT64_MIN) ? INT64_MAX : -i; + n++; + } + po10=10; + while (i>=po10) { + n++; + if (po10 > PO10i_LIMIT) break; + po10*=10; + } + return n; +} + +#define PO10u_LIMIT (UINT64_MAX/10) + +/* based on https://stackoverflow.com/a/4143288 */ +static int num_digits_u(uint64_t i) +{ + int n; + uint64_t po10; + n=1; + po10=10; + while (i>=po10) { + n++; + if (po10 > PO10u_LIMIT) break; + po10*=10; + } + return n; +} + +static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify) +{ + plist_data_t data; + if (!node) { + return PLIST_ERR_INVALID_ARG; + } + data = plist_get_data(node); + if (node->children) { + node_t ch; + unsigned int n_children = node_n_children(node); + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + plist_err_t res = node_estimate_size(ch, size, depth + 1, prettify); + if (res < 0) { + return res; + } + } + switch (data->type) { + case PLIST_DICT: + *size += 2; // '{' and '}' + *size += n_children; // number of '=' and ';' + if (prettify) { + *size += n_children*2; // number of '\n' and extra spaces + *size += (uint64_t)n_children * (depth+1); // indent for every 2nd child + *size += 1; // additional '\n' + } + break; + case PLIST_ARRAY: + *size += 2; // '(' and ')' + *size += n_children-1; // number of ',' + if (prettify) { + *size += n_children; // number of '\n' + *size += (uint64_t)n_children * ((depth+1)<<1); // indent for every child + *size += 1; // additional '\n' + } + break; + default: + break; + } + if (prettify) + *size += (depth << 1); // indent for {} and () + } else { + switch (data->type) { + case PLIST_STRING: + case PLIST_KEY: + *size += data->length; + *size += 2; + break; + case PLIST_INT: + if (data->length == 16) { + *size += num_digits_u(data->intval); + } else { + *size += num_digits_i((int64_t)data->intval); + } + break; + case PLIST_REAL: + *size += dtostr(NULL, 0, data->realval); + break; + case PLIST_DICT: + case PLIST_ARRAY: + *size += 2; + break; + case PLIST_DATA: + *size += 2; // < and > + *size += data->length*2; + if (prettify) + *size += data->length/4; + break; + case PLIST_BOOLEAN: + // NOT VALID FOR OPENSTEP + PLIST_OSTEP_WRITE_ERR("PLIST_BOOLEAN type is not valid for OpenStep format\n"); + return PLIST_ERR_FORMAT; + case PLIST_DATE: + // NOT VALID FOR OPENSTEP + PLIST_OSTEP_WRITE_ERR("PLIST_DATE type is not valid for OpenStep format\n"); + return PLIST_ERR_FORMAT; + case PLIST_UID: + // NOT VALID FOR OPENSTEP + PLIST_OSTEP_WRITE_ERR("PLIST_UID type is not valid for OpenStep format\n"); + return PLIST_ERR_FORMAT; + default: + PLIST_OSTEP_WRITE_ERR("invalid node type encountered\n"); + return PLIST_ERR_UNKNOWN; + } + } + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_to_openstep(plist_t plist, char **openstep, uint32_t* length, int prettify) +{ + uint64_t size = 0; + plist_err_t res; + + if (!plist || !openstep || !length) { + return PLIST_ERR_INVALID_ARG; + } + + res = node_estimate_size((node_t)plist, &size, 0, prettify); + if (res < 0) { + return res; + } + + strbuf_t *outbuf = str_buf_new(size); + if (!outbuf) { + PLIST_OSTEP_WRITE_ERR("Could not allocate output buffer"); + return PLIST_ERR_NO_MEM; + } + + res = node_to_openstep((node_t)plist, &outbuf, 0, prettify); + if (res < 0) { + str_buf_free(outbuf); + *openstep = NULL; + *length = 0; + return res; + } + if (prettify) { + str_buf_append(outbuf, "\n", 1); + } + + str_buf_append(outbuf, "\0", 1); + + *openstep = (char*)outbuf->data; + *length = outbuf->len - 1; + + outbuf->data = NULL; + str_buf_free(outbuf); + + return PLIST_ERR_SUCCESS; +} + +struct _parse_ctx { + const char *start; + const char *pos; + const char *end; + int err; + uint32_t depth; +}; +typedef struct _parse_ctx* parse_ctx; + +static void parse_skip_ws(parse_ctx ctx) +{ + while (ctx->pos < ctx->end) { + // skip comments + if (*ctx->pos == '/' && (ctx->end - ctx->pos > 1)) { + if (*(ctx->pos+1) == '/') { + ctx->pos++; + while (ctx->pos < ctx->end) { + if ((*ctx->pos == '\n') || (*ctx->pos == '\r')) { + break; + } + ctx->pos++; + } + } else if (*(ctx->pos+1) == '*') { + ctx->pos++; + while (ctx->pos < ctx->end) { + if (*ctx->pos == '*' && (ctx->end - ctx->pos > 1)) { + if (*(ctx->pos+1) == '/') { + ctx->pos+=2; + break; + } + } + ctx->pos++; + } + } + if (ctx->pos >= ctx->end) { + break; + } + } + // break on any char that's not white space + if (!(((*(ctx->pos) == ' ') || (*(ctx->pos) == '\t') || (*(ctx->pos) == '\r') || (*(ctx->pos) == '\n')))) { + break; + } + ctx->pos++; + } +} + +#define HEX_DIGIT(x) ((x <= '9') ? (x - '0') : ((x <= 'F') ? (x - 'A' + 10) : (x - 'a' + 10))) + +static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist); + +static void parse_dict_data(parse_ctx ctx, plist_t dict) +{ + plist_t key = NULL; + plist_t val = NULL; + while (ctx->pos < ctx->end && !ctx->err) { + parse_skip_ws(ctx); + if (ctx->pos >= ctx->end || *ctx->pos == '}') { + break; + } + key = NULL; + ctx->err = node_from_openstep(ctx, &key); + if (ctx->err != 0) { + break; + } + if (!PLIST_IS_STRING(key)) { + PLIST_OSTEP_ERR("Invalid type for dictionary key at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + parse_skip_ws(ctx); + if (ctx->pos >= ctx->end) { + PLIST_OSTEP_ERR("EOF while parsing dictionary '=' delimiter at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + if (*ctx->pos != '=') { + PLIST_OSTEP_ERR("Missing '=' while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + ctx->pos++; + if (ctx->pos >= ctx->end) { + PLIST_OSTEP_ERR("EOF while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + val = NULL; + ctx->err = node_from_openstep(ctx, &val); + if (ctx->err != 0) { + break; + } + if (!val) { + PLIST_OSTEP_ERR("Missing value for dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + parse_skip_ws(ctx); + if (ctx->pos >= ctx->end) { + PLIST_OSTEP_ERR("EOF while parsing dictionary item terminator ';' at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + if (*ctx->pos != ';') { + PLIST_OSTEP_ERR("Missing terminating ';' while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + + plist_dict_set_item(dict, plist_get_string_ptr(key, NULL), val); + plist_free(key); + key = NULL; + val = NULL; + + ctx->pos++; + } + plist_free(key); + plist_free(val); +} + +static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) +{ + plist_t subnode = NULL; + const char *p = NULL; + ctx->depth++; + if (ctx->depth > 1000) { + PLIST_OSTEP_ERR("Too many levels of recursion (%u) at offset %ld\n", ctx->depth, (long int)(ctx->pos - ctx->start)); + ctx->err++; + return PLIST_ERR_PARSE; + } + while (ctx->pos < ctx->end && !ctx->err) { + parse_skip_ws(ctx); + if (ctx->pos >= ctx->end) { + break; + } + plist_data_t data = plist_new_plist_data(); + if (*ctx->pos == '{') { + data->type = PLIST_DICT; + subnode = plist_new_node(data); + ctx->pos++; + parse_dict_data(ctx, subnode); + if (ctx->err) { + goto err_out; + } + if (ctx->pos >= ctx->end) { + PLIST_OSTEP_ERR("EOF while parsing dictionary terminator '}' at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + if (*ctx->pos != '}') { + PLIST_OSTEP_ERR("Missing terminating '}' at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + goto err_out; + } + ctx->pos++; + *plist = subnode; + parse_skip_ws(ctx); + break; + } else if (*ctx->pos == '(') { + data->type = PLIST_ARRAY; + subnode = plist_new_node(data); + ctx->pos++; + plist_t tmp = NULL; + while (ctx->pos < ctx->end && !ctx->err) { + parse_skip_ws(ctx); + if (ctx->pos >= ctx->end || *ctx->pos == ')') { + break; + } + ctx->err = node_from_openstep(ctx, &tmp); + if (ctx->err != 0) { + break; + } + if (!tmp) { + ctx->err++; + break; + } + plist_array_append_item(subnode, tmp); + tmp = NULL; + parse_skip_ws(ctx); + if (ctx->pos >= ctx->end) { + PLIST_OSTEP_ERR("EOF while parsing array item delimiter ',' at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + if (*ctx->pos != ',') { + break; + } + ctx->pos++; + } + plist_free(tmp); + tmp = NULL; + if (ctx->err) { + goto err_out; + } + if (ctx->pos >= ctx->end) { + PLIST_OSTEP_ERR("EOF while parsing array terminator ')' at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + if (*ctx->pos != ')') { + PLIST_OSTEP_ERR("Missing terminating ')' at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + goto err_out; + } + ctx->pos++; + *plist = subnode; + parse_skip_ws(ctx); + break; + } else if (*ctx->pos == '<') { + data->type = PLIST_DATA; + ctx->pos++; + bytearray_t *bytes = byte_array_new(256); + while (ctx->pos < ctx->end && !ctx->err) { + parse_skip_ws(ctx); + if (ctx->pos >= ctx->end) { + PLIST_OSTEP_ERR("EOF while parsing data terminator '>' at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + if (*ctx->pos == '>') { + break; + } + if (!isxdigit(*ctx->pos)) { + PLIST_OSTEP_ERR("Invalid byte group in data at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + uint8_t b = HEX_DIGIT(*ctx->pos); + ctx->pos++; + if (ctx->pos >= ctx->end) { + PLIST_OSTEP_ERR("Unexpected end of data at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + if (!isxdigit(*ctx->pos)) { + PLIST_OSTEP_ERR("Invalid byte group in data at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + b = (b << 4) + HEX_DIGIT(*ctx->pos); + byte_array_append(bytes, &b, 1); + ctx->pos++; + } + if (ctx->err) { + byte_array_free(bytes); + plist_free_data(data); + goto err_out; + } + if (ctx->pos >= ctx->end) { + byte_array_free(bytes); + plist_free_data(data); + PLIST_OSTEP_ERR("EOF while parsing data terminator '>' at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + goto err_out; + } + if (*ctx->pos != '>') { + byte_array_free(bytes); + plist_free_data(data); + PLIST_OSTEP_ERR("Missing terminating '>' at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + goto err_out; + } + ctx->pos++; + data->buff = (uint8_t*)bytes->data; + data->length = bytes->len; + bytes->data = NULL; + byte_array_free(bytes); + *plist = plist_new_node(data); + parse_skip_ws(ctx); + break; + } else if (*ctx->pos == '"' || *ctx->pos == '\'') { + char c = *ctx->pos; + ctx->pos++; + p = ctx->pos; + int num_escapes = 0; + while (ctx->pos < ctx->end) { + if (*ctx->pos == '\\') { + num_escapes++; + } + if ((*ctx->pos == c) && (*(ctx->pos-1) != '\\')) { + break; + } + ctx->pos++; + } + if (ctx->pos >= ctx->end) { + plist_free_data(data); + PLIST_OSTEP_ERR("EOF while parsing quoted string at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + goto err_out; + } + if (*ctx->pos != c) { + plist_free_data(data); + PLIST_OSTEP_ERR("Missing closing quote (%c) at offset %ld\n", c, (long int)(ctx->pos - ctx->start)); + ctx->err++; + goto err_out; + } + size_t slen = ctx->pos - p; + ctx->pos++; // skip the closing quote + char* strbuf = (char*)malloc(slen+1); + if (num_escapes > 0) { + size_t i = 0; + size_t o = 0; + while (i < slen) { + if (p[i] == '\\') { + /* handle escape sequence */ + i++; + switch (p[i]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + // max 3 digits octal + unsigned char chr = 0; + int maxd = 3; + while ((i < slen) && (p[i] >= '0' && p[i] <= '7') && --maxd) { + chr = (chr << 3) + p[i] - '0'; + i++; + } + strbuf[o++] = (char)chr; + } break; + case 'U': { + i++; + // max 4 digits hex + uint16_t wchr = 0; + int maxd = 4; + while ((i < slen) && isxdigit(p[i]) && maxd--) { + wchr = (wchr << 4) + ((p[i] <= '9') ? (p[i] - '0') : ((p[i] <= 'F') ? (p[i] - 'A' + 10) : (p[i] - 'a' + 10))); + i++; + } + if (wchr >= 0x800) { + strbuf[o++] = (char)(0xE0 + ((wchr >> 12) & 0xF)); + strbuf[o++] = (char)(0x80 + ((wchr >> 6) & 0x3F)); + strbuf[o++] = (char)(0x80 + (wchr & 0x3F)); + } else if (wchr >= 0x80) { + strbuf[o++] = (char)(0xC0 + ((wchr >> 6) & 0x1F)); + strbuf[o++] = (char)(0x80 + (wchr & 0x3F)); + } else { + strbuf[o++] = (char)(wchr & 0x7F); + } + } break; + case 'a': strbuf[o++] = '\a'; i++; break; + case 'b': strbuf[o++] = '\b'; i++; break; + case 'f': strbuf[o++] = '\f'; i++; break; + case 'n': strbuf[o++] = '\n'; i++; break; + case 'r': strbuf[o++] = '\r'; i++; break; + case 't': strbuf[o++] = '\t'; i++; break; + case 'v': strbuf[o++] = '\v'; i++; break; + case '"': strbuf[o++] = '"'; i++; break; + case '\'': strbuf[o++] = '\''; i++; break; + default: + break; + } + } else { + strbuf[o++] = p[i++]; + } + } + strbuf[o] = '\0'; + slen = o; + } else { + strncpy(strbuf, p, slen); + strbuf[slen] = '\0'; + } + data->type = PLIST_STRING; + data->strval = strbuf; + data->length = slen; + *plist = plist_new_node(data); + parse_skip_ws(ctx); + break; + } else { + // unquoted string + size_t slen = 0; + parse_skip_ws(ctx); + p = ctx->pos; + while (ctx->pos < ctx->end) { + if (!allowed_unquoted_chars[(uint8_t)*ctx->pos]) { + break; + } + ctx->pos++; + } + slen = ctx->pos-p; + if (slen > 0) { + data->type = PLIST_STRING; + data->strval = strndup(p, slen); + data->length = slen; + *plist = plist_new_node(data); + parse_skip_ws(ctx); + break; + } else { + plist_free_data(data); + PLIST_OSTEP_ERR("Unexpected character when parsing unquoted string at offset %ld\n", (long int)(ctx->pos - ctx->start)); + ctx->err++; + break; + } + } + ctx->pos++; + } + ctx->depth--; + +err_out: + if (ctx->err) { + plist_free(subnode); + plist_free(*plist); + *plist = NULL; + return PLIST_ERR_PARSE; + } + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_from_openstep(const char *plist_ostep, uint32_t length, plist_t * plist) +{ + if (!plist) { + return PLIST_ERR_INVALID_ARG; + } + *plist = NULL; + if (!plist_ostep || (length == 0)) { + return PLIST_ERR_INVALID_ARG; + } + + struct _parse_ctx ctx = { plist_ostep, plist_ostep, plist_ostep + length, 0 , 0 }; + + plist_err_t err = node_from_openstep(&ctx, plist); + if (err == 0) { + if (!*plist) { + /* whitespace only file is considered an empty dictionary */ + *plist = plist_new_dict(); + } else if (ctx.pos < ctx.end && *ctx.pos == '=') { + /* attempt to parse this as 'strings' data */ + plist_free(*plist); + *plist = NULL; + plist_t pl = plist_new_dict(); + ctx.pos = plist_ostep; + parse_dict_data(&ctx, pl); + if (ctx.err > 0) { + plist_free(pl); + PLIST_OSTEP_ERR("Failed to parse strings data\n"); + err = PLIST_ERR_PARSE; + } else { + *plist = pl; + } + } + } + + return err; +} diff --git a/src/out-default.c b/src/out-default.c new file mode 100644 index 0000000..266070b --- /dev/null +++ b/src/out-default.c @@ -0,0 +1,491 @@ +/* + * out-default.c + * libplist default *output-only* format - NOT for machine parsing + * + * Copyright (c) 2022-2023 Nikias Bassen All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +#include <inttypes.h> +#include <ctype.h> +#include <math.h> +#include <limits.h> + +#include <node.h> + +#include "plist.h" +#include "strbuf.h" +#include "time64.h" + +#define MAC_EPOCH 978307200 + +static size_t dtostr(char *buf, size_t bufsize, double realval) +{ + size_t len = 0; + if (isnan(realval)) { + len = snprintf(buf, bufsize, "nan"); + } else if (isinf(realval)) { + len = snprintf(buf, bufsize, "%cinfinity", (realval > 0.0) ? '+' : '-'); + } else if (realval == 0.0f) { + len = snprintf(buf, bufsize, "0.0"); + } else { + size_t i = 0; + len = snprintf(buf, bufsize, "%.*g", 17, realval); + for (i = 0; buf && i < len; i++) { + if (buf[i] == ',') { + buf[i] = '.'; + break; + } else if (buf[i] == '.') { + break; + } + } + } + return len; +} + +static plist_err_t node_to_string(node_t node, bytearray_t **outbuf, uint32_t depth, uint32_t indent, int partial_data) +{ + plist_data_t node_data = NULL; + + char *val = NULL; + size_t val_len = 0; + + uint32_t i = 0; + + if (!node) + return PLIST_ERR_INVALID_ARG; + + node_data = plist_get_data(node); + + switch (node_data->type) + { + case PLIST_BOOLEAN: + { + if (node_data->boolval) { + str_buf_append(*outbuf, "true", 4); + } else { + str_buf_append(*outbuf, "false", 5); + } + } + break; + + case PLIST_NULL: + str_buf_append(*outbuf, "null", 4); + break; + + case PLIST_INT: + val = (char*)malloc(64); + if (node_data->length == 16) { + val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); + } else { + val_len = snprintf(val, 64, "%" PRIi64, node_data->intval); + } + str_buf_append(*outbuf, val, val_len); + free(val); + break; + + case PLIST_REAL: + val = (char*)malloc(64); + val_len = dtostr(val, 64, node_data->realval); + str_buf_append(*outbuf, val, val_len); + free(val); + break; + + case PLIST_STRING: + case PLIST_KEY: { + const char *charmap[32] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", + "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", "\\u000e", "\\u000f", + "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017", + "\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f", + }; + size_t j = 0; + size_t len = 0; + off_t start = 0; + off_t cur = 0; + + str_buf_append(*outbuf, "\"", 1); + + len = node_data->length; + for (j = 0; j < len; j++) { + unsigned char ch = (unsigned char)node_data->strval[j]; + if (ch < 0x20) { + str_buf_append(*outbuf, node_data->strval + start, cur - start); + str_buf_append(*outbuf, charmap[ch], (charmap[ch][1] == 'u') ? 6 : 2); + start = cur+1; + } else if (ch == '"') { + str_buf_append(*outbuf, node_data->strval + start, cur - start); + str_buf_append(*outbuf, "\\\"", 2); + start = cur+1; + } + cur++; + } + str_buf_append(*outbuf, node_data->strval + start, cur - start); + + str_buf_append(*outbuf, "\"", 1); + } break; + + case PLIST_ARRAY: { + str_buf_append(*outbuf, "[", 1); + node_t ch; + uint32_t cnt = 0; + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + if (cnt > 0) { + str_buf_append(*outbuf, ",", 1); + } + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i <= depth+indent; i++) { + str_buf_append(*outbuf, " ", 2); + } + plist_err_t res = node_to_string(ch, outbuf, depth+1, indent, partial_data); + if (res < 0) { + return res; + } + cnt++; + } + if (cnt > 0) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i < depth+indent; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + str_buf_append(*outbuf, "]", 1); + } break; + case PLIST_DICT: { + str_buf_append(*outbuf, "{", 1); + node_t ch; + uint32_t cnt = 0; + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + if (cnt > 0 && cnt % 2 == 0) { + str_buf_append(*outbuf, ",", 1); + } + if (cnt % 2 == 0) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i <= depth+indent; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + plist_err_t res = node_to_string(ch, outbuf, depth+1, indent, partial_data); + if (res < 0) { + return res; + } + if (cnt % 2 == 0) { + str_buf_append(*outbuf, ": ", 2); + } + cnt++; + } + if (cnt > 0) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i < depth+indent; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + str_buf_append(*outbuf, "}", 1); + } break; + case PLIST_DATA: + { + str_buf_append(*outbuf, "<", 1); + size_t len = node_data->length; + char charb[4]; + if (!partial_data || len <= 24) { + for (i = 0; i < len; i++) { + if (i > 0 && (i % 4 == 0)) + str_buf_append(*outbuf, " ", 1); + sprintf(charb, "%02x", (unsigned char)node_data->buff[i]); + str_buf_append(*outbuf, charb, 2); + } + } else { + for (i = 0; i < 16; i++) { + if (i > 0 && (i % 4 == 0)) + str_buf_append(*outbuf, " ", 1); + sprintf(charb, "%02x", (unsigned char)node_data->buff[i]); + str_buf_append(*outbuf, charb, 2); + } + str_buf_append(*outbuf, " ... ", 5); + for (i = len - 8; i < len; i++) { + sprintf(charb, "%02x", (unsigned char)node_data->buff[i]); + str_buf_append(*outbuf, charb, 2); + if (i > 0 && i < len-1 && (i % 4 == 0)) + str_buf_append(*outbuf, " ", 1); + } + } + str_buf_append(*outbuf, ">", 1); + } + break; + case PLIST_DATE: + { + Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH; + struct TM _btime; + struct TM *btime = gmtime64_r(&timev, &_btime); + if (btime) { + val = (char*)calloc(1, 26); + struct tm _tmcopy; + copy_TM64_to_tm(btime, &_tmcopy); + val_len = strftime(val, 26, "%Y-%m-%d %H:%M:%S +0000", &_tmcopy); + if (val_len > 0) { + str_buf_append(*outbuf, val, val_len); + } + free(val); + val = NULL; + } + } + break; + case PLIST_UID: + { + str_buf_append(*outbuf, "CF$UID:", 7); + val = (char*)malloc(64); + if (node_data->length == 16) { + val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); + } else { + val_len = snprintf(val, 64, "%" PRIi64, node_data->intval); + } + str_buf_append(*outbuf, val, val_len); + free(val); + } + break; + default: + return PLIST_ERR_UNKNOWN; + } + + return PLIST_ERR_SUCCESS; +} + +#define PO10i_LIMIT (INT64_MAX/10) + +/* based on https://stackoverflow.com/a/4143288 */ +static int num_digits_i(int64_t i) +{ + int n; + int64_t po10; + n=1; + if (i < 0) { + i = (i == INT64_MIN) ? INT64_MAX : -i; + n++; + } + po10=10; + while (i>=po10) { + n++; + if (po10 > PO10i_LIMIT) break; + po10*=10; + } + return n; +} + +#define PO10u_LIMIT (UINT64_MAX/10) + +/* based on https://stackoverflow.com/a/4143288 */ +static int num_digits_u(uint64_t i) +{ + int n; + uint64_t po10; + n=1; + po10=10; + while (i>=po10) { + n++; + if (po10 > PO10u_LIMIT) break; + po10*=10; + } + return n; +} + +static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent, int partial_data) +{ + plist_data_t data; + if (!node) { + return PLIST_ERR_INVALID_ARG; + } + data = plist_get_data(node); + if (node->children) { + node_t ch; + unsigned int n_children = node_n_children(node); + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + plist_err_t res = node_estimate_size(ch, size, depth + 1, indent, partial_data); + if (res < 0) { + return res; + } + } + switch (data->type) { + case PLIST_DICT: + *size += 2; // '{' and '}' + *size += n_children-1; // number of ':' and ',' + *size += n_children; // number of '\n' and extra space + *size += (uint64_t)n_children * (depth+indent+1); // indent for every 2nd child + *size += indent+1; // additional '\n' + break; + case PLIST_ARRAY: + *size += 2; // '[' and ']' + *size += n_children-1; // number of ',' + *size += n_children; // number of '\n' + *size += (uint64_t)n_children * ((depth+indent+1)<<1); // indent for every child + *size += indent+1; // additional '\n' + break; + default: + break; + } + *size += ((depth+indent) << 1); // indent for {} and [] + } else { + switch (data->type) { + case PLIST_STRING: + case PLIST_KEY: + *size += data->length; + *size += 2; + break; + case PLIST_INT: + if (data->length == 16) { + *size += num_digits_u(data->intval); + } else { + *size += num_digits_i((int64_t)data->intval); + } + break; + case PLIST_REAL: + *size += dtostr(NULL, 0, data->realval); + break; + case PLIST_BOOLEAN: + *size += ((data->boolval) ? 4 : 5); + break; + case PLIST_NULL: + *size += 4; + break; + case PLIST_DICT: + case PLIST_ARRAY: + *size += 2; + break; + case PLIST_DATA: + *size += 2; // < and > + if (partial_data) { + *size += 58; + } else { + *size += data->length * 2; + *size += data->length / 4; // space between 4 byte groups + } + break; + case PLIST_DATE: + *size += 25; + break; + case PLIST_UID: + *size += 7; // "CF$UID:" + *size += num_digits_u(data->intval); + break; + default: +#ifdef DEBUG + fprintf(stderr, "%s: invalid node type encountered\n", __func__); +#endif + return PLIST_ERR_UNKNOWN; + } + } + if (depth == 0) { + *size += 1; // final newline + } + return PLIST_ERR_SUCCESS; +} + +static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options) +{ + uint8_t indent = 0; + if (options & PLIST_OPT_INDENT) { + indent = (options >> 24) & 0xFF; + } + uint8_t i; + for (i = 0; i < indent; i++) { + str_buf_append(outbuf, " ", 2); + } + plist_err_t res = node_to_string((node_t)plist, &outbuf, 0, indent, options & PLIST_OPT_PARTIAL_DATA); + if (res < 0) { + return res; + } + if (!(options & PLIST_OPT_NO_NEWLINE)) { + str_buf_append(outbuf, "\n", 1); + } + return res; +} + +plist_err_t plist_write_to_string_default(plist_t plist, char **output, uint32_t* length, plist_write_options_t options) +{ + uint64_t size = 0; + plist_err_t res; + + if (!plist || !output || !length) { + return PLIST_ERR_INVALID_ARG; + } + + uint8_t indent = 0; + if (options & PLIST_OPT_INDENT) { + indent = (options >> 24) & 0xFF; + } + + res = node_estimate_size((node_t)plist, &size, 0, indent, options & PLIST_OPT_PARTIAL_DATA); + if (res < 0) { + return res; + } + + strbuf_t *outbuf = str_buf_new(size); + if (!outbuf) { +#if DEBUG + fprintf(stderr, "%s: Could not allocate output buffer\n", __func__); +#endif + return PLIST_ERR_NO_MEM; + } + + res = _plist_write_to_strbuf(plist, outbuf, options); + if (res < 0) { + str_buf_free(outbuf); + *output = NULL; + *length = 0; + return res; + } + str_buf_append(outbuf, "\0", 1); + + *output = (char*)outbuf->data; + *length = outbuf->len - 1; + + outbuf->data = NULL; + str_buf_free(outbuf); + + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_write_to_stream_default(plist_t plist, FILE *stream, plist_write_options_t options) +{ + if (!plist || !stream) { + return PLIST_ERR_INVALID_ARG; + } + strbuf_t *outbuf = str_buf_new_for_stream(stream); + if (!outbuf) { +#if DEBUG + fprintf(stderr, "%s: Could not allocate output buffer\n", __func__); +#endif + return PLIST_ERR_NO_MEM; + } + + plist_err_t res = _plist_write_to_strbuf(plist, outbuf, options); + if (res < 0) { + str_buf_free(outbuf); + return res; + } + + str_buf_free(outbuf); + + return PLIST_ERR_SUCCESS; +} diff --git a/src/out-limd.c b/src/out-limd.c new file mode 100644 index 0000000..7d861f8 --- /dev/null +++ b/src/out-limd.c @@ -0,0 +1,449 @@ +/* + * out-limd.c + * libplist *output-only* format introduced by libimobiledevice/ideviceinfo + * - NOT for machine parsing + * + * Copyright (c) 2022-2023 Nikias Bassen All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +#include <inttypes.h> +#include <ctype.h> +#include <math.h> +#include <limits.h> + +#include <node.h> + +#include "plist.h" +#include "strbuf.h" +#include "time64.h" +#include "base64.h" + +#define MAC_EPOCH 978307200 + +static size_t dtostr(char *buf, size_t bufsize, double realval) +{ + size_t len = 0; + if (isnan(realval)) { + len = snprintf(buf, bufsize, "nan"); + } else if (isinf(realval)) { + len = snprintf(buf, bufsize, "%cinfinity", (realval > 0.0) ? '+' : '-'); + } else if (realval == 0.0f) { + len = snprintf(buf, bufsize, "0.0"); + } else { + size_t i = 0; + len = snprintf(buf, bufsize, "%.*g", 17, realval); + for (i = 0; buf && i < len; i++) { + if (buf[i] == ',') { + buf[i] = '.'; + break; + } else if (buf[i] == '.') { + break; + } + } + } + return len; +} + +static plist_err_t node_to_string(node_t node, bytearray_t **outbuf, uint32_t depth, uint32_t indent) +{ + plist_data_t node_data = NULL; + + char *val = NULL; + size_t val_len = 0; + char buf[16]; + + uint32_t i = 0; + + if (!node) + return PLIST_ERR_INVALID_ARG; + + node_data = plist_get_data(node); + + switch (node_data->type) + { + case PLIST_BOOLEAN: + { + if (node_data->boolval) { + str_buf_append(*outbuf, "true", 4); + } else { + str_buf_append(*outbuf, "false", 5); + } + } + break; + + case PLIST_NULL: + str_buf_append(*outbuf, "null", 4); + break; + + case PLIST_INT: + val = (char*)malloc(64); + if (node_data->length == 16) { + val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); + } else { + val_len = snprintf(val, 64, "%" PRIi64, node_data->intval); + } + str_buf_append(*outbuf, val, val_len); + free(val); + break; + + case PLIST_REAL: + val = (char*)malloc(64); + val_len = dtostr(val, 64, node_data->realval); + str_buf_append(*outbuf, val, val_len); + free(val); + break; + + case PLIST_STRING: + case PLIST_KEY: { + const char *charmap[32] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", + "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", "\\u000e", "\\u000f", + "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017", + "\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f", + }; + size_t j = 0; + size_t len = 0; + off_t start = 0; + off_t cur = 0; + + len = node_data->length; + for (j = 0; j < len; j++) { + unsigned char ch = (unsigned char)node_data->strval[j]; + if (ch < 0x20) { + str_buf_append(*outbuf, node_data->strval + start, cur - start); + str_buf_append(*outbuf, charmap[ch], (charmap[ch][1] == 'u') ? 6 : 2); + start = cur+1; + } + cur++; + } + str_buf_append(*outbuf, node_data->strval + start, cur - start); + } break; + + case PLIST_ARRAY: { + node_t ch; + uint32_t cnt = 0; + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + if (cnt > 0 || (cnt == 0 && node->parent != NULL)) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i < depth+indent; i++) { + str_buf_append(*outbuf, " ", 1); + } + } + size_t sl = sprintf(buf, "%u: ", cnt); + str_buf_append(*outbuf, buf, sl); + plist_err_t res = node_to_string(ch, outbuf, depth+1, indent); + if (res < 0) { + return res; + } + cnt++; + } + } break; + case PLIST_DICT: { + node_t ch; + uint32_t cnt = 0; + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + if (cnt > 0 && cnt % 2 == 0) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i < depth+indent; i++) { + str_buf_append(*outbuf, " ", 1); + } + } + plist_err_t res = node_to_string(ch, outbuf, depth+1, indent); + if (res < 0) { + return res; + } + if (cnt % 2 == 0) { + plist_t valnode = (plist_t)node_next_sibling(ch); + if (PLIST_IS_ARRAY(valnode)) { + size_t sl = sprintf(buf, "[%u]:", plist_array_get_size(valnode)); + str_buf_append(*outbuf, buf, sl); + } else { + str_buf_append(*outbuf, ": ", 2); + } + } + cnt++; + } + } break; + case PLIST_DATA: + { + val = (char*)malloc(4096); + size_t done = 0; + while (done < node_data->length) { + size_t amount = node_data->length - done; + if (amount > 3072) { + amount = 3072; + } + size_t bsize = base64encode(val, node_data->buff + done, amount); + str_buf_append(*outbuf, val, bsize); + done += amount; + } + } + break; + case PLIST_DATE: + { + Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH; + struct TM _btime; + struct TM *btime = gmtime64_r(&timev, &_btime); + if (btime) { + val = (char*)calloc(1, 24); + struct tm _tmcopy; + copy_TM64_to_tm(btime, &_tmcopy); + val_len = strftime(val, 24, "%Y-%m-%dT%H:%M:%SZ", &_tmcopy); + if (val_len > 0) { + str_buf_append(*outbuf, val, val_len); + } + free(val); + val = NULL; + } + } + break; + case PLIST_UID: + { + str_buf_append(*outbuf, "CF$UID:", 7); + val = (char*)malloc(64); + if (node_data->length == 16) { + val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); + } else { + val_len = snprintf(val, 64, "%" PRIi64, node_data->intval); + } + str_buf_append(*outbuf, val, val_len); + free(val); + } + break; + default: + return PLIST_ERR_UNKNOWN; + } + + return PLIST_ERR_SUCCESS; +} + +#define PO10i_LIMIT (INT64_MAX/10) + +/* based on https://stackoverflow.com/a/4143288 */ +static int num_digits_i(int64_t i) +{ + int n; + int64_t po10; + n=1; + if (i < 0) { + i = (i == INT64_MIN) ? INT64_MAX : -i; + n++; + } + po10=10; + while (i>=po10) { + n++; + if (po10 > PO10i_LIMIT) break; + po10*=10; + } + return n; +} + +#define PO10u_LIMIT (UINT64_MAX/10) + +/* based on https://stackoverflow.com/a/4143288 */ +static int num_digits_u(uint64_t i) +{ + int n; + uint64_t po10; + n=1; + po10=10; + while (i>=po10) { + n++; + if (po10 > PO10u_LIMIT) break; + po10*=10; + } + return n; +} + +static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent) +{ + plist_data_t data; + if (!node) { + return PLIST_ERR_INVALID_ARG; + } + data = plist_get_data(node); + if (node->children) { + node_t ch; + unsigned int n_children = node_n_children(node); + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + plist_err_t res = node_estimate_size(ch, size, depth + 1, indent); + if (res < 0) { + return res; + } + } + switch (data->type) { + case PLIST_DICT: + *size += n_children-1; // number of ':' and ' ' + *size += n_children; // number of '\n' and extra space + *size += (uint64_t)n_children * (depth+indent+1); // indent for every 2nd child + *size += indent+1; // additional '\n' + break; + case PLIST_ARRAY: + *size += n_children-1; // number of ',' + *size += n_children; // number of '\n' + *size += (uint64_t)n_children * ((depth+indent+1)<<1); // indent for every child + *size += indent+1; // additional '\n' + break; + default: + break; + } + } else { + switch (data->type) { + case PLIST_STRING: + case PLIST_KEY: + *size += data->length; + break; + case PLIST_INT: + if (data->length == 16) { + *size += num_digits_u(data->intval); + } else { + *size += num_digits_i((int64_t)data->intval); + } + break; + case PLIST_REAL: + *size += dtostr(NULL, 0, data->realval); + break; + case PLIST_BOOLEAN: + *size += ((data->boolval) ? 4 : 5); + break; + case PLIST_NULL: + *size += 4; + break; + case PLIST_DICT: + case PLIST_ARRAY: + *size += 3; + break; + case PLIST_DATA: + *size += (data->length / 3) * 4 + 4; + break; + case PLIST_DATE: + *size += 23; + break; + case PLIST_UID: + *size += 7; // "CF$UID:" + *size += num_digits_u(data->intval); + break; + default: +#ifdef DEBUG + fprintf(stderr, "%s: invalid node type encountered\n", __func__); +#endif + return PLIST_ERR_UNKNOWN; + } + } + if (depth == 0) { + *size += 1; // final newline + } + return PLIST_ERR_SUCCESS; +} + +static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options) +{ + uint8_t indent = 0; + if (options & PLIST_OPT_INDENT) { + indent = (options >> 24) & 0xFF; + } + uint8_t i; + for (i = 0; i < indent; i++) { + str_buf_append(outbuf, " ", 1); + } + plist_err_t res = node_to_string((node_t)plist, &outbuf, 0, indent); + if (res < 0) { + return res; + } + if (!(options & PLIST_OPT_NO_NEWLINE)) { + str_buf_append(outbuf, "\n", 1); + } + return res; +} + +plist_err_t plist_write_to_string_limd(plist_t plist, char **output, uint32_t* length, plist_write_options_t options) +{ + uint64_t size = 0; + plist_err_t res; + + if (!plist || !output || !length) { + return PLIST_ERR_INVALID_ARG; + } + + uint8_t indent = 0; + if (options & PLIST_OPT_INDENT) { + indent = (options >> 24) & 0xFF; + } + + res = node_estimate_size((node_t)plist, &size, 0, indent); + if (res < 0) { + return res; + } + + strbuf_t *outbuf = str_buf_new(size); + if (!outbuf) { +#if DEBUG + fprintf(stderr, "%s: Could not allocate output buffer\n", __func__); +#endif + return PLIST_ERR_NO_MEM; + } + + res = _plist_write_to_strbuf(plist, outbuf, options); + if (res < 0) { + str_buf_free(outbuf); + *output = NULL; + *length = 0; + return res; + } + str_buf_append(outbuf, "\0", 1); + + *output = (char*)outbuf->data; + *length = outbuf->len - 1; + + outbuf->data = NULL; + str_buf_free(outbuf); + + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_write_to_stream_limd(plist_t plist, FILE *stream, plist_write_options_t options) +{ + if (!plist || !stream) { + return PLIST_ERR_INVALID_ARG; + } + strbuf_t *outbuf = str_buf_new_for_stream(stream); + if (!outbuf) { +#if DEBUG + fprintf(stderr, "%s: Could not allocate output buffer\n", __func__); +#endif + return PLIST_ERR_NO_MEM; + } + + plist_err_t res = _plist_write_to_strbuf(plist, outbuf, options); + if (res < 0) { + str_buf_free(outbuf); + return res; + } + + str_buf_free(outbuf); + + return PLIST_ERR_SUCCESS; +} diff --git a/src/out-plutil.c b/src/out-plutil.c new file mode 100644 index 0000000..d85f22c --- /dev/null +++ b/src/out-plutil.c @@ -0,0 +1,465 @@ +/* + * out-plutil.c + * plutil-like *output-only* format - NOT for machine parsing + * + * Copyright (c) 2023 Nikias Bassen All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +#include <inttypes.h> +#include <ctype.h> +#include <math.h> +#include <limits.h> + +#include <node.h> + +#include "plist.h" +#include "strbuf.h" +#include "time64.h" + +#define MAC_EPOCH 978307200 + +static size_t dtostr(char *buf, size_t bufsize, double realval) +{ + size_t len = 0; + if (isnan(realval)) { + len = snprintf(buf, bufsize, "nan"); + } else if (isinf(realval)) { + len = snprintf(buf, bufsize, "%cinfinity", (realval > 0.0) ? '+' : '-'); + } else if (realval == 0.0f) { + len = snprintf(buf, bufsize, "0.0"); + } else { + size_t i = 0; + len = snprintf(buf, bufsize, "%.*g", 17, realval); + for (i = 0; buf && i < len; i++) { + if (buf[i] == ',') { + buf[i] = '.'; + break; + } else if (buf[i] == '.') { + break; + } + } + } + return len; +} + +static plist_err_t node_to_string(node_t node, bytearray_t **outbuf, uint32_t depth) +{ + plist_data_t node_data = NULL; + + char *val = NULL; + size_t val_len = 0; + + uint32_t i = 0; + + if (!node) + return PLIST_ERR_INVALID_ARG; + + node_data = plist_get_data(node); + + switch (node_data->type) + { + case PLIST_BOOLEAN: + { + if (node_data->boolval) { + str_buf_append(*outbuf, "1", 1); + } else { + str_buf_append(*outbuf, "0", 1); + } + } + break; + + case PLIST_NULL: + str_buf_append(*outbuf, "<null>", 6); + break; + + case PLIST_INT: + val = (char*)malloc(64); + if (node_data->length == 16) { + val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); + } else { + val_len = snprintf(val, 64, "%" PRIi64, node_data->intval); + } + str_buf_append(*outbuf, val, val_len); + free(val); + break; + + case PLIST_REAL: + val = (char*)malloc(64); + val_len = dtostr(val, 64, node_data->realval); + str_buf_append(*outbuf, val, val_len); + free(val); + break; + + case PLIST_STRING: + case PLIST_KEY: { + const char *charmap[32] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", + "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", "\\u000e", "\\u000f", + "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017", + "\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f", + }; + size_t j = 0; + size_t len = 0; + off_t start = 0; + off_t cur = 0; + + str_buf_append(*outbuf, "\"", 1); + + len = node_data->length; + for (j = 0; j < len; j++) { + unsigned char ch = (unsigned char)node_data->strval[j]; + if (ch < 0x20) { + str_buf_append(*outbuf, node_data->strval + start, cur - start); + str_buf_append(*outbuf, charmap[ch], (charmap[ch][1] == 'u') ? 6 : 2); + start = cur+1; + } else if (ch == '"') { + str_buf_append(*outbuf, node_data->strval + start, cur - start); + str_buf_append(*outbuf, "\\\"", 2); + start = cur+1; + } + cur++; + } + str_buf_append(*outbuf, node_data->strval + start, cur - start); + + str_buf_append(*outbuf, "\"", 1); + } break; + + case PLIST_ARRAY: { + str_buf_append(*outbuf, "[", 1); + node_t ch; + uint32_t cnt = 0; + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i <= depth; i++) { + str_buf_append(*outbuf, " ", 2); + } + char indexbuf[16]; + int l = sprintf(indexbuf, "%u => ", cnt); + str_buf_append(*outbuf, indexbuf, l); + plist_err_t res = node_to_string(ch, outbuf, depth+1); + if (res < 0) { + return res; + } + cnt++; + } + if (cnt > 0) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i < depth; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + str_buf_append(*outbuf, "]", 1); + } break; + case PLIST_DICT: { + str_buf_append(*outbuf, "{", 1); + node_t ch; + uint32_t cnt = 0; + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + if (cnt % 2 == 0) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i <= depth; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + plist_err_t res = node_to_string(ch, outbuf, depth+1); + if (res < 0) { + return res; + } + if (cnt % 2 == 0) { + str_buf_append(*outbuf, " => ", 4); + } + cnt++; + } + if (cnt > 0) { + str_buf_append(*outbuf, "\n", 1); + for (i = 0; i < depth; i++) { + str_buf_append(*outbuf, " ", 2); + } + } + str_buf_append(*outbuf, "}", 1); + } break; + case PLIST_DATA: + { + val = (char*)calloc(1, 48); + size_t len = node_data->length; + size_t slen = snprintf(val, 48, "{length = %" PRIu64 ", bytes = 0x", (uint64_t)len); + str_buf_append(*outbuf, val, slen); + if (len <= 24) { + for (i = 0; i < len; i++) { + sprintf(val, "%02x", (unsigned char)node_data->buff[i]); + str_buf_append(*outbuf, val, 2); + } + } else { + for (i = 0; i < 16; i++) { + if (i > 0 && (i % 4 == 0)) + str_buf_append(*outbuf, " ", 1); + sprintf(val, "%02x", (unsigned char)node_data->buff[i]); + str_buf_append(*outbuf, val, 2); + } + str_buf_append(*outbuf, " ... ", 5); + for (i = len - 8; i < len; i++) { + sprintf(val, "%02x", (unsigned char)node_data->buff[i]); + str_buf_append(*outbuf, val, 2); + if (i > 0 && (i % 4 == 0)) + str_buf_append(*outbuf, " ", 1); + } + } + free(val); + val = NULL; + str_buf_append(*outbuf, "}", 1); + } + break; + case PLIST_DATE: + { + Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH; + struct TM _btime; + struct TM *btime = gmtime64_r(&timev, &_btime); + if (btime) { + val = (char*)calloc(1, 26); + struct tm _tmcopy; + copy_TM64_to_tm(btime, &_tmcopy); + val_len = strftime(val, 26, "%Y-%m-%d %H:%M:%S +0000", &_tmcopy); + if (val_len > 0) { + str_buf_append(*outbuf, val, val_len); + } + free(val); + val = NULL; + } + } + break; + case PLIST_UID: + { + val = (char*)malloc(88); + val_len = sprintf(val, "<CFKeyedArchiverUID %p [%p]>{value = %" PRIu64 "}", node, node_data, node_data->intval); + str_buf_append(*outbuf, val, val_len); + free(val); + val = NULL; + } + break; + default: + return PLIST_ERR_UNKNOWN; + } + + return PLIST_ERR_SUCCESS; +} + +#define PO10i_LIMIT (INT64_MAX/10) + +/* based on https://stackoverflow.com/a/4143288 */ +static int num_digits_i(int64_t i) +{ + int n; + int64_t po10; + n=1; + if (i < 0) { + i = (i == INT64_MIN) ? INT64_MAX : -i; + n++; + } + po10=10; + while (i>=po10) { + n++; + if (po10 > PO10i_LIMIT) break; + po10*=10; + } + return n; +} + +#define PO10u_LIMIT (UINT64_MAX/10) + +/* based on https://stackoverflow.com/a/4143288 */ +static int num_digits_u(uint64_t i) +{ + int n; + uint64_t po10; + n=1; + po10=10; + while (i>=po10) { + n++; + if (po10 > PO10u_LIMIT) break; + po10*=10; + } + return n; +} + +static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth) +{ + plist_data_t data; + if (!node) { + return PLIST_ERR_INVALID_ARG; + } + data = plist_get_data(node); + if (node->children) { + node_t ch; + unsigned int n_children = node_n_children(node); + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + plist_err_t res = node_estimate_size(ch, size, depth + 1); + if (res < 0) { + return res; + } + } + switch (data->type) { + case PLIST_DICT: + *size += 2; // '{' and '}' + *size += n_children-1; // number of ':' and ',' + *size += n_children; // number of '\n' and extra space + *size += (uint64_t)n_children * (depth+1); // indent for every 2nd child + *size += 1; // additional '\n' + break; + case PLIST_ARRAY: + *size += 2; // '[' and ']' + *size += n_children-1; // number of ',' + *size += n_children; // number of '\n' + *size += (uint64_t)n_children * ((depth+1)<<1); // indent for every child + *size += 1; // additional '\n' + break; + default: + break; + } + *size += (depth << 1); // indent for {} and [] + } else { + switch (data->type) { + case PLIST_STRING: + case PLIST_KEY: + *size += data->length; + *size += 2; + break; + case PLIST_INT: + if (data->length == 16) { + *size += num_digits_u(data->intval); + } else { + *size += num_digits_i((int64_t)data->intval); + } + break; + case PLIST_REAL: + *size += dtostr(NULL, 0, data->realval); + break; + case PLIST_BOOLEAN: + *size += 1; + break; + case PLIST_NULL: + *size += 6; + break; + case PLIST_DICT: + case PLIST_ARRAY: + *size += 2; + break; + case PLIST_DATA: + *size = (data->length <= 24) ? 73 : 100; + break; + case PLIST_DATE: + *size += 25; + break; + case PLIST_UID: + *size += 88; + break; + default: +#ifdef DEBUG + fprintf(stderr, "invalid node type encountered\n"); +#endif + return PLIST_ERR_UNKNOWN; + } + } + if (depth == 0) { + *size += 1; // final newline + } + return PLIST_ERR_SUCCESS; +} + +static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options) +{ + plist_err_t res = node_to_string((node_t)plist, &outbuf, 0); + if (res < 0) { + return res; + } + if (!(options & PLIST_OPT_NO_NEWLINE)) { + str_buf_append(outbuf, "\n", 1); + } + return res; +} + +plist_err_t plist_write_to_string_plutil(plist_t plist, char **output, uint32_t* length, plist_write_options_t options) +{ + uint64_t size = 0; + plist_err_t res; + + if (!plist || !output || !length) { + return PLIST_ERR_INVALID_ARG; + } + + res = node_estimate_size((node_t)plist, &size, 0); + if (res < 0) { + return res; + } + + strbuf_t *outbuf = str_buf_new(size); + if (!outbuf) { +#if DEBUG + fprintf(stderr, "%s: Could not allocate output buffer\n", __func__); +#endif + return PLIST_ERR_NO_MEM; + } + + res = _plist_write_to_strbuf(plist, outbuf, options); + if (res < 0) { + str_buf_free(outbuf); + *output = NULL; + *length = 0; + return res; + } + str_buf_append(outbuf, "\0", 1); + + *output = (char*)outbuf->data; + *length = outbuf->len - 1; + + outbuf->data = NULL; + str_buf_free(outbuf); + + return PLIST_ERR_SUCCESS; +} + +plist_err_t plist_write_to_stream_plutil(plist_t plist, FILE *stream, plist_write_options_t options) +{ + if (!plist || !stream) { + return PLIST_ERR_INVALID_ARG; + } + strbuf_t *outbuf = str_buf_new_for_stream(stream); + if (!outbuf) { +#if DEBUG + fprintf(stderr, "%s: Could not allocate output buffer\n", __func__); +#endif + return PLIST_ERR_NO_MEM; + } + + plist_err_t res = _plist_write_to_strbuf(plist, outbuf, options); + if (res < 0) { + str_buf_free(outbuf); + return res; + } + + str_buf_free(outbuf); + + return PLIST_ERR_SUCCESS; +} diff --git a/src/plist.c b/src/plist.c index 27f90b1..d1b0b5a 100644 --- a/src/plist.c +++ b/src/plist.c @@ -2,7 +2,7 @@ * plist.c * Builds plist XML structures * - * Copyright (c) 2009-2019 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2009-2023 Nikias Bassen, All Rights Reserved. * Copyright (c) 2010-2015 Martin Szulecki, All Rights Reserved. * Copyright (c) 2008 Zach C., All Rights Reserved. * @@ -34,6 +34,8 @@ #include <assert.h> #include <limits.h> #include <float.h> +#include <ctype.h> +#include <inttypes.h> #ifdef WIN32 #include <windows.h> @@ -42,28 +44,93 @@ #endif #include <node.h> +#include <node_list.h> #include <hashtable.h> #include <ptrarray.h> +#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 + extern void plist_xml_init(void); extern void plist_xml_deinit(void); extern void plist_bin_init(void); extern void plist_bin_deinit(void); +extern void plist_json_init(void); +extern void plist_json_deinit(void); +extern void plist_ostep_init(void); +extern void plist_ostep_deinit(void); + +#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 __LITTLE_ENDIAN__ +#define le16toh(x) (x) +#else +#define le16toh(x) bswap16(x) +#endif +#endif + +#ifndef le32toh +#ifdef __LITTLE_ENDIAN__ +#define le32toh(x) (x) +#else +#define le32toh(x) bswap32(x) +#endif +#endif + +#ifndef le64toh +#ifdef __LITTLE_ENDIAN__ +#define le64toh(x) (x) +#else +#define le64toh(x) bswap64(x) +#endif +#endif 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(); plist_xml_deinit(); + plist_json_deinit(); + plist_ostep_deinit(); } #ifdef WIN32 - typedef volatile struct { LONG lock; int state; @@ -72,7 +139,7 @@ typedef volatile struct { static thread_once_t init_once = {0, 0}; static thread_once_t deinit_once = {0, 0}; -void thread_once(thread_once_t *once_control, void (*init_routine)(void)) +static void thread_once(thread_once_t *once_control, void (*init_routine)(void)) { while (InterlockedExchange(&(once_control->lock), 1) != 0) { Sleep(1); @@ -83,7 +150,29 @@ void thread_once(thread_once_t *once_control, void (*init_routine)(void)) } 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) { @@ -98,22 +187,8 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) } return 1; } - #else - -static pthread_once_t init_once = PTHREAD_ONCE_INIT; -static pthread_once_t deinit_once = PTHREAD_ONCE_INIT; - -static void __attribute__((constructor)) libplist_initialize(void) -{ - pthread_once(&init_once, internal_plist_init); -} - -static void __attribute__((destructor)) libplist_deinitialize(void) -{ - pthread_once(&deinit_once, internal_plist_deinit); -} - +#warning No compiler support for constructor/destructor attributes, some features might not be available. #endif #ifndef HAVE_MEMMEM @@ -166,7 +241,7 @@ void* memmem(const void* haystack, size_t haystack_len, const void* needle, size } #endif -PLIST_API int plist_is_binary(const char *plist_data, uint32_t length) +int plist_is_binary(const char *plist_data, uint32_t length) { if (length < 8) { return 0; @@ -175,19 +250,131 @@ PLIST_API int plist_is_binary(const char *plist_data, uint32_t length) return (memcmp(plist_data, "bplist00", 8) == 0); } +#define SKIP_WS(blob, pos, len) \ + while (pos < len && ((blob[pos] == ' ') || (blob[pos] == '\t') || (blob[pos] == '\r') || (blob[pos] == '\n'))) pos++; +#define FIND_NEXT(blob, pos, len, chr) \ + while (pos < len && (blob[pos] != chr)) pos++; -PLIST_API void plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist) +plist_err_t plist_from_memory(const char *plist_data, uint32_t length, plist_t *plist, plist_format_t *format) { - if (length < 8) { - *plist = NULL; - return; + plist_err_t res = PLIST_ERR_UNKNOWN; + if (!plist) { + return PLIST_ERR_INVALID_ARG; } - + *plist = NULL; + if (!plist_data || length == 0) { + return PLIST_ERR_INVALID_ARG; + } + plist_format_t fmt = PLIST_FORMAT_NONE; + if (format) *format = PLIST_FORMAT_NONE; if (plist_is_binary(plist_data, length)) { - plist_from_bin(plist_data, length, plist); + res = plist_from_bin(plist_data, length, plist); + fmt = PLIST_FORMAT_BINARY; } else { - plist_from_xml(plist_data, length, plist); + uint32_t pos = 0; + int is_json = 0; + int is_xml = 0; + /* skip whitespace */ + SKIP_WS(plist_data, pos, length); + if (pos >= length) { + return PLIST_ERR_PARSE; + } + if (plist_data[pos] == '<' && (length-pos > 3) && !isxdigit(plist_data[pos+1]) && !isxdigit(plist_data[pos+2]) && !isxdigit(plist_data[pos+3])) { + is_xml = 1; + } else if (plist_data[pos] == '[') { + /* only valid for json */ + is_json = 1; + } else if (plist_data[pos] == '(') { + /* only valid for openstep */ + } else if (plist_data[pos] == '{') { + /* this could be json or openstep */ + pos++; + SKIP_WS(plist_data, pos, length); + if (pos >= length) { + return PLIST_ERR_PARSE; + } + if (plist_data[pos] == '"') { + /* still could be both */ + pos++; + while (pos < length) { + FIND_NEXT(plist_data, pos, length, '"'); + if (plist_data[pos-1] != '\\') { + break; + } + pos++; + } + if (pos >= length) { + return PLIST_ERR_PARSE; + } + if (plist_data[pos] == '"') { + pos++; + SKIP_WS(plist_data, pos, length); + if (pos >= length) { + return PLIST_ERR_PARSE; + } + if (plist_data[pos] == ':') { + /* this is definitely json */ + is_json = 1; + } + } + } + } + if (is_xml) { + res = plist_from_xml(plist_data, length, plist); + fmt = PLIST_FORMAT_XML; + } else if (is_json) { + res = plist_from_json(plist_data, length, plist); + fmt = PLIST_FORMAT_JSON; + } else { + res = plist_from_openstep(plist_data, length, plist); + fmt = PLIST_FORMAT_OSTEP; + } } + if (format && res == PLIST_ERR_SUCCESS) { + *format = fmt; + } + return res; +} + +plist_err_t plist_read_from_file(const char *filename, plist_t *plist, plist_format_t *format) +{ + if (!filename || !plist) { + return PLIST_ERR_INVALID_ARG; + } + FILE *f = fopen(filename, "rb"); + if (!f) { + return PLIST_ERR_IO; + } + struct stat fst; + fstat(fileno(f), &fst); + if ((uint64_t)fst.st_size > UINT32_MAX) { + return PLIST_ERR_NO_MEM; + } + uint32_t total = (uint32_t)fst.st_size; + if (total == 0) { + return PLIST_ERR_PARSE; + } + char *buf = (char*)malloc(total); + if (!buf) { + fclose(f); + return PLIST_ERR_NO_MEM; + } + uint32_t done = 0; + while (done < total) { + ssize_t r = fread(buf + done, 1, total - done, f); + if (r <= 0) { + break; + } + done += r; + } + fclose(f); + if (done < total) { + free(buf); + return PLIST_ERR_IO; + } + plist_err_t res = plist_from_memory(buf, total, plist, format); + free(buf); + return res; } plist_t plist_new_node(plist_data_t data) @@ -195,11 +382,11 @@ plist_t plist_new_node(plist_data_t data) return (plist_t) node_create(NULL, data); } -plist_data_t plist_get_data(const plist_t node) +plist_data_t plist_get_data(plist_t node) { if (!node) return NULL; - return ((node_t*)node)->data; + return (plist_data_t)((node_t)node)->data; } plist_data_t plist_new_plist_data(void) @@ -247,10 +434,10 @@ void plist_free_data(plist_data_t data) free(data->buff); break; case PLIST_ARRAY: - ptr_array_free(data->hashtable); + ptr_array_free((ptrarray_t*)data->hashtable); break; case PLIST_DICT: - hash_table_destroy(data->hashtable); + hash_table_destroy((hashtable_t*)data->hashtable); break; default: break; @@ -259,7 +446,7 @@ void plist_free_data(plist_data_t data) } } -static int plist_free_node(node_t* node) +static int plist_free_node(node_t node) { plist_data_t data = NULL; int node_index = node_detach(node->parent, node); @@ -267,9 +454,9 @@ static int plist_free_node(node_t* node) plist_free_data(data); node->data = NULL; - node_t *ch; + node_t ch; for (ch = node_first_child(node); ch; ) { - node_t *next = node_next_sibling(ch); + node_t next = node_next_sibling(ch); plist_free_node(ch); ch = next; } @@ -279,14 +466,14 @@ static int plist_free_node(node_t* node) return node_index; } -PLIST_API plist_t plist_new_dict(void) +plist_t plist_new_dict(void) { plist_data_t data = plist_new_plist_data(); data->type = PLIST_DICT; return plist_new_node(data); } -PLIST_API plist_t plist_new_array(void) +plist_t plist_new_array(void) { plist_data_t data = plist_new_plist_data(); data->type = PLIST_ARRAY; @@ -303,7 +490,7 @@ static plist_t plist_new_key(const char *val) return plist_new_node(data); } -PLIST_API plist_t plist_new_string(const char *val) +plist_t plist_new_string(const char *val) { plist_data_t data = plist_new_plist_data(); data->type = PLIST_STRING; @@ -312,7 +499,7 @@ PLIST_API plist_t plist_new_string(const char *val) return plist_new_node(data); } -PLIST_API plist_t plist_new_bool(uint8_t val) +plist_t plist_new_bool(uint8_t val) { plist_data_t data = plist_new_plist_data(); data->type = PLIST_BOOLEAN; @@ -321,16 +508,25 @@ PLIST_API plist_t plist_new_bool(uint8_t val) return plist_new_node(data); } -PLIST_API plist_t plist_new_uint(uint64_t val) +plist_t plist_new_uint(uint64_t val) +{ + plist_data_t data = plist_new_plist_data(); + data->type = PLIST_INT; + data->intval = val; + data->length = (val > INT_MAX) ? sizeof(uint64_t)*2 : sizeof(uint64_t); + return plist_new_node(data); +} + +plist_t plist_new_int(int64_t val) { plist_data_t data = plist_new_plist_data(); - data->type = PLIST_UINT; + data->type = PLIST_INT; data->intval = val; data->length = sizeof(uint64_t); return plist_new_node(data); } -PLIST_API plist_t plist_new_uid(uint64_t val) +plist_t plist_new_uid(uint64_t val) { plist_data_t data = plist_new_plist_data(); data->type = PLIST_UID; @@ -339,7 +535,7 @@ PLIST_API plist_t plist_new_uid(uint64_t val) return plist_new_node(data); } -PLIST_API plist_t plist_new_real(double val) +plist_t plist_new_real(double val) { plist_data_t data = plist_new_plist_data(); data->type = PLIST_REAL; @@ -348,7 +544,7 @@ PLIST_API plist_t plist_new_real(double val) return plist_new_node(data); } -PLIST_API plist_t plist_new_data(const char *val, uint64_t length) +plist_t plist_new_data(const uint8_t *val, uint64_t length) { plist_data_t data = plist_new_plist_data(); data->type = PLIST_DATA; @@ -358,7 +554,7 @@ PLIST_API plist_t plist_new_data(const char *val, uint64_t length) return plist_new_node(data); } -PLIST_API plist_t plist_new_date(int32_t sec, int32_t usec) +plist_t plist_new_date(int32_t sec, int32_t usec) { plist_data_t data = plist_new_plist_data(); data->type = PLIST_DATE; @@ -367,15 +563,32 @@ PLIST_API plist_t plist_new_date(int32_t sec, int32_t usec) return plist_new_node(data); } -PLIST_API void plist_free(plist_t plist) +plist_t plist_new_null(void) +{ + plist_data_t data = plist_new_plist_data(); + data->type = PLIST_NULL; + data->intval = 0; + data->length = 0; + return plist_new_node(data); +} + +void plist_free(plist_t plist) { if (plist) { - plist_free_node(plist); + plist_free_node((node_t)plist); } } -static plist_t plist_copy_node(node_t *node) +void plist_mem_free(void* ptr) +{ + if (ptr) + { + free(ptr); + } +} + +static plist_t plist_copy_node(node_t node) { plist_type node_type = PLIST_NONE; plist_t newnode = NULL; @@ -395,7 +608,7 @@ static plist_t plist_copy_node(node_t *node) break; case PLIST_KEY: case PLIST_STRING: - newdata->strval = strdup((char *) data->strval); + newdata->strval = strdup(data->strval); break; case PLIST_ARRAY: if (data->hashtable) { @@ -416,13 +629,13 @@ static plist_t plist_copy_node(node_t *node) } newnode = plist_new_node(newdata); - node_t *ch; + node_t ch; unsigned int node_index = 0; for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { /* copy child node */ plist_t newch = plist_copy_node(ch); /* attach to new parent node */ - node_attach(newnode, newch); + node_attach((node_t)newnode, (node_t)newch); /* if needed, add child node to lookup table of parent node */ switch (node_type) { case PLIST_ARRAY: @@ -432,7 +645,7 @@ static plist_t plist_copy_node(node_t *node) break; case PLIST_DICT: if (newdata->hashtable && (node_index % 2 != 0)) { - hash_table_insert((hashtable_t*)newdata->hashtable, (node_prev_sibling((node_t*)newch))->data, newch); + hash_table_insert((hashtable_t*)newdata->hashtable, (node_prev_sibling((node_t)newch))->data, newch); } break; default: @@ -443,136 +656,140 @@ static plist_t plist_copy_node(node_t *node) return newnode; } -PLIST_API plist_t plist_copy(plist_t node) +plist_t plist_copy(plist_t node) { - return node ? plist_copy_node(node) : NULL; + return node ? plist_copy_node((node_t)node) : NULL; } -PLIST_API uint32_t plist_array_get_size(plist_t node) +uint32_t plist_array_get_size(plist_t node) { uint32_t ret = 0; if (node && PLIST_ARRAY == plist_get_node_type(node)) { - ret = node_n_children(node); + ret = node_n_children((node_t)node); } return ret; } -PLIST_API plist_t plist_array_get_item(plist_t node, uint32_t n) +plist_t plist_array_get_item(plist_t node, uint32_t n) { plist_t ret = NULL; if (node && PLIST_ARRAY == plist_get_node_type(node) && n < INT_MAX) { - ptrarray_t *pa = ((plist_data_t)((node_t*)node)->data)->hashtable; + ptrarray_t *pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable; if (pa) { ret = (plist_t)ptr_array_index(pa, n); } else { - ret = (plist_t)node_nth_child(node, n); + ret = (plist_t)node_nth_child((node_t)node, n); } } return ret; } -PLIST_API uint32_t plist_array_get_item_index(plist_t node) +uint32_t plist_array_get_item_index(plist_t node) { plist_t father = plist_get_parent(node); if (PLIST_ARRAY == plist_get_node_type(father)) { - return node_child_position(father, node); + return node_child_position((node_t)father, (node_t)node); } return UINT_MAX; } static void _plist_array_post_insert(plist_t node, plist_t item, long n) { - ptrarray_t *pa = ((plist_data_t)((node_t*)node)->data)->hashtable; + ptrarray_t *pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable; if (pa) { /* store pointer to item in array */ ptr_array_insert(pa, item, n); } else { - if (((node_t*)node)->count > 100) { + if (((node_t)node)->count > 100) { /* make new lookup array */ pa = ptr_array_new(128); plist_t current = NULL; - for (current = (plist_t)node_first_child(node); + for (current = (plist_t)node_first_child((node_t)node); pa && current; - current = (plist_t)node_next_sibling(current)) + current = (plist_t)node_next_sibling((node_t)current)) { ptr_array_add(pa, current); } - ((plist_data_t)((node_t*)node)->data)->hashtable = pa; + ((plist_data_t)((node_t)node)->data)->hashtable = pa; } } } -PLIST_API void plist_array_set_item(plist_t node, plist_t item, uint32_t n) +void plist_array_set_item(plist_t node, plist_t item, uint32_t n) { + if (!item) { + return; + } if (node && PLIST_ARRAY == plist_get_node_type(node) && n < INT_MAX) { plist_t old_item = plist_array_get_item(node, n); if (old_item) { - int idx = plist_free_node(old_item); + int idx = plist_free_node((node_t)old_item); assert(idx >= 0); if (idx < 0) { return; - } else { - node_insert(node, idx, item); - ptrarray_t* pa = ((plist_data_t)((node_t*)node)->data)->hashtable; - if (pa) { - ptr_array_set(pa, item, idx); - } + } + node_insert((node_t)node, idx, (node_t)item); + ptrarray_t* pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable; + if (pa) { + ptr_array_set(pa, item, idx); } } } - return; } -PLIST_API void plist_array_append_item(plist_t node, plist_t item) +void plist_array_append_item(plist_t node, plist_t item) { + if (!item) { + return; + } if (node && PLIST_ARRAY == plist_get_node_type(node)) { - node_attach(node, item); + node_attach((node_t)node, (node_t)item); _plist_array_post_insert(node, item, -1); } - return; } -PLIST_API void plist_array_insert_item(plist_t node, plist_t item, uint32_t n) +void plist_array_insert_item(plist_t node, plist_t item, uint32_t n) { + if (!item) { + return; + } if (node && PLIST_ARRAY == plist_get_node_type(node) && n < INT_MAX) { - node_insert(node, n, item); + node_insert((node_t)node, n, (node_t)item); _plist_array_post_insert(node, item, (long)n); } - return; } -PLIST_API void plist_array_remove_item(plist_t node, uint32_t n) +void plist_array_remove_item(plist_t node, uint32_t n) { if (node && PLIST_ARRAY == plist_get_node_type(node) && n < INT_MAX) { plist_t old_item = plist_array_get_item(node, n); if (old_item) { - ptrarray_t* pa = ((plist_data_t)((node_t*)node)->data)->hashtable; + ptrarray_t* pa = (ptrarray_t*)((plist_data_t)((node_t)node)->data)->hashtable; if (pa) { ptr_array_remove(pa, n); } plist_free(old_item); } } - return; } -PLIST_API void plist_array_item_remove(plist_t node) +void plist_array_item_remove(plist_t node) { plist_t father = plist_get_parent(node); if (PLIST_ARRAY == plist_get_node_type(father)) { - int n = node_child_position(father, node); + int n = node_child_position((node_t)father, (node_t)node); if (n < 0) return; - ptrarray_t* pa = ((plist_data_t)((node_t*)father)->data)->hashtable; + ptrarray_t* pa = (ptrarray_t*)((plist_data_t)((node_t)father)->data)->hashtable; if (pa) { ptr_array_remove(pa, n); } @@ -580,19 +797,18 @@ PLIST_API void plist_array_item_remove(plist_t node) } } -PLIST_API void plist_array_new_iter(plist_t node, plist_array_iter *iter) +void plist_array_new_iter(plist_t node, plist_array_iter *iter) { if (iter) { - *iter = malloc(sizeof(node_t*)); - *((node_t**)(*iter)) = node_first_child(node); + *iter = malloc(sizeof(node_t)); + *((node_t*)(*iter)) = node_first_child((node_t)node); } - return; } -PLIST_API void plist_array_next_item(plist_t node, plist_array_iter iter, plist_t *item) +void plist_array_next_item(plist_t node, plist_array_iter iter, plist_t *item) { - node_t** iter_node = (node_t**)iter; + node_t* iter_node = (node_t*)iter; if (item) { @@ -607,32 +823,30 @@ PLIST_API void plist_array_next_item(plist_t node, plist_array_iter iter, plist_ } *iter_node = node_next_sibling(*iter_node); } - return; } -PLIST_API uint32_t plist_dict_get_size(plist_t node) +uint32_t plist_dict_get_size(plist_t node) { uint32_t ret = 0; if (node && PLIST_DICT == plist_get_node_type(node)) { - ret = node_n_children(node) / 2; + ret = node_n_children((node_t)node) / 2; } return ret; } -PLIST_API void plist_dict_new_iter(plist_t node, plist_dict_iter *iter) +void plist_dict_new_iter(plist_t node, plist_dict_iter *iter) { if (iter) { - *iter = malloc(sizeof(node_t*)); - *((node_t**)(*iter)) = node_first_child(node); + *iter = malloc(sizeof(node_t)); + *((node_t*)(*iter)) = node_first_child((node_t)node); } - return; } -PLIST_API void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **key, plist_t *val) +void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **key, plist_t *val) { - node_t** iter_node = (node_t**)iter; + node_t* iter_node = (node_t*)iter; if (key) { @@ -656,30 +870,29 @@ PLIST_API void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **k } *iter_node = node_next_sibling(*iter_node); } - return; } -PLIST_API void plist_dict_get_item_key(plist_t node, char **key) +void plist_dict_get_item_key(plist_t node, char **key) { plist_t father = plist_get_parent(node); if (PLIST_DICT == plist_get_node_type(father)) { - plist_get_key_val( (plist_t) node_prev_sibling(node), key); + plist_get_key_val( (plist_t) node_prev_sibling((node_t)node), key); } } -PLIST_API plist_t plist_dict_item_get_key(plist_t node) +plist_t plist_dict_item_get_key(plist_t node) { plist_t ret = NULL; plist_t father = plist_get_parent(node); if (PLIST_DICT == plist_get_node_type(father)) { - ret = (plist_t)node_prev_sibling(node); + ret = (plist_t)node_prev_sibling((node_t)node); } return ret; } -PLIST_API plist_t plist_dict_get_item(plist_t node, const char* key) +plist_t plist_dict_get_item(plist_t node, const char* key) { plist_t ret = NULL; @@ -694,16 +907,16 @@ PLIST_API plist_t plist_dict_get_item(plist_t node, const char* key) ret = (plist_t)hash_table_lookup(ht, &sdata); } else { plist_t current = NULL; - for (current = (plist_t)node_first_child(node); + for (current = (plist_t)node_first_child((node_t)node); current; - current = (plist_t)node_next_sibling(node_next_sibling(current))) + current = (plist_t)node_next_sibling(node_next_sibling((node_t)current))) { data = plist_get_data(current); assert( PLIST_KEY == plist_get_node_type(current) ); if (data && !strcmp(key, data->strval)) { - ret = (plist_t)node_next_sibling(current); + ret = (plist_t)node_next_sibling((node_t)current); break; } } @@ -712,74 +925,69 @@ PLIST_API plist_t plist_dict_get_item(plist_t node, const char* key) return ret; } -PLIST_API void plist_dict_set_item(plist_t node, const char* key, plist_t item) +void plist_dict_set_item(plist_t node, const char* key, plist_t item) { + if (!item) { + return; + } if (node && PLIST_DICT == plist_get_node_type(node)) { - node_t* old_item = plist_dict_get_item(node, key); + plist_t old_item = plist_dict_get_item(node, key); plist_t key_node = NULL; if (old_item) { - int idx = plist_free_node(old_item); + int idx = plist_free_node((node_t)old_item); assert(idx >= 0); if (idx < 0) { return; - } else { - node_insert(node, idx, item); } - key_node = node_prev_sibling(item); + node_insert((node_t)node, idx, (node_t)item); + key_node = node_prev_sibling((node_t)item); } else { key_node = plist_new_key(key); - node_attach(node, key_node); - node_attach(node, item); + node_attach((node_t)node, (node_t)key_node); + node_attach((node_t)node, (node_t)item); } - hashtable_t *ht = ((plist_data_t)((node_t*)node)->data)->hashtable; + hashtable_t *ht = (hashtable_t*)((plist_data_t)((node_t)node)->data)->hashtable; if (ht) { /* store pointer to item in hash table */ - hash_table_insert(ht, (plist_data_t)((node_t*)key_node)->data, item); + hash_table_insert(ht, (plist_data_t)((node_t)key_node)->data, item); } else { - if (((node_t*)node)->count > 500) { + if (((node_t)node)->count > 500) { /* make new hash table */ ht = hash_table_new(dict_key_hash, dict_key_compare, NULL); /* calculate the hashes for all entries we have so far */ plist_t current = NULL; - for (current = (plist_t)node_first_child(node); + for (current = (plist_t)node_first_child((node_t)node); ht && current; - current = (plist_t)node_next_sibling(node_next_sibling(current))) + current = (plist_t)node_next_sibling(node_next_sibling((node_t)current))) { - hash_table_insert(ht, ((node_t*)current)->data, node_next_sibling(current)); + hash_table_insert(ht, ((node_t)current)->data, node_next_sibling((node_t)current)); } - ((plist_data_t)((node_t*)node)->data)->hashtable = ht; + ((plist_data_t)((node_t)node)->data)->hashtable = ht; } } } - return; -} - -PLIST_API void plist_dict_insert_item(plist_t node, const char* key, plist_t item) -{ - plist_dict_set_item(node, key, item); } -PLIST_API void plist_dict_remove_item(plist_t node, const char* key) +void plist_dict_remove_item(plist_t node, const char* key) { if (node && PLIST_DICT == plist_get_node_type(node)) { plist_t old_item = plist_dict_get_item(node, key); if (old_item) { - plist_t key_node = node_prev_sibling(old_item); - hashtable_t* ht = ((plist_data_t)((node_t*)node)->data)->hashtable; + plist_t key_node = node_prev_sibling((node_t)old_item); + hashtable_t* ht = (hashtable_t*)((plist_data_t)((node_t)node)->data)->hashtable; if (ht) { - hash_table_remove(ht, ((node_t*)key_node)->data); + hash_table_remove(ht, ((node_t)key_node)->data); } plist_free(key_node); plist_free(old_item); } } - return; } -PLIST_API void plist_dict_merge(plist_t *target, plist_t source) +void plist_dict_merge(plist_t *target, plist_t source) { if (!target || !*target || (plist_get_node_type(*target) != PLIST_DICT) || !source || (plist_get_node_type(source) != PLIST_DICT)) return; @@ -800,10 +1008,199 @@ PLIST_API void plist_dict_merge(plist_t *target, plist_t source) free(key); key = NULL; } while (1); - free(it); -} - -PLIST_API plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v) + 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; plist_type type = PLIST_NONE; @@ -827,7 +1224,7 @@ PLIST_API plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v) return current; } -PLIST_API plist_t plist_access_path(plist_t plist, uint32_t length, ...) +plist_t plist_access_path(plist_t plist, uint32_t length, ...) { plist_t ret = NULL; va_list v; @@ -855,7 +1252,7 @@ static void plist_get_type_and_value(plist_t node, plist_type * type, void *valu case PLIST_BOOLEAN: *((char *) value) = data->boolval; break; - case PLIST_UINT: + case PLIST_INT: case PLIST_UID: *((uint64_t *) value) = data->intval; break; @@ -878,12 +1275,12 @@ static void plist_get_type_and_value(plist_t node, plist_type * type, void *valu } } -PLIST_API plist_t plist_get_parent(plist_t node) +plist_t plist_get_parent(plist_t node) { - return node ? (plist_t) ((node_t*) node)->parent : NULL; + return node ? (plist_t) ((node_t) node)->parent : NULL; } -PLIST_API plist_type plist_get_node_type(plist_t node) +plist_type plist_get_node_type(plist_t node) { if (node) { @@ -894,7 +1291,7 @@ PLIST_API plist_type plist_get_node_type(plist_t node) return PLIST_NONE; } -PLIST_API void plist_get_key_val(plist_t node, char **val) +void plist_get_key_val(plist_t node, char **val) { if (!node || !val) return; @@ -908,7 +1305,7 @@ PLIST_API void plist_get_key_val(plist_t node, char **val) assert(length == strlen(*val)); } -PLIST_API void plist_get_string_val(plist_t node, char **val) +void plist_get_string_val(plist_t node, char **val) { if (!node || !val) return; @@ -922,7 +1319,7 @@ PLIST_API void plist_get_string_val(plist_t node, char **val) assert(length == strlen(*val)); } -PLIST_API const char* plist_get_string_ptr(plist_t node, uint64_t* length) +const char* plist_get_string_ptr(plist_t node, uint64_t* length) { if (!node) return NULL; @@ -935,7 +1332,7 @@ PLIST_API const char* plist_get_string_ptr(plist_t node, uint64_t* length) return (const char*)data->strval; } -PLIST_API void plist_get_bool_val(plist_t node, uint8_t * val) +void plist_get_bool_val(plist_t node, uint8_t * val) { if (!node || !val) return; @@ -947,19 +1344,24 @@ PLIST_API void plist_get_bool_val(plist_t node, uint8_t * val) assert(length == sizeof(uint8_t)); } -PLIST_API void plist_get_uint_val(plist_t node, uint64_t * val) +void plist_get_uint_val(plist_t node, uint64_t * val) { if (!node || !val) return; plist_type type = plist_get_node_type(node); uint64_t length = 0; - if (PLIST_UINT != type) + if (PLIST_INT != type) return; plist_get_type_and_value(node, &type, (void *) val, &length); assert(length == sizeof(uint64_t) || length == 16); } -PLIST_API void plist_get_uid_val(plist_t node, uint64_t * val) +void plist_get_int_val(plist_t node, int64_t * val) +{ + plist_get_uint_val(node, (uint64_t*)val); +} + +void plist_get_uid_val(plist_t node, uint64_t * val) { if (!node || !val) return; @@ -971,7 +1373,7 @@ PLIST_API void plist_get_uid_val(plist_t node, uint64_t * val) assert(length == sizeof(uint64_t)); } -PLIST_API void plist_get_real_val(plist_t node, double *val) +void plist_get_real_val(plist_t node, double *val) { if (!node || !val) return; @@ -983,7 +1385,7 @@ PLIST_API void plist_get_real_val(plist_t node, double *val) assert(length == sizeof(double)); } -PLIST_API void plist_get_data_val(plist_t node, char **val, uint64_t * length) +void plist_get_data_val(plist_t node, uint8_t **val, uint64_t * length) { if (!node || !val || !length) return; @@ -993,7 +1395,7 @@ PLIST_API void plist_get_data_val(plist_t node, char **val, uint64_t * length) plist_get_type_and_value(node, &type, (void *) val, length); } -PLIST_API const char* plist_get_data_ptr(plist_t node, uint64_t* length) +const uint8_t* plist_get_data_ptr(plist_t node, uint64_t* length) { if (!node || !length) return NULL; @@ -1002,10 +1404,10 @@ PLIST_API const char* plist_get_data_ptr(plist_t node, uint64_t* length) return NULL; plist_data_t data = plist_get_data(node); *length = data->length; - return (const char*)data->buff; + return data->buff; } -PLIST_API void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec) +void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec) { if (!node) return; @@ -1019,7 +1421,10 @@ PLIST_API void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec) if (sec) *sec = (int32_t)val; if (usec) - *usec = (int32_t)fabs((val - (int64_t)val) * 1000000); + { + val = fabs((val - (int64_t)val) * 1000000); + *usec = (int32_t)val; + } } int plist_data_compare(const void *a, const void *b) @@ -1030,7 +1435,7 @@ int plist_data_compare(const void *a, const void *b) if (!a || !b) return FALSE; - if (!((node_t*) a)->data || !((node_t*) b)->data) + if (!((node_t) a)->data || !((node_t) b)->data) return FALSE; val_a = plist_get_data((plist_t) a); @@ -1042,46 +1447,36 @@ int plist_data_compare(const void *a, const void *b) switch (val_a->type) { case PLIST_BOOLEAN: - case PLIST_UINT: + case PLIST_NULL: + case PLIST_INT: case PLIST_REAL: case PLIST_DATE: case PLIST_UID: if (val_a->length != val_b->length) return FALSE; - if (val_a->intval == val_b->intval) //it is an union so this is sufficient - return TRUE; - else - return FALSE; + return val_a->intval == val_b->intval; //it is an union so this is sufficient case PLIST_KEY: case PLIST_STRING: - if (!strcmp(val_a->strval, val_b->strval)) - return TRUE; - else - return FALSE; + return strcmp(val_a->strval, val_b->strval) == 0; case PLIST_DATA: if (val_a->length != val_b->length) return FALSE; - if (!memcmp(val_a->buff, val_b->buff, val_a->length)) - return TRUE; - else - return FALSE; + return memcmp(val_a->buff, val_b->buff, val_a->length) == 0; + case PLIST_ARRAY: case PLIST_DICT: //compare pointer - if (a == b) - return TRUE; - else - return FALSE; - break; + return a == b; + default: break; } return FALSE; } -PLIST_API char plist_compare_node_value(plist_t node_l, plist_t node_r) +char plist_compare_node_value(plist_t node_l, plist_t node_r) { return plist_data_compare(node_l, node_r); } @@ -1117,7 +1512,7 @@ static void plist_set_element_val(plist_t node, plist_type type, const void *val case PLIST_BOOLEAN: data->boolval = *((char *) value); break; - case PLIST_UINT: + case PLIST_INT: case PLIST_UID: data->intval = *((uint64_t *) value); break; @@ -1140,7 +1535,7 @@ static void plist_set_element_val(plist_t node, plist_type type, const void *val } } -PLIST_API void plist_set_key_val(plist_t node, const char *val) +void plist_set_key_val(plist_t node, const char *val) { plist_t father = plist_get_parent(node); plist_t item = plist_dict_get_item(father, val); @@ -1150,43 +1545,48 @@ PLIST_API void plist_set_key_val(plist_t node, const char *val) plist_set_element_val(node, PLIST_KEY, val, strlen(val)); } -PLIST_API void plist_set_string_val(plist_t node, const char *val) +void plist_set_string_val(plist_t node, const char *val) { plist_set_element_val(node, PLIST_STRING, val, strlen(val)); } -PLIST_API void plist_set_bool_val(plist_t node, uint8_t val) +void plist_set_bool_val(plist_t node, uint8_t val) { plist_set_element_val(node, PLIST_BOOLEAN, &val, sizeof(uint8_t)); } -PLIST_API void plist_set_uint_val(plist_t node, uint64_t val) +void plist_set_uint_val(plist_t node, uint64_t val) { - plist_set_element_val(node, PLIST_UINT, &val, sizeof(uint64_t)); + plist_set_element_val(node, PLIST_INT, &val, (val > INT64_MAX) ? sizeof(uint64_t)*2 : sizeof(uint64_t)); } -PLIST_API void plist_set_uid_val(plist_t node, uint64_t val) +void plist_set_int_val(plist_t node, int64_t val) +{ + plist_set_element_val(node, PLIST_INT, &val, sizeof(uint64_t)); +} + +void plist_set_uid_val(plist_t node, uint64_t val) { plist_set_element_val(node, PLIST_UID, &val, sizeof(uint64_t)); } -PLIST_API void plist_set_real_val(plist_t node, double val) +void plist_set_real_val(plist_t node, double val) { plist_set_element_val(node, PLIST_REAL, &val, sizeof(double)); } -PLIST_API void plist_set_data_val(plist_t node, const char *val, uint64_t length) +void plist_set_data_val(plist_t node, const uint8_t *val, uint64_t length) { plist_set_element_val(node, PLIST_DATA, val, length); } -PLIST_API void plist_set_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) { double val = (double)sec + (double)usec / 1000000; plist_set_element_val(node, PLIST_DATE, &val, sizeof(struct timeval)); } -PLIST_API int plist_bool_val_is_true(plist_t boolnode) +int plist_bool_val_is_true(plist_t boolnode) { if (!PLIST_IS_BOOLEAN(boolnode)) { return 0; @@ -1196,23 +1596,58 @@ PLIST_API int plist_bool_val_is_true(plist_t boolnode) return (bv == 1); } -PLIST_API int plist_uint_val_compare(plist_t uintnode, uint64_t cmpval) +int plist_int_val_is_negative(plist_t intnode) { - if (!PLIST_IS_UINT(uintnode)) { + if (!PLIST_IS_INT(intnode)) { + return 0; + } + plist_data_t data = plist_get_data(intnode); + if (data->length == 16) { + return 0; + } + if ((int64_t)data->intval < 0) { + return 1; + } + return 0; +} + +int plist_int_val_compare(plist_t uintnode, int64_t cmpval) +{ + if (!PLIST_IS_INT(uintnode)) { + return -1; + } + int64_t uintval = 0; + plist_get_int_val(uintnode, &uintval); + if (uintval == cmpval) { + return 0; + } + + if (uintval < cmpval) { + return -1; + } + + return 1; +} + +int plist_uint_val_compare(plist_t uintnode, uint64_t cmpval) +{ + if (!PLIST_IS_INT(uintnode)) { return -1; } uint64_t uintval = 0; plist_get_uint_val(uintnode, &uintval); if (uintval == cmpval) { return 0; - } else if (uintval < cmpval) { + } + + if (uintval < cmpval) { return -1; - } else { - return 1; } + + return 1; } -PLIST_API int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval) +int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval) { if (!PLIST_IS_UID(uidnode)) { return -1; @@ -1221,14 +1656,16 @@ PLIST_API int plist_uid_val_compare(plist_t uidnode, uint64_t cmpval) plist_get_uid_val(uidnode, &uidval); if (uidval == cmpval) { return 0; - } else if (uidval < cmpval) { + } + + if (uidval < cmpval) { return -1; - } else { - return 1; } + + return 1; } -PLIST_API int plist_real_val_compare(plist_t realnode, double cmpval) +int plist_real_val_compare(plist_t realnode, double cmpval) { if (!PLIST_IS_REAL(realnode)) { return -1; @@ -1241,23 +1678,29 @@ PLIST_API int plist_real_val_compare(plist_t realnode, double cmpval) double diff = fabs(a - b); if (a == b) { return 0; - } else if (a == 0 || b == 0 || (abs_a + abs_b < DBL_MIN)) { + } + + if (a == 0 || b == 0 || (abs_a + abs_b < DBL_MIN)) { if (diff < (DBL_EPSILON * DBL_MIN)) { return 0; - } else if (a < b) { + } + + if (a < b) { return -1; } } else { if ((diff / fmin(abs_a + abs_b, DBL_MAX)) < DBL_EPSILON) { return 0; - } else if (a < b) { + } + + if (a < b) { return -1; } } return 1; } -PLIST_API int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec) +int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t cmpusec) { if (!PLIST_IS_DATE(datenode)) { return -1; @@ -1269,14 +1712,16 @@ PLIST_API int plist_date_val_compare(plist_t datenode, int32_t cmpsec, int32_t c uint64_t cmpval = ((int64_t)cmpsec << 32) | cmpusec; if (dateval == cmpval) { return 0; - } else if (dateval < cmpval) { + } + + if (dateval < cmpval) { return -1; - } else { - return 1; } + + return 1; } -PLIST_API int plist_string_val_compare(plist_t strnode, const char* cmpval) +int plist_string_val_compare(plist_t strnode, const char* cmpval) { if (!PLIST_IS_STRING(strnode)) { return -1; @@ -1285,7 +1730,7 @@ PLIST_API int plist_string_val_compare(plist_t strnode, const char* cmpval) return strcmp(data->strval, cmpval); } -PLIST_API int plist_string_val_compare_with_size(plist_t strnode, const char* cmpval, size_t n) +int plist_string_val_compare_with_size(plist_t strnode, const char* cmpval, size_t n) { if (!PLIST_IS_STRING(strnode)) { return -1; @@ -1294,7 +1739,7 @@ PLIST_API int plist_string_val_compare_with_size(plist_t strnode, const char* cm return strncmp(data->strval, cmpval, n); } -PLIST_API int plist_string_val_contains(plist_t strnode, const char* substr) +int plist_string_val_contains(plist_t strnode, const char* substr) { if (!PLIST_IS_STRING(strnode)) { return 0; @@ -1303,7 +1748,7 @@ PLIST_API int plist_string_val_contains(plist_t strnode, const char* substr) return (strstr(data->strval, substr) != NULL); } -PLIST_API int plist_key_val_compare(plist_t keynode, const char* cmpval) +int plist_key_val_compare(plist_t keynode, const char* cmpval) { if (!PLIST_IS_KEY(keynode)) { return -1; @@ -1312,7 +1757,7 @@ PLIST_API int plist_key_val_compare(plist_t keynode, const char* cmpval) return strcmp(data->strval, cmpval); } -PLIST_API int plist_key_val_compare_with_size(plist_t keynode, const char* cmpval, size_t n) +int plist_key_val_compare_with_size(plist_t keynode, const char* cmpval, size_t n) { if (!PLIST_IS_KEY(keynode)) { return -1; @@ -1321,7 +1766,7 @@ PLIST_API int plist_key_val_compare_with_size(plist_t keynode, const char* cmpva return strncmp(data->strval, cmpval, n); } -PLIST_API int plist_key_val_contains(plist_t keynode, const char* substr) +int plist_key_val_contains(plist_t keynode, const char* substr) { if (!PLIST_IS_KEY(keynode)) { return 0; @@ -1330,7 +1775,7 @@ PLIST_API int plist_key_val_contains(plist_t keynode, const char* substr) return (strstr(data->strval, substr) != NULL); } -PLIST_API int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, size_t n) +int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, size_t n) { if (!PLIST_IS_DATA(datanode)) { return -1; @@ -1338,13 +1783,16 @@ PLIST_API int plist_data_val_compare(plist_t datanode, const uint8_t* cmpval, si plist_data_t data = plist_get_data(datanode); if (data->length < n) { return -1; - } else if (data->length > n) { + } + + 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) +int plist_data_val_compare_with_size(plist_t datanode, const uint8_t* cmpval, size_t n) { if (!PLIST_IS_DATA(datanode)) { return -1; @@ -1356,7 +1804,7 @@ PLIST_API int plist_data_val_compare_with_size(plist_t datanode, const uint8_t* return memcmp(data->buff, cmpval, n); } -PLIST_API int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, size_t n) +int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, size_t n) { if (!PLIST_IS_DATA(datanode)) { return -1; @@ -1364,3 +1812,184 @@ PLIST_API int plist_data_val_contains(plist_t datanode, const uint8_t* cmpval, s plist_data_t data = plist_get_data(datanode); return (memmem(data->buff, data->length, cmpval, n) != NULL); } + +extern void plist_xml_set_debug(int debug); +extern void plist_bin_set_debug(int debug); +extern void plist_json_set_debug(int debug); +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); + plist_ostep_set_debug(debug); +} + +void plist_sort(plist_t plist) +{ + if (!plist) { + return; + } + if (PLIST_IS_ARRAY(plist)) { + uint32_t n = plist_array_get_size(plist); + uint32_t i = 0; + for (i = 0; i < n; i++) { + plist_sort(plist_array_get_item(plist, i)); + } + } else if (PLIST_IS_DICT(plist)) { + node_t node = (node_t)plist; + node_t ch; + for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { + ch = node_next_sibling(ch); + plist_sort((plist_t)ch); + } + #define KEY_DATA(x) (x->data) + #define NEXT_KEY(x) (x->next->next) + #define KEY_STRVAL(x) ((plist_data_t)(KEY_DATA(x)))->strval + int swapped = 0; + do { + swapped = 0; + node_t lptr = NULL; + node_t cur_key = node_first_child((node_t)plist); + + while (NEXT_KEY(cur_key) != lptr) { + node_t next_key = NEXT_KEY(cur_key); + if (strcmp(KEY_STRVAL(cur_key), KEY_STRVAL(next_key)) > 0) { + node_t cur_val = cur_key->next; + node_t next_val = next_key->next; + // we need to swap 2 consecutive nodes with the 2 after them + // a -> b -> [c] -> [d] -> [e] -> [f] -> g -> h + // cur next + // swapped: + // a -> b -> [e] -> [f] -> [c] -> [d] -> g -> h + // next cur + node_t tmp_prev = cur_key->prev; + node_t tmp_next = next_val->next; + cur_key->prev = next_val; + cur_val->next = tmp_next; + next_val->next = cur_key; + next_key->prev = tmp_prev; + if (tmp_prev) { + tmp_prev->next = next_key; + } else { + ((node_t)plist)->children->begin = next_key; + } + if (tmp_next) { + tmp_next->prev = cur_val; + } else { + ((node_t)plist)->children->end = cur_val; + } + cur_key = next_key; + swapped = 1; + } + cur_key = NEXT_KEY(cur_key); + } + lptr = cur_key; + } while (swapped); + } +} + +plist_err_t plist_write_to_string(plist_t plist, char **output, uint32_t* length, plist_format_t format, plist_write_options_t options) +{ + plist_err_t err = PLIST_ERR_UNKNOWN; + switch (format) { + case PLIST_FORMAT_XML: + err = plist_to_xml(plist, output, length); + break; + case PLIST_FORMAT_JSON: + err = plist_to_json(plist, output, length, ((options & PLIST_OPT_COMPACT) == 0)); + break; + case PLIST_FORMAT_OSTEP: + err = plist_to_openstep(plist, output, length, ((options & PLIST_OPT_COMPACT) == 0)); + break; + case PLIST_FORMAT_PRINT: + err = plist_write_to_string_default(plist, output, length, options); + break; + case PLIST_FORMAT_LIMD: + err = plist_write_to_string_limd(plist, output, length, options); + break; + case PLIST_FORMAT_PLUTIL: + err = plist_write_to_string_plutil(plist, output, length, options); + break; + default: + // unsupported output format + err = PLIST_ERR_FORMAT; + break; + } + return err; +} + +plist_err_t plist_write_to_stream(plist_t plist, FILE *stream, plist_format_t format, plist_write_options_t options) +{ + if (!plist || !stream) { + return PLIST_ERR_INVALID_ARG; + } + plist_err_t err = PLIST_ERR_UNKNOWN; + char *output = NULL; + uint32_t length = 0; + switch (format) { + case PLIST_FORMAT_BINARY: + err = plist_to_bin(plist, &output, &length); + break; + case PLIST_FORMAT_XML: + err = plist_to_xml(plist, &output, &length); + break; + case PLIST_FORMAT_JSON: + err = plist_to_json(plist, &output, &length, ((options & PLIST_OPT_COMPACT) == 0)); + break; + case PLIST_FORMAT_OSTEP: + err = plist_to_openstep(plist, &output, &length, ((options & PLIST_OPT_COMPACT) == 0)); + break; + case PLIST_FORMAT_PRINT: + err = plist_write_to_stream_default(plist, stream, options); + break; + case PLIST_FORMAT_LIMD: + err = plist_write_to_stream_limd(plist, stream, options); + break; + case PLIST_FORMAT_PLUTIL: + err = plist_write_to_stream_plutil(plist, stream, options); + break; + default: + // unsupported output format + err = PLIST_ERR_FORMAT; + break; + } + if (output && err == PLIST_ERR_SUCCESS) { + if (fwrite(output, 1, length, stream) < length) { + err = PLIST_ERR_IO; + } + free(output); + } + return err; +} + +plist_err_t plist_write_to_file(plist_t plist, const char* filename, plist_format_t format, plist_write_options_t options) +{ + if (!plist || !filename) { + return PLIST_ERR_INVALID_ARG; + } + FILE* f = fopen(filename, "wb"); + if (!f) { + return PLIST_ERR_IO; + } + plist_err_t err = plist_write_to_stream(plist, f, format, options); + fclose(f); + return err; +} + +void plist_print(plist_t plist) +{ + plist_write_to_stream(plist, stdout, PLIST_FORMAT_PRINT, PLIST_OPT_PARTIAL_DATA); +} + +const char* libplist_version() +{ +#ifndef PACKAGE_VERSION +#error PACKAGE_VERSION is not defined! +#endif + return PACKAGE_VERSION; +} diff --git a/src/plist.h b/src/plist.h index 7bf62a8..a993e3a 100644 --- a/src/plist.h +++ b/src/plist.h @@ -26,27 +26,31 @@ #include <config.h> #endif -#include "plist/plist.h" - #include <sys/types.h> #include <sys/stat.h> -#include <sys/time.h> #ifdef _MSC_VER #pragma warning(disable:4996) #pragma warning(disable:4244) +#include <winsock2.h> +#else +#include <sys/time.h> #endif -#ifdef WIN32 +#ifdef LIBPLIST_STATIC + #define PLIST_API +#elif defined(_WIN32) #define PLIST_API __declspec( dllexport ) #else - #ifdef HAVE_FVISIBILITY + #if __GNUC__ >= 4 #define PLIST_API __attribute__((visibility("default"))) #else #define PLIST_API #endif #endif +#include "plist/plist.h" + struct plist_data_s { union @@ -65,10 +69,16 @@ struct plist_data_s typedef struct plist_data_s *plist_data_t; plist_t plist_new_node(plist_data_t data); -plist_data_t plist_get_data(const plist_t node); +plist_data_t plist_get_data(plist_t node); plist_data_t plist_new_plist_data(void); void plist_free_data(plist_data_t data); int plist_data_compare(const void *a, const void *b); +extern plist_err_t plist_write_to_string_default(plist_t plist, char **output, uint32_t* length, plist_write_options_t options); +extern plist_err_t plist_write_to_string_limd(plist_t plist, char **output, uint32_t* length, plist_write_options_t options); +extern plist_err_t plist_write_to_string_plutil(plist_t plist, char **output, uint32_t* length, plist_write_options_t options); +extern plist_err_t plist_write_to_stream_default(plist_t plist, FILE *stream, plist_write_options_t options); +extern plist_err_t plist_write_to_stream_limd(plist_t plist, FILE *stream, plist_write_options_t options); +extern plist_err_t plist_write_to_stream_plutil(plist_t plist, FILE *stream, plist_write_options_t options); #endif diff --git a/src/ptrarray.c b/src/ptrarray.c index c499773..3a11031 100644 --- a/src/ptrarray.c +++ b/src/ptrarray.c @@ -45,7 +45,7 @@ void ptr_array_insert(ptrarray_t *pa, void *data, long array_index) if (!pa || !pa->pdata) return; long remaining = pa->capacity-pa->len; if (remaining == 0) { - pa->pdata = realloc(pa->pdata, sizeof(void*) * (pa->capacity + pa->capacity_step)); + pa->pdata = (void**)realloc(pa->pdata, sizeof(void*) * (pa->capacity + pa->capacity_step)); pa->capacity += pa->capacity_step; } if (array_index < 0 || array_index >= pa->len) { diff --git a/src/strbuf.h b/src/strbuf.h index 0d28edf..2fbfe93 100644 --- a/src/strbuf.h +++ b/src/strbuf.h @@ -27,6 +27,7 @@ typedef struct bytearray_t strbuf_t; #define str_buf_new(__sz) byte_array_new(__sz) +#define str_buf_new_for_stream(__stream) byte_array_new_for_stream(__stream) #define str_buf_free(__ba) byte_array_free(__ba) #define str_buf_grow(__ba, __am) byte_array_grow(__ba, __am) #define str_buf_append(__ba, __str, __len) byte_array_append(__ba, (void*)(__str), __len) diff --git a/src/time64.c b/src/time64.c index f1cffd4..07639df 100644 --- a/src/time64.c +++ b/src/time64.c @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2007-2010 Michael G Schwern @@ -30,7 +30,7 @@ THE SOFTWARE. Programmers who have available to them 64-bit time values as a 'long long' type can use localtime64_r() and gmtime64_r() which correctly -converts the time even on 32-bit systems. Whether you have 64-bit time +converts the time even on 32-bit systems. Whether you have 64-bit time values will depend on the operating system. localtime64_r() is a 64-bit equivalent of localtime_r(). @@ -59,11 +59,11 @@ static const short julian_days_by_month[2][12] = { {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}, }; -static char wday_name[7][4] = { +static const char wday_name[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; -static char mon_name[12][4] = { +static const char mon_name[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; @@ -176,34 +176,28 @@ static int is_exception_century(Year year) static int cmp_date( const struct TM* left, const struct tm* right ) { if( left->tm_year > right->tm_year ) return 1; - else if( left->tm_year < right->tm_year ) + if( left->tm_year < right->tm_year ) return -1; - if( left->tm_mon > right->tm_mon ) return 1; - else if( left->tm_mon < right->tm_mon ) + if( left->tm_mon < right->tm_mon ) return -1; - if( left->tm_mday > right->tm_mday ) return 1; - else if( left->tm_mday < right->tm_mday ) + if( left->tm_mday < right->tm_mday ) return -1; - if( left->tm_hour > right->tm_hour ) return 1; - else if( left->tm_hour < right->tm_hour ) + if( left->tm_hour < right->tm_hour ) return -1; - if( left->tm_min > right->tm_min ) return 1; - else if( left->tm_min < right->tm_min ) + if( left->tm_min < right->tm_min ) return -1; - if( left->tm_sec > right->tm_sec ) return 1; - else if( left->tm_sec < right->tm_sec ) + if( left->tm_sec < right->tm_sec ) return -1; - return 0; } @@ -233,12 +227,7 @@ Time64_T timegm64(const struct TM *date) { Year orig_year = (Year)date->tm_year; int cycles = 0; - if( orig_year > 100 ) { - cycles = (orig_year - 100) / 400; - orig_year -= cycles * 400; - days += (Time64_T)cycles * days_in_gregorian_cycle; - } - else if( orig_year < -300 ) { + if( (orig_year > 100) || (orig_year < -300) ) { cycles = (orig_year - 100) / 400; orig_year -= cycles * 400; days += (Time64_T)cycles * days_in_gregorian_cycle; @@ -293,7 +282,7 @@ static int check_tm(struct TM *tm) assert(tm->tm_wday >= 0); assert(tm->tm_wday <= 6); - + assert(tm->tm_yday >= 0); assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]); @@ -368,7 +357,7 @@ static int safe_year(const Year year) year_cycle += 17; year_cycle %= SOLAR_CYCLE_LENGTH; - if( year_cycle < 0 ) + if( year_cycle < 0 ) year_cycle = SOLAR_CYCLE_LENGTH + year_cycle; assert( year_cycle >= 0 ); @@ -542,7 +531,7 @@ Time64_T mktime64(struct TM *input_date) { /* Correct the user's possibly out of bound input date */ copy_tm_to_TM64(&safe_date, input_date); - timev += seconds_between_years(year, (Year)(safe_date.tm_year + 1900)); + timev += seconds_between_years(year, (Year)(safe_date.tm_year) + 1900); return timev; } @@ -674,7 +663,7 @@ struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p) p->tm_hour = v_tm_hour; p->tm_mon = v_tm_mon; p->tm_wday = v_tm_wday; - + assert(check_tm(p)); return p; @@ -717,7 +706,7 @@ struct TM *localtime64_r (const Time64_T *timev, struct TM *local_tm) ) { TIME64_TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year); - gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900; + gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year) + 1900) - 1900; } safe_time = (time_t)timegm64(&gm_tm); @@ -756,7 +745,7 @@ struct TM *localtime64_r (const Time64_T *timev, struct TM *local_tm) local_tm->tm_year++; } - /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st + /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st in a non-leap xx00. There is one point in the cycle we can't account for which the safe xx00 year is a leap year. So we need to correct for Dec 31st comming out as @@ -766,7 +755,7 @@ struct TM *localtime64_r (const Time64_T *timev, struct TM *local_tm) local_tm->tm_yday--; assert(check_tm(local_tm)); - + return local_tm; } @@ -774,15 +763,15 @@ struct TM *localtime64_r (const Time64_T *timev, struct TM *local_tm) static int valid_tm_wday( const struct TM* date ) { if( 0 <= date->tm_wday && date->tm_wday <= 6 ) return 1; - else - return 0; + + return 0; } static int valid_tm_mon( const struct TM* date ) { if( 0 <= date->tm_mon && date->tm_mon <= 11 ) return 1; - else - return 0; + + return 0; } diff --git a/src/time64.h b/src/time64.h index bf174c6..efdc716 100644 --- a/src/time64.h +++ b/src/time64.h @@ -39,7 +39,7 @@ struct TM64 { #define TM TM64 #else #define TM tm -#endif +#endif /* Declare public functions */ diff --git a/src/xplist.c b/src/xplist.c index a7d52e5..66e1dba 100644 --- a/src/xplist.c +++ b/src/xplist.c @@ -41,7 +41,6 @@ #include <limits.h> #include <node.h> -#include <node_list.h> #include "plist.h" #include "base64.h" @@ -71,7 +70,7 @@ #define MAC_EPOCH 978307200 -#define MAX_DATA_BYTES_PER_LINE(__i) (((76 - (__i << 3)) >> 2) * 3) +#define MAX_DATA_BYTES_PER_LINE(__i) (((76 - ((__i) << 3)) >> 2) * 3) static const char XML_PLIST_PROLOG[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\ <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\ @@ -81,8 +80,10 @@ static const char XML_PLIST_EPILOG[] = "</plist>\n"; #ifdef DEBUG static int plist_xml_debug = 0; #define PLIST_XML_ERR(...) if (plist_xml_debug) { fprintf(stderr, "libplist[xmlparser] ERROR: " __VA_ARGS__); } +#define PLIST_XML_WRITE_ERR(...) if (plist_xml_debug) { fprintf(stderr, "libplist[xmlwriter] ERROR: " __VA_ARGS__); } #else #define PLIST_XML_ERR(...) +#define PLIST_XML_WRITE_ERR(...) #endif void plist_xml_init(void) @@ -101,6 +102,13 @@ void plist_xml_deinit(void) /* deinit XML stuff */ } +void plist_xml_set_debug(int debug) +{ +#if DEBUG + plist_xml_debug = debug; +#endif +} + static size_t dtostr(char *buf, size_t bufsize, double realval) { size_t len = 0; @@ -113,7 +121,7 @@ static size_t dtostr(char *buf, size_t bufsize, double realval) } else { size_t i = 0; len = snprintf(buf, bufsize, "%.*g", 17, realval); - for (i = 0; i < len; i++) { + for (i = 0; buf && i < len; i++) { if (buf[i] == ',') { buf[i] = '.'; break; @@ -125,7 +133,7 @@ static size_t dtostr(char *buf, size_t bufsize, double realval) return len; } -static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) +static plist_err_t node_to_xml(node_t node, bytearray_t **outbuf, uint32_t depth) { plist_data_t node_data = NULL; @@ -139,8 +147,10 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) uint32_t i = 0; - if (!node) - return; + if (!node) { + PLIST_XML_WRITE_ERR("Encountered invalid empty node in property list\n"); + return PLIST_ERR_INVALID_ARG; + } node_data = plist_get_data(node); @@ -158,14 +168,14 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) } break; - case PLIST_UINT: + case PLIST_INT: tag = XPLIST_INT; tag_len = XPLIST_INT_LEN; val = (char*)malloc(64); if (node_data->length == 16) { - val_len = snprintf(val, 64, "%"PRIu64, node_data->intval); + val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); } else { - val_len = snprintf(val, 64, "%"PRIi64, node_data->intval); + val_len = snprintf(val, 64, "%" PRIi64, node_data->intval); } break; @@ -211,8 +221,7 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) struct TM _btime; struct TM *btime = gmtime64_r(&timev, &_btime); if (btime) { - val = (char*)malloc(24); - memset(val, 0, 24); + val = (char*)calloc(1, 24); struct tm _tmcopy; copy_TM64_to_tm(btime, &_tmcopy); val_len = strftime(val, 24, "%Y-%m-%dT%H:%M:%SZ", &_tmcopy); @@ -228,13 +237,16 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) tag_len = XPLIST_DICT_LEN; val = (char*)malloc(64); if (node_data->length == 16) { - val_len = snprintf(val, 64, "%"PRIu64, node_data->intval); + val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); } else { - val_len = snprintf(val, 64, "%"PRIi64, node_data->intval); + val_len = snprintf(val, 64, "%" PRIi64, node_data->intval); } break; + case PLIST_NULL: + PLIST_XML_WRITE_ERR("PLIST_NULL type is not valid for XML format\n"); + return PLIST_ERR_FORMAT; default: - break; + return PLIST_ERR_UNKNOWN; } for (i = 0; i < depth; i++) { @@ -352,9 +364,10 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) if (node_data->type == PLIST_DICT && node->children) { assert((node->children->count % 2) == 0); } - node_t *ch; + node_t ch; for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { - node_to_xml(ch, outbuf, depth+1); + plist_err_t res = node_to_xml(ch, outbuf, depth+1); + if (res < 0) return res; } /* fix indent for structured types */ @@ -370,8 +383,7 @@ static void node_to_xml(node_t* node, bytearray_t **outbuf, uint32_t depth) str_buf_append(*outbuf, ">", 1); } str_buf_append(*outbuf, "\n", 1); - - return; + return PLIST_ERR_SUCCESS; } static void parse_date(const char *strval, struct TM *btime) @@ -403,7 +415,7 @@ static int num_digits_i(int64_t i) int64_t po10; n=1; if (i < 0) { - i = -i; + i = (i == INT64_MIN) ? INT64_MAX : -i; n++; } po10=10; @@ -432,15 +444,15 @@ static int num_digits_u(uint64_t i) return n; } -static void node_estimate_size(node_t *node, uint64_t *size, uint32_t depth) +static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth) { plist_data_t data; if (!node) { - return; + return PLIST_ERR_INVALID_ARG; } data = plist_get_data(node); if (node->children) { - node_t *ch; + node_t ch; for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { node_estimate_size(ch, size, depth + 1); } @@ -473,7 +485,7 @@ static void node_estimate_size(node_t *node, uint64_t *size, uint32_t depth) *size += data->length; *size += (XPLIST_KEY_LEN << 1) + 6; break; - case PLIST_UINT: + case PLIST_INT: if (data->length == 16) { *size += num_digits_u(data->intval); } else { @@ -482,7 +494,7 @@ static void node_estimate_size(node_t *node, uint64_t *size, uint32_t depth) *size += (XPLIST_INT_LEN << 1) + 6; break; case PLIST_REAL: - *size += num_digits_i((int64_t)data->realval) + 7; + *size += dtostr(NULL, 0, data->realval); *size += (XPLIST_REAL_LEN << 1) + 6; break; case PLIST_DATE: @@ -505,37 +517,58 @@ static void node_estimate_size(node_t *node, uint64_t *size, uint32_t depth) *size += 18; /* <key>CF$UID</key> */ *size += (XPLIST_INT_LEN << 1) + 6; break; + case PLIST_NULL: + PLIST_XML_WRITE_ERR("PLIST_NULL type is not valid for XML format\n"); + return PLIST_ERR_FORMAT; default: - break; + PLIST_XML_WRITE_ERR("invalid node type encountered\n"); + return PLIST_ERR_UNKNOWN; } *size += indent; } + return PLIST_ERR_SUCCESS; } -PLIST_API void plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length) +plist_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length) { uint64_t size = 0; - node_estimate_size(plist, &size, 0); + plist_err_t res; + + if (!plist || !plist_xml || !length) { + return PLIST_ERR_INVALID_ARG; + } + + res = node_estimate_size((node_t)plist, &size, 0); + if (res < 0) { + return res; + } size += sizeof(XML_PLIST_PROLOG) + sizeof(XML_PLIST_EPILOG) - 1; strbuf_t *outbuf = str_buf_new(size); + if (!outbuf) { + PLIST_XML_WRITE_ERR("Could not allocate output buffer\n"); + return PLIST_ERR_NO_MEM; + } str_buf_append(outbuf, XML_PLIST_PROLOG, sizeof(XML_PLIST_PROLOG)-1); - node_to_xml(plist, &outbuf, 0); + res = node_to_xml((node_t)plist, &outbuf, 0); + if (res < 0) { + str_buf_free(outbuf); + *plist_xml = NULL; + *length = 0; + return res; + } str_buf_append(outbuf, XML_PLIST_EPILOG, sizeof(XML_PLIST_EPILOG)); - *plist_xml = outbuf->data; + *plist_xml = (char*)outbuf->data; *length = outbuf->len - 1; outbuf->data = NULL; str_buf_free(outbuf); -} -PLIST_API void plist_to_xml_free(char *plist_xml) -{ - free(plist_xml); + return PLIST_ERR_SUCCESS; } struct _parse_ctx { @@ -638,14 +671,14 @@ static void text_parts_free(text_part_t *tp) { while (tp) { text_part_t *tmp = tp; - tp = tp->next; + tp = (text_part_t*)tp->next; free(tmp); } } static text_part_t* text_part_append(text_part_t* parts, const char *begin, size_t length, int is_cdata) { - text_part_t* newpart = malloc(sizeof(text_part_t)); + text_part_t* newpart = (text_part_t*)malloc(sizeof(text_part_t)); assert(newpart); parts->next = text_part_init(newpart, begin, length, is_cdata); return newpart; @@ -751,7 +784,7 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le } } while (1); ctx->pos++; - if (ctx->pos >= ctx->end-tag_len || strncmp(ctx->pos, tag, tag_len)) { + if (ctx->pos >= ctx->end-tag_len || strncmp(ctx->pos, tag, tag_len) != 0) { PLIST_XML_ERR("EOF or end tag mismatch\n"); ctx->err++; return NULL; @@ -897,9 +930,9 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t text_part_t *tmp = tp; while (tp && tp->begin) { total_length += tp->length; - tp = tp->next; + tp = (text_part_t*)tp->next; } - str = malloc(total_length + 1); + str = (char*)malloc(total_length + 1); assert(str); p = str; tp = tmp; @@ -914,7 +947,7 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t } } p += len; - tp = tp->next; + tp = (text_part_t*)tp->next; } *p = '\0'; if (length) { @@ -926,7 +959,7 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t return str; } -static void node_from_xml(parse_ctx ctx, plist_t *plist) +static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) { char *tag = NULL; char *keyname = NULL; @@ -967,7 +1000,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) ctx->err++; goto err_out; } - if (strncmp(ctx->pos, "?>", 2)) { + if (strncmp(ctx->pos, "?>", 2) != 0) { PLIST_XML_ERR("Couldn't find <? tag closing marker\n"); ctx->err++; goto err_out; @@ -979,7 +1012,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) if (((ctx->end - ctx->pos) > 3) && !strncmp(ctx->pos, "!--", 3)) { ctx->pos += 3; find_str(ctx,"-->", 3, 0); - if (ctx->pos > ctx->end-3 || strncmp(ctx->pos, "-->", 3)) { + if (ctx->pos > ctx->end-3 || strncmp(ctx->pos, "-->", 3) != 0) { PLIST_XML_ERR("Couldn't find end of comment\n"); ctx->err++; goto err_out; @@ -1008,7 +1041,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) } if (embedded_dtd) { find_str(ctx, "]>", 2, 1); - if (ctx->pos > ctx->end-2 || strncmp(ctx->pos, "]>", 2)) { + if (ctx->pos > ctx->end-2 || strncmp(ctx->pos, "]>", 2) != 0) { PLIST_XML_ERR("Couldn't find end of DOCTYPE\n"); ctx->err++; goto err_out; @@ -1034,7 +1067,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) goto err_out; } int taglen = ctx->pos - p; - tag = malloc(taglen + 1); + tag = (char*)malloc(taglen + 1); strncpy(tag, p, taglen); tag[taglen] = '\0'; if (*ctx->pos != '>') { @@ -1072,7 +1105,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) goto err_out; } - struct node_path_item *path_item = malloc(sizeof(struct node_path_item)); + struct node_path_item *path_item = (struct node_path_item*)malloc(sizeof(struct node_path_item)); if (!path_item) { PLIST_XML_ERR("out of memory when allocating node path item\n"); ctx->err++; @@ -1100,7 +1133,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) goto err_out; } struct node_path_item *path_item = node_path; - node_path = node_path->prev; + node_path = (struct node_path_item*)node_path->prev; free(path_item); free(tag); @@ -1123,7 +1156,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) text_part_t *tp = get_text_parts(ctx, tag, taglen, 1, &first_part); if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); - text_parts_free(first_part.next); + text_parts_free((text_part_t*)first_part.next); ctx->err++; goto err_out; } @@ -1132,7 +1165,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) char *str_content = text_parts_get_content(tp, 0, NULL, &requires_free); if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); - text_parts_free(first_part.next); + text_parts_free((text_part_t*)first_part.next); ctx->err++; goto err_out; } @@ -1144,7 +1177,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) } str++; } - data->intval = strtoull((char*)str, NULL, 0); + data->intval = strtoull(str, NULL, 0); if (is_negative || (data->intval <= INT64_MAX)) { uint64_t v = data->intval; if (is_negative) { @@ -1161,20 +1194,20 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) } else { is_empty = 1; } - text_parts_free(tp->next); + text_parts_free((text_part_t*)tp->next); } if (is_empty) { data->intval = 0; data->length = 8; } - data->type = PLIST_UINT; + data->type = PLIST_INT; } else if (!strcmp(tag, XPLIST_REAL)) { if (!is_empty) { text_part_t first_part = { NULL, 0, 0, NULL }; text_part_t *tp = get_text_parts(ctx, tag, taglen, 1, &first_part); if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); - text_parts_free(first_part.next); + text_parts_free((text_part_t*)first_part.next); ctx->err++; goto err_out; } @@ -1183,7 +1216,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) char *str_content = text_parts_get_content(tp, 0, NULL, &requires_free); if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); - text_parts_free(first_part.next); + text_parts_free((text_part_t*)first_part.next); ctx->err++; goto err_out; } @@ -1192,7 +1225,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) free(str_content); } } - text_parts_free(tp->next); + text_parts_free((text_part_t*)tp->next); } data->type = PLIST_REAL; data->length = 8; @@ -1218,12 +1251,12 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) size_t length = 0; if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); - text_parts_free(first_part.next); + text_parts_free((text_part_t*)first_part.next); ctx->err++; goto err_out; } str = text_parts_get_content(tp, 1, &length, NULL); - text_parts_free(first_part.next); + text_parts_free((text_part_t*)first_part.next); if (!str) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); ctx->err++; @@ -1251,7 +1284,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) text_part_t *tp = get_text_parts(ctx, tag, taglen, 1, &first_part); if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); - text_parts_free(first_part.next); + text_parts_free((text_part_t*)first_part.next); ctx->err++; goto err_out; } @@ -1260,7 +1293,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) char *str_content = text_parts_get_content(tp, 0, NULL, &requires_free); if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); - text_parts_free(first_part.next); + text_parts_free((text_part_t*)first_part.next); ctx->err++; goto err_out; } @@ -1274,7 +1307,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) free(str_content); } } - text_parts_free(tp->next); + text_parts_free((text_part_t*)tp->next); } data->type = PLIST_DATA; } else if (!strcmp(tag, XPLIST_DATE)) { @@ -1283,7 +1316,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) text_part_t *tp = get_text_parts(ctx, tag, taglen, 1, &first_part); if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); - text_parts_free(first_part.next); + text_parts_free((text_part_t*)first_part.next); ctx->err++; goto err_out; } @@ -1294,7 +1327,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) char *str_content = text_parts_get_content(tp, 0, &length, &requires_free); if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); - text_parts_free(first_part.next); + text_parts_free((text_part_t*)first_part.next); ctx->err++; goto err_out; } @@ -1314,7 +1347,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) free(str_content); } } - text_parts_free(tp->next); + text_parts_free((text_part_t*)tp->next); data->realval = (double)(timev - MAC_EPOCH); } data->length = sizeof(double); @@ -1358,7 +1391,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) } } if (!is_empty && (data->type == PLIST_DICT || data->type == PLIST_ARRAY)) { - struct node_path_item *path_item = malloc(sizeof(struct node_path_item)); + struct node_path_item *path_item = (struct node_path_item*)malloc(sizeof(struct node_path_item)); if (!path_item) { PLIST_XML_ERR("out of memory when allocating node path item\n"); ctx->err++; @@ -1383,10 +1416,10 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) goto err_out; } struct node_path_item *path_item = node_path; - node_path = node_path->prev; + node_path = (struct node_path_item*)node_path->prev; free(path_item); - parent = ((node_t*)parent)->parent; + parent = ((node_t)parent)->parent; if (!parent) { goto err_out; } @@ -1414,24 +1447,41 @@ err_out: /* clean up node_path if required */ while (node_path) { struct node_path_item *path_item = node_path; - node_path = path_item->prev; + node_path = (struct node_path_item*)path_item->prev; free(path_item); } if (ctx->err) { plist_free(*plist); *plist = NULL; + return PLIST_ERR_PARSE; } + + /* check if we have a UID "dict" so we can replace it with a proper UID node */ + if (PLIST_IS_DICT(*plist) && plist_dict_get_size(*plist) == 1) { + plist_t value = plist_dict_get_item(*plist, "CF$UID"); + if (PLIST_IS_UINT(value)) { + uint64_t u64val = 0; + plist_get_uint_val(value, &u64val); + plist_free(*plist); + *plist = plist_new_uid(u64val); + } + } + + return PLIST_ERR_SUCCESS; } -PLIST_API void plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist) +plist_err_t plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist) { + if (!plist) { + return PLIST_ERR_INVALID_ARG; + } + *plist = NULL; if (!plist_xml || (length == 0)) { - *plist = NULL; - return; + return PLIST_ERR_INVALID_ARG; } struct _parse_ctx ctx = { plist_xml, plist_xml + length, 0 }; - node_from_xml(&ctx, plist); + return node_from_xml(&ctx, plist); } |