2 * Copyright (c) 2013 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-2013, 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"
53 CF_EXPORT CFNumberType
_CFNumberGetType2(CFNumberRef number
);
69 #define PLIST_TAG_LENGTH 5
70 #define ARRAY_TAG_LENGTH 5
71 #define DICT_TAG_LENGTH 4
72 #define KEY_TAG_LENGTH 3
73 #define STRING_TAG_LENGTH 6
74 #define DATA_TAG_LENGTH 4
75 #define DATE_TAG_LENGTH 4
76 #define REAL_TAG_LENGTH 4
77 #define INTEGER_TAG_LENGTH 7
78 #define TRUE_TAG_LENGTH 4
79 #define FALSE_TAG_LENGTH 5
80 #define DOCTYPE_TAG_LENGTH 7
81 #define CDSECT_TAG_LENGTH 9
83 #if !defined(new_cftype_array)
84 #define new_cftype_array(N, C) \
85 size_t N ## _count__ = (C); \
86 if (N ## _count__ > LONG_MAX / sizeof(CFTypeRef)) { \
87 CRSetCrashLogMessage("CFPropertyList ran out of memory while attempting to allocate temporary storage."); \
90 Boolean N ## _is_stack__ = (N ## _count__ <= 256); \
91 if (N ## _count__ == 0) N ## _count__ = 1; \
92 STACK_BUFFER_DECL(CFTypeRef, N ## _buffer__, N ## _is_stack__ ? N ## _count__ : 1); \
93 if (N ## _is_stack__) memset(N ## _buffer__, 0, N ## _count__ * sizeof(CFTypeRef)); \
94 CFTypeRef * N = N ## _is_stack__ ? N ## _buffer__ : (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, (N ## _count__) * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory); \
96 CRSetCrashLogMessage("CFPropertyList ran out of memory while attempting to allocate temporary storage."); \
102 #if !defined(free_cftype_array)
103 #define free_cftype_array(N) \
104 if (! N ## _is_stack__) { \
105 CFAllocatorDeallocate(kCFAllocatorSystemDefault, N); \
110 // Used to reference an old-style plist parser error inside of a more general XML error
111 #define CFPropertyListOldStyleParserErrorKey CFSTR("kCFPropertyListOldStyleParsingError")
113 static CFTypeID stringtype
, datatype
, numbertype
, datetype
;
114 static CFTypeID booltype
, nulltype
, dicttype
, arraytype
, settype
;
116 static void initStatics() {
117 static dispatch_once_t once
;
118 dispatch_once(&once
, ^{
119 stringtype
= CFStringGetTypeID();
120 datatype
= CFDataGetTypeID();
121 numbertype
= CFNumberGetTypeID();
122 booltype
= CFBooleanGetTypeID();
123 datetype
= CFDateGetTypeID();
124 dicttype
= CFDictionaryGetTypeID();
125 arraytype
= CFArrayGetTypeID();
126 settype
= CFSetGetTypeID();
127 nulltype
= CFNullGetTypeID();
131 CF_PRIVATE CFErrorRef
__CFPropertyListCreateError(CFIndex code
, CFStringRef debugString
, ...) {
133 CFErrorRef error
= NULL
;
135 if (debugString
!= NULL
) {
136 CFStringRef debugMessage
= NULL
;
137 va_start(argList
, debugString
);
138 debugMessage
= CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault
, NULL
, debugString
, argList
);
141 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
142 CFDictionarySetValue(userInfo
, kCFErrorDebugDescriptionKey
, debugMessage
);
144 error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, code
, userInfo
);
146 CFRelease(debugMessage
);
149 error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, code
, NULL
);
155 static CFStringRef
__copyErrorDebugDescription(CFErrorRef error
) {
156 CFStringRef result
= NULL
;
158 CFDictionaryRef userInfo
= CFErrorCopyUserInfo(error
);
159 if (userInfo
!= NULL
) {
160 CFStringRef desc
= (CFStringRef
) CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
162 result
= CFStringCreateCopy(kCFAllocatorSystemDefault
, desc
);
171 #pragma mark Property List Validation
173 // don't allow _CFKeyedArchiverUID here
174 #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);
179 CFPropertyListFormat format
;
183 static bool __CFPropertyListIsValidAux(CFPropertyListRef plist
, bool recursive
, CFMutableSetRef set
, CFPropertyListFormat format
, CFStringRef
*error
);
185 static void __CFPropertyListIsArrayPlistAux(const void *value
, void *context
) {
186 struct context
*ctx
= (struct context
*)context
;
187 if (!ctx
->answer
) return;
188 if (!value
&& !*(ctx
->error
)) {
189 *(ctx
->error
) = (CFStringRef
)CFRetain(CFSTR("property list arrays cannot contain NULL"));
191 ctx
->answer
= value
&& __CFPropertyListIsValidAux(value
, true, ctx
->set
, ctx
->format
, ctx
->error
);
194 static void __CFPropertyListIsDictPlistAux(const void *key
, const void *value
, void *context
) {
195 struct context
*ctx
= (struct context
*)context
;
196 if (!ctx
->answer
) return;
197 if (!key
&& !*(ctx
->error
)) *(ctx
->error
) = (CFStringRef
)CFRetain(CFSTR("property list dictionaries cannot contain NULL keys"));
198 if (!value
&& !*(ctx
->error
)) *(ctx
->error
) = (CFStringRef
)CFRetain(CFSTR("property list dictionaries cannot contain NULL values"));
199 if (stringtype
!= CFGetTypeID(key
) && !*(ctx
->error
)) {
200 CFStringRef desc
= CFCopyTypeIDDescription(CFGetTypeID(key
));
201 *(ctx
->error
) = CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("property list dictionaries may only have keys which are CFStrings, not '%@'"), desc
);
204 ctx
->answer
= key
&& value
&& (stringtype
== CFGetTypeID(key
)) && __CFPropertyListIsValidAux(value
, true, ctx
->set
, ctx
->format
, ctx
->error
);
207 static bool __CFPropertyListIsValidAux(CFPropertyListRef plist
, bool recursive
, CFMutableSetRef set
, CFPropertyListFormat format
, CFStringRef
*error
) {
210 *error
= (CFStringRef
)CFRetain(CFSTR("property lists cannot contain NULL"));
213 type
= CFGetTypeID(plist
);
214 if (stringtype
== type
) return true;
215 if (datatype
== type
) return true;
216 if (kCFPropertyListOpenStepFormat
!= format
) {
217 if (booltype
== type
) return true;
218 if (numbertype
== type
) return true;
219 if (datetype
== type
) return true;
220 if (_CFKeyedArchiverUIDGetTypeID() == type
) return true;
222 if (!recursive
&& arraytype
== type
) return true;
223 if (!recursive
&& dicttype
== type
) return true;
224 // at any one invocation of this function, set should contain the objects in the "path" down to this object
225 if (CFSetContainsValue(set
, plist
)) {
226 *error
= (CFStringRef
)CFRetain(CFSTR("property lists cannot contain recursive container references"));
229 if (arraytype
== type
) {
230 struct context ctx
= {true, set
, format
, error
};
231 CFSetAddValue(set
, plist
);
232 CFArrayApplyFunction((CFArrayRef
)plist
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
)plist
)), __CFPropertyListIsArrayPlistAux
, &ctx
);
233 CFSetRemoveValue(set
, plist
);
236 if (dicttype
== type
) {
237 struct context ctx
= {true, set
, format
, error
};
238 CFSetAddValue(set
, plist
);
239 CFDictionaryApplyFunction((CFDictionaryRef
)plist
, __CFPropertyListIsDictPlistAux
, &ctx
);
240 CFSetRemoveValue(set
, plist
);
244 CFStringRef desc
= CFCopyTypeIDDescription(type
);
245 *error
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("property lists cannot contain objects of type '%@'"), desc
);
251 static Boolean
_CFPropertyListIsValidWithErrorString(CFPropertyListRef plist
, CFPropertyListFormat format
, CFStringRef
*error
) {
253 CFAssert1(plist
!= NULL
, __kCFLogAssertion
, "%s(): NULL is not a property list", __PRETTY_FUNCTION__
);
254 CFMutableSetRef set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
255 bool result
= __CFPropertyListIsValidAux(plist
, true, set
, format
, error
);
261 #pragma mark Writing Property Lists
263 static const char CFXMLPlistTags
[13][10]= {
264 {'p', 'l', 'i', 's', 't', '\0', '\0', '\0', '\0', '\0'},
265 {'a', 'r', 'r', 'a', 'y', '\0', '\0', '\0', '\0', '\0'},
266 {'d', 'i', 'c', 't', '\0', '\0', '\0', '\0', '\0', '\0'},
267 {'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
268 {'s', 't', 'r', 'i', 'n', 'g', '\0', '\0', '\0', '\0'},
269 {'d', 'a', 't', 'a', '\0', '\0', '\0', '\0', '\0', '\0'},
270 {'d', 'a', 't', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
271 {'r', 'e', 'a', 'l', '\0', '\0', '\0', '\0', '\0', '\0'},
272 {'i', 'n', 't', 'e', 'g', 'e', 'r', '\0', '\0', '\0'},
273 {'t', 'r', 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
274 {'f', 'a', 'l', 's', 'e', '\0', '\0', '\0', '\0', '\0'},
275 {'D', 'O', 'C', 'T', 'Y', 'P', 'E', '\0', '\0', '\0'},
276 {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[', '\0'}
279 static const UniChar CFXMLPlistTagsUnicode
[13][10]= {
280 {'p', 'l', 'i', 's', 't', '\0', '\0', '\0', '\0', '\0'},
281 {'a', 'r', 'r', 'a', 'y', '\0', '\0', '\0', '\0', '\0'},
282 {'d', 'i', 'c', 't', '\0', '\0', '\0', '\0', '\0', '\0'},
283 {'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
284 {'s', 't', 'r', 'i', 'n', 'g', '\0', '\0', '\0', '\0'},
285 {'d', 'a', 't', 'a', '\0', '\0', '\0', '\0', '\0', '\0'},
286 {'d', 'a', 't', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
287 {'r', 'e', 'a', 'l', '\0', '\0', '\0', '\0', '\0', '\0'},
288 {'i', 'n', 't', 'e', 'g', 'e', 'r', '\0', '\0', '\0'},
289 {'t', 'r', 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
290 {'f', 'a', 'l', 's', 'e', '\0', '\0', '\0', '\0', '\0'},
291 {'D', 'O', 'C', 'T', 'Y', 'P', 'E', '\0', '\0', '\0'},
292 {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[', '\0'}
296 const char *begin
; // first character of the XML to be parsed
297 const char *curr
; // current parse location
298 const char *end
; // the first character _after_ the end of the XML
300 CFAllocatorRef allocator
;
301 UInt32 mutabilityOption
;
302 CFBurstTrieRef stringTrie
; // map of cached strings
303 CFMutableArrayRef stringCache
; // retaining array of strings
304 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)
305 CFSetRef keyPaths
; // if NULL, no filtering
306 Boolean skip
; // if true, do not create any objects.
307 } _CFXMLPlistParseInfo
;
309 CF_PRIVATE CFTypeRef
__CFCreateOldStylePropertyListOrStringsFile(CFAllocatorRef allocator
, CFDataRef xmlData
, CFStringRef originalString
, CFStringEncoding guessedEncoding
, CFOptionFlags option
, CFErrorRef
*outError
,CFPropertyListFormat
*format
);
311 CF_INLINE
void __CFPListRelease(CFTypeRef cf
, CFAllocatorRef allocator
) {
312 if (cf
&& !(0)) CFRelease(cf
);
316 // 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.
318 // Null-terminated, ASCII or UTF8 string
320 static void _plistAppendUTF8CString(CFMutableDataRef mData
, const char *cString
) {
321 CFDataAppendBytes (mData
, (const UInt8
*)cString
, strlen(cString
));
326 static void _plistAppendCharacters(CFMutableDataRef mData
, const UniChar
*chars
, CFIndex length
) {
329 do { // Flush out ASCII chars, BUFLEN at a time
331 UInt8 buf
[BUFLEN
], *bufPtr
= buf
;
333 while (cnt
< length
&& (cnt
- curLoc
< BUFLEN
) && (chars
[cnt
] < 128)) *bufPtr
++ = (UInt8
)(chars
[cnt
++]);
334 if (cnt
> curLoc
) { // Flush any ASCII bytes
335 CFDataAppendBytes(mData
, buf
, cnt
- curLoc
);
338 } while (curLoc
< length
&& (chars
[curLoc
] < 128)); // We will exit out of here when we run out of chars or hit a non-ASCII char
340 if (curLoc
< length
) { // Now deal with non-ASCII chars
341 CFDataRef data
= NULL
;
342 CFStringRef str
= NULL
;
343 if ((str
= CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault
, chars
+ curLoc
, length
- curLoc
, kCFAllocatorNull
))) {
344 if ((data
= CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
, 0))) {
345 CFDataAppendBytes (mData
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
350 CFAssert1(str
&& data
, __kCFLogAssertion
, "%s(): Error writing plist", __PRETTY_FUNCTION__
);
356 static void _plistAppendString(CFMutableDataRef mData
, CFStringRef str
) {
357 const UniChar
*chars
;
360 if ((chars
= CFStringGetCharactersPtr(str
))) {
361 _plistAppendCharacters(mData
, chars
, CFStringGetLength(str
));
362 } else if ((cStr
= CFStringGetCStringPtr(str
, kCFStringEncodingASCII
)) || (cStr
= CFStringGetCStringPtr(str
, kCFStringEncodingUTF8
))) {
363 _plistAppendUTF8CString(mData
, cStr
);
364 } else if ((data
= CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
, 0))) {
365 CFDataAppendBytes (mData
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
368 CFAssert1(TRUE
, __kCFLogAssertion
, "%s(): Error in plist writing", __PRETTY_FUNCTION__
);
373 // Append CFString-style format + arguments
375 static void _plistAppendFormat(CFMutableDataRef mData
, CFStringRef format
, ...) {
379 va_start(argList
, format
);
380 fStr
= CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault
, NULL
, format
, argList
);
383 CFAssert1(fStr
, __kCFLogAssertion
, "%s(): Error writing plist", __PRETTY_FUNCTION__
);
384 _plistAppendString(mData
, fStr
);
390 static void _appendIndents(CFIndex numIndents
, CFMutableDataRef str
) {
392 static const UniChar tabs
[NUMTABS
] = {'\t','\t','\t','\t'};
393 for (; numIndents
> 0; numIndents
-= NUMTABS
) _plistAppendCharacters(str
, tabs
, (numIndents
>= NUMTABS
) ? NUMTABS
: numIndents
);
396 /* Append the escaped version of origStr to mStr.
398 static void _appendEscapedString(CFStringRef origStr
, CFMutableDataRef mStr
) {
400 CFIndex i
, length
= CFStringGetLength(origStr
);
402 UniChar buf
[BUFSIZE
];
403 CFStringInlineBuffer inlineBuffer
;
405 CFStringInitInlineBuffer(origStr
, &inlineBuffer
, CFRangeMake(0, length
));
407 for (i
= 0; i
< length
; i
++) {
408 UniChar ch
= __CFStringGetCharacterFromInlineBufferQuick(&inlineBuffer
, i
);
409 if (CFStringIsSurrogateHighCharacter(ch
) && (bufCnt
+ 2 >= BUFSIZE
)) {
410 // flush the buffer first so we have room for a low/high pair and do not split them
411 _plistAppendCharacters(mStr
, buf
, bufCnt
);
417 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
419 _plistAppendUTF8CString(mStr
, "<");
422 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
424 _plistAppendUTF8CString(mStr
, ">");
427 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
429 _plistAppendUTF8CString(mStr
, "&");
433 if (bufCnt
== BUFSIZE
) {
434 _plistAppendCharacters(mStr
, buf
, bufCnt
);
440 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
445 /* Base-64 encoding/decoding */
447 /* The base-64 encoding packs three 8-bit bytes into four 7-bit ASCII
448 * characters. If the number of bytes in the original data isn't divisable
449 * by three, "=" characters are used to pad the encoded data. The complete
450 * set of characters used in base-64 are:
460 // Write the inputData to the mData using Base 64 encoding
462 static void _XMLPlistAppendDataUsingBase64(CFMutableDataRef mData
, CFDataRef inputData
, CFIndex indent
) {
463 static const char __CFPLDataEncodeTable
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
464 #define MAXLINELEN 76
465 char buf
[MAXLINELEN
+ 4 + 2]; // For the slop and carriage return and terminating NULL
467 const uint8_t *bytes
= CFDataGetBytePtr(inputData
);
468 CFIndex length
= CFDataGetLength(inputData
);
472 if (indent
> 8) indent
= 8; // refuse to indent more than 64 characters
474 pos
= 0; // position within buf
476 for (i
= 0, p
= bytes
; i
< length
; i
++, p
++) {
477 /* 3 bytes are encoded as 4 */
480 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[0] >> 2) & 0x3f)];
483 buf
[pos
++] = __CFPLDataEncodeTable
[ ((((p
[-1] << 8) | p
[0]) >> 4) & 0x3f)];
486 buf
[pos
++] = __CFPLDataEncodeTable
[ ((((p
[-1] << 8) | p
[0]) >> 6) & 0x3f)];
487 buf
[pos
++] = __CFPLDataEncodeTable
[ (p
[0] & 0x3f)];
490 /* Flush the line out every 76 (or fewer) chars --- indents count against the line length*/
491 if (pos
>= MAXLINELEN
- 8 * indent
) {
494 _appendIndents(indent
, mData
);
495 _plistAppendUTF8CString(mData
, buf
);
504 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[-1] << 4) & 0x30)];
509 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[-1] << 2) & 0x3c)];
517 _appendIndents(indent
, mData
);
518 _plistAppendUTF8CString(mData
, buf
);
522 extern CFStringRef
__CFNumberCopyFormattingDescriptionAsFloat64(CFTypeRef cf
);
524 static void _CFAppendXML0(CFTypeRef object
, UInt32 indentation
, CFMutableDataRef xmlString
) {
525 UInt32 typeID
= CFGetTypeID(object
);
526 _appendIndents(indentation
, xmlString
);
527 if (typeID
== stringtype
) {
528 _plistAppendUTF8CString(xmlString
, "<");
529 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[STRING_IX
], STRING_TAG_LENGTH
);
530 _plistAppendUTF8CString(xmlString
, ">");
531 _appendEscapedString((CFStringRef
)object
, xmlString
);
532 _plistAppendUTF8CString(xmlString
, "</");
533 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[STRING_IX
], STRING_TAG_LENGTH
);
534 _plistAppendUTF8CString(xmlString
, ">\n");
535 } else if (typeID
== arraytype
) {
536 UInt32 i
, count
= CFArrayGetCount((CFArrayRef
)object
);
538 _plistAppendUTF8CString(xmlString
, "<");
539 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
540 _plistAppendUTF8CString(xmlString
, "/>\n");
543 _plistAppendUTF8CString(xmlString
, "<");
544 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
545 _plistAppendUTF8CString(xmlString
, ">\n");
546 for (i
= 0; i
< count
; i
++) {
547 _CFAppendXML0(CFArrayGetValueAtIndex((CFArrayRef
)object
, i
), indentation
+1, xmlString
);
549 _appendIndents(indentation
, xmlString
);
550 _plistAppendUTF8CString(xmlString
, "</");
551 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
552 _plistAppendUTF8CString(xmlString
, ">\n");
553 } else if (typeID
== dicttype
) {
554 UInt32 i
, count
= CFDictionaryGetCount((CFDictionaryRef
)object
);
555 CFMutableArrayRef keyArray
;
557 _plistAppendUTF8CString(xmlString
, "<");
558 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DICT_IX
], DICT_TAG_LENGTH
);
559 _plistAppendUTF8CString(xmlString
, "/>\n");
562 _plistAppendUTF8CString(xmlString
, "<");
563 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DICT_IX
], DICT_TAG_LENGTH
);
564 _plistAppendUTF8CString(xmlString
, ">\n");
565 new_cftype_array(keys
, count
);
566 CFDictionaryGetKeysAndValues((CFDictionaryRef
)object
, keys
, NULL
);
567 keyArray
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, count
, &kCFTypeArrayCallBacks
);
568 CFArrayReplaceValues(keyArray
, CFRangeMake(0, 0), keys
, count
);
569 CFArraySortValues(keyArray
, CFRangeMake(0, count
), (CFComparatorFunction
)CFStringCompare
, NULL
);
570 CFArrayGetValues(keyArray
, CFRangeMake(0, count
), keys
);
572 for (i
= 0; i
< count
; i
++) {
573 CFTypeRef key
= keys
[i
];
574 _appendIndents(indentation
+1, xmlString
);
575 _plistAppendUTF8CString(xmlString
, "<");
576 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[KEY_IX
], KEY_TAG_LENGTH
);
577 _plistAppendUTF8CString(xmlString
, ">");
578 _appendEscapedString((CFStringRef
)key
, xmlString
);
579 _plistAppendUTF8CString(xmlString
, "</");
580 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[KEY_IX
], KEY_TAG_LENGTH
);
581 _plistAppendUTF8CString(xmlString
, ">\n");
582 _CFAppendXML0(CFDictionaryGetValue((CFDictionaryRef
)object
, key
), indentation
+1, xmlString
);
584 free_cftype_array(keys
);
585 _appendIndents(indentation
, xmlString
);
586 _plistAppendUTF8CString(xmlString
, "</");
587 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DICT_IX
], DICT_TAG_LENGTH
);
588 _plistAppendUTF8CString(xmlString
, ">\n");
589 } else if (typeID
== datatype
) {
590 _plistAppendUTF8CString(xmlString
, "<");
591 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DATA_IX
], DATA_TAG_LENGTH
);
592 _plistAppendUTF8CString(xmlString
, ">\n");
593 _XMLPlistAppendDataUsingBase64(xmlString
, (CFDataRef
)object
, indentation
);
594 _appendIndents(indentation
, xmlString
);
595 _plistAppendUTF8CString(xmlString
, "</");
596 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DATA_IX
], DATA_TAG_LENGTH
);
597 _plistAppendUTF8CString(xmlString
, ">\n");
598 } else if (typeID
== datetype
) {
599 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
600 int32_t y
= 0, M
= 0, d
= 0, H
= 0, m
= 0, s
= 0;
602 CFGregorianDate date
= CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime((CFDateRef
)object
), NULL
);
608 s
= (int32_t)date
.second
;
610 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, kCFCalendarIdentifierGregorian
);
611 CFTimeZoneRef tz
= CFTimeZoneCreateWithName(kCFAllocatorSystemDefault
, CFSTR("GMT"), true);
612 CFCalendarSetTimeZone(calendar
, tz
);
613 CFCalendarDecomposeAbsoluteTime(calendar
, CFDateGetAbsoluteTime((CFDateRef
)object
), (const uint8_t *)"yMdHms", &y
, &M
, &d
, &H
, &m
, &s
);
617 _plistAppendUTF8CString(xmlString
, "<");
618 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DATE_IX
], DATE_TAG_LENGTH
);
619 _plistAppendUTF8CString(xmlString
, ">");
620 _plistAppendFormat(xmlString
, CFSTR("%04d-%02d-%02dT%02d:%02d:%02dZ"), y
, M
, d
, H
, m
, s
);
621 _plistAppendUTF8CString(xmlString
, "</");
622 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[DATE_IX
], DATE_TAG_LENGTH
);
623 _plistAppendUTF8CString(xmlString
, ">\n");
624 } else if (typeID
== numbertype
) {
625 if (CFNumberIsFloatType((CFNumberRef
)object
)) {
626 _plistAppendUTF8CString(xmlString
, "<");
627 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[REAL_IX
], REAL_TAG_LENGTH
);
628 _plistAppendUTF8CString(xmlString
, ">");
629 CFStringRef s
= __CFNumberCopyFormattingDescriptionAsFloat64(object
);
630 _plistAppendString(xmlString
, s
);
632 _plistAppendUTF8CString(xmlString
, "</");
633 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[REAL_IX
], REAL_TAG_LENGTH
);
634 _plistAppendUTF8CString(xmlString
, ">\n");
636 _plistAppendUTF8CString(xmlString
, "<");
637 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
638 _plistAppendUTF8CString(xmlString
, ">");
640 _plistAppendFormat(xmlString
, CFSTR("%@"), object
);
642 _plistAppendUTF8CString(xmlString
, "</");
643 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
644 _plistAppendUTF8CString(xmlString
, ">\n");
646 } else if (typeID
== booltype
) {
647 if (CFBooleanGetValue((CFBooleanRef
)object
)) {
648 _plistAppendUTF8CString(xmlString
, "<");
649 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[TRUE_IX
], TRUE_TAG_LENGTH
);
650 _plistAppendUTF8CString(xmlString
, "/>\n");
652 _plistAppendUTF8CString(xmlString
, "<");
653 _plistAppendCharacters(xmlString
, CFXMLPlistTagsUnicode
[FALSE_IX
], FALSE_TAG_LENGTH
);
654 _plistAppendUTF8CString(xmlString
, "/>\n");
659 static void _CFGenerateXMLPropertyListToData(CFMutableDataRef xml
, CFTypeRef propertyList
) {
660 _plistAppendUTF8CString(xml
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE ");
661 _plistAppendCharacters(xml
, CFXMLPlistTagsUnicode
[PLIST_IX
], PLIST_TAG_LENGTH
);
662 _plistAppendUTF8CString(xml
, " PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<");
663 _plistAppendCharacters(xml
, CFXMLPlistTagsUnicode
[PLIST_IX
], PLIST_TAG_LENGTH
);
664 _plistAppendUTF8CString(xml
, " version=\"1.0\">\n");
666 _CFAppendXML0(propertyList
, 0, xml
);
668 _plistAppendUTF8CString(xml
, "</");
669 _plistAppendCharacters(xml
, CFXMLPlistTagsUnicode
[PLIST_IX
], PLIST_TAG_LENGTH
);
670 _plistAppendUTF8CString(xml
, ">\n");
673 // ========================================================================
675 #pragma mark Exported Creation Functions
677 CFDataRef
_CFPropertyListCreateXMLData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, Boolean checkValidPlist
) {
679 CFMutableDataRef xml
;
680 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
681 if (checkValidPlist
&& !CFPropertyListIsValid(propertyList
, kCFPropertyListXMLFormat_v1_0
)) {
682 __CFAssertIsPList(propertyList
);
685 xml
= CFDataCreateMutable(allocator
, 0);
686 _CFGenerateXMLPropertyListToData(xml
, propertyList
);
690 CFDataRef
CFPropertyListCreateXMLData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
) {
691 return _CFPropertyListCreateXMLData(allocator
, propertyList
, true);
694 CF_EXPORT CFDataRef
_CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator
, CFPropertyListRef propertyList
) {
695 return _CFPropertyListCreateXMLData(allocator
, propertyList
, false);
698 Boolean
CFPropertyListIsValid(CFPropertyListRef plist
, CFPropertyListFormat format
) {
700 CFAssert1(plist
!= NULL
, __kCFLogAssertion
, "%s(): NULL is not a property list", __PRETTY_FUNCTION__
);
701 CFMutableSetRef set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
702 CFStringRef error
= NULL
;
703 bool result
= __CFPropertyListIsValidAux(plist
, true, set
, format
, &error
);
706 CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListIsValid(): %@"), error
);
714 // ========================================================================
716 #pragma mark Reading Plists
719 // ------------------------- Reading plists ------------------
722 static void skipInlineDTD(_CFXMLPlistParseInfo
*pInfo
);
723 static Boolean
parseXMLElement(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
, CFTypeRef
*out
);
725 // warning: doesn't have a good idea of Unicode line separators
726 static UInt32
lineNumber(_CFXMLPlistParseInfo
*pInfo
) {
727 const char *p
= pInfo
->begin
;
729 while (p
< pInfo
->curr
) {
732 if (*(p
+ 1) == '\n')
734 } else if (*p
== '\n') {
742 // warning: doesn't have a good idea of Unicode white space
743 CF_INLINE
void skipWhitespace(_CFXMLPlistParseInfo
*pInfo
) {
744 while (pInfo
->curr
< pInfo
->end
) {
745 switch (*(pInfo
->curr
)) {
758 /* 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. */
760 // pInfo should be just past "<!--"
761 static void skipXMLComment(_CFXMLPlistParseInfo
*pInfo
) {
762 const char *p
= pInfo
->curr
;
763 const char *end
= pInfo
->end
- 3; // Need at least 3 characters to compare against
765 if (*p
== '-' && *(p
+1) == '-' && *(p
+2) == '>') {
771 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unterminated comment started on line %d"), lineNumber(pInfo
));
774 // pInfo should be set to the first character after "<?"
775 static void skipXMLProcessingInstruction(_CFXMLPlistParseInfo
*pInfo
) {
776 const char *begin
= pInfo
->curr
, *end
= pInfo
->end
- 2; // Looking for "?>" so we need at least 2 characters
777 while (pInfo
->curr
< end
) {
778 if (*(pInfo
->curr
) == '?' && *(pInfo
->curr
+1) == '>') {
785 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing the processing instruction begun on line %d"), lineNumber(pInfo
));
788 // first character should be immediately after the "<!"
789 static void skipDTD(_CFXMLPlistParseInfo
*pInfo
) {
790 // First pass "DOCTYPE"
791 if (pInfo
->end
- pInfo
->curr
< DOCTYPE_TAG_LENGTH
|| memcmp(pInfo
->curr
, CFXMLPlistTags
[DOCTYPE_IX
], DOCTYPE_TAG_LENGTH
)) {
792 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Malformed DTD on line %d"), lineNumber(pInfo
));
795 pInfo
->curr
+= DOCTYPE_TAG_LENGTH
;
796 skipWhitespace(pInfo
);
798 // Look for either the beginning of a complex DTD or the end of the DOCTYPE structure
799 while (pInfo
->curr
< pInfo
->end
) {
800 char ch
= *(pInfo
->curr
);
801 if (ch
== '[') break; // inline DTD
802 if (ch
== '>') { // End of the DTD
808 if (pInfo
->curr
== pInfo
->end
) {
809 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing DTD"));
813 // *Sigh* Must parse in-line DTD
814 skipInlineDTD(pInfo
);
815 if (pInfo
->error
) return;
816 skipWhitespace(pInfo
);
817 if (pInfo
->error
) return;
818 if (pInfo
->curr
< pInfo
->end
) {
819 if (*(pInfo
->curr
) == '>') {
822 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while parsing DTD"), *(pInfo
->curr
), lineNumber(pInfo
));
825 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing DTD"));
829 static void skipPERef(_CFXMLPlistParseInfo
*pInfo
) {
830 const char *p
= pInfo
->curr
;
831 while (p
< pInfo
->end
) {
838 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing percent-escape sequence begun on line %d"), lineNumber(pInfo
));
841 // First character should be just past '['
842 static void skipInlineDTD(_CFXMLPlistParseInfo
*pInfo
) {
843 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
845 skipWhitespace(pInfo
);
850 } else if (ch
== '<') {
852 if (pInfo
->curr
>= pInfo
->end
) {
853 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
859 skipXMLProcessingInstruction(pInfo
);
860 } else if (ch
== '!') {
861 if (pInfo
->curr
+ 2 < pInfo
->end
&& (*(pInfo
->curr
+1) == '-' && *(pInfo
->curr
+2) == '-')) {
863 skipXMLComment(pInfo
);
865 // Skip the myriad of DTD declarations of the form "<!string" ... ">"
866 pInfo
->curr
++; // Past both '<' and '!'
867 while (pInfo
->curr
< pInfo
->end
) {
868 if (*(pInfo
->curr
) == '>') break;
871 if (*(pInfo
->curr
) != '>') {
872 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
878 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch
, lineNumber(pInfo
));
881 } else if (ch
== ']') {
885 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch
, lineNumber(pInfo
));
890 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
894 // content ::== (element | CharData | Reference | CDSect | PI | Comment)*
895 // 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).
896 static Boolean
getContentObject(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
, CFTypeRef
*out
) {
897 if (isKey
) *isKey
= false;
898 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
899 skipWhitespace(pInfo
);
900 if (pInfo
->curr
>= pInfo
->end
) {
901 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
904 if (*(pInfo
->curr
) != '<') {
905 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while looking for open tag"), *(pInfo
->curr
), lineNumber(pInfo
));
909 if (pInfo
->curr
>= pInfo
->end
) {
910 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
913 switch (*(pInfo
->curr
)) {
915 // Processing instruction
916 skipXMLProcessingInstruction(pInfo
);
919 // Could be a comment
920 if (pInfo
->curr
+2 >= pInfo
->end
) {
921 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
924 if (*(pInfo
->curr
+1) == '-' && *(pInfo
->curr
+2) == '-') {
926 skipXMLComment(pInfo
);
928 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
933 // Whoops! Looks like we got to the end tag for the element whose content we're parsing
934 pInfo
->curr
--; // Back off to the '<'
937 // Should be an element
938 return parseXMLElement(pInfo
, isKey
, out
);
941 // 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"
945 static void parseCDSect_pl(_CFXMLPlistParseInfo
*pInfo
, CFMutableDataRef stringData
) {
946 const char *end
, *begin
;
947 if (pInfo
->end
- pInfo
->curr
< CDSECT_TAG_LENGTH
) {
948 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
951 if (memcmp(pInfo
->curr
, CFXMLPlistTags
[CDSECT_IX
], CDSECT_TAG_LENGTH
)) {
952 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered improper CDATA opening at line %d"), lineNumber(pInfo
));
955 pInfo
->curr
+= CDSECT_TAG_LENGTH
;
956 begin
= pInfo
->curr
; // Marks the first character of the CDATA content
957 end
= pInfo
->end
-2; // So we can safely look 2 characters beyond p
958 while (pInfo
->curr
< end
) {
959 if (*(pInfo
->curr
) == ']' && *(pInfo
->curr
+1) == ']' && *(pInfo
->curr
+2) == '>') {
961 CFDataAppendBytes(stringData
, (const UInt8
*)begin
, pInfo
->curr
-begin
);
967 // Never found the end mark
969 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not find end of CDATA started on line %d"), lineNumber(pInfo
));
972 // Only legal references are {lt, gt, amp, apos, quote, #ddd, #xAAA}
973 static void parseEntityReference_pl(_CFXMLPlistParseInfo
*pInfo
, CFMutableDataRef stringData
) {
975 pInfo
->curr
++; // move past the '&';
976 len
= pInfo
->end
- pInfo
->curr
; // how many bytes we can safely scan
978 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
983 switch (*(pInfo
->curr
)) {
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
));
993 if (len
>= 3 && *(pInfo
->curr
+1) == 't' && *(pInfo
->curr
+2) == ';') {
998 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1000 case 'a': // "apos" or "amp"
1001 if (len
< 4) { // Not enough characters for either conversion
1002 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1005 if (*(pInfo
->curr
+1) == 'm') {
1007 if (*(pInfo
->curr
+2) == 'p' && *(pInfo
->curr
+3) == ';') {
1012 } else if (*(pInfo
->curr
+1) == 'p') {
1014 if (len
> 4 && *(pInfo
->curr
+2) == 'o' && *(pInfo
->curr
+3) == 's' && *(pInfo
->curr
+4) == ';') {
1020 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1022 case 'q': // "quote"
1023 if (len
>= 5 && *(pInfo
->curr
+1) == 'u' && *(pInfo
->curr
+2) == 'o' && *(pInfo
->curr
+3) == 't' && *(pInfo
->curr
+4) == ';') {
1028 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1033 Boolean isHex
= false;
1034 if ( len
< 4) { // Not enough characters to make it all fit! Need at least "&#d;"
1035 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1039 if (*(pInfo
->curr
) == 'x') {
1043 while (pInfo
->curr
< pInfo
->end
) {
1044 ch
= *(pInfo
->curr
);
1047 // The value in num always refers to the unicode code point. We'll have to convert since the calling function expects UTF8 data.
1048 CFStringRef oneChar
= CFStringCreateWithBytes(pInfo
->allocator
, (const uint8_t *)&num
, 2, kCFStringEncodingUnicode
, NO
);
1049 uint8_t tmpBuf
[6]; // max of 6 bytes for UTF8
1050 CFIndex tmpBufLength
= 0;
1051 CFStringGetBytes(oneChar
, CFRangeMake(0, 1), kCFStringEncodingUTF8
, 0, NO
, tmpBuf
, 6, &tmpBufLength
);
1052 CFDataAppendBytes(stringData
, tmpBuf
, tmpBufLength
);
1053 __CFPListRelease(oneChar
, pInfo
->allocator
);
1056 if (!isHex
) num
= num
*10;
1057 else num
= num
<< 4;
1058 if (ch
<= '9' && ch
>= '0') {
1060 } else if (!isHex
) {
1061 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c at line %d while parsing data"), ch
, lineNumber(pInfo
));
1063 } else if (ch
>= 'a' && ch
<= 'f') {
1064 num
+= 10 + (ch
- 'a');
1065 } else if (ch
>= 'A' && ch
<= 'F') {
1066 num
+= 10 + (ch
- 'A');
1068 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c at line %d while parsing data"), ch
, lineNumber(pInfo
));
1072 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1076 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1079 CFDataAppendBytes(stringData
, (const UInt8
*)&ch
, 1);
1082 static void _createStringMap(_CFXMLPlistParseInfo
*pInfo
) {
1083 pInfo
->stringTrie
= CFBurstTrieCreate();
1084 pInfo
->stringCache
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
1087 static void _cleanupStringMap(_CFXMLPlistParseInfo
*pInfo
) {
1088 CFBurstTrieRelease(pInfo
->stringTrie
);
1089 CFRelease(pInfo
->stringCache
);
1090 pInfo
->stringTrie
= NULL
;
1091 pInfo
->stringCache
= NULL
;
1094 static CFStringRef
_createUniqueStringWithUTF8Bytes(_CFXMLPlistParseInfo
*pInfo
, const char *base
, CFIndex length
) {
1095 if (length
== 0) return !(0) ? (CFStringRef
)CFRetain(CFSTR("")) : CFSTR("");
1097 CFStringRef result
= NULL
;
1098 uint32_t payload
= 0;
1099 Boolean uniqued
= CFBurstTrieContainsUTF8String(pInfo
->stringTrie
, (UInt8
*)base
, length
, &payload
);
1100 if (uniqued
&& payload
> 0) {
1101 // The index is at payload - 1 (see below).
1102 result
= (CFStringRef
)CFArrayGetValueAtIndex(pInfo
->stringCache
, (CFIndex
)payload
- 1);
1105 result
= CFStringCreateWithBytes(pInfo
->allocator
, (const UInt8
*)base
, length
, kCFStringEncodingUTF8
, NO
);
1106 if (!result
) return NULL
;
1107 // Payload must be >0, so the actual index of the value is at payload - 1
1108 // We also get add to the array after we make sure that CFBurstTrieAddUTF8String succeeds (it can fail, if the string is too large, for example)
1109 payload
= CFArrayGetCount(pInfo
->stringCache
) + 1;
1110 Boolean didAddToBurstTrie
= CFBurstTrieAddUTF8String(pInfo
->stringTrie
, (UInt8
*)base
, length
, payload
);
1111 if (didAddToBurstTrie
) {
1112 CFArrayAppendValue(pInfo
->stringCache
, result
);
1118 // String could be comprised of characters, CDSects, or references to one of the "well-known" entities ('<', '>', '&', ''', '"')
1119 static Boolean
parseStringTag(_CFXMLPlistParseInfo
*pInfo
, CFStringRef
*out
) {
1120 const char *mark
= pInfo
->curr
;
1121 CFMutableDataRef stringData
= NULL
;
1122 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
1123 char ch
= *(pInfo
->curr
);
1125 if (pInfo
->curr
+ 1 >= pInfo
->end
) break;
1126 // Could be a CDSect; could be the end of the string
1127 if (*(pInfo
->curr
+1) != '!') break; // End of the string
1128 if (!stringData
) stringData
= CFDataCreateMutable(pInfo
->allocator
, 0);
1129 CFDataAppendBytes(stringData
, (const UInt8
*)mark
, pInfo
->curr
- mark
);
1130 parseCDSect_pl(pInfo
, stringData
); // TODO: move to return boolean
1132 } else if (ch
== '&') {
1133 if (!stringData
) stringData
= CFDataCreateMutable(pInfo
->allocator
, 0);
1134 CFDataAppendBytes(stringData
, (const UInt8
*)mark
, pInfo
->curr
- mark
);
1135 parseEntityReference_pl(pInfo
, stringData
); // TODO: move to return boolean
1143 __CFPListRelease(stringData
, pInfo
->allocator
);
1151 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
1152 CFStringRef s
= _createUniqueStringWithUTF8Bytes(pInfo
, mark
, pInfo
->curr
- mark
);
1154 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unable to convert string to correct encoding"));
1159 CFStringRef s
= CFStringCreateWithBytes(pInfo
->allocator
, (const UInt8
*)mark
, pInfo
->curr
- mark
, kCFStringEncodingUTF8
, NO
);
1161 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unable to convert string to correct encoding"));
1164 *out
= CFStringCreateMutableCopy(pInfo
->allocator
, 0, s
);
1165 __CFPListRelease(s
, pInfo
->allocator
);
1173 CFDataAppendBytes(stringData
, (const UInt8
*)mark
, pInfo
->curr
- mark
);
1174 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
1175 CFStringRef s
= _createUniqueStringWithUTF8Bytes(pInfo
, (const char *)CFDataGetBytePtr(stringData
), CFDataGetLength(stringData
));
1177 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unable to convert string to correct encoding"));
1182 CFStringRef s
= CFStringCreateWithBytes(pInfo
->allocator
, (const UInt8
*)CFDataGetBytePtr(stringData
), CFDataGetLength(stringData
), kCFStringEncodingUTF8
, NO
);
1184 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unable to convert string to correct encoding"));
1187 *out
= CFStringCreateMutableCopy(pInfo
->allocator
, 0, s
);
1188 __CFPListRelease(s
, pInfo
->allocator
);
1191 __CFPListRelease(stringData
, pInfo
->allocator
);
1196 static Boolean
checkForCloseTag(_CFXMLPlistParseInfo
*pInfo
, const char *tag
, CFIndex tagLen
) {
1197 if (pInfo
->end
- pInfo
->curr
< tagLen
+ 3) {
1198 if (!pInfo
->error
) {
1199 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1203 if (*(pInfo
->curr
) != '<' || *(++pInfo
->curr
) != '/') {
1204 if (!pInfo
->error
) {
1205 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while looking for close tag"), *(pInfo
->curr
), lineNumber(pInfo
));
1210 if (memcmp(pInfo
->curr
, tag
, tagLen
)) {
1211 CFStringRef str
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, (const UInt8
*)tag
, tagLen
, kCFStringEncodingUTF8
, NO
);
1212 if (!pInfo
->error
) {
1213 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Close tag on line %d does not match open tag %@"), lineNumber(pInfo
), str
);
1218 pInfo
->curr
+= tagLen
;
1219 skipWhitespace(pInfo
);
1220 if (pInfo
->curr
== pInfo
->end
) {
1221 if (!pInfo
->error
) {
1222 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1226 if (*(pInfo
->curr
) != '>') {
1227 if (!pInfo
->error
) {
1228 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while looking for close tag"), *(pInfo
->curr
), lineNumber(pInfo
));
1236 // pInfo should be set to the first content character of the <plist>
1237 static Boolean
parsePListTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1238 CFTypeRef result
= NULL
;
1239 if (!getContentObject(pInfo
, NULL
, &result
)) {
1240 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty plist tag"));
1243 const char *save
= pInfo
->curr
; // Save this in case the next step fails
1244 CFTypeRef tmp
= NULL
;
1245 if (getContentObject(pInfo
, NULL
, &tmp
)) {
1246 // Got an extra object
1247 __CFPListRelease(tmp
, pInfo
->allocator
);
1248 __CFPListRelease(result
, pInfo
->allocator
);
1250 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected element at line %d (plist can only include one object)"), lineNumber(pInfo
));
1254 // Parse failed catastrophically
1255 __CFPListRelease(result
, pInfo
->allocator
);
1258 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
)) {
1259 __CFPListRelease(result
, pInfo
->allocator
);
1266 static int allowImmutableCollections
= -1;
1268 static void checkImmutableCollections(void) {
1269 allowImmutableCollections
= (NULL
== __CFgetenv("CFPropertyListAllowImmutableCollections")) ? 0 : 1;
1272 // 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
1273 static CFSetRef
createTopLevelKeypaths(CFAllocatorRef allocator
, CFSetRef keyPaths
) {
1274 if (!keyPaths
) return NULL
;
1276 CFIndex count
= CFSetGetCount(keyPaths
);
1277 new_cftype_array(keyPathValues
, count
);
1278 CFSetGetValues(keyPaths
, keyPathValues
);
1279 CFMutableSetRef splitKeyPathSet
= CFSetCreateMutable(allocator
, count
, &kCFTypeSetCallBacks
);
1280 for (CFIndex i
= 0; i
< count
; i
++) {
1281 // Split each key path and add it to the split path set, which we will reference throughout parsing
1282 CFArrayRef split
= CFStringCreateArrayBySeparatingStrings(allocator
, (CFStringRef
)(keyPathValues
[i
]), CFSTR(":"));
1283 CFSetAddValue(splitKeyPathSet
, split
);
1284 __CFPListRelease(split
, allocator
);
1286 free_cftype_array(keyPathValues
);
1287 return splitKeyPathSet
;
1290 // 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)
1291 CF_PRIVATE
void __CFPropertyListCreateSplitKeypaths(CFAllocatorRef allocator
, CFSetRef currentKeys
, CFSetRef
*theseKeys
, CFSetRef
*nextKeys
) {
1292 if (!currentKeys
) { *theseKeys
= NULL
; *nextKeys
= NULL
; return; }
1294 CFIndex count
= CFSetGetCount(currentKeys
);
1296 // 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.
1297 CFMutableSetRef outTheseKeys
= NULL
;
1298 CFMutableSetRef outNextKeys
= NULL
;
1300 new_cftype_array(currentKeyPaths
, count
);
1301 CFSetGetValues(currentKeys
, currentKeyPaths
);
1302 for (CFIndex i
= 0; i
< count
; i
++) {
1303 CFArrayRef oneKeyPath
= (CFArrayRef
)currentKeyPaths
[i
];
1304 CFIndex keyPathCount
= CFArrayGetCount(oneKeyPath
);
1306 if (keyPathCount
> 0) {
1307 if (!outTheseKeys
) outTheseKeys
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
1309 CFSetAddValue(outTheseKeys
, CFArrayGetValueAtIndex(oneKeyPath
, 0));
1312 if (keyPathCount
> 1) {
1313 if (!outNextKeys
) outNextKeys
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
1315 // Create an array with values from 1 - end of list
1316 new_cftype_array(restOfKeys
, keyPathCount
- 1);
1317 CFArrayGetValues(oneKeyPath
, CFRangeMake(1, CFArrayGetCount(oneKeyPath
) - 1), restOfKeys
);
1318 CFArrayRef newNextKeys
= CFArrayCreate(allocator
, restOfKeys
, CFArrayGetCount(oneKeyPath
) - 1, &kCFTypeArrayCallBacks
);
1319 CFSetAddValue(outNextKeys
, newNextKeys
);
1320 __CFPListRelease(newNextKeys
, allocator
);
1321 free_cftype_array(restOfKeys
);
1326 *theseKeys
= outTheseKeys
;
1327 *nextKeys
= outNextKeys
;
1330 static Boolean
parseArrayTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1331 CFTypeRef tmp
= NULL
;
1334 Boolean result
= getContentObject(pInfo
, NULL
, &tmp
);
1337 // Shouldn't happen (if skipping, all content values should be null), but just in case
1338 __CFPListRelease(tmp
, pInfo
->allocator
);
1340 result
= getContentObject(pInfo
, NULL
, &tmp
);
1344 // getContentObject encountered a parse error
1347 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
)) {
1355 CFMutableArrayRef array
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
1359 CFSetRef oldKeyPaths
= pInfo
->keyPaths
;
1360 CFSetRef newKeyPaths
, keys
;
1361 __CFPropertyListCreateSplitKeypaths(pInfo
->allocator
, pInfo
->keyPaths
, &keys
, &newKeyPaths
);
1364 CFStringRef countString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("%ld"), count
);
1365 if (!CFSetContainsValue(keys
, countString
)) pInfo
->skip
= true;
1366 __CFPListRelease(countString
, pInfo
->allocator
);
1368 pInfo
->keyPaths
= newKeyPaths
;
1370 result
= getContentObject(pInfo
, NULL
, &tmp
);
1372 pInfo
->keyPaths
= oldKeyPaths
;
1373 pInfo
->skip
= false;
1378 CFArrayAppendValue(array
, tmp
);
1379 __CFPListRelease(tmp
, pInfo
->allocator
);
1383 // prep for getting next object
1384 CFStringRef countString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("%ld"), count
);
1385 if (!CFSetContainsValue(keys
, countString
)) pInfo
->skip
= true;
1386 __CFPListRelease(countString
, pInfo
->allocator
);
1388 pInfo
->keyPaths
= newKeyPaths
;
1390 result
= getContentObject(pInfo
, NULL
, &tmp
);
1392 // reset after getting object
1393 pInfo
->keyPaths
= oldKeyPaths
;
1394 pInfo
->skip
= false;
1399 __CFPListRelease(newKeyPaths
, pInfo
->allocator
);
1400 __CFPListRelease(keys
, pInfo
->allocator
);
1402 if (pInfo
->error
) { // getContentObject encountered a parse error
1403 __CFPListRelease(array
, pInfo
->allocator
);
1406 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
)) {
1407 __CFPListRelease(array
, pInfo
->allocator
);
1410 if (-1 == allowImmutableCollections
) {
1411 checkImmutableCollections();
1413 if (1 == allowImmutableCollections
) {
1414 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1415 CFArrayRef newArray
= CFArrayCreateCopy(pInfo
->allocator
, array
);
1416 __CFPListRelease(array
, pInfo
->allocator
);
1417 array
= (CFMutableArrayRef
)newArray
;
1424 static Boolean
parseDictTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1427 CFTypeRef key
= NULL
, value
= NULL
;
1430 result
= getContentObject(pInfo
, &gotKey
, &key
);
1433 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo
));
1436 result
= getContentObject(pInfo
, NULL
, &value
);
1438 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo
));
1441 // key and value should be null, but we'll release just in case here
1442 __CFPListRelease(key
, pInfo
->allocator
);
1444 __CFPListRelease(value
, pInfo
->allocator
);
1446 result
= getContentObject(pInfo
, &gotKey
, &key
);
1448 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
)) {
1456 CFSetRef oldKeyPaths
= pInfo
->keyPaths
;
1457 CFSetRef nextKeyPaths
, theseKeyPaths
;
1458 __CFPropertyListCreateSplitKeypaths(pInfo
->allocator
, pInfo
->keyPaths
, &theseKeyPaths
, &nextKeyPaths
);
1460 CFMutableDictionaryRef dict
= NULL
;
1462 result
= getContentObject(pInfo
, &gotKey
, &key
);
1463 while (result
&& key
) {
1465 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo
));
1466 __CFPListRelease(key
, pInfo
->allocator
);
1467 __CFPListRelease(nextKeyPaths
, pInfo
->allocator
);
1468 __CFPListRelease(theseKeyPaths
, pInfo
->allocator
);
1469 __CFPListRelease(dict
, pInfo
->allocator
);
1473 if (theseKeyPaths
) {
1474 if (!CFSetContainsValue(theseKeyPaths
, key
)) pInfo
->skip
= true;
1475 pInfo
->keyPaths
= nextKeyPaths
;
1477 result
= getContentObject(pInfo
, NULL
, &value
);
1478 if (theseKeyPaths
) {
1479 pInfo
->keyPaths
= oldKeyPaths
;
1480 pInfo
->skip
= false;
1484 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo
));
1485 __CFPListRelease(key
, pInfo
->allocator
);
1486 __CFPListRelease(nextKeyPaths
, pInfo
->allocator
);
1487 __CFPListRelease(theseKeyPaths
, pInfo
->allocator
);
1488 __CFPListRelease(dict
, pInfo
->allocator
);
1494 dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1495 _CFDictionarySetCapacity(dict
, 10);
1497 CFDictionarySetValue(dict
, key
, value
);
1500 __CFPListRelease(key
, pInfo
->allocator
);
1502 __CFPListRelease(value
, pInfo
->allocator
);
1505 result
= getContentObject(pInfo
, &gotKey
, &key
);
1508 __CFPListRelease(nextKeyPaths
, pInfo
->allocator
);
1509 __CFPListRelease(theseKeyPaths
, pInfo
->allocator
);
1511 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
)) {
1513 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1514 dict
= (CFMutableDictionaryRef
)CFDictionaryCreate(pInfo
->allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1516 dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1519 CFIndex cnt
= CFDictionaryGetCount(dict
);
1521 CFTypeRef val
= CFDictionaryGetValue(dict
, CFSTR("CF$UID"));
1522 if (val
&& CFGetTypeID(val
) == numbertype
) {
1525 CFNumberGetValue((CFNumberRef
)val
, kCFNumberSInt32Type
, &v
);
1526 uid
= (CFTypeRef
)_CFKeyedArchiverUIDCreate(pInfo
->allocator
, v
);
1527 __CFPListRelease(dict
, pInfo
->allocator
);
1532 if (-1 == allowImmutableCollections
) checkImmutableCollections();
1533 if (1 == allowImmutableCollections
) {
1534 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1535 CFDictionaryRef newDict
= CFDictionaryCreateCopy(pInfo
->allocator
, dict
);
1536 __CFPListRelease(dict
, pInfo
->allocator
);
1537 dict
= (CFMutableDictionaryRef
)newDict
;
1545 if (dict
) __CFPListRelease(dict
, pInfo
->allocator
);
1549 static Boolean
parseDataTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1550 const char *base
= pInfo
->curr
;
1551 static const signed char dataDecodeTable
[128] = {
1552 /* 000 */ -1, -1, -1, -1, -1, -1, -1, -1,
1553 /* 010 */ -1, -1, -1, -1, -1, -1, -1, -1,
1554 /* 020 */ -1, -1, -1, -1, -1, -1, -1, -1,
1555 /* 030 */ -1, -1, -1, -1, -1, -1, -1, -1,
1556 /* ' ' */ -1, -1, -1, -1, -1, -1, -1, -1,
1557 /* '(' */ -1, -1, -1, 62, -1, -1, -1, 63,
1558 /* '0' */ 52, 53, 54, 55, 56, 57, 58, 59,
1559 /* '8' */ 60, 61, -1, -1, -1, 0, -1, -1,
1560 /* '@' */ -1, 0, 1, 2, 3, 4, 5, 6,
1561 /* 'H' */ 7, 8, 9, 10, 11, 12, 13, 14,
1562 /* 'P' */ 15, 16, 17, 18, 19, 20, 21, 22,
1563 /* 'X' */ 23, 24, 25, -1, -1, -1, -1, -1,
1564 /* '`' */ -1, 26, 27, 28, 29, 30, 31, 32,
1565 /* 'h' */ 33, 34, 35, 36, 37, 38, 39, 40,
1566 /* 'p' */ 41, 42, 43, 44, 45, 46, 47, 48,
1567 /* 'x' */ 49, 50, 51, -1, -1, -1, -1, -1
1571 int tmpbuflen
= 256;
1572 uint8_t *tmpbuf
= pInfo
->skip
? NULL
: (uint8_t *)CFAllocatorAllocate(pInfo
->allocator
, tmpbuflen
, 0);
1577 for (; pInfo
->curr
< pInfo
->end
; pInfo
->curr
++) {
1578 signed char c
= *(pInfo
->curr
);
1584 } else if (!isspace(c
)) {
1587 if (dataDecodeTable
[c
] < 0)
1591 acc
+= dataDecodeTable
[c
];
1592 if (!pInfo
->skip
&& 0 == (cntr
& 0x3)) {
1593 if (tmpbuflen
<= tmpbufpos
+ 2) {
1594 if (tmpbuflen
< 256 * 1024) {
1596 } else if (tmpbuflen
< 16 * 1024 * 1024) {
1599 // once in this stage, this will be really slow
1600 // and really potentially fragment memory
1601 tmpbuflen
+= 256 * 1024;
1603 tmpbuf
= (uint8_t *)CFAllocatorReallocate(pInfo
->allocator
, tmpbuf
, tmpbuflen
, 0);
1604 if (!tmpbuf
) HALT
; // out of memory
1606 tmpbuf
[tmpbufpos
++] = (acc
>> 16) & 0xff;
1607 if (numeq
< 2) tmpbuf
[tmpbufpos
++] = (acc
>> 8) & 0xff;
1608 if (numeq
< 1) tmpbuf
[tmpbufpos
++] = acc
& 0xff;
1612 CFDataRef result
= NULL
;
1614 if (pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1615 result
= (CFDataRef
)CFDataCreateMutable(pInfo
->allocator
, 0);
1616 CFDataAppendBytes((CFMutableDataRef
)result
, tmpbuf
, tmpbufpos
);
1617 CFAllocatorDeallocate(pInfo
->allocator
, tmpbuf
);
1619 result
= CFDataCreateWithBytesNoCopy(pInfo
->allocator
, tmpbuf
, tmpbufpos
, pInfo
->allocator
);
1623 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not interpret <data> at line %d (should be base64-encoded)"), lineNumber(pInfo
));
1628 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
)) {
1632 __CFPListRelease(result
, pInfo
->allocator
);
1637 CF_INLINE Boolean
read2DigitNumber(_CFXMLPlistParseInfo
*pInfo
, int32_t *result
) {
1639 if (pInfo
->curr
+ 2 >= pInfo
->end
) {
1643 ch2
= *(pInfo
->curr
+ 1);
1645 if (!isdigit(ch1
) || !isdigit(ch2
)) {
1648 *result
= (ch1
- '0')*10 + (ch2
- '0');
1652 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
1653 static Boolean
parseDateTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1654 int32_t year
= 0, month
= 0, day
= 0, hour
= 0, minute
= 0, second
= 0;
1656 Boolean badForm
= false;
1657 Boolean yearIsNegative
= false;
1659 if (pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== '-') {
1660 yearIsNegative
= true;
1664 while (pInfo
->curr
< pInfo
->end
&& isdigit(*pInfo
->curr
)) {
1665 year
= 10*year
+ (*pInfo
->curr
) - '0';
1668 if (pInfo
->curr
>= pInfo
->end
|| *pInfo
->curr
!= '-') {
1674 if (!badForm
&& read2DigitNumber(pInfo
, &month
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== '-') {
1680 if (!badForm
&& read2DigitNumber(pInfo
, &day
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== 'T') {
1686 if (!badForm
&& read2DigitNumber(pInfo
, &hour
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== ':') {
1692 if (!badForm
&& read2DigitNumber(pInfo
, &minute
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== ':') {
1698 if (!badForm
&& read2DigitNumber(pInfo
, &num
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== 'Z') {
1705 if (badForm
|| !checkForCloseTag(pInfo
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
)) {
1706 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not interpret <date> at line %d"), lineNumber(pInfo
));
1710 CFAbsoluteTime at
= 0.0;
1712 CFGregorianDate date
= {yearIsNegative
? -year
: year
, month
, day
, hour
, minute
, second
};
1713 at
= CFGregorianDateGetAbsoluteTime(date
, NULL
);
1715 // this doesn't work
1716 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, kCFCalendarIdentifierGregorian
);
1717 CFTimeZoneRef tz
= CFTimeZoneCreateWithName(kCFAllocatorSystemDefault
, CFSTR("GMT"), true);
1718 CFCalendarSetTimeZone(calendar
, tz
);
1719 CFCalendarComposeAbsoluteTime(calendar
, &at
, (const uint8_t *)"yMdHms", year
, month
, day
, hour
, minute
, second
);
1720 CFRelease(calendar
);
1726 *out
= CFDateCreate(pInfo
->allocator
, at
);
1731 static Boolean
parseRealTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1732 CFStringRef str
= NULL
;
1733 if (!parseStringTag(pInfo
, &str
)) {
1734 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo
));
1738 CFNumberRef result
= NULL
;
1741 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("nan"), kCFCompareCaseInsensitive
)) result
= kCFNumberNaN
;
1742 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("+infinity"), kCFCompareCaseInsensitive
)) result
= kCFNumberPositiveInfinity
;
1743 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("-infinity"), kCFCompareCaseInsensitive
)) result
= kCFNumberNegativeInfinity
;
1744 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("infinity"), kCFCompareCaseInsensitive
)) result
= kCFNumberPositiveInfinity
;
1745 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("-inf"), kCFCompareCaseInsensitive
)) result
= kCFNumberNegativeInfinity
;
1746 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("inf"), kCFCompareCaseInsensitive
)) result
= kCFNumberPositiveInfinity
;
1747 else if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("+inf"), kCFCompareCaseInsensitive
)) result
= kCFNumberPositiveInfinity
;
1752 CFIndex len
= CFStringGetLength(str
);
1753 CFStringInlineBuffer buf
;
1754 CFStringInitInlineBuffer(str
, &buf
, CFRangeMake(0, len
));
1757 if (!__CFStringScanDouble(&buf
, NULL
, &idx
, &val
) || idx
!= len
) {
1758 __CFPListRelease(str
, pInfo
->allocator
);
1759 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered misformatted real on line %d"), lineNumber(pInfo
));
1762 result
= CFNumberCreate(pInfo
->allocator
, kCFNumberDoubleType
, &val
);
1766 __CFPListRelease(str
, pInfo
->allocator
);
1767 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) {
1771 __CFPListRelease(result
, pInfo
->allocator
);
1776 #define GET_CH if (pInfo->curr == pInfo->end) { \
1777 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Premature end of file after <integer> on line %d"), lineNumber(pInfo)); \
1788 kCFNumberSInt128Type
= 17
1791 CF_INLINE
bool isWhitespace(const char *utf8bytes
, const char *end
) {
1792 // Converted UTF-16 isWhitespace from CFString to UTF8 bytes to get full list of UTF8 whitespace
1814 // Except we consider some additional values from 0x0 to 0x21 and 0x7E to 0xA1 as whitespace, for compatability
1815 unsigned char byte1
= *utf8bytes
;
1816 if (byte1
< 0x21 || (byte1
> 0x7E && byte1
< 0xA1)) return true;
1817 if ((byte1
== 0xe2 || byte1
== 0xe3) && (end
- utf8bytes
>= 3)) {
1818 // Check other possibilities in the 3-bytes range
1819 unsigned char byte2
= *(utf8bytes
+ 1);
1820 unsigned char byte3
= *(utf8bytes
+ 2);
1821 if (byte1
== 0xe2 && byte2
== 0x80) {
1822 return ((byte3
>= 80 && byte3
<= 0x8b) || byte3
== 0xaf);
1823 } else if (byte1
== 0xe2 && byte2
== 0x81) {
1824 return byte3
== 0x9f;
1825 } else if (byte1
== 0xe3 && byte2
== 0x80 && byte3
== 0x80) {
1832 static Boolean
parseIntegerTag(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
1833 bool isHex
= false, isNeg
= false, hadLeadingZero
= false;
1836 // decimal_constant S*(-|+)?S*[0-9]+ (S == space)
1837 // hex_constant S*(-|+)?S*0[xX][0-9a-fA-F]+ (S == space)
1839 while (pInfo
->curr
< pInfo
->end
&& isWhitespace(pInfo
->curr
, pInfo
->end
)) pInfo
->curr
++;
1842 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo
));
1845 if ('-' == ch
|| '+' == ch
) {
1846 isNeg
= ('-' == ch
);
1848 while (pInfo
->curr
< pInfo
->end
&& isWhitespace(pInfo
->curr
, pInfo
->end
)) pInfo
->curr
++;
1852 if (pInfo
->curr
+ 1 < pInfo
->end
&& ('x' == *(pInfo
->curr
+ 1) || 'X' == *(pInfo
->curr
+ 1))) {
1856 hadLeadingZero
= true;
1862 hadLeadingZero
= true;
1866 if ('<' == ch
&& hadLeadingZero
) { // nothing but zeros
1868 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
)) {
1869 // checkForCloseTag() sets error string
1875 *out
= CFNumberCreate(pInfo
->allocator
, kCFNumberSInt32Type
, &val
);
1880 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Incomplete <integer> on line %d"), lineNumber(pInfo
));
1884 uint32_t multiplier
= (isHex
? 16 : 10);
1886 uint32_t new_digit
= 0;
1888 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
1889 new_digit
= (ch
- '0');
1891 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1892 new_digit
= (ch
- 'a' + 10);
1894 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1895 new_digit
= (ch
- 'A' + 10);
1897 default: // other character
1898 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unknown character '%c' (0x%x) in <integer> on line %d"), ch
, ch
, lineNumber(pInfo
));
1901 if (!isHex
&& new_digit
> 9) {
1902 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Hex digit in non-hex <integer> on line %d"), lineNumber(pInfo
));
1905 if (UINT64_MAX
/ multiplier
< value
) {
1906 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo
));
1909 value
= multiplier
* value
;
1910 if (UINT64_MAX
- new_digit
< value
) {
1911 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo
));
1914 value
= value
+ new_digit
;
1915 if (isNeg
&& (uint64_t)INT64_MAX
+ 1 < value
) {
1916 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Integer underflow in <integer> on line %d"), lineNumber(pInfo
));
1922 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
)) {
1923 // checkForCloseTag() sets error string
1930 if (isNeg
|| value
<= INT64_MAX
) {
1932 if (isNeg
) v
= -v
; // no-op if INT64_MIN
1933 *out
= CFNumberCreate(pInfo
->allocator
, kCFNumberSInt64Type
, &v
);
1935 CFSInt128Struct val
;
1938 *out
= CFNumberCreate(pInfo
->allocator
, kCFNumberSInt128Type
, &val
);
1946 // Returned object is retained; caller must free. pInfo->curr expected to point to the first character after the '<'
1947 static Boolean
parseXMLElement(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
, CFTypeRef
*out
) {
1948 const char *marker
= pInfo
->curr
;
1949 int markerLength
= -1;
1953 if (isKey
) *isKey
= false;
1954 while (pInfo
->curr
< pInfo
->end
) {
1955 char ch
= *(pInfo
->curr
);
1956 if (ch
== ' ' || ch
== '\t' || ch
== '\n' || ch
=='\r') {
1957 if (markerLength
== -1) markerLength
= pInfo
->curr
- marker
;
1958 } else if (ch
== '>') {
1963 if (pInfo
->curr
>= pInfo
->end
) {
1966 isEmpty
= (*(pInfo
->curr
-1) == '/');
1967 if (markerLength
== -1)
1968 markerLength
= pInfo
->curr
- (isEmpty
? 1 : 0) - marker
;
1969 pInfo
->curr
++; // Advance past '>'
1970 if (markerLength
== 0) {
1971 // Back up to the beginning of the marker
1972 pInfo
->curr
= marker
;
1973 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Malformed tag on line %d"), lineNumber(pInfo
));
1978 if (markerLength
== ARRAY_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
))
1979 markerIx
= ARRAY_IX
;
1981 case 'd': // Dictionary, data, or date; Fortunately, they all have the same marker length....
1982 if (markerLength
!= DICT_TAG_LENGTH
)
1984 if (!memcmp(marker
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
))
1986 else if (!memcmp(marker
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
))
1988 else if (!memcmp(marker
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
))
1991 case 'f': // false (boolean)
1992 if (markerLength
== FALSE_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
)) {
1993 markerIx
= FALSE_IX
;
1996 case 'i': // integer
1997 if (markerLength
== INTEGER_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
))
1998 markerIx
= INTEGER_IX
;
2000 case 'k': // Key of a dictionary
2001 if (markerLength
== KEY_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
)) {
2003 if (isKey
) *isKey
= true;
2007 if (markerLength
== PLIST_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
))
2008 markerIx
= PLIST_IX
;
2011 if (markerLength
== REAL_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
))
2015 if (markerLength
== STRING_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[STRING_IX
], STRING_TAG_LENGTH
))
2016 markerIx
= STRING_IX
;
2018 case 't': // true (boolean)
2019 if (markerLength
== TRUE_TAG_LENGTH
&& !memcmp(marker
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
))
2024 if (!pInfo
->allowNewTypes
&& markerIx
!= PLIST_IX
&& markerIx
!= ARRAY_IX
&& markerIx
!= DICT_IX
&& markerIx
!= STRING_IX
&& markerIx
!= KEY_IX
&& markerIx
!= DATA_IX
) {
2025 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered new tag when expecting only old-style property list objects"));
2032 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty plist tag"));
2035 return parsePListTag(pInfo
, out
);
2041 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
2042 *out
= CFArrayCreate(pInfo
->allocator
, NULL
, 0, &kCFTypeArrayCallBacks
);
2044 *out
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
2049 return parseArrayTag(pInfo
, out
);
2056 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
2057 *out
= CFDictionaryCreate(pInfo
->allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2059 *out
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2064 return parseDictTag(pInfo
, out
);
2069 int tagLen
= (markerIx
== KEY_IX
) ? KEY_TAG_LENGTH
: STRING_TAG_LENGTH
;
2074 if (pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
2075 *out
= CFStringCreateMutable(pInfo
->allocator
, 0);
2077 *out
= CFStringCreateWithCharacters(pInfo
->allocator
, NULL
, 0);
2082 if (!parseStringTag(pInfo
, (CFStringRef
*)out
)) {
2083 return false; // parseStringTag will already have set the error string
2085 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[markerIx
], tagLen
)) {
2086 __CFPListRelease(*out
, pInfo
->allocator
);
2094 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <data> on line %d"), lineNumber(pInfo
));
2097 return parseDataTag(pInfo
, out
);
2101 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <date> on line %d"), lineNumber(pInfo
));
2104 return parseDateTag(pInfo
, out
);
2108 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
)) {
2109 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered non-empty <true> on line %d"), lineNumber(pInfo
));
2116 *out
= CFRetain(kCFBooleanTrue
);
2121 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
)) {
2122 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered non-empty <false> on line %d"), lineNumber(pInfo
));
2129 *out
= CFRetain(kCFBooleanFalse
);
2134 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo
));
2137 return parseRealTag(pInfo
, out
);
2141 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo
));
2144 return parseIntegerTag(pInfo
, out
);
2147 CFStringRef markerStr
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, (const UInt8
*)marker
, markerLength
, kCFStringEncodingUTF8
, NO
);
2148 pInfo
->curr
= marker
;
2149 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown tag %@ on line %d"), markerStr
? markerStr
: CFSTR("<unknown>"), lineNumber(pInfo
));
2150 if (markerStr
) CFRelease(markerStr
);
2156 static Boolean
parseXMLPropertyList(_CFXMLPlistParseInfo
*pInfo
, CFTypeRef
*out
) {
2157 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
2159 skipWhitespace(pInfo
);
2160 if (pInfo
->curr
+1 >= pInfo
->end
) {
2161 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("No XML content found"));
2164 if (*(pInfo
->curr
) != '<') {
2165 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected character %c at line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
2168 ch
= *(++ pInfo
->curr
);
2172 if (pInfo
->curr
+1 < pInfo
->end
&& *pInfo
->curr
== '-' && *(pInfo
->curr
+1) == '-') {
2175 skipXMLComment(pInfo
);
2179 } else if (ch
== '?') {
2180 // Processing instruction
2182 skipXMLProcessingInstruction(pInfo
);
2185 return parseXMLElement(pInfo
, NULL
, out
);
2186 // 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
2189 // Should never get here
2190 if (!(pInfo
->error
)) {
2191 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
2196 static CFStringEncoding
encodingForXMLData(CFDataRef data
, CFErrorRef
*error
, CFIndex
*skip
) {
2197 const uint8_t *bytes
= (uint8_t *)CFDataGetBytePtr(data
);
2198 UInt32 length
= CFDataGetLength(data
);
2199 const uint8_t *idx
, *end
;
2202 // 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.
2204 if (*bytes
== 0x00 && *(bytes
+1) == 0x00 && *(bytes
+2) == 0xFE && *(bytes
+3) == 0xFF) {
2206 return kCFStringEncodingUTF32BE
;
2207 } else if (*bytes
== 0xFF && *(bytes
+1) == 0xFE && *(bytes
+2) == 0x00 && *(bytes
+3) == 0x00) {
2209 return kCFStringEncodingUTF32LE
;
2214 if (*bytes
== 0xEF && *(bytes
+1) == 0xBB && *(bytes
+2) == 0xBF) {
2216 return kCFStringEncodingUTF8
;
2221 if (*bytes
== 0xFF && *(bytes
+1) == 0xFE) {
2223 return kCFStringEncodingUTF16LE
;
2224 } else if (*bytes
== 0xFE && *(bytes
+1) == 0xFF) {
2226 return kCFStringEncodingUTF16BE
;
2227 } 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
2229 return kCFStringEncodingUnicode
;
2233 // Scan for the <?xml.... ?> opening
2234 if (length
< 5 || strncmp((char const *) bytes
, "<?xml", 5) != 0) return kCFStringEncodingUTF8
;
2236 end
= bytes
+ length
;
2237 // Found "<?xml"; now we scan for "encoding"
2240 const uint8_t *scan
;
2241 if ( ch
== '?' || ch
== '>') return kCFStringEncodingUTF8
;
2244 if (idx
+ 8 >= end
) {
2245 if (error
) *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("End of buffer while looking for encoding name"));
2248 if (ch
== 'e' && *scan
++ == 'n' && *scan
++ == 'c' && *scan
++ == 'o' && *scan
++ == 'd' && *scan
++ == 'i'
2249 && *scan
++ == 'n' && *scan
++ == 'g' && *scan
++ == '=') {
2254 if (idx
>= end
) return kCFStringEncodingUTF8
;
2256 if (quote
!= '\'' && quote
!= '\"') return kCFStringEncodingUTF8
;
2258 CFStringRef encodingName
;
2259 const uint8_t *base
= idx
+1; // Move past the quote character
2262 while (idx
< end
&& *idx
!= quote
) idx
++;
2263 if (idx
>= end
) return kCFStringEncodingUTF8
;
2265 if (len
== 5 && (*base
== 'u' || *base
== 'U') && (base
[1] == 't' || base
[1] == 'T') && (base
[2] == 'f' || base
[2] == 'F') && (base
[3] == '-') && (base
[4] == '8'))
2266 return kCFStringEncodingUTF8
;
2267 encodingName
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, base
, len
, kCFStringEncodingISOLatin1
, false);
2268 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
2269 CFStringEncoding enc
= CFStringConvertIANACharSetNameToEncoding(encodingName
);
2270 if (enc
!= kCFStringEncodingInvalidId
) {
2271 CFRelease(encodingName
);
2277 *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown encoding (%@)"), encodingName
);
2278 CFRelease(encodingName
);
2284 bool __CFTryParseBinaryPlist(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFPropertyListRef
*plist
, CFStringRef
*errorString
);
2286 #define SAVE_PLISTS 0
2289 static Boolean
__savePlistData(CFDataRef data
, CFOptionFlags opt
) {
2292 uint32_t pnlen
= sizeof(pn
);
2293 uint8_t *pnp
= NULL
;
2294 if (0 == _NSGetExecutablePath((char *)pn
, &pnlen
)) {
2295 pnp
= strrchr((char *)pn
, '/');
2302 if (0 == strcmp((char *)pnp
, "parse_plists")) return true;
2303 CFUUIDRef r
= CFUUIDCreate(kCFAllocatorSystemDefault
);
2304 CFStringRef s
= CFUUIDCreateString(kCFAllocatorSystemDefault
, r
);
2305 CFStringRef p
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("/tmp/plists/%s#%@#0x%x"), pnp
, s
, opt
);
2306 _CFStringGetFileSystemRepresentation(p
, fn
, sizeof(fn
));
2310 int fd
= open((const char *)fn
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
2311 if (fd
< 0) return false;
2312 int len
= CFDataGetLength(data
);
2313 int w
= write(fd
, CFDataGetBytePtr(data
), len
);
2316 if (w
!= len
) return false;
2321 // If the data is from a converted string, then originalString is non-NULL. If originalString is NULL, then pass in guessedEncoding.
2322 // keyPaths is a set of CFStrings, ':'-separated paths
2323 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
) {
2326 CFAssert1(xmlData
!= NULL
, __kCFLogAssertion
, "%s(): NULL data not allowed", __PRETTY_FUNCTION__
);
2327 CFAssert2(option
== kCFPropertyListImmutable
|| option
== kCFPropertyListMutableContainers
|| option
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, option
);
2329 CFIndex length
= CFDataGetLength(xmlData
);
2331 if (outError
) *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Conversion of string failed. The string is empty."));
2335 _CFXMLPlistParseInfo pInfoBuf
;
2336 _CFXMLPlistParseInfo
*pInfo
= &pInfoBuf
;
2339 // Ensure that the data is not collected while we are using it
2341 const char *buf
= (const char *)CFDataGetBytePtr(xmlData
);
2343 // We may have to skip over starting stuff like BOM markers.
2347 pInfo
->end
= buf
+length
;
2349 pInfo
->allocator
= allocator
;
2350 pInfo
->error
= NULL
;
2351 _createStringMap(pInfo
);
2352 pInfo
->mutabilityOption
= option
;
2353 pInfo
->allowNewTypes
= allowNewTypes
;
2354 pInfo
->skip
= false;
2355 pInfo
->keyPaths
= createTopLevelKeypaths(allocator
, keyPaths
);
2357 Boolean success
= parseXMLPropertyList(pInfo
, &result
);
2358 if (success
&& result
&& format
) *format
= kCFPropertyListXMLFormat_v1_0
;
2360 _cleanupStringMap(pInfo
);
2361 if (pInfo
->keyPaths
&& !(0)) CFRelease(pInfo
->keyPaths
);
2365 *out
= result
; // caller releases
2369 // Try again, old-style
2370 CFErrorRef oldStyleError
= NULL
;
2371 result
= __CFCreateOldStylePropertyListOrStringsFile(allocator
, xmlData
, originalString
, guessedEncoding
, option
, outError
? &oldStyleError
: NULL
, format
);
2373 // Release old error, return
2374 if (pInfo
->error
) CFRelease(pInfo
->error
);
2379 // Failure, both ways. Set up the error to be given back to caller.
2381 if (pInfo
->error
) CFRelease(pInfo
->error
);
2385 // Caller's responsibility to release outError
2386 if (pInfo
->error
&& oldStyleError
) {
2387 // Add the error from the old-style property list parser to the user info of the original error (pInfo->error), which had better exist
2388 CFDictionaryRef oldUserInfo
= CFErrorCopyUserInfo(pInfo
->error
);
2389 CFMutableDictionaryRef newUserInfo
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, CFDictionaryGetCount(oldUserInfo
) + 1, oldUserInfo
);
2390 CFDictionaryAddValue(newUserInfo
, CFPropertyListOldStyleParserErrorKey
, oldStyleError
);
2392 // Re-create the xml parser error with this new user info dictionary
2393 CFErrorRef newError
= CFErrorCreate(kCFAllocatorSystemDefault
, CFErrorGetDomain(pInfo
->error
), CFErrorGetCode(pInfo
->error
), newUserInfo
);
2395 CFRelease(oldUserInfo
);
2396 CFRelease(newUserInfo
);
2397 CFRelease(oldStyleError
);
2398 CFRelease(pInfo
->error
);
2399 *outError
= newError
;
2401 } else if (pInfo
->error
&& !oldStyleError
) {
2402 // Return original error
2403 *outError
= pInfo
->error
;
2404 } else if (!pInfo
->error
&& oldStyleError
) {
2405 // Return only old-style error
2406 // Probably shouldn't get here
2407 *outError
= oldStyleError
;
2408 } else if (!pInfo
->error
&& !oldStyleError
) {
2409 // Return unknown error
2410 *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown error during parse"));
2415 static CFDataRef
_createUTF8DataFromString(CFAllocatorRef allocator
, CFStringRef str
) {
2416 CFIndex bytesNeeded
= 0;
2417 CFStringGetBytes(str
, CFRangeMake(0, CFStringGetLength(str
)), kCFStringEncodingUTF8
, 0, false, NULL
, 0, &bytesNeeded
);
2419 const char *bytes
= (const char *)CFAllocatorAllocate(allocator
, bytesNeeded
, 0);
2420 CFStringGetBytes(str
, CFRangeMake(0, CFStringGetLength(str
)), kCFStringEncodingUTF8
, 0, false, (uint8_t *)bytes
, bytesNeeded
, NULL
);
2422 CFDataRef utf8Data
= CFDataCreateWithBytesNoCopy(allocator
, (const UInt8
*)bytes
, bytesNeeded
, allocator
);
2426 // 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.
2427 static Boolean
_CFPropertyListCreateWithData(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFErrorRef
*outError
, Boolean allowNewTypes
, CFPropertyListFormat
*format
, CFSetRef topLevelKeys
, CFTypeRef
*out
) {
2429 CFStringEncoding encoding
;
2431 if (!data
|| CFDataGetLength(data
) == 0) {
2433 *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Cannot parse a NULL or zero-length data"));
2439 __savePlistData(data
, option
);
2442 // Ignore the error from CFTryParseBinaryPlist -- if it doesn't work, we're going to try again anyway using the XML parser
2443 if (__CFTryParseBinaryPlist(allocator
, data
, option
, out
, NULL
)) {
2444 if (format
) *format
= kCFPropertyListBinaryFormat_v1_0
;
2448 // Use our own error variable here so we can check it against NULL later
2449 CFErrorRef subError
= NULL
;
2451 encoding
= encodingForXMLData(data
, &subError
, &skip
); // 0 is an error return, NOT MacRoman.
2453 if (encoding
== 0) {
2454 // Couldn't find an encoding
2455 // Note that encodingForXMLData() will give us the right values for a standard plist, too.
2456 if (outError
&& subError
== NULL
) {
2457 // encodingForXMLData didn't set an error, so we create a new one here
2458 *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not determine the encoding of the XML data"));
2459 } else if (outError
&& subError
) {
2460 // give the caller the subError, they will release
2461 *outError
= subError
;
2462 } else if (!outError
&& subError
) {
2463 // Release the error
2464 CFRelease(subError
);
2469 if (encoding
== kCFStringEncodingUTF8
) {
2471 return _CFPropertyListCreateFromUTF8Data(allocator
, data
, skip
, NULL
, encoding
, option
, outError
, allowNewTypes
, format
, topLevelKeys
, out
);
2474 // Convert to UTF8 first
2475 CFStringRef xmlString
= CFStringCreateWithBytes(allocator
, CFDataGetBytePtr(data
) + skip
, CFDataGetLength(data
) - skip
, encoding
, false);
2477 if (outError
) *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not determine the encoding of the XML data (string creation failed)"));
2481 CFDataRef utf8Data
= _createUTF8DataFromString(allocator
, xmlString
);
2483 Boolean result
= _CFPropertyListCreateFromUTF8Data(allocator
, utf8Data
, 0, xmlString
, 0, option
, outError
, allowNewTypes
, format
, topLevelKeys
, out
);
2485 if (xmlString
&& !(0)) CFRelease(xmlString
);
2486 if (utf8Data
&& !(0)) CFRelease(utf8Data
);
2491 // -----------------------------------------------------------------------------------------------------------------------
2494 #pragma mark Exported Parsing Functions
2496 CFTypeRef
_CFPropertyListCreateFromXMLStringError(CFAllocatorRef allocator
, CFStringRef xmlString
, CFOptionFlags option
, CFErrorRef
*error
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2497 // Convert to UTF8 first
2498 CFDataRef utf8Data
= _createUTF8DataFromString(allocator
, xmlString
);
2499 CFTypeRef result
= NULL
;
2500 _CFPropertyListCreateFromUTF8Data(allocator
, utf8Data
, 0, xmlString
, 0, option
, error
, allowNewTypes
, format
, NULL
, &result
);
2501 if (utf8Data
&& !(0)) CFRelease(utf8Data
);
2506 CFTypeRef
_CFPropertyListCreateFromXMLString(CFAllocatorRef allocator
, CFStringRef xmlString
, CFOptionFlags option
, CFStringRef
*errorString
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2508 if (errorString
) *errorString
= NULL
;
2509 CFErrorRef error
= NULL
;
2510 CFTypeRef result
= _CFPropertyListCreateFromXMLStringError(allocator
, xmlString
, option
, &error
, allowNewTypes
, format
);
2512 if (errorString
&& error
) {
2513 // The caller is interested in receiving an error message. Pull the debug string out of the CFError returned above
2514 CFDictionaryRef userInfo
= CFErrorCopyUserInfo(error
);
2515 CFStringRef debugString
= NULL
;
2517 // Handle a special-case for compatibility - if the XML parse failed and the old-style plist parse failed, construct a special string
2518 CFErrorRef underlyingError
= NULL
;
2520 Boolean oldStyleFailed
= CFDictionaryGetValueIfPresent(userInfo
, CFPropertyListOldStyleParserErrorKey
, (const void **)&underlyingError
);
2522 if (oldStyleFailed
) {
2523 CFStringRef xmlParserErrorString
, oldStyleParserErrorString
;
2524 xmlParserErrorString
= (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
2526 CFDictionaryRef oldStyleParserUserInfo
= CFErrorCopyUserInfo(underlyingError
);
2527 oldStyleParserErrorString
= (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
2529 // Combine the two strings into a single one that matches the previous implementation
2530 debugString
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("XML parser error:\n\t%@\nOld-style plist parser error:\n\t%@\n"), xmlParserErrorString
, oldStyleParserErrorString
);
2532 CFRelease(oldStyleParserUserInfo
);
2534 debugString
= (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
2535 if (debugString
) CFRetain(debugString
);
2538 // Give the debugString to the caller, who is responsible for releasing it
2539 CFRelease(userInfo
);
2540 *errorString
= debugString
;
2550 CF_PRIVATE
bool __CFBinaryPlistCreateObjectFiltered(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFAllocatorRef allocator
, CFOptionFlags mutabilityOption
, CFMutableDictionaryRef objects
, CFMutableSetRef set
, CFIndex curDepth
, CFSetRef keyPaths
, CFPropertyListRef
*plist
);
2552 // Returns a subset of the property list, only including the key paths in the CFSet.
2553 bool _CFPropertyListCreateFiltered(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFSetRef keyPaths
, CFPropertyListRef
*value
, CFErrorRef
*error
) {
2557 if (!keyPaths
|| !data
) {
2562 CFBinaryPlistTrailer trailer
;
2564 const uint8_t *databytes
= CFDataGetBytePtr(data
);
2565 uint64_t datalen
= CFDataGetLength(data
);
2566 Boolean success
= false;
2567 CFTypeRef out
= NULL
;
2569 // First check to see if it is a binary property list
2570 if (8 <= datalen
&& __CFBinaryPlistGetTopLevelInfo(databytes
, datalen
, &marker
, &offset
, &trailer
)) {
2571 uint64_t valueOffset
= offset
;
2573 // Split up the key path
2574 CFSetRef splitKeyPaths
= createTopLevelKeypaths(allocator
, keyPaths
);
2576 // Create a dictionary to cache objects in
2577 CFMutableDictionaryRef objects
= CFDictionaryCreateMutable(allocator
, 0, NULL
, &kCFTypeDictionaryValueCallBacks
);
2578 success
= __CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, valueOffset
, &trailer
, allocator
, option
, objects
, NULL
, 0, splitKeyPaths
, &out
);
2580 CFRelease(splitKeyPaths
);
2583 // Try an XML property list
2584 success
= _CFPropertyListCreateWithData(allocator
, data
, option
, error
, true, NULL
, keyPaths
, &out
);
2587 if (success
&& value
) {
2588 *value
= out
; // caller releases
2595 /* Get a single value for a given key in a top-level dictionary in a property list.
2596 @param allocator The allocator to use.
2597 @param data The property list data.
2598 @param option Currently unused, set to 0.
2599 @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).
2600 @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.
2601 @param error If an error occurs, will be set to a valid CFErrorRef. It is the caller's responsibility to release this value.
2602 @return True if the key is found, false otherwise.
2604 bool _CFPropertyListCreateSingleValue(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFStringRef keyPath
, CFPropertyListRef
*value
, CFErrorRef
*error
) {
2608 if (!keyPath
|| CFStringGetLength(keyPath
) == 0) {
2613 CFBinaryPlistTrailer trailer
;
2615 const uint8_t *databytes
= CFDataGetBytePtr(data
);
2616 uint64_t datalen
= CFDataGetLength(data
);
2617 Boolean success
= false;
2619 // First check to see if it is a binary property list
2620 if (8 <= datalen
&& __CFBinaryPlistGetTopLevelInfo(databytes
, datalen
, &marker
, &offset
, &trailer
)) {
2621 // Split up the key path
2622 CFArrayRef keyPathArray
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorSystemDefault
, keyPath
, CFSTR(":"));
2623 uint64_t keyOffset
, valueOffset
= offset
;
2625 // Create a dictionary to cache objects in
2626 CFMutableDictionaryRef objects
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, &kCFTypeDictionaryValueCallBacks
);
2627 CFIndex keyPathCount
= CFArrayGetCount(keyPathArray
);
2628 _CFDictionarySetCapacity(objects
, keyPathCount
+ 1);
2630 for (CFIndex i
= 0; i
< keyPathCount
; i
++) {
2631 CFStringRef oneKey
= (CFStringRef
)CFArrayGetValueAtIndex(keyPathArray
, i
);
2632 SInt32 intValue
= CFStringGetIntValue(oneKey
);
2633 if ((intValue
== 0 && CFStringCompare(CFSTR("0"), oneKey
, 0) != kCFCompareEqualTo
) || intValue
== INT_MAX
|| intValue
== INT_MIN
|| intValue
< 0) {
2634 // Treat as a string key into a dictionary
2635 success
= __CFBinaryPlistGetOffsetForValueFromDictionary3(databytes
, datalen
, valueOffset
, &trailer
, (CFTypeRef
)oneKey
, &keyOffset
, &valueOffset
, false, objects
);
2637 // Treat as integer index into an array
2638 success
= __CFBinaryPlistGetOffsetForValueFromArray2(databytes
, datalen
, valueOffset
, &trailer
, intValue
, &valueOffset
, objects
);
2646 // value could be null if the caller wanted to check for the existence of a key but not bother creating it
2647 if (success
&& value
) {
2648 CFPropertyListRef pl
;
2649 success
= __CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, valueOffset
, &trailer
, allocator
, option
, objects
, NULL
, 0, NULL
, &pl
);
2651 // caller's responsibility to release the created object
2656 CFRelease(keyPathArray
);
2659 // Try an XML property list
2660 // Note: This is currently not any more efficient than grabbing the whole thing. This could be improved in the future.
2661 CFPropertyListRef plist
= CFPropertyListCreateWithData(allocator
, data
, option
, NULL
, error
);
2663 CFPropertyListRef nextObject
= plist
;
2665 CFArrayRef keyPathArray
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorSystemDefault
, keyPath
, CFSTR(":"));
2666 for (CFIndex i
= 0; i
< CFArrayGetCount(keyPathArray
); i
++) {
2667 CFStringRef oneKey
= (CFStringRef
)CFArrayGetValueAtIndex(keyPathArray
, i
);
2668 SInt32 intValue
= CFStringGetIntValue(oneKey
);
2669 if (((intValue
== 0 && CFStringCompare(CFSTR("0"), oneKey
, 0) != kCFCompareEqualTo
) || intValue
== INT_MAX
|| intValue
== INT_MIN
) && CFGetTypeID((CFTypeRef
)nextObject
) == dicttype
) {
2670 // Treat as a string key into a dictionary
2671 nextObject
= (CFPropertyListRef
)CFDictionaryGetValue((CFDictionaryRef
)nextObject
, oneKey
);
2672 } else if (CFGetTypeID((CFTypeRef
)nextObject
) == arraytype
) {
2673 // Treat as integer index into an array
2674 nextObject
= (CFPropertyListRef
)CFArrayGetValueAtIndex((CFArrayRef
)nextObject
, intValue
);
2681 if (success
&& nextObject
&& value
) {
2682 *value
= nextObject
;
2683 // caller's responsibility to release the created object
2685 } else if (!nextObject
) {
2689 CFRelease(keyPathArray
);
2698 CFTypeRef
_CFPropertyListCreateFromXMLData(CFAllocatorRef allocator
, CFDataRef xmlData
, CFOptionFlags option
, CFStringRef
*errorString
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2700 CFTypeRef out
= NULL
;
2701 if (errorString
) *errorString
= NULL
;
2702 CFErrorRef error
= NULL
;
2703 Boolean result
= _CFPropertyListCreateWithData(allocator
, xmlData
, option
, &error
, allowNewTypes
, format
, NULL
, &out
);
2704 if (!result
&& error
&& errorString
) {
2705 *errorString
= __copyErrorDebugDescription(error
);
2707 if (error
) CFRelease(error
);
2711 CFPropertyListRef
CFPropertyListCreateWithData(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags options
, CFPropertyListFormat
*format
, CFErrorRef
*error
) {
2713 CFAssert1(data
!= NULL
, __kCFLogAssertion
, "%s(): NULL data not allowed", __PRETTY_FUNCTION__
);
2714 CFAssert2(options
== kCFPropertyListImmutable
|| options
== kCFPropertyListMutableContainers
|| options
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, options
);
2715 CFPropertyListRef out
= NULL
;
2716 _CFPropertyListCreateWithData(allocator
, data
, options
, error
, true, format
, NULL
, &out
);
2720 CFPropertyListRef
CFPropertyListCreateFromXMLData(CFAllocatorRef allocator
, CFDataRef xmlData
, CFOptionFlags option
, CFStringRef
*errorString
) {
2722 if (errorString
) *errorString
= NULL
;
2723 CFErrorRef error
= NULL
;
2724 CFPropertyListRef result
= CFPropertyListCreateWithData(allocator
, xmlData
, option
, NULL
, &error
);
2725 if (error
&& errorString
) {
2726 *errorString
= __copyErrorDebugDescription(error
);
2728 if (error
) CFRelease(error
);
2732 CFDataRef
CFPropertyListCreateData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, CFPropertyListFormat format
, CFOptionFlags options
, CFErrorRef
*error
) {
2734 CFAssert1(format
!= kCFPropertyListOpenStepFormat
, __kCFLogAssertion
, "%s(): kCFPropertyListOpenStepFormat not supported for writing", __PRETTY_FUNCTION__
);
2735 CFAssert2(format
== kCFPropertyListXMLFormat_v1_0
|| format
== kCFPropertyListBinaryFormat_v1_0
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, format
);
2736 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
2737 __CFAssertIsPList(propertyList
);
2739 CFDataRef data
= NULL
;
2742 CFStringRef validErr
= NULL
;
2743 if (!_CFPropertyListIsValidWithErrorString(propertyList
, format
, &validErr
)) {
2744 CFLog(kCFLogLevelError
, CFSTR("Property list invalid for format: %d (%@)"), format
, validErr
);
2745 if (validErr
) CFRelease(validErr
);
2749 if (format
== kCFPropertyListOpenStepFormat
) {
2750 CFLog(kCFLogLevelError
, CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2752 } else if (format
== kCFPropertyListXMLFormat_v1_0
) {
2753 data
= _CFPropertyListCreateXMLData(allocator
, propertyList
, true);
2754 } else if (format
== kCFPropertyListBinaryFormat_v1_0
) {
2755 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2756 // TODO: Is it more efficient to create a stream here or just use a mutable data?
2757 CFWriteStreamRef stream
= CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorSystemDefault
, allocator
);
2758 CFWriteStreamOpen(stream
);
2759 CFIndex len
= CFPropertyListWrite(propertyList
, stream
, format
, options
, error
);
2761 data
= (CFDataRef
)CFWriteStreamCopyProperty(stream
, kCFStreamPropertyDataWritten
);
2763 CFWriteStreamClose(stream
);
2766 CFMutableDataRef dataForPlist
= CFDataCreateMutable(allocator
, 0);
2767 __CFBinaryPlistWrite(propertyList
, dataForPlist
, 0, options
, error
);
2768 return dataForPlist
;
2771 CFLog(kCFLogLevelError
, CFSTR("Unknown format option"));
2777 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2779 CFIndex
CFPropertyListWrite(CFPropertyListRef propertyList
, CFWriteStreamRef stream
, CFPropertyListFormat format
, CFOptionFlags options
, CFErrorRef
*error
) {
2781 CFAssert1(stream
!= NULL
, __kCFLogAssertion
, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__
);
2782 CFAssert1(format
!= kCFPropertyListOpenStepFormat
, __kCFLogAssertion
, "%s(): kCFPropertyListOpenStepFormat not supported for writing", __PRETTY_FUNCTION__
);
2783 CFAssert2(format
== kCFPropertyListXMLFormat_v1_0
|| format
== kCFPropertyListBinaryFormat_v1_0
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, format
);
2784 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
2785 __CFAssertIsPList(propertyList
);
2786 CFAssert1(CFWriteStreamGetTypeID() == CFGetTypeID(stream
), __kCFLogAssertion
, "%s(): stream argument is not a write stream", __PRETTY_FUNCTION__
);
2787 CFAssert1(kCFStreamStatusOpen
== CFWriteStreamGetStatus(stream
) || kCFStreamStatusWriting
== CFWriteStreamGetStatus(stream
), __kCFLogAssertion
, "%s(): stream is not open", __PRETTY_FUNCTION__
);
2789 CFStringRef validErr
= NULL
;
2790 if (!_CFPropertyListIsValidWithErrorString(propertyList
, format
, &validErr
)) {
2791 CFLog(kCFLogLevelError
, CFSTR("Property list invalid for format: %d (%@)"), format
, validErr
);
2792 if (validErr
) CFRelease(validErr
);
2795 if (format
== kCFPropertyListOpenStepFormat
) {
2796 CFLog(kCFLogLevelError
, CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2799 if (format
== kCFPropertyListXMLFormat_v1_0
) {
2800 CFDataRef data
= _CFPropertyListCreateXMLData(kCFAllocatorSystemDefault
, propertyList
, true);
2802 CFLog(kCFLogLevelError
, CFSTR("Property list format kCFPropertyListXMLFormat_v1_0 specified but was not a valid property list type"));
2805 CFIndex len
= CFDataGetLength(data
);
2806 const uint8_t *ptr
= CFDataGetBytePtr(data
);
2808 CFIndex ret
= CFWriteStreamWrite(stream
, ptr
, len
);
2810 if (error
) *error
= __CFPropertyListCreateError(kCFPropertyListWriteStreamError
, CFSTR("Property list writing could not be completed because stream is full."));
2815 CFErrorRef underlyingError
= CFWriteStreamCopyError(stream
);
2816 if (underlyingError
) {
2818 // Wrap the error from CFWriteStreamCopy in a new error
2819 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2820 CFDictionarySetValue(userInfo
, kCFErrorDebugDescriptionKey
, CFSTR("Property list writing could not be completed because the stream had an unknown error."));
2821 CFDictionarySetValue(userInfo
, kCFErrorUnderlyingErrorKey
, underlyingError
);
2822 *error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, kCFPropertyListWriteStreamError
, userInfo
);
2823 CFRelease(userInfo
);
2825 CFRelease(underlyingError
);
2833 len
= CFDataGetLength(data
);
2837 if (format
== kCFPropertyListBinaryFormat_v1_0
) {
2838 CFIndex len
= __CFBinaryPlistWrite(propertyList
, stream
, 0, options
, error
);
2841 CFLog(kCFLogLevelError
, CFSTR("Unknown format option"));
2845 CFIndex
CFPropertyListWriteToStream(CFPropertyListRef propertyList
, CFWriteStreamRef stream
, CFPropertyListFormat format
, CFStringRef
*errorString
) {
2847 if (errorString
) *errorString
= NULL
;
2848 CFErrorRef error
= NULL
;
2850 // For backwards compatibility, we check the format parameter up front since these do not have CFError counterparts in the newer API
2851 CFStringRef validErr
= NULL
;
2852 if (!_CFPropertyListIsValidWithErrorString(propertyList
, format
, &validErr
)) {
2853 if (errorString
) *errorString
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("Property list invalid for format (%@)"), validErr
);
2854 if (validErr
) CFRelease(validErr
);
2857 if (format
== kCFPropertyListOpenStepFormat
) {
2858 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2861 if (format
!= kCFPropertyListBinaryFormat_v1_0
&& format
!= kCFPropertyListXMLFormat_v1_0
) {
2862 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Unknown format option"));
2866 CFIndex result
= CFPropertyListWrite(propertyList
, stream
, format
, 0, &error
);
2867 if (error
&& errorString
) {
2868 *errorString
= __copyErrorDebugDescription(error
);
2870 if (error
) CFRelease(error
);
2874 static bool __convertReadStreamToBytes(CFReadStreamRef stream
, CFIndex max
, uint8_t **buffer
, CFIndex
*length
, CFErrorRef
*error
) {
2875 int32_t buflen
= 0, bufsize
= 0, retlen
;
2876 uint8_t *buf
= NULL
, sbuf
[8192];
2878 retlen
= CFReadStreamRead(stream
, sbuf
, __CFMin(8192, max
));
2883 if (retlen
< 0 && error
) {
2884 // Copy the error out
2885 *error
= CFReadStreamCopyError(stream
);
2891 if (bufsize
< buflen
+ retlen
) {
2892 if (bufsize
< 256 * 1024) {
2894 } else if (bufsize
< 16 * 1024 * 1024) {
2897 // once in this stage, this will be really slow
2898 // and really potentially fragment memory
2899 bufsize
+= 256 * 1024;
2901 if (bufsize
< buflen
+ retlen
) bufsize
= buflen
+ retlen
;
2902 buf
= (uint8_t *)CFAllocatorReallocate(kCFAllocatorSystemDefault
, buf
, bufsize
, 0);
2905 memmove(buf
+ buflen
, sbuf
, retlen
);
2917 CFPropertyListRef
CFPropertyListCreateWithStream(CFAllocatorRef allocator
, CFReadStreamRef stream
, CFIndex streamLength
, CFOptionFlags mutabilityOption
, CFPropertyListFormat
*format
, CFErrorRef
*error
) {
2920 CFAssert1(stream
!= NULL
, __kCFLogAssertion
, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__
);
2921 CFAssert1(CFReadStreamGetTypeID() == CFGetTypeID(stream
), __kCFLogAssertion
, "%s(): stream argument is not a read stream", __PRETTY_FUNCTION__
);
2922 CFAssert1(kCFStreamStatusOpen
== CFReadStreamGetStatus(stream
) || kCFStreamStatusReading
== CFReadStreamGetStatus(stream
), __kCFLogAssertion
, "%s(): stream is not open", __PRETTY_FUNCTION__
);
2923 CFAssert2(mutabilityOption
== kCFPropertyListImmutable
|| mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, mutabilityOption
);
2925 if (0 == streamLength
) streamLength
= LONG_MAX
;
2926 CFErrorRef underlyingError
= NULL
;
2928 uint8_t *buffer
= NULL
;
2929 if (!__convertReadStreamToBytes(stream
, streamLength
, &buffer
, &buflen
, &underlyingError
)) {
2931 // Wrap the error from CFReadStream in a new error in the cocoa domain
2932 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2933 CFDictionarySetValue(userInfo
, kCFErrorDebugDescriptionKey
, CFSTR("Property list reading could not be completed because the stream had an unknown error. Did you forget to open the stream?"));
2934 if (underlyingError
) {
2935 CFDictionarySetValue(userInfo
, kCFErrorUnderlyingErrorKey
, underlyingError
);
2937 *error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, kCFPropertyListReadStreamError
, userInfo
);
2938 CFRelease(userInfo
);
2940 if (underlyingError
) {
2941 CFRelease(underlyingError
);
2946 if (!buffer
|| buflen
< 6) {
2947 if (buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buffer
);
2948 if (error
) *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("stream had too few bytes"));
2952 CFDataRef data
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, buffer
, buflen
, kCFAllocatorSystemDefault
);
2953 CFPropertyListRef pl
= NULL
; // initialize to null, because if the following call fails we must return NULL
2954 _CFPropertyListCreateWithData(allocator
, data
, mutabilityOption
, error
, true, format
, NULL
, &pl
);
2960 CFPropertyListRef
CFPropertyListCreateFromStream(CFAllocatorRef allocator
, CFReadStreamRef stream
, CFIndex length
, CFOptionFlags mutabilityOption
, CFPropertyListFormat
*format
, CFStringRef
*errorString
) {
2962 if (errorString
) *errorString
= NULL
;
2963 CFErrorRef error
= NULL
;
2964 CFPropertyListRef result
= CFPropertyListCreateWithStream(allocator
, stream
, length
, mutabilityOption
, format
, &error
);
2965 if (error
&& errorString
) {
2966 *errorString
= __copyErrorDebugDescription(error
);
2968 if (error
) CFRelease(error
);
2972 #endif //DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2975 #pragma mark Property List Copies
2977 static CFArrayRef
_arrayDeepImmutableCopy(CFAllocatorRef allocator
, CFArrayRef array
, CFOptionFlags mutabilityOption
) {
2978 CFArrayRef result
= NULL
;
2979 CFIndex i
, c
= CFArrayGetCount(array
);
2981 result
= CFArrayCreate(allocator
, NULL
, 0, &kCFTypeArrayCallBacks
);
2983 new_cftype_array(values
, c
);
2984 CFArrayGetValues(array
, CFRangeMake(0, c
), values
);
2985 for (i
= 0; i
< c
; i
++) {
2986 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, values
[i
], mutabilityOption
);
2987 if (newValue
== NULL
) {
2990 __CFAssignWithWriteBarrier((void **)values
+ i
, (void *)newValue
);
2992 result
= (i
== c
) ? CFArrayCreate(allocator
, values
, c
, &kCFTypeArrayCallBacks
) : NULL
;
2994 for (i
= 0; i
< c
; i
++) CFRelease(values
[i
]);
2995 free_cftype_array(values
);
3000 static CFMutableArrayRef
_arrayDeepMutableCopy(CFAllocatorRef allocator
, CFArrayRef array
, CFOptionFlags mutabilityOption
) {
3001 CFIndex i
, c
= CFArrayGetCount(array
);
3002 CFMutableArrayRef result
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
3004 for (i
= 0; i
< c
; i
++) {
3005 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, CFArrayGetValueAtIndex(array
, i
), mutabilityOption
);
3006 if (!newValue
) break;
3007 CFArrayAppendValue(result
, newValue
);
3008 CFRelease(newValue
);
3018 CFPropertyListRef
CFPropertyListCreateDeepCopy(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, CFOptionFlags mutabilityOption
) {
3020 CFPropertyListRef result
= NULL
;
3021 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): cannot copy a NULL property list", __PRETTY_FUNCTION__
);
3022 __CFAssertIsPList(propertyList
);
3023 CFAssert2(mutabilityOption
== kCFPropertyListImmutable
|| mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, mutabilityOption
);
3024 if (!CFPropertyListIsValid(propertyList
, kCFPropertyListBinaryFormat_v1_0
)) return NULL
;
3026 CFTypeID typeID
= CFGetTypeID(propertyList
);
3027 if (typeID
== dicttype
) {
3028 CFDictionaryRef dict
= (CFDictionaryRef
)propertyList
;
3029 Boolean isMutable
= (mutabilityOption
!= kCFPropertyListImmutable
);
3030 CFIndex count
= CFDictionaryGetCount(dict
);
3032 result
= isMutable
? CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
): CFDictionaryCreate(allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3034 new_cftype_array(keys
, 2 * count
);
3037 values
= keys
+count
;
3038 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
3039 for (i
= 0; i
< count
; i
++) {
3040 CFTypeRef newKey
= CFStringCreateCopy(allocator
, (CFStringRef
)keys
[i
]);
3041 if (newKey
== NULL
) {
3044 __CFAssignWithWriteBarrier((void **)keys
+ i
, (void *)newKey
);
3045 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, values
[i
], mutabilityOption
);
3046 if (newValue
== NULL
) {
3050 __CFAssignWithWriteBarrier((void **)values
+ i
, (void *)newValue
);
3053 result
= isMutable
? CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
) : CFDictionaryCreate(allocator
, keys
, values
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3054 for (i
= 0; i
< count
; i
++) {
3056 CFDictionarySetValue((CFMutableDictionaryRef
)result
, keys
[i
], values
[i
]);
3059 CFRelease(values
[i
]);
3064 for (i
= 0; i
< count
; i
++) {
3066 CFRelease(values
[i
]);
3069 free_cftype_array(keys
);
3071 } else if (typeID
== arraytype
) {
3072 if (mutabilityOption
== kCFPropertyListImmutable
) {
3073 result
= _arrayDeepImmutableCopy(allocator
, (CFArrayRef
)propertyList
, mutabilityOption
);
3075 result
= _arrayDeepMutableCopy(allocator
, (CFArrayRef
)propertyList
, mutabilityOption
);
3077 } else if (typeID
== datatype
) {
3078 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
3079 result
= CFDataCreateMutableCopy(allocator
, 0, (CFDataRef
)propertyList
);
3081 result
= CFDataCreateCopy(allocator
, (CFDataRef
)propertyList
);
3083 } else if (typeID
== numbertype
) {
3084 // Warning - this will break if byteSize is ever greater than 128
3086 CFNumberType numType
= _CFNumberGetType2((CFNumberRef
)propertyList
);
3087 CFNumberGetValue((CFNumberRef
)propertyList
, numType
, (void *)bytes
);
3088 result
= CFNumberCreate(allocator
, numType
, (void *)bytes
);
3089 } else if (typeID
== booltype
) {
3090 // Booleans are immutable & shared instances
3091 CFRetain(propertyList
);
3092 result
= propertyList
;
3093 } else if (typeID
== datetype
) {
3094 // Dates are immutable
3095 result
= CFDateCreate(allocator
, CFDateGetAbsoluteTime((CFDateRef
)propertyList
));
3096 } else if (typeID
== stringtype
) {
3097 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
3098 result
= CFStringCreateMutableCopy(allocator
, 0, (CFStringRef
)propertyList
);
3100 result
= CFStringCreateCopy(allocator
, (CFStringRef
)propertyList
);
3103 CFAssert2(false, __kCFLogAssertion
, "%s(): %p is not a property list type", __PRETTY_FUNCTION__
, propertyList
);