2 * Copyright (c) 2011 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-2011, 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 <CoreFoundation/CFInternal.h>
38 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
39 #include <CoreFoundation/CFStream.h>
41 #include <CoreFoundation/CFCalendar.h>
42 #include "CFLocaleInternal.h"
64 #define PLIST_TAG_LENGTH 5
65 #define ARRAY_TAG_LENGTH 5
66 #define DICT_TAG_LENGTH 4
67 #define KEY_TAG_LENGTH 3
68 #define STRING_TAG_LENGTH 6
69 #define DATA_TAG_LENGTH 4
70 #define DATE_TAG_LENGTH 4
71 #define REAL_TAG_LENGTH 4
72 #define INTEGER_TAG_LENGTH 7
73 #define TRUE_TAG_LENGTH 4
74 #define FALSE_TAG_LENGTH 5
75 #define DOCTYPE_TAG_LENGTH 7
76 #define CDSECT_TAG_LENGTH 9
78 #if DEPLOYMENT_TARGET_MACOSX
79 // Set for some exceptional circumstances, like running out of memory
80 extern char * __crashreporter_info__
;
82 #define out_of_memory_warning() \
84 __crashreporter_info__ = "CFPropertyList ran out of memory while attempting to allocate temporary storage."; \
87 #define out_of_memory_warning() do {} while (0)
90 #if !defined(new_cftype_array)
91 #define new_cftype_array(N, C) \
92 size_t N ## _count__ = (C); \
93 if (N ## _count__ > LONG_MAX / sizeof(CFTypeRef)) { \
94 out_of_memory_warning(); \
97 Boolean N ## _is_stack__ = (N ## _count__ <= 256); \
98 if (N ## _count__ == 0) N ## _count__ = 1; \
99 STACK_BUFFER_DECL(CFTypeRef, N ## _buffer__, N ## _is_stack__ ? N ## _count__ : 1); \
100 if (N ## _is_stack__) memset(N ## _buffer__, 0, N ## _count__ * sizeof(CFTypeRef)); \
101 CFTypeRef * N = N ## _is_stack__ ? N ## _buffer__ : (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, (N ## _count__) * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory); \
103 out_of_memory_warning(); \
109 #if !defined(free_cftype_array)
110 #define free_cftype_array(N) \
111 if (! N ## _is_stack__) { \
112 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFAllocatorDeallocate(kCFAllocatorSystemDefault, N); \
117 // Used to reference an old-style plist parser error inside of a more general XML error
118 #define CFPropertyListOldStyleParserErrorKey CFSTR("kCFPropertyListOldStyleParsingError")
120 // don't allow _CFKeyedArchiverUID here
121 #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);
123 static bool __CFPropertyListIsValidAux(CFPropertyListRef plist
, bool recursive
, CFMutableSetRef set
, CFPropertyListFormat format
, CFStringRef
*error
);
125 static CFTypeID stringtype
= -1, datatype
= -1, numbertype
= -1, datetype
= -1;
126 static CFTypeID booltype
= -1, nulltype
= -1, dicttype
= -1, arraytype
= -1, settype
= -1;
128 static void initStatics() {
129 if ((CFTypeID
)-1 == stringtype
) {
130 stringtype
= CFStringGetTypeID();
132 if ((CFTypeID
)-1 == datatype
) {
133 datatype
= CFDataGetTypeID();
135 if ((CFTypeID
)-1 == numbertype
) {
136 numbertype
= CFNumberGetTypeID();
138 if ((CFTypeID
)-1 == booltype
) {
139 booltype
= CFBooleanGetTypeID();
141 if ((CFTypeID
)-1 == datetype
) {
142 datetype
= CFDateGetTypeID();
144 if ((CFTypeID
)-1 == dicttype
) {
145 dicttype
= CFDictionaryGetTypeID();
147 if ((CFTypeID
)-1 == arraytype
) {
148 arraytype
= CFArrayGetTypeID();
150 if ((CFTypeID
)-1 == settype
) {
151 settype
= CFSetGetTypeID();
153 if ((CFTypeID
)-1 == nulltype
) {
154 nulltype
= CFNullGetTypeID();
158 __private_extern__ CFErrorRef
__CFPropertyListCreateError(CFIndex code
, CFStringRef debugString
, ...) {
160 CFErrorRef error
= NULL
;
162 if (debugString
!= NULL
) {
163 CFStringRef debugMessage
= NULL
;
164 va_start(argList
, debugString
);
165 debugMessage
= CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault
, NULL
, debugString
, argList
);
168 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
169 CFDictionarySetValue(userInfo
, kCFErrorDebugDescriptionKey
, debugMessage
);
171 error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, code
, userInfo
);
173 CFRelease(debugMessage
);
176 error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, code
, NULL
);
182 CFStringRef
__CFPropertyListCopyErrorDebugDescription(CFErrorRef error
) {
183 CFStringRef result
= NULL
;
185 CFDictionaryRef userInfo
= CFErrorCopyUserInfo(error
);
186 result
= CFStringCreateCopy(kCFAllocatorSystemDefault
, (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
));
196 CFPropertyListFormat format
;
200 static void __CFPropertyListIsArrayPlistAux(const void *value
, void *context
) {
201 struct context
*ctx
= (struct context
*)context
;
202 if (!ctx
->answer
) return;
203 if (!value
&& !*(ctx
->error
)) {
204 *(ctx
->error
) = (CFStringRef
)CFRetain(CFSTR("property list arrays cannot contain NULL"));
206 ctx
->answer
= value
&& __CFPropertyListIsValidAux(value
, true, ctx
->set
, ctx
->format
, ctx
->error
);
209 static void __CFPropertyListIsDictPlistAux(const void *key
, const void *value
, void *context
) {
210 struct context
*ctx
= (struct context
*)context
;
211 if (!ctx
->answer
) return;
212 if (!key
&& !*(ctx
->error
)) *(ctx
->error
) = (CFStringRef
)CFRetain(CFSTR("property list dictionaries cannot contain NULL keys"));
213 if (!value
&& !*(ctx
->error
)) *(ctx
->error
) = (CFStringRef
)CFRetain(CFSTR("property list dictionaries cannot contain NULL values"));
214 if (stringtype
!= CFGetTypeID(key
) && !*(ctx
->error
)) {
215 CFStringRef desc
= CFCopyTypeIDDescription(CFGetTypeID(key
));
216 *(ctx
->error
) = CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("property list dictionaries may only have keys which are CFStrings, not '%@'"), desc
);
219 ctx
->answer
= key
&& value
&& (stringtype
== CFGetTypeID(key
)) && __CFPropertyListIsValidAux(value
, true, ctx
->set
, ctx
->format
, ctx
->error
);
222 static bool __CFPropertyListIsValidAux(CFPropertyListRef plist
, bool recursive
, CFMutableSetRef set
, CFPropertyListFormat format
, CFStringRef
*error
) {
225 *error
= (CFStringRef
)CFRetain(CFSTR("property lists cannot contain NULL"));
228 type
= CFGetTypeID(plist
);
229 if (stringtype
== type
) return true;
230 if (datatype
== type
) return true;
231 if (kCFPropertyListOpenStepFormat
!= format
) {
232 if (booltype
== type
) return true;
233 if (numbertype
== type
) return true;
234 if (datetype
== type
) return true;
235 if (_CFKeyedArchiverUIDGetTypeID() == type
) return true;
237 if (!recursive
&& arraytype
== type
) return true;
238 if (!recursive
&& dicttype
== type
) return true;
239 // at any one invocation of this function, set should contain the objects in the "path" down to this object
240 if (CFSetContainsValue(set
, plist
)) {
241 *error
= (CFStringRef
)CFRetain(CFSTR("property lists cannot contain recursive container references"));
244 if (arraytype
== type
) {
245 struct context ctx
= {true, set
, format
, error
};
246 CFSetAddValue(set
, plist
);
247 CFArrayApplyFunction((CFArrayRef
)plist
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
)plist
)), __CFPropertyListIsArrayPlistAux
, &ctx
);
248 CFSetRemoveValue(set
, plist
);
251 if (dicttype
== type
) {
252 struct context ctx
= {true, set
, format
, error
};
253 CFSetAddValue(set
, plist
);
254 CFDictionaryApplyFunction((CFDictionaryRef
)plist
, __CFPropertyListIsDictPlistAux
, &ctx
);
255 CFSetRemoveValue(set
, plist
);
259 CFStringRef desc
= CFCopyTypeIDDescription(type
);
260 *error
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("property lists cannot contain objects of type '%@'"), desc
);
266 Boolean
CFPropertyListIsValid(CFPropertyListRef plist
, CFPropertyListFormat format
) {
268 CFAssert1(plist
!= NULL
, __kCFLogAssertion
, "%s(): NULL is not a property list", __PRETTY_FUNCTION__
);
269 CFMutableSetRef set
= CFSetCreateMutable(kCFAllocatorSystemDefaultGCRefZero
, 0, NULL
);
270 CFStringRef error
= NULL
;
271 bool result
= __CFPropertyListIsValidAux(plist
, true, set
, format
, &error
);
274 CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListIsValid(): %@"), error
);
278 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero
)) CFRelease(set
);
282 static Boolean
_CFPropertyListIsValidWithErrorString(CFPropertyListRef plist
, CFPropertyListFormat format
, CFStringRef
*error
) {
284 CFAssert1(plist
!= NULL
, __kCFLogAssertion
, "%s(): NULL is not a property list", __PRETTY_FUNCTION__
);
285 CFMutableSetRef set
= CFSetCreateMutable(kCFAllocatorSystemDefaultGCRefZero
, 0, NULL
);
286 bool result
= __CFPropertyListIsValidAux(plist
, true, set
, format
, error
);
287 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero
)) CFRelease(set
);
291 static const UniChar CFXMLPlistTags
[13][10]= {
292 {'p', 'l', 'i', 's', 't', '\0', '\0', '\0', '\0', '\0'},
293 {'a', 'r', 'r', 'a', 'y', '\0', '\0', '\0', '\0', '\0'},
294 {'d', 'i', 'c', 't', '\0', '\0', '\0', '\0', '\0', '\0'},
295 {'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
296 {'s', 't', 'r', 'i', 'n', 'g', '\0', '\0', '\0', '\0'},
297 {'d', 'a', 't', 'a', '\0', '\0', '\0', '\0', '\0', '\0'},
298 {'d', 'a', 't', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
299 {'r', 'e', 'a', 'l', '\0', '\0', '\0', '\0', '\0', '\0'},
300 {'i', 'n', 't', 'e', 'g', 'e', 'r', '\0', '\0', '\0'},
301 {'t', 'r', 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
302 {'f', 'a', 'l', 's', 'e', '\0', '\0', '\0', '\0', '\0'},
303 {'D', 'O', 'C', 'T', 'Y', 'P', 'E', '\0', '\0', '\0'},
304 {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[', '\0'}
308 const UniChar
*begin
; // first character of the XML to be parsed
309 const UniChar
*curr
; // current parse location
310 const UniChar
*end
; // the first character _after_ the end of the XML
312 CFAllocatorRef allocator
;
313 UInt32 mutabilityOption
;
314 CFMutableSetRef stringSet
; // set of all strings involved in this parse; allows us to share non-mutable strings in the returned plist
315 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)
317 } _CFXMLPlistParseInfo
;
319 static CFTypeRef
parseOldStylePropertyListOrStringsFile(_CFXMLPlistParseInfo
*pInfo
);
321 CF_INLINE
void __CFPListRelease(CFTypeRef cf
, _CFXMLPlistParseInfo
*pInfo
) {
322 if (cf
&& !_CFAllocatorIsGCRefZero(pInfo
->allocator
)) CFRelease(cf
);
326 // 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.
328 // Null-terminated, ASCII or UTF8 string
330 static void _plistAppendUTF8CString(CFMutableDataRef mData
, const char *cString
) {
331 CFDataAppendBytes (mData
, (const UInt8
*)cString
, strlen(cString
));
336 static void _plistAppendCharacters(CFMutableDataRef mData
, const UniChar
*chars
, CFIndex length
) {
339 do { // Flush out ASCII chars, BUFLEN at a time
341 UInt8 buf
[BUFLEN
], *bufPtr
= buf
;
343 while (cnt
< length
&& (cnt
- curLoc
< BUFLEN
) && (chars
[cnt
] < 128)) *bufPtr
++ = (UInt8
)(chars
[cnt
++]);
344 if (cnt
> curLoc
) { // Flush any ASCII bytes
345 CFDataAppendBytes(mData
, buf
, cnt
- curLoc
);
348 } while (curLoc
< length
&& (chars
[curLoc
] < 128)); // We will exit out of here when we run out of chars or hit a non-ASCII char
350 if (curLoc
< length
) { // Now deal with non-ASCII chars
351 CFDataRef data
= NULL
;
352 CFStringRef str
= NULL
;
353 if ((str
= CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault
, chars
+ curLoc
, length
- curLoc
, kCFAllocatorNull
))) {
354 if ((data
= CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
, 0))) {
355 CFDataAppendBytes (mData
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
360 CFAssert1(str
&& data
, __kCFLogAssertion
, "%s(): Error writing plist", __PRETTY_FUNCTION__
);
366 static void _plistAppendString(CFMutableDataRef mData
, CFStringRef str
) {
367 const UniChar
*chars
;
370 if ((chars
= CFStringGetCharactersPtr(str
))) {
371 _plistAppendCharacters(mData
, chars
, CFStringGetLength(str
));
372 } else if ((cStr
= CFStringGetCStringPtr(str
, kCFStringEncodingASCII
)) || (cStr
= CFStringGetCStringPtr(str
, kCFStringEncodingUTF8
))) {
373 _plistAppendUTF8CString(mData
, cStr
);
374 } else if ((data
= CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
, 0))) {
375 CFDataAppendBytes (mData
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
378 CFAssert1(TRUE
, __kCFLogAssertion
, "%s(): Error in plist writing", __PRETTY_FUNCTION__
);
383 // Append CFString-style format + arguments
385 static void _plistAppendFormat(CFMutableDataRef mData
, CFStringRef format
, ...) {
389 va_start(argList
, format
);
390 fStr
= CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault
, NULL
, format
, argList
);
393 CFAssert1(fStr
, __kCFLogAssertion
, "%s(): Error writing plist", __PRETTY_FUNCTION__
);
394 _plistAppendString(mData
, fStr
);
400 static void _appendIndents(CFIndex numIndents
, CFMutableDataRef str
) {
402 static const UniChar tabs
[NUMTABS
] = {'\t','\t','\t','\t'};
403 for (; numIndents
> 0; numIndents
-= NUMTABS
) _plistAppendCharacters(str
, tabs
, (numIndents
>= NUMTABS
) ? NUMTABS
: numIndents
);
406 /* Append the escaped version of origStr to mStr.
408 static void _appendEscapedString(CFStringRef origStr
, CFMutableDataRef mStr
) {
410 CFIndex i
, length
= CFStringGetLength(origStr
);
412 UniChar buf
[BUFSIZE
];
413 CFStringInlineBuffer inlineBuffer
;
415 CFStringInitInlineBuffer(origStr
, &inlineBuffer
, CFRangeMake(0, length
));
417 for (i
= 0; i
< length
; i
++) {
418 UniChar ch
= __CFStringGetCharacterFromInlineBufferQuick(&inlineBuffer
, i
);
419 if (CFStringIsSurrogateHighCharacter(ch
) && (bufCnt
+ 2 >= BUFSIZE
)) {
420 // flush the buffer first so we have room for a low/high pair and do not split them
421 _plistAppendCharacters(mStr
, buf
, bufCnt
);
427 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
429 _plistAppendUTF8CString(mStr
, "<");
432 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
434 _plistAppendUTF8CString(mStr
, ">");
437 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
439 _plistAppendUTF8CString(mStr
, "&");
443 if (bufCnt
== BUFSIZE
) {
444 _plistAppendCharacters(mStr
, buf
, bufCnt
);
450 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
455 /* Base-64 encoding/decoding */
457 /* The base-64 encoding packs three 8-bit bytes into four 7-bit ASCII
458 * characters. If the number of bytes in the original data isn't divisable
459 * by three, "=" characters are used to pad the encoded data. The complete
460 * set of characters used in base-64 are:
470 // Write the inputData to the mData using Base 64 encoding
472 static void _XMLPlistAppendDataUsingBase64(CFMutableDataRef mData
, CFDataRef inputData
, CFIndex indent
) {
473 static const char __CFPLDataEncodeTable
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
474 #define MAXLINELEN 76
475 char buf
[MAXLINELEN
+ 4 + 2]; // For the slop and carriage return and terminating NULL
477 const uint8_t *bytes
= CFDataGetBytePtr(inputData
);
478 CFIndex length
= CFDataGetLength(inputData
);
482 if (indent
> 8) indent
= 8; // refuse to indent more than 64 characters
484 pos
= 0; // position within buf
486 for (i
= 0, p
= bytes
; i
< length
; i
++, p
++) {
487 /* 3 bytes are encoded as 4 */
490 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[0] >> 2) & 0x3f)];
493 buf
[pos
++] = __CFPLDataEncodeTable
[ ((((p
[-1] << 8) | p
[0]) >> 4) & 0x3f)];
496 buf
[pos
++] = __CFPLDataEncodeTable
[ ((((p
[-1] << 8) | p
[0]) >> 6) & 0x3f)];
497 buf
[pos
++] = __CFPLDataEncodeTable
[ (p
[0] & 0x3f)];
500 /* Flush the line out every 76 (or fewer) chars --- indents count against the line length*/
501 if (pos
>= MAXLINELEN
- 8 * indent
) {
504 _appendIndents(indent
, mData
);
505 _plistAppendUTF8CString(mData
, buf
);
514 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[-1] << 4) & 0x30)];
519 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[-1] << 2) & 0x3c)];
527 _appendIndents(indent
, mData
);
528 _plistAppendUTF8CString(mData
, buf
);
532 extern CFStringRef
__CFNumberCopyFormattingDescriptionAsFloat64(CFTypeRef cf
);
534 static void _CFAppendXML0(CFTypeRef object
, UInt32 indentation
, CFMutableDataRef xmlString
) {
535 UInt32 typeID
= CFGetTypeID(object
);
536 _appendIndents(indentation
, xmlString
);
537 if (typeID
== stringtype
) {
538 _plistAppendUTF8CString(xmlString
, "<");
539 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[STRING_IX
], STRING_TAG_LENGTH
);
540 _plistAppendUTF8CString(xmlString
, ">");
541 _appendEscapedString((CFStringRef
)object
, xmlString
);
542 _plistAppendUTF8CString(xmlString
, "</");
543 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[STRING_IX
], STRING_TAG_LENGTH
);
544 _plistAppendUTF8CString(xmlString
, ">\n");
545 } else if (typeID
== arraytype
) {
546 UInt32 i
, count
= CFArrayGetCount((CFArrayRef
)object
);
548 _plistAppendUTF8CString(xmlString
, "<");
549 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
550 _plistAppendUTF8CString(xmlString
, "/>\n");
553 _plistAppendUTF8CString(xmlString
, "<");
554 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
555 _plistAppendUTF8CString(xmlString
, ">\n");
556 for (i
= 0; i
< count
; i
++) {
557 _CFAppendXML0(CFArrayGetValueAtIndex((CFArrayRef
)object
, i
), indentation
+1, xmlString
);
559 _appendIndents(indentation
, xmlString
);
560 _plistAppendUTF8CString(xmlString
, "</");
561 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
562 _plistAppendUTF8CString(xmlString
, ">\n");
563 } else if (typeID
== dicttype
) {
564 UInt32 i
, count
= CFDictionaryGetCount((CFDictionaryRef
)object
);
565 CFMutableArrayRef keyArray
;
567 _plistAppendUTF8CString(xmlString
, "<");
568 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
569 _plistAppendUTF8CString(xmlString
, "/>\n");
572 _plistAppendUTF8CString(xmlString
, "<");
573 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
574 _plistAppendUTF8CString(xmlString
, ">\n");
575 new_cftype_array(keys
, count
);
576 CFDictionaryGetKeysAndValues((CFDictionaryRef
)object
, keys
, NULL
);
577 keyArray
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, count
, &kCFTypeArrayCallBacks
);
578 CFArrayReplaceValues(keyArray
, CFRangeMake(0, 0), keys
, count
);
579 CFArraySortValues(keyArray
, CFRangeMake(0, count
), (CFComparatorFunction
)CFStringCompare
, NULL
);
580 CFArrayGetValues(keyArray
, CFRangeMake(0, count
), keys
);
582 for (i
= 0; i
< count
; i
++) {
583 CFTypeRef key
= keys
[i
];
584 _appendIndents(indentation
+1, xmlString
);
585 _plistAppendUTF8CString(xmlString
, "<");
586 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
);
587 _plistAppendUTF8CString(xmlString
, ">");
588 _appendEscapedString((CFStringRef
)key
, xmlString
);
589 _plistAppendUTF8CString(xmlString
, "</");
590 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
);
591 _plistAppendUTF8CString(xmlString
, ">\n");
592 _CFAppendXML0(CFDictionaryGetValue((CFDictionaryRef
)object
, key
), indentation
+1, xmlString
);
594 free_cftype_array(keys
);
595 _appendIndents(indentation
, xmlString
);
596 _plistAppendUTF8CString(xmlString
, "</");
597 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
598 _plistAppendUTF8CString(xmlString
, ">\n");
599 } else if (typeID
== datatype
) {
600 _plistAppendUTF8CString(xmlString
, "<");
601 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
);
602 _plistAppendUTF8CString(xmlString
, ">\n");
603 _XMLPlistAppendDataUsingBase64(xmlString
, (CFDataRef
)object
, indentation
);
604 _appendIndents(indentation
, xmlString
);
605 _plistAppendUTF8CString(xmlString
, "</");
606 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
);
607 _plistAppendUTF8CString(xmlString
, ">\n");
608 } else if (typeID
== datetype
) {
609 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
610 int32_t y
= 0, M
= 0, d
= 0, H
= 0, m
= 0, s
= 0;
612 CFGregorianDate date
= CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime((CFDateRef
)object
), NULL
);
618 s
= (int32_t)date
.second
;
620 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, kCFCalendarIdentifierGregorian
);
621 CFTimeZoneRef tz
= CFTimeZoneCreateWithName(kCFAllocatorSystemDefault
, CFSTR("GMT"), true);
622 CFCalendarSetTimeZone(calendar
, tz
);
623 CFCalendarDecomposeAbsoluteTime(calendar
, CFDateGetAbsoluteTime((CFDateRef
)object
), (const uint8_t *)"yMdHms", &y
, &M
, &d
, &H
, &m
, &s
);
627 _plistAppendUTF8CString(xmlString
, "<");
628 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
);
629 _plistAppendUTF8CString(xmlString
, ">");
630 _plistAppendFormat(xmlString
, CFSTR("%04d-%02d-%02dT%02d:%02d:%02dZ"), y
, M
, d
, H
, m
, s
);
631 _plistAppendUTF8CString(xmlString
, "</");
632 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
);
633 _plistAppendUTF8CString(xmlString
, ">\n");
634 } else if (typeID
== numbertype
) {
635 if (CFNumberIsFloatType((CFNumberRef
)object
)) {
636 _plistAppendUTF8CString(xmlString
, "<");
637 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
);
638 _plistAppendUTF8CString(xmlString
, ">");
639 CFStringRef s
= __CFNumberCopyFormattingDescriptionAsFloat64(object
);
640 _plistAppendString(xmlString
, s
);
642 _plistAppendUTF8CString(xmlString
, "</");
643 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
);
644 _plistAppendUTF8CString(xmlString
, ">\n");
646 _plistAppendUTF8CString(xmlString
, "<");
647 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
648 _plistAppendUTF8CString(xmlString
, ">");
650 _plistAppendFormat(xmlString
, CFSTR("%@"), object
);
652 _plistAppendUTF8CString(xmlString
, "</");
653 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
654 _plistAppendUTF8CString(xmlString
, ">\n");
656 } else if (typeID
== booltype
) {
657 if (CFBooleanGetValue((CFBooleanRef
)object
)) {
658 _plistAppendUTF8CString(xmlString
, "<");
659 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
);
660 _plistAppendUTF8CString(xmlString
, "/>\n");
662 _plistAppendUTF8CString(xmlString
, "<");
663 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
);
664 _plistAppendUTF8CString(xmlString
, "/>\n");
669 static void _CFGenerateXMLPropertyListToData(CFMutableDataRef xml
, CFTypeRef propertyList
) {
670 _plistAppendUTF8CString(xml
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE ");
671 _plistAppendCharacters(xml
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
);
672 _plistAppendUTF8CString(xml
, " PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<");
673 _plistAppendCharacters(xml
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
);
674 _plistAppendUTF8CString(xml
, " version=\"1.0\">\n");
676 _CFAppendXML0(propertyList
, 0, xml
);
678 _plistAppendUTF8CString(xml
, "</");
679 _plistAppendCharacters(xml
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
);
680 _plistAppendUTF8CString(xml
, ">\n");
683 CFDataRef
_CFPropertyListCreateXMLData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, Boolean checkValidPlist
) {
685 CFMutableDataRef xml
;
686 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
687 if (checkValidPlist
&& !CFPropertyListIsValid(propertyList
, kCFPropertyListXMLFormat_v1_0
)) {
688 __CFAssertIsPList(propertyList
);
691 xml
= CFDataCreateMutable(allocator
, 0);
692 _CFGenerateXMLPropertyListToData(xml
, propertyList
);
696 CFDataRef
CFPropertyListCreateXMLData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
) {
697 return _CFPropertyListCreateXMLData(allocator
, propertyList
, true);
700 CF_EXPORT CFDataRef
_CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator
, CFPropertyListRef propertyList
) {
701 return _CFPropertyListCreateXMLData(allocator
, propertyList
, false);
704 // ========================================================================
707 // ------------------------- Reading plists ------------------
710 static void skipInlineDTD(_CFXMLPlistParseInfo
*pInfo
);
711 static CFTypeRef
parseXMLElement(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
);
713 // warning: doesn't have a good idea of Unicode line separators
714 static UInt32
lineNumber(_CFXMLPlistParseInfo
*pInfo
) {
715 const UniChar
*p
= pInfo
->begin
;
717 while (p
< pInfo
->curr
) {
720 if (*(p
+ 1) == '\n')
722 } else if (*p
== '\n') {
730 // warning: doesn't have a good idea of Unicode white space
731 CF_INLINE
void skipWhitespace(_CFXMLPlistParseInfo
*pInfo
) {
732 while (pInfo
->curr
< pInfo
->end
) {
733 switch (*(pInfo
->curr
)) {
746 /* 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. */
748 // pInfo should be just past "<!--"
749 static void skipXMLComment(_CFXMLPlistParseInfo
*pInfo
) {
750 const UniChar
*p
= pInfo
->curr
;
751 const UniChar
*end
= pInfo
->end
- 3; // Need at least 3 characters to compare against
753 if (*p
== '-' && *(p
+1) == '-' && *(p
+2) == '>') {
759 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unterminated comment started on line %d"), lineNumber(pInfo
));
762 // stringToMatch and buf must both be of at least len
763 static Boolean
matchString(const UniChar
*buf
, const UniChar
*stringToMatch
, UInt32 len
) {
765 case 10: if (buf
[9] != stringToMatch
[9]) return false;
766 case 9: if (buf
[8] != stringToMatch
[8]) return false;
767 case 8: if (buf
[7] != stringToMatch
[7]) return false;
768 case 7: if (buf
[6] != stringToMatch
[6]) return false;
769 case 6: if (buf
[5] != stringToMatch
[5]) return false;
770 case 5: if (buf
[4] != stringToMatch
[4]) return false;
771 case 4: if (buf
[3] != stringToMatch
[3]) return false;
772 case 3: if (buf
[2] != stringToMatch
[2]) return false;
773 case 2: if (buf
[1] != stringToMatch
[1]) return false;
774 case 1: if (buf
[0] != stringToMatch
[0]) return false;
777 return false; // internal error
780 // pInfo should be set to the first character after "<?"
781 static void skipXMLProcessingInstruction(_CFXMLPlistParseInfo
*pInfo
) {
782 const UniChar
*begin
= pInfo
->curr
, *end
= pInfo
->end
- 2; // Looking for "?>" so we need at least 2 characters
783 while (pInfo
->curr
< end
) {
784 if (*(pInfo
->curr
) == '?' && *(pInfo
->curr
+1) == '>') {
791 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing the processing instruction begun on line %d"), lineNumber(pInfo
));
794 // first character should be immediately after the "<!"
795 static void skipDTD(_CFXMLPlistParseInfo
*pInfo
) {
796 // First pass "DOCTYPE"
797 if (pInfo
->end
- pInfo
->curr
< DOCTYPE_TAG_LENGTH
|| !matchString(pInfo
->curr
, CFXMLPlistTags
[DOCTYPE_IX
], DOCTYPE_TAG_LENGTH
)) {
798 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Malformed DTD on line %d"), lineNumber(pInfo
));
801 pInfo
->curr
+= DOCTYPE_TAG_LENGTH
;
802 skipWhitespace(pInfo
);
804 // Look for either the beginning of a complex DTD or the end of the DOCTYPE structure
805 while (pInfo
->curr
< pInfo
->end
) {
806 UniChar ch
= *(pInfo
->curr
);
807 if (ch
== '[') break; // inline DTD
808 if (ch
== '>') { // End of the DTD
814 if (pInfo
->curr
== pInfo
->end
) {
815 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing DTD"));
819 // *Sigh* Must parse in-line DTD
820 skipInlineDTD(pInfo
);
821 if (pInfo
->error
) return;
822 skipWhitespace(pInfo
);
823 if (pInfo
->error
) return;
824 if (pInfo
->curr
< pInfo
->end
) {
825 if (*(pInfo
->curr
) == '>') {
828 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while parsing DTD"), *(pInfo
->curr
), lineNumber(pInfo
));
831 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing DTD"));
835 static void skipPERef(_CFXMLPlistParseInfo
*pInfo
) {
836 const UniChar
*p
= pInfo
->curr
;
837 while (p
< pInfo
->end
) {
844 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing percent-escape sequence begun on line %d"), lineNumber(pInfo
));
847 // First character should be just past '['
848 static void skipInlineDTD(_CFXMLPlistParseInfo
*pInfo
) {
849 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
851 skipWhitespace(pInfo
);
856 } else if (ch
== '<') {
858 if (pInfo
->curr
>= pInfo
->end
) {
859 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
865 skipXMLProcessingInstruction(pInfo
);
866 } else if (ch
== '!') {
867 if (pInfo
->curr
+ 2 < pInfo
->end
&& (*(pInfo
->curr
+1) == '-' && *(pInfo
->curr
+2) == '-')) {
869 skipXMLComment(pInfo
);
871 // Skip the myriad of DTD declarations of the form "<!string" ... ">"
872 pInfo
->curr
++; // Past both '<' and '!'
873 while (pInfo
->curr
< pInfo
->end
) {
874 if (*(pInfo
->curr
) == '>') break;
877 if (*(pInfo
->curr
) != '>') {
878 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
884 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch
, lineNumber(pInfo
));
887 } else if (ch
== ']') {
891 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch
, lineNumber(pInfo
));
896 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
900 /* A bit wasteful to do everything with unichars (since we know all the characters we're going to see are 7-bit ASCII), but since our data is coming from or going to a CFString, this prevents the extra cost of converting formats. */
902 static const signed char __CFPLDataDecodeTable
[128] = {
903 /* 000 */ -1, -1, -1, -1, -1, -1, -1, -1,
904 /* 010 */ -1, -1, -1, -1, -1, -1, -1, -1,
905 /* 020 */ -1, -1, -1, -1, -1, -1, -1, -1,
906 /* 030 */ -1, -1, -1, -1, -1, -1, -1, -1,
907 /* ' ' */ -1, -1, -1, -1, -1, -1, -1, -1,
908 /* '(' */ -1, -1, -1, 62, -1, -1, -1, 63,
909 /* '0' */ 52, 53, 54, 55, 56, 57, 58, 59,
910 /* '8' */ 60, 61, -1, -1, -1, 0, -1, -1,
911 /* '@' */ -1, 0, 1, 2, 3, 4, 5, 6,
912 /* 'H' */ 7, 8, 9, 10, 11, 12, 13, 14,
913 /* 'P' */ 15, 16, 17, 18, 19, 20, 21, 22,
914 /* 'X' */ 23, 24, 25, -1, -1, -1, -1, -1,
915 /* '`' */ -1, 26, 27, 28, 29, 30, 31, 32,
916 /* 'h' */ 33, 34, 35, 36, 37, 38, 39, 40,
917 /* 'p' */ 41, 42, 43, 44, 45, 46, 47, 48,
918 /* 'x' */ 49, 50, 51, -1, -1, -1, -1, -1
921 static CFDataRef
__CFPLDataDecode(_CFXMLPlistParseInfo
*pInfo
, Boolean isMutable
) {
929 tmpbuf
= (uint8_t *)CFAllocatorAllocate(pInfo
->allocator
, tmpbuflen
, 0);
930 for (; pInfo
->curr
< pInfo
->end
; pInfo
->curr
++) {
931 UniChar c
= *(pInfo
->curr
);
937 } else if (!isspace(c
)) {
940 if (__CFPLDataDecodeTable
[c
] < 0)
944 acc
+= __CFPLDataDecodeTable
[c
];
945 if (0 == (cntr
& 0x3)) {
946 if (tmpbuflen
<= tmpbufpos
+ 2) {
947 if (tmpbuflen
< 256 * 1024) {
949 } else if (tmpbuflen
< 16 * 1024 * 1024) {
952 // once in this stage, this will be really slow
953 // and really potentially fragment memory
954 tmpbuflen
+= 256 * 1024;
956 tmpbuf
= (uint8_t *)CFAllocatorReallocate(pInfo
->allocator
, tmpbuf
, tmpbuflen
, 0);
959 tmpbuf
[tmpbufpos
++] = (acc
>> 16) & 0xff;
961 tmpbuf
[tmpbufpos
++] = (acc
>> 8) & 0xff;
963 tmpbuf
[tmpbufpos
++] = acc
& 0xff;
967 CFMutableDataRef result
= CFDataCreateMutable(pInfo
->allocator
, 0);
968 CFDataAppendBytes(result
, tmpbuf
, tmpbufpos
);
969 CFAllocatorDeallocate(pInfo
->allocator
, tmpbuf
);
972 return CFDataCreateWithBytesNoCopy(pInfo
->allocator
, tmpbuf
, tmpbufpos
, pInfo
->allocator
);
976 // content ::== (element | CharData | Reference | CDSect | PI | Comment)*
977 // 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).
978 static CFTypeRef
getContentObject(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
) {
979 if (isKey
) *isKey
= false;
980 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
981 skipWhitespace(pInfo
);
982 if (pInfo
->curr
>= pInfo
->end
) {
983 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
986 if (*(pInfo
->curr
) != '<') {
987 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
991 if (pInfo
->curr
>= pInfo
->end
) {
992 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
995 switch (*(pInfo
->curr
)) {
997 // Processing instruction
998 skipXMLProcessingInstruction(pInfo
);
1001 // Could be a comment
1002 if (pInfo
->curr
+2 >= pInfo
->end
) {
1003 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1006 if (*(pInfo
->curr
+1) == '-' && *(pInfo
->curr
+2) == '-') {
1008 skipXMLComment(pInfo
);
1010 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1015 // Whoops! Looks like we got to the end tag for the element whose content we're parsing
1016 pInfo
->curr
--; // Back off to the '<'
1019 // Should be an element
1020 return parseXMLElement(pInfo
, isKey
);
1023 // 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"
1027 static void _catFromMarkToBuf(const UniChar
*mark
, const UniChar
*buf
, CFMutableStringRef
*string
, _CFXMLPlistParseInfo
*pInfo
) {
1029 *string
= CFStringCreateMutable(pInfo
->allocator
, 0);
1031 CFStringAppendCharacters(*string
, mark
, buf
-mark
);
1034 static void parseCDSect_pl(_CFXMLPlistParseInfo
*pInfo
, CFMutableStringRef string
) {
1035 const UniChar
*end
, *begin
;
1036 if (pInfo
->end
- pInfo
->curr
< CDSECT_TAG_LENGTH
) {
1037 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1040 if (!matchString(pInfo
->curr
, CFXMLPlistTags
[CDSECT_IX
], CDSECT_TAG_LENGTH
)) {
1041 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered improper CDATA opening at line %d"), lineNumber(pInfo
));
1044 pInfo
->curr
+= CDSECT_TAG_LENGTH
;
1045 begin
= pInfo
->curr
; // Marks the first character of the CDATA content
1046 end
= pInfo
->end
-2; // So we can safely look 2 characters beyond p
1047 while (pInfo
->curr
< end
) {
1048 if (*(pInfo
->curr
) == ']' && *(pInfo
->curr
+1) == ']' && *(pInfo
->curr
+2) == '>') {
1050 CFStringAppendCharacters(string
, begin
, pInfo
->curr
-begin
);
1056 // Never found the end mark
1057 pInfo
->curr
= begin
;
1058 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not find end of CDATA started on line %d"), lineNumber(pInfo
));
1061 // Only legal references are {lt, gt, amp, apos, quote, #ddd, #xAAA}
1062 static void parseEntityReference_pl(_CFXMLPlistParseInfo
*pInfo
, CFMutableStringRef string
) {
1065 pInfo
->curr
++; // move past the '&';
1066 len
= pInfo
->end
- pInfo
->curr
; // how many characters we can safely scan
1068 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1071 switch (*(pInfo
->curr
)) {
1073 if (len
>= 3 && *(pInfo
->curr
+1) == 't' && *(pInfo
->curr
+2) == ';') {
1078 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1081 if (len
>= 3 && *(pInfo
->curr
+1) == 't' && *(pInfo
->curr
+2) == ';') {
1086 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1088 case 'a': // "apos" or "amp"
1089 if (len
< 4) { // Not enough characters for either conversion
1090 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1093 if (*(pInfo
->curr
+1) == 'm') {
1095 if (*(pInfo
->curr
+2) == 'p' && *(pInfo
->curr
+3) == ';') {
1100 } else if (*(pInfo
->curr
+1) == 'p') {
1102 if (len
> 4 && *(pInfo
->curr
+2) == 'o' && *(pInfo
->curr
+3) == 's' && *(pInfo
->curr
+4) == ';') {
1108 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1110 case 'q': // "quote"
1111 if (len
>= 5 && *(pInfo
->curr
+1) == 'u' && *(pInfo
->curr
+2) == 'o' && *(pInfo
->curr
+3) == 't' && *(pInfo
->curr
+4) == ';') {
1116 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1121 Boolean isHex
= false;
1122 if ( len
< 4) { // Not enough characters to make it all fit! Need at least "&#d;"
1123 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1127 if (*(pInfo
->curr
) == 'x') {
1131 while (pInfo
->curr
< pInfo
->end
) {
1132 ch
= *(pInfo
->curr
);
1135 CFStringAppendCharacters(string
, &num
, 1);
1138 if (!isHex
) num
= num
*10;
1139 else num
= num
<< 4;
1140 if (ch
<= '9' && ch
>= '0') {
1142 } else if (!isHex
) {
1143 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c at line %d"), ch
, lineNumber(pInfo
));
1145 } else if (ch
>= 'a' && ch
<= 'f') {
1146 num
+= 10 + (ch
- 'a');
1147 } else if (ch
>= 'A' && ch
<= 'F') {
1148 num
+= 10 + (ch
- 'A');
1150 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c at line %d"), ch
, lineNumber(pInfo
));
1154 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1158 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1161 CFStringAppendCharacters(string
, &ch
, 1);
1164 extern void _CFStrSetDesiredCapacity(CFMutableStringRef str
, CFIndex len
);
1166 static CFStringRef
_uniqueStringForString(_CFXMLPlistParseInfo
*pInfo
, CFStringRef stringToUnique
) {
1167 CFStringRef uniqued
= (CFStringRef
)CFSetGetValue(pInfo
->stringSet
, stringToUnique
);
1169 uniqued
= (CFStringRef
)__CFStringCollectionCopy(pInfo
->allocator
, stringToUnique
);
1170 CFSetAddValue(pInfo
->stringSet
, uniqued
);
1171 __CFTypeCollectionRelease(pInfo
->allocator
, uniqued
);
1173 if (uniqued
&& !_CFAllocatorIsGCRefZero(pInfo
->allocator
)) CFRetain(uniqued
);
1177 static CFStringRef
_uniqueStringForCharacters(_CFXMLPlistParseInfo
*pInfo
, const UniChar
*base
, CFIndex length
) {
1178 if (0 == length
) return !_CFAllocatorIsGCRefZero(pInfo
->allocator
) ? (CFStringRef
)CFRetain(CFSTR("")) : CFSTR("");
1179 // This is to avoid having to promote the buffers of all the strings compared against
1180 // during the set probe; if a Unicode string is passed in, that's what happens.
1181 CFStringRef stringToUnique
= NULL
;
1182 Boolean use_stack
= (length
< 2048);
1183 STACK_BUFFER_DECL(uint8_t, buffer
, use_stack
? length
+ 1 : 1);
1184 uint8_t *ascii
= use_stack
? buffer
: (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault
, length
+ 1, 0);
1185 for (CFIndex idx
= 0; idx
< length
; idx
++) {
1186 UniChar ch
= base
[idx
];
1188 ascii
[idx
] = (uint8_t)ch
;
1190 stringToUnique
= CFStringCreateWithCharacters(pInfo
->allocator
, base
, length
);
1194 if (!stringToUnique
) {
1195 ascii
[length
] = '\0';
1196 stringToUnique
= CFStringCreateWithBytes(pInfo
->allocator
, ascii
, length
, kCFStringEncodingASCII
, false);
1198 if (ascii
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, ascii
);
1199 CFStringRef uniqued
= (CFStringRef
)CFSetGetValue(pInfo
->stringSet
, stringToUnique
);
1201 CFSetAddValue(pInfo
->stringSet
, stringToUnique
);
1202 uniqued
= stringToUnique
;
1204 __CFPListRelease(stringToUnique
, pInfo
);
1205 if (uniqued
&& !_CFAllocatorIsGCRefZero(pInfo
->allocator
)) CFRetain(uniqued
);
1209 // String could be comprised of characters, CDSects, or references to one of the "well-known" entities ('<', '>', '&', ''', '"')
1210 // returns a retained object in *string.
1211 static CFStringRef
getString(_CFXMLPlistParseInfo
*pInfo
) {
1212 const UniChar
*mark
= pInfo
->curr
; // At any time in the while loop below, the characters between mark and p have not yet been added to *string
1213 CFMutableStringRef string
= NULL
;
1214 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
1215 UniChar ch
= *(pInfo
->curr
);
1217 if (pInfo
->curr
+ 1 >= pInfo
->end
) break;
1218 // Could be a CDSect; could be the end of the string
1219 if (*(pInfo
->curr
+1) != '!') break; // End of the string
1220 _catFromMarkToBuf(mark
, pInfo
->curr
, &string
, pInfo
);
1221 parseCDSect_pl(pInfo
, string
);
1223 } else if (ch
== '&') {
1224 _catFromMarkToBuf(mark
, pInfo
->curr
, &string
, pInfo
);
1225 parseEntityReference_pl(pInfo
, string
);
1233 __CFPListRelease(string
, pInfo
);
1237 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
1238 CFStringRef uniqueString
= _uniqueStringForCharacters(pInfo
, mark
, pInfo
->curr
-mark
);
1239 return uniqueString
;
1241 string
= CFStringCreateMutable(pInfo
->allocator
, 0);
1242 CFStringAppendCharacters(string
, mark
, pInfo
->curr
- mark
);
1246 _catFromMarkToBuf(mark
, pInfo
->curr
, &string
, pInfo
);
1247 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
1248 CFStringRef uniqueString
= _uniqueStringForString(pInfo
, string
);
1249 __CFPListRelease(string
, pInfo
);
1250 return uniqueString
;
1255 static Boolean
checkForCloseTag(_CFXMLPlistParseInfo
*pInfo
, const UniChar
*tag
, CFIndex tagLen
) {
1256 if (pInfo
->end
- pInfo
->curr
< tagLen
+ 3) {
1257 if (!pInfo
->error
) {
1258 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1262 if (*(pInfo
->curr
) != '<' || *(++pInfo
->curr
) != '/') {
1263 if (!pInfo
->error
) {
1264 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
1269 if (!matchString(pInfo
->curr
, tag
, tagLen
)) {
1270 CFStringRef str
= CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault
, tag
, tagLen
, kCFAllocatorNull
);
1271 if (!pInfo
->error
) {
1272 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Close tag on line %d does not match open tag %@"), lineNumber(pInfo
), str
);
1277 pInfo
->curr
+= tagLen
;
1278 skipWhitespace(pInfo
);
1279 if (pInfo
->curr
== pInfo
->end
) {
1280 if (!pInfo
->error
) {
1281 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1285 if (*(pInfo
->curr
) != '>') {
1286 if (!pInfo
->error
) {
1287 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
1295 // pInfo should be set to the first content character of the <plist>
1296 static CFTypeRef
parsePListTag(_CFXMLPlistParseInfo
*pInfo
) {
1297 CFTypeRef result
, tmp
= NULL
;
1298 const UniChar
*save
;
1299 result
= getContentObject(pInfo
, NULL
);
1301 if (!pInfo
->error
) pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty plist tag"));
1304 save
= pInfo
->curr
; // Save this in case the next step fails
1305 tmp
= getContentObject(pInfo
, NULL
);
1307 // Got an extra object
1308 __CFPListRelease(tmp
, pInfo
);
1309 __CFPListRelease(result
, pInfo
);
1311 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected element at line %d (plist can only include one object)"), lineNumber(pInfo
));
1315 // Parse failed catastrophically
1316 __CFPListRelease(result
, pInfo
);
1319 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
)) {
1322 __CFPListRelease(result
, pInfo
);
1326 static int allowImmutableCollections
= -1;
1328 static void checkImmutableCollections(void) {
1329 allowImmutableCollections
= (NULL
== __CFgetenv("CFPropertyListAllowImmutableCollections")) ? 0 : 1;
1332 static CFTypeRef
parseArrayTag(_CFXMLPlistParseInfo
*pInfo
) {
1333 CFMutableArrayRef array
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
1334 CFTypeRef tmp
= getContentObject(pInfo
, NULL
);
1336 CFArrayAppendValue(array
, tmp
);
1337 __CFPListRelease(tmp
, pInfo
);
1338 tmp
= getContentObject(pInfo
, NULL
);
1340 if (pInfo
->error
) { // getContentObject encountered a parse error
1341 __CFPListRelease(array
, pInfo
);
1344 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
)) {
1345 if (-1 == allowImmutableCollections
) checkImmutableCollections();
1346 if (1 == allowImmutableCollections
) {
1347 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1348 CFArrayRef newArray
= CFArrayCreateCopy(pInfo
->allocator
, array
);
1349 __CFPListRelease(array
, pInfo
);
1350 array
= (CFMutableArrayRef
)newArray
;
1355 __CFPListRelease(array
, pInfo
);
1359 static CFTypeRef
parseDictTag(_CFXMLPlistParseInfo
*pInfo
) {
1360 CFMutableDictionaryRef dict
= NULL
;
1361 CFTypeRef key
=NULL
, value
=NULL
;
1363 const UniChar
*base
= pInfo
->curr
;
1364 key
= getContentObject(pInfo
, &gotKey
);
1367 __CFPListRelease(key
, pInfo
);
1368 __CFPListRelease(dict
, pInfo
);
1370 if (!pInfo
->error
) {
1371 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo
));
1375 value
= getContentObject(pInfo
, NULL
);
1377 __CFPListRelease(key
, pInfo
);
1378 __CFPListRelease(dict
, pInfo
);
1379 if (!pInfo
->error
) {
1380 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo
));
1385 dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1386 _CFDictionarySetCapacity(dict
, 10);
1388 CFDictionarySetValue(dict
, key
, value
);
1389 __CFPListRelease(key
, pInfo
);
1391 __CFPListRelease(value
, pInfo
);
1394 key
= getContentObject(pInfo
, &gotKey
);
1396 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
)) {
1398 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1399 dict
= (CFMutableDictionaryRef
)CFDictionaryCreate(pInfo
->allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1401 dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1404 CFIndex cnt
= CFDictionaryGetCount(dict
);
1406 CFTypeRef val
= CFDictionaryGetValue(dict
, CFSTR("CF$UID"));
1407 if (val
&& CFGetTypeID(val
) == numbertype
) {
1410 CFNumberGetValue((CFNumberRef
)val
, kCFNumberSInt32Type
, &v
);
1411 uid
= (CFTypeRef
)_CFKeyedArchiverUIDCreate(pInfo
->allocator
, v
);
1412 __CFPListRelease(dict
, pInfo
);
1416 if (-1 == allowImmutableCollections
) checkImmutableCollections();
1417 if (1 == allowImmutableCollections
) {
1418 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1419 CFDictionaryRef newDict
= CFDictionaryCreateCopy(pInfo
->allocator
, dict
);
1420 __CFPListRelease(dict
, pInfo
);
1421 dict
= (CFMutableDictionaryRef
)newDict
;
1427 __CFPListRelease(dict
, pInfo
);
1431 static CFTypeRef
parseDataTag(_CFXMLPlistParseInfo
*pInfo
) {
1433 const UniChar
*base
= pInfo
->curr
;
1434 result
= __CFPLDataDecode(pInfo
, pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
);
1437 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not interpret <data> at line %d (should be base64-encoded)"), lineNumber(pInfo
));
1440 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
)) return result
;
1441 __CFPListRelease(result
, pInfo
);
1445 CF_INLINE Boolean
read2DigitNumber(_CFXMLPlistParseInfo
*pInfo
, int32_t *result
) {
1447 if (pInfo
->curr
+ 2 >= pInfo
->end
) return false;
1449 ch2
= *(pInfo
->curr
+ 1);
1451 if (!isdigit(ch1
) || !isdigit(ch2
)) return false;
1452 *result
= (ch1
- '0')*10 + (ch2
- '0');
1456 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
1457 static CFTypeRef
parseDateTag(_CFXMLPlistParseInfo
*pInfo
) {
1458 int32_t year
= 0, month
= 0, day
= 0, hour
= 0, minute
= 0, second
= 0;
1460 Boolean badForm
= false;
1461 Boolean yearIsNegative
= false;
1463 if (pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== '-') {
1464 yearIsNegative
= true;
1468 while (pInfo
->curr
< pInfo
->end
&& isdigit(*pInfo
->curr
)) {
1469 year
= 10*year
+ (*pInfo
->curr
) - '0';
1472 if (pInfo
->curr
>= pInfo
->end
|| *pInfo
->curr
!= '-') {
1478 if (!badForm
&& read2DigitNumber(pInfo
, &month
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== '-') {
1484 if (!badForm
&& read2DigitNumber(pInfo
, &day
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== 'T') {
1490 if (!badForm
&& read2DigitNumber(pInfo
, &hour
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== ':') {
1496 if (!badForm
&& read2DigitNumber(pInfo
, &minute
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== ':') {
1502 if (!badForm
&& read2DigitNumber(pInfo
, &num
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== 'Z') {
1510 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not interpret <date> at line %d"), lineNumber(pInfo
));
1513 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
)) return NULL
;
1515 CFAbsoluteTime at
= 0.0;
1517 CFGregorianDate date
= {yearIsNegative
? -year
: year
, month
, day
, hour
, minute
, second
};
1518 at
= CFGregorianDateGetAbsoluteTime(date
, NULL
);
1520 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, kCFCalendarIdentifierGregorian
);
1521 CFTimeZoneRef tz
= CFTimeZoneCreateWithName(kCFAllocatorSystemDefault
, CFSTR("GMT"), true);
1522 CFCalendarSetTimeZone(calendar
, tz
);
1523 CFCalendarComposeAbsoluteTime(calendar
, &at
, (const uint8_t *)"yMdHms", year
, month
, day
, hour
, minute
, second
);
1524 CFRelease(calendar
);
1527 return CFDateCreate(pInfo
->allocator
, at
);
1530 static CFTypeRef
parseRealTag(_CFXMLPlistParseInfo
*pInfo
) {
1531 CFStringRef str
= getString(pInfo
);
1535 CFStringInlineBuffer buf
;
1537 if (!pInfo
->error
) {
1538 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo
));
1543 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("nan"), kCFCompareCaseInsensitive
)) {
1544 __CFPListRelease(str
, pInfo
);
1545 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberNaN
) : NULL
;
1547 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("+infinity"), kCFCompareCaseInsensitive
)) {
1548 __CFPListRelease(str
, pInfo
);
1549 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberPositiveInfinity
) : NULL
;
1551 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("-infinity"), kCFCompareCaseInsensitive
)) {
1552 __CFPListRelease(str
, pInfo
);
1553 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberNegativeInfinity
) : NULL
;
1555 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("infinity"), kCFCompareCaseInsensitive
)) {
1556 __CFPListRelease(str
, pInfo
);
1557 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberPositiveInfinity
) : NULL
;
1559 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("-inf"), kCFCompareCaseInsensitive
)) {
1560 __CFPListRelease(str
, pInfo
);
1561 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberNegativeInfinity
) : NULL
;
1563 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("inf"), kCFCompareCaseInsensitive
)) {
1564 __CFPListRelease(str
, pInfo
);
1565 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberPositiveInfinity
) : NULL
;
1567 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("+inf"), kCFCompareCaseInsensitive
)) {
1568 __CFPListRelease(str
, pInfo
);
1569 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberPositiveInfinity
) : NULL
;
1572 len
= CFStringGetLength(str
);
1573 CFStringInitInlineBuffer(str
, &buf
, CFRangeMake(0, len
));
1575 if (!__CFStringScanDouble(&buf
, NULL
, &idx
, &val
) || idx
!= len
) {
1576 __CFPListRelease(str
, pInfo
);
1577 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered misformatted real on line %d"), lineNumber(pInfo
));
1580 __CFPListRelease(str
, pInfo
);
1581 result
= CFNumberCreate(pInfo
->allocator
, kCFNumberDoubleType
, &val
);
1582 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) return result
;
1583 __CFPListRelease(result
, pInfo
);
1587 #define GET_CH if (pInfo->curr == pInfo->end) { \
1588 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Premature end of file after <integer> on line %d"), lineNumber(pInfo)); \
1599 kCFNumberSInt128Type
= 17
1602 static CFTypeRef
parseIntegerTag(_CFXMLPlistParseInfo
*pInfo
) {
1603 bool isHex
= false, isNeg
= false, hadLeadingZero
= false;
1606 // decimal_constant S*(-|+)?S*[0-9]+ (S == space)
1607 // hex_constant S*(-|+)?S*0[xX][0-9a-fA-F]+ (S == space)
1609 while (pInfo
->curr
< pInfo
->end
&& __CFIsWhitespace(*(pInfo
->curr
))) pInfo
->curr
++;
1612 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo
));
1615 if ('-' == ch
|| '+' == ch
) {
1616 isNeg
= ('-' == ch
);
1618 while (pInfo
->curr
< pInfo
->end
&& __CFIsWhitespace(*(pInfo
->curr
))) pInfo
->curr
++;
1622 if (pInfo
->curr
+ 1 < pInfo
->end
&& ('x' == *(pInfo
->curr
+ 1) || 'X' == *(pInfo
->curr
+ 1))) {
1626 hadLeadingZero
= true;
1632 hadLeadingZero
= true;
1636 if ('<' == ch
&& hadLeadingZero
) { // nothing but zeros
1638 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
)) {
1639 // checkForCloseTag() sets error string
1642 return CFNumberCreate(pInfo
->allocator
, kCFNumberSInt32Type
, &val
);
1645 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Incomplete <integer> on line %d"), lineNumber(pInfo
));
1649 uint32_t multiplier
= (isHex
? 16 : 10);
1651 uint32_t new_digit
= 0;
1653 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
1654 new_digit
= (ch
- '0');
1656 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1657 new_digit
= (ch
- 'a' + 10);
1659 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1660 new_digit
= (ch
- 'A' + 10);
1662 default: // other character
1663 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unknown character '%c' (0x%x) in <integer> on line %d"), ch
, ch
, lineNumber(pInfo
));
1666 if (!isHex
&& new_digit
> 9) {
1667 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Hex digit in non-hex <integer> on line %d"), lineNumber(pInfo
));
1670 if (UINT64_MAX
/ multiplier
< value
) {
1671 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo
));
1674 value
= multiplier
* value
;
1675 if (UINT64_MAX
- new_digit
< value
) {
1676 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo
));
1679 value
= value
+ new_digit
;
1680 if (isNeg
&& (uint64_t)INT64_MAX
+ 1 < value
) {
1681 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Integer underflow in <integer> on line %d"), lineNumber(pInfo
));
1687 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
)) {
1688 // checkForCloseTag() sets error string
1691 if (isNeg
|| value
<= INT64_MAX
) {
1693 if (isNeg
) v
= -v
; // no-op if INT64_MIN
1694 return CFNumberCreate(pInfo
->allocator
, kCFNumberSInt64Type
, &v
);
1696 CFSInt128Struct val
;
1699 return CFNumberCreate(pInfo
->allocator
, kCFNumberSInt128Type
, &val
);
1704 // Returned object is retained; caller must free. pInfo->curr expected to point to the first character after the '<'
1705 static CFTypeRef
parseXMLElement(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
) {
1706 const UniChar
*marker
= pInfo
->curr
;
1707 int markerLength
= -1;
1711 if (isKey
) *isKey
= false;
1712 while (pInfo
->curr
< pInfo
->end
) {
1713 UniChar ch
= *(pInfo
->curr
);
1714 if (ch
== ' ' || ch
== '\t' || ch
== '\n' || ch
=='\r') {
1715 if (markerLength
== -1) markerLength
= pInfo
->curr
- marker
;
1716 } else if (ch
== '>') {
1721 if (pInfo
->curr
>= pInfo
->end
) return NULL
;
1722 isEmpty
= (*(pInfo
->curr
-1) == '/');
1723 if (markerLength
== -1)
1724 markerLength
= pInfo
->curr
- (isEmpty
? 1 : 0) - marker
;
1725 pInfo
->curr
++; // Advance past '>'
1726 if (markerLength
== 0) {
1727 // Back up to the beginning of the marker
1728 pInfo
->curr
= marker
;
1729 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Malformed tag on line %d"), lineNumber(pInfo
));
1734 if (markerLength
== ARRAY_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
))
1735 markerIx
= ARRAY_IX
;
1737 case 'd': // Dictionary, data, or date; Fortunately, they all have the same marker length....
1738 if (markerLength
!= DICT_TAG_LENGTH
)
1740 if (matchString(marker
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
))
1742 else if (matchString(marker
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
))
1744 else if (matchString(marker
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
))
1747 case 'f': // false (boolean)
1748 if (markerLength
== FALSE_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
)) {
1749 markerIx
= FALSE_IX
;
1752 case 'i': // integer
1753 if (markerLength
== INTEGER_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
))
1754 markerIx
= INTEGER_IX
;
1756 case 'k': // Key of a dictionary
1757 if (markerLength
== KEY_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
)) {
1759 if (isKey
) *isKey
= true;
1763 if (markerLength
== PLIST_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
))
1764 markerIx
= PLIST_IX
;
1767 if (markerLength
== REAL_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
))
1771 if (markerLength
== STRING_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[STRING_IX
], STRING_TAG_LENGTH
))
1772 markerIx
= STRING_IX
;
1774 case 't': // true (boolean)
1775 if (markerLength
== TRUE_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
))
1780 if (!pInfo
->allowNewTypes
&& markerIx
!= PLIST_IX
&& markerIx
!= ARRAY_IX
&& markerIx
!= DICT_IX
&& markerIx
!= STRING_IX
&& markerIx
!= KEY_IX
&& markerIx
!= DATA_IX
) {
1781 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered new tag when expecting only old-style property list objects"));
1788 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty plist tag"));
1791 return parsePListTag(pInfo
);
1794 return pInfo
->mutabilityOption
== kCFPropertyListImmutable
? CFArrayCreate(pInfo
->allocator
, NULL
, 0, &kCFTypeArrayCallBacks
) : CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
1796 return parseArrayTag(pInfo
);
1800 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1801 return CFDictionaryCreate(pInfo
->allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1803 return CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1806 return parseDictTag(pInfo
);
1812 int tagLen
= (markerIx
== KEY_IX
) ? KEY_TAG_LENGTH
: STRING_TAG_LENGTH
;
1814 return pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
? CFStringCreateMutable(pInfo
->allocator
, 0) : CFStringCreateWithCharacters(pInfo
->allocator
, NULL
, 0);
1816 str
= getString(pInfo
);
1817 if (!str
) return NULL
; // getString will already have set the error string
1818 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[markerIx
], tagLen
)) {
1819 __CFPListRelease(str
, pInfo
);
1826 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <data> on line %d"), lineNumber(pInfo
));
1829 return parseDataTag(pInfo
);
1833 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <date> on line %d"), lineNumber(pInfo
));
1836 return parseDateTag(pInfo
);
1840 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
)) return NULL
;
1842 return CFRetain(kCFBooleanTrue
);
1845 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
)) return NULL
;
1847 return CFRetain(kCFBooleanFalse
);
1850 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo
));
1853 return parseRealTag(pInfo
);
1857 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo
));
1860 return parseIntegerTag(pInfo
);
1863 CFStringRef markerStr
= CFStringCreateWithCharacters(pInfo
->allocator
, marker
, markerLength
);
1864 pInfo
->curr
= marker
;
1865 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown tag %@ on line %d"), markerStr
, lineNumber(pInfo
));
1866 __CFPListRelease(markerStr
, pInfo
);
1872 static CFTypeRef
parseXMLPropertyList(_CFXMLPlistParseInfo
*pInfo
) {
1873 while (!pInfo
->error
&& pInfo
->curr
< pInfo
->end
) {
1875 skipWhitespace(pInfo
);
1876 if (pInfo
->curr
+1 >= pInfo
->end
) {
1877 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("No XML content found"));
1880 if (*(pInfo
->curr
) != '<') {
1881 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected character %c at line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
1884 ch
= *(++ pInfo
->curr
);
1888 if (pInfo
->curr
+1 < pInfo
->end
&& *pInfo
->curr
== '-' && *(pInfo
->curr
+1) == '-') {
1891 skipXMLComment(pInfo
);
1895 } else if (ch
== '?') {
1896 // Processing instruction
1898 skipXMLProcessingInstruction(pInfo
);
1901 return parseXMLElement(pInfo
, NULL
);
1902 // 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
1905 // Should never get here
1906 if (!(pInfo
->error
)) {
1907 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unexpected EOF"));
1912 static CFStringEncoding
encodingForXMLData(CFDataRef data
, CFErrorRef
*error
) {
1913 const uint8_t *bytes
= (uint8_t *)CFDataGetBytePtr(data
);
1914 UInt32 length
= CFDataGetLength(data
);
1915 const uint8_t *idx
, *end
;
1918 // Check for the byte order mark first
1920 ((*bytes
== 0xFF && *(bytes
+1) == 0xFE) ||
1921 (*bytes
== 0xFE && *(bytes
+1) == 0xFF) ||
1922 *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
1923 return kCFStringEncodingUnicode
;
1925 // Scan for the <?xml.... ?> opening
1926 if (length
< 5 || strncmp((char const *) bytes
, "<?xml", 5) != 0) return kCFStringEncodingUTF8
;
1928 end
= bytes
+ length
;
1929 // Found "<?xml"; now we scan for "encoding"
1932 const uint8_t *scan
;
1933 if ( ch
== '?' || ch
== '>') return kCFStringEncodingUTF8
;
1936 if (idx
+ 8 >= end
) {
1937 if (error
) *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("End of buffer while looking for encoding name"));
1940 if (ch
== 'e' && *scan
++ == 'n' && *scan
++ == 'c' && *scan
++ == 'o' && *scan
++ == 'd' && *scan
++ == 'i'
1941 && *scan
++ == 'n' && *scan
++ == 'g' && *scan
++ == '=') {
1946 if (idx
>= end
) return kCFStringEncodingUTF8
;
1948 if (quote
!= '\'' && quote
!= '\"') return kCFStringEncodingUTF8
;
1950 CFStringRef encodingName
;
1951 const uint8_t *base
= idx
+1; // Move past the quote character
1952 CFStringEncoding enc
;
1955 while (idx
< end
&& *idx
!= quote
) idx
++;
1956 if (idx
>= end
) return kCFStringEncodingUTF8
;
1958 if (len
== 5 && (*base
== 'u' || *base
== 'U') && (base
[1] == 't' || base
[1] == 'T') && (base
[2] == 'f' || base
[2] == 'F') && (base
[3] == '-') && (base
[4] == '8'))
1959 return kCFStringEncodingUTF8
;
1960 encodingName
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, base
, len
, kCFStringEncodingISOLatin1
, false);
1961 enc
= CFStringConvertIANACharSetNameToEncoding(encodingName
);
1962 if (enc
!= kCFStringEncodingInvalidId
) {
1963 CFRelease(encodingName
);
1968 *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Encountered unknown encoding (%@)"), encodingName
);
1969 CFRelease(encodingName
);
1975 bool __CFTryParseBinaryPlist(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFPropertyListRef
*plist
, CFStringRef
*errorString
);
1976 unsigned long _CFPropertyListAllowNonUTF8
= 0;
1978 #define SAVE_PLISTS 0
1981 static Boolean
__savePlistData(CFDataRef data
, CFOptionFlags opt
) {
1984 uint32_t pnlen
= sizeof(pn
);
1985 uint8_t *pnp
= NULL
;
1986 if (0 == _NSGetExecutablePath((char *)pn
, &pnlen
)) {
1987 pnp
= strrchr((char *)pn
, '/');
1994 if (0 == strcmp((char *)pnp
, "parse_plists")) return true;
1995 CFUUIDRef r
= CFUUIDCreate(kCFAllocatorSystemDefault
);
1996 CFStringRef s
= CFUUIDCreateString(kCFAllocatorSystemDefault
, r
);
1997 CFStringRef p
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("/tmp/plists/%s#%@#0x%x"), pnp
, s
, opt
);
1998 _CFStringGetFileSystemRepresentation(p
, fn
, sizeof(fn
));
2002 int fd
= open((const char *)fn
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
2003 if (fd
< 0) return false;
2004 int len
= CFDataGetLength(data
);
2005 int w
= write(fd
, CFDataGetBytePtr(data
), len
);
2008 if (w
!= len
) return false;
2014 CFTypeRef
_CFPropertyListCreateFromXMLStringError(CFAllocatorRef allocator
, CFStringRef xmlString
, CFOptionFlags option
, CFErrorRef
*error
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2017 CFAssert1(xmlString
!= NULL
, __kCFLogAssertion
, "%s(): NULL string not allowed", __PRETTY_FUNCTION__
);
2018 CFAssert2(option
== kCFPropertyListImmutable
|| option
== kCFPropertyListMutableContainers
|| option
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, option
);
2021 Boolean createdBuffer
= false;
2022 length
= xmlString
? CFStringGetLength(xmlString
) : 0;
2026 *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Conversion of string failed. The string is empty."));
2031 _CFXMLPlistParseInfo pInfoBuf
;
2032 _CFXMLPlistParseInfo
*pInfo
= &pInfoBuf
;
2035 // Ensure that xmlString is not collected while we are using it
2036 CFRetain(xmlString
);
2037 UniChar
*buf
= (UniChar
*)CFStringGetCharactersPtr(xmlString
);
2040 buf
= (UniChar
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, length
* sizeof(UniChar
), 0);
2042 out_of_memory_warning();
2045 CFStringGetCharacters(xmlString
, CFRangeMake(0, length
), buf
);
2046 createdBuffer
= true;
2049 pInfo
->end
= buf
+length
;
2051 pInfo
->allocator
= allocator
;
2052 pInfo
->error
= NULL
;
2053 pInfo
->stringSet
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
2054 _CFSetSetCapacity(pInfo
->stringSet
, CFStringGetLength(xmlString
) / 250); // avoid lots of rehashes, may waste some memory; simple heuristic
2055 pInfo
->mutabilityOption
= option
;
2056 pInfo
->allowNewTypes
= allowNewTypes
;
2058 result
= parseXMLPropertyList(pInfo
);
2059 if (result
&& format
) *format
= kCFPropertyListXMLFormat_v1_0
;
2061 CFErrorRef xmlParserErr
= pInfo
->error
;
2062 // Reset pInfo so we can try again
2063 pInfo
->curr
= pInfo
->begin
;
2064 pInfo
->error
= NULL
;
2067 result
= parseOldStylePropertyListOrStringsFile(pInfo
);
2068 if (result
&& format
) {
2069 *format
= kCFPropertyListOpenStepFormat
;
2072 if (!result
&& xmlParserErr
&& error
) {
2073 // Add the new error from the old-style property list parser to the user info of the original error
2074 CFDictionaryRef xmlUserInfo
= CFErrorCopyUserInfo(xmlParserErr
);
2075 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault
, CFDictionaryGetCount(xmlUserInfo
) + 1, xmlUserInfo
);
2076 CFDictionaryAddValue(userInfo
, CFPropertyListOldStyleParserErrorKey
, pInfo
->error
);
2078 // Re-create the xml parser error with this new user info dictionary
2079 CFErrorRef newError
= CFErrorCreate(kCFAllocatorSystemDefault
, CFErrorGetDomain(xmlParserErr
), CFErrorGetCode(xmlParserErr
), userInfo
);
2081 CFRelease(xmlUserInfo
);
2082 CFRelease(userInfo
);
2084 // It is the responsibility of the caller to release this newly created error
2089 CFRelease(xmlParserErr
);
2093 if (createdBuffer
) {
2094 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, (void *)pInfo
->begin
);
2096 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(pInfo
->stringSet
);
2097 if (pInfo
->error
) CFRelease(pInfo
->error
);
2098 CFRelease(xmlString
);
2102 CFTypeRef
_CFPropertyListCreateFromXMLString(CFAllocatorRef allocator
, CFStringRef xmlString
, CFOptionFlags option
, CFStringRef
*errorString
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2104 if (errorString
) *errorString
= NULL
;
2105 CFErrorRef error
= NULL
;
2106 CFTypeRef result
= _CFPropertyListCreateFromXMLStringError(allocator
, xmlString
, option
, &error
, allowNewTypes
, format
);
2108 if (errorString
&& error
) {
2109 // The caller is interested in receiving an error message. Pull the debug string out of the CFError returned above
2110 CFDictionaryRef userInfo
= CFErrorCopyUserInfo(error
);
2111 CFStringRef debugString
= NULL
;
2113 // Handle a special-case for compatibility - if the XML parse failed and the old-style plist parse failed, construct a special string
2114 CFErrorRef underlyingError
= NULL
;
2116 Boolean oldStyleFailed
= CFDictionaryGetValueIfPresent(userInfo
, CFPropertyListOldStyleParserErrorKey
, (const void **)&underlyingError
);
2118 if (oldStyleFailed
) {
2119 CFStringRef xmlParserErrorString
, oldStyleParserErrorString
;
2120 xmlParserErrorString
= (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
2122 CFDictionaryRef oldStyleParserUserInfo
= CFErrorCopyUserInfo(underlyingError
);
2123 oldStyleParserErrorString
= (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
2125 // Combine the two strings into a single one that matches the previous implementation
2126 debugString
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("XML parser error:\n\t%@\nOld-style plist parser error:\n\t%@\n"), xmlParserErrorString
, oldStyleParserErrorString
);
2128 CFRelease(oldStyleParserUserInfo
);
2130 debugString
= (CFStringRef
)CFDictionaryGetValue(userInfo
, kCFErrorDebugDescriptionKey
);
2131 CFRetain(debugString
);
2134 // Give the debugString to the caller, who is responsible for releasing it
2135 CFRelease(userInfo
);
2136 *errorString
= debugString
;
2146 /* Get a single value for a given key in a top-level dictionary in a property list.
2147 @param allocator The allocator to use.
2148 @param data The property list data.
2149 @param option Currently unused, set to 0.
2150 @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).
2151 @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.
2152 @param error If an error occurs, will be set to a valid CFErrorRef. It is the caller's responsibility to release this value.
2153 @return True if the key is found, false otherwise.
2155 bool _CFPropertyListCreateSingleValue(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFStringRef keyPath
, CFPropertyListRef
*value
, CFErrorRef
*error
) {
2159 if (!keyPath
|| CFStringGetLength(keyPath
) == 0) {
2164 CFBinaryPlistTrailer trailer
;
2166 const uint8_t *databytes
= CFDataGetBytePtr(data
);
2167 uint64_t datalen
= CFDataGetLength(data
);
2168 bool result
= false;
2170 // First check to see if it is a binary property list
2171 if (8 <= datalen
&& __CFBinaryPlistGetTopLevelInfo(databytes
, datalen
, &marker
, &offset
, &trailer
)) {
2172 // Split up the key path
2173 CFArrayRef keyPathArray
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorSystemDefaultGCRefZero
, keyPath
, CFSTR(":"));
2174 uint64_t keyOffset
, valueOffset
= offset
;
2176 // Create a dictionary to cache objects in
2177 CFMutableDictionaryRef objects
= CFDictionaryCreateMutable(kCFAllocatorSystemDefaultGCRefZero
, 0, NULL
, &kCFTypeDictionaryValueCallBacks
);
2178 CFIndex keyPathCount
= CFArrayGetCount(keyPathArray
);
2179 _CFDictionarySetCapacity(objects
, keyPathCount
+ 1);
2181 for (CFIndex i
= 0; i
< keyPathCount
; i
++) {
2182 CFStringRef oneKey
= (CFStringRef
)CFArrayGetValueAtIndex(keyPathArray
, i
);
2183 SInt32 intValue
= CFStringGetIntValue(oneKey
);
2184 if ((intValue
== 0 && CFStringCompare(CFSTR("0"), oneKey
, 0) != kCFCompareEqualTo
) || intValue
== INT_MAX
|| intValue
== INT_MIN
) {
2185 // Treat as a string key into a dictionary
2186 result
= __CFBinaryPlistGetOffsetForValueFromDictionary3(databytes
, datalen
, valueOffset
, &trailer
, (CFTypeRef
)oneKey
, &keyOffset
, &valueOffset
, false, objects
);
2188 // Treat as integer index into an array
2189 result
= __CFBinaryPlistGetOffsetForValueFromArray2(databytes
, datalen
, valueOffset
, &trailer
, intValue
, &valueOffset
, objects
);
2197 // value could be null if the caller wanted to check for the existence of a key but not bother creating it
2198 if (result
&& value
) {
2199 CFPropertyListRef pl
;
2200 result
= __CFBinaryPlistCreateObject2(databytes
, datalen
, valueOffset
, &trailer
, allocator
, option
, objects
, NULL
, 0, &pl
);
2202 // caller's responsibility to release the created object
2207 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero
)) CFRelease(keyPathArray
);
2208 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero
)) CFRelease(objects
);
2210 // Try an XML property list
2211 // Note: This is currently not any more efficient than grabbing the whole thing. This could be improved in the future.
2212 CFPropertyListRef plist
= CFPropertyListCreateWithData(allocator
, data
, option
, NULL
, error
);
2213 CFPropertyListRef nextObject
= plist
;
2216 if (!(*error
) && plist
) {
2217 CFArrayRef keyPathArray
= CFStringCreateArrayBySeparatingStrings(kCFAllocatorSystemDefaultGCRefZero
, keyPath
, CFSTR(":"));
2218 for (CFIndex i
= 0; i
< CFArrayGetCount(keyPathArray
); i
++) {
2219 CFStringRef oneKey
= (CFStringRef
)CFArrayGetValueAtIndex(keyPathArray
, i
);
2220 SInt32 intValue
= CFStringGetIntValue(oneKey
);
2221 if (((intValue
== 0 && CFStringCompare(CFSTR("0"), oneKey
, 0) != kCFCompareEqualTo
) || intValue
== INT_MAX
|| intValue
== INT_MIN
) && CFGetTypeID((CFTypeRef
)nextObject
) == dicttype
) {
2222 // Treat as a string key into a dictionary
2223 nextObject
= (CFPropertyListRef
)CFDictionaryGetValue((CFDictionaryRef
)nextObject
, oneKey
);
2224 } else if (CFGetTypeID((CFTypeRef
)nextObject
) == arraytype
) {
2225 // Treat as integer index into an array
2226 nextObject
= (CFPropertyListRef
)CFArrayGetValueAtIndex((CFArrayRef
)nextObject
, intValue
);
2233 if (result
&& nextObject
&& value
) {
2234 *value
= nextObject
;
2235 // caller's responsibility to release the created object
2239 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero
)) CFRelease(keyPathArray
);
2241 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(plist
);
2247 CFTypeRef
_CFPropertyListCreateWithData(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFErrorRef
*error
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2249 CFStringEncoding encoding
;
2250 CFPropertyListRef plist
;
2252 if (!data
|| CFDataGetLength(data
) == 0) {
2254 *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Cannot parse a NULL or zero-length data"));
2260 __savePlistData(data
, option
);
2263 // Ignore the error from CFTryParseBinaryPlist -- if it doesn't work, we're going to try again anyway using the XML parser
2264 if (__CFTryParseBinaryPlist(allocator
, data
, option
, &plist
, NULL
)) {
2265 if (format
) *format
= kCFPropertyListBinaryFormat_v1_0
;
2269 // Use our own error variable here so we can check it against NULL later
2270 CFErrorRef subError
= NULL
;
2271 encoding
= encodingForXMLData(data
, &subError
); // 0 is an error return, NOT MacRoman.
2273 if (encoding
== 0) {
2274 // Couldn't find an encoding
2275 // Note that encodingForXMLData() will give us the right values for a standard plist, too.
2276 if (error
&& subError
== NULL
) {
2277 // encodingForXMLData didn't set an error, so we create a new one here
2278 *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Could not determine the encoding of the XML data"));
2279 } else if (error
&& subError
) {
2280 // give the caller the subError, they will release
2282 } else if (!error
&& subError
) {
2283 // Release the error
2284 CFRelease(subError
);
2289 CFStringRef xmlString
= CFStringCreateWithBytes(allocator
, CFDataGetBytePtr(data
), CFDataGetLength(data
), encoding
, true);
2290 if (NULL
== xmlString
&& (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard
) || _CFPropertyListAllowNonUTF8
)) { // conversion failed, probably because not in proper encoding
2291 // Call __CFStringCreateImmutableFunnel3() the same way CFStringCreateWithBytes() does, except with the addt'l flag
2292 if (encoding
== kCFStringEncodingUTF8
) xmlString
= __CFStringCreateImmutableFunnel3(allocator
, CFDataGetBytePtr(data
), CFDataGetLength(data
), kCFStringEncodingUTF8
, true, true, false, false, false, (CFAllocatorRef
)-1 /* ALLOCATORSFREEFUNC */, kCFStringEncodingLenientUTF8Conversion
);
2295 // Haven't done anything XML-specific to this point. However, the encoding we used to translate the bytes should be kept in mind; we used Unicode if the byte-order mark was present; UTF-8 otherwise. If the system encoding is not UTF-8 or some variant of 7-bit ASCII, we'll be in trouble.....
2296 plist
= _CFPropertyListCreateFromXMLStringError(allocator
, xmlString
, option
, error
, allowNewTypes
, format
);
2299 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(xmlString
);
2307 CFTypeRef
_CFPropertyListCreateFromXMLData(CFAllocatorRef allocator
, CFDataRef xmlData
, CFOptionFlags option
, CFStringRef
*errorString
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
2310 if (errorString
) *errorString
= NULL
;
2311 CFErrorRef error
= NULL
;
2312 result
= _CFPropertyListCreateWithData(allocator
, xmlData
, option
, &error
, allowNewTypes
, format
);
2313 if (error
&& errorString
) {
2314 *errorString
= __CFPropertyListCopyErrorDebugDescription(error
);
2316 if (error
) CFRelease(error
);
2320 CFPropertyListRef
CFPropertyListCreateWithData(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags options
, CFPropertyListFormat
*format
, CFErrorRef
*error
) {
2322 CFAssert1(data
!= NULL
, __kCFLogAssertion
, "%s(): NULL data not allowed", __PRETTY_FUNCTION__
);
2323 CFAssert2(options
== kCFPropertyListImmutable
|| options
== kCFPropertyListMutableContainers
|| options
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, options
);
2324 return _CFPropertyListCreateWithData(allocator
, data
, options
, error
, true, format
);
2327 CFPropertyListRef
CFPropertyListCreateFromXMLData(CFAllocatorRef allocator
, CFDataRef xmlData
, CFOptionFlags option
, CFStringRef
*errorString
) {
2329 if (errorString
) *errorString
= NULL
;
2330 CFErrorRef error
= NULL
;
2331 CFPropertyListRef result
= CFPropertyListCreateWithData(allocator
, xmlData
, option
, NULL
, &error
);
2332 if (error
&& errorString
) {
2333 *errorString
= __CFPropertyListCopyErrorDebugDescription(error
);
2335 if (error
) CFRelease(error
);
2339 CFDataRef
CFPropertyListCreateData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, CFPropertyListFormat format
, CFOptionFlags options
, CFErrorRef
*error
) {
2341 CFAssert1(format
!= kCFPropertyListOpenStepFormat
, __kCFLogAssertion
, "%s(): kCFPropertyListOpenStepFormat not supported for writing", __PRETTY_FUNCTION__
);
2342 CFAssert2(format
== kCFPropertyListXMLFormat_v1_0
|| format
== kCFPropertyListBinaryFormat_v1_0
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, format
);
2343 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
2344 __CFAssertIsPList(propertyList
);
2346 CFDataRef data
= NULL
;
2349 CFStringRef validErr
= NULL
;
2350 if (!_CFPropertyListIsValidWithErrorString(propertyList
, format
, &validErr
)) {
2351 CFLog(kCFLogLevelError
, CFSTR("Property list invalid for format: %d (%@)"), format
, validErr
);
2352 if (validErr
) CFRelease(validErr
);
2356 if (format
== kCFPropertyListOpenStepFormat
) {
2357 CFLog(kCFLogLevelError
, CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2359 } else if (format
== kCFPropertyListXMLFormat_v1_0
) {
2360 data
= _CFPropertyListCreateXMLData(allocator
, propertyList
, true);
2361 } else if (format
== kCFPropertyListBinaryFormat_v1_0
) {
2362 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2363 // TODO: Is it more efficient to create a stream here or just use a mutable data?
2364 CFWriteStreamRef stream
= CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorSystemDefault
, allocator
);
2365 CFWriteStreamOpen(stream
);
2366 CFIndex len
= CFPropertyListWrite(propertyList
, stream
, format
, options
, error
);
2368 data
= (CFDataRef
)CFWriteStreamCopyProperty(stream
, kCFStreamPropertyDataWritten
);
2370 CFWriteStreamClose(stream
);
2373 CFMutableDataRef dataForPlist
= CFDataCreateMutable(allocator
, 0);
2374 __CFBinaryPlistWrite(propertyList
, dataForPlist
, 0, options
, error
);
2375 return dataForPlist
;
2378 CFLog(kCFLogLevelError
, CFSTR("Unknown format option"));
2384 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2386 CFIndex
CFPropertyListWrite(CFPropertyListRef propertyList
, CFWriteStreamRef stream
, CFPropertyListFormat format
, CFOptionFlags options
, CFErrorRef
*error
) {
2388 CFAssert1(stream
!= NULL
, __kCFLogAssertion
, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__
);
2389 CFAssert1(format
!= kCFPropertyListOpenStepFormat
, __kCFLogAssertion
, "%s(): kCFPropertyListOpenStepFormat not supported for writing", __PRETTY_FUNCTION__
);
2390 CFAssert2(format
== kCFPropertyListXMLFormat_v1_0
|| format
== kCFPropertyListBinaryFormat_v1_0
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, format
);
2391 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
2392 __CFAssertIsPList(propertyList
);
2393 CFAssert1(CFWriteStreamGetTypeID() == CFGetTypeID(stream
), __kCFLogAssertion
, "%s(): stream argument is not a write stream", __PRETTY_FUNCTION__
);
2394 CFAssert1(kCFStreamStatusOpen
== CFWriteStreamGetStatus(stream
) || kCFStreamStatusWriting
== CFWriteStreamGetStatus(stream
), __kCFLogAssertion
, "%s(): stream is not open", __PRETTY_FUNCTION__
);
2396 CFStringRef validErr
= NULL
;
2397 if (!_CFPropertyListIsValidWithErrorString(propertyList
, format
, &validErr
)) {
2398 CFLog(kCFLogLevelError
, CFSTR("Property list invalid for format: %d (%@)"), format
, validErr
);
2399 if (validErr
) CFRelease(validErr
);
2402 if (format
== kCFPropertyListOpenStepFormat
) {
2403 CFLog(kCFLogLevelError
, CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2406 if (format
== kCFPropertyListXMLFormat_v1_0
) {
2407 CFDataRef data
= _CFPropertyListCreateXMLData(kCFAllocatorSystemDefault
, propertyList
, true);
2408 CFIndex len
= CFDataGetLength(data
);
2409 const uint8_t *ptr
= CFDataGetBytePtr(data
);
2411 CFIndex ret
= CFWriteStreamWrite(stream
, ptr
, len
);
2413 if (error
) *error
= __CFPropertyListCreateError(kCFPropertyListWriteStreamError
, CFSTR("Property list writing could not be completed because stream is full."));
2418 CFErrorRef underlyingError
= CFWriteStreamCopyError(stream
);
2419 if (underlyingError
) {
2421 // Wrap the error from CFWriteStreamCopy in a new error
2422 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2423 CFDictionarySetValue(userInfo
, kCFErrorDebugDescriptionKey
, CFSTR("Property list writing could not be completed because the stream had an unknown error."));
2424 CFDictionarySetValue(userInfo
, kCFErrorUnderlyingErrorKey
, underlyingError
);
2425 *error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, kCFPropertyListWriteStreamError
, userInfo
);
2426 CFRelease(userInfo
);
2428 CFRelease(underlyingError
);
2436 len
= CFDataGetLength(data
);
2440 if (format
== kCFPropertyListBinaryFormat_v1_0
) {
2441 CFIndex len
= __CFBinaryPlistWrite(propertyList
, stream
, 0, options
, error
);
2444 CFLog(kCFLogLevelError
, CFSTR("Unknown format option"));
2448 CFIndex
CFPropertyListWriteToStream(CFPropertyListRef propertyList
, CFWriteStreamRef stream
, CFPropertyListFormat format
, CFStringRef
*errorString
) {
2450 if (errorString
) *errorString
= NULL
;
2451 CFErrorRef error
= NULL
;
2453 // For backwards compatibility, we check the format parameter up front since these do not have CFError counterparts in the newer API
2454 CFStringRef validErr
= NULL
;
2455 if (!_CFPropertyListIsValidWithErrorString(propertyList
, format
, &validErr
)) {
2456 if (errorString
) *errorString
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("Property list invalid for format (%@)"), validErr
);
2457 if (validErr
) CFRelease(validErr
);
2460 if (format
== kCFPropertyListOpenStepFormat
) {
2461 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2464 if (format
!= kCFPropertyListBinaryFormat_v1_0
&& format
!= kCFPropertyListXMLFormat_v1_0
) {
2465 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Unknown format option"));
2469 CFIndex result
= CFPropertyListWrite(propertyList
, stream
, format
, 0, &error
);
2470 if (error
&& errorString
) {
2471 *errorString
= __CFPropertyListCopyErrorDebugDescription(error
);
2473 if (error
) CFRelease(error
);
2477 static void __CFConvertReadStreamToBytes(CFReadStreamRef stream
, CFIndex max
, uint8_t **buffer
, CFIndex
*length
, CFErrorRef
*error
) {
2478 int32_t buflen
= 0, bufsize
= 0, retlen
;
2479 uint8_t *buf
= NULL
, sbuf
[8192];
2481 retlen
= CFReadStreamRead(stream
, sbuf
, __CFMin(8192, max
));
2486 if (retlen
< 0 && error
) {
2487 // Copy the error out
2488 *error
= CFReadStreamCopyError(stream
);
2493 if (bufsize
< buflen
+ retlen
) {
2494 if (bufsize
< 256 * 1024) {
2496 } else if (bufsize
< 16 * 1024 * 1024) {
2499 // once in this stage, this will be really slow
2500 // and really potentially fragment memory
2501 bufsize
+= 256 * 1024;
2503 if (bufsize
< buflen
+ retlen
) bufsize
= buflen
+ retlen
;
2504 buf
= (uint8_t *)CFAllocatorReallocate(kCFAllocatorSystemDefault
, buf
, bufsize
, 0);
2507 memmove(buf
+ buflen
, sbuf
, retlen
);
2518 CFPropertyListRef
CFPropertyListCreateWithStream(CFAllocatorRef allocator
, CFReadStreamRef stream
, CFIndex streamLength
, CFOptionFlags mutabilityOption
, CFPropertyListFormat
*format
, CFErrorRef
*error
) {
2520 CFPropertyListRef pl
;
2523 uint8_t *buffer
= NULL
;
2524 CFAssert1(stream
!= NULL
, __kCFLogAssertion
, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__
);
2525 CFAssert1(CFReadStreamGetTypeID() == CFGetTypeID(stream
), __kCFLogAssertion
, "%s(): stream argument is not a read stream", __PRETTY_FUNCTION__
);
2526 CFAssert1(kCFStreamStatusOpen
== CFReadStreamGetStatus(stream
) || kCFStreamStatusReading
== CFReadStreamGetStatus(stream
), __kCFLogAssertion
, "%s(): stream is not open", __PRETTY_FUNCTION__
);
2527 CFAssert2(mutabilityOption
== kCFPropertyListImmutable
|| mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, mutabilityOption
);
2529 if (0 == streamLength
) streamLength
= LONG_MAX
;
2530 CFErrorRef underlyingError
= NULL
;
2531 __CFConvertReadStreamToBytes(stream
, streamLength
, &buffer
, &buflen
, &underlyingError
);
2532 if (underlyingError
) {
2534 // Wrap the error from CFReadStream in a new error in the cocoa domain
2535 CFMutableDictionaryRef userInfo
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFCopyStringDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2536 CFDictionarySetValue(userInfo
, kCFErrorDebugDescriptionKey
, CFSTR("Property list reading could not be completed because the stream had an unknown error."));
2537 CFDictionarySetValue(userInfo
, kCFErrorUnderlyingErrorKey
, underlyingError
);
2538 *error
= CFErrorCreate(kCFAllocatorSystemDefault
, kCFErrorDomainCocoa
, kCFPropertyListReadStreamError
, userInfo
);
2539 CFRelease(userInfo
);
2541 CFRelease(underlyingError
);
2545 if (!buffer
|| buflen
< 6) {
2546 if (buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buffer
);
2547 if (error
) *error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("stream had too few bytes"));
2550 data
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, buffer
, buflen
, kCFAllocatorSystemDefault
);
2551 pl
= _CFPropertyListCreateWithData(allocator
, data
, mutabilityOption
, error
, true, format
);
2556 CFPropertyListRef
CFPropertyListCreateFromStream(CFAllocatorRef allocator
, CFReadStreamRef stream
, CFIndex length
, CFOptionFlags mutabilityOption
, CFPropertyListFormat
*format
, CFStringRef
*errorString
) {
2558 if (errorString
) *errorString
= NULL
;
2559 CFErrorRef error
= NULL
;
2560 CFPropertyListRef result
= CFPropertyListCreateWithStream(allocator
, stream
, length
, mutabilityOption
, format
, &error
);
2561 if (error
&& errorString
) {
2562 *errorString
= __CFPropertyListCopyErrorDebugDescription(error
);
2564 if (error
) CFRelease(error
);
2568 #endif //DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2570 // ========================================================================
2573 // Old NeXT-style property lists
2576 static CFTypeRef
parsePlistObject(_CFXMLPlistParseInfo
*pInfo
, bool requireObject
);
2578 #define isValidUnquotedStringCharacter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z') || ((x) >= '0' && (x) <= '9') || (x) == '_' || (x) == '$' || (x) == '/' || (x) == ':' || (x) == '.' || (x) == '-')
2580 // Returns true if the advance found something before the end of the buffer, false otherwise
2581 static Boolean
advanceToNonSpace(_CFXMLPlistParseInfo
*pInfo
) {
2583 while (pInfo
->curr
< pInfo
->end
) {
2584 ch2
= *(pInfo
->curr
);
2586 if (ch2
>= 9 && ch2
<= 0x0d) continue; // tab, newline, vt, form feed, carriage return
2587 if (ch2
== ' ' || ch2
== 0x2028 || ch2
== 0x2029) continue; // space and Unicode line sep, para sep
2589 if (pInfo
->curr
>= pInfo
->end
) {
2590 // whoops; back up and return
2593 } else if (*(pInfo
->curr
) == '/') {
2595 while (pInfo
->curr
< pInfo
->end
) { // go to end of comment line
2596 UniChar ch3
= *(pInfo
->curr
);
2597 if (ch3
== '\n' || ch3
== '\r' || ch3
== 0x2028 || ch3
== 0x2029) break;
2600 } else if (*(pInfo
->curr
) == '*') { // handle /* ... */
2602 while (pInfo
->curr
< pInfo
->end
) {
2603 ch2
= *(pInfo
->curr
);
2605 if (ch2
== '*' && pInfo
->curr
< pInfo
->end
&& *(pInfo
->curr
) == '/') {
2606 pInfo
->curr
++; // advance past the '/'
2622 static UniChar
getSlashedChar(_CFXMLPlistParseInfo
*pInfo
) {
2623 UniChar ch
= *(pInfo
->curr
);
2634 uint8_t num
= ch
- '0';
2636 CFIndex usedCharLen
;
2637 /* three digits maximum to avoid reading \000 followed by 5 as \5 ! */
2638 if ((ch
= *(pInfo
->curr
)) >= '0' && ch
<= '7') { // we use in this test the fact that the buffer is zero-terminated
2640 num
= (num
<< 3) + ch
- '0';
2641 if ((pInfo
->curr
< pInfo
->end
) && (ch
= *(pInfo
->curr
)) >= '0' && ch
<= '7') {
2643 num
= (num
<< 3) + ch
- '0';
2646 CFStringEncodingBytesToUnicode(kCFStringEncodingNextStepLatin
, 0, &num
, sizeof(uint8_t), NULL
, &result
, 1, &usedCharLen
);
2647 return (usedCharLen
== 1) ? result
: 0;
2650 unsigned num
= 0, numDigits
= 4; /* Parse four digits */
2651 while (pInfo
->curr
< pInfo
->end
&& numDigits
--) {
2652 if (((ch
= *(pInfo
->curr
)) < 128) && isxdigit(ch
)) {
2654 num
= (num
<< 4) + ((ch
<= '9') ? (ch
- '0') : ((ch
<= 'F') ? (ch
- 'A' + 10) : (ch
- 'a' + 10)));
2659 case 'a': return '\a'; // Note: the meaning of '\a' varies with -traditional to gcc
2660 case 'b': return '\b';
2661 case 'f': return '\f';
2662 case 'n': return '\n';
2663 case 'r': return '\r';
2664 case 't': return '\t';
2665 case 'v': return '\v';
2666 case '"': return '\"';
2667 case '\n': return '\n';
2672 static CFStringRef
parseQuotedPlistString(_CFXMLPlistParseInfo
*pInfo
, UniChar quote
) {
2673 CFMutableStringRef str
= NULL
;
2674 const UniChar
*startMark
= pInfo
->curr
;
2675 const UniChar
*mark
= pInfo
->curr
;
2676 while (pInfo
->curr
< pInfo
->end
) {
2677 UniChar ch
= *(pInfo
->curr
);
2678 if (ch
== quote
) break;
2680 _catFromMarkToBuf(mark
, pInfo
->curr
, &str
, pInfo
);
2682 ch
= getSlashedChar(pInfo
);
2683 CFStringAppendCharacters(str
, &ch
, 1);
2686 // Note that the original NSParser code was much more complex at this point, but it had to deal with 8-bit characters in a non-UniChar stream. We always have UniChar (we translated the data by the system encoding at the very beginning, hopefully), so this is safe.
2690 if (pInfo
->end
<= pInfo
->curr
) {
2691 __CFPListRelease(str
, pInfo
);
2692 pInfo
->curr
= startMark
;
2693 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unterminated quoted string starting on line %d"), lineNumber(pInfo
));
2697 if (pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
2698 _catFromMarkToBuf(mark
, pInfo
->curr
, &str
, pInfo
);
2700 str
= (CFMutableStringRef
)_uniqueStringForCharacters(pInfo
, mark
, pInfo
->curr
-mark
);
2703 if (mark
!= pInfo
->curr
) {
2704 _catFromMarkToBuf(mark
, pInfo
->curr
, &str
, pInfo
);
2706 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
2707 CFStringRef uniqueString
= _uniqueStringForString(pInfo
, str
);
2708 __CFPListRelease(str
, pInfo
);
2709 str
= (CFMutableStringRef
)uniqueString
;
2712 pInfo
->curr
++; // Advance past the quote character before returning.
2714 CFRelease(pInfo
->error
);
2715 pInfo
->error
= NULL
;
2720 static CFStringRef
parseUnquotedPlistString(_CFXMLPlistParseInfo
*pInfo
) {
2721 const UniChar
*mark
= pInfo
->curr
;
2722 while (pInfo
->curr
< pInfo
->end
) {
2723 UniChar ch
= *pInfo
->curr
;
2724 if (isValidUnquotedStringCharacter(ch
))
2728 if (pInfo
->curr
!= mark
) {
2729 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
2730 CFStringRef str
= _uniqueStringForCharacters(pInfo
, mark
, pInfo
->curr
-mark
);
2733 CFMutableStringRef str
= CFStringCreateMutable(pInfo
->allocator
, 0);
2734 CFStringAppendCharacters(str
, mark
, pInfo
->curr
- mark
);
2738 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected EOF"));
2742 static CFStringRef
parsePlistString(_CFXMLPlistParseInfo
*pInfo
, bool requireObject
) {
2744 Boolean foundChar
= advanceToNonSpace(pInfo
);
2746 if (requireObject
) {
2747 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected EOF while parsing string"));
2751 ch
= *(pInfo
->curr
);
2752 if (ch
== '\'' || ch
== '\"') {
2754 return parseQuotedPlistString(pInfo
, ch
);
2755 } else if (isValidUnquotedStringCharacter(ch
)) {
2756 return parseUnquotedPlistString(pInfo
);
2758 if (requireObject
) {
2759 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Invalid string character at line %d"), lineNumber(pInfo
));
2765 static CFTypeRef
parsePlistArray(_CFXMLPlistParseInfo
*pInfo
) {
2766 CFMutableArrayRef array
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
2767 CFTypeRef tmp
= parsePlistObject(pInfo
, false);
2770 CFArrayAppendValue(array
, tmp
);
2771 __CFPListRelease(tmp
, pInfo
);
2772 foundChar
= advanceToNonSpace(pInfo
);
2774 __CFPListRelease(array
, pInfo
);
2775 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Expected ',' for array at line %d"), lineNumber(pInfo
));
2778 if (*pInfo
->curr
!= ',') {
2782 tmp
= parsePlistObject(pInfo
, false);
2785 foundChar
= advanceToNonSpace(pInfo
);
2786 if (!foundChar
|| *pInfo
->curr
!= ')') {
2787 __CFPListRelease(array
, pInfo
);
2788 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Expected terminating ')' for array at line %d"), lineNumber(pInfo
));
2792 CFRelease(pInfo
->error
);
2793 pInfo
->error
= NULL
;
2799 static CFDictionaryRef
parsePlistDictContent(_CFXMLPlistParseInfo
*pInfo
) {
2800 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2801 CFStringRef key
= NULL
;
2802 Boolean failedParse
= false;
2803 key
= parsePlistString(pInfo
, false);
2806 Boolean foundChar
= advanceToNonSpace(pInfo
);
2808 CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListCreateFromXMLData(): Unexpected end of file. Missing semicolon or value in dictionary."));
2810 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Missing ';' on line %d"), lineNumber(pInfo
));
2814 if (*pInfo
->curr
== ';') {
2815 /* This is a strings file using the shortcut format */
2816 /* although this check here really applies to all plists. */
2817 value
= CFRetain(key
);
2818 } else if (*pInfo
->curr
== '=') {
2820 value
= parsePlistObject(pInfo
, true);
2826 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected ';' or '=' after key at line %d"), lineNumber(pInfo
));
2830 CFDictionarySetValue(dict
, key
, value
);
2831 __CFPListRelease(key
, pInfo
);
2833 __CFPListRelease(value
, pInfo
);
2835 foundChar
= advanceToNonSpace(pInfo
);
2836 if (foundChar
&& *pInfo
->curr
== ';') {
2838 key
= parsePlistString(pInfo
, false);
2839 } else if (true || !foundChar
) {
2840 CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListCreateFromXMLData(): Old-style plist parser: missing semicolon in dictionary."));
2842 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Missing ';' on line %d"), lineNumber(pInfo
));
2847 __CFPListRelease(key
, pInfo
);
2848 __CFPListRelease(dict
, pInfo
);
2852 CFRelease(pInfo
->error
);
2853 pInfo
->error
= NULL
;
2858 static CFTypeRef
parsePlistDict(_CFXMLPlistParseInfo
*pInfo
) {
2859 CFDictionaryRef dict
= parsePlistDictContent(pInfo
);
2860 if (!dict
) return NULL
;
2861 Boolean foundChar
= advanceToNonSpace(pInfo
);
2862 if (!foundChar
|| *pInfo
->curr
!= '}') {
2863 __CFPListRelease(dict
, pInfo
);
2864 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Expected terminating '}' for dictionary at line %d"), lineNumber(pInfo
));
2871 CF_INLINE
unsigned char fromHexDigit(unsigned char ch
) {
2872 if (isdigit(ch
)) return ch
- '0';
2873 if ((ch
>= 'a') && (ch
<= 'f')) return ch
- 'a' + 10;
2874 if ((ch
>= 'A') && (ch
<= 'F')) return ch
- 'A' + 10;
2875 return 0xff; // Just choose a large number for the error code
2878 /* Gets up to bytesSize bytes from a plist data. Returns number of bytes actually read. Leaves cursor at first non-space, non-hex character.
2879 -1 is returned for unexpected char, -2 for uneven number of hex digits
2881 static int getDataBytes(_CFXMLPlistParseInfo
*pInfo
, unsigned char *bytes
, int bytesSize
) {
2882 int numBytesRead
= 0;
2883 while ((pInfo
->curr
< pInfo
->end
) && (numBytesRead
< bytesSize
)) {
2885 UniChar ch1
= *pInfo
->curr
;
2886 if (ch1
== '>') return numBytesRead
; // Meaning we're done
2887 first
= fromHexDigit((unsigned char)ch1
);
2888 if (first
!= 0xff) { // If the first char is a hex, then try to read a second hex
2890 if (pInfo
->curr
>= pInfo
->end
) return -2; // Error: uneven number of hex digits
2891 UniChar ch2
= *pInfo
->curr
;
2892 second
= fromHexDigit((unsigned char)ch2
);
2893 if (second
== 0xff) return -2; // Error: uneven number of hex digits
2894 bytes
[numBytesRead
++] = (first
<< 4) + second
;
2896 } else if (ch1
== ' ' || ch1
== '\n' || ch1
== '\t' || ch1
== '\r' || ch1
== 0x2028 || ch1
== 0x2029) {
2899 return -1; // Error: unexpected character
2902 return numBytesRead
; // This does likely mean we didn't encounter a '>', but we'll let the caller deal with that
2905 #define numBytes 400
2906 static CFTypeRef
parsePlistData(_CFXMLPlistParseInfo
*pInfo
) {
2907 CFMutableDataRef result
= CFDataCreateMutable(pInfo
->allocator
, 0);
2909 // Read hex bytes and append them to result
2911 unsigned char bytes
[numBytes
];
2912 int numBytesRead
= getDataBytes(pInfo
, bytes
, numBytes
);
2913 if (numBytesRead
< 0) {
2914 __CFPListRelease(result
, pInfo
);
2915 switch (numBytesRead
) {
2917 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Malformed data byte group at line %d; uneven length"), lineNumber(pInfo
));
2920 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Malformed data byte group at line %d; invalid hex"), lineNumber(pInfo
));
2925 if (numBytesRead
== 0) break;
2926 CFDataAppendBytes(result
, bytes
, numBytesRead
);
2930 CFRelease(pInfo
->error
);
2931 pInfo
->error
= NULL
;
2934 if (*(pInfo
->curr
) == '>') {
2935 pInfo
->curr
++; // Move past '>'
2938 __CFPListRelease(result
, pInfo
);
2939 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Expected terminating '>' for data at line %d"), lineNumber(pInfo
));
2945 // Returned object is retained; caller must free.
2946 static CFTypeRef
parsePlistObject(_CFXMLPlistParseInfo
*pInfo
, bool requireObject
) {
2948 Boolean foundChar
= advanceToNonSpace(pInfo
);
2950 if (requireObject
) {
2951 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected EOF while parsing plist"));
2955 ch
= *(pInfo
->curr
);
2958 return parsePlistDict(pInfo
);
2959 } else if (ch
== '(') {
2960 return parsePlistArray(pInfo
);
2961 } else if (ch
== '<') {
2962 return parsePlistData(pInfo
);
2963 } else if (ch
== '\'' || ch
== '\"') {
2964 return parseQuotedPlistString(pInfo
, ch
);
2965 } else if (isValidUnquotedStringCharacter(ch
)) {
2967 return parseUnquotedPlistString(pInfo
);
2969 pInfo
->curr
--; // Must back off the charcter we just read
2970 if (requireObject
) {
2971 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected character '0x%x' at line %d"), ch
, lineNumber(pInfo
));
2977 static CFTypeRef
parseOldStylePropertyListOrStringsFile(_CFXMLPlistParseInfo
*pInfo
) {
2978 const UniChar
*begin
= pInfo
->curr
;
2980 Boolean foundChar
= advanceToNonSpace(pInfo
);
2981 // A file consisting only of whitespace (or empty) is now defined to be an empty dictionary
2982 if (!foundChar
) return CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2983 result
= parsePlistObject(pInfo
, true);
2984 foundChar
= advanceToNonSpace(pInfo
);
2985 if (!foundChar
) return result
;
2986 if (!result
) return NULL
;
2987 if (CFGetTypeID(result
) != stringtype
) {
2988 __CFPListRelease(result
, pInfo
);
2989 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Junk after plist at line %d"), lineNumber(pInfo
));
2992 __CFPListRelease(result
, pInfo
);
2993 // Check for a strings file (looks like a dictionary without the opening/closing curly braces)
2994 pInfo
->curr
= begin
;
2995 return parsePlistDictContent(pInfo
);
2998 #undef isValidUnquotedStringCharacter
3000 static CFArrayRef
_arrayDeepImmutableCopy(CFAllocatorRef allocator
, CFArrayRef array
, CFOptionFlags mutabilityOption
) {
3001 CFArrayRef result
= NULL
;
3002 CFIndex i
, c
= CFArrayGetCount(array
);
3004 result
= CFArrayCreate(allocator
, NULL
, 0, &kCFTypeArrayCallBacks
);
3006 new_cftype_array(values
, c
);
3007 CFArrayGetValues(array
, CFRangeMake(0, c
), values
);
3008 for (i
= 0; i
< c
; i
++) {
3009 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, values
[i
], mutabilityOption
);
3010 if (newValue
== NULL
) {
3013 __CFAssignWithWriteBarrier((void **)values
+ i
, (void *)newValue
);
3015 result
= (i
== c
) ? CFArrayCreate(allocator
, values
, c
, &kCFTypeArrayCallBacks
) : NULL
;
3017 if (!_CFAllocatorIsGCRefZero(allocator
)) {
3018 for (i
= 0; i
< c
; i
++) CFRelease(values
[i
]);
3020 free_cftype_array(values
);
3025 static CFMutableArrayRef
_arrayDeepMutableCopy(CFAllocatorRef allocator
, CFArrayRef array
, CFOptionFlags mutabilityOption
) {
3026 CFIndex i
, c
= CFArrayGetCount(array
);
3027 CFMutableArrayRef result
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
3029 for (i
= 0; i
< c
; i
++) {
3030 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, CFArrayGetValueAtIndex(array
, i
), mutabilityOption
);
3031 if (!newValue
) break;
3032 CFArrayAppendValue(result
, newValue
);
3033 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(newValue
);
3036 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(result
);
3043 CFPropertyListRef
CFPropertyListCreateDeepCopy(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, CFOptionFlags mutabilityOption
) {
3045 CFPropertyListRef result
= NULL
;
3046 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): cannot copy a NULL property list", __PRETTY_FUNCTION__
);
3047 __CFAssertIsPList(propertyList
);
3048 CFAssert2(mutabilityOption
== kCFPropertyListImmutable
|| mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, mutabilityOption
);
3049 if (!CFPropertyListIsValid(propertyList
, kCFPropertyListBinaryFormat_v1_0
)) return NULL
;
3051 CFTypeID typeID
= CFGetTypeID(propertyList
);
3052 if (typeID
== dicttype
) {
3053 CFDictionaryRef dict
= (CFDictionaryRef
)propertyList
;
3054 Boolean isMutable
= (mutabilityOption
!= kCFPropertyListImmutable
);
3055 CFIndex count
= CFDictionaryGetCount(dict
);
3057 result
= isMutable
? CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
): CFDictionaryCreate(allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3059 new_cftype_array(keys
, 2 * count
);
3062 values
= keys
+count
;
3063 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
3064 for (i
= 0; i
< count
; i
++) {
3065 CFTypeRef newKey
= CFStringCreateCopy(allocator
, (CFStringRef
)keys
[i
]);
3066 if (newKey
== NULL
) {
3069 __CFAssignWithWriteBarrier((void **)keys
+ i
, (void *)newKey
);
3070 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, values
[i
], mutabilityOption
);
3071 if (newValue
== NULL
) {
3072 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(keys
[i
]);
3075 __CFAssignWithWriteBarrier((void **)values
+ i
, (void *)newValue
);
3078 result
= isMutable
? CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
) : CFDictionaryCreate(allocator
, keys
, values
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3079 for (i
= 0; i
< count
; i
++) {
3081 CFDictionarySetValue((CFMutableDictionaryRef
)result
, keys
[i
], values
[i
]);
3083 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(keys
[i
]);
3084 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(values
[i
]);
3089 for (i
= 0; i
< count
; i
++) {
3090 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(keys
[i
]);
3091 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(values
[i
]);
3094 free_cftype_array(keys
);
3096 } else if (typeID
== arraytype
) {
3097 if (mutabilityOption
== kCFPropertyListImmutable
) {
3098 result
= _arrayDeepImmutableCopy(allocator
, (CFArrayRef
)propertyList
, mutabilityOption
);
3100 result
= _arrayDeepMutableCopy(allocator
, (CFArrayRef
)propertyList
, mutabilityOption
);
3102 } else if (typeID
== datatype
) {
3103 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
3104 result
= CFDataCreateMutableCopy(allocator
, 0, (CFDataRef
)propertyList
);
3106 result
= CFDataCreateCopy(allocator
, (CFDataRef
)propertyList
);
3108 } else if (typeID
== numbertype
) {
3109 // Warning - this will break if byteSize is ever greater than 32
3111 CFNumberType numType
= CFNumberGetType((CFNumberRef
)propertyList
);
3112 CFNumberGetValue((CFNumberRef
)propertyList
, numType
, (void *)bytes
);
3113 result
= CFNumberCreate(allocator
, numType
, (void *)bytes
);
3114 } else if (typeID
== booltype
) {
3115 // Booleans are immutable & shared instances
3116 CFRetain(propertyList
);
3117 result
= propertyList
;
3118 } else if (typeID
== datetype
) {
3119 // Dates are immutable
3120 result
= CFDateCreate(allocator
, CFDateGetAbsoluteTime((CFDateRef
)propertyList
));
3121 } else if (typeID
== stringtype
) {
3122 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
3123 result
= CFStringCreateMutableCopy(allocator
, 0, (CFStringRef
)propertyList
);
3125 result
= CFStringCreateCopy(allocator
, (CFStringRef
)propertyList
);
3128 CFAssert2(false, __kCFLogAssertion
, "%s(): %p is not a property list type", __PRETTY_FUNCTION__
, propertyList
);