2 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1999-2014, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
29 #include <CoreFoundation/CFPropertyList.h>
30 #include <CoreFoundation/CFDate.h>
31 #include <CoreFoundation/CFNumber.h>
32 #include <CoreFoundation/CFSet.h>
33 #include <CoreFoundation/CFError.h>
34 #include <CoreFoundation/CFError_Private.h>
35 #include <CoreFoundation/CFPriv.h>
36 #include <CoreFoundation/CFStringEncodingConverter.h>
37 #include "CFInternal.h"
38 #include <CoreFoundation/CFBurstTrie.h>
39 #include <CoreFoundation/CFString.h>
40 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
41 #include <CoreFoundation/CFStream.h>
43 #include <CoreFoundation/CFCalendar.h>
44 #include "CFLocaleInternal.h"
54 CF_EXPORT CFNumberType
_CFNumberGetType2(CFNumberRef number
);
70 #define PLIST_TAG_LENGTH 5
71 #define ARRAY_TAG_LENGTH 5
72 #define DICT_TAG_LENGTH 4
73 #define KEY_TAG_LENGTH 3
74 #define STRING_TAG_LENGTH 6
75 #define DATA_TAG_LENGTH 4
76 #define DATE_TAG_LENGTH 4
77 #define REAL_TAG_LENGTH 4
78 #define INTEGER_TAG_LENGTH 7
79 #define TRUE_TAG_LENGTH 4
80 #define FALSE_TAG_LENGTH 5
81 #define DOCTYPE_TAG_LENGTH 7
82 #define CDSECT_TAG_LENGTH 9
84 #if !defined(new_cftype_array)
85 #define new_cftype_array(N, C) \
86 size_t N ## _count__ = (C); \
87 if (N ## _count__ > LONG_MAX / sizeof(CFTypeRef)) { \
88 CRSetCrashLogMessage("CFPropertyList ran out of memory while attempting to allocate temporary storage."); \
91 Boolean N ## _is_stack__ = (N ## _count__ <= 256); \
92 if (N ## _count__ == 0) N ## _count__ = 1; \
93 STACK_BUFFER_DECL(CFTypeRef, N ## _buffer__, N ## _is_stack__ ? N ## _count__ : 1); \
94 if (N ## _is_stack__) memset(N ## _buffer__, 0, N ## _count__ * sizeof(CFTypeRef)); \
95 CFTypeRef * N = N ## _is_stack__ ? N ## _buffer__ : (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, (N ## _count__) * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory); \
97 CRSetCrashLogMessage("CFPropertyList ran out of memory while attempting to allocate temporary storage."); \
103 #if !defined(free_cftype_array)
104 #define free_cftype_array(N) \
105 if (! N ## _is_stack__) { \
106 CFAllocatorDeallocate(kCFAllocatorSystemDefault, N); \
111 // Used to reference an old-style plist parser error inside of a more general XML error
112 #define CFPropertyListOldStyleParserErrorKey CFSTR("kCFPropertyListOldStyleParsingError")
114 static CFTypeID stringtype
, datatype
, numbertype
, datetype
;
115 static CFTypeID booltype
, nulltype
, dicttype
, arraytype
, settype
;
117 static void initStatics() {
118 static dispatch_once_t once
;
119 dispatch_once(&once
, ^{
120 stringtype
= CFStringGetTypeID();
121 datatype
= CFDataGetTypeID();
122 numbertype
= CFNumberGetTypeID();
123 booltype
= CFBooleanGetTypeID();
124 datetype
= CFDateGetTypeID();
125 dicttype
= CFDictionaryGetTypeID();
126 arraytype
= CFArrayGetTypeID();
127 settype
= CFSetGetTypeID();
128 nulltype
= CFNullGetTypeID();
132 CF_PRIVATE CFErrorRef
__CFPropertyListCreateError(CFIndex code
, CFStringRef debugString
, ...) {
134 CFErrorRef error
= NULL
;
136 if (debugString
!= NULL
) {
137 CFStringRef debugMessage
= NULL
;
138 va_start(argList
, debugString
);
139 debugMessage
= CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault
, NULL
, debugString
, argList
);
142 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
143 CFDictionarySetValue(userInfo
, kCFErrorDebugDescriptionKey
, debugMessage
);
145 error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, code
, userInfo
);
147 CFRelease(debugMessage
);
150 error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, code
, NULL
);
156 static CFStringRef
__copyErrorDebugDescription(CFErrorRef error
) {
157 CFStringRef result
= NULL
;
159 CFDictionaryRef userInfo
= CFErrorCopyUserInfo(error
);
160 if (userInfo
!= NULL
) {
161 CFStringRef desc
= (CFStringRef
) CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
163 result
= CFStringCreateCopy(kCFAllocatorSystemDefault
, desc
);
172 #pragma mark Property List Validation
174 // don't allow _CFKeyedArchiverUID here
175 #define __CFAssertIsPList(cf) CFAssert2(CFGetTypeID(cf) == CFStringGetTypeID() || CFGetTypeID(cf) == CFArrayGetTypeID() || CFGetTypeID(cf) == CFBooleanGetTypeID() || CFGetTypeID(cf) == CFNumberGetTypeID() || CFGetTypeID(cf) == CFDictionaryGetTypeID() || CFGetTypeID(cf) == CFDateGetTypeID() || CFGetTypeID(cf) == CFDataGetTypeID(), __kCFLogAssertion, "%s(): %p not of a property list type", __PRETTY_FUNCTION__, cf);
180 CFPropertyListFormat format
;
184 static bool __CFPropertyListIsValidAux(CFPropertyListRef plist
, bool recursive
, CFMutableSetRef set
, CFPropertyListFormat format
, CFStringRef
*error
);
186 static void __CFPropertyListIsArrayPlistAux(const void *value
, void *context
) {
187 struct context
*ctx
= (struct context
*)context
;
188 if (!ctx
->answer
) return;
189 if (!value
&& !*(ctx
->error
)) {
190 *(ctx
->error
) = (CFStringRef
)CFRetain(CFSTR("property list arrays cannot contain NULL"));
192 ctx
->answer
= value
&& __CFPropertyListIsValidAux(value
, true, ctx
->set
, ctx
->format
, ctx
->error
);
195 static void __CFPropertyListIsDictPlistAux(const void *key
, const void *value
, void *context
) {
196 struct context
*ctx
= (struct context
*)context
;
197 if (!ctx
->answer
) return;
198 if (!key
&& !*(ctx
->error
)) *(ctx
->error
) = (CFStringRef
)CFRetain(CFSTR("property list dictionaries cannot contain NULL keys"));
199 if (!value
&& !*(ctx
->error
)) *(ctx
->error
) = (CFStringRef
)CFRetain(CFSTR("property list dictionaries cannot contain NULL values"));
200 if (stringtype
!= CFGetTypeID(key
) && !*(ctx
->error
)) {
201 CFStringRef desc
= CFCopyTypeIDDescription(CFGetTypeID(key
));
202 *(ctx
->error
) = CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("property list dictionaries may only have keys which are CFStrings, not '%@'"), desc
);
205 ctx
->answer
= key
&& value
&& (stringtype
== CFGetTypeID(key
)) && __CFPropertyListIsValidAux(value
, true, ctx
->set
, ctx
->format
, ctx
->error
);
208 static bool __CFPropertyListIsValidAux(CFPropertyListRef plist
, bool recursive
, CFMutableSetRef set
, CFPropertyListFormat format
, CFStringRef
*error
) {
211 *error
= (CFStringRef
)CFRetain(CFSTR("property lists cannot contain NULL"));
214 type
= CFGetTypeID(plist
);
215 if (stringtype
== type
) return true;
216 if (datatype
== type
) return true;
217 if (kCFPropertyListOpenStepFormat
!= format
) {
218 if (booltype
== type
) return true;
219 if (numbertype
== type
) return true;
220 if (datetype
== type
) return true;
221 if (_CFKeyedArchiverUIDGetTypeID() == type
) return true;
223 if (!recursive
&& arraytype
== type
) return true;
224 if (!recursive
&& dicttype
== type
) return true;
225 // at any one invocation of this function, set should contain the objects in the "path" down to this object
226 if (CFSetContainsValue(set
, plist
)) {
227 *error
= (CFStringRef
)CFRetain(CFSTR("property lists cannot contain recursive container references"));
230 if (arraytype
== type
) {
231 struct context ctx
= {true, set
, format
, error
};
232 CFSetAddValue(set
, plist
);
233 CFArrayApplyFunction((CFArrayRef
)plist
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
)plist
)), __CFPropertyListIsArrayPlistAux
, &ctx
);
234 CFSetRemoveValue(set
, plist
);
237 if (dicttype
== type
) {
238 struct context ctx
= {true, set
, format
, error
};
239 CFSetAddValue(set
, plist
);
240 CFDictionaryApplyFunction((CFDictionaryRef
)plist
, __CFPropertyListIsDictPlistAux
, &ctx
);
241 CFSetRemoveValue(set
, plist
);
245 CFStringRef desc
= CFCopyTypeIDDescription(type
);
246 *error
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("property lists cannot contain objects of type '%@'"), desc
);
252 static Boolean
_CFPropertyListIsValidWithErrorString(CFPropertyListRef plist
, CFPropertyListFormat format
, CFStringRef
*error
) {
254 CFAssert1(plist
!= NULL
, __kCFLogAssertion
, "%s(): NULL is not a property list", __PRETTY_FUNCTION__
);
255 CFMutableSetRef set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
256 bool result
= __CFPropertyListIsValidAux(plist
, true, set
, format
, error
);
262 #pragma mark Writing Property Lists
264 static const char CFXMLPlistTags
[13][10]= {
265 {'p', 'l', 'i', 's', 't', '\0', '\0', '\0', '\0', '\0'},
266 {'a', 'r', 'r', 'a', 'y', '\0', '\0', '\0', '\0', '\0'},
267 {'d', 'i', 'c', 't', '\0', '\0', '\0', '\0', '\0', '\0'},
268 {'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
269 {'s', 't', 'r', 'i', 'n', 'g', '\0', '\0', '\0', '\0'},
270 {'d', 'a', 't', 'a', '\0', '\0', '\0', '\0', '\0', '\0'},
271 {'d', 'a', 't', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
272 {'r', 'e', 'a', 'l', '\0', '\0', '\0', '\0', '\0', '\0'},
273 {'i', 'n', 't', 'e', 'g', 'e', 'r', '\0', '\0', '\0'},
274 {'t', 'r', 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
275 {'f', 'a', 'l', 's', 'e', '\0', '\0', '\0', '\0', '\0'},
276 {'D', 'O', 'C', 'T', 'Y', 'P', 'E', '\0', '\0', '\0'},
277 {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[', '\0'}
280 static const UniChar CFXMLPlistTagsUnicode
[13][10]= {
281 {'p', 'l', 'i', 's', 't', '\0', '\0', '\0', '\0', '\0'},
282 {'a', 'r', 'r', 'a', 'y', '\0', '\0', '\0', '\0', '\0'},
283 {'d', 'i', 'c', 't', '\0', '\0', '\0', '\0', '\0', '\0'},
284 {'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
285 {'s', 't', 'r', 'i', 'n', 'g', '\0', '\0', '\0', '\0'},
286 {'d', 'a', 't', 'a', '\0', '\0', '\0', '\0', '\0', '\0'},
287 {'d', 'a', 't', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
288 {'r', 'e', 'a', 'l', '\0', '\0', '\0', '\0', '\0', '\0'},
289 {'i', 'n', 't', 'e', 'g', 'e', 'r', '\0', '\0', '\0'},
290 {'t', 'r', 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
291 {'f', 'a', 'l', 's', 'e', '\0', '\0', '\0', '\0', '\0'},
292 {'D', 'O', 'C', 'T', 'Y', 'P', 'E', '\0', '\0', '\0'},
293 {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[', '\0'}
297 const char *begin
; // first character of the XML to be parsed
298 const char *curr
; // current parse location
299 const char *end
; // the first character _after_ the end of the XML
301 CFAllocatorRef allocator
;
302 UInt32 mutabilityOption
;
303 CFBurstTrieRef stringTrie
; // map of cached strings
304 CFMutableArrayRef stringCache
; // retaining array of strings
305 Boolean allowNewTypes
; // Whether to allow the new types supported by XML property lists, but not by the old, OPENSTEP ASCII property lists (CFNumber, CFBoolean, CFDate)
306 CFSetRef keyPaths
; // if NULL, no filtering
307 Boolean skip
; // if true, do not create any objects.
308 } _CFXMLPlistParseInfo
;
310 CF_PRIVATE CFTypeRef
__CFCreateOldStylePropertyListOrStringsFile(CFAllocatorRef allocator
, CFDataRef xmlData
, CFStringRef originalString
, CFStringEncoding guessedEncoding
, CFOptionFlags option
, CFErrorRef
*outError
,CFPropertyListFormat
*format
);
312 CF_INLINE
void __CFPListRelease(CFTypeRef cf
, CFAllocatorRef allocator
) {
313 if (cf
&& !(0)) CFRelease(cf
);
317 // The following set of _plist... functions append various things to a mutable data which is in UTF8 encoding. These are pretty general. Assumption is call characters and CFStrings can be converted to UTF8 and appeneded.
319 // Null-terminated, ASCII or UTF8 string
321 static void _plistAppendUTF8CString(CFMutableDataRef mData
, const char *cString
) {
322 CFDataAppendBytes (mData
, (const UInt8
*)cString
, strlen(cString
));
327 static void _plistAppendCharacters(CFMutableDataRef mData
, const UniChar
*chars
, CFIndex length
) {
330 do { // Flush out ASCII chars, BUFLEN at a time
332 UInt8 buf
[BUFLEN
], *bufPtr
= buf
;
334 while (cnt
< length
&& (cnt
- curLoc
< BUFLEN
) && (chars
[cnt
] < 128)) *bufPtr
++ = (UInt8
)(chars
[cnt
++]);
335 if (cnt
> curLoc
) { // Flush any ASCII bytes
336 CFDataAppendBytes(mData
, buf
, cnt
- curLoc
);
339 } while (curLoc
< length
&& (chars
[curLoc
] < 128)); // We will exit out of here when we run out of chars or hit a non-ASCII char
341 if (curLoc
< length
) { // Now deal with non-ASCII chars
342 CFDataRef data
= NULL
;
343 CFStringRef str
= NULL
;
344 if ((str
= CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault
, chars
+ curLoc
, length
- curLoc
, kCFAllocatorNull
))) {
345 if ((data
= CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
, 0))) {
346 CFDataAppendBytes (mData
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
351 CFAssert1(str
&& data
, __kCFLogAssertion
, "%s(): Error writing plist", __PRETTY_FUNCTION__
);
357 static void _plistAppendString(CFMutableDataRef mData
, CFStringRef str
) {
358 const UniChar
*chars
;
361 if ((chars
= CFStringGetCharactersPtr(str
))) {
362 _plistAppendCharacters(mData
, chars
, CFStringGetLength(str
));
363 } else if ((cStr
= CFStringGetCStringPtr(str
, kCFStringEncodingASCII
)) || (cStr
= CFStringGetCStringPtr(str
, kCFStringEncodingUTF8
))) {
364 _plistAppendUTF8CString(mData
, cStr
);
365 } else if ((data
= CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
, 0))) {
366 CFDataAppendBytes (mData
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
369 CFAssert1(TRUE
, __kCFLogAssertion
, "%s(): Error in plist writing", __PRETTY_FUNCTION__
);
374 // Append CFString-style format + arguments
376 static void _plistAppendFormat(CFMutableDataRef mData
, CFStringRef format
, ...) {
380 va_start(argList
, format
);
381 fStr
= CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault
, NULL
, format
, argList
);
384 CFAssert1(fStr
, __kCFLogAssertion
, "%s(): Error writing plist", __PRETTY_FUNCTION__
);
385 _plistAppendString(mData
, fStr
);
391 static void _appendIndents(CFIndex numIndents
, CFMutableDataRef str
) {
393 static const UniChar tabs
[NUMTABS
] = {'\t','\t','\t','\t'};
394 for (; numIndents
> 0; numIndents
-= NUMTABS
) _plistAppendCharacters(str
, tabs
, (numIndents
>= NUMTABS
) ? NUMTABS
: numIndents
);
397 /* Append the escaped version of origStr to mStr.
399 static void _appendEscapedString(CFStringRef origStr
, CFMutableDataRef mStr
) {
401 CFIndex i
, length
= CFStringGetLength(origStr
);
403 UniChar buf
[BUFSIZE
];
404 CFStringInlineBuffer inlineBuffer
;
406 CFStringInitInlineBuffer(origStr
, &inlineBuffer
, CFRangeMake(0, length
));
408 for (i
= 0; i
< length
; i
++) {
409 UniChar ch
= __CFStringGetCharacterFromInlineBufferQuick(&inlineBuffer
, i
);
410 if (CFStringIsSurrogateHighCharacter(ch
) && (bufCnt
+ 2 >= BUFSIZE
)) {
411 // flush the buffer first so we have room for a low/high pair and do not split them
412 _plistAppendCharacters(mStr
, buf
, bufCnt
);
418 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
420 _plistAppendUTF8CString(mStr
, "<");
423 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
425 _plistAppendUTF8CString(mStr
, ">");
428 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
430 _plistAppendUTF8CString(mStr
, "&");
434 if (bufCnt
== BUFSIZE
) {
435 _plistAppendCharacters(mStr
, buf
, bufCnt
);
441 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
446 /* Base-64 encoding/decoding */
448 /* The base-64 encoding packs three 8-bit bytes into four 7-bit ASCII
449 * characters. If the number of bytes in the original data isn't divisable
450 * by three, "=" characters are used to pad the encoded data. The complete
451 * set of characters used in base-64 are:
461 // Write the inputData to the mData using Base 64 encoding
463 static void _XMLPlistAppendDataUsingBase64(CFMutableDataRef mData
, CFDataRef inputData
, CFIndex indent
) {
464 static const char __CFPLDataEncodeTable
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
465 #define MAXLINELEN 76
466 char buf
[MAXLINELEN
+ 4 + 2]; // For the slop and carriage return and terminating NULL
468 const uint8_t *bytes
= CFDataGetBytePtr(inputData
);
469 CFIndex length
= CFDataGetLength(inputData
);
473 if (indent
> 8) indent
= 8; // refuse to indent more than 64 characters
475 pos
= 0; // position within buf
477 for (i
= 0, p
= bytes
; i
< length
; i
++, p
++) {
478 /* 3 bytes are encoded as 4 */
481 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[0] >> 2) & 0x3f)];
484 buf
[pos
++] = __CFPLDataEncodeTable
[ ((((p
[-1] << 8) | p
[0]) >> 4) & 0x3f)];
487 buf
[pos
++] = __CFPLDataEncodeTable
[ ((((p
[-1] << 8) | p
[0]) >> 6) & 0x3f)];
488 buf
[pos
++] = __CFPLDataEncodeTable
[ (p
[0] & 0x3f)];
491 /* Flush the line out every 76 (or fewer) chars --- indents count against the line length*/
492 if (pos
>= MAXLINELEN
- 8 * indent
) {
495 _appendIndents(indent
, mData
);
496 _plistAppendUTF8CString(mData
, buf
);
505 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[-1] << 4) & 0x30)];
510 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[-1] << 2) & 0x3c)];
518 _appendIndents(indent
, mData
);
519 _plistAppendUTF8CString(mData
, buf
);
523 extern CFStringRef
__CFNumberCopyFormattingDescriptionAsFloat64(CFTypeRef cf
);
525 static void _CFAppendXML0(CFTypeRef object
, UInt32 indentation
, CFMutableDataRef xmlString
) {
526 UInt32 typeID
= CFGetTypeID(object
);
527 _appendIndents(indentation
, xmlString
);
528 if (typeID
== stringtype
) {
529 _plistAppendUTF8CString(xmlString
, "<");
530 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[STRING_IX
], STRING_TAG_LENGTH
);
531 _plistAppendUTF8CString(xmlString
, ">");
532 _appendEscapedString((CFStringRef
)object
, xmlString
);
533 _plistAppendUTF8CString(xmlString
, "</");
534 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[STRING_IX
], STRING_TAG_LENGTH
);
535 _plistAppendUTF8CString(xmlString
, ">\n");
536 } else if (typeID
== arraytype
) {
537 UInt32 i
, count
= CFArrayGetCount((CFArrayRef
)object
);
539 _plistAppendUTF8CString(xmlString
, "<");
540 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
541 _plistAppendUTF8CString(xmlString
, "/>\n");
544 _plistAppendUTF8CString(xmlString
, "<");
545 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
546 _plistAppendUTF8CString(xmlString
, ">\n");
547 for (i
= 0; i
< count
; i
++) {
548 _CFAppendXML0(CFArrayGetValueAtIndex((CFArrayRef
)object
, i
), indentation
+1, xmlString
);
550 _appendIndents(indentation
, xmlString
);
551 _plistAppendUTF8CString(xmlString
, "</");
552 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
553 _plistAppendUTF8CString(xmlString
, ">\n");
554 } else if (typeID
== dicttype
) {
555 UInt32 i
, count
= CFDictionaryGetCount((CFDictionaryRef
)object
);
556 CFMutableArrayRef keyArray
;
558 _plistAppendUTF8CString(xmlString
, "<");
559 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DICT_IX
], DICT_TAG_LENGTH
);
560 _plistAppendUTF8CString(xmlString
, "/>\n");
563 _plistAppendUTF8CString(xmlString
, "<");
564 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DICT_IX
], DICT_TAG_LENGTH
);
565 _plistAppendUTF8CString(xmlString
, ">\n");
566 new_cftype_array(keys
, count
);
567 CFDictionaryGetKeysAndValues((CFDictionaryRef
)object
, keys
, NULL
);
568 keyArray
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, count
, &kCFTypeArrayCallBacks
);
569 CFArrayReplaceValues(keyArray
, CFRangeMake(0, 0), keys
, count
);
570 CFArraySortValues(keyArray
, CFRangeMake(0, count
), (CFComparatorFunction
)CFStringCompare
, NULL
);
571 CFArrayGetValues(keyArray
, CFRangeMake(0, count
), keys
);
573 for (i
= 0; i
< count
; i
++) {
574 CFTypeRef key
= keys
[i
];
575 _appendIndents(indentation
+1, xmlString
);
576 _plistAppendUTF8CString(xmlString
, "<");
577 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[KEY_IX
], KEY_TAG_LENGTH
);
578 _plistAppendUTF8CString(xmlString
, ">");
579 _appendEscapedString((CFStringRef
)key
, xmlString
);
580 _plistAppendUTF8CString(xmlString
, "</");
581 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[KEY_IX
], KEY_TAG_LENGTH
);
582 _plistAppendUTF8CString(xmlString
, ">\n");
583 _CFAppendXML0(CFDictionaryGetValue((CFDictionaryRef
)object
, key
), indentation
+1, xmlString
);
585 free_cftype_array(keys
);
586 _appendIndents(indentation
, xmlString
);
587 _plistAppendUTF8CString(xmlString
, "</");
588 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DICT_IX
], DICT_TAG_LENGTH
);
589 _plistAppendUTF8CString(xmlString
, ">\n");
590 } else if (typeID
== datatype
) {
591 _plistAppendUTF8CString(xmlString
, "<");
592 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DATA_IX
], DATA_TAG_LENGTH
);
593 _plistAppendUTF8CString(xmlString
, ">\n");
594 _XMLPlistAppendDataUsingBase64(xmlString
, (CFDataRef
)object
, indentation
);
595 _appendIndents(indentation
, xmlString
);
596 _plistAppendUTF8CString(xmlString
, "</");
597 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DATA_IX
], DATA_TAG_LENGTH
);
598 _plistAppendUTF8CString(xmlString
, ">\n");
599 } else if (typeID
== datetype
) {
600 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
601 int32_t y
= 0, M
= 0, d
= 0, H
= 0, m
= 0, s
= 0;
602 CFAbsoluteTime at
= CFDateGetAbsoluteTime((CFDateRef
)object
);
604 // Alternative to the CFAbsoluteTimeGetGregorianDate() code which works well
607 tv
.tv_sec
= floor(at
+ kCFAbsoluteTimeIntervalSince1970
);
608 gmtime_r(&tv
.tv_sec
, &mine
);
609 y
= mine
.tm_year
+ 1900;
617 #pragma GCC diagnostic push
618 #pragma GCC diagnostic ignored "-Wdeprecated"
619 CFGregorianDate date
= CFAbsoluteTimeGetGregorianDate(at
, NULL
);
620 #pragma GCC diagnostic pop
623 if (date
.year
!= y
|| date
.month
!= M
|| date
.day
!= d
|| date
.hour
!= H
|| date
.minute
!= m
|| (int32_t)date
.second
!= s
) {
624 CFLog(4, CFSTR("DATE ERROR {%d, %d, %d, %d, %d, %d} != {%d, %d, %d, %d, %d, %d}\n"), (int)date
.year
, (int)date
.month
, (int)date
.day
, (int)date
.hour
, (int)date
.minute
, (int32_t)date
.second
, y
, M
, d
, H
, m
, s
);
632 s
= (int32_t)date
.second
;
634 _plistAppendUTF8CString(xmlString
, "<");
635 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DATE_IX
], DATE_TAG_LENGTH
);
636 _plistAppendUTF8CString(xmlString
, ">");
637 _plistAppendFormat(xmlString
, CFSTR("%04d-%02d-%02dT%02d:%02d:%02dZ"), y
, M
, d
, H
, m
, s
);
638 _plistAppendUTF8CString(xmlString
, "</");
639 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DATE_IX
], DATE_TAG_LENGTH
);
640 _plistAppendUTF8CString(xmlString
, ">\n");
641 } else if (typeID
== numbertype
) {
642 if (CFNumberIsFloatType((CFNumberRef
)object
)) {
643 _plistAppendUTF8CString(xmlString
, "<");
644 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[REAL_IX
], REAL_TAG_LENGTH
);
645 _plistAppendUTF8CString(xmlString
, ">");
646 CFStringRef s
= __CFNumberCopyFormattingDescriptionAsFloat64(object
);
647 _plistAppendString(xmlString
, s
);
649 _plistAppendUTF8CString(xmlString
, "</");
650 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[REAL_IX
], REAL_TAG_LENGTH
);
651 _plistAppendUTF8CString(xmlString
, ">\n");
653 _plistAppendUTF8CString(xmlString
, "<");
654 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
655 _plistAppendUTF8CString(xmlString
, ">");
657 _plistAppendFormat(xmlString
, CFSTR("%@"), object
);
659 _plistAppendUTF8CString(xmlString
, "</");
660 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
661 _plistAppendUTF8CString(xmlString
, ">\n");
663 } else if (typeID
== booltype
) {
664 if (CFBooleanGetValue((CFBooleanRef
)object
)) {
665 _plistAppendUTF8CString(xmlString
, "<");
666 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[TRUE_IX
], TRUE_TAG_LENGTH
);
667 _plistAppendUTF8CString(xmlString
, "/>\n");
669 _plistAppendUTF8CString(xmlString
, "<");
670 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[FALSE_IX
], FALSE_TAG_LENGTH
);
671 _plistAppendUTF8CString(xmlString
, "/>\n");
676 static void _CFGenerateXMLPropertyListToData(CFMutableDataRef xml
, CFTypeRef propertyList
) {
677 _plistAppendUTF8CString(xml
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE ");
678 _plistAppendCharacters(xml
, CFXMLPlistTagsUnicode
[PLIST_IX
], PLIST_TAG_LENGTH
);
679 _plistAppendUTF8CString(xml
, " PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<");
680 _plistAppendCharacters(xml
, CFXMLPlistTagsUnicode
[PLIST_IX
], PLIST_TAG_LENGTH
);
681 _plistAppendUTF8CString(xml
, " version=\"1.0\">\n");
683 _CFAppendXML0(propertyList
, 0, xml
);
685 _plistAppendUTF8CString(xml
, "</");
686 _plistAppendCharacters(xml
, CFXMLPlistTagsUnicode
[PLIST_IX
], PLIST_TAG_LENGTH
);
687 _plistAppendUTF8CString(xml
, ">\n");
690 // ========================================================================
692 #pragma mark Exported Creation Functions
694 CFDataRef
_CFPropertyListCreateXMLData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, Boolean checkValidPlist
) {
696 CFMutableDataRef xml
;
697 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
698 if (checkValidPlist
&& !CFPropertyListIsValid(propertyList
, kCFPropertyListXMLFormat_v1_0
)) {
699 __CFAssertIsPList(propertyList
);
702 xml
= CFDataCreateMutable(allocator
, 0);
703 _CFGenerateXMLPropertyListToData(xml
, propertyList
);
707 CFDataRef
CFPropertyListCreateXMLData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
) {
708 return _CFPropertyListCreateXMLData(allocator
, propertyList
, true);
711 CF_EXPORT CFDataRef
_CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator
, CFPropertyListRef propertyList
) {
712 return _CFPropertyListCreateXMLData(allocator
, propertyList
, false);
715 Boolean
CFPropertyListIsValid(CFPropertyListRef plist
, CFPropertyListFormat format
) {
717 CFAssert1(plist
!= NULL
, __kCFLogAssertion
, "%s(): NULL is not a property list", __PRETTY_FUNCTION__
);
718 CFMutableSetRef set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
719 CFStringRef error
= NULL
;
720 bool result
= __CFPropertyListIsValidAux(plist
, true, set
, format
, &error
);
723 CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListIsValid(): %@"), error
);
731 // ========================================================================
733 #pragma mark Reading Plists
736 // ------------------------- Reading plists ------------------
739 static void skipInlineDTD(_CFXMLPlistParseInfo
*pInfo
);
740 static Boolean
parseXMLElement(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
, CFTypeRef
*out
);
742 // warning: doesn't have a good idea of Unicode line separators
743 static UInt32
lineNumber(_CFXMLPlistParseInfo
*pInfo
) {
744 const char *p
= pInfo
->begin
;
746 while (p
< pInfo
->curr
) {
749 if (*(p
+ 1) == '\n')
751 } else if (*p
== '\n') {
759 // warning: doesn't have a good idea of Unicode white space
760 CF_INLINE
void skipWhitespace(_CFXMLPlistParseInfo
*pInfo
) {
761 while (pInfo
->curr
< pInfo
->end
) {
762 switch (*(pInfo
->curr
)) {
775 /* All of these advance to the end of the given construct and return a pointer to the first character beyond the construct. If the construct doesn't parse properly, NULL is returned. */
777 // pInfo should be just past "<!--"
778 static void skipXMLComment(_CFXMLPlistParseInfo
*pInfo
) {
779 const char *p
= pInfo
->curr
;
780 const char *end
= pInfo
->end
- 3; // Need at least 3 characters to compare against
782 if (*p
== '-' && *(p
+1) == '-' && *(p
+2) == '>') {
788 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unterminated comment started on line %d"), lineNumber(pInfo
));
791 // pInfo should be set to the first character after "<?"
792 static void skipXMLProcessingInstruction(_CFXMLPlistParseInfo
*pInfo
) {
793 const char *begin
= pInfo
->curr
, *end
= pInfo
->end
- 2; // Looking for "?>" so we need at least 2 characters
794 while (pInfo
->curr
< end
) {
795 if (*(pInfo
->curr
) == '?' && *(pInfo
->curr
+1) == '>') {
802 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing the processing instruction begun on line %d"), lineNumber(pInfo
));
805 // first character should be immediately after the "<!"
806 static void skipDTD(_CFXMLPlistParseInfo
*pInfo
) {
807 // First pass "DOCTYPE"
808 if (pInfo
->end
- pInfo
->curr
< DOCTYPE_TAG_LENGTH
|| memcmp(pInfo
->curr
, CFXMLPlistTags
[DOCTYPE_IX
], DOCTYPE_TAG_LENGTH
)) {
809 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Malformed DTD on line %d"), lineNumber(pInfo
));
812 pInfo
->curr
+= DOCTYPE_TAG_LENGTH
;
813 skipWhitespace(pInfo
);
815 // Look for either the beginning of a complex DTD or the end of the DOCTYPE structure
816 while (pInfo
->curr
< pInfo
->end
) {
817 char ch
= *(pInfo
->curr
);
818 if (ch
== '[') break; // inline DTD
819 if (ch
== '>') { // End of the DTD
825 if (pInfo
->curr
== pInfo
->end
) {
826 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing DTD"));
830 // *Sigh* Must parse in-line DTD
831 skipInlineDTD(pInfo
);
832 if (pInfo
->error
) return;
833 skipWhitespace(pInfo
);
834 if (pInfo
->error
) return;
835 if (pInfo
->curr
< pInfo
->end
) {
836 if (*(pInfo
->curr
) == '>') {
839 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while parsing DTD"), *(pInfo
->curr
), lineNumber(pInfo
));
842 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing DTD"));
846 static void skipPERef(_CFXMLPlistParseInfo
*pInfo
) {
847 const char *p
= pInfo
->curr
;
848 while (p
< pInfo
->end
) {
855 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing percent-escape sequence begun on line %d"), lineNumber(pInfo
));
858 // First character should be just past '['
859 static void skipInlineDTD(_CFXMLPlistParseInfo
*pInfo
) {
860 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
862 skipWhitespace(pInfo
);
867 } else if (ch
== '<') {
869 if (pInfo
->curr
>= pInfo
->end
) {
870 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
876 skipXMLProcessingInstruction(pInfo
);
877 } else if (ch
== '!') {
878 if (pInfo
->curr
+ 2 < pInfo
->end
&& (*(pInfo
->curr
+1) == '-' && *(pInfo
->curr
+2) == '-')) {
880 skipXMLComment(pInfo
);
882 // Skip the myriad of DTD declarations of the form "<!string" ... ">"
883 pInfo
->curr
++; // Past both '<' and '!'
884 while (pInfo
->curr
< pInfo
->end
) {
885 if (*(pInfo
->curr
) == '>') break;
888 if (*(pInfo
->curr
) != '>') {
889 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
895 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch
, lineNumber(pInfo
));
898 } else if (ch
== ']') {
902 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch
, lineNumber(pInfo
));
907 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
911 // content ::== (element | CharData | Reference | CDSect | PI | Comment)*
912 // In the context of a plist, CharData, Reference and CDSect are not legal (they all resolve to strings). Skipping whitespace, then, the next character should be '<'. From there, we figure out which of the three remaining cases we have (element, PI, or Comment).
913 static Boolean
getContentObject(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
, CFTypeRef
*out
) {
914 if (isKey
) *isKey
= false;
915 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
916 skipWhitespace(pInfo
);
917 if (pInfo
->curr
>= pInfo
->end
) {
918 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
921 if (*(pInfo
->curr
) != '<') {
922 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while looking for open tag"), *(pInfo
->curr
), lineNumber(pInfo
));
926 if (pInfo
->curr
>= pInfo
->end
) {
927 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
930 switch (*(pInfo
->curr
)) {
932 // Processing instruction
933 skipXMLProcessingInstruction(pInfo
);
936 // Could be a comment
937 if (pInfo
->curr
+2 >= pInfo
->end
) {
938 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
941 if (*(pInfo
->curr
+1) == '-' && *(pInfo
->curr
+2) == '-') {
943 skipXMLComment(pInfo
);
945 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
950 // Whoops! Looks like we got to the end tag for the element whose content we're parsing
951 pInfo
->curr
--; // Back off to the '<'
954 // Should be an element
955 return parseXMLElement(pInfo
, isKey
, out
);
958 // Do not set the error string here; if it wasn't already set by one of the recursive parsing calls, the caller will quickly detect the failure (b/c pInfo->curr >= pInfo->end) and provide a more useful one of the form "end tag for <blah> not found"
962 static void parseCDSect_pl(_CFXMLPlistParseInfo
*pInfo
, CFMutableDataRef stringData
) {
963 const char *end
, *begin
;
964 if (pInfo
->end
- pInfo
->curr
< CDSECT_TAG_LENGTH
) {
965 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
968 if (memcmp(pInfo
->curr
, CFXMLPlistTags
[CDSECT_IX
], CDSECT_TAG_LENGTH
)) {
969 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered improper CDATA opening at line %d"), lineNumber(pInfo
));
972 pInfo
->curr
+= CDSECT_TAG_LENGTH
;
973 begin
= pInfo
->curr
; // Marks the first character of the CDATA content
974 end
= pInfo
->end
-2; // So we can safely look 2 characters beyond p
975 while (pInfo
->curr
< end
) {
976 if (*(pInfo
->curr
) == ']' && *(pInfo
->curr
+1) == ']' && *(pInfo
->curr
+2) == '>') {
978 CFDataAppendBytes(stringData
, (const UInt8
*)begin
, pInfo
->curr
-begin
);
984 // Never found the end mark
986 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not find end of CDATA started on line %d"), lineNumber(pInfo
));
989 // Only legal references are {lt, gt, amp, apos, quote, #ddd, #xAAA}
990 static void parseEntityReference_pl(_CFXMLPlistParseInfo
*pInfo
, CFMutableDataRef stringData
) {
992 pInfo
->curr
++; // move past the '&';
993 len
= pInfo
->end
- pInfo
->curr
; // how many bytes we can safely scan
995 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1000 switch (*(pInfo
->curr
)) {
1002 if (len
>= 3 && *(pInfo
->curr
+1) == 't' && *(pInfo
->curr
+2) == ';') {
1007 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1010 if (len
>= 3 && *(pInfo
->curr
+1) == 't' && *(pInfo
->curr
+2) == ';') {
1015 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1017 case 'a': // "apos" or "amp"
1018 if (len
< 4) { // Not enough characters for either conversion
1019 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1022 if (*(pInfo
->curr
+1) == 'm') {
1024 if (*(pInfo
->curr
+2) == 'p' && *(pInfo
->curr
+3) == ';') {
1029 } else if (*(pInfo
->curr
+1) == 'p') {
1031 if (len
> 4 && *(pInfo
->curr
+2) == 'o' && *(pInfo
->curr
+3) == 's' && *(pInfo
->curr
+4) == ';') {
1037 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1039 case 'q': // "quote"
1040 if (len
>= 5 && *(pInfo
->curr
+1) == 'u' && *(pInfo
->curr
+2) == 'o' && *(pInfo
->curr
+3) == 't' && *(pInfo
->curr
+4) == ';') {
1045 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1050 Boolean isHex
= false;
1051 if ( len
< 4) { // Not enough characters to make it all fit! Need at least "&#d;"
1052 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1056 if (*(pInfo
->curr
) == 'x') {
1060 while (pInfo
->curr
< pInfo
->end
) {
1061 ch
= *(pInfo
->curr
);
1064 // The value in num always refers to the unicode code point. We'll have to convert since the calling function expects UTF8 data.
1065 CFStringRef oneChar
= CFStringCreateWithBytes(pInfo
->allocator
, (const uint8_t *)&num
, 2, kCFStringEncodingUnicode
, NO
);
1066 uint8_t tmpBuf
[6]; // max of 6 bytes for UTF8
1067 CFIndex tmpBufLength
= 0;
1068 CFStringGetBytes(oneChar
, CFRangeMake(0, 1), kCFStringEncodingUTF8
, 0, NO
, tmpBuf
, 6, &tmpBufLength
);
1069 CFDataAppendBytes(stringData
, tmpBuf
, tmpBufLength
);
1070 __CFPListRelease(oneChar
, pInfo
->allocator
);
1073 if (!isHex
) num
= num
*10;
1074 else num
= num
<< 4;
1075 if (ch
<= '9' && ch
>= '0') {
1077 } else if (!isHex
) {
1078 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c at line %d while parsing data"), ch
, lineNumber(pInfo
));
1080 } else if (ch
>= 'a' && ch
<= 'f') {
1081 num
+= 10 + (ch
- 'a');
1082 } else if (ch
>= 'A' && ch
<= 'F') {
1083 num
+= 10 + (ch
- 'A');
1085 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c at line %d while parsing data"), ch
, lineNumber(pInfo
));
1089 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1093 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1096 CFDataAppendBytes(stringData
, (const UInt8
*)&ch
, 1);
1099 static void _createStringMap(_CFXMLPlistParseInfo
*pInfo
) {
1100 pInfo
->stringTrie
= CFBurstTrieCreate();
1101 pInfo
->stringCache
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
1104 static void _cleanupStringMap(_CFXMLPlistParseInfo
*pInfo
) {
1105 CFBurstTrieRelease(pInfo
->stringTrie
);
1106 CFRelease(pInfo
->stringCache
);
1107 pInfo
->stringTrie
= NULL
;
1108 pInfo
->stringCache
= NULL
;
1111 static CFStringRef
_createUniqueStringWithUTF8Bytes(_CFXMLPlistParseInfo
*pInfo
, const char *base
, CFIndex length
) {
1112 if (length
== 0) return !(0) ? (CFStringRef
)CFRetain(CFSTR("")) : CFSTR("");
1114 CFStringRef result
= NULL
;
1115 uint32_t payload
= 0;
1116 Boolean uniqued
= CFBurstTrieContainsUTF8String(pInfo
->stringTrie
, (UInt8
*)base
, length
, &payload
);
1117 if (uniqued
&& payload
> 0) {
1118 // The index is at payload - 1 (see below).
1119 result
= (CFStringRef
)CFArrayGetValueAtIndex(pInfo
->stringCache
, (CFIndex
)payload
- 1);
1122 result
= CFStringCreateWithBytes(pInfo
->allocator
, (const UInt8
*)base
, length
, kCFStringEncodingUTF8
, NO
);
1123 if (!result
) return NULL
;
1124 // Payload must be >0, so the actual index of the value is at payload - 1
1125 // We also get add to the array after we make sure that CFBurstTrieAddUTF8String succeeds (it can fail, if the string is too large, for example)
1126 payload
= CFArrayGetCount(pInfo
->stringCache
) + 1;
1127 Boolean didAddToBurstTrie
= CFBurstTrieAddUTF8String(pInfo
->stringTrie
, (UInt8
*)base
, length
, payload
);
1128 if (didAddToBurstTrie
) {
1129 CFArrayAppendValue(pInfo
->stringCache
, result
);
1135 // String could be comprised of characters, CDSects, or references to one of the "well-known" entities ('<', '>', '&', ''', '"')
1136 static Boolean
parseStringTag(_CFXMLPlistParseInfo
*pInfo
, CFStringRef
*out
) {
1137 const char *mark
= pInfo
->curr
;
1138 CFMutableDataRef stringData
= NULL
;
1139 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
1140 char ch
= *(pInfo
->curr
);
1142 if (pInfo
->curr
+ 1 >= pInfo
->end
) break;
1143 // Could be a CDSect; could be the end of the string
1144 if (*(pInfo
->curr
+1) != '!') break; // End of the string
1145 if (!stringData
) stringData
= CFDataCreateMutable(pInfo
->allocator
, 0);
1146 CFDataAppendBytes(stringData
, (const UInt8
*)mark
, pInfo
->curr
- mark
);
1147 parseCDSect_pl(pInfo
, stringData
); // TODO: move to return boolean
1149 } else if (ch
== '&') {
1150 if (!stringData
) stringData
= CFDataCreateMutable(pInfo
->allocator
, 0);
1151 CFDataAppendBytes(stringData
, (const UInt8
*)mark
, pInfo
->curr
- mark
);
1152 parseEntityReference_pl(pInfo
, stringData
); // TODO: move to return boolean
1160 __CFPListRelease(stringData
, pInfo
->allocator
);
1168 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
1169 CFStringRef s
= _createUniqueStringWithUTF8Bytes(pInfo
, mark
, pInfo
->curr
- mark
);
1171 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unable to convert string to correct encoding"));
1176 CFStringRef s
= CFStringCreateWithBytes(pInfo
->allocator
, (const UInt8
*)mark
, pInfo
->curr
- mark
, kCFStringEncodingUTF8
, NO
);
1178 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unable to convert string to correct encoding"));
1181 *out
= CFStringCreateMutableCopy(pInfo
->allocator
, 0, s
);
1182 __CFPListRelease(s
, pInfo
->allocator
);
1190 CFDataAppendBytes(stringData
, (const UInt8
*)mark
, pInfo
->curr
- mark
);
1191 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
1192 CFStringRef s
= _createUniqueStringWithUTF8Bytes(pInfo
, (const char *)CFDataGetBytePtr(stringData
), CFDataGetLength(stringData
));
1194 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unable to convert string to correct encoding"));
1199 CFStringRef s
= CFStringCreateWithBytes(pInfo
->allocator
, (const UInt8
*)CFDataGetBytePtr(stringData
), CFDataGetLength(stringData
), kCFStringEncodingUTF8
, NO
);
1201 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unable to convert string to correct encoding"));
1204 *out
= CFStringCreateMutableCopy(pInfo
->allocator
, 0, s
);
1205 __CFPListRelease(s
, pInfo
->allocator
);
1208 __CFPListRelease(stringData
, pInfo
->allocator
);
1213 static Boolean
checkForCloseTag(_CFXMLPlistParseInfo
*pInfo
, const char *tag
, CFIndex tagLen
) {
1214 if (pInfo
->end
- pInfo
->curr
< tagLen
+ 3) {
1215 if (!pInfo
->error
) {
1216 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1220 if (*(pInfo
->curr
) != '<' || *(++pInfo
->curr
) != '/') {
1221 if (!pInfo
->error
) {
1222 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while looking for close tag"), *(pInfo
->curr
), lineNumber(pInfo
));
1227 if (memcmp(pInfo
->curr
, tag
, tagLen
)) {
1228 CFStringRef str
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, (const UInt8
*)tag
, tagLen
, kCFStringEncodingUTF8
, NO
);
1229 if (!pInfo
->error
) {
1230 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Close tag on line %d does not match open tag %@"), lineNumber(pInfo
), str
);
1235 pInfo
->curr
+= tagLen
;
1236 skipWhitespace(pInfo
);
1237 if (pInfo
->curr
== pInfo
->end
) {
1238 if (!pInfo
->error
) {
1239 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1243 if (*(pInfo
->curr
) != '>') {
1244 if (!pInfo
->error
) {
1245 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while looking for close tag"), *(pInfo
->curr
), lineNumber(pInfo
));
1253 // pInfo should be set to the first content character of the <plist>
1254 static Boolean
parsePListTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1255 CFTypeRef result
= NULL
;
1256 if (!getContentObject(pInfo
, NULL
, &result
)) {
1257 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty plist tag"));
1260 const char *save
= pInfo
->curr
; // Save this in case the next step fails
1261 CFTypeRef tmp
= NULL
;
1262 if (getContentObject(pInfo
, NULL
, &tmp
)) {
1263 // Got an extra object
1264 __CFPListRelease(tmp
, pInfo
->allocator
);
1265 __CFPListRelease(result
, pInfo
->allocator
);
1267 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected element at line %d (plist can only include one object)"), lineNumber(pInfo
));
1271 // Parse failed catastrophically
1272 __CFPListRelease(result
, pInfo
->allocator
);
1275 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
)) {
1276 __CFPListRelease(result
, pInfo
->allocator
);
1283 static int allowImmutableCollections
= -1;
1285 static void checkImmutableCollections(void) {
1286 allowImmutableCollections
= (NULL
== __CFgetenv("CFPropertyListAllowImmutableCollections")) ? 0 : 1;
1289 // This converts the input value, a set of strings, into the form that's efficient for using during recursive decent parsing, a set of arrays
1290 static CFSetRef
createTopLevelKeypaths(CFAllocatorRef allocator
, CFSetRef keyPaths
) {
1291 if (!keyPaths
) return NULL
;
1293 CFIndex count
= CFSetGetCount(keyPaths
);
1294 new_cftype_array(keyPathValues
, count
);
1295 CFSetGetValues(keyPaths
, keyPathValues
);
1296 CFMutableSetRef splitKeyPathSet
= CFSetCreateMutable(allocator
, count
, &kCFTypeSetCallBacks
);
1297 for (CFIndex i
= 0; i
< count
; i
++) {
1298 // Split each key path and add it to the split path set, which we will reference throughout parsing
1299 CFArrayRef split
= CFStringCreateArrayBySeparatingStrings(allocator
, (CFStringRef
)(keyPathValues
[i
]), CFSTR(":"));
1300 CFSetAddValue(splitKeyPathSet
, split
);
1301 __CFPListRelease(split
, allocator
);
1303 free_cftype_array(keyPathValues
);
1304 return splitKeyPathSet
;
1307 // This splits up the keypaths into the ones relevant for this level (of array or dictionary), and the ones for the next level (of array or dictionary)
1308 CF_PRIVATE
void __CFPropertyListCreateSplitKeypaths(CFAllocatorRef allocator
, CFSetRef currentKeys
, CFSetRef
*theseKeys
, CFSetRef
*nextKeys
) {
1309 if (!currentKeys
) { *theseKeys
= NULL
; *nextKeys
= NULL
; return; }
1311 CFIndex count
= CFSetGetCount(currentKeys
);
1313 // For each array in the current key path set, grab the item at the start of the list and put it into theseKeys. The rest of the array goes into nextKeys.
1314 CFMutableSetRef outTheseKeys
= NULL
;
1315 CFMutableSetRef outNextKeys
= NULL
;
1317 new_cftype_array(currentKeyPaths
, count
);
1318 CFSetGetValues(currentKeys
, currentKeyPaths
);
1319 for (CFIndex i
= 0; i
< count
; i
++) {
1320 CFArrayRef oneKeyPath
= (CFArrayRef
)currentKeyPaths
[i
];
1321 CFIndex keyPathCount
= CFArrayGetCount(oneKeyPath
);
1323 if (keyPathCount
> 0) {
1324 if (!outTheseKeys
) outTheseKeys
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
1326 CFSetAddValue(outTheseKeys
, CFArrayGetValueAtIndex(oneKeyPath
, 0));
1329 if (keyPathCount
> 1) {
1330 if (!outNextKeys
) outNextKeys
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
1332 // Create an array with values from 1 - end of list
1333 new_cftype_array(restOfKeys
, keyPathCount
- 1);
1334 CFArrayGetValues(oneKeyPath
, CFRangeMake(1, CFArrayGetCount(oneKeyPath
) - 1), restOfKeys
);
1335 CFArrayRef newNextKeys
= CFArrayCreate(allocator
, restOfKeys
, CFArrayGetCount(oneKeyPath
) - 1, &kCFTypeArrayCallBacks
);
1336 CFSetAddValue(outNextKeys
, newNextKeys
);
1337 __CFPListRelease(newNextKeys
, allocator
);
1338 free_cftype_array(restOfKeys
);
1343 *theseKeys
= outTheseKeys
;
1344 *nextKeys
= outNextKeys
;
1347 static Boolean
parseArrayTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1348 CFTypeRef tmp
= NULL
;
1351 Boolean result
= getContentObject(pInfo
, NULL
, &tmp
);
1354 // Shouldn't happen (if skipping, all content values should be null), but just in case
1355 __CFPListRelease(tmp
, pInfo
->allocator
);
1357 result
= getContentObject(pInfo
, NULL
, &tmp
);
1361 // getContentObject encountered a parse error
1364 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
)) {
1372 CFMutableArrayRef array
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
1376 CFSetRef oldKeyPaths
= pInfo
->keyPaths
;
1377 CFSetRef newKeyPaths
, keys
;
1378 __CFPropertyListCreateSplitKeypaths(pInfo
->allocator
, pInfo
->keyPaths
, &keys
, &newKeyPaths
);
1381 CFStringRef countString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("%ld"), count
);
1382 if (!CFSetContainsValue(keys
, countString
)) pInfo
->skip
= true;
1383 __CFPListRelease(countString
, pInfo
->allocator
);
1385 pInfo
->keyPaths
= newKeyPaths
;
1387 result
= getContentObject(pInfo
, NULL
, &tmp
);
1389 pInfo
->keyPaths
= oldKeyPaths
;
1390 pInfo
->skip
= false;
1395 CFArrayAppendValue(array
, tmp
);
1396 __CFPListRelease(tmp
, pInfo
->allocator
);
1400 // prep for getting next object
1401 CFStringRef countString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("%ld"), count
);
1402 if (!CFSetContainsValue(keys
, countString
)) pInfo
->skip
= true;
1403 __CFPListRelease(countString
, pInfo
->allocator
);
1405 pInfo
->keyPaths
= newKeyPaths
;
1407 result
= getContentObject(pInfo
, NULL
, &tmp
);
1409 // reset after getting object
1410 pInfo
->keyPaths
= oldKeyPaths
;
1411 pInfo
->skip
= false;
1416 __CFPListRelease(newKeyPaths
, pInfo
->allocator
);
1417 __CFPListRelease(keys
, pInfo
->allocator
);
1419 if (pInfo
->error
) { // getContentObject encountered a parse error
1420 __CFPListRelease(array
, pInfo
->allocator
);
1423 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
)) {
1424 __CFPListRelease(array
, pInfo
->allocator
);
1427 if (-1 == allowImmutableCollections
) {
1428 checkImmutableCollections();
1430 if (1 == allowImmutableCollections
) {
1431 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1432 CFArrayRef newArray
= CFArrayCreateCopy(pInfo
->allocator
, array
);
1433 __CFPListRelease(array
, pInfo
->allocator
);
1434 array
= (CFMutableArrayRef
)newArray
;
1441 static Boolean
parseDictTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1444 CFTypeRef key
= NULL
, value
= NULL
;
1447 result
= getContentObject(pInfo
, &gotKey
, &key
);
1450 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo
));
1453 result
= getContentObject(pInfo
, NULL
, &value
);
1455 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo
));
1458 // key and value should be null, but we'll release just in case here
1459 __CFPListRelease(key
, pInfo
->allocator
);
1461 __CFPListRelease(value
, pInfo
->allocator
);
1463 result
= getContentObject(pInfo
, &gotKey
, &key
);
1465 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
)) {
1473 CFSetRef oldKeyPaths
= pInfo
->keyPaths
;
1474 CFSetRef nextKeyPaths
, theseKeyPaths
;
1475 __CFPropertyListCreateSplitKeypaths(pInfo
->allocator
, pInfo
->keyPaths
, &theseKeyPaths
, &nextKeyPaths
);
1477 CFMutableDictionaryRef dict
= NULL
;
1479 result
= getContentObject(pInfo
, &gotKey
, &key
);
1480 while (result
&& key
) {
1482 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo
));
1483 __CFPListRelease(key
, pInfo
->allocator
);
1484 __CFPListRelease(nextKeyPaths
, pInfo
->allocator
);
1485 __CFPListRelease(theseKeyPaths
, pInfo
->allocator
);
1486 __CFPListRelease(dict
, pInfo
->allocator
);
1490 if (theseKeyPaths
) {
1491 if (!CFSetContainsValue(theseKeyPaths
, key
)) pInfo
->skip
= true;
1492 pInfo
->keyPaths
= nextKeyPaths
;
1494 result
= getContentObject(pInfo
, NULL
, &value
);
1495 if (theseKeyPaths
) {
1496 pInfo
->keyPaths
= oldKeyPaths
;
1497 pInfo
->skip
= false;
1501 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo
));
1502 __CFPListRelease(key
, pInfo
->allocator
);
1503 __CFPListRelease(nextKeyPaths
, pInfo
->allocator
);
1504 __CFPListRelease(theseKeyPaths
, pInfo
->allocator
);
1505 __CFPListRelease(dict
, pInfo
->allocator
);
1511 dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1512 _CFDictionarySetCapacity(dict
, 10);
1514 CFDictionarySetValue(dict
, key
, value
);
1517 __CFPListRelease(key
, pInfo
->allocator
);
1519 __CFPListRelease(value
, pInfo
->allocator
);
1522 result
= getContentObject(pInfo
, &gotKey
, &key
);
1525 __CFPListRelease(nextKeyPaths
, pInfo
->allocator
);
1526 __CFPListRelease(theseKeyPaths
, pInfo
->allocator
);
1528 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
)) {
1530 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1531 dict
= (CFMutableDictionaryRef
)CFDictionaryCreate(pInfo
->allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1533 dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1536 CFIndex cnt
= CFDictionaryGetCount(dict
);
1538 CFTypeRef val
= CFDictionaryGetValue(dict
, CFSTR("CF$UID"));
1539 if (val
&& CFGetTypeID(val
) == numbertype
) {
1542 CFNumberGetValue((CFNumberRef
)val
, kCFNumberSInt32Type
, &v
);
1543 uid
= (CFTypeRef
)_CFKeyedArchiverUIDCreate(pInfo
->allocator
, v
);
1544 __CFPListRelease(dict
, pInfo
->allocator
);
1549 if (-1 == allowImmutableCollections
) checkImmutableCollections();
1550 if (1 == allowImmutableCollections
) {
1551 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1552 CFDictionaryRef newDict
= CFDictionaryCreateCopy(pInfo
->allocator
, dict
);
1553 __CFPListRelease(dict
, pInfo
->allocator
);
1554 dict
= (CFMutableDictionaryRef
)newDict
;
1562 if (dict
) __CFPListRelease(dict
, pInfo
->allocator
);
1566 static Boolean
parseDataTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1567 const char *base
= pInfo
->curr
;
1568 static const signed char dataDecodeTable
[128] = {
1569 /* 000 */ -1, -1, -1, -1, -1, -1, -1, -1,
1570 /* 010 */ -1, -1, -1, -1, -1, -1, -1, -1,
1571 /* 020 */ -1, -1, -1, -1, -1, -1, -1, -1,
1572 /* 030 */ -1, -1, -1, -1, -1, -1, -1, -1,
1573 /* ' ' */ -1, -1, -1, -1, -1, -1, -1, -1,
1574 /* '(' */ -1, -1, -1, 62, -1, -1, -1, 63,
1575 /* '0' */ 52, 53, 54, 55, 56, 57, 58, 59,
1576 /* '8' */ 60, 61, -1, -1, -1, 0, -1, -1,
1577 /* '@' */ -1, 0, 1, 2, 3, 4, 5, 6,
1578 /* 'H' */ 7, 8, 9, 10, 11, 12, 13, 14,
1579 /* 'P' */ 15, 16, 17, 18, 19, 20, 21, 22,
1580 /* 'X' */ 23, 24, 25, -1, -1, -1, -1, -1,
1581 /* '`' */ -1, 26, 27, 28, 29, 30, 31, 32,
1582 /* 'h' */ 33, 34, 35, 36, 37, 38, 39, 40,
1583 /* 'p' */ 41, 42, 43, 44, 45, 46, 47, 48,
1584 /* 'x' */ 49, 50, 51, -1, -1, -1, -1, -1
1588 int tmpbuflen
= 256;
1589 uint8_t *tmpbuf
= pInfo
->skip
? NULL
: (uint8_t *)CFAllocatorAllocate(pInfo
->allocator
, tmpbuflen
, 0);
1594 for (; pInfo
->curr
< pInfo
->end
; pInfo
->curr
++) {
1595 signed char c
= *(pInfo
->curr
);
1601 } else if (!isspace(c
)) {
1604 if (dataDecodeTable
[c
] < 0)
1608 acc
+= dataDecodeTable
[c
];
1609 if (!pInfo
->skip
&& 0 == (cntr
& 0x3)) {
1610 if (tmpbuflen
<= tmpbufpos
+ 2) {
1611 if (tmpbuflen
< 256 * 1024) {
1613 } else if (tmpbuflen
< 16 * 1024 * 1024) {
1616 // once in this stage, this will be really slow
1617 // and really potentially fragment memory
1618 tmpbuflen
+= 256 * 1024;
1620 tmpbuf
= (uint8_t *)CFAllocatorReallocate(pInfo
->allocator
, tmpbuf
, tmpbuflen
, 0);
1621 if (!tmpbuf
) HALT
; // out of memory
1623 tmpbuf
[tmpbufpos
++] = (acc
>> 16) & 0xff;
1624 if (numeq
< 2) tmpbuf
[tmpbufpos
++] = (acc
>> 8) & 0xff;
1625 if (numeq
< 1) tmpbuf
[tmpbufpos
++] = acc
& 0xff;
1629 CFDataRef result
= NULL
;
1631 if (pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1632 result
= (CFDataRef
)CFDataCreateMutable(pInfo
->allocator
, 0);
1633 CFDataAppendBytes((CFMutableDataRef
)result
, tmpbuf
, tmpbufpos
);
1634 CFAllocatorDeallocate(pInfo
->allocator
, tmpbuf
);
1636 result
= CFDataCreateWithBytesNoCopy(pInfo
->allocator
, tmpbuf
, tmpbufpos
, pInfo
->allocator
);
1640 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not interpret <data> at line %d (should be base64-encoded)"), lineNumber(pInfo
));
1645 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
)) {
1649 __CFPListRelease(result
, pInfo
->allocator
);
1654 CF_INLINE Boolean
read2DigitNumber(_CFXMLPlistParseInfo
*pInfo
, int32_t *result
) {
1656 if (pInfo
->curr
+ 2 >= pInfo
->end
) {
1660 ch2
= *(pInfo
->curr
+ 1);
1662 if (!isdigit(ch1
) || !isdigit(ch2
)) {
1665 *result
= (ch1
- '0')*10 + (ch2
- '0');
1669 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
1670 static Boolean
parseDateTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1671 int32_t year
= 0, month
= 0, day
= 0, hour
= 0, minute
= 0, second
= 0;
1673 Boolean badForm
= false;
1674 Boolean yearIsNegative
= false;
1676 if (pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== '-') {
1677 yearIsNegative
= true;
1681 while (pInfo
->curr
< pInfo
->end
&& isdigit(*pInfo
->curr
)) {
1682 year
= 10*year
+ (*pInfo
->curr
) - '0';
1685 if (pInfo
->curr
>= pInfo
->end
|| *pInfo
->curr
!= '-') {
1691 if (!badForm
&& read2DigitNumber(pInfo
, &month
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== '-') {
1697 if (!badForm
&& read2DigitNumber(pInfo
, &day
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== 'T') {
1703 if (!badForm
&& read2DigitNumber(pInfo
, &hour
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== ':') {
1709 if (!badForm
&& read2DigitNumber(pInfo
, &minute
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== ':') {
1715 if (!badForm
&& read2DigitNumber(pInfo
, &num
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== 'Z') {
1722 if (badForm
|| !checkForCloseTag(pInfo
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
)) {
1723 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not interpret <date> at line %d"), lineNumber(pInfo
));
1727 CFAbsoluteTime at
= 0.0;
1730 { // alternative to CFGregorianDateGetAbsoluteTime() below; also, cheaper than CFCalendar would be;
1731 // clearly not thread-safe with that environment variable having to be set;
1732 // timegm() could be used instead of mktime(), on platforms which have it
1734 mine
.tm_year
= (yearIsNegative
? -year
: year
) - 1900;
1735 mine
.tm_mon
= month
- 1;
1737 mine
.tm_hour
= hour
;
1738 mine
.tm_min
= minute
;
1739 mine
.tm_sec
= second
;
1740 char *tz
= getenv("TZ");
1741 setenv("TZ", "", 1);
1743 at
= mktime(tm
) - kCFAbsoluteTimeIntervalSince1970
;
1745 setenv("TZ", tz
, 1);
1753 // See <rdar://problem/5052483> Revisit the CFGregorianDate -> CFCalendar change in CFPropertyList.c
1754 // for why we can't use CFCalendar
1755 #pragma GCC diagnostic push
1756 #pragma GCC diagnostic ignored "-Wdeprecated"
1757 CFGregorianDate date
= {yearIsNegative
? -year
: year
, month
, day
, hour
, minute
, second
};
1758 at
= CFGregorianDateGetAbsoluteTime(date
, NULL
);
1759 #pragma GCC diagnostic pop
1764 *out
= CFDateCreate(pInfo
->allocator
, at
);
1769 static Boolean
parseRealTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1770 CFStringRef str
= NULL
;
1771 if (!parseStringTag(pInfo
, &str
)) {
1772 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo
));
1776 CFNumberRef result
= NULL
;
1779 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("nan"), kCFCompareCaseInsensitive
)) result
= kCFNumberNaN
;
1780 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("+infinity"), kCFCompareCaseInsensitive
)) result
= kCFNumberPositiveInfinity
;
1781 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("-infinity"), kCFCompareCaseInsensitive
)) result
= kCFNumberNegativeInfinity
;
1782 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("infinity"), kCFCompareCaseInsensitive
)) result
= kCFNumberPositiveInfinity
;
1783 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("-inf"), kCFCompareCaseInsensitive
)) result
= kCFNumberNegativeInfinity
;
1784 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("inf"), kCFCompareCaseInsensitive
)) result
= kCFNumberPositiveInfinity
;
1785 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("+inf"), kCFCompareCaseInsensitive
)) result
= kCFNumberPositiveInfinity
;
1790 CFIndex len
= CFStringGetLength(str
);
1791 CFStringInlineBuffer buf
;
1792 CFStringInitInlineBuffer(str
, &buf
, CFRangeMake(0, len
));
1795 if (!__CFStringScanDouble(&buf
, NULL
, &idx
, &val
) || idx
!= len
) {
1796 __CFPListRelease(str
, pInfo
->allocator
);
1797 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered misformatted real on line %d"), lineNumber(pInfo
));
1800 result
= CFNumberCreate(pInfo
->allocator
, kCFNumberDoubleType
, &val
);
1804 __CFPListRelease(str
, pInfo
->allocator
);
1805 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) {
1809 __CFPListRelease(result
, pInfo
->allocator
);
1814 #define GET_CH if (pInfo->curr == pInfo->end) { \
1815 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Premature end of file after <integer> on line %d"), lineNumber(pInfo)); \
1826 kCFNumberSInt128Type
= 17
1829 CF_INLINE
bool isWhitespace(const char *utf8bytes
, const char *end
) {
1830 // Converted UTF-16 isWhitespace from CFString to UTF8 bytes to get full list of UTF8 whitespace
1852 // Except we consider some additional values from 0x0 to 0x21 and 0x7E to 0xA1 as whitespace, for compatability
1853 unsigned char byte1
= *utf8bytes
;
1854 if (byte1
< 0x21 || (byte1
> 0x7E && byte1
< 0xA1)) return true;
1855 if ((byte1
== 0xe2 || byte1
== 0xe3) && (end
- utf8bytes
>= 3)) {
1856 // Check other possibilities in the 3-bytes range
1857 unsigned char byte2
= *(utf8bytes
+ 1);
1858 unsigned char byte3
= *(utf8bytes
+ 2);
1859 if (byte1
== 0xe2 && byte2
== 0x80) {
1860 return ((byte3
>= 80 && byte3
<= 0x8b) || byte3
== 0xaf);
1861 } else if (byte1
== 0xe2 && byte2
== 0x81) {
1862 return byte3
== 0x9f;
1863 } else if (byte1
== 0xe3 && byte2
== 0x80 && byte3
== 0x80) {
1870 static Boolean
parseIntegerTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1871 bool isHex
= false, isNeg
= false, hadLeadingZero
= false;
1874 // decimal_constant S*(-|+)?S*[0-9]+ (S == space)
1875 // hex_constant S*(-|+)?S*0[xX][0-9a-fA-F]+ (S == space)
1877 while (pInfo
->curr
< pInfo
->end
&& isWhitespace(pInfo
->curr
, pInfo
->end
)) pInfo
->curr
++;
1880 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo
));
1883 if ('-' == ch
|| '+' == ch
) {
1884 isNeg
= ('-' == ch
);
1886 while (pInfo
->curr
< pInfo
->end
&& isWhitespace(pInfo
->curr
, pInfo
->end
)) pInfo
->curr
++;
1890 if (pInfo
->curr
+ 1 < pInfo
->end
&& ('x' == *(pInfo
->curr
+ 1) || 'X' == *(pInfo
->curr
+ 1))) {
1894 hadLeadingZero
= true;
1900 hadLeadingZero
= true;
1904 if ('<' == ch
&& hadLeadingZero
) { // nothing but zeros
1906 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
)) {
1907 // checkForCloseTag() sets error string
1913 *out
= CFNumberCreate(pInfo
->allocator
, kCFNumberSInt32Type
, &val
);
1918 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Incomplete <integer> on line %d"), lineNumber(pInfo
));
1922 uint32_t multiplier
= (isHex
? 16 : 10);
1924 uint32_t new_digit
= 0;
1926 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
1927 new_digit
= (ch
- '0');
1929 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1930 new_digit
= (ch
- 'a' + 10);
1932 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1933 new_digit
= (ch
- 'A' + 10);
1935 default: // other character
1936 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unknown character '%c' (0x%x) in <integer> on line %d"), ch
, ch
, lineNumber(pInfo
));
1939 if (!isHex
&& new_digit
> 9) {
1940 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Hex digit in non-hex <integer> on line %d"), lineNumber(pInfo
));
1943 if (UINT64_MAX
/ multiplier
< value
) {
1944 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo
));
1947 value
= multiplier
* value
;
1948 if (UINT64_MAX
- new_digit
< value
) {
1949 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo
));
1952 value
= value
+ new_digit
;
1953 if (isNeg
&& (uint64_t)INT64_MAX
+ 1 < value
) {
1954 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Integer underflow in <integer> on line %d"), lineNumber(pInfo
));
1960 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
)) {
1961 // checkForCloseTag() sets error string
1968 if (isNeg
|| value
<= INT64_MAX
) {
1970 if (isNeg
) v
= -v
; // no-op if INT64_MIN
1971 *out
= CFNumberCreate(pInfo
->allocator
, kCFNumberSInt64Type
, &v
);
1973 CFSInt128Struct val
;
1976 *out
= CFNumberCreate(pInfo
->allocator
, kCFNumberSInt128Type
, &val
);
1984 // Returned object is retained; caller must free. pInfo->curr expected to point to the first character after the '<'
1985 static Boolean
parseXMLElement(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
, CFTypeRef
*out
) {
1986 const char *marker
= pInfo
->curr
;
1987 int markerLength
= -1;
1991 if (isKey
) *isKey
= false;
1992 while (pInfo
->curr
< pInfo
->end
) {
1993 char ch
= *(pInfo
->curr
);
1994 if (ch
== ' ' || ch
== '\t' || ch
== '\n' || ch
=='\r') {
1995 if (markerLength
== -1) markerLength
= pInfo
->curr
- marker
;
1996 } else if (ch
== '>') {
2001 if (pInfo
->curr
>= pInfo
->end
) {
2004 isEmpty
= (*(pInfo
->curr
-1) == '/');
2005 if (markerLength
== -1)
2006 markerLength
= pInfo
->curr
- (isEmpty
? 1 : 0) - marker
;
2007 pInfo
->curr
++; // Advance past '>'
2008 if (markerLength
== 0) {
2009 // Back up to the beginning of the marker
2010 pInfo
->curr
= marker
;
2011 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Malformed tag on line %d"), lineNumber(pInfo
));
2016 if (markerLength
== ARRAY_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
))
2017 markerIx
= ARRAY_IX
;
2019 case 'd': // Dictionary, data, or date; Fortunately, they all have the same marker length....
2020 if (markerLength
!= DICT_TAG_LENGTH
)
2022 if (!memcmp(marker
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
))
2024 else if (!memcmp(marker
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
))
2026 else if (!memcmp(marker
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
))
2029 case 'f': // false (boolean)
2030 if (markerLength
== FALSE_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
)) {
2031 markerIx
= FALSE_IX
;
2034 case 'i': // integer
2035 if (markerLength
== INTEGER_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
))
2036 markerIx
= INTEGER_IX
;
2038 case 'k': // Key of a dictionary
2039 if (markerLength
== KEY_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
)) {
2041 if (isKey
) *isKey
= true;
2045 if (markerLength
== PLIST_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
))
2046 markerIx
= PLIST_IX
;
2049 if (markerLength
== REAL_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
))
2053 if (markerLength
== STRING_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[STRING_IX
], STRING_TAG_LENGTH
))
2054 markerIx
= STRING_IX
;
2056 case 't': // true (boolean)
2057 if (markerLength
== TRUE_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
))
2062 if (!pInfo
->allowNewTypes
&& markerIx
!= PLIST_IX
&& markerIx
!= ARRAY_IX
&& markerIx
!= DICT_IX
&& markerIx
!= STRING_IX
&& markerIx
!= KEY_IX
&& markerIx
!= DATA_IX
) {
2063 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered new tag when expecting only old-style property list objects"));
2070 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty plist tag"));
2073 return parsePListTag(pInfo
, out
);
2079 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
2080 *out
= CFArrayCreate(pInfo
->allocator
, NULL
, 0, &kCFTypeArrayCallBacks
);
2082 *out
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
2087 return parseArrayTag(pInfo
, out
);
2094 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
2095 *out
= CFDictionaryCreate(pInfo
->allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2097 *out
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2102 return parseDictTag(pInfo
, out
);
2107 int tagLen
= (markerIx
== KEY_IX
) ? KEY_TAG_LENGTH
: STRING_TAG_LENGTH
;
2112 if (pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
2113 *out
= CFStringCreateMutable(pInfo
->allocator
, 0);
2115 *out
= CFStringCreateWithCharacters(pInfo
->allocator
, NULL
, 0);
2120 if (!parseStringTag(pInfo
, (CFStringRef
*)out
)) {
2121 return false; // parseStringTag will already have set the error string
2123 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[markerIx
], tagLen
)) {
2124 __CFPListRelease(*out
, pInfo
->allocator
);
2132 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <data> on line %d"), lineNumber(pInfo
));
2135 return parseDataTag(pInfo
, out
);
2139 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <date> on line %d"), lineNumber(pInfo
));
2142 return parseDateTag(pInfo
, out
);
2146 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
)) {
2147 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered non-empty <true> on line %d"), lineNumber(pInfo
));
2154 *out
= CFRetain(kCFBooleanTrue
);
2159 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
)) {
2160 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered non-empty <false> on line %d"), lineNumber(pInfo
));
2167 *out
= CFRetain(kCFBooleanFalse
);
2172 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo
));
2175 return parseRealTag(pInfo
, out
);
2179 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo
));
2182 return parseIntegerTag(pInfo
, out
);
2185 CFStringRef markerStr
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, (const UInt8
*)marker
, markerLength
, kCFStringEncodingUTF8
, NO
);
2186 pInfo
->curr
= marker
;
2187 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown tag %@ on line %d"), markerStr
? markerStr
: CFSTR("<unknown>"), lineNumber(pInfo
));
2188 if (markerStr
) CFRelease(markerStr
);
2194 static Boolean
parseXMLPropertyList(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
2195 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
2197 skipWhitespace(pInfo
);
2198 if (pInfo
->curr
+1 >= pInfo
->end
) {
2199 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("No XML content found"));
2202 if (*(pInfo
->curr
) != '<') {
2203 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected character %c at line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
2206 ch
= *(++ pInfo
->curr
);
2210 if (pInfo
->curr
+1 < pInfo
->end
&& *pInfo
->curr
== '-' && *(pInfo
->curr
+1) == '-') {
2213 skipXMLComment(pInfo
);
2217 } else if (ch
== '?') {
2218 // Processing instruction
2220 skipXMLProcessingInstruction(pInfo
);
2223 return parseXMLElement(pInfo
, NULL
, out
);
2224 // Note we do not verify that there was only one element, so a file that has garbage after the first element will nonetheless successfully parse
2227 // Should never get here
2228 if (!(pInfo
->error
)) {
2229 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
2234 static CFStringEncoding
encodingForXMLData(CFDataRef data
, CFErrorRef
*error
, CFIndex
*skip
) {
2235 const uint8_t *bytes
= (uint8_t *)CFDataGetBytePtr(data
);
2236 UInt32 length
= CFDataGetLength(data
);
2237 const uint8_t *idx
, *end
;
2240 // Check for the byte order mark first. If we find it, set the skip value so the parser doesn't attempt to parse the BOM itself.
2242 if (*bytes
== 0x00 && *(bytes
+1) == 0x00 && *(bytes
+2) == 0xFE && *(bytes
+3) == 0xFF) {
2244 return kCFStringEncodingUTF32BE
;
2245 } else if (*bytes
== 0xFF && *(bytes
+1) == 0xFE && *(bytes
+2) == 0x00 && *(bytes
+3) == 0x00) {
2247 return kCFStringEncodingUTF32LE
;
2252 if (*bytes
== 0xEF && *(bytes
+1) == 0xBB && *(bytes
+2) == 0xBF) {
2254 return kCFStringEncodingUTF8
;
2259 if (*bytes
== 0xFF && *(bytes
+1) == 0xFE) {
2261 return kCFStringEncodingUTF16LE
;
2262 } else if (*bytes
== 0xFE && *(bytes
+1) == 0xFF) {
2264 return kCFStringEncodingUTF16BE
;
2265 } else if (*bytes
== 0x00 || *(bytes
+1) == 0x00) { // This clause checks for a Unicode sequence lacking the byte order mark; technically an error, but this check is recommended by the XML spec
2267 return kCFStringEncodingUnicode
;
2271 // Scan for the <?xml.... ?> opening
2272 if (length
< 5 || strncmp((char const *) bytes
, "<?xml", 5) != 0) return kCFStringEncodingUTF8
;
2274 end
= bytes
+ length
;
2275 // Found "<?xml"; now we scan for "encoding"
2278 const uint8_t *scan
;
2279 if ( ch
== '?' || ch
== '>') return kCFStringEncodingUTF8
;
2282 if (idx
+ 8 >= end
) {
2283 if (error
) *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("End of buffer while looking for encoding name"));
2286 if (ch
== 'e' && *scan
++ == 'n' && *scan
++ == 'c' && *scan
++ == 'o' && *scan
++ == 'd' && *scan
++ == 'i'
2287 && *scan
++ == 'n' && *scan
++ == 'g' && *scan
++ == '=') {
2292 if (idx
>= end
) return kCFStringEncodingUTF8
;
2294 if (quote
!= '\'' && quote
!= '\"') return kCFStringEncodingUTF8
;
2296 CFStringRef encodingName
;
2297 const uint8_t *base
= idx
+1; // Move past the quote character
2300 while (idx
< end
&& *idx
!= quote
) idx
++;
2301 if (idx
>= end
) return kCFStringEncodingUTF8
;
2303 if (len
== 5 && (*base
== 'u' || *base
== 'U') && (base
[1] == 't' || base
[1] == 'T') && (base
[2] == 'f' || base
[2] == 'F') && (base
[3] == '-') && (base
[4] == '8'))
2304 return kCFStringEncodingUTF8
;
2305 encodingName
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, base
, len
, kCFStringEncodingISOLatin1
, false);
2306 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
2307 CFStringEncoding enc
= CFStringConvertIANACharSetNameToEncoding(encodingName
);
2308 if (enc
!= kCFStringEncodingInvalidId
) {
2309 CFRelease(encodingName
);
2315 *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown encoding (%@)"), encodingName
);
2316 CFRelease(encodingName
);
2322 bool __CFTryParseBinaryPlist(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFPropertyListRef
*plist
, CFStringRef
*errorString
);
2324 #define SAVE_PLISTS 0
2327 static Boolean
__savePlistData(CFDataRef data
, CFOptionFlags opt
) {
2330 uint32_t pnlen
= sizeof(pn
);
2331 uint8_t *pnp
= NULL
;
2332 if (0 == _NSGetExecutablePath((char *)pn
, &pnlen
)) {
2333 pnp
= strrchr((char *)pn
, '/');
2340 if (0 == strcmp((char *)pnp
, "parse_plists")) return true;
2341 CFUUIDRef r
= CFUUIDCreate(kCFAllocatorSystemDefault
);
2342 CFStringRef s
= CFUUIDCreateString(kCFAllocatorSystemDefault
, r
);
2343 CFStringRef p
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("/tmp/plists/%s#%@#0x%x"), pnp
, s
, opt
);
2344 _CFStringGetFileSystemRepresentation(p
, fn
, sizeof(fn
));
2348 int fd
= open((const char *)fn
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
2349 if (fd
< 0) return false;
2350 int len
= CFDataGetLength(data
);
2351 int w
= write(fd
, CFDataGetBytePtr(data
), len
);
2354 if (w
!= len
) return false;
2359 // If the data is from a converted string, then originalString is non-NULL. If originalString is NULL, then pass in guessedEncoding.
2360 // keyPaths is a set of CFStrings, ':'-separated paths
2361 static Boolean
_CFPropertyListCreateFromUTF8Data(CFAllocatorRef allocator
, CFDataRef xmlData
, CFIndex skipBytes
, CFStringRef originalString
, CFStringEncoding guessedEncoding
, CFOptionFlags option
, CFErrorRef
*outError
, Boolean allowNewTypes
, CFPropertyListFormat
*format
, CFSetRef keyPaths
, CFTypeRef
*out
) {
2364 CFAssert1(xmlData
!= NULL
, __kCFLogAssertion
, "%s(): NULL data not allowed", __PRETTY_FUNCTION__
);
2365 CFAssert2(option
== kCFPropertyListImmutable
|| option
== kCFPropertyListMutableContainers
|| option
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, option
);
2367 CFIndex length
= CFDataGetLength(xmlData
);
2369 if (outError
) *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Conversion of string failed. The string is empty."));
2373 _CFXMLPlistParseInfo pInfoBuf
;
2374 _CFXMLPlistParseInfo
*pInfo
= &pInfoBuf
;
2377 // Ensure that the data is not collected while we are using it
2379 const char *buf
= (const char *)CFDataGetBytePtr(xmlData
);
2381 // We may have to skip over starting stuff like BOM markers.
2385 pInfo
->end
= buf
+length
;
2387 pInfo
->allocator
= allocator
;
2388 pInfo
->error
= NULL
;
2389 _createStringMap(pInfo
);
2390 pInfo
->mutabilityOption
= option
;
2391 pInfo
->allowNewTypes
= allowNewTypes
;
2392 pInfo
->skip
= false;
2393 pInfo
->keyPaths
= createTopLevelKeypaths(allocator
, keyPaths
);
2395 Boolean success
= parseXMLPropertyList(pInfo
, &result
);
2396 if (success
&& result
&& format
) *format
= kCFPropertyListXMLFormat_v1_0
;
2398 _cleanupStringMap(pInfo
);
2399 if (pInfo
->keyPaths
&& !(0)) CFRelease(pInfo
->keyPaths
);
2403 *out
= result
; // caller releases
2407 // Try again, old-style
2408 CFErrorRef oldStyleError
= NULL
;
2409 result
= __CFCreateOldStylePropertyListOrStringsFile(allocator
, xmlData
, originalString
, guessedEncoding
, option
, outError
? &oldStyleError
: NULL
, format
);
2411 // Release old error, return
2412 if (pInfo
->error
) CFRelease(pInfo
->error
);
2417 // Failure, both ways. Set up the error to be given back to caller.
2419 if (pInfo
->error
) CFRelease(pInfo
->error
);
2423 // Caller's responsibility to release outError
2424 if (pInfo
->error
&& oldStyleError
) {
2425 // Add the error from the old-style property list parser to the user info of the original error (pInfo->error), which had better exist
2426 CFDictionaryRef oldUserInfo
= CFErrorCopyUserInfo(pInfo
->error
);
2427 CFMutableDictionaryRef newUserInfo
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, CFDictionaryGetCount(oldUserInfo
) + 1, oldUserInfo
);
2428 CFDictionaryAddValue(newUserInfo
, CFPropertyListOldStyleParserErrorKey
, oldStyleError
);
2430 // Re-create the xml parser error with this new user info dictionary
2431 CFErrorRef newError
= CFErrorCreate(kCFAllocatorSystemDefault
, CFErrorGetDomain(pInfo
->error
), CFErrorGetCode(pInfo
->error
), newUserInfo
);
2433 CFRelease(oldUserInfo
);
2434 CFRelease(newUserInfo
);
2435 CFRelease(oldStyleError
);
2436 CFRelease(pInfo
->error
);
2437 *outError
= newError
;
2439 } else if (pInfo
->error
&& !oldStyleError
) {
2440 // Return original error
2441 *outError
= pInfo
->error
;
2442 } else if (!pInfo
->error
&& oldStyleError
) {
2443 // Return only old-style error
2444 // Probably shouldn't get here
2445 *outError
= oldStyleError
;
2446 } else if (!pInfo
->error
&& !oldStyleError
) {
2447 // Return unknown error
2448 *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown error during parse"));
2453 static CFDataRef
_createUTF8DataFromString(CFAllocatorRef allocator
, CFStringRef str
) {
2454 CFIndex bytesNeeded
= 0;
2455 CFStringGetBytes(str
, CFRangeMake(0, CFStringGetLength(str
)), kCFStringEncodingUTF8
, 0, false, NULL
, 0, &bytesNeeded
);
2457 const char *bytes
= (const char *)CFAllocatorAllocate(allocator
, bytesNeeded
, 0);
2458 CFStringGetBytes(str
, CFRangeMake(0, CFStringGetLength(str
)), kCFStringEncodingUTF8
, 0, false, (uint8_t *)bytes
, bytesNeeded
, NULL
);
2460 CFDataRef utf8Data
= CFDataCreateWithBytesNoCopy(allocator
, (const UInt8
*)bytes
, bytesNeeded
, allocator
);
2464 // Set topLevelKeys to a set of top level keys to decode. If NULL, all keys are decoded. If the top level object is not a dictionary, all objects are decoded. If the plist is not XML, all objects are decoded.
2465 static Boolean
_CFPropertyListCreateWithData(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFErrorRef
*outError
, Boolean allowNewTypes
, CFPropertyListFormat
*format
, CFSetRef topLevelKeys
, CFTypeRef
*out
) {
2467 CFStringEncoding encoding
;
2469 if (!data
|| CFDataGetLength(data
) == 0) {
2471 *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Cannot parse a NULL or zero-length data"));
2477 __savePlistData(data
, option
);
2480 // Ignore the error from CFTryParseBinaryPlist -- if it doesn't work, we're going to try again anyway using the XML parser
2481 if (__CFTryParseBinaryPlist(allocator
, data
, option
, out
, NULL
)) {
2482 if (format
) *format
= kCFPropertyListBinaryFormat_v1_0
;
2486 // Use our own error variable here so we can check it against NULL later
2487 CFErrorRef subError
= NULL
;
2489 encoding
= encodingForXMLData(data
, &subError
, &skip
); // 0 is an error return, NOT MacRoman.
2491 if (encoding
== 0) {
2492 // Couldn't find an encoding
2493 // Note that encodingForXMLData() will give us the right values for a standard plist, too.
2494 if (outError
&& subError
== NULL
) {
2495 // encodingForXMLData didn't set an error, so we create a new one here
2496 *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not determine the encoding of the XML data"));
2497 } else if (outError
&& subError
) {
2498 // give the caller the subError, they will release
2499 *outError
= subError
;
2500 } else if (!outError
&& subError
) {
2501 // Release the error
2502 CFRelease(subError
);
2507 if (encoding
== kCFStringEncodingUTF8
) {
2509 return _CFPropertyListCreateFromUTF8Data(allocator
, data
, skip
, NULL
, encoding
, option
, outError
, allowNewTypes
, format
, topLevelKeys
, out
);
2512 // Convert to UTF8 first
2513 CFStringRef xmlString
= CFStringCreateWithBytes(allocator
, CFDataGetBytePtr(data
) + skip
, CFDataGetLength(data
) - skip
, encoding
, false);
2515 if (outError
) *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not determine the encoding of the XML data (string creation failed)"));
2519 CFDataRef utf8Data
= _createUTF8DataFromString(allocator
, xmlString
);
2521 Boolean result
= _CFPropertyListCreateFromUTF8Data(allocator
, utf8Data
, 0, xmlString
, 0, option
, outError
, allowNewTypes
, format
, topLevelKeys
, out
);
2523 if (xmlString
&& !(0)) CFRelease(xmlString
);
2524 if (utf8Data
&& !(0)) CFRelease(utf8Data
);
2529 // -----------------------------------------------------------------------------------------------------------------------
2532 #pragma mark Exported Parsing Functions
2534 CFTypeRef
_CFPropertyListCreateFromXMLStringError(CFAllocatorRef allocator
, CFStringRef xmlString
, CFOptionFlags option
, CFErrorRef
*error
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2535 // Convert to UTF8 first
2536 CFDataRef utf8Data
= _createUTF8DataFromString(allocator
, xmlString
);
2537 CFTypeRef result
= NULL
;
2538 _CFPropertyListCreateFromUTF8Data(allocator
, utf8Data
, 0, xmlString
, 0, option
, error
, allowNewTypes
, format
, NULL
, &result
);
2539 if (utf8Data
&& !(0)) CFRelease(utf8Data
);
2544 CFTypeRef
_CFPropertyListCreateFromXMLString(CFAllocatorRef allocator
, CFStringRef xmlString
, CFOptionFlags option
, CFStringRef
*errorString
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2546 if (errorString
) *errorString
= NULL
;
2547 CFErrorRef error
= NULL
;
2548 CFTypeRef result
= _CFPropertyListCreateFromXMLStringError(allocator
, xmlString
, option
, &error
, allowNewTypes
, format
);
2550 if (errorString
&& error
) {
2551 // The caller is interested in receiving an error message. Pull the debug string out of the CFError returned above
2552 CFDictionaryRef userInfo
= CFErrorCopyUserInfo(error
);
2553 CFStringRef debugString
= NULL
;
2555 // Handle a special-case for compatibility - if the XML parse failed and the old-style plist parse failed, construct a special string
2556 CFErrorRef underlyingError
= NULL
;
2558 Boolean oldStyleFailed
= CFDictionaryGetValueIfPresent(userInfo
, CFPropertyListOldStyleParserErrorKey
, (const void **)&underlyingError
);
2560 if (oldStyleFailed
) {
2561 CFStringRef xmlParserErrorString
, oldStyleParserErrorString
;
2562 xmlParserErrorString
= (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
2564 CFDictionaryRef oldStyleParserUserInfo
= CFErrorCopyUserInfo(underlyingError
);
2565 oldStyleParserErrorString
= (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
2567 // Combine the two strings into a single one that matches the previous implementation
2568 debugString
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("XML parser error:\n\t%@\nOld-style plist parser error:\n\t%@\n"), xmlParserErrorString
, oldStyleParserErrorString
);
2570 CFRelease(oldStyleParserUserInfo
);
2572 debugString
= (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
2573 if (debugString
) CFRetain(debugString
);
2576 // Give the debugString to the caller, who is responsible for releasing it
2577 CFRelease(userInfo
);
2578 *errorString
= debugString
;
2588 CF_PRIVATE
bool __CFBinaryPlistCreateObjectFiltered(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFAllocatorRef allocator
, CFOptionFlags mutabilityOption
, CFMutableDictionaryRef objects
, CFMutableSetRef set
, CFIndex curDepth
, CFSetRef keyPaths
, CFPropertyListRef
*plist
);
2590 // Returns a subset of the property list, only including the key paths in the CFSet.
2591 bool _CFPropertyListCreateFiltered(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFSetRef keyPaths
, CFPropertyListRef
*value
, CFErrorRef
*error
) {
2595 if (!keyPaths
|| !data
) {
2600 CFBinaryPlistTrailer trailer
;
2602 const uint8_t *databytes
= CFDataGetBytePtr(data
);
2603 uint64_t datalen
= CFDataGetLength(data
);
2604 Boolean success
= false;
2605 CFTypeRef out
= NULL
;
2607 // First check to see if it is a binary property list
2608 if (8 <= datalen
&& __CFBinaryPlistGetTopLevelInfo(databytes
, datalen
, &marker
, &offset
, &trailer
)) {
2609 uint64_t valueOffset
= offset
;
2611 // Split up the key path
2612 CFSetRef splitKeyPaths
= createTopLevelKeypaths(allocator
, keyPaths
);
2614 // Create a dictionary to cache objects in
2615 CFMutableDictionaryRef objects
= CFDictionaryCreateMutable(allocator
, 0, NULL
, &kCFTypeDictionaryValueCallBacks
);
2616 success
= __CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, valueOffset
, &trailer
, allocator
, option
, objects
, NULL
, 0, splitKeyPaths
, &out
);
2618 CFRelease(splitKeyPaths
);
2621 // Try an XML property list
2622 success
= _CFPropertyListCreateWithData(allocator
, data
, option
, error
, true, NULL
, keyPaths
, &out
);
2625 if (success
&& value
) {
2626 *value
= out
; // caller releases
2633 /* Get a single value for a given key in a top-level dictionary in a property list.
2634 @param allocator The allocator to use.
2635 @param data The property list data.
2636 @param option Currently unused, set to 0.
2637 @param keyPath The keyPath to search for in the property list. Keys are colon-separated. Indexes into arrays are specified with an integer (zero-based).
2638 @param value If the key is found and the parameter is non-NULL, this will be set to a reference to the value. It is the caller's responsibility to release the object. If no object is found, will be set to NULL. If this parameter is NULL, this function can be used to check for the existence of a key without creating it by just checking the return value.
2639 @param error If an error occurs, will be set to a valid CFErrorRef. It is the caller's responsibility to release this value.
2640 @return True if the key is found, false otherwise.
2642 bool _CFPropertyListCreateSingleValue(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFStringRef keyPath
, CFPropertyListRef
*value
, CFErrorRef
*error
) {
2646 if (!keyPath
|| CFStringGetLength(keyPath
) == 0) {
2651 CFBinaryPlistTrailer trailer
;
2653 const uint8_t *databytes
= CFDataGetBytePtr(data
);
2654 uint64_t datalen
= CFDataGetLength(data
);
2655 Boolean success
= false;
2657 // First check to see if it is a binary property list
2658 if (8 <= datalen
&& __CFBinaryPlistGetTopLevelInfo(databytes
, datalen
, &marker
, &offset
, &trailer
)) {
2659 // Split up the key path
2660 CFArrayRef keyPathArray
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorSystemDefault
, keyPath
, CFSTR(":"));
2661 uint64_t keyOffset
, valueOffset
= offset
;
2663 // Create a dictionary to cache objects in
2664 CFMutableDictionaryRef objects
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, &kCFTypeDictionaryValueCallBacks
);
2665 CFIndex keyPathCount
= CFArrayGetCount(keyPathArray
);
2666 _CFDictionarySetCapacity(objects
, keyPathCount
+ 1);
2668 for (CFIndex i
= 0; i
< keyPathCount
; i
++) {
2669 CFStringRef oneKey
= (CFStringRef
)CFArrayGetValueAtIndex(keyPathArray
, i
);
2670 SInt32 intValue
= CFStringGetIntValue(oneKey
);
2671 if ((intValue
== 0 && CFStringCompare(CFSTR("0"), oneKey
, 0) != kCFCompareEqualTo
) || intValue
== INT_MAX
|| intValue
== INT_MIN
|| intValue
< 0) {
2672 // Treat as a string key into a dictionary
2673 success
= __CFBinaryPlistGetOffsetForValueFromDictionary3(databytes
, datalen
, valueOffset
, &trailer
, (CFTypeRef
)oneKey
, &keyOffset
, &valueOffset
, false, objects
);
2675 // Treat as integer index into an array
2676 success
= __CFBinaryPlistGetOffsetForValueFromArray2(databytes
, datalen
, valueOffset
, &trailer
, intValue
, &valueOffset
, objects
);
2684 // value could be null if the caller wanted to check for the existence of a key but not bother creating it
2685 if (success
&& value
) {
2686 CFPropertyListRef pl
;
2687 success
= __CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, valueOffset
, &trailer
, allocator
, option
, objects
, NULL
, 0, NULL
, &pl
);
2689 // caller's responsibility to release the created object
2694 CFRelease(keyPathArray
);
2697 // Try an XML property list
2698 // Note: This is currently not any more efficient than grabbing the whole thing. This could be improved in the future.
2699 CFPropertyListRef plist
= CFPropertyListCreateWithData(allocator
, data
, option
, NULL
, error
);
2701 CFPropertyListRef nextObject
= plist
;
2703 CFArrayRef keyPathArray
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorSystemDefault
, keyPath
, CFSTR(":"));
2704 for (CFIndex i
= 0; i
< CFArrayGetCount(keyPathArray
); i
++) {
2705 CFStringRef oneKey
= (CFStringRef
)CFArrayGetValueAtIndex(keyPathArray
, i
);
2706 SInt32 intValue
= CFStringGetIntValue(oneKey
);
2707 if (((intValue
== 0 && CFStringCompare(CFSTR("0"), oneKey
, 0) != kCFCompareEqualTo
) || intValue
== INT_MAX
|| intValue
== INT_MIN
) && CFGetTypeID((CFTypeRef
)nextObject
) == dicttype
) {
2708 // Treat as a string key into a dictionary
2709 nextObject
= (CFPropertyListRef
)CFDictionaryGetValue((CFDictionaryRef
)nextObject
, oneKey
);
2710 } else if (CFGetTypeID((CFTypeRef
)nextObject
) == arraytype
) {
2711 // Treat as integer index into an array
2712 nextObject
= (CFPropertyListRef
)CFArrayGetValueAtIndex((CFArrayRef
)nextObject
, intValue
);
2719 if (success
&& nextObject
&& value
) {
2720 *value
= nextObject
;
2721 // caller's responsibility to release the created object
2723 } else if (!nextObject
) {
2727 CFRelease(keyPathArray
);
2736 CFTypeRef
_CFPropertyListCreateFromXMLData(CFAllocatorRef allocator
, CFDataRef xmlData
, CFOptionFlags option
, CFStringRef
*errorString
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2738 CFTypeRef out
= NULL
;
2739 if (errorString
) *errorString
= NULL
;
2740 CFErrorRef error
= NULL
;
2741 Boolean result
= _CFPropertyListCreateWithData(allocator
, xmlData
, option
, &error
, allowNewTypes
, format
, NULL
, &out
);
2742 if (!result
&& error
&& errorString
) {
2743 *errorString
= __copyErrorDebugDescription(error
);
2745 if (error
) CFRelease(error
);
2749 CFPropertyListRef
CFPropertyListCreateWithData(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags options
, CFPropertyListFormat
*format
, CFErrorRef
*error
) {
2751 CFAssert1(data
!= NULL
, __kCFLogAssertion
, "%s(): NULL data not allowed", __PRETTY_FUNCTION__
);
2752 CFAssert2(options
== kCFPropertyListImmutable
|| options
== kCFPropertyListMutableContainers
|| options
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, options
);
2753 CFPropertyListRef out
= NULL
;
2754 _CFPropertyListCreateWithData(allocator
, data
, options
, error
, true, format
, NULL
, &out
);
2758 CFPropertyListRef
CFPropertyListCreateFromXMLData(CFAllocatorRef allocator
, CFDataRef xmlData
, CFOptionFlags option
, CFStringRef
*errorString
) {
2760 if (errorString
) *errorString
= NULL
;
2761 CFErrorRef error
= NULL
;
2762 CFPropertyListRef result
= CFPropertyListCreateWithData(allocator
, xmlData
, option
, NULL
, &error
);
2763 if (error
&& errorString
) {
2764 *errorString
= __copyErrorDebugDescription(error
);
2766 if (error
) CFRelease(error
);
2770 CFDataRef
CFPropertyListCreateData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, CFPropertyListFormat format
, CFOptionFlags options
, CFErrorRef
*error
) {
2772 CFAssert1(format
!= kCFPropertyListOpenStepFormat
, __kCFLogAssertion
, "%s(): kCFPropertyListOpenStepFormat not supported for writing", __PRETTY_FUNCTION__
);
2773 CFAssert2(format
== kCFPropertyListXMLFormat_v1_0
|| format
== kCFPropertyListBinaryFormat_v1_0
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, format
);
2774 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
2775 __CFAssertIsPList(propertyList
);
2777 CFDataRef data
= NULL
;
2780 CFStringRef validErr
= NULL
;
2781 if (!_CFPropertyListIsValidWithErrorString(propertyList
, format
, &validErr
)) {
2782 CFLog(kCFLogLevelError
, CFSTR("Property list invalid for format: %d (%@)"), format
, validErr
);
2783 if (validErr
) CFRelease(validErr
);
2787 if (format
== kCFPropertyListOpenStepFormat
) {
2788 CFLog(kCFLogLevelError
, CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2790 } else if (format
== kCFPropertyListXMLFormat_v1_0
) {
2791 data
= _CFPropertyListCreateXMLData(allocator
, propertyList
, true);
2792 } else if (format
== kCFPropertyListBinaryFormat_v1_0
) {
2793 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2794 // TODO: Is it more efficient to create a stream here or just use a mutable data?
2795 CFWriteStreamRef stream
= CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorSystemDefault
, allocator
);
2796 CFWriteStreamOpen(stream
);
2797 CFIndex len
= CFPropertyListWrite(propertyList
, stream
, format
, options
, error
);
2799 data
= (CFDataRef
)CFWriteStreamCopyProperty(stream
, kCFStreamPropertyDataWritten
);
2801 CFWriteStreamClose(stream
);
2804 CFMutableDataRef dataForPlist
= CFDataCreateMutable(allocator
, 0);
2805 __CFBinaryPlistWrite(propertyList
, dataForPlist
, 0, options
, error
);
2806 return dataForPlist
;
2809 CFLog(kCFLogLevelError
, CFSTR("Unknown format option"));
2815 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2817 CFIndex
CFPropertyListWrite(CFPropertyListRef propertyList
, CFWriteStreamRef stream
, CFPropertyListFormat format
, CFOptionFlags options
, CFErrorRef
*error
) {
2819 CFAssert1(stream
!= NULL
, __kCFLogAssertion
, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__
);
2820 CFAssert1(format
!= kCFPropertyListOpenStepFormat
, __kCFLogAssertion
, "%s(): kCFPropertyListOpenStepFormat not supported for writing", __PRETTY_FUNCTION__
);
2821 CFAssert2(format
== kCFPropertyListXMLFormat_v1_0
|| format
== kCFPropertyListBinaryFormat_v1_0
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, format
);
2822 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
2823 __CFAssertIsPList(propertyList
);
2824 CFAssert1(CFWriteStreamGetTypeID() == CFGetTypeID(stream
), __kCFLogAssertion
, "%s(): stream argument is not a write stream", __PRETTY_FUNCTION__
);
2825 CFAssert1(kCFStreamStatusOpen
== CFWriteStreamGetStatus(stream
) || kCFStreamStatusWriting
== CFWriteStreamGetStatus(stream
), __kCFLogAssertion
, "%s(): stream is not open", __PRETTY_FUNCTION__
);
2827 CFStringRef validErr
= NULL
;
2828 if (!_CFPropertyListIsValidWithErrorString(propertyList
, format
, &validErr
)) {
2829 CFLog(kCFLogLevelError
, CFSTR("Property list invalid for format: %d (%@)"), format
, validErr
);
2830 if (validErr
) CFRelease(validErr
);
2833 if (format
== kCFPropertyListOpenStepFormat
) {
2834 CFLog(kCFLogLevelError
, CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2837 if (format
== kCFPropertyListXMLFormat_v1_0
) {
2838 CFDataRef data
= _CFPropertyListCreateXMLData(kCFAllocatorSystemDefault
, propertyList
, true);
2840 CFLog(kCFLogLevelError
, CFSTR("Property list format kCFPropertyListXMLFormat_v1_0 specified but was not a valid property list type"));
2843 CFIndex len
= CFDataGetLength(data
);
2844 const uint8_t *ptr
= CFDataGetBytePtr(data
);
2846 CFIndex ret
= CFWriteStreamWrite(stream
, ptr
, len
);
2848 if (error
) *error
= __CFPropertyListCreateError(kCFPropertyListWriteStreamError
, CFSTR("Property list writing could not be completed because stream is full."));
2853 CFErrorRef underlyingError
= CFWriteStreamCopyError(stream
);
2854 if (underlyingError
) {
2856 // Wrap the error from CFWriteStreamCopy in a new error
2857 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2858 CFDictionarySetValue(userInfo
, kCFErrorDebugDescriptionKey
, CFSTR("Property list writing could not be completed because the stream had an unknown error."));
2859 CFDictionarySetValue(userInfo
, kCFErrorUnderlyingErrorKey
, underlyingError
);
2860 *error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, kCFPropertyListWriteStreamError
, userInfo
);
2861 CFRelease(userInfo
);
2863 CFRelease(underlyingError
);
2871 len
= CFDataGetLength(data
);
2875 if (format
== kCFPropertyListBinaryFormat_v1_0
) {
2876 CFIndex len
= __CFBinaryPlistWrite(propertyList
, stream
, 0, options
, error
);
2879 CFLog(kCFLogLevelError
, CFSTR("Unknown format option"));
2883 CFIndex
CFPropertyListWriteToStream(CFPropertyListRef propertyList
, CFWriteStreamRef stream
, CFPropertyListFormat format
, CFStringRef
*errorString
) {
2885 if (errorString
) *errorString
= NULL
;
2886 CFErrorRef error
= NULL
;
2888 // For backwards compatibility, we check the format parameter up front since these do not have CFError counterparts in the newer API
2889 CFStringRef validErr
= NULL
;
2890 if (!_CFPropertyListIsValidWithErrorString(propertyList
, format
, &validErr
)) {
2891 if (errorString
) *errorString
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("Property list invalid for format (%@)"), validErr
);
2892 if (validErr
) CFRelease(validErr
);
2895 if (format
== kCFPropertyListOpenStepFormat
) {
2896 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2899 if (format
!= kCFPropertyListBinaryFormat_v1_0
&& format
!= kCFPropertyListXMLFormat_v1_0
) {
2900 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Unknown format option"));
2904 CFIndex result
= CFPropertyListWrite(propertyList
, stream
, format
, 0, &error
);
2905 if (error
&& errorString
) {
2906 *errorString
= __copyErrorDebugDescription(error
);
2908 if (error
) CFRelease(error
);
2912 static bool __convertReadStreamToBytes(CFReadStreamRef stream
, CFIndex max
, uint8_t **buffer
, CFIndex
*length
, CFErrorRef
*error
) {
2913 int32_t buflen
= 0, bufsize
= 0, retlen
;
2914 uint8_t *buf
= NULL
, sbuf
[8192];
2916 retlen
= CFReadStreamRead(stream
, sbuf
, __CFMin(8192, max
));
2921 if (retlen
< 0 && error
) {
2922 // Copy the error out
2923 *error
= CFReadStreamCopyError(stream
);
2929 if (bufsize
< buflen
+ retlen
) {
2930 if (bufsize
< 256 * 1024) {
2932 } else if (bufsize
< 16 * 1024 * 1024) {
2935 // once in this stage, this will be really slow
2936 // and really potentially fragment memory
2937 bufsize
+= 256 * 1024;
2939 if (bufsize
< buflen
+ retlen
) bufsize
= buflen
+ retlen
;
2940 buf
= (uint8_t *)CFAllocatorReallocate(kCFAllocatorSystemDefault
, buf
, bufsize
, 0);
2943 memmove(buf
+ buflen
, sbuf
, retlen
);
2955 CFPropertyListRef
CFPropertyListCreateWithStream(CFAllocatorRef allocator
, CFReadStreamRef stream
, CFIndex streamLength
, CFOptionFlags mutabilityOption
, CFPropertyListFormat
*format
, CFErrorRef
*error
) {
2958 CFAssert1(stream
!= NULL
, __kCFLogAssertion
, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__
);
2959 CFAssert1(CFReadStreamGetTypeID() == CFGetTypeID(stream
), __kCFLogAssertion
, "%s(): stream argument is not a read stream", __PRETTY_FUNCTION__
);
2960 CFAssert1(kCFStreamStatusOpen
== CFReadStreamGetStatus(stream
) || kCFStreamStatusReading
== CFReadStreamGetStatus(stream
), __kCFLogAssertion
, "%s(): stream is not open", __PRETTY_FUNCTION__
);
2961 CFAssert2(mutabilityOption
== kCFPropertyListImmutable
|| mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, mutabilityOption
);
2963 if (0 == streamLength
) streamLength
= LONG_MAX
;
2964 CFErrorRef underlyingError
= NULL
;
2966 uint8_t *buffer
= NULL
;
2967 if (!__convertReadStreamToBytes(stream
, streamLength
, &buffer
, &buflen
, &underlyingError
)) {
2969 // Wrap the error from CFReadStream in a new error in the cocoa domain
2970 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2971 CFDictionarySetValue(userInfo
, kCFErrorDebugDescriptionKey
, CFSTR("Property list reading could not be completed because the stream had an unknown error. Did you forget to open the stream?"));
2972 if (underlyingError
) {
2973 CFDictionarySetValue(userInfo
, kCFErrorUnderlyingErrorKey
, underlyingError
);
2975 *error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, kCFPropertyListReadStreamError
, userInfo
);
2976 CFRelease(userInfo
);
2978 if (underlyingError
) {
2979 CFRelease(underlyingError
);
2984 if (!buffer
|| buflen
< 6) {
2985 if (buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buffer
);
2986 if (error
) *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("stream had too few bytes"));
2990 CFDataRef data
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, buffer
, buflen
, kCFAllocatorSystemDefault
);
2991 CFPropertyListRef pl
= NULL
; // initialize to null, because if the following call fails we must return NULL
2992 _CFPropertyListCreateWithData(allocator
, data
, mutabilityOption
, error
, true, format
, NULL
, &pl
);
2998 CFPropertyListRef
CFPropertyListCreateFromStream(CFAllocatorRef allocator
, CFReadStreamRef stream
, CFIndex length
, CFOptionFlags mutabilityOption
, CFPropertyListFormat
*format
, CFStringRef
*errorString
) {
3000 if (errorString
) *errorString
= NULL
;
3001 CFErrorRef error
= NULL
;
3002 CFPropertyListRef result
= CFPropertyListCreateWithStream(allocator
, stream
, length
, mutabilityOption
, format
, &error
);
3003 if (error
&& errorString
) {
3004 *errorString
= __copyErrorDebugDescription(error
);
3006 if (error
) CFRelease(error
);
3010 #endif //DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
3013 #pragma mark Property List Copies
3015 static CFArrayRef
_arrayDeepImmutableCopy(CFAllocatorRef allocator
, CFArrayRef array
, CFOptionFlags mutabilityOption
) {
3016 CFArrayRef result
= NULL
;
3017 CFIndex i
, c
= CFArrayGetCount(array
);
3019 result
= CFArrayCreate(allocator
, NULL
, 0, &kCFTypeArrayCallBacks
);
3021 new_cftype_array(values
, c
);
3022 CFArrayGetValues(array
, CFRangeMake(0, c
), values
);
3023 for (i
= 0; i
< c
; i
++) {
3024 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, values
[i
], mutabilityOption
);
3025 if (newValue
== NULL
) {
3028 __CFAssignWithWriteBarrier((void **)values
+ i
, (void *)newValue
);
3030 result
= (i
== c
) ? CFArrayCreate(allocator
, values
, c
, &kCFTypeArrayCallBacks
) : NULL
;
3032 for (i
= 0; i
< c
; i
++) CFRelease(values
[i
]);
3033 free_cftype_array(values
);
3038 static CFMutableArrayRef
_arrayDeepMutableCopy(CFAllocatorRef allocator
, CFArrayRef array
, CFOptionFlags mutabilityOption
) {
3039 CFIndex i
, c
= CFArrayGetCount(array
);
3040 CFMutableArrayRef result
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
3042 for (i
= 0; i
< c
; i
++) {
3043 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, CFArrayGetValueAtIndex(array
, i
), mutabilityOption
);
3044 if (!newValue
) break;
3045 CFArrayAppendValue(result
, newValue
);
3046 CFRelease(newValue
);
3056 CFPropertyListRef
CFPropertyListCreateDeepCopy(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, CFOptionFlags mutabilityOption
) {
3058 CFPropertyListRef result
= NULL
;
3059 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): cannot copy a NULL property list", __PRETTY_FUNCTION__
);
3060 __CFAssertIsPList(propertyList
);
3061 CFAssert2(mutabilityOption
== kCFPropertyListImmutable
|| mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, mutabilityOption
);
3062 if (!CFPropertyListIsValid(propertyList
, kCFPropertyListBinaryFormat_v1_0
)) return NULL
;
3064 CFTypeID typeID
= CFGetTypeID(propertyList
);
3065 if (typeID
== dicttype
) {
3066 CFDictionaryRef dict
= (CFDictionaryRef
)propertyList
;
3067 Boolean isMutable
= (mutabilityOption
!= kCFPropertyListImmutable
);
3068 CFIndex count
= CFDictionaryGetCount(dict
);
3070 result
= isMutable
? CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
): CFDictionaryCreate(allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3072 new_cftype_array(keys
, 2 * count
);
3075 values
= keys
+count
;
3076 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
3077 for (i
= 0; i
< count
; i
++) {
3078 CFTypeRef newKey
= CFStringCreateCopy(allocator
, (CFStringRef
)keys
[i
]);
3079 if (newKey
== NULL
) {
3082 __CFAssignWithWriteBarrier((void **)keys
+ i
, (void *)newKey
);
3083 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, values
[i
], mutabilityOption
);
3084 if (newValue
== NULL
) {
3088 __CFAssignWithWriteBarrier((void **)values
+ i
, (void *)newValue
);
3091 result
= isMutable
? CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
) : CFDictionaryCreate(allocator
, keys
, values
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3092 for (i
= 0; i
< count
; i
++) {
3094 CFDictionarySetValue((CFMutableDictionaryRef
)result
, keys
[i
], values
[i
]);
3097 CFRelease(values
[i
]);
3102 for (i
= 0; i
< count
; i
++) {
3104 CFRelease(values
[i
]);
3107 free_cftype_array(keys
);
3109 } else if (typeID
== arraytype
) {
3110 if (mutabilityOption
== kCFPropertyListImmutable
) {
3111 result
= _arrayDeepImmutableCopy(allocator
, (CFArrayRef
)propertyList
, mutabilityOption
);
3113 result
= _arrayDeepMutableCopy(allocator
, (CFArrayRef
)propertyList
, mutabilityOption
);
3115 } else if (typeID
== datatype
) {
3116 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
3117 result
= CFDataCreateMutableCopy(allocator
, 0, (CFDataRef
)propertyList
);
3119 result
= CFDataCreateCopy(allocator
, (CFDataRef
)propertyList
);
3121 } else if (typeID
== numbertype
) {
3122 // Warning - this will break if byteSize is ever greater than 128
3124 CFNumberType numType
= _CFNumberGetType2((CFNumberRef
)propertyList
);
3125 CFNumberGetValue((CFNumberRef
)propertyList
, numType
, (void *)bytes
);
3126 result
= CFNumberCreate(allocator
, numType
, (void *)bytes
);
3127 } else if (typeID
== booltype
) {
3128 // Booleans are immutable & shared instances
3129 CFRetain(propertyList
);
3130 result
= propertyList
;
3131 } else if (typeID
== datetype
) {
3132 // Dates are immutable
3133 result
= CFDateCreate(allocator
, CFDateGetAbsoluteTime((CFDateRef
)propertyList
));
3134 } else if (typeID
== stringtype
) {
3135 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
3136 result
= CFStringCreateMutableCopy(allocator
, 0, (CFStringRef
)propertyList
);
3138 result
= CFStringCreateCopy(allocator
, (CFStringRef
)propertyList
);
3141 CFAssert2(false, __kCFLogAssertion
, "%s(): %p is not a property list type", __PRETTY_FUNCTION__
, propertyList
);