3 readbinaryplist.c -- Roger B. Dannenberg, Jun 2008
4 Based on ReadBinaryPList.m by Jens Ayton, 2007
6 Note that this code is intended to read preference files and has an upper
7 bound on file size (currently 100MB) and assumes in some places that 32 bit
8 offsets are sufficient.
10 Here are his comments:
12 Reader for binary property list files (version 00).
14 This has been found to work on all 566 binary plists in my ~/Library/Preferences/
15 and /Library/Preferences/ directories. This probably does not provide full
16 test coverage. It has also been found to provide different data to Apple's
17 implementation when presented with a key-value archive. This is because Apple's
18 implementation produces undocumented CFKeyArchiverUID objects. My implementation
19 produces dictionaries instead, matching the in-file representation used in XML
20 and OpenStep plists. See extract_uid().
22 Full disclosure: in implementing this software, I read one comment and one
23 struct defintion in CFLite, Apple's implementation, which is under the APSL
24 license. I also deduced the information about CFKeyArchiverUID from that code.
25 However, none of the implementation was copied.
27 Copyright (C) 2007 Jens Ayton
29 Permission is hereby granted, free of charge, to any person obtaining a copy
30 of this software and associated documentation files (the "Software"), to deal
31 in the Software without restriction, including without limitation the rights
32 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33 copies of the Software, and to permit persons to whom the Software is
34 furnished to do so, subject to the following conditions:
36 The above copyright notice and this permission notice shall be included in all
37 copies or substantial portions of the Software.
39 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
42 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
44 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
49 /* A note about memory management:
50 Strings and possibly other values are unique and because the values
51 associated with IDs are cached, you end up with a directed graph rather
52 than a tree. It is tricky to free the data because if you do a simple
53 depth-first search to free nodes, you will free nodes twice. I decided
54 to allocate memory from blocks of 1024 bytes and keep the blocks in a
55 list associated with but private to this module. So the user should
56 access this module by calling:
57 bplist_read_file() or bplist_read_user_pref() or
58 bplist_read_system_pref()
59 which returns a value. When you are done with the value, call
61 This will of course free the value_ptr returned by bplist_read_*()
63 To deal with memory exhaustion (what happens when malloc returns
64 NULL?), use setjmp/longjmp -- a single setjmp protects the whole
65 parser, and allocate uses longjmp to abort. After abort, memory
66 is freed and NULL is returned to caller. There is not much here
67 in the way of error reporting.
69 Memory is obtained by calling allocate which either returns the
70 memory requested or calls longjmp, so callers don't have to check.
74 #include <sys/types.h>
80 #include "readbinaryplist.h"
81 #include <Carbon/Carbon.h>
87 #define MAXPATHLEN 256
89 /* there are 2 levels of error logging/printing:
90 * BPLIST_LOG and BPLIST_LOG_VERBOSE
91 * either or both can be set to non-zero to turn on
92 * If BPLIST_LOG_VERBOSE is true, then BPLIST_LOG
95 * In the code, logging is done by calling either
96 * bplist_log() or bplist_log_verbose(), which take
97 * parameters like printf but might be a no-op.
100 /* #define BPLIST_LOG_VERBOSE 1 */
102 #if BPLIST_LOG_VERBOSE
109 #define bplist_log printf
111 #define bplist_log(...)
114 #if BPLIST_LOG_VERBOSE
115 #define bplist_log_verbose bplist_log
117 #define bplist_log_verbose(...)
121 /********* MEMORY MANAGEMENT ********/
122 #define BLOCK_SIZE 1024
123 // memory is aligned to multiples of this; assume malloc automatically
124 // aligns to this number and assume this number is > sizeof(void *)
126 static void *block_list = NULL;
127 static char *free_ptr = NULL;
128 static char *end_ptr = NULL;
129 static jmp_buf abort_parsing;
131 static void *allocate(size_t size)
134 if (free_ptr + size > end_ptr) {
135 size_t how_much = BLOCK_SIZE;
136 // align everything to 8 bytes
137 if (size > BLOCK_SIZE - ALIGNMENT) {
138 how_much = size + ALIGNMENT;
140 result = malloc(how_much);
141 if (result == NULL) {
142 /* serious problem */
143 longjmp(abort_parsing, 1);
145 *((void **)result) = block_list;
147 free_ptr = ((char *) result) + ALIGNMENT;
148 end_ptr = ((char *) result) + how_much;
150 // now, there is enough rooom at free_ptr
156 void bplist_free_data()
159 void *next = *(void **)block_list;
167 // layout of trailer -- last 32 bytes in plist data
169 uint8_t offset_int_size;
170 uint8_t object_ref_size;
171 uint64_t object_count;
172 uint64_t top_level_object;
173 uint64_t offset_table_offset;
179 kTRAILER_SIZE = 32, //sizeof(bplist_trailer_node),
180 kMINIMUM_SANE_SIZE = kHEADER_SIZE + kTRAILER_SIZE
184 static const char kHEADER_BYTES[kHEADER_SIZE] = "bplist00";
186 // map from UID key to previously parsed value
187 typedef struct cache_struct {
190 struct cache_struct *next;
191 } cache_node, *cache_ptr;
194 typedef struct bplist_info
196 uint64_t object_count;
197 const uint8_t *data_bytes;
199 uint64_t offset_table_offset;
200 uint8_t offset_int_size;
201 uint8_t object_ref_size;
203 } bplist_info_node, *bplist_info_ptr;
206 static value_ptr bplist_read_pldata(pldata_ptr data);
207 static value_ptr bplist_read_pref(char *filename, OSType folder_type);
208 static uint64_t read_sized_int(bplist_info_ptr bplist, uint64_t offset, uint8_t size);
209 static uint64_t read_offset(bplist_info_ptr bplist, uint64_t index);
210 static BOOL read_self_sized_int(bplist_info_ptr bplist, uint64_t offset, uint64_t *outValue, size_t *outSize);
212 static value_ptr extract_object(bplist_info_ptr bplist, uint64_t objectRef);
213 static value_ptr extract_simple(bplist_info_ptr bplist, uint64_t offset);
214 static value_ptr extract_int(bplist_info_ptr bplist, uint64_t offset);
215 static value_ptr extract_real(bplist_info_ptr bplist, uint64_t offset);
216 static value_ptr extract_date(bplist_info_ptr bplist, uint64_t offset);
217 static value_ptr extract_data(bplist_info_ptr bplist, uint64_t offset);
218 static value_ptr extract_ascii_string(bplist_info_ptr bplist, uint64_t offset);
219 static value_ptr extract_unicode_string(bplist_info_ptr bplist, uint64_t offset);
220 static value_ptr extract_uid(bplist_info_ptr bplist, uint64_t offset);
221 static value_ptr extract_array(bplist_info_ptr bplist, uint64_t offset);
222 static value_ptr extract_dictionary(bplist_info_ptr bplist, uint64_t offset);
225 value_ptr value_create()
227 value_ptr value = (value_ptr) allocate(sizeof(value_node));
232 void value_set_integer(value_ptr v, int64_t i) {
233 v->tag = kTAG_INT; v->integer = i;
236 void value_set_real(value_ptr v, double d) {
237 v->tag = kTAG_REAL; v->real = d;
240 // d is seconds since 1 January 2001
241 void value_set_date(value_ptr v, double d) {
242 v->tag = kTAG_DATE; v->real = d;
245 void value_set_ascii_string(value_ptr v, const uint8_t *s, size_t len) {
246 v->tag = kTAG_ASCIISTRING;
247 v->string = (char *) allocate(len + 1);
248 memcpy(v->string, s, len);
252 void value_set_unicode_string(value_ptr v, const uint8_t *s, size_t len) {
253 v->tag = kTAG_UNICODESTRING;
254 v->string = (char *) allocate(len + 1);
255 memcpy(v->string, s, len);
259 void value_set_uid(value_ptr v, uint64_t uid)
261 v->tag = kTAG_UID; v->uinteger = uid;
264 // v->data points to a pldata that points to the actual bytes
265 // the bytes are copied, so caller must free byte source (*data)
266 void value_set_data(value_ptr v, const uint8_t *data, size_t len) {
268 pldata_ptr pldata = (pldata_ptr) allocate(sizeof(pldata_node));
269 pldata->data = (uint8_t *) allocate(len);
270 memcpy(pldata->data, data, len);
273 printf("value at %p gets data at %p\n", v, pldata);
276 // caller releases ownership of array to value_ptr v
277 void value_set_array(value_ptr v, value_ptr *array, size_t length) {
278 array_ptr a = (array_ptr) allocate(sizeof(array_node));
285 // caller releases ownership of dict to value_ptr v
286 void value_set_dict(value_ptr v, dict_ptr dict) {
287 v->tag = kTAG_DICTIONARY;
292 // look up an objectref in the cache, a ref->value_ptr mapping
293 value_ptr cache_lookup(cache_ptr cache, uint64_t ref)
296 if (cache->key == ref) {
305 // insert an objectref and value in the cache
306 void cache_insert(cache_ptr *cache, uint64_t ref, value_ptr value)
308 cache_ptr c = (cache_ptr) allocate(sizeof(cache_node));
316 // insert an objectref and value in a dictionary
317 void dict_insert(dict_ptr *dict, value_ptr key, value_ptr value)
319 dict_ptr d = (dict_ptr) allocate(sizeof(dict_node));
327 BOOL is_binary_plist(pldata_ptr data)
329 if (data->len < kMINIMUM_SANE_SIZE) return NO;
330 return memcmp(data->data, kHEADER_BYTES, kHEADER_SIZE) == 0;
334 value_ptr bplist_read_file(char *filename)
341 int rslt = stat(filename, &stbuf);
346 bplist_log("Could not stat %s, error %d\n", filename, rslt);
349 // if file is >100MB, assume it is not a preferences file and give up
350 if (stbuf.st_size > 100000000) {
351 bplist_log("Large file %s encountered (%llu bytes) -- not read\n",
352 filename, stbuf.st_size);
355 pldata.len = (size_t) stbuf.st_size;
356 // note: this is supposed to be malloc, not allocate. It is separate
357 // from the graph structure, large, and easy to free right after
359 pldata.data = (uint8_t *) malloc(pldata.len);
361 bplist_log("Could not allocate %lu bytes for %s\n",
362 (unsigned long) pldata.len, filename);
365 file = fopen(filename, "rb");
367 bplist_log("Could not open %s\n", filename);
370 n = fread(pldata.data, 1, pldata.len, file);
371 if (n != pldata.len) {
372 bplist_log("Error reading from %s\n", filename);
375 value = bplist_read_pldata(&pldata);
381 value_ptr bplist_read_pref(char *filename, OSType folder_type)
384 char cstr[MAXPATHLEN];
386 OSErr err = FSFindFolder(kOnAppropriateDisk, folder_type,
389 bplist_log("Error finding preferences folder: %d\n", err);
392 err = FSRefMakePath(&prefdir, (UInt8 *) cstr, (UInt32) (MAXPATHLEN - 1));
394 bplist_log("Error making path name for preferences folder: %d\n", err);
397 strlcat(cstr, "/", MAXPATHLEN);
398 strlcat(cstr, filename, MAXPATHLEN);
399 return bplist_read_file(cstr);
403 value_ptr bplist_read_system_pref(char *filename) {
404 return bplist_read_pref(filename, kSystemPreferencesFolderType);
408 value_ptr bplist_read_user_pref(char *filename) {
409 return bplist_read_pref(filename, kPreferencesFolderType);
413 // data is stored with high-order bytes first.
414 // read from plist data in a machine-independent fashion
416 uint64_t convert_uint64(uint8_t *ptr)
420 // shift in bytes, high-order first
421 for (i = 0; i < sizeof(uint64_t); i++) {
429 value_ptr bplist_read_pldata(pldata_ptr data)
431 value_ptr result = NULL;
432 bplist_info_node bplist;
434 uint64_t top_level_object;
437 if (data == NULL) return NULL;
438 if (!is_binary_plist(data)) {
439 bplist_log("Bad binary plist: too short or invalid header.\n");
444 ptr = (uint8_t *) (data->data + data->len - kTRAILER_SIZE);
445 bplist.offset_int_size = ptr[6];
446 bplist.object_ref_size = ptr[7];
447 bplist.object_count = convert_uint64(ptr + 8);
448 top_level_object = convert_uint64(ptr + 16);
449 bplist.offset_table_offset = convert_uint64(ptr + 24);
451 // Basic sanity checks
452 if (bplist.offset_int_size < 1 || bplist.offset_int_size > 8 ||
453 bplist.object_ref_size < 1 || bplist.object_ref_size > 8 ||
454 bplist.offset_table_offset < kHEADER_SIZE) {
455 bplist_log("Bad binary plist: trailer declared insane.\n");
459 // Ensure offset table is inside file
460 uint64_t offsetTableSize = bplist.offset_int_size * bplist.object_count;
461 if (offsetTableSize + bplist.offset_table_offset + kTRAILER_SIZE >
463 bplist_log("Bad binary plist: offset table overlaps end of container.\n");
467 bplist.data_bytes = data->data;
468 bplist.length = data->len;
469 bplist.cache = NULL; /* dictionary is empty */
471 bplist_log_verbose("Got a sane bplist with %llu items, offset_int_size: %u, object_ref_size: %u\n",
472 bplist.object_count, bplist.offset_int_size,
473 bplist.object_ref_size);
474 /* at this point, we are ready to do some parsing which allocates
475 memory for the result data structure. If memory allocation (using
476 allocate fails, a longjmp will return to here and we simply give up
478 i = setjmp(abort_parsing);
480 result = extract_object(&bplist, top_level_object);
482 bplist_log("allocate() failed to allocate memory. Giving up.\n");
492 static value_ptr extract_object(bplist_info_ptr bplist, uint64_t objectRef)
495 value_ptr result = NULL;
498 if (objectRef >= bplist->object_count) {
499 // Out-of-range object reference.
500 bplist_log("Bad binary plist: object index is out of range.\n");
504 // Use cached object if it exists
505 result = cache_lookup(bplist->cache, objectRef);
506 if (result != NULL) return result;
508 // Otherwise, find object in file.
509 offset = read_offset(bplist, objectRef);
510 if (offset > bplist->length) {
511 // Out-of-range offset.
512 bplist_log("Bad binary plist: object outside container.\n");
515 objectTag = *(bplist->data_bytes + offset);
516 switch (objectTag & 0xF0) {
518 result = extract_simple(bplist, offset);
522 result = extract_int(bplist, offset);
526 result = extract_real(bplist, offset);
530 result = extract_date(bplist, offset);
534 result = extract_data(bplist, offset);
537 case kTAG_ASCIISTRING:
538 result = extract_ascii_string(bplist, offset);
541 case kTAG_UNICODESTRING:
542 result = extract_unicode_string(bplist, offset);
546 result = extract_uid(bplist, offset);
550 result = extract_array(bplist, offset);
553 case kTAG_DICTIONARY:
554 result = extract_dictionary(bplist, offset);
559 bplist_log("Bad binary plist: unknown tag 0x%X.\n",
560 (objectTag & 0x0F) >> 4);
564 // Cache and return result.
566 cache_insert(&bplist->cache, objectRef, result);
571 static uint64_t read_sized_int(bplist_info_ptr bplist, uint64_t offset,
574 assert(bplist->data_bytes != NULL && size >= 1 && size <= 8 &&
575 offset + size <= bplist->length);
578 const uint8_t *byte = bplist->data_bytes + offset;
581 // note that ints seem to be high-order first
582 result = (result << 8) | *byte++;
589 static uint64_t read_offset(bplist_info_ptr bplist, uint64_t index)
591 assert(index < bplist->object_count);
593 return read_sized_int(bplist,
594 bplist->offset_table_offset + bplist->offset_int_size * index,
595 bplist->offset_int_size);
599 static BOOL read_self_sized_int(bplist_info_ptr bplist, uint64_t offset,
600 uint64_t *outValue, size_t *outSize)
605 assert(bplist->data_bytes != NULL && offset < bplist->length);
607 size = 1 << (bplist->data_bytes[offset] & 0x0F);
609 // Maximum allowable size in this implementation is 1<<3 = 8 bytes.
610 // This also happens to be the biggest we can handle.
614 if (offset + 1 + size > bplist->length) {
619 value = read_sized_int(bplist, offset + 1, size);
621 if (outValue != NULL) *outValue = value;
622 if (outSize != NULL) *outSize = size + 1; // +1 for tag byte.
627 static value_ptr extract_simple(bplist_info_ptr bplist, uint64_t offset)
629 assert(bplist->data_bytes != NULL && offset < bplist->length);
630 value_ptr value = value_create();
632 switch (bplist->data_bytes[offset]) {
634 value->tag = kVALUE_NULL;
638 value->tag = kVALUE_TRUE;
642 value->tag = kVALUE_FALSE;
646 // Note: kVALUE_FILLER is treated as invalid, because it, er, is.
647 bplist_log("Bad binary plist: invalid atom.\n");
653 static value_ptr extract_int(bplist_info_ptr bplist, uint64_t offset)
655 value_ptr value = value_create();
656 value->tag = kTAG_INT;
658 if (!read_self_sized_int(bplist, offset, &value->uinteger, NULL)) {
659 bplist_log("Bad binary plist: invalid integer object.\n");
662 /* NOTE: originally, I sign-extended here. This was the wrong thing; it
663 turns out that negative ints are always stored as 64-bit, and smaller
670 static value_ptr extract_real(bplist_info_ptr bplist, uint64_t offset)
672 value_ptr value = value_create();
675 assert(bplist->data_bytes != NULL && offset < bplist->length);
677 size = 1 << (bplist->data_bytes[offset] & 0x0F);
679 // FIXME: what to do if faced with other sizes for float/double?
680 assert (sizeof (float) == sizeof (uint32_t) &&
681 sizeof (double) == sizeof (uint64_t));
683 if (offset + 1 + size > bplist->length) {
684 bplist_log("Bad binary plist: %s object overlaps end of container.\n",
685 "floating-point number");
690 if (size == sizeof (float)) {
691 // cast is ok because we know size is 4 bytes
692 uint32_t i = (uint32_t) read_sized_int(bplist, offset + 1, size);
693 // Note that this handles byte swapping.
694 value_set_real(value, *(float *)&i);
696 } else if (size == sizeof (double)) {
697 uint64_t i = read_sized_int(bplist, offset + 1, size);
698 // Note that this handles byte swapping.
699 value_set_real(value, *(double *)&i);
702 // Can't handle floats of other sizes.
703 bplist_log("Bad binary plist: can't handle %u-byte float.\n", size);
710 static value_ptr extract_date(bplist_info_ptr bplist, uint64_t offset)
713 assert(bplist->data_bytes != NULL && offset < bplist->length);
715 // Data has size code like int and real, but only 3 (meaning 8 bytes) is valid.
716 if (bplist->data_bytes[offset] != kVALUE_FULLDATETAG) {
717 bplist_log("Bad binary plist: invalid size for date object.\n");
721 if (offset + 1 + sizeof (double) > bplist->length) {
722 bplist_log("Bad binary plist: %s object overlaps end of container.\n",
727 // FIXME: what to do if faced with other sizes for double?
728 assert (sizeof (double) == sizeof (uint64_t));
730 uint64_t date = read_sized_int(bplist, offset + 1, sizeof(double));
731 // Note that this handles byte swapping.
732 value = value_create();
733 value_set_date(value, *(double *)&date);
738 uint64_t bplist_get_a_size(bplist_info_ptr bplist,
739 uint64_t *offset_ptr, char *msg)
741 uint64_t size = bplist->data_bytes[*offset_ptr] & 0x0F;
744 // 0x0F means separate int size follows.
745 // Smaller values are used for short data.
746 size_t extra; // the length of the data size we are about to read
747 if ((bplist->data_bytes[*offset_ptr] & 0xF0) != kTAG_INT) {
748 // Bad data, mistagged size int
749 bplist_log("Bad binary plist: %s object size is not tagged as int.\n",
751 return UINT64_MAX; // error
754 // read integer data as size, extra tells how many bytes to skip
755 if (!read_self_sized_int(bplist, *offset_ptr, &size, &extra)) {
756 bplist_log("Bad binary plist: invalid %s object size tag.\n",
758 return UINT64_MAX; // error
760 (*offset_ptr) += extra;
763 if (*offset_ptr + size > bplist->length) {
764 bplist_log("Bad binary plist: %s object overlaps end of container.\n",
766 return UINT64_MAX; // error
772 static value_ptr extract_data(bplist_info_ptr bplist, uint64_t offset)
777 assert(bplist->data_bytes != NULL && offset < bplist->length);
779 if ((size = bplist_get_a_size(bplist, &offset, "data")) == UINT64_MAX)
782 value = value_create();
783 // cast is ok because we only allow files up to 100MB:
784 value_set_data(value, bplist->data_bytes + (size_t) offset, (size_t) size);
789 static value_ptr extract_ascii_string(bplist_info_ptr bplist, uint64_t offset)
792 value_ptr value; // return value
794 assert(bplist->data_bytes != NULL && offset < bplist->length);
796 if ((size = bplist_get_a_size(bplist, &offset, "ascii string")) ==
800 value = value_create();
801 // cast is ok because we only allow 100MB files
802 value_set_ascii_string(value, bplist->data_bytes + (size_t) offset,
808 static value_ptr extract_unicode_string(bplist_info_ptr bplist, uint64_t offset)
813 assert(bplist->data_bytes != NULL && offset < bplist->length);
815 if ((size = bplist_get_a_size(bplist, &offset, "unicode string")) ==
819 value = value_create();
820 // cast is ok because we only allow 100MB files
821 value_set_unicode_string(value, bplist->data_bytes + (size_t) offset,
827 static value_ptr extract_uid(bplist_info_ptr bplist, uint64_t offset)
829 /* UIDs are used by Cocoa's key-value coder.
830 When writing other plist formats, they are expanded to dictionaries of
831 the form <dict><key>CF$UID</key><integer>value</integer></dict>, so we
832 do the same here on reading. This results in plists identical to what
833 running plutil -convert xml1 gives us. However, this is not the same
834 result as [Core]Foundation's plist parser, which extracts them as un-
835 introspectable CF objects. In fact, it even seems to convert the CF$UID
836 dictionaries from XML plists on the fly.
842 if (!read_self_sized_int(bplist, offset, &uid, NULL)) {
843 bplist_log("Bad binary plist: invalid UID object.\n");
847 // assert(NO); // original code suggests using a string for a key
848 // but our dictionaries all use big ints for keys, so I don't know
851 // In practice, I believe this code is never executed by PortMidi.
852 // I changed it to do something and not raise compiler warnings, but
853 // not sure what the code should do.
855 value = value_create();
856 value_set_uid(value, uid);
857 // return [NSDictionary dictionaryWithObject:
858 // [NSNumber numberWithUnsignedLongLong:value]
864 static value_ptr extract_array(bplist_info_ptr bplist, uint64_t offset)
869 value_ptr element = NULL;
870 value_ptr *array = NULL;
871 value_ptr value = NULL;
874 assert(bplist->data_bytes != NULL && offset < bplist->length);
876 if ((count = bplist_get_a_size(bplist, &offset, "array")) == UINT64_MAX)
879 if (count > UINT64_MAX / bplist->object_ref_size - offset) {
881 bplist_log("Bad binary plist: %s object overlaps end of container.\n",
886 size = bplist->object_ref_size * count;
887 if (size + offset > bplist->length) {
888 bplist_log("Bad binary plist: %s object overlaps end of container.\n",
893 // got count, the number of array elements
895 value = value_create();
899 // count must be size_t or smaller because max file size is 100MB
900 value_set_array(value, array, (size_t) count);
904 array = allocate(sizeof(value_ptr) * (size_t) count);
906 for (i = 0; i != count; ++i) {
907 bplist_log_verbose("[%u]\n", i);
908 elementID = read_sized_int(bplist, offset + i * bplist->object_ref_size,
909 bplist->object_ref_size);
910 element = extract_object(bplist, elementID);
911 if (element != NULL) {
918 if (ok) { // count is smaller than size_t max because of 100MB file limit
919 value_set_array(value, array, (size_t) count);
926 static value_ptr extract_dictionary(bplist_info_ptr bplist, uint64_t offset)
931 value_ptr value = NULL;
932 dict_ptr dict = NULL;
935 assert(bplist->data_bytes != NULL && offset < bplist->length);
938 if ((count = bplist_get_a_size(bplist, &offset, "array")) == UINT64_MAX)
941 if (count > UINT64_MAX / (bplist->object_ref_size * 2) - offset) {
943 bplist_log("Bad binary plist: %s object overlaps end of container.\n",
948 size = bplist->object_ref_size * count * 2;
949 if (size + offset > bplist->length) {
950 bplist_log("Bad binary plist: %s object overlaps end of container.\n",
955 value = value_create();
957 value_set_dict(value, NULL);
961 for (i = 0; i != count; ++i) {
964 elementID = read_sized_int(bplist, offset + i * bplist->object_ref_size,
965 bplist->object_ref_size);
966 key = extract_object(bplist, elementID);
968 bplist_log_verbose("key: %p\n", key);
974 elementID = read_sized_int(bplist,
975 offset + (i + count) * bplist->object_ref_size,
976 bplist->object_ref_size);
977 val = extract_object(bplist, elementID);
979 dict_insert(&dict, key, val);
986 value_set_dict(value, dict);
992 /*************** functions for accessing values ****************/
995 char *value_get_asciistring(value_ptr v)
997 if (v->tag != kTAG_ASCIISTRING) return NULL;
1002 value_ptr value_dict_lookup_using_string(value_ptr v, char *key)
1005 if (v->tag != kTAG_DICTIONARY) return NULL; // not a dictionary
1007 /* search for key */
1009 if (dict->key && dict->key->tag == kTAG_ASCIISTRING &&
1010 strcmp(key, dict->key->string) == 0) { // found it
1015 return NULL; /* not found */
1018 value_ptr value_dict_lookup_using_path(value_ptr v, char *path)
1020 char key[MAX_KEY_SIZE];
1021 while (*path) { /* more to the path */
1023 while (i < MAX_KEY_SIZE - 1) {
1025 if (key[i] == '/') { /* end of entry in path */
1030 path--; /* back up to end of string char */
1031 break; /* this will cause outer loop to exit */
1035 if (!v || v->tag != kTAG_DICTIONARY) return NULL;
1036 /* now, look up the key to get next value */
1037 v = value_dict_lookup_using_string(v, key);
1038 if (v == NULL) return NULL;
1044 /*************** functions for debugging ***************/
1046 void plist_print(value_ptr v)
1055 switch (v->tag & 0xF0) {
1059 printf("NULL@%p", v); break;
1061 printf("FALSE@%p", v); break;
1063 printf("TRUE@%p", v); break;
1065 printf("UNKNOWN tag=%x@%p", v->tag, v); break;
1069 printf("%lld@%p", v->integer, v); break;
1071 printf("%g@%p", v->real, v); break;
1073 printf("date:%g@%p", v->real, v); break;
1075 printf("data@%p->%p:[%p:", v, v->data, v->data->data);
1076 for (i = 0; i < v->data->len; i++) {
1077 printf(" %2x", v->data->data[i]);
1080 case kTAG_ASCIISTRING:
1081 printf("%p:\"%s\"@%p", v->string, v->string, v); break;
1082 case kTAG_UNICODESTRING:
1083 printf("unicode:%p:\"%s\"@%p", v->string, v->string, v); break;
1085 printf("UID:%llu@%p", v->uinteger, v); break;
1087 comma_needed = FALSE;
1088 printf("%p->%p:[%p:", v, v->array, v->array->array);
1089 for (i = 0; i < v->array->length; i++) {
1090 if (comma_needed) printf(", ");
1091 plist_print(v->array->array[i]);
1092 comma_needed = TRUE;
1095 case kTAG_DICTIONARY:
1096 comma_needed = FALSE;
1100 if (comma_needed) printf(", ");
1101 printf("%p:", dict);
1102 plist_print(dict->key);
1104 plist_print(dict->value);
1105 comma_needed = TRUE;
1110 printf("UNKNOWN tag=%x", v->tag);