summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/plist/plist.h1
-rw-r--r--src/bplist.c64
-rw-r--r--src/jplist.c23
-rw-r--r--src/oplist.c23
-rw-r--r--src/out-default.c27
-rw-r--r--src/out-limd.c27
-rw-r--r--src/out-plutil.c27
-rw-r--r--src/plist.h13
-rw-r--r--src/xplist.c26
9 files changed, 203 insertions, 28 deletions
diff --git a/include/plist/plist.h b/include/plist/plist.h
index 41af8ce..5ea30b8 100644
--- a/include/plist/plist.h
+++ b/include/plist/plist.h
@@ -144,6 +144,7 @@ extern "C"
144 PLIST_ERR_PARSE = -3, /**< parsing of the input format failed */ 144 PLIST_ERR_PARSE = -3, /**< parsing of the input format failed */
145 PLIST_ERR_NO_MEM = -4, /**< not enough memory to handle the operation */ 145 PLIST_ERR_NO_MEM = -4, /**< not enough memory to handle the operation */
146 PLIST_ERR_IO = -5, /**< I/O error */ 146 PLIST_ERR_IO = -5, /**< I/O error */
147 PLIST_ERR_CIRCULAR_REF = -6, /**< circular reference detected */
147 PLIST_ERR_UNKNOWN = -255 /**< an unspecified error occurred */ 148 PLIST_ERR_UNKNOWN = -255 /**< an unspecified error occurred */
148 } plist_err_t; 149 } plist_err_t;
149 150
diff --git a/src/bplist.c b/src/bplist.c
index b2d0e7c..ca2a556 100644
--- a/src/bplist.c
+++ b/src/bplist.c
@@ -244,8 +244,10 @@ struct bplist_data {
244#ifdef DEBUG 244#ifdef DEBUG
245static int plist_bin_debug = 0; 245static int plist_bin_debug = 0;
246#define PLIST_BIN_ERR(...) if (plist_bin_debug) { fprintf(stderr, "libplist[binparser] ERROR: " __VA_ARGS__); } 246#define PLIST_BIN_ERR(...) if (plist_bin_debug) { fprintf(stderr, "libplist[binparser] ERROR: " __VA_ARGS__); }
247#define PLIST_BIN_WRITE_ERR(...) if (plist_bin_debug) { fprintf(stderr, "libplist[binwriter] ERROR: " __VA_ARGS__); }
247#else 248#else
248#define PLIST_BIN_ERR(...) 249#define PLIST_BIN_ERR(...)
250#define PLIST_BIN_WRITE_ERR(...)
249#endif 251#endif
250 252
251void plist_bin_init(void) 253void plist_bin_init(void)
@@ -997,35 +999,53 @@ struct serialize_s
997{ 999{
998 ptrarray_t* objects; 1000 ptrarray_t* objects;
999 hashtable_t* ref_table; 1001 hashtable_t* ref_table;
1002 hashtable_t* in_stack;
1000}; 1003};
1001 1004
1002static void serialize_plist(node_t node, void* data) 1005static plist_err_t serialize_plist(node_t node, void* data)
1003{ 1006{
1004 uint64_t *index_val = NULL; 1007 uint64_t *index_val = NULL;
1005 struct serialize_s *ser = (struct serialize_s *) data; 1008 struct serialize_s *ser = (struct serialize_s *) data;
1006 uint64_t current_index = ser->objects->len;
1007 1009
1008 //first check that node is not yet in objects 1010 // circular reference check: is node on current recursion stack?
1011 if (hash_table_lookup(ser->in_stack, node)) {
1012 PLIST_BIN_WRITE_ERR("circular reference detected\n");
1013 return PLIST_ERR_CIRCULAR_REF;
1014 }
1015
1016 // first check that node is not yet in objects
1009 void* val = hash_table_lookup(ser->ref_table, node); 1017 void* val = hash_table_lookup(ser->ref_table, node);
1010 if (val) 1018 if (val) {
1011 { 1019 // data is already in table
1012 //data is already in table 1020 return PLIST_ERR_SUCCESS;
1013 return;
1014 } 1021 }
1015 //insert new ref 1022
1023 // mark as active
1024 hash_table_insert(ser->in_stack, node, (void*)1);
1025
1026 // insert new ref
1016 index_val = (uint64_t *) malloc(sizeof(uint64_t)); 1027 index_val = (uint64_t *) malloc(sizeof(uint64_t));
1017 assert(index_val != NULL); 1028 assert(index_val != NULL);
1018 *index_val = current_index; 1029 *index_val = ser->objects->len;
1019 hash_table_insert(ser->ref_table, node, index_val); 1030 hash_table_insert(ser->ref_table, node, index_val);
1020 1031
1021 //now append current node to object array 1032 // now append current node to object array
1022 ptr_array_add(ser->objects, node); 1033 ptr_array_add(ser->objects, node);
1023 1034
1024 //now recurse on children 1035 // now recurse on children
1025 node_t ch; 1036 node_t ch;
1037 plist_err_t err = PLIST_ERR_SUCCESS;
1026 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { 1038 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
1027 serialize_plist(ch, data); 1039 err = serialize_plist(ch, data);
1040 if (err != PLIST_ERR_SUCCESS) {
1041 break;
1042 }
1028 } 1043 }
1044
1045 // leave recursion stack
1046 hash_table_remove(ser->in_stack, node);
1047
1048 return err;
1029} 1049}
1030 1050
1031#define Log2(x) ((x) == 8 ? 3 : ((x) == 4 ? 2 : ((x) == 2 ? 1 : 0))) 1051#define Log2(x) ((x) == 8 ? 3 : ((x) == 4 ? 2 : ((x) == 2 ? 1 : 0)))
@@ -1251,6 +1271,7 @@ plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length)
1251{ 1271{
1252 ptrarray_t* objects = NULL; 1272 ptrarray_t* objects = NULL;
1253 hashtable_t* ref_table = NULL; 1273 hashtable_t* ref_table = NULL;
1274 hashtable_t* in_stack = NULL;
1254 struct serialize_s ser_s; 1275 struct serialize_s ser_s;
1255 uint8_t offset_size = 0; 1276 uint8_t offset_size = 0;
1256 uint8_t ref_size = 0; 1277 uint8_t ref_size = 0;
@@ -1280,11 +1301,28 @@ plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length)
1280 ptr_array_free(objects); 1301 ptr_array_free(objects);
1281 return PLIST_ERR_NO_MEM; 1302 return PLIST_ERR_NO_MEM;
1282 } 1303 }
1304 //hashtable for circular reference detection
1305 in_stack = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL);
1306 if (!in_stack) {
1307 ptr_array_free(objects);
1308 hash_table_destroy(ref_table);
1309 return PLIST_ERR_NO_MEM;
1310 }
1283 1311
1284 //serialize plist 1312 //serialize plist
1285 ser_s.objects = objects; 1313 ser_s.objects = objects;
1286 ser_s.ref_table = ref_table; 1314 ser_s.ref_table = ref_table;
1287 serialize_plist((node_t)plist, &ser_s); 1315 ser_s.in_stack = in_stack;
1316 plist_err_t err = serialize_plist((node_t)plist, &ser_s);
1317 if (err != PLIST_ERR_SUCCESS) {
1318 ptr_array_free(objects);
1319 hash_table_destroy(ref_table);
1320 hash_table_destroy(in_stack);
1321 return err;
1322 }
1323 //no longer needed
1324 hash_table_destroy(in_stack);
1325 ser_s.in_stack = NULL;
1288 1326
1289 //now stream to output buffer 1327 //now stream to output buffer
1290 offset_size = 0; //unknown yet 1328 offset_size = 0; //unknown yet
diff --git a/src/jplist.c b/src/jplist.c
index 1c7a932..2e53400 100644
--- a/src/jplist.c
+++ b/src/jplist.c
@@ -38,6 +38,7 @@
38#include "plist.h" 38#include "plist.h"
39#include "strbuf.h" 39#include "strbuf.h"
40#include "jsmn.h" 40#include "jsmn.h"
41#include "hashtable.h"
41 42
42#ifdef DEBUG 43#ifdef DEBUG
43static int plist_json_debug = 0; 44static int plist_json_debug = 0;
@@ -315,18 +316,27 @@ static int num_digits_u(uint64_t i)
315 return n; 316 return n;
316} 317}
317 318
318static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify) 319static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, hashtable_t *visited)
319{ 320{
320 plist_data_t data; 321 plist_data_t data;
321 if (!node) { 322 if (!node) {
322 return PLIST_ERR_INVALID_ARG; 323 return PLIST_ERR_INVALID_ARG;
323 } 324 }
325
326 if (hash_table_lookup(visited, node)) {
327 PLIST_JSON_WRITE_ERR("circular reference detected\n");
328 return PLIST_ERR_CIRCULAR_REF;
329 }
330
331 // mark as visited
332 hash_table_insert(visited, node, (void*)1);
333
324 data = plist_get_data(node); 334 data = plist_get_data(node);
325 if (node->children) { 335 if (node->children) {
326 node_t ch; 336 node_t ch;
327 unsigned int n_children = node_n_children(node); 337 unsigned int n_children = node_n_children(node);
328 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { 338 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
329 plist_err_t res = node_estimate_size(ch, size, depth + 1, prettify); 339 plist_err_t res = _node_estimate_size(ch, size, depth + 1, prettify, visited);
330 if (res < 0) { 340 if (res < 0) {
331 return res; 341 return res;
332 } 342 }
@@ -402,6 +412,15 @@ static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t dept
402 return PLIST_ERR_SUCCESS; 412 return PLIST_ERR_SUCCESS;
403} 413}
404 414
415static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify)
416{
417 hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL);
418 if (!visited) return PLIST_ERR_NO_MEM;
419 plist_err_t err = _node_estimate_size(node, size, depth, prettify, visited);
420 hash_table_destroy(visited);
421 return err;
422}
423
405plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, int prettify) 424plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, int prettify)
406{ 425{
407 uint64_t size = 0; 426 uint64_t size = 0;
diff --git a/src/oplist.c b/src/oplist.c
index 277693f..680873c 100644
--- a/src/oplist.c
+++ b/src/oplist.c
@@ -37,6 +37,7 @@
37 37
38#include "plist.h" 38#include "plist.h"
39#include "strbuf.h" 39#include "strbuf.h"
40#include "hashtable.h"
40 41
41#ifdef DEBUG 42#ifdef DEBUG
42static int plist_ostep_debug = 0; 43static int plist_ostep_debug = 0;
@@ -359,18 +360,27 @@ static int num_digits_u(uint64_t i)
359 return n; 360 return n;
360} 361}
361 362
362static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify) 363static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, hashtable_t *visited)
363{ 364{
364 plist_data_t data; 365 plist_data_t data;
365 if (!node) { 366 if (!node) {
366 return PLIST_ERR_INVALID_ARG; 367 return PLIST_ERR_INVALID_ARG;
367 } 368 }
369
370 if (hash_table_lookup(visited, node)) {
371 PLIST_OSTEP_WRITE_ERR("circular reference detected\n");
372 return PLIST_ERR_CIRCULAR_REF;
373 }
374
375 // mark as visited
376 hash_table_insert(visited, node, (void*)1);
377
368 data = plist_get_data(node); 378 data = plist_get_data(node);
369 if (node->children) { 379 if (node->children) {
370 node_t ch; 380 node_t ch;
371 unsigned int n_children = node_n_children(node); 381 unsigned int n_children = node_n_children(node);
372 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { 382 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
373 plist_err_t res = node_estimate_size(ch, size, depth + 1, prettify); 383 plist_err_t res = _node_estimate_size(ch, size, depth + 1, prettify, visited);
374 if (res < 0) { 384 if (res < 0) {
375 return res; 385 return res;
376 } 386 }
@@ -446,6 +456,15 @@ static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t dept
446 return PLIST_ERR_SUCCESS; 456 return PLIST_ERR_SUCCESS;
447} 457}
448 458
459static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify)
460{
461 hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL);
462 if (!visited) return PLIST_ERR_NO_MEM;
463 plist_err_t err = _node_estimate_size(node, size, depth, prettify, visited);
464 hash_table_destroy(visited);
465 return err;
466}
467
449plist_err_t plist_to_openstep(plist_t plist, char **openstep, uint32_t* length, int prettify) 468plist_err_t plist_to_openstep(plist_t plist, char **openstep, uint32_t* length, int prettify)
450{ 469{
451 uint64_t size = 0; 470 uint64_t size = 0;
diff --git a/src/out-default.c b/src/out-default.c
index 266070b..09e64c3 100644
--- a/src/out-default.c
+++ b/src/out-default.c
@@ -38,6 +38,7 @@
38#include "plist.h" 38#include "plist.h"
39#include "strbuf.h" 39#include "strbuf.h"
40#include "time64.h" 40#include "time64.h"
41#include "hashtable.h"
41 42
42#define MAC_EPOCH 978307200 43#define MAC_EPOCH 978307200
43 44
@@ -310,19 +311,30 @@ static int num_digits_u(uint64_t i)
310 return n; 311 return n;
311} 312}
312 313
313static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent, int partial_data) 314static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent, int partial_data, hashtable_t *visited)
314{ 315{
315 plist_data_t data; 316 plist_data_t data;
316 if (!node) { 317 if (!node) {
317 return PLIST_ERR_INVALID_ARG; 318 return PLIST_ERR_INVALID_ARG;
318 } 319 }
320
321 if (hash_table_lookup(visited, node)) {
322#if DEBUG
323 fprintf(stderr, "libplist: ERROR: circular reference detected\n");
324#endif
325 return PLIST_ERR_CIRCULAR_REF;
326 }
327
328 // mark as visited
329 hash_table_insert(visited, node, (void*)1);
330
319 data = plist_get_data(node); 331 data = plist_get_data(node);
320 if (node->children) { 332 if (node->children) {
321 node_t ch; 333 node_t ch;
322 unsigned int n_children = node_n_children(node); 334 unsigned int n_children = node_n_children(node);
323 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { 335 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
324 plist_err_t res = node_estimate_size(ch, size, depth + 1, indent, partial_data); 336 plist_err_t res = _node_estimate_size(ch, size, depth + 1, indent, partial_data, visited);
325 if (res < 0) { 337 if (res != PLIST_ERR_SUCCESS) {
326 return res; 338 return res;
327 } 339 }
328 } 340 }
@@ -401,6 +413,15 @@ static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t dept
401 return PLIST_ERR_SUCCESS; 413 return PLIST_ERR_SUCCESS;
402} 414}
403 415
416static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent, int partial_data)
417{
418 hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL);
419 if (!visited) return PLIST_ERR_NO_MEM;
420 plist_err_t err = _node_estimate_size(node, size, depth, indent, partial_data, visited);
421 hash_table_destroy(visited);
422 return err;
423}
424
404static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options) 425static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options)
405{ 426{
406 uint8_t indent = 0; 427 uint8_t indent = 0;
diff --git a/src/out-limd.c b/src/out-limd.c
index 7d861f8..e281644 100644
--- a/src/out-limd.c
+++ b/src/out-limd.c
@@ -40,6 +40,7 @@
40#include "strbuf.h" 40#include "strbuf.h"
41#include "time64.h" 41#include "time64.h"
42#include "base64.h" 42#include "base64.h"
43#include "hashtable.h"
43 44
44#define MAC_EPOCH 978307200 45#define MAC_EPOCH 978307200
45 46
@@ -278,19 +279,30 @@ static int num_digits_u(uint64_t i)
278 return n; 279 return n;
279} 280}
280 281
281static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent) 282static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent, hashtable_t *visited)
282{ 283{
283 plist_data_t data; 284 plist_data_t data;
284 if (!node) { 285 if (!node) {
285 return PLIST_ERR_INVALID_ARG; 286 return PLIST_ERR_INVALID_ARG;
286 } 287 }
288
289 if (hash_table_lookup(visited, node)) {
290#if DEBUG
291 fprintf(stderr, "libplist: ERROR: circular reference detected\n");
292#endif
293 return PLIST_ERR_CIRCULAR_REF;
294 }
295
296 // mark as visited
297 hash_table_insert(visited, node, (void*)1);
298
287 data = plist_get_data(node); 299 data = plist_get_data(node);
288 if (node->children) { 300 if (node->children) {
289 node_t ch; 301 node_t ch;
290 unsigned int n_children = node_n_children(node); 302 unsigned int n_children = node_n_children(node);
291 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { 303 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
292 plist_err_t res = node_estimate_size(ch, size, depth + 1, indent); 304 plist_err_t res = _node_estimate_size(ch, size, depth + 1, indent, visited);
293 if (res < 0) { 305 if (res != PLIST_ERR_SUCCESS) {
294 return res; 306 return res;
295 } 307 }
296 } 308 }
@@ -359,6 +371,15 @@ static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t dept
359 return PLIST_ERR_SUCCESS; 371 return PLIST_ERR_SUCCESS;
360} 372}
361 373
374static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent)
375{
376 hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL);
377 if (!visited) return PLIST_ERR_NO_MEM;
378 plist_err_t err = _node_estimate_size(node, size, depth, indent, visited);
379 hash_table_destroy(visited);
380 return err;
381}
382
362static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options) 383static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options)
363{ 384{
364 uint8_t indent = 0; 385 uint8_t indent = 0;
diff --git a/src/out-plutil.c b/src/out-plutil.c
index d85f22c..5354aa3 100644
--- a/src/out-plutil.c
+++ b/src/out-plutil.c
@@ -38,6 +38,7 @@
38#include "plist.h" 38#include "plist.h"
39#include "strbuf.h" 39#include "strbuf.h"
40#include "time64.h" 40#include "time64.h"
41#include "hashtable.h"
41 42
42#define MAC_EPOCH 978307200 43#define MAC_EPOCH 978307200
43 44
@@ -304,19 +305,30 @@ static int num_digits_u(uint64_t i)
304 return n; 305 return n;
305} 306}
306 307
307static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth) 308static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, hashtable_t *visited)
308{ 309{
309 plist_data_t data; 310 plist_data_t data;
310 if (!node) { 311 if (!node) {
311 return PLIST_ERR_INVALID_ARG; 312 return PLIST_ERR_INVALID_ARG;
312 } 313 }
314
315 if (hash_table_lookup(visited, node)) {
316#if DEBUG
317 fprintf(stderr, "libplist: ERROR: circular reference detected\n");
318#endif
319 return PLIST_ERR_CIRCULAR_REF;
320 }
321
322 // mark as visited
323 hash_table_insert(visited, node, (void*)1);
324
313 data = plist_get_data(node); 325 data = plist_get_data(node);
314 if (node->children) { 326 if (node->children) {
315 node_t ch; 327 node_t ch;
316 unsigned int n_children = node_n_children(node); 328 unsigned int n_children = node_n_children(node);
317 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { 329 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
318 plist_err_t res = node_estimate_size(ch, size, depth + 1); 330 plist_err_t res = _node_estimate_size(ch, size, depth + 1, visited);
319 if (res < 0) { 331 if (res != PLIST_ERR_SUCCESS) {
320 return res; 332 return res;
321 } 333 }
322 } 334 }
@@ -388,6 +400,15 @@ static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t dept
388 return PLIST_ERR_SUCCESS; 400 return PLIST_ERR_SUCCESS;
389} 401}
390 402
403static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth)
404{
405 hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL);
406 if (!visited) return PLIST_ERR_NO_MEM;
407 plist_err_t err = _node_estimate_size(node, size, depth, visited);
408 hash_table_destroy(visited);
409 return err;
410}
411
391static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options) 412static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options)
392{ 413{
393 plist_err_t res = node_to_string((node_t)plist, &outbuf, 0); 414 plist_err_t res = node_to_string((node_t)plist, &outbuf, 0);
diff --git a/src/plist.h b/src/plist.h
index a993e3a..3043fb7 100644
--- a/src/plist.h
+++ b/src/plist.h
@@ -81,4 +81,17 @@ extern plist_err_t plist_write_to_stream_default(plist_t plist, FILE *stream, pl
81extern plist_err_t plist_write_to_stream_limd(plist_t plist, FILE *stream, plist_write_options_t options); 81extern plist_err_t plist_write_to_stream_limd(plist_t plist, FILE *stream, plist_write_options_t options);
82extern plist_err_t plist_write_to_stream_plutil(plist_t plist, FILE *stream, plist_write_options_t options); 82extern plist_err_t plist_write_to_stream_plutil(plist_t plist, FILE *stream, plist_write_options_t options);
83 83
84static inline unsigned int plist_node_ptr_hash(const void *ptr)
85{
86 uintptr_t h = (uintptr_t)ptr;
87 h ^= (h >> 16);
88 h *= 0x85ebca6b;
89 return (unsigned int)h;
90}
91
92static inline int plist_node_ptr_compare(const void *a, const void *b)
93{
94 return a == b;
95}
96
84#endif 97#endif
diff --git a/src/xplist.c b/src/xplist.c
index dc5213b..c25c6b9 100644
--- a/src/xplist.c
+++ b/src/xplist.c
@@ -46,6 +46,7 @@
46#include "base64.h" 46#include "base64.h"
47#include "strbuf.h" 47#include "strbuf.h"
48#include "time64.h" 48#include "time64.h"
49#include "hashtable.h"
49 50
50#define XPLIST_KEY "key" 51#define XPLIST_KEY "key"
51#define XPLIST_KEY_LEN 3 52#define XPLIST_KEY_LEN 3
@@ -444,17 +445,29 @@ static int num_digits_u(uint64_t i)
444 return n; 445 return n;
445} 446}
446 447
447static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth) 448static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, hashtable_t *visited)
448{ 449{
449 plist_data_t data; 450 plist_data_t data;
450 if (!node) { 451 if (!node) {
451 return PLIST_ERR_INVALID_ARG; 452 return PLIST_ERR_INVALID_ARG;
452 } 453 }
454
455 if (hash_table_lookup(visited, node)) {
456 PLIST_XML_WRITE_ERR("circular reference detected\n");
457 return PLIST_ERR_CIRCULAR_REF;
458 }
459
460 // mark as visited
461 hash_table_insert(visited, node, (void*)1);
462
453 data = plist_get_data(node); 463 data = plist_get_data(node);
454 if (node->children) { 464 if (node->children) {
455 node_t ch; 465 node_t ch;
456 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { 466 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
457 node_estimate_size(ch, size, depth + 1); 467 plist_err_t err = _node_estimate_size(ch, size, depth + 1, visited);
468 if (err != PLIST_ERR_SUCCESS) {
469 return err;
470 }
458 } 471 }
459 switch (data->type) { 472 switch (data->type) {
460 case PLIST_DICT: 473 case PLIST_DICT:
@@ -529,6 +542,15 @@ static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t dept
529 return PLIST_ERR_SUCCESS; 542 return PLIST_ERR_SUCCESS;
530} 543}
531 544
545static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth)
546{
547 hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL);
548 if (!visited) return PLIST_ERR_NO_MEM;
549 plist_err_t err = _node_estimate_size(node, size, depth, visited);
550 hash_table_destroy(visited);
551 return err;
552}
553
532plist_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length) 554plist_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length)
533{ 555{
534 uint64_t size = 0; 556 uint64_t size = 0;