diff options
| -rw-r--r-- | include/plist/plist.h | 1 | ||||
| -rw-r--r-- | src/bplist.c | 64 | ||||
| -rw-r--r-- | src/jplist.c | 23 | ||||
| -rw-r--r-- | src/oplist.c | 23 | ||||
| -rw-r--r-- | src/out-default.c | 27 | ||||
| -rw-r--r-- | src/out-limd.c | 27 | ||||
| -rw-r--r-- | src/out-plutil.c | 27 | ||||
| -rw-r--r-- | src/plist.h | 13 | ||||
| -rw-r--r-- | src/xplist.c | 26 |
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 |
| 245 | static int plist_bin_debug = 0; | 245 | static 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 | ||
| 251 | void plist_bin_init(void) | 253 | void 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 | ||
| 1002 | static void serialize_plist(node_t node, void* data) | 1005 | static 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 |
| 43 | static int plist_json_debug = 0; | 44 | static 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 | ||
| 318 | static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify) | 319 | static 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 | ||
| 415 | static 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 | |||
| 405 | plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, int prettify) | 424 | plist_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 |
| 42 | static int plist_ostep_debug = 0; | 43 | static 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 | ||
| 362 | static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify) | 363 | static 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 | ||
| 459 | static 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 | |||
| 449 | plist_err_t plist_to_openstep(plist_t plist, char **openstep, uint32_t* length, int prettify) | 468 | plist_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 | ||
| 313 | static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent, int partial_data) | 314 | static 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 | ||
| 416 | static 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 | |||
| 404 | static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options) | 425 | static 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 | ||
| 281 | static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, uint32_t indent) | 282 | static 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 | ||
| 374 | static 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 | |||
| 362 | static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options) | 383 | static 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 | ||
| 307 | static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth) | 308 | static 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 | ||
| 403 | static 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 | |||
| 391 | static plist_err_t _plist_write_to_strbuf(plist_t plist, strbuf_t *outbuf, plist_write_options_t options) | 412 | static 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 | |||
| 81 | extern plist_err_t plist_write_to_stream_limd(plist_t plist, FILE *stream, plist_write_options_t options); | 81 | extern plist_err_t plist_write_to_stream_limd(plist_t plist, FILE *stream, plist_write_options_t options); |
| 82 | extern plist_err_t plist_write_to_stream_plutil(plist_t plist, FILE *stream, plist_write_options_t options); | 82 | extern plist_err_t plist_write_to_stream_plutil(plist_t plist, FILE *stream, plist_write_options_t options); |
| 83 | 83 | ||
| 84 | static 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 | |||
| 92 | static 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 | ||
| 447 | static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth) | 448 | static 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 | ||
| 545 | static 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 | |||
| 532 | plist_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length) | 554 | plist_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; |
