2 * Copyright (c) 2012 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-2012, 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"
66 #define PLIST_TAG_LENGTH 5
67 #define ARRAY_TAG_LENGTH 5
68 #define DICT_TAG_LENGTH 4
69 #define KEY_TAG_LENGTH 3
70 #define STRING_TAG_LENGTH 6
71 #define DATA_TAG_LENGTH 4
72 #define DATE_TAG_LENGTH 4
73 #define REAL_TAG_LENGTH 4
74 #define INTEGER_TAG_LENGTH 7
75 #define TRUE_TAG_LENGTH 4
76 #define FALSE_TAG_LENGTH 5
77 #define DOCTYPE_TAG_LENGTH 7
78 #define CDSECT_TAG_LENGTH 9
80 #if !defined(new_cftype_array)
81 #define new_cftype_array(N, C) \
82 size_t N ## _count__ = (C); \
83 if (N ## _count__ > LONG_MAX / sizeof(CFTypeRef)) { \
84 CRSetCrashLogMessage("CFPropertyList ran out of memory while attempting to allocate temporary storage."); \
87 Boolean N ## _is_stack__ = (N ## _count__ <= 256); \
88 if (N ## _count__ == 0) N ## _count__ = 1; \
89 STACK_BUFFER_DECL(CFTypeRef, N ## _buffer__, N ## _is_stack__ ? N ## _count__ : 1); \
90 if (N ## _is_stack__) memset(N ## _buffer__, 0, N ## _count__ * sizeof(CFTypeRef)); \
91 CFTypeRef * N = N ## _is_stack__ ? N ## _buffer__ : (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, (N ## _count__) * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory); \
93 CRSetCrashLogMessage("CFPropertyList ran out of memory while attempting to allocate temporary storage."); \
99 #if !defined(free_cftype_array)
100 #define free_cftype_array(N) \
101 if (! N ## _is_stack__) { \
102 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFAllocatorDeallocate(kCFAllocatorSystemDefault, N); \
107 // Used to reference an old-style plist parser error inside of a more general XML error
108 #define CFPropertyListOldStyleParserErrorKey CFSTR("kCFPropertyListOldStyleParsingError")
110 static CFTypeID stringtype
, datatype
, numbertype
, datetype
;
111 static CFTypeID booltype
, nulltype
, dicttype
, arraytype
, settype
;
113 static void initStatics() {
114 static dispatch_once_t once
;
115 dispatch_once(&once
, ^{
116 stringtype
= CFStringGetTypeID();
117 datatype
= CFDataGetTypeID();
118 numbertype
= CFNumberGetTypeID();
119 booltype
= CFBooleanGetTypeID();
120 datetype
= CFDateGetTypeID();
121 dicttype
= CFDictionaryGetTypeID();
122 arraytype
= CFArrayGetTypeID();
123 settype
= CFSetGetTypeID();
124 nulltype
= CFNullGetTypeID();
128 __private_extern__ CFErrorRef
__CFPropertyListCreateError(CFIndex code
, CFStringRef debugString
, ...) {
130 CFErrorRef error
= NULL
;
132 if (debugString
!= NULL
) {
133 CFStringRef debugMessage
= NULL
;
134 va_start(argList
, debugString
);
135 debugMessage
= CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault
, NULL
, debugString
, argList
);
138 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
139 CFDictionarySetValue(userInfo
, kCFErrorDebugDescriptionKey
, debugMessage
);
141 error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, code
, userInfo
);
143 CFRelease(debugMessage
);
146 error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, code
, NULL
);
152 static CFStringRef
__copyErrorDebugDescription(CFErrorRef error
) {
153 CFStringRef result
= NULL
;
155 CFDictionaryRef userInfo
= CFErrorCopyUserInfo(error
);
156 result
= CFStringCreateCopy(kCFAllocatorSystemDefault
, (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
));
163 #pragma mark Property List Validation
165 // don't allow _CFKeyedArchiverUID here
166 #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);
171 CFPropertyListFormat format
;
175 static bool __CFPropertyListIsValidAux(CFPropertyListRef plist
, bool recursive
, CFMutableSetRef set
, CFPropertyListFormat format
, CFStringRef
*error
);
177 static void __CFPropertyListIsArrayPlistAux(const void *value
, void *context
) {
178 struct context
*ctx
= (struct context
*)context
;
179 if (!ctx
->answer
) return;
180 if (!value
&& !*(ctx
->error
)) {
181 *(ctx
->error
) = (CFStringRef
)CFRetain(CFSTR("property list arrays cannot contain NULL"));
183 ctx
->answer
= value
&& __CFPropertyListIsValidAux(value
, true, ctx
->set
, ctx
->format
, ctx
->error
);
186 static void __CFPropertyListIsDictPlistAux(const void *key
, const void *value
, void *context
) {
187 struct context
*ctx
= (struct context
*)context
;
188 if (!ctx
->answer
) return;
189 if (!key
&& !*(ctx
->error
)) *(ctx
->error
) = (CFStringRef
)CFRetain(CFSTR("property list dictionaries cannot contain NULL keys"));
190 if (!value
&& !*(ctx
->error
)) *(ctx
->error
) = (CFStringRef
)CFRetain(CFSTR("property list dictionaries cannot contain NULL values"));
191 if (stringtype
!= CFGetTypeID(key
) && !*(ctx
->error
)) {
192 CFStringRef desc
= CFCopyTypeIDDescription(CFGetTypeID(key
));
193 *(ctx
->error
) = CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("property list dictionaries may only have keys which are CFStrings, not '%@'"), desc
);
196 ctx
->answer
= key
&& value
&& (stringtype
== CFGetTypeID(key
)) && __CFPropertyListIsValidAux(value
, true, ctx
->set
, ctx
->format
, ctx
->error
);
199 static bool __CFPropertyListIsValidAux(CFPropertyListRef plist
, bool recursive
, CFMutableSetRef set
, CFPropertyListFormat format
, CFStringRef
*error
) {
202 *error
= (CFStringRef
)CFRetain(CFSTR("property lists cannot contain NULL"));
205 type
= CFGetTypeID(plist
);
206 if (stringtype
== type
) return true;
207 if (datatype
== type
) return true;
208 if (kCFPropertyListOpenStepFormat
!= format
) {
209 if (booltype
== type
) return true;
210 if (numbertype
== type
) return true;
211 if (datetype
== type
) return true;
212 if (_CFKeyedArchiverUIDGetTypeID() == type
) return true;
214 if (!recursive
&& arraytype
== type
) return true;
215 if (!recursive
&& dicttype
== type
) return true;
216 // at any one invocation of this function, set should contain the objects in the "path" down to this object
217 if (CFSetContainsValue(set
, plist
)) {
218 *error
= (CFStringRef
)CFRetain(CFSTR("property lists cannot contain recursive container references"));
221 if (arraytype
== type
) {
222 struct context ctx
= {true, set
, format
, error
};
223 CFSetAddValue(set
, plist
);
224 CFArrayApplyFunction((CFArrayRef
)plist
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
)plist
)), __CFPropertyListIsArrayPlistAux
, &ctx
);
225 CFSetRemoveValue(set
, plist
);
228 if (dicttype
== type
) {
229 struct context ctx
= {true, set
, format
, error
};
230 CFSetAddValue(set
, plist
);
231 CFDictionaryApplyFunction((CFDictionaryRef
)plist
, __CFPropertyListIsDictPlistAux
, &ctx
);
232 CFSetRemoveValue(set
, plist
);
236 CFStringRef desc
= CFCopyTypeIDDescription(type
);
237 *error
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("property lists cannot contain objects of type '%@'"), desc
);
243 static Boolean
_CFPropertyListIsValidWithErrorString(CFPropertyListRef plist
, CFPropertyListFormat format
, CFStringRef
*error
) {
245 CFAssert1(plist
!= NULL
, __kCFLogAssertion
, "%s(): NULL is not a property list", __PRETTY_FUNCTION__
);
246 CFMutableSetRef set
= CFSetCreateMutable(kCFAllocatorSystemDefaultGCRefZero
, 0, NULL
);
247 bool result
= __CFPropertyListIsValidAux(plist
, true, set
, format
, error
);
248 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero
)) CFRelease(set
);
253 #pragma mark Writing Property Lists
255 static const char CFXMLPlistTags
[13][10]= {
256 {'p', 'l', 'i', 's', 't', '\0', '\0', '\0', '\0', '\0'},
257 {'a', 'r', 'r', 'a', 'y', '\0', '\0', '\0', '\0', '\0'},
258 {'d', 'i', 'c', 't', '\0', '\0', '\0', '\0', '\0', '\0'},
259 {'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
260 {'s', 't', 'r', 'i', 'n', 'g', '\0', '\0', '\0', '\0'},
261 {'d', 'a', 't', 'a', '\0', '\0', '\0', '\0', '\0', '\0'},
262 {'d', 'a', 't', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
263 {'r', 'e', 'a', 'l', '\0', '\0', '\0', '\0', '\0', '\0'},
264 {'i', 'n', 't', 'e', 'g', 'e', 'r', '\0', '\0', '\0'},
265 {'t', 'r', 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
266 {'f', 'a', 'l', 's', 'e', '\0', '\0', '\0', '\0', '\0'},
267 {'D', 'O', 'C', 'T', 'Y', 'P', 'E', '\0', '\0', '\0'},
268 {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[', '\0'}
271 static const UniChar CFXMLPlistTagsUnicode
[13][10]= {
272 {'p', 'l', 'i', 's', 't', '\0', '\0', '\0', '\0', '\0'},
273 {'a', 'r', 'r', 'a', 'y', '\0', '\0', '\0', '\0', '\0'},
274 {'d', 'i', 'c', 't', '\0', '\0', '\0', '\0', '\0', '\0'},
275 {'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
276 {'s', 't', 'r', 'i', 'n', 'g', '\0', '\0', '\0', '\0'},
277 {'d', 'a', 't', 'a', '\0', '\0', '\0', '\0', '\0', '\0'},
278 {'d', 'a', 't', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
279 {'r', 'e', 'a', 'l', '\0', '\0', '\0', '\0', '\0', '\0'},
280 {'i', 'n', 't', 'e', 'g', 'e', 'r', '\0', '\0', '\0'},
281 {'t', 'r', 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
282 {'f', 'a', 'l', 's', 'e', '\0', '\0', '\0', '\0', '\0'},
283 {'D', 'O', 'C', 'T', 'Y', 'P', 'E', '\0', '\0', '\0'},
284 {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[', '\0'}
288 const char *begin
; // first character of the XML to be parsed
289 const char *curr
; // current parse location
290 const char *end
; // the first character _after_ the end of the XML
292 CFAllocatorRef allocator
;
293 UInt32 mutabilityOption
;
294 CFBurstTrieRef stringTrie
; // map of cached strings
295 CFMutableArrayRef stringCache
; // retaining array of strings
296 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)
297 CFSetRef keyPaths
; // if NULL, no filtering
298 Boolean skip
; // if true, do not create any objects.
299 } _CFXMLPlistParseInfo
;
301 __private_extern__ CFTypeRef
__CFParseOldStylePropertyListOrStringsFile(CFAllocatorRef allocator
, CFDataRef xmlData
, CFStringRef originalString
, CFStringEncoding guessedEncoding
, CFOptionFlags option
, CFErrorRef
*outError
,CFPropertyListFormat
*format
);
303 CF_INLINE
void __CFPListRelease(CFTypeRef cf
, CFAllocatorRef allocator
) {
304 if (cf
&& !_CFAllocatorIsGCRefZero(allocator
)) CFRelease(cf
);
308 // 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.
310 // Null-terminated, ASCII or UTF8 string
312 static void _plistAppendUTF8CString(CFMutableDataRef mData
, const char *cString
) {
313 CFDataAppendBytes (mData
, (const UInt8
*)cString
, strlen(cString
));
318 static void _plistAppendCharacters(CFMutableDataRef mData
, const UniChar
*chars
, CFIndex length
) {
321 do { // Flush out ASCII chars, BUFLEN at a time
323 UInt8 buf
[BUFLEN
], *bufPtr
= buf
;
325 while (cnt
< length
&& (cnt
- curLoc
< BUFLEN
) && (chars
[cnt
] < 128)) *bufPtr
++ = (UInt8
)(chars
[cnt
++]);
326 if (cnt
> curLoc
) { // Flush any ASCII bytes
327 CFDataAppendBytes(mData
, buf
, cnt
- curLoc
);
330 } while (curLoc
< length
&& (chars
[curLoc
] < 128)); // We will exit out of here when we run out of chars or hit a non-ASCII char
332 if (curLoc
< length
) { // Now deal with non-ASCII chars
333 CFDataRef data
= NULL
;
334 CFStringRef str
= NULL
;
335 if ((str
= CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault
, chars
+ curLoc
, length
- curLoc
, kCFAllocatorNull
))) {
336 if ((data
= CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
, 0))) {
337 CFDataAppendBytes (mData
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
342 CFAssert1(str
&& data
, __kCFLogAssertion
, "%s(): Error writing plist", __PRETTY_FUNCTION__
);
348 static void _plistAppendString(CFMutableDataRef mData
, CFStringRef str
) {
349 const UniChar
*chars
;
352 if ((chars
= CFStringGetCharactersPtr(str
))) {
353 _plistAppendCharacters(mData
, chars
, CFStringGetLength(str
));
354 } else if ((cStr
= CFStringGetCStringPtr(str
, kCFStringEncodingASCII
)) || (cStr
= CFStringGetCStringPtr(str
, kCFStringEncodingUTF8
))) {
355 _plistAppendUTF8CString(mData
, cStr
);
356 } else if ((data
= CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
, 0))) {
357 CFDataAppendBytes (mData
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
360 CFAssert1(TRUE
, __kCFLogAssertion
, "%s(): Error in plist writing", __PRETTY_FUNCTION__
);
365 // Append CFString-style format + arguments
367 static void _plistAppendFormat(CFMutableDataRef mData
, CFStringRef format
, ...) {
371 va_start(argList
, format
);
372 fStr
= CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault
, NULL
, format
, argList
);
375 CFAssert1(fStr
, __kCFLogAssertion
, "%s(): Error writing plist", __PRETTY_FUNCTION__
);
376 _plistAppendString(mData
, fStr
);
382 static void _appendIndents(CFIndex numIndents
, CFMutableDataRef str
) {
384 static const UniChar tabs
[NUMTABS
] = {'\t','\t','\t','\t'};
385 for (; numIndents
> 0; numIndents
-= NUMTABS
) _plistAppendCharacters(str
, tabs
, (numIndents
>= NUMTABS
) ? NUMTABS
: numIndents
);
388 /* Append the escaped version of origStr to mStr.
390 static void _appendEscapedString(CFStringRef origStr
, CFMutableDataRef mStr
) {
392 CFIndex i
, length
= CFStringGetLength(origStr
);
394 UniChar buf
[BUFSIZE
];
395 CFStringInlineBuffer inlineBuffer
;
397 CFStringInitInlineBuffer(origStr
, &inlineBuffer
, CFRangeMake(0, length
));
399 for (i
= 0; i
< length
; i
++) {
400 UniChar ch
= __CFStringGetCharacterFromInlineBufferQuick(&inlineBuffer
, i
);
401 if (CFStringIsSurrogateHighCharacter(ch
) && (bufCnt
+ 2 >= BUFSIZE
)) {
402 // flush the buffer first so we have room for a low/high pair and do not split them
403 _plistAppendCharacters(mStr
, buf
, bufCnt
);
409 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
411 _plistAppendUTF8CString(mStr
, "<");
414 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
416 _plistAppendUTF8CString(mStr
, ">");
419 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
421 _plistAppendUTF8CString(mStr
, "&");
425 if (bufCnt
== BUFSIZE
) {
426 _plistAppendCharacters(mStr
, buf
, bufCnt
);
432 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
437 /* Base-64 encoding/decoding */
439 /* The base-64 encoding packs three 8-bit bytes into four 7-bit ASCII
440 * characters. If the number of bytes in the original data isn't divisable
441 * by three, "=" characters are used to pad the encoded data. The complete
442 * set of characters used in base-64 are:
452 // Write the inputData to the mData using Base 64 encoding
454 static void _XMLPlistAppendDataUsingBase64(CFMutableDataRef mData
, CFDataRef inputData
, CFIndex indent
) {
455 static const char __CFPLDataEncodeTable
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
456 #define MAXLINELEN 76
457 char buf
[MAXLINELEN
+ 4 + 2]; // For the slop and carriage return and terminating NULL
459 const uint8_t *bytes
= CFDataGetBytePtr(inputData
);
460 CFIndex length
= CFDataGetLength(inputData
);
464 if (indent
> 8) indent
= 8; // refuse to indent more than 64 characters
466 pos
= 0; // position within buf
468 for (i
= 0, p
= bytes
; i
< length
; i
++, p
++) {
469 /* 3 bytes are encoded as 4 */
472 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[0] >> 2) & 0x3f)];
475 buf
[pos
++] = __CFPLDataEncodeTable
[ ((((p
[-1] << 8) | p
[0]) >> 4) & 0x3f)];
478 buf
[pos
++] = __CFPLDataEncodeTable
[ ((((p
[-1] << 8) | p
[0]) >> 6) & 0x3f)];
479 buf
[pos
++] = __CFPLDataEncodeTable
[ (p
[0] & 0x3f)];
482 /* Flush the line out every 76 (or fewer) chars --- indents count against the line length*/
483 if (pos
>= MAXLINELEN
- 8 * indent
) {
486 _appendIndents(indent
, mData
);
487 _plistAppendUTF8CString(mData
, buf
);
496 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[-1] << 4) & 0x30)];
501 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[-1] << 2) & 0x3c)];
509 _appendIndents(indent
, mData
);
510 _plistAppendUTF8CString(mData
, buf
);
514 extern CFStringRef
__CFNumberCopyFormattingDescriptionAsFloat64(CFTypeRef cf
);
516 static void _CFAppendXML0(CFTypeRef object
, UInt32 indentation
, CFMutableDataRef xmlString
) {
517 UInt32 typeID
= CFGetTypeID(object
);
518 _appendIndents(indentation
, xmlString
);
519 if (typeID
== stringtype
) {
520 _plistAppendUTF8CString(xmlString
, "<");
521 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[STRING_IX
], STRING_TAG_LENGTH
);
522 _plistAppendUTF8CString(xmlString
, ">");
523 _appendEscapedString((CFStringRef
)object
, xmlString
);
524 _plistAppendUTF8CString(xmlString
, "</");
525 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[STRING_IX
], STRING_TAG_LENGTH
);
526 _plistAppendUTF8CString(xmlString
, ">\n");
527 } else if (typeID
== arraytype
) {
528 UInt32 i
, count
= CFArrayGetCount((CFArrayRef
)object
);
530 _plistAppendUTF8CString(xmlString
, "<");
531 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
532 _plistAppendUTF8CString(xmlString
, "/>\n");
535 _plistAppendUTF8CString(xmlString
, "<");
536 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
537 _plistAppendUTF8CString(xmlString
, ">\n");
538 for (i
= 0; i
< count
; i
++) {
539 _CFAppendXML0(CFArrayGetValueAtIndex((CFArrayRef
)object
, i
), indentation
+1, xmlString
);
541 _appendIndents(indentation
, xmlString
);
542 _plistAppendUTF8CString(xmlString
, "</");
543 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
544 _plistAppendUTF8CString(xmlString
, ">\n");
545 } else if (typeID
== dicttype
) {
546 UInt32 i
, count
= CFDictionaryGetCount((CFDictionaryRef
)object
);
547 CFMutableArrayRef keyArray
;
549 _plistAppendUTF8CString(xmlString
, "<");
550 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DICT_IX
], DICT_TAG_LENGTH
);
551 _plistAppendUTF8CString(xmlString
, "/>\n");
554 _plistAppendUTF8CString(xmlString
, "<");
555 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DICT_IX
], DICT_TAG_LENGTH
);
556 _plistAppendUTF8CString(xmlString
, ">\n");
557 new_cftype_array(keys
, count
);
558 CFDictionaryGetKeysAndValues((CFDictionaryRef
)object
, keys
, NULL
);
559 keyArray
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, count
, &kCFTypeArrayCallBacks
);
560 CFArrayReplaceValues(keyArray
, CFRangeMake(0, 0), keys
, count
);
561 CFArraySortValues(keyArray
, CFRangeMake(0, count
), (CFComparatorFunction
)CFStringCompare
, NULL
);
562 CFArrayGetValues(keyArray
, CFRangeMake(0, count
), keys
);
564 for (i
= 0; i
< count
; i
++) {
565 CFTypeRef key
= keys
[i
];
566 _appendIndents(indentation
+1, xmlString
);
567 _plistAppendUTF8CString(xmlString
, "<");
568 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[KEY_IX
], KEY_TAG_LENGTH
);
569 _plistAppendUTF8CString(xmlString
, ">");
570 _appendEscapedString((CFStringRef
)key
, xmlString
);
571 _plistAppendUTF8CString(xmlString
, "</");
572 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[KEY_IX
], KEY_TAG_LENGTH
);
573 _plistAppendUTF8CString(xmlString
, ">\n");
574 _CFAppendXML0(CFDictionaryGetValue((CFDictionaryRef
)object
, key
), indentation
+1, xmlString
);
576 free_cftype_array(keys
);
577 _appendIndents(indentation
, xmlString
);
578 _plistAppendUTF8CString(xmlString
, "</");
579 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DICT_IX
], DICT_TAG_LENGTH
);
580 _plistAppendUTF8CString(xmlString
, ">\n");
581 } else if (typeID
== datatype
) {
582 _plistAppendUTF8CString(xmlString
, "<");
583 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DATA_IX
], DATA_TAG_LENGTH
);
584 _plistAppendUTF8CString(xmlString
, ">\n");
585 _XMLPlistAppendDataUsingBase64(xmlString
, (CFDataRef
)object
, indentation
);
586 _appendIndents(indentation
, xmlString
);
587 _plistAppendUTF8CString(xmlString
, "</");
588 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DATA_IX
], DATA_TAG_LENGTH
);
589 _plistAppendUTF8CString(xmlString
, ">\n");
590 } else if (typeID
== datetype
) {
591 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
592 int32_t y
= 0, M
= 0, d
= 0, H
= 0, m
= 0, s
= 0;
594 CFGregorianDate date
= CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime((CFDateRef
)object
), NULL
);
600 s
= (int32_t)date
.second
;
602 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, kCFCalendarIdentifierGregorian
);
603 CFTimeZoneRef tz
= CFTimeZoneCreateWithName(kCFAllocatorSystemDefault
, CFSTR("GMT"), true);
604 CFCalendarSetTimeZone(calendar
, tz
);
605 CFCalendarDecomposeAbsoluteTime(calendar
, CFDateGetAbsoluteTime((CFDateRef
)object
), (const uint8_t *)"yMdHms", &y
, &M
, &d
, &H
, &m
, &s
);
609 _plistAppendUTF8CString(xmlString
, "<");
610 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DATE_IX
], DATE_TAG_LENGTH
);
611 _plistAppendUTF8CString(xmlString
, ">");
612 _plistAppendFormat(xmlString
, CFSTR("%04d-%02d-%02dT%02d:%02d:%02dZ"), y
, M
, d
, H
, m
, s
);
613 _plistAppendUTF8CString(xmlString
, "</");
614 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DATE_IX
], DATE_TAG_LENGTH
);
615 _plistAppendUTF8CString(xmlString
, ">\n");
616 } else if (typeID
== numbertype
) {
617 if (CFNumberIsFloatType((CFNumberRef
)object
)) {
618 _plistAppendUTF8CString(xmlString
, "<");
619 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[REAL_IX
], REAL_TAG_LENGTH
);
620 _plistAppendUTF8CString(xmlString
, ">");
621 CFStringRef s
= __CFNumberCopyFormattingDescriptionAsFloat64(object
);
622 _plistAppendString(xmlString
, s
);
624 _plistAppendUTF8CString(xmlString
, "</");
625 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[REAL_IX
], REAL_TAG_LENGTH
);
626 _plistAppendUTF8CString(xmlString
, ">\n");
628 _plistAppendUTF8CString(xmlString
, "<");
629 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
630 _plistAppendUTF8CString(xmlString
, ">");
632 _plistAppendFormat(xmlString
, CFSTR("%@"), object
);
634 _plistAppendUTF8CString(xmlString
, "</");
635 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
636 _plistAppendUTF8CString(xmlString
, ">\n");
638 } else if (typeID
== booltype
) {
639 if (CFBooleanGetValue((CFBooleanRef
)object
)) {
640 _plistAppendUTF8CString(xmlString
, "<");
641 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[TRUE_IX
], TRUE_TAG_LENGTH
);
642 _plistAppendUTF8CString(xmlString
, "/>\n");
644 _plistAppendUTF8CString(xmlString
, "<");
645 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[FALSE_IX
], FALSE_TAG_LENGTH
);
646 _plistAppendUTF8CString(xmlString
, "/>\n");
651 static void _CFGenerateXMLPropertyListToData(CFMutableDataRef xml
, CFTypeRef propertyList
) {
652 _plistAppendUTF8CString(xml
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE ");
653 _plistAppendCharacters(xml
, CFXMLPlistTagsUnicode
[PLIST_IX
], PLIST_TAG_LENGTH
);
654 _plistAppendUTF8CString(xml
, " PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<");
655 _plistAppendCharacters(xml
, CFXMLPlistTagsUnicode
[PLIST_IX
], PLIST_TAG_LENGTH
);
656 _plistAppendUTF8CString(xml
, " version=\"1.0\">\n");
658 _CFAppendXML0(propertyList
, 0, xml
);
660 _plistAppendUTF8CString(xml
, "</");
661 _plistAppendCharacters(xml
, CFXMLPlistTagsUnicode
[PLIST_IX
], PLIST_TAG_LENGTH
);
662 _plistAppendUTF8CString(xml
, ">\n");
665 // ========================================================================
667 #pragma mark Exported Creation Functions
669 CFDataRef
_CFPropertyListCreateXMLData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, Boolean checkValidPlist
) {
671 CFMutableDataRef xml
;
672 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
673 if (checkValidPlist
&& !CFPropertyListIsValid(propertyList
, kCFPropertyListXMLFormat_v1_0
)) {
674 __CFAssertIsPList(propertyList
);
677 xml
= CFDataCreateMutable(allocator
, 0);
678 _CFGenerateXMLPropertyListToData(xml
, propertyList
);
682 CFDataRef
CFPropertyListCreateXMLData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
) {
683 return _CFPropertyListCreateXMLData(allocator
, propertyList
, true);
686 CF_EXPORT CFDataRef
_CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator
, CFPropertyListRef propertyList
) {
687 return _CFPropertyListCreateXMLData(allocator
, propertyList
, false);
690 Boolean
CFPropertyListIsValid(CFPropertyListRef plist
, CFPropertyListFormat format
) {
692 CFAssert1(plist
!= NULL
, __kCFLogAssertion
, "%s(): NULL is not a property list", __PRETTY_FUNCTION__
);
693 CFMutableSetRef set
= CFSetCreateMutable(kCFAllocatorSystemDefaultGCRefZero
, 0, NULL
);
694 CFStringRef error
= NULL
;
695 bool result
= __CFPropertyListIsValidAux(plist
, true, set
, format
, &error
);
698 CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListIsValid(): %@"), error
);
702 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero
)) CFRelease(set
);
706 // ========================================================================
708 #pragma mark Reading Plists
711 // ------------------------- Reading plists ------------------
714 static void skipInlineDTD(_CFXMLPlistParseInfo
*pInfo
);
715 static Boolean
parseXMLElement(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
, CFTypeRef
*out
);
717 // warning: doesn't have a good idea of Unicode line separators
718 static UInt32
lineNumber(_CFXMLPlistParseInfo
*pInfo
) {
719 const char *p
= pInfo
->begin
;
721 while (p
< pInfo
->curr
) {
724 if (*(p
+ 1) == '\n')
726 } else if (*p
== '\n') {
734 // warning: doesn't have a good idea of Unicode white space
735 CF_INLINE
void skipWhitespace(_CFXMLPlistParseInfo
*pInfo
) {
736 while (pInfo
->curr
< pInfo
->end
) {
737 switch (*(pInfo
->curr
)) {
750 /* 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. */
752 // pInfo should be just past "<!--"
753 static void skipXMLComment(_CFXMLPlistParseInfo
*pInfo
) {
754 const char *p
= pInfo
->curr
;
755 const char *end
= pInfo
->end
- 3; // Need at least 3 characters to compare against
757 if (*p
== '-' && *(p
+1) == '-' && *(p
+2) == '>') {
763 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unterminated comment started on line %d"), lineNumber(pInfo
));
766 // pInfo should be set to the first character after "<?"
767 static void skipXMLProcessingInstruction(_CFXMLPlistParseInfo
*pInfo
) {
768 const char *begin
= pInfo
->curr
, *end
= pInfo
->end
- 2; // Looking for "?>" so we need at least 2 characters
769 while (pInfo
->curr
< end
) {
770 if (*(pInfo
->curr
) == '?' && *(pInfo
->curr
+1) == '>') {
777 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing the processing instruction begun on line %d"), lineNumber(pInfo
));
780 // first character should be immediately after the "<!"
781 static void skipDTD(_CFXMLPlistParseInfo
*pInfo
) {
782 // First pass "DOCTYPE"
783 if (pInfo
->end
- pInfo
->curr
< DOCTYPE_TAG_LENGTH
|| memcmp(pInfo
->curr
, CFXMLPlistTags
[DOCTYPE_IX
], DOCTYPE_TAG_LENGTH
)) {
784 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Malformed DTD on line %d"), lineNumber(pInfo
));
787 pInfo
->curr
+= DOCTYPE_TAG_LENGTH
;
788 skipWhitespace(pInfo
);
790 // Look for either the beginning of a complex DTD or the end of the DOCTYPE structure
791 while (pInfo
->curr
< pInfo
->end
) {
792 char ch
= *(pInfo
->curr
);
793 if (ch
== '[') break; // inline DTD
794 if (ch
== '>') { // End of the DTD
800 if (pInfo
->curr
== pInfo
->end
) {
801 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing DTD"));
805 // *Sigh* Must parse in-line DTD
806 skipInlineDTD(pInfo
);
807 if (pInfo
->error
) return;
808 skipWhitespace(pInfo
);
809 if (pInfo
->error
) return;
810 if (pInfo
->curr
< pInfo
->end
) {
811 if (*(pInfo
->curr
) == '>') {
814 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while parsing DTD"), *(pInfo
->curr
), lineNumber(pInfo
));
817 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing DTD"));
821 static void skipPERef(_CFXMLPlistParseInfo
*pInfo
) {
822 const char *p
= pInfo
->curr
;
823 while (p
< pInfo
->end
) {
830 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing percent-escape sequence begun on line %d"), lineNumber(pInfo
));
833 // First character should be just past '['
834 static void skipInlineDTD(_CFXMLPlistParseInfo
*pInfo
) {
835 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
837 skipWhitespace(pInfo
);
842 } else if (ch
== '<') {
844 if (pInfo
->curr
>= pInfo
->end
) {
845 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
851 skipXMLProcessingInstruction(pInfo
);
852 } else if (ch
== '!') {
853 if (pInfo
->curr
+ 2 < pInfo
->end
&& (*(pInfo
->curr
+1) == '-' && *(pInfo
->curr
+2) == '-')) {
855 skipXMLComment(pInfo
);
857 // Skip the myriad of DTD declarations of the form "<!string" ... ">"
858 pInfo
->curr
++; // Past both '<' and '!'
859 while (pInfo
->curr
< pInfo
->end
) {
860 if (*(pInfo
->curr
) == '>') break;
863 if (*(pInfo
->curr
) != '>') {
864 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
870 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch
, lineNumber(pInfo
));
873 } else if (ch
== ']') {
877 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch
, lineNumber(pInfo
));
882 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
886 // content ::== (element | CharData | Reference | CDSect | PI | Comment)*
887 // 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).
888 static Boolean
getContentObject(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
, CFTypeRef
*out
) {
889 if (isKey
) *isKey
= false;
890 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
891 skipWhitespace(pInfo
);
892 if (pInfo
->curr
>= pInfo
->end
) {
893 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
896 if (*(pInfo
->curr
) != '<') {
897 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while looking for open tag"), *(pInfo
->curr
), lineNumber(pInfo
));
901 if (pInfo
->curr
>= pInfo
->end
) {
902 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
905 switch (*(pInfo
->curr
)) {
907 // Processing instruction
908 skipXMLProcessingInstruction(pInfo
);
911 // Could be a comment
912 if (pInfo
->curr
+2 >= pInfo
->end
) {
913 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
916 if (*(pInfo
->curr
+1) == '-' && *(pInfo
->curr
+2) == '-') {
918 skipXMLComment(pInfo
);
920 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
925 // Whoops! Looks like we got to the end tag for the element whose content we're parsing
926 pInfo
->curr
--; // Back off to the '<'
929 // Should be an element
930 return parseXMLElement(pInfo
, isKey
, out
);
933 // 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"
937 static void parseCDSect_pl(_CFXMLPlistParseInfo
*pInfo
, CFMutableDataRef stringData
) {
938 const char *end
, *begin
;
939 if (pInfo
->end
- pInfo
->curr
< CDSECT_TAG_LENGTH
) {
940 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
943 if (memcmp(pInfo
->curr
, CFXMLPlistTags
[CDSECT_IX
], CDSECT_TAG_LENGTH
)) {
944 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered improper CDATA opening at line %d"), lineNumber(pInfo
));
947 pInfo
->curr
+= CDSECT_TAG_LENGTH
;
948 begin
= pInfo
->curr
; // Marks the first character of the CDATA content
949 end
= pInfo
->end
-2; // So we can safely look 2 characters beyond p
950 while (pInfo
->curr
< end
) {
951 if (*(pInfo
->curr
) == ']' && *(pInfo
->curr
+1) == ']' && *(pInfo
->curr
+2) == '>') {
953 CFDataAppendBytes(stringData
, (const UInt8
*)begin
, pInfo
->curr
-begin
);
959 // Never found the end mark
961 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not find end of CDATA started on line %d"), lineNumber(pInfo
));
964 // Only legal references are {lt, gt, amp, apos, quote, #ddd, #xAAA}
965 static void parseEntityReference_pl(_CFXMLPlistParseInfo
*pInfo
, CFMutableDataRef stringData
) {
967 pInfo
->curr
++; // move past the '&';
968 len
= pInfo
->end
- pInfo
->curr
; // how many bytes we can safely scan
970 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
975 switch (*(pInfo
->curr
)) {
977 if (len
>= 3 && *(pInfo
->curr
+1) == 't' && *(pInfo
->curr
+2) == ';') {
982 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
985 if (len
>= 3 && *(pInfo
->curr
+1) == 't' && *(pInfo
->curr
+2) == ';') {
990 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
992 case 'a': // "apos" or "amp"
993 if (len
< 4) { // Not enough characters for either conversion
994 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
997 if (*(pInfo
->curr
+1) == 'm') {
999 if (*(pInfo
->curr
+2) == 'p' && *(pInfo
->curr
+3) == ';') {
1004 } else if (*(pInfo
->curr
+1) == 'p') {
1006 if (len
> 4 && *(pInfo
->curr
+2) == 'o' && *(pInfo
->curr
+3) == 's' && *(pInfo
->curr
+4) == ';') {
1012 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1014 case 'q': // "quote"
1015 if (len
>= 5 && *(pInfo
->curr
+1) == 'u' && *(pInfo
->curr
+2) == 'o' && *(pInfo
->curr
+3) == 't' && *(pInfo
->curr
+4) == ';') {
1020 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1025 Boolean isHex
= false;
1026 if ( len
< 4) { // Not enough characters to make it all fit! Need at least "&#d;"
1027 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1031 if (*(pInfo
->curr
) == 'x') {
1035 while (pInfo
->curr
< pInfo
->end
) {
1036 ch
= *(pInfo
->curr
);
1039 // The value in num always refers to the unicode code point. We'll have to convert since the calling function expects UTF8 data.
1040 CFStringRef oneChar
= CFStringCreateWithBytes(pInfo
->allocator
, (const uint8_t *)&num
, 2, kCFStringEncodingUnicode
, NO
);
1041 uint8_t tmpBuf
[6]; // max of 6 bytes for UTF8
1042 CFIndex tmpBufLength
= 0;
1043 CFStringGetBytes(oneChar
, CFRangeMake(0, 1), kCFStringEncodingUTF8
, 0, NO
, tmpBuf
, 6, &tmpBufLength
);
1044 CFDataAppendBytes(stringData
, tmpBuf
, tmpBufLength
);
1045 __CFPListRelease(oneChar
, pInfo
->allocator
);
1048 if (!isHex
) num
= num
*10;
1049 else num
= num
<< 4;
1050 if (ch
<= '9' && ch
>= '0') {
1052 } else if (!isHex
) {
1053 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c at line %d while parsing data"), ch
, lineNumber(pInfo
));
1055 } else if (ch
>= 'a' && ch
<= 'f') {
1056 num
+= 10 + (ch
- 'a');
1057 } else if (ch
>= 'A' && ch
<= 'F') {
1058 num
+= 10 + (ch
- 'A');
1060 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c at line %d while parsing data"), ch
, lineNumber(pInfo
));
1064 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1068 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1071 CFDataAppendBytes(stringData
, (const UInt8
*)&ch
, 1);
1074 extern void _CFStrSetDesiredCapacity(CFMutableStringRef str
, CFIndex len
);
1076 static void _createStringMap(_CFXMLPlistParseInfo
*pInfo
) {
1077 pInfo
->stringTrie
= CFBurstTrieCreate();
1078 pInfo
->stringCache
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
1081 static void _cleanupStringMap(_CFXMLPlistParseInfo
*pInfo
) {
1082 CFBurstTrieRelease(pInfo
->stringTrie
);
1083 if (!_CFAllocatorIsGCRefZero(pInfo
->allocator
)) CFRelease(pInfo
->stringCache
);
1086 static CFStringRef
_uniqueStringForUTF8Bytes(_CFXMLPlistParseInfo
*pInfo
, const char *base
, CFIndex length
) {
1087 if (length
== 0) return !_CFAllocatorIsGCRefZero(pInfo
->allocator
) ? (CFStringRef
)CFRetain(CFSTR("")) : CFSTR("");
1089 CFStringRef result
= NULL
;
1090 uint32_t payload
= 0;
1091 Boolean uniqued
= CFBurstTrieContainsUTF8String(pInfo
->stringTrie
, (UInt8
*)base
, length
, &payload
);
1093 result
= (CFStringRef
)CFArrayGetValueAtIndex(pInfo
->stringCache
, (CFIndex
)payload
);
1094 if (!_CFAllocatorIsGCRefZero(pInfo
->allocator
)) CFRetain(result
);
1096 result
= CFStringCreateWithBytes(pInfo
->allocator
, (const UInt8
*)base
, length
, kCFStringEncodingUTF8
, NO
);
1097 if (!result
) return NULL
;
1098 payload
= CFArrayGetCount(pInfo
->stringCache
);
1099 CFArrayAppendValue(pInfo
->stringCache
, result
);
1100 CFBurstTrieAddUTF8String(pInfo
->stringTrie
, (UInt8
*)base
, length
, payload
);
1105 // String could be comprised of characters, CDSects, or references to one of the "well-known" entities ('<', '>', '&', ''', '"')
1106 static Boolean
parseStringTag(_CFXMLPlistParseInfo
*pInfo
, CFStringRef
*out
) {
1107 const char *mark
= pInfo
->curr
;
1108 CFMutableDataRef stringData
= NULL
;
1109 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
1110 char ch
= *(pInfo
->curr
);
1112 if (pInfo
->curr
+ 1 >= pInfo
->end
) break;
1113 // Could be a CDSect; could be the end of the string
1114 if (*(pInfo
->curr
+1) != '!') break; // End of the string
1115 if (!stringData
) stringData
= CFDataCreateMutable(pInfo
->allocator
, 0);
1116 CFDataAppendBytes(stringData
, (const UInt8
*)mark
, pInfo
->curr
- mark
);
1117 parseCDSect_pl(pInfo
, stringData
); // TODO: move to return boolean
1119 } else if (ch
== '&') {
1120 if (!stringData
) stringData
= CFDataCreateMutable(pInfo
->allocator
, 0);
1121 CFDataAppendBytes(stringData
, (const UInt8
*)mark
, pInfo
->curr
- mark
);
1122 parseEntityReference_pl(pInfo
, stringData
); // TODO: move to return boolean
1130 __CFPListRelease(stringData
, pInfo
->allocator
);
1138 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
1139 CFStringRef s
= _uniqueStringForUTF8Bytes(pInfo
, mark
, pInfo
->curr
- mark
);
1141 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unable to convert string to correct encoding"));
1146 CFStringRef s
= CFStringCreateWithBytes(pInfo
->allocator
, (const UInt8
*)mark
, pInfo
->curr
- mark
, kCFStringEncodingUTF8
, NO
);
1148 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unable to convert string to correct encoding"));
1151 *out
= CFStringCreateMutableCopy(pInfo
->allocator
, 0, s
);
1152 __CFPListRelease(s
, pInfo
->allocator
);
1160 CFDataAppendBytes(stringData
, (const UInt8
*)mark
, pInfo
->curr
- mark
);
1161 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
1162 CFStringRef s
= _uniqueStringForUTF8Bytes(pInfo
, (const char *)CFDataGetBytePtr(stringData
), CFDataGetLength(stringData
));
1164 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unable to convert string to correct encoding"));
1169 CFStringRef s
= CFStringCreateWithBytes(pInfo
->allocator
, (const UInt8
*)CFDataGetBytePtr(stringData
), CFDataGetLength(stringData
), kCFStringEncodingUTF8
, NO
);
1171 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unable to convert string to correct encoding"));
1174 *out
= CFStringCreateMutableCopy(pInfo
->allocator
, 0, s
);
1175 __CFPListRelease(s
, pInfo
->allocator
);
1178 __CFPListRelease(stringData
, pInfo
->allocator
);
1183 static Boolean
checkForCloseTag(_CFXMLPlistParseInfo
*pInfo
, const char *tag
, CFIndex tagLen
) {
1184 if (pInfo
->end
- pInfo
->curr
< tagLen
+ 3) {
1185 if (!pInfo
->error
) {
1186 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1190 if (*(pInfo
->curr
) != '<' || *(++pInfo
->curr
) != '/') {
1191 if (!pInfo
->error
) {
1192 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while looking for close tag"), *(pInfo
->curr
), lineNumber(pInfo
));
1197 if (memcmp(pInfo
->curr
, tag
, tagLen
)) {
1198 CFStringRef str
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, (const UInt8
*)tag
, tagLen
, kCFStringEncodingUTF8
, NO
);
1199 if (!pInfo
->error
) {
1200 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Close tag on line %d does not match open tag %@"), lineNumber(pInfo
), str
);
1205 pInfo
->curr
+= tagLen
;
1206 skipWhitespace(pInfo
);
1207 if (pInfo
->curr
== pInfo
->end
) {
1208 if (!pInfo
->error
) {
1209 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1213 if (*(pInfo
->curr
) != '>') {
1214 if (!pInfo
->error
) {
1215 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while looking for close tag"), *(pInfo
->curr
), lineNumber(pInfo
));
1223 // pInfo should be set to the first content character of the <plist>
1224 static Boolean
parsePListTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1225 CFTypeRef result
= NULL
;
1226 if (!getContentObject(pInfo
, NULL
, &result
)) {
1227 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty plist tag"));
1230 const char *save
= pInfo
->curr
; // Save this in case the next step fails
1231 CFTypeRef tmp
= NULL
;
1232 if (getContentObject(pInfo
, NULL
, &tmp
)) {
1233 // Got an extra object
1234 __CFPListRelease(tmp
, pInfo
->allocator
);
1235 __CFPListRelease(result
, pInfo
->allocator
);
1237 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected element at line %d (plist can only include one object)"), lineNumber(pInfo
));
1241 // Parse failed catastrophically
1242 __CFPListRelease(result
, pInfo
->allocator
);
1245 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
)) {
1246 __CFPListRelease(result
, pInfo
->allocator
);
1253 static int allowImmutableCollections
= -1;
1255 static void checkImmutableCollections(void) {
1256 allowImmutableCollections
= (NULL
== __CFgetenv("CFPropertyListAllowImmutableCollections")) ? 0 : 1;
1259 // 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
1260 static CFSetRef
createTopLevelKeypaths(CFAllocatorRef allocator
, CFSetRef keyPaths
) {
1261 if (!keyPaths
) return NULL
;
1263 CFIndex count
= CFSetGetCount(keyPaths
);
1264 new_cftype_array(keyPathValues
, count
);
1265 CFSetGetValues(keyPaths
, keyPathValues
);
1266 CFMutableSetRef splitKeyPathSet
= CFSetCreateMutable(allocator
, count
, &kCFTypeSetCallBacks
);
1267 for (CFIndex i
= 0; i
< count
; i
++) {
1268 // Split each key path and add it to the split path set, which we will reference throughout parsing
1269 CFArrayRef split
= CFStringCreateArrayBySeparatingStrings(allocator
, (CFStringRef
)(keyPathValues
[i
]), CFSTR(":"));
1270 CFSetAddValue(splitKeyPathSet
, split
);
1271 __CFPListRelease(split
, allocator
);
1273 free_cftype_array(keyPathValues
);
1274 return splitKeyPathSet
;
1277 // 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)
1278 __private_extern__
void __CFPropertyListCreateSplitKeypaths(CFAllocatorRef allocator
, CFSetRef currentKeys
, CFSetRef
*theseKeys
, CFSetRef
*nextKeys
) {
1279 if (!currentKeys
) { *theseKeys
= NULL
; *nextKeys
= NULL
; return; }
1281 CFIndex count
= CFSetGetCount(currentKeys
);
1283 // 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.
1284 CFMutableSetRef outTheseKeys
= NULL
;
1285 CFMutableSetRef outNextKeys
= NULL
;
1287 new_cftype_array(currentKeyPaths
, count
);
1288 CFSetGetValues(currentKeys
, currentKeyPaths
);
1289 for (CFIndex i
= 0; i
< count
; i
++) {
1290 CFArrayRef oneKeyPath
= (CFArrayRef
)currentKeyPaths
[i
];
1291 CFIndex keyPathCount
= CFArrayGetCount(oneKeyPath
);
1293 if (keyPathCount
> 0) {
1294 if (!outTheseKeys
) outTheseKeys
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
1296 CFSetAddValue(outTheseKeys
, CFArrayGetValueAtIndex(oneKeyPath
, 0));
1299 if (keyPathCount
> 1) {
1300 if (!outNextKeys
) outNextKeys
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
1302 // Create an array with values from 1 - end of list
1303 new_cftype_array(restOfKeys
, keyPathCount
- 1);
1304 CFArrayGetValues(oneKeyPath
, CFRangeMake(1, CFArrayGetCount(oneKeyPath
) - 1), restOfKeys
);
1305 CFArrayRef newNextKeys
= CFArrayCreate(allocator
, restOfKeys
, CFArrayGetCount(oneKeyPath
) - 1, &kCFTypeArrayCallBacks
);
1306 CFSetAddValue(outNextKeys
, newNextKeys
);
1307 __CFPListRelease(newNextKeys
, allocator
);
1308 free_cftype_array(restOfKeys
);
1313 *theseKeys
= outTheseKeys
;
1314 *nextKeys
= outNextKeys
;
1317 static Boolean
parseArrayTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1318 CFTypeRef tmp
= NULL
;
1321 Boolean result
= getContentObject(pInfo
, NULL
, &tmp
);
1324 // Shouldn't happen (if skipping, all content values should be null), but just in case
1325 __CFPListRelease(tmp
, pInfo
->allocator
);
1327 result
= getContentObject(pInfo
, NULL
, &tmp
);
1331 // getContentObject encountered a parse error
1334 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
)) {
1342 CFMutableArrayRef array
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
1346 CFSetRef oldKeyPaths
= pInfo
->keyPaths
;
1347 CFSetRef newKeyPaths
, keys
;
1348 __CFPropertyListCreateSplitKeypaths(pInfo
->allocator
, pInfo
->keyPaths
, &keys
, &newKeyPaths
);
1351 CFStringRef countString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("%ld"), count
);
1352 if (!CFSetContainsValue(keys
, countString
)) pInfo
->skip
= true;
1353 __CFPListRelease(countString
, pInfo
->allocator
);
1355 pInfo
->keyPaths
= newKeyPaths
;
1357 result
= getContentObject(pInfo
, NULL
, &tmp
);
1359 pInfo
->keyPaths
= oldKeyPaths
;
1360 pInfo
->skip
= false;
1365 CFArrayAppendValue(array
, tmp
);
1366 __CFPListRelease(tmp
, pInfo
->allocator
);
1370 // prep for getting next object
1371 CFStringRef countString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("%ld"), count
);
1372 if (!CFSetContainsValue(keys
, countString
)) pInfo
->skip
= true;
1373 __CFPListRelease(countString
, pInfo
->allocator
);
1375 pInfo
->keyPaths
= newKeyPaths
;
1377 result
= getContentObject(pInfo
, NULL
, &tmp
);
1379 // reset after getting object
1380 pInfo
->keyPaths
= oldKeyPaths
;
1381 pInfo
->skip
= false;
1386 __CFPListRelease(newKeyPaths
, pInfo
->allocator
);
1387 __CFPListRelease(keys
, pInfo
->allocator
);
1389 if (pInfo
->error
) { // getContentObject encountered a parse error
1390 __CFPListRelease(array
, pInfo
->allocator
);
1393 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
)) {
1394 __CFPListRelease(array
, pInfo
->allocator
);
1397 if (-1 == allowImmutableCollections
) {
1398 checkImmutableCollections();
1400 if (1 == allowImmutableCollections
) {
1401 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1402 CFArrayRef newArray
= CFArrayCreateCopy(pInfo
->allocator
, array
);
1403 __CFPListRelease(array
, pInfo
->allocator
);
1404 array
= (CFMutableArrayRef
)newArray
;
1411 static Boolean
parseDictTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1414 CFTypeRef key
= NULL
, value
= NULL
;
1417 result
= getContentObject(pInfo
, &gotKey
, &key
);
1420 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo
));
1423 result
= getContentObject(pInfo
, NULL
, &value
);
1425 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo
));
1428 // key and value should be null, but we'll release just in case here
1429 __CFPListRelease(key
, pInfo
->allocator
);
1431 __CFPListRelease(value
, pInfo
->allocator
);
1433 result
= getContentObject(pInfo
, &gotKey
, &key
);
1435 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
)) {
1443 CFSetRef oldKeyPaths
= pInfo
->keyPaths
;
1444 CFSetRef nextKeyPaths
, theseKeyPaths
;
1445 __CFPropertyListCreateSplitKeypaths(pInfo
->allocator
, pInfo
->keyPaths
, &theseKeyPaths
, &nextKeyPaths
);
1447 CFMutableDictionaryRef dict
= NULL
;
1449 result
= getContentObject(pInfo
, &gotKey
, &key
);
1450 while (result
&& key
) {
1452 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo
));
1453 __CFPListRelease(key
, pInfo
->allocator
);
1454 __CFPListRelease(nextKeyPaths
, pInfo
->allocator
);
1455 __CFPListRelease(theseKeyPaths
, pInfo
->allocator
);
1456 __CFPListRelease(dict
, pInfo
->allocator
);
1460 if (theseKeyPaths
) {
1461 if (!CFSetContainsValue(theseKeyPaths
, key
)) pInfo
->skip
= true;
1462 pInfo
->keyPaths
= nextKeyPaths
;
1464 result
= getContentObject(pInfo
, NULL
, &value
);
1465 if (theseKeyPaths
) {
1466 pInfo
->keyPaths
= oldKeyPaths
;
1467 pInfo
->skip
= false;
1471 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo
));
1472 __CFPListRelease(key
, pInfo
->allocator
);
1473 __CFPListRelease(nextKeyPaths
, pInfo
->allocator
);
1474 __CFPListRelease(theseKeyPaths
, pInfo
->allocator
);
1475 __CFPListRelease(dict
, pInfo
->allocator
);
1481 dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1482 _CFDictionarySetCapacity(dict
, 10);
1484 CFDictionarySetValue(dict
, key
, value
);
1487 __CFPListRelease(key
, pInfo
->allocator
);
1489 __CFPListRelease(value
, pInfo
->allocator
);
1492 result
= getContentObject(pInfo
, &gotKey
, &key
);
1495 __CFPListRelease(nextKeyPaths
, pInfo
->allocator
);
1496 __CFPListRelease(theseKeyPaths
, pInfo
->allocator
);
1498 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
)) {
1500 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1501 dict
= (CFMutableDictionaryRef
)CFDictionaryCreate(pInfo
->allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1503 dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1506 CFIndex cnt
= CFDictionaryGetCount(dict
);
1508 CFTypeRef val
= CFDictionaryGetValue(dict
, CFSTR("CF$UID"));
1509 if (val
&& CFGetTypeID(val
) == numbertype
) {
1512 CFNumberGetValue((CFNumberRef
)val
, kCFNumberSInt32Type
, &v
);
1513 uid
= (CFTypeRef
)_CFKeyedArchiverUIDCreate(pInfo
->allocator
, v
);
1514 __CFPListRelease(dict
, pInfo
->allocator
);
1519 if (-1 == allowImmutableCollections
) checkImmutableCollections();
1520 if (1 == allowImmutableCollections
) {
1521 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1522 CFDictionaryRef newDict
= CFDictionaryCreateCopy(pInfo
->allocator
, dict
);
1523 __CFPListRelease(dict
, pInfo
->allocator
);
1524 dict
= (CFMutableDictionaryRef
)newDict
;
1535 static Boolean
parseDataTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1536 const char *base
= pInfo
->curr
;
1537 static const signed char dataDecodeTable
[128] = {
1538 /* 000 */ -1, -1, -1, -1, -1, -1, -1, -1,
1539 /* 010 */ -1, -1, -1, -1, -1, -1, -1, -1,
1540 /* 020 */ -1, -1, -1, -1, -1, -1, -1, -1,
1541 /* 030 */ -1, -1, -1, -1, -1, -1, -1, -1,
1542 /* ' ' */ -1, -1, -1, -1, -1, -1, -1, -1,
1543 /* '(' */ -1, -1, -1, 62, -1, -1, -1, 63,
1544 /* '0' */ 52, 53, 54, 55, 56, 57, 58, 59,
1545 /* '8' */ 60, 61, -1, -1, -1, 0, -1, -1,
1546 /* '@' */ -1, 0, 1, 2, 3, 4, 5, 6,
1547 /* 'H' */ 7, 8, 9, 10, 11, 12, 13, 14,
1548 /* 'P' */ 15, 16, 17, 18, 19, 20, 21, 22,
1549 /* 'X' */ 23, 24, 25, -1, -1, -1, -1, -1,
1550 /* '`' */ -1, 26, 27, 28, 29, 30, 31, 32,
1551 /* 'h' */ 33, 34, 35, 36, 37, 38, 39, 40,
1552 /* 'p' */ 41, 42, 43, 44, 45, 46, 47, 48,
1553 /* 'x' */ 49, 50, 51, -1, -1, -1, -1, -1
1557 int tmpbuflen
= 256;
1558 uint8_t *tmpbuf
= pInfo
->skip
? NULL
: (uint8_t *)CFAllocatorAllocate(pInfo
->allocator
, tmpbuflen
, 0);
1563 for (; pInfo
->curr
< pInfo
->end
; pInfo
->curr
++) {
1564 signed char c
= *(pInfo
->curr
);
1570 } else if (!isspace(c
)) {
1573 if (dataDecodeTable
[c
] < 0)
1577 acc
+= dataDecodeTable
[c
];
1578 if (!pInfo
->skip
&& 0 == (cntr
& 0x3)) {
1579 if (tmpbuflen
<= tmpbufpos
+ 2) {
1580 if (tmpbuflen
< 256 * 1024) {
1582 } else if (tmpbuflen
< 16 * 1024 * 1024) {
1585 // once in this stage, this will be really slow
1586 // and really potentially fragment memory
1587 tmpbuflen
+= 256 * 1024;
1589 tmpbuf
= (uint8_t *)CFAllocatorReallocate(pInfo
->allocator
, tmpbuf
, tmpbuflen
, 0);
1590 if (!tmpbuf
) HALT
; // out of memory
1592 tmpbuf
[tmpbufpos
++] = (acc
>> 16) & 0xff;
1593 if (numeq
< 2) tmpbuf
[tmpbufpos
++] = (acc
>> 8) & 0xff;
1594 if (numeq
< 1) tmpbuf
[tmpbufpos
++] = acc
& 0xff;
1598 CFDataRef result
= NULL
;
1600 if (pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1601 result
= (CFDataRef
)CFDataCreateMutable(pInfo
->allocator
, 0);
1602 CFDataAppendBytes((CFMutableDataRef
)result
, tmpbuf
, tmpbufpos
);
1603 CFAllocatorDeallocate(pInfo
->allocator
, tmpbuf
);
1605 result
= CFDataCreateWithBytesNoCopy(pInfo
->allocator
, tmpbuf
, tmpbufpos
, pInfo
->allocator
);
1609 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not interpret <data> at line %d (should be base64-encoded)"), lineNumber(pInfo
));
1614 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
)) {
1618 __CFPListRelease(result
, pInfo
->allocator
);
1623 CF_INLINE Boolean
read2DigitNumber(_CFXMLPlistParseInfo
*pInfo
, int32_t *result
) {
1625 if (pInfo
->curr
+ 2 >= pInfo
->end
) return false;
1627 ch2
= *(pInfo
->curr
+ 1);
1629 if (!isdigit(ch1
) || !isdigit(ch2
)) return false;
1630 *result
= (ch1
- '0')*10 + (ch2
- '0');
1634 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
1635 static Boolean
parseDateTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1636 int32_t year
= 0, month
= 0, day
= 0, hour
= 0, minute
= 0, second
= 0;
1638 Boolean badForm
= false;
1639 Boolean yearIsNegative
= false;
1641 if (pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== '-') {
1642 yearIsNegative
= true;
1646 while (pInfo
->curr
< pInfo
->end
&& isdigit(*pInfo
->curr
)) {
1647 year
= 10*year
+ (*pInfo
->curr
) - '0';
1650 if (pInfo
->curr
>= pInfo
->end
|| *pInfo
->curr
!= '-') {
1656 if (!badForm
&& read2DigitNumber(pInfo
, &month
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== '-') {
1662 if (!badForm
&& read2DigitNumber(pInfo
, &day
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== 'T') {
1668 if (!badForm
&& read2DigitNumber(pInfo
, &hour
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== ':') {
1674 if (!badForm
&& read2DigitNumber(pInfo
, &minute
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== ':') {
1680 if (!badForm
&& read2DigitNumber(pInfo
, &num
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== 'Z') {
1687 if (badForm
|| !checkForCloseTag(pInfo
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
)) {
1688 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not interpret <date> at line %d"), lineNumber(pInfo
));
1692 CFAbsoluteTime at
= 0.0;
1694 CFGregorianDate date
= {yearIsNegative
? -year
: year
, month
, day
, hour
, minute
, second
};
1695 at
= CFGregorianDateGetAbsoluteTime(date
, NULL
);
1697 // this doesn't work
1698 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, kCFCalendarIdentifierGregorian
);
1699 CFTimeZoneRef tz
= CFTimeZoneCreateWithName(kCFAllocatorSystemDefault
, CFSTR("GMT"), true);
1700 CFCalendarSetTimeZone(calendar
, tz
);
1701 CFCalendarComposeAbsoluteTime(calendar
, &at
, (const uint8_t *)"yMdHms", year
, month
, day
, hour
, minute
, second
);
1702 CFRelease(calendar
);
1708 *out
= CFDateCreate(pInfo
->allocator
, at
);
1713 static Boolean
parseRealTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1714 CFStringRef str
= NULL
;
1715 if (!parseStringTag(pInfo
, &str
)) {
1716 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo
));
1720 CFNumberRef result
= NULL
;
1723 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("nan"), kCFCompareCaseInsensitive
)) result
= kCFNumberNaN
;
1724 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("+infinity"), kCFCompareCaseInsensitive
)) result
= kCFNumberPositiveInfinity
;
1725 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("-infinity"), kCFCompareCaseInsensitive
)) result
= kCFNumberNegativeInfinity
;
1726 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("infinity"), kCFCompareCaseInsensitive
)) result
= kCFNumberPositiveInfinity
;
1727 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("-inf"), kCFCompareCaseInsensitive
)) result
= kCFNumberNegativeInfinity
;
1728 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("inf"), kCFCompareCaseInsensitive
)) result
= kCFNumberPositiveInfinity
;
1729 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("+inf"), kCFCompareCaseInsensitive
)) result
= kCFNumberPositiveInfinity
;
1734 CFIndex len
= CFStringGetLength(str
);
1735 CFStringInlineBuffer buf
;
1736 CFStringInitInlineBuffer(str
, &buf
, CFRangeMake(0, len
));
1739 if (!__CFStringScanDouble(&buf
, NULL
, &idx
, &val
) || idx
!= len
) {
1740 __CFPListRelease(str
, pInfo
->allocator
);
1741 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered misformatted real on line %d"), lineNumber(pInfo
));
1744 result
= CFNumberCreate(pInfo
->allocator
, kCFNumberDoubleType
, &val
);
1748 __CFPListRelease(str
, pInfo
->allocator
);
1749 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) {
1753 __CFPListRelease(result
, pInfo
->allocator
);
1758 #define GET_CH if (pInfo->curr == pInfo->end) { \
1759 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Premature end of file after <integer> on line %d"), lineNumber(pInfo)); \
1770 kCFNumberSInt128Type
= 17
1773 CF_INLINE
bool isWhitespace(const char *utf8bytes
, const char *end
) {
1774 // Converted UTF-16 isWhitespace from CFString to UTF8 bytes to get full list of UTF8 whitespace
1796 // Except we consider some additional values from 0x0 to 0x21 and 0x7E to 0xA1 as whitespace, for compatability
1797 char byte1
= *utf8bytes
;
1798 if (byte1
< 0x21 || (byte1
> 0x7E && byte1
< 0xA1)) return true;
1799 if ((byte1
== 0xe2 || byte1
== 0xe3) && (end
- utf8bytes
>= 3)) {
1800 // Check other possibilities in the 3-bytes range
1801 char byte2
= *(utf8bytes
+ 1);
1802 char byte3
= *(utf8bytes
+ 2);
1803 if (byte1
== 0xe2 && byte2
== 0x80) {
1804 return ((byte3
>= 80 && byte3
<= 0x8b) || byte3
== 0xaf);
1805 } else if (byte1
== 0xe2 && byte2
== 0x81) {
1806 return byte3
== 0x9f;
1807 } else if (byte1
== 0xe3 && byte2
== 0x80 && byte3
== 0x80) {
1814 static Boolean
parseIntegerTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1815 bool isHex
= false, isNeg
= false, hadLeadingZero
= false;
1818 // decimal_constant S*(-|+)?S*[0-9]+ (S == space)
1819 // hex_constant S*(-|+)?S*0[xX][0-9a-fA-F]+ (S == space)
1821 while (pInfo
->curr
< pInfo
->end
&& isWhitespace(pInfo
->curr
, pInfo
->end
)) pInfo
->curr
++;
1824 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo
));
1827 if ('-' == ch
|| '+' == ch
) {
1828 isNeg
= ('-' == ch
);
1830 while (pInfo
->curr
< pInfo
->end
&& isWhitespace(pInfo
->curr
, pInfo
->end
)) pInfo
->curr
++;
1834 if (pInfo
->curr
+ 1 < pInfo
->end
&& ('x' == *(pInfo
->curr
+ 1) || 'X' == *(pInfo
->curr
+ 1))) {
1838 hadLeadingZero
= true;
1844 hadLeadingZero
= true;
1848 if ('<' == ch
&& hadLeadingZero
) { // nothing but zeros
1850 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
)) {
1851 // checkForCloseTag() sets error string
1857 *out
= CFNumberCreate(pInfo
->allocator
, kCFNumberSInt32Type
, &val
);
1862 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Incomplete <integer> on line %d"), lineNumber(pInfo
));
1866 uint32_t multiplier
= (isHex
? 16 : 10);
1868 uint32_t new_digit
= 0;
1870 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
1871 new_digit
= (ch
- '0');
1873 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1874 new_digit
= (ch
- 'a' + 10);
1876 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1877 new_digit
= (ch
- 'A' + 10);
1879 default: // other character
1880 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unknown character '%c' (0x%x) in <integer> on line %d"), ch
, ch
, lineNumber(pInfo
));
1883 if (!isHex
&& new_digit
> 9) {
1884 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Hex digit in non-hex <integer> on line %d"), lineNumber(pInfo
));
1887 if (UINT64_MAX
/ multiplier
< value
) {
1888 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo
));
1891 value
= multiplier
* value
;
1892 if (UINT64_MAX
- new_digit
< value
) {
1893 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo
));
1896 value
= value
+ new_digit
;
1897 if (isNeg
&& (uint64_t)INT64_MAX
+ 1 < value
) {
1898 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Integer underflow in <integer> on line %d"), lineNumber(pInfo
));
1904 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
)) {
1905 // checkForCloseTag() sets error string
1912 if (isNeg
|| value
<= INT64_MAX
) {
1914 if (isNeg
) v
= -v
; // no-op if INT64_MIN
1915 *out
= CFNumberCreate(pInfo
->allocator
, kCFNumberSInt64Type
, &v
);
1917 CFSInt128Struct val
;
1920 *out
= CFNumberCreate(pInfo
->allocator
, kCFNumberSInt128Type
, &val
);
1928 // Returned object is retained; caller must free. pInfo->curr expected to point to the first character after the '<'
1929 static Boolean
parseXMLElement(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
, CFTypeRef
*out
) {
1930 const char *marker
= pInfo
->curr
;
1931 int markerLength
= -1;
1935 if (isKey
) *isKey
= false;
1936 while (pInfo
->curr
< pInfo
->end
) {
1937 char ch
= *(pInfo
->curr
);
1938 if (ch
== ' ' || ch
== '\t' || ch
== '\n' || ch
=='\r') {
1939 if (markerLength
== -1) markerLength
= pInfo
->curr
- marker
;
1940 } else if (ch
== '>') {
1945 if (pInfo
->curr
>= pInfo
->end
) return false;
1946 isEmpty
= (*(pInfo
->curr
-1) == '/');
1947 if (markerLength
== -1)
1948 markerLength
= pInfo
->curr
- (isEmpty
? 1 : 0) - marker
;
1949 pInfo
->curr
++; // Advance past '>'
1950 if (markerLength
== 0) {
1951 // Back up to the beginning of the marker
1952 pInfo
->curr
= marker
;
1953 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Malformed tag on line %d"), lineNumber(pInfo
));
1958 if (markerLength
== ARRAY_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
))
1959 markerIx
= ARRAY_IX
;
1961 case 'd': // Dictionary, data, or date; Fortunately, they all have the same marker length....
1962 if (markerLength
!= DICT_TAG_LENGTH
)
1964 if (!memcmp(marker
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
))
1966 else if (!memcmp(marker
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
))
1968 else if (!memcmp(marker
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
))
1971 case 'f': // false (boolean)
1972 if (markerLength
== FALSE_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
)) {
1973 markerIx
= FALSE_IX
;
1976 case 'i': // integer
1977 if (markerLength
== INTEGER_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
))
1978 markerIx
= INTEGER_IX
;
1980 case 'k': // Key of a dictionary
1981 if (markerLength
== KEY_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
)) {
1983 if (isKey
) *isKey
= true;
1987 if (markerLength
== PLIST_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
))
1988 markerIx
= PLIST_IX
;
1991 if (markerLength
== REAL_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
))
1995 if (markerLength
== STRING_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[STRING_IX
], STRING_TAG_LENGTH
))
1996 markerIx
= STRING_IX
;
1998 case 't': // true (boolean)
1999 if (markerLength
== TRUE_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
))
2004 if (!pInfo
->allowNewTypes
&& markerIx
!= PLIST_IX
&& markerIx
!= ARRAY_IX
&& markerIx
!= DICT_IX
&& markerIx
!= STRING_IX
&& markerIx
!= KEY_IX
&& markerIx
!= DATA_IX
) {
2005 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered new tag when expecting only old-style property list objects"));
2012 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty plist tag"));
2015 return parsePListTag(pInfo
, out
);
2021 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
2022 *out
= CFArrayCreate(pInfo
->allocator
, NULL
, 0, &kCFTypeArrayCallBacks
);
2024 *out
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
2029 return parseArrayTag(pInfo
, out
);
2036 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
2037 *out
= CFDictionaryCreate(pInfo
->allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2039 *out
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2044 return parseDictTag(pInfo
, out
);
2049 int tagLen
= (markerIx
== KEY_IX
) ? KEY_TAG_LENGTH
: STRING_TAG_LENGTH
;
2054 if (pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
2055 *out
= CFStringCreateMutable(pInfo
->allocator
, 0);
2057 *out
= CFStringCreateWithCharacters(pInfo
->allocator
, NULL
, 0);
2062 if (!parseStringTag(pInfo
, (CFStringRef
*)out
)) {
2063 return false; // parseStringTag will already have set the error string
2065 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[markerIx
], tagLen
)) {
2066 __CFPListRelease(*out
, pInfo
->allocator
);
2074 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <data> on line %d"), lineNumber(pInfo
));
2077 return parseDataTag(pInfo
, out
);
2081 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <date> on line %d"), lineNumber(pInfo
));
2084 return parseDateTag(pInfo
, out
);
2088 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
)) {
2089 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered non-empty <true> on line %d"), lineNumber(pInfo
));
2096 *out
= CFRetain(kCFBooleanTrue
);
2101 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
)) {
2102 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered non-empty <false> on line %d"), lineNumber(pInfo
));
2109 *out
= CFRetain(kCFBooleanFalse
);
2114 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo
));
2117 return parseRealTag(pInfo
, out
);
2121 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo
));
2124 return parseIntegerTag(pInfo
, out
);
2127 CFStringRef markerStr
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, (const UInt8
*)marker
, markerLength
, kCFStringEncodingUTF8
, NO
);
2128 pInfo
->curr
= marker
;
2129 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown tag %@ on line %d"), markerStr
? markerStr
: CFSTR("<unknown>"), lineNumber(pInfo
));
2130 if (markerStr
) CFRelease(markerStr
);
2136 static Boolean
parseXMLPropertyList(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
2137 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
2139 skipWhitespace(pInfo
);
2140 if (pInfo
->curr
+1 >= pInfo
->end
) {
2141 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("No XML content found"));
2144 if (*(pInfo
->curr
) != '<') {
2145 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected character %c at line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
2148 ch
= *(++ pInfo
->curr
);
2152 if (pInfo
->curr
+1 < pInfo
->end
&& *pInfo
->curr
== '-' && *(pInfo
->curr
+1) == '-') {
2155 skipXMLComment(pInfo
);
2159 } else if (ch
== '?') {
2160 // Processing instruction
2162 skipXMLProcessingInstruction(pInfo
);
2165 return parseXMLElement(pInfo
, NULL
, out
);
2166 // 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
2169 // Should never get here
2170 if (!(pInfo
->error
)) {
2171 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
2176 static CFStringEncoding
encodingForXMLData(CFDataRef data
, CFErrorRef
*error
, CFIndex
*skip
) {
2177 const uint8_t *bytes
= (uint8_t *)CFDataGetBytePtr(data
);
2178 UInt32 length
= CFDataGetLength(data
);
2179 const uint8_t *idx
, *end
;
2182 // 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.
2184 if (*bytes
== 0x00 && *(bytes
+1) == 0x00 && *(bytes
+2) == 0xFE && *(bytes
+3) == 0xFF) {
2186 return kCFStringEncodingUTF32BE
;
2187 } else if (*bytes
== 0xFF && *(bytes
+1) == 0xFE && *(bytes
+2) == 0x00 && *(bytes
+3) == 0x00) {
2189 return kCFStringEncodingUTF32LE
;
2194 if (*bytes
== 0xEF && *(bytes
+1) == 0xBB && *(bytes
+2) == 0xBF) {
2196 return kCFStringEncodingUTF8
;
2201 if (*bytes
== 0xFF && *(bytes
+1) == 0xFE) {
2203 return kCFStringEncodingUTF16LE
;
2204 } else if (*bytes
== 0xFE && *(bytes
+1) == 0xFF) {
2206 return kCFStringEncodingUTF16BE
;
2207 } 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
2209 return kCFStringEncodingUnicode
;
2213 // Scan for the <?xml.... ?> opening
2214 if (length
< 5 || strncmp((char const *) bytes
, "<?xml", 5) != 0) return kCFStringEncodingUTF8
;
2216 end
= bytes
+ length
;
2217 // Found "<?xml"; now we scan for "encoding"
2220 const uint8_t *scan
;
2221 if ( ch
== '?' || ch
== '>') return kCFStringEncodingUTF8
;
2224 if (idx
+ 8 >= end
) {
2225 if (error
) *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("End of buffer while looking for encoding name"));
2228 if (ch
== 'e' && *scan
++ == 'n' && *scan
++ == 'c' && *scan
++ == 'o' && *scan
++ == 'd' && *scan
++ == 'i'
2229 && *scan
++ == 'n' && *scan
++ == 'g' && *scan
++ == '=') {
2234 if (idx
>= end
) return kCFStringEncodingUTF8
;
2236 if (quote
!= '\'' && quote
!= '\"') return kCFStringEncodingUTF8
;
2238 CFStringRef encodingName
;
2239 const uint8_t *base
= idx
+1; // Move past the quote character
2242 while (idx
< end
&& *idx
!= quote
) idx
++;
2243 if (idx
>= end
) return kCFStringEncodingUTF8
;
2245 if (len
== 5 && (*base
== 'u' || *base
== 'U') && (base
[1] == 't' || base
[1] == 'T') && (base
[2] == 'f' || base
[2] == 'F') && (base
[3] == '-') && (base
[4] == '8'))
2246 return kCFStringEncodingUTF8
;
2247 encodingName
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, base
, len
, kCFStringEncodingISOLatin1
, false);
2248 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
2249 CFStringEncoding enc
= CFStringConvertIANACharSetNameToEncoding(encodingName
);
2250 if (enc
!= kCFStringEncodingInvalidId
) {
2251 CFRelease(encodingName
);
2257 *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown encoding (%@)"), encodingName
);
2258 CFRelease(encodingName
);
2264 bool __CFTryParseBinaryPlist(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFPropertyListRef
*plist
, CFStringRef
*errorString
);
2266 #define SAVE_PLISTS 0
2269 static Boolean
__savePlistData(CFDataRef data
, CFOptionFlags opt
) {
2272 uint32_t pnlen
= sizeof(pn
);
2273 uint8_t *pnp
= NULL
;
2274 if (0 == _NSGetExecutablePath((char *)pn
, &pnlen
)) {
2275 pnp
= strrchr((char *)pn
, '/');
2282 if (0 == strcmp((char *)pnp
, "parse_plists")) return true;
2283 CFUUIDRef r
= CFUUIDCreate(kCFAllocatorSystemDefault
);
2284 CFStringRef s
= CFUUIDCreateString(kCFAllocatorSystemDefault
, r
);
2285 CFStringRef p
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("/tmp/plists/%s#%@#0x%x"), pnp
, s
, opt
);
2286 _CFStringGetFileSystemRepresentation(p
, fn
, sizeof(fn
));
2290 int fd
= open((const char *)fn
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
2291 if (fd
< 0) return false;
2292 int len
= CFDataGetLength(data
);
2293 int w
= write(fd
, CFDataGetBytePtr(data
), len
);
2296 if (w
!= len
) return false;
2301 // If the data is from a converted string, then originalString is non-NULL. If originalString is NULL, then pass in guessedEncoding.
2302 // keyPaths is a set of CFStrings, ':'-separated paths
2303 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
) {
2306 CFAssert1(xmlData
!= NULL
, __kCFLogAssertion
, "%s(): NULL data not allowed", __PRETTY_FUNCTION__
);
2307 CFAssert2(option
== kCFPropertyListImmutable
|| option
== kCFPropertyListMutableContainers
|| option
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, option
);
2309 CFIndex length
= CFDataGetLength(xmlData
);
2311 if (outError
) *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Conversion of string failed. The string is empty."));
2315 _CFXMLPlistParseInfo pInfoBuf
;
2316 _CFXMLPlistParseInfo
*pInfo
= &pInfoBuf
;
2319 // Ensure that the data is not collected while we are using it
2321 const char *buf
= (const char *)CFDataGetBytePtr(xmlData
);
2323 // We may have to skip over starting stuff like BOM markers.
2327 pInfo
->end
= buf
+length
;
2329 pInfo
->allocator
= allocator
;
2330 pInfo
->error
= NULL
;
2331 _createStringMap(pInfo
);
2332 pInfo
->mutabilityOption
= option
;
2333 pInfo
->allowNewTypes
= allowNewTypes
;
2334 pInfo
->skip
= false;
2335 pInfo
->keyPaths
= createTopLevelKeypaths(allocator
, keyPaths
);
2337 Boolean success
= parseXMLPropertyList(pInfo
, &result
);
2338 if (success
&& result
&& format
) *format
= kCFPropertyListXMLFormat_v1_0
;
2340 _cleanupStringMap(pInfo
);
2341 if (pInfo
->keyPaths
&& !_CFAllocatorIsGCRefZero(allocator
)) CFRelease(pInfo
->keyPaths
);
2345 *out
= result
; // caller releases
2349 // Try again, old-style
2350 CFErrorRef oldStyleError
= NULL
;
2351 result
= __CFParseOldStylePropertyListOrStringsFile(allocator
, xmlData
, originalString
, guessedEncoding
, option
, outError
? &oldStyleError
: NULL
, format
);
2353 // Release old error, return
2354 if (pInfo
->error
) CFRelease(pInfo
->error
);
2359 // Failure, both ways. Set up the error to be given back to caller.
2361 if (pInfo
->error
) CFRelease(pInfo
->error
);
2365 // Caller's responsibility to release outError
2366 if (pInfo
->error
&& oldStyleError
) {
2367 // Add the error from the old-style property list parser to the user info of the original error (pInfo->error), which had better exist
2368 CFDictionaryRef oldUserInfo
= CFErrorCopyUserInfo(pInfo
->error
);
2369 CFMutableDictionaryRef newUserInfo
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, CFDictionaryGetCount(oldUserInfo
) + 1, oldUserInfo
);
2370 CFDictionaryAddValue(newUserInfo
, CFPropertyListOldStyleParserErrorKey
, oldStyleError
);
2372 // Re-create the xml parser error with this new user info dictionary
2373 CFErrorRef newError
= CFErrorCreate(kCFAllocatorSystemDefault
, CFErrorGetDomain(pInfo
->error
), CFErrorGetCode(pInfo
->error
), newUserInfo
);
2375 CFRelease(oldUserInfo
);
2376 CFRelease(newUserInfo
);
2377 CFRelease(oldStyleError
);
2378 CFRelease(pInfo
->error
);
2379 *outError
= newError
;
2381 } else if (pInfo
->error
&& !oldStyleError
) {
2382 // Return original error
2383 *outError
= pInfo
->error
;
2384 } else if (!pInfo
->error
&& oldStyleError
) {
2385 // Return only old-style error
2386 // Probably shouldn't get here
2387 *outError
= oldStyleError
;
2388 } else if (!pInfo
->error
&& !oldStyleError
) {
2389 // Return unknown error
2390 *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown error during parse"));
2395 static CFDataRef
_createUTF8DataFromString(CFAllocatorRef allocator
, CFStringRef str
) {
2396 CFIndex bytesNeeded
= 0;
2397 CFStringGetBytes(str
, CFRangeMake(0, CFStringGetLength(str
)), kCFStringEncodingUTF8
, 0, false, NULL
, 0, &bytesNeeded
);
2399 const char *bytes
= (const char *)CFAllocatorAllocate(allocator
, bytesNeeded
, 0);
2400 CFStringGetBytes(str
, CFRangeMake(0, CFStringGetLength(str
)), kCFStringEncodingUTF8
, 0, false, (uint8_t *)bytes
, bytesNeeded
, NULL
);
2402 CFDataRef utf8Data
= CFDataCreateWithBytesNoCopy(allocator
, (const UInt8
*)bytes
, bytesNeeded
, allocator
);
2406 // 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.
2407 static Boolean
_CFPropertyListCreateWithData(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFErrorRef
*outError
, Boolean allowNewTypes
, CFPropertyListFormat
*format
, CFSetRef topLevelKeys
, CFTypeRef
*out
) {
2409 CFStringEncoding encoding
;
2411 if (!data
|| CFDataGetLength(data
) == 0) {
2413 *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Cannot parse a NULL or zero-length data"));
2419 __savePlistData(data
, option
);
2422 // Ignore the error from CFTryParseBinaryPlist -- if it doesn't work, we're going to try again anyway using the XML parser
2423 if (__CFTryParseBinaryPlist(allocator
, data
, option
, out
, NULL
)) {
2424 if (format
) *format
= kCFPropertyListBinaryFormat_v1_0
;
2428 // Use our own error variable here so we can check it against NULL later
2429 CFErrorRef subError
= NULL
;
2431 encoding
= encodingForXMLData(data
, &subError
, &skip
); // 0 is an error return, NOT MacRoman.
2433 if (encoding
== 0) {
2434 // Couldn't find an encoding
2435 // Note that encodingForXMLData() will give us the right values for a standard plist, too.
2436 if (outError
&& subError
== NULL
) {
2437 // encodingForXMLData didn't set an error, so we create a new one here
2438 *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not determine the encoding of the XML data"));
2439 } else if (outError
&& subError
) {
2440 // give the caller the subError, they will release
2441 *outError
= subError
;
2442 } else if (!outError
&& subError
) {
2443 // Release the error
2444 CFRelease(subError
);
2449 if (encoding
== kCFStringEncodingUTF8
) {
2451 return _CFPropertyListCreateFromUTF8Data(allocator
, data
, skip
, NULL
, encoding
, option
, outError
, allowNewTypes
, format
, topLevelKeys
, out
);
2454 // Convert to UTF8 first
2455 CFStringRef xmlString
= CFStringCreateWithBytes(allocator
, CFDataGetBytePtr(data
) + skip
, CFDataGetLength(data
) - skip
, encoding
, false);
2456 CFDataRef utf8Data
= _createUTF8DataFromString(allocator
, xmlString
);
2458 Boolean result
= _CFPropertyListCreateFromUTF8Data(allocator
, utf8Data
, 0, xmlString
, 0, option
, outError
, allowNewTypes
, format
, topLevelKeys
, out
);
2460 if (xmlString
&& !_CFAllocatorIsGCRefZero(allocator
)) CFRelease(xmlString
);
2461 if (utf8Data
&& !_CFAllocatorIsGCRefZero(allocator
)) CFRelease(utf8Data
);
2466 // -----------------------------------------------------------------------------------------------------------------------
2469 #pragma mark Exported Parsing Functions
2471 CFTypeRef
_CFPropertyListCreateFromXMLStringError(CFAllocatorRef allocator
, CFStringRef xmlString
, CFOptionFlags option
, CFErrorRef
*error
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2472 // Convert to UTF8 first
2473 CFDataRef utf8Data
= _createUTF8DataFromString(allocator
, xmlString
);
2474 CFTypeRef result
= NULL
;
2475 _CFPropertyListCreateFromUTF8Data(allocator
, utf8Data
, 0, xmlString
, 0, option
, error
, allowNewTypes
, format
, NULL
, &result
);
2476 if (utf8Data
&& !_CFAllocatorIsGCRefZero(allocator
)) CFRelease(utf8Data
);
2481 CFTypeRef
_CFPropertyListCreateFromXMLString(CFAllocatorRef allocator
, CFStringRef xmlString
, CFOptionFlags option
, CFStringRef
*errorString
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2483 if (errorString
) *errorString
= NULL
;
2484 CFErrorRef error
= NULL
;
2485 CFTypeRef result
= _CFPropertyListCreateFromXMLStringError(allocator
, xmlString
, option
, &error
, allowNewTypes
, format
);
2487 if (errorString
&& error
) {
2488 // The caller is interested in receiving an error message. Pull the debug string out of the CFError returned above
2489 CFDictionaryRef userInfo
= CFErrorCopyUserInfo(error
);
2490 CFStringRef debugString
= NULL
;
2492 // Handle a special-case for compatibility - if the XML parse failed and the old-style plist parse failed, construct a special string
2493 CFErrorRef underlyingError
= NULL
;
2495 Boolean oldStyleFailed
= CFDictionaryGetValueIfPresent(userInfo
, CFPropertyListOldStyleParserErrorKey
, (const void **)&underlyingError
);
2497 if (oldStyleFailed
) {
2498 CFStringRef xmlParserErrorString
, oldStyleParserErrorString
;
2499 xmlParserErrorString
= (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
2501 CFDictionaryRef oldStyleParserUserInfo
= CFErrorCopyUserInfo(underlyingError
);
2502 oldStyleParserErrorString
= (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
2504 // Combine the two strings into a single one that matches the previous implementation
2505 debugString
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("XML parser error:\n\t%@\nOld-style plist parser error:\n\t%@\n"), xmlParserErrorString
, oldStyleParserErrorString
);
2507 CFRelease(oldStyleParserUserInfo
);
2509 debugString
= (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
2510 CFRetain(debugString
);
2513 // Give the debugString to the caller, who is responsible for releasing it
2514 CFRelease(userInfo
);
2515 *errorString
= debugString
;
2525 __private_extern__
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
);
2527 // Returns a subset of the property list, only including the key paths in the CFSet.
2528 bool _CFPropertyListCreateFiltered(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFSetRef keyPaths
, CFPropertyListRef
*value
, CFErrorRef
*error
) {
2532 if (!keyPaths
|| !data
) {
2537 CFBinaryPlistTrailer trailer
;
2539 const uint8_t *databytes
= CFDataGetBytePtr(data
);
2540 uint64_t datalen
= CFDataGetLength(data
);
2541 Boolean success
= false;
2542 CFTypeRef out
= NULL
;
2544 // First check to see if it is a binary property list
2545 if (8 <= datalen
&& __CFBinaryPlistGetTopLevelInfo(databytes
, datalen
, &marker
, &offset
, &trailer
)) {
2546 uint64_t valueOffset
= offset
;
2548 // Split up the key path
2549 CFSetRef splitKeyPaths
= createTopLevelKeypaths(allocator
, keyPaths
);
2551 // Create a dictionary to cache objects in
2552 CFMutableDictionaryRef objects
= CFDictionaryCreateMutable(allocator
, 0, NULL
, &kCFTypeDictionaryValueCallBacks
);
2553 success
= __CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, valueOffset
, &trailer
, allocator
, option
, objects
, NULL
, 0, splitKeyPaths
, &out
);
2555 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(splitKeyPaths
);
2556 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(objects
);
2558 // Try an XML property list
2559 success
= _CFPropertyListCreateWithData(allocator
, data
, option
, error
, true, NULL
, keyPaths
, &out
);
2562 if (success
&& value
) {
2563 *value
= out
; // caller releases
2570 /* Get a single value for a given key in a top-level dictionary in a property list.
2571 @param allocator The allocator to use.
2572 @param data The property list data.
2573 @param option Currently unused, set to 0.
2574 @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).
2575 @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.
2576 @param error If an error occurs, will be set to a valid CFErrorRef. It is the caller's responsibility to release this value.
2577 @return True if the key is found, false otherwise.
2579 bool _CFPropertyListCreateSingleValue(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFStringRef keyPath
, CFPropertyListRef
*value
, CFErrorRef
*error
) {
2583 if (!keyPath
|| CFStringGetLength(keyPath
) == 0) {
2588 CFBinaryPlistTrailer trailer
;
2590 const uint8_t *databytes
= CFDataGetBytePtr(data
);
2591 uint64_t datalen
= CFDataGetLength(data
);
2592 Boolean success
= false;
2594 // First check to see if it is a binary property list
2595 if (8 <= datalen
&& __CFBinaryPlistGetTopLevelInfo(databytes
, datalen
, &marker
, &offset
, &trailer
)) {
2596 // Split up the key path
2597 CFArrayRef keyPathArray
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorSystemDefaultGCRefZero
, keyPath
, CFSTR(":"));
2598 uint64_t keyOffset
, valueOffset
= offset
;
2600 // Create a dictionary to cache objects in
2601 CFMutableDictionaryRef objects
= CFDictionaryCreateMutable(kCFAllocatorSystemDefaultGCRefZero
, 0, NULL
, &kCFTypeDictionaryValueCallBacks
);
2602 CFIndex keyPathCount
= CFArrayGetCount(keyPathArray
);
2603 _CFDictionarySetCapacity(objects
, keyPathCount
+ 1);
2605 for (CFIndex i
= 0; i
< keyPathCount
; i
++) {
2606 CFStringRef oneKey
= (CFStringRef
)CFArrayGetValueAtIndex(keyPathArray
, i
);
2607 SInt32 intValue
= CFStringGetIntValue(oneKey
);
2608 if ((intValue
== 0 && CFStringCompare(CFSTR("0"), oneKey
, 0) != kCFCompareEqualTo
) || intValue
== INT_MAX
|| intValue
== INT_MIN
|| intValue
< 0) {
2609 // Treat as a string key into a dictionary
2610 success
= __CFBinaryPlistGetOffsetForValueFromDictionary3(databytes
, datalen
, valueOffset
, &trailer
, (CFTypeRef
)oneKey
, &keyOffset
, &valueOffset
, false, objects
);
2612 // Treat as integer index into an array
2613 success
= __CFBinaryPlistGetOffsetForValueFromArray2(databytes
, datalen
, valueOffset
, &trailer
, intValue
, &valueOffset
, objects
);
2621 // value could be null if the caller wanted to check for the existence of a key but not bother creating it
2622 if (success
&& value
) {
2623 CFPropertyListRef pl
;
2624 success
= __CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, valueOffset
, &trailer
, allocator
, option
, objects
, NULL
, 0, NULL
, &pl
);
2626 // caller's responsibility to release the created object
2631 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero
)) CFRelease(keyPathArray
);
2632 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero
)) CFRelease(objects
);
2634 // Try an XML property list
2635 // Note: This is currently not any more efficient than grabbing the whole thing. This could be improved in the future.
2636 CFPropertyListRef plist
= CFPropertyListCreateWithData(allocator
, data
, option
, NULL
, error
);
2637 CFPropertyListRef nextObject
= plist
;
2639 if (!(*error
) && plist
) {
2640 CFArrayRef keyPathArray
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorSystemDefaultGCRefZero
, keyPath
, CFSTR(":"));
2641 for (CFIndex i
= 0; i
< CFArrayGetCount(keyPathArray
); i
++) {
2642 CFStringRef oneKey
= (CFStringRef
)CFArrayGetValueAtIndex(keyPathArray
, i
);
2643 SInt32 intValue
= CFStringGetIntValue(oneKey
);
2644 if (((intValue
== 0 && CFStringCompare(CFSTR("0"), oneKey
, 0) != kCFCompareEqualTo
) || intValue
== INT_MAX
|| intValue
== INT_MIN
) && CFGetTypeID((CFTypeRef
)nextObject
) == dicttype
) {
2645 // Treat as a string key into a dictionary
2646 nextObject
= (CFPropertyListRef
)CFDictionaryGetValue((CFDictionaryRef
)nextObject
, oneKey
);
2647 } else if (CFGetTypeID((CFTypeRef
)nextObject
) == arraytype
) {
2648 // Treat as integer index into an array
2649 nextObject
= (CFPropertyListRef
)CFArrayGetValueAtIndex((CFArrayRef
)nextObject
, intValue
);
2656 if (success
&& nextObject
&& value
) {
2657 *value
= nextObject
;
2658 // caller's responsibility to release the created object
2660 } else if (!nextObject
) {
2664 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero
)) CFRelease(keyPathArray
);
2666 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(plist
);
2673 CFTypeRef
_CFPropertyListCreateFromXMLData(CFAllocatorRef allocator
, CFDataRef xmlData
, CFOptionFlags option
, CFStringRef
*errorString
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2675 CFTypeRef out
= NULL
;
2676 if (errorString
) *errorString
= NULL
;
2677 CFErrorRef error
= NULL
;
2678 Boolean result
= _CFPropertyListCreateWithData(allocator
, xmlData
, option
, &error
, allowNewTypes
, format
, NULL
, &out
);
2679 if (!result
&& error
&& errorString
) {
2680 *errorString
= __copyErrorDebugDescription(error
);
2682 if (error
) CFRelease(error
);
2686 CFPropertyListRef
CFPropertyListCreateWithData(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags options
, CFPropertyListFormat
*format
, CFErrorRef
*error
) {
2688 CFAssert1(data
!= NULL
, __kCFLogAssertion
, "%s(): NULL data not allowed", __PRETTY_FUNCTION__
);
2689 CFAssert2(options
== kCFPropertyListImmutable
|| options
== kCFPropertyListMutableContainers
|| options
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, options
);
2690 CFPropertyListRef out
= NULL
;
2691 _CFPropertyListCreateWithData(allocator
, data
, options
, error
, true, format
, NULL
, &out
);
2695 CFPropertyListRef
CFPropertyListCreateFromXMLData(CFAllocatorRef allocator
, CFDataRef xmlData
, CFOptionFlags option
, CFStringRef
*errorString
) {
2697 if (errorString
) *errorString
= NULL
;
2698 CFErrorRef error
= NULL
;
2699 CFPropertyListRef result
= CFPropertyListCreateWithData(allocator
, xmlData
, option
, NULL
, &error
);
2700 if (error
&& errorString
) {
2701 *errorString
= __copyErrorDebugDescription(error
);
2703 if (error
) CFRelease(error
);
2707 CFDataRef
CFPropertyListCreateData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, CFPropertyListFormat format
, CFOptionFlags options
, CFErrorRef
*error
) {
2709 CFAssert1(format
!= kCFPropertyListOpenStepFormat
, __kCFLogAssertion
, "%s(): kCFPropertyListOpenStepFormat not supported for writing", __PRETTY_FUNCTION__
);
2710 CFAssert2(format
== kCFPropertyListXMLFormat_v1_0
|| format
== kCFPropertyListBinaryFormat_v1_0
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, format
);
2711 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
2712 __CFAssertIsPList(propertyList
);
2714 CFDataRef data
= NULL
;
2717 CFStringRef validErr
= NULL
;
2718 if (!_CFPropertyListIsValidWithErrorString(propertyList
, format
, &validErr
)) {
2719 CFLog(kCFLogLevelError
, CFSTR("Property list invalid for format: %d (%@)"), format
, validErr
);
2720 if (validErr
) CFRelease(validErr
);
2724 if (format
== kCFPropertyListOpenStepFormat
) {
2725 CFLog(kCFLogLevelError
, CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2727 } else if (format
== kCFPropertyListXMLFormat_v1_0
) {
2728 data
= _CFPropertyListCreateXMLData(allocator
, propertyList
, true);
2729 } else if (format
== kCFPropertyListBinaryFormat_v1_0
) {
2730 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2731 // TODO: Is it more efficient to create a stream here or just use a mutable data?
2732 CFWriteStreamRef stream
= CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorSystemDefault
, allocator
);
2733 CFWriteStreamOpen(stream
);
2734 CFIndex len
= CFPropertyListWrite(propertyList
, stream
, format
, options
, error
);
2736 data
= (CFDataRef
)CFWriteStreamCopyProperty(stream
, kCFStreamPropertyDataWritten
);
2738 CFWriteStreamClose(stream
);
2741 CFMutableDataRef dataForPlist
= CFDataCreateMutable(allocator
, 0);
2742 __CFBinaryPlistWrite(propertyList
, dataForPlist
, 0, options
, error
);
2743 return dataForPlist
;
2746 CFLog(kCFLogLevelError
, CFSTR("Unknown format option"));
2752 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2754 CFIndex
CFPropertyListWrite(CFPropertyListRef propertyList
, CFWriteStreamRef stream
, CFPropertyListFormat format
, CFOptionFlags options
, CFErrorRef
*error
) {
2756 CFAssert1(stream
!= NULL
, __kCFLogAssertion
, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__
);
2757 CFAssert1(format
!= kCFPropertyListOpenStepFormat
, __kCFLogAssertion
, "%s(): kCFPropertyListOpenStepFormat not supported for writing", __PRETTY_FUNCTION__
);
2758 CFAssert2(format
== kCFPropertyListXMLFormat_v1_0
|| format
== kCFPropertyListBinaryFormat_v1_0
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, format
);
2759 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
2760 __CFAssertIsPList(propertyList
);
2761 CFAssert1(CFWriteStreamGetTypeID() == CFGetTypeID(stream
), __kCFLogAssertion
, "%s(): stream argument is not a write stream", __PRETTY_FUNCTION__
);
2762 CFAssert1(kCFStreamStatusOpen
== CFWriteStreamGetStatus(stream
) || kCFStreamStatusWriting
== CFWriteStreamGetStatus(stream
), __kCFLogAssertion
, "%s(): stream is not open", __PRETTY_FUNCTION__
);
2764 CFStringRef validErr
= NULL
;
2765 if (!_CFPropertyListIsValidWithErrorString(propertyList
, format
, &validErr
)) {
2766 CFLog(kCFLogLevelError
, CFSTR("Property list invalid for format: %d (%@)"), format
, validErr
);
2767 if (validErr
) CFRelease(validErr
);
2770 if (format
== kCFPropertyListOpenStepFormat
) {
2771 CFLog(kCFLogLevelError
, CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2774 if (format
== kCFPropertyListXMLFormat_v1_0
) {
2775 CFDataRef data
= _CFPropertyListCreateXMLData(kCFAllocatorSystemDefault
, propertyList
, true);
2776 CFIndex len
= CFDataGetLength(data
);
2777 const uint8_t *ptr
= CFDataGetBytePtr(data
);
2779 CFIndex ret
= CFWriteStreamWrite(stream
, ptr
, len
);
2781 if (error
) *error
= __CFPropertyListCreateError(kCFPropertyListWriteStreamError
, CFSTR("Property list writing could not be completed because stream is full."));
2786 CFErrorRef underlyingError
= CFWriteStreamCopyError(stream
);
2787 if (underlyingError
) {
2789 // Wrap the error from CFWriteStreamCopy in a new error
2790 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2791 CFDictionarySetValue(userInfo
, kCFErrorDebugDescriptionKey
, CFSTR("Property list writing could not be completed because the stream had an unknown error."));
2792 CFDictionarySetValue(userInfo
, kCFErrorUnderlyingErrorKey
, underlyingError
);
2793 *error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, kCFPropertyListWriteStreamError
, userInfo
);
2794 CFRelease(userInfo
);
2796 CFRelease(underlyingError
);
2804 len
= CFDataGetLength(data
);
2808 if (format
== kCFPropertyListBinaryFormat_v1_0
) {
2809 CFIndex len
= __CFBinaryPlistWrite(propertyList
, stream
, 0, options
, error
);
2812 CFLog(kCFLogLevelError
, CFSTR("Unknown format option"));
2816 CFIndex
CFPropertyListWriteToStream(CFPropertyListRef propertyList
, CFWriteStreamRef stream
, CFPropertyListFormat format
, CFStringRef
*errorString
) {
2818 if (errorString
) *errorString
= NULL
;
2819 CFErrorRef error
= NULL
;
2821 // For backwards compatibility, we check the format parameter up front since these do not have CFError counterparts in the newer API
2822 CFStringRef validErr
= NULL
;
2823 if (!_CFPropertyListIsValidWithErrorString(propertyList
, format
, &validErr
)) {
2824 if (errorString
) *errorString
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("Property list invalid for format (%@)"), validErr
);
2825 if (validErr
) CFRelease(validErr
);
2828 if (format
== kCFPropertyListOpenStepFormat
) {
2829 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2832 if (format
!= kCFPropertyListBinaryFormat_v1_0
&& format
!= kCFPropertyListXMLFormat_v1_0
) {
2833 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Unknown format option"));
2837 CFIndex result
= CFPropertyListWrite(propertyList
, stream
, format
, 0, &error
);
2838 if (error
&& errorString
) {
2839 *errorString
= __copyErrorDebugDescription(error
);
2841 if (error
) CFRelease(error
);
2845 static void __convertReadStreamToBytes(CFReadStreamRef stream
, CFIndex max
, uint8_t **buffer
, CFIndex
*length
, CFErrorRef
*error
) {
2846 int32_t buflen
= 0, bufsize
= 0, retlen
;
2847 uint8_t *buf
= NULL
, sbuf
[8192];
2849 retlen
= CFReadStreamRead(stream
, sbuf
, __CFMin(8192, max
));
2854 if (retlen
< 0 && error
) {
2855 // Copy the error out
2856 *error
= CFReadStreamCopyError(stream
);
2861 if (bufsize
< buflen
+ retlen
) {
2862 if (bufsize
< 256 * 1024) {
2864 } else if (bufsize
< 16 * 1024 * 1024) {
2867 // once in this stage, this will be really slow
2868 // and really potentially fragment memory
2869 bufsize
+= 256 * 1024;
2871 if (bufsize
< buflen
+ retlen
) bufsize
= buflen
+ retlen
;
2872 buf
= (uint8_t *)CFAllocatorReallocate(kCFAllocatorSystemDefault
, buf
, bufsize
, 0);
2875 memmove(buf
+ buflen
, sbuf
, retlen
);
2886 CFPropertyListRef
CFPropertyListCreateWithStream(CFAllocatorRef allocator
, CFReadStreamRef stream
, CFIndex streamLength
, CFOptionFlags mutabilityOption
, CFPropertyListFormat
*format
, CFErrorRef
*error
) {
2889 CFAssert1(stream
!= NULL
, __kCFLogAssertion
, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__
);
2890 CFAssert1(CFReadStreamGetTypeID() == CFGetTypeID(stream
), __kCFLogAssertion
, "%s(): stream argument is not a read stream", __PRETTY_FUNCTION__
);
2891 CFAssert1(kCFStreamStatusOpen
== CFReadStreamGetStatus(stream
) || kCFStreamStatusReading
== CFReadStreamGetStatus(stream
), __kCFLogAssertion
, "%s(): stream is not open", __PRETTY_FUNCTION__
);
2892 CFAssert2(mutabilityOption
== kCFPropertyListImmutable
|| mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, mutabilityOption
);
2894 if (0 == streamLength
) streamLength
= LONG_MAX
;
2895 CFErrorRef underlyingError
= NULL
;
2897 uint8_t *buffer
= NULL
;
2898 __convertReadStreamToBytes(stream
, streamLength
, &buffer
, &buflen
, &underlyingError
);
2899 if (underlyingError
) {
2901 // Wrap the error from CFReadStream in a new error in the cocoa domain
2902 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2903 CFDictionarySetValue(userInfo
, kCFErrorDebugDescriptionKey
, CFSTR("Property list reading could not be completed because the stream had an unknown error."));
2904 CFDictionarySetValue(userInfo
, kCFErrorUnderlyingErrorKey
, underlyingError
);
2905 *error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, kCFPropertyListReadStreamError
, userInfo
);
2906 CFRelease(userInfo
);
2908 CFRelease(underlyingError
);
2912 if (!buffer
|| buflen
< 6) {
2913 if (buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buffer
);
2914 if (error
) *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("stream had too few bytes"));
2918 CFDataRef data
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, buffer
, buflen
, kCFAllocatorSystemDefault
);
2919 CFPropertyListRef pl
= NULL
; // initialize to null, because if the following call fails we must return NULL
2920 _CFPropertyListCreateWithData(allocator
, data
, mutabilityOption
, error
, true, format
, NULL
, &pl
);
2926 CFPropertyListRef
CFPropertyListCreateFromStream(CFAllocatorRef allocator
, CFReadStreamRef stream
, CFIndex length
, CFOptionFlags mutabilityOption
, CFPropertyListFormat
*format
, CFStringRef
*errorString
) {
2928 if (errorString
) *errorString
= NULL
;
2929 CFErrorRef error
= NULL
;
2930 CFPropertyListRef result
= CFPropertyListCreateWithStream(allocator
, stream
, length
, mutabilityOption
, format
, &error
);
2931 if (error
&& errorString
) {
2932 *errorString
= __copyErrorDebugDescription(error
);
2934 if (error
) CFRelease(error
);
2938 #endif //DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2941 #pragma mark Property List Copies
2943 static CFArrayRef
_arrayDeepImmutableCopy(CFAllocatorRef allocator
, CFArrayRef array
, CFOptionFlags mutabilityOption
) {
2944 CFArrayRef result
= NULL
;
2945 CFIndex i
, c
= CFArrayGetCount(array
);
2947 result
= CFArrayCreate(allocator
, NULL
, 0, &kCFTypeArrayCallBacks
);
2949 new_cftype_array(values
, c
);
2950 CFArrayGetValues(array
, CFRangeMake(0, c
), values
);
2951 for (i
= 0; i
< c
; i
++) {
2952 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, values
[i
], mutabilityOption
);
2953 if (newValue
== NULL
) {
2956 __CFAssignWithWriteBarrier((void **)values
+ i
, (void *)newValue
);
2958 result
= (i
== c
) ? CFArrayCreate(allocator
, values
, c
, &kCFTypeArrayCallBacks
) : NULL
;
2960 if (!_CFAllocatorIsGCRefZero(allocator
)) {
2961 for (i
= 0; i
< c
; i
++) CFRelease(values
[i
]);
2963 free_cftype_array(values
);
2968 static CFMutableArrayRef
_arrayDeepMutableCopy(CFAllocatorRef allocator
, CFArrayRef array
, CFOptionFlags mutabilityOption
) {
2969 CFIndex i
, c
= CFArrayGetCount(array
);
2970 CFMutableArrayRef result
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2972 for (i
= 0; i
< c
; i
++) {
2973 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, CFArrayGetValueAtIndex(array
, i
), mutabilityOption
);
2974 if (!newValue
) break;
2975 CFArrayAppendValue(result
, newValue
);
2976 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(newValue
);
2979 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(result
);
2986 CFPropertyListRef
CFPropertyListCreateDeepCopy(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, CFOptionFlags mutabilityOption
) {
2988 CFPropertyListRef result
= NULL
;
2989 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): cannot copy a NULL property list", __PRETTY_FUNCTION__
);
2990 __CFAssertIsPList(propertyList
);
2991 CFAssert2(mutabilityOption
== kCFPropertyListImmutable
|| mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, mutabilityOption
);
2992 if (!CFPropertyListIsValid(propertyList
, kCFPropertyListBinaryFormat_v1_0
)) return NULL
;
2994 CFTypeID typeID
= CFGetTypeID(propertyList
);
2995 if (typeID
== dicttype
) {
2996 CFDictionaryRef dict
= (CFDictionaryRef
)propertyList
;
2997 Boolean isMutable
= (mutabilityOption
!= kCFPropertyListImmutable
);
2998 CFIndex count
= CFDictionaryGetCount(dict
);
3000 result
= isMutable
? CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
): CFDictionaryCreate(allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3002 new_cftype_array(keys
, 2 * count
);
3005 values
= keys
+count
;
3006 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
3007 for (i
= 0; i
< count
; i
++) {
3008 CFTypeRef newKey
= CFStringCreateCopy(allocator
, (CFStringRef
)keys
[i
]);
3009 if (newKey
== NULL
) {
3012 __CFAssignWithWriteBarrier((void **)keys
+ i
, (void *)newKey
);
3013 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, values
[i
], mutabilityOption
);
3014 if (newValue
== NULL
) {
3015 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(keys
[i
]);
3018 __CFAssignWithWriteBarrier((void **)values
+ i
, (void *)newValue
);
3021 result
= isMutable
? CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
) : CFDictionaryCreate(allocator
, keys
, values
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3022 for (i
= 0; i
< count
; i
++) {
3024 CFDictionarySetValue((CFMutableDictionaryRef
)result
, keys
[i
], values
[i
]);
3026 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(keys
[i
]);
3027 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(values
[i
]);
3032 for (i
= 0; i
< count
; i
++) {
3033 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(keys
[i
]);
3034 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(values
[i
]);
3037 free_cftype_array(keys
);
3039 } else if (typeID
== arraytype
) {
3040 if (mutabilityOption
== kCFPropertyListImmutable
) {
3041 result
= _arrayDeepImmutableCopy(allocator
, (CFArrayRef
)propertyList
, mutabilityOption
);
3043 result
= _arrayDeepMutableCopy(allocator
, (CFArrayRef
)propertyList
, mutabilityOption
);
3045 } else if (typeID
== datatype
) {
3046 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
3047 result
= CFDataCreateMutableCopy(allocator
, 0, (CFDataRef
)propertyList
);
3049 result
= CFDataCreateCopy(allocator
, (CFDataRef
)propertyList
);
3051 } else if (typeID
== numbertype
) {
3052 // Warning - this will break if byteSize is ever greater than 32
3054 CFNumberType numType
= CFNumberGetType((CFNumberRef
)propertyList
);
3055 CFNumberGetValue((CFNumberRef
)propertyList
, numType
, (void *)bytes
);
3056 result
= CFNumberCreate(allocator
, numType
, (void *)bytes
);
3057 } else if (typeID
== booltype
) {
3058 // Booleans are immutable & shared instances
3059 CFRetain(propertyList
);
3060 result
= propertyList
;
3061 } else if (typeID
== datetype
) {
3062 // Dates are immutable
3063 result
= CFDateCreate(allocator
, CFDateGetAbsoluteTime((CFDateRef
)propertyList
));
3064 } else if (typeID
== stringtype
) {
3065 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
3066 result
= CFStringCreateMutableCopy(allocator
, 0, (CFStringRef
)propertyList
);
3068 result
= CFStringCreateCopy(allocator
, (CFStringRef
)propertyList
);
3071 CFAssert2(false, __kCFLogAssertion
, "%s(): %p is not a property list type", __PRETTY_FUNCTION__
, propertyList
);