2 * Copyright (c) 2008 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@
24 Copyright 1999-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
28 #include <CoreFoundation/CFPropertyList.h>
29 #include <CoreFoundation/CFDate.h>
30 #include <CoreFoundation/CFNumber.h>
31 #include <CoreFoundation/CFSet.h>
33 #include "CFStringEncodingConverter.h"
34 #include "CFInternal.h"
35 #include <CoreFoundation/CFStream.h>
36 #include <CoreFoundation/CFCalendar.h>
43 #if DEPLOYMENT_TARGET_MACOSX
44 #include <CoreFoundation/CFPreferences.h>
45 #include <mach-o/dyld.h>
49 __private_extern__
bool allowMissingSemi
= false;
51 // Should move this somewhere else
52 intptr_t _CFDoOperation(intptr_t code
, intptr_t subcode1
, intptr_t subcode2
) {
54 case 15317: allowMissingSemi
= subcode1
? true : false; break;
73 #define PLIST_TAG_LENGTH 5
74 #define ARRAY_TAG_LENGTH 5
75 #define DICT_TAG_LENGTH 4
76 #define KEY_TAG_LENGTH 3
77 #define STRING_TAG_LENGTH 6
78 #define DATA_TAG_LENGTH 4
79 #define DATE_TAG_LENGTH 4
80 #define REAL_TAG_LENGTH 4
81 #define INTEGER_TAG_LENGTH 7
82 #define TRUE_TAG_LENGTH 4
83 #define FALSE_TAG_LENGTH 5
84 #define DOCTYPE_TAG_LENGTH 7
85 #define CDSECT_TAG_LENGTH 9
87 // don't allow _CFKeyedArchiverUID here
88 #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);
90 static bool __CFPropertyListIsValidAux(CFPropertyListRef plist
, bool recursive
, CFMutableSetRef set
, CFPropertyListFormat format
);
92 static CFTypeID stringtype
= -1, datatype
= -1, numbertype
= -1, datetype
= -1;
93 static CFTypeID booltype
= -1, nulltype
= -1, dicttype
= -1, arraytype
= -1, settype
= -1;
95 static void initStatics() {
96 if ((CFTypeID
)-1 == stringtype
) {
97 stringtype
= CFStringGetTypeID();
99 if ((CFTypeID
)-1 == datatype
) {
100 datatype
= CFDataGetTypeID();
102 if ((CFTypeID
)-1 == numbertype
) {
103 numbertype
= CFNumberGetTypeID();
105 if ((CFTypeID
)-1 == booltype
) {
106 booltype
= CFBooleanGetTypeID();
108 if ((CFTypeID
)-1 == datetype
) {
109 datetype
= CFDateGetTypeID();
111 if ((CFTypeID
)-1 == dicttype
) {
112 dicttype
= CFDictionaryGetTypeID();
114 if ((CFTypeID
)-1 == arraytype
) {
115 arraytype
= CFArrayGetTypeID();
117 if ((CFTypeID
)-1 == settype
) {
118 settype
= CFSetGetTypeID();
120 if ((CFTypeID
)-1 == nulltype
) {
121 nulltype
= CFNullGetTypeID();
128 CFPropertyListFormat format
;
131 static void __CFPropertyListIsArrayPlistAux(const void *value
, void *context
) {
132 struct context
*ctx
= (struct context
*)context
;
133 if (!ctx
->answer
) return;
135 if (!value
) CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListIsValid(): property list arrays cannot contain NULL"));
137 ctx
->answer
= value
&& __CFPropertyListIsValidAux(value
, true, ctx
->set
, ctx
->format
);
140 static void __CFPropertyListIsDictPlistAux(const void *key
, const void *value
, void *context
) {
141 struct context
*ctx
= (struct context
*)context
;
142 if (!ctx
->answer
) return;
144 if (!key
) CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListIsValid(): property list dictionaries cannot contain NULL keys"));
145 if (!value
) CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListIsValid(): property list dictionaries cannot contain NULL values"));
146 if (stringtype
!= CFGetTypeID(key
)) {
147 CFStringRef desc
= CFCopyTypeIDDescription(CFGetTypeID(key
));
148 CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListIsValid(): property list dictionaries may only have keys which are CFStrings, not '%@'"), desc
);
152 ctx
->answer
= key
&& value
&& (stringtype
== CFGetTypeID(key
)) && __CFPropertyListIsValidAux(value
, true, ctx
->set
, ctx
->format
);
155 static bool __CFPropertyListIsValidAux(CFPropertyListRef plist
, bool recursive
, CFMutableSetRef set
, CFPropertyListFormat format
) {
158 if (!plist
) CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListIsValid(): property lists cannot contain NULL"));
160 if (!plist
) return false;
161 type
= CFGetTypeID(plist
);
162 if (stringtype
== type
) return true;
163 if (datatype
== type
) return true;
164 if (kCFPropertyListOpenStepFormat
!= format
) {
165 if (booltype
== type
) return true;
166 if (numbertype
== type
) return true;
167 if (datetype
== type
) return true;
168 #if DEPLOYMENT_TARGET_MACOSX
169 if (_CFKeyedArchiverUIDGetTypeID() == type
) return true;
172 if (!recursive
&& arraytype
== type
) return true;
173 if (!recursive
&& dicttype
== type
) return true;
174 // at any one invocation of this function, set should contain the objects in the "path" down to this object
176 if (CFSetContainsValue(set
, plist
)) CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListIsValid(): property lists cannot contain recursive container references"));
178 if (CFSetContainsValue(set
, plist
)) return false;
179 if (arraytype
== type
) {
180 struct context ctx
= {true, set
, format
};
181 CFSetAddValue(set
, plist
);
182 CFArrayApplyFunction((CFArrayRef
)plist
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
)plist
)), __CFPropertyListIsArrayPlistAux
, &ctx
);
183 CFSetRemoveValue(set
, plist
);
186 if (dicttype
== type
) {
187 struct context ctx
= {true, set
, format
};
188 CFSetAddValue(set
, plist
);
189 CFDictionaryApplyFunction((CFDictionaryRef
)plist
, __CFPropertyListIsDictPlistAux
, &ctx
);
190 CFSetRemoveValue(set
, plist
);
195 CFStringRef desc
= CFCopyTypeIDDescription(type
);
196 CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListIsValid(): property lists cannot contain objects of type '%@'"), desc
);
203 Boolean
CFPropertyListIsValid(CFPropertyListRef plist
, CFPropertyListFormat format
) {
205 CFAssert1(plist
!= NULL
, __kCFLogAssertion
, "%s(): NULL is not a property list", __PRETTY_FUNCTION__
);
206 CFMutableSetRef set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
207 bool result
= __CFPropertyListIsValidAux(plist
, true, set
, format
);
212 static const UniChar CFXMLPlistTags
[13][10]= {
213 {'p', 'l', 'i', 's', 't', '\0', '\0', '\0', '\0', '\0'},
214 {'a', 'r', 'r', 'a', 'y', '\0', '\0', '\0', '\0', '\0'},
215 {'d', 'i', 'c', 't', '\0', '\0', '\0', '\0', '\0', '\0'},
216 {'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
217 {'s', 't', 'r', 'i', 'n', 'g', '\0', '\0', '\0', '\0'},
218 {'d', 'a', 't', 'a', '\0', '\0', '\0', '\0', '\0', '\0'},
219 {'d', 'a', 't', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
220 {'r', 'e', 'a', 'l', '\0', '\0', '\0', '\0', '\0', '\0'},
221 {'i', 'n', 't', 'e', 'g', 'e', 'r', '\0', '\0', '\0'},
222 {'t', 'r', 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
223 {'f', 'a', 'l', 's', 'e', '\0', '\0', '\0', '\0', '\0'},
224 {'D', 'O', 'C', 'T', 'Y', 'P', 'E', '\0', '\0', '\0'},
225 {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[', '\0'}
229 const UniChar
*begin
; // first character of the XML to be parsed
230 const UniChar
*curr
; // current parse location
231 const UniChar
*end
; // the first character _after_ the end of the XML
232 CFStringRef errorString
;
233 CFAllocatorRef allocator
;
234 UInt32 mutabilityOption
;
235 CFMutableSetRef stringSet
; // set of all strings involved in this parse; allows us to share non-mutable strings in the returned plist
236 CFMutableStringRef tmpString
; // Mutable string with external characters that functions can feel free to use as temporary storage as the parse progresses
237 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)
239 } _CFXMLPlistParseInfo
;
241 static CFTypeRef
parseOldStylePropertyListOrStringsFile(_CFXMLPlistParseInfo
*pInfo
);
245 // 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.
247 // Null-terminated, ASCII or UTF8 string
249 static void _plistAppendUTF8CString(CFMutableDataRef mData
, const char *cString
) {
250 CFDataAppendBytes (mData
, (const UInt8
*)cString
, strlen(cString
));
255 static void _plistAppendCharacters(CFMutableDataRef mData
, const UniChar
*chars
, CFIndex length
) {
258 do { // Flush out ASCII chars, BUFLEN at a time
260 UInt8 buf
[BUFLEN
], *bufPtr
= buf
;
262 while (cnt
< length
&& (cnt
- curLoc
< BUFLEN
) && (chars
[cnt
] < 128)) *bufPtr
++ = (UInt8
)(chars
[cnt
++]);
263 if (cnt
> curLoc
) { // Flush any ASCII bytes
264 CFDataAppendBytes(mData
, buf
, cnt
- curLoc
);
267 } while (curLoc
< length
&& (chars
[curLoc
] < 128)); // We will exit out of here when we run out of chars or hit a non-ASCII char
269 if (curLoc
< length
) { // Now deal with non-ASCII chars
270 CFDataRef data
= NULL
;
271 CFStringRef str
= NULL
;
272 if ((str
= CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault
, chars
+ curLoc
, length
- curLoc
, kCFAllocatorNull
))) {
273 if ((data
= CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
, 0))) {
274 CFDataAppendBytes (mData
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
279 CFAssert1(str
&& data
, __kCFLogAssertion
, "%s(): Error writing plist", __PRETTY_FUNCTION__
);
285 static void _plistAppendString(CFMutableDataRef mData
, CFStringRef str
) {
286 const UniChar
*chars
;
289 if ((chars
= CFStringGetCharactersPtr(str
))) {
290 _plistAppendCharacters(mData
, chars
, CFStringGetLength(str
));
291 } else if ((cStr
= CFStringGetCStringPtr(str
, kCFStringEncodingASCII
)) || (cStr
= CFStringGetCStringPtr(str
, kCFStringEncodingUTF8
))) {
292 _plistAppendUTF8CString(mData
, cStr
);
293 } else if ((data
= CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
, 0))) {
294 CFDataAppendBytes (mData
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
297 CFAssert1(TRUE
, __kCFLogAssertion
, "%s(): Error in plist writing", __PRETTY_FUNCTION__
);
302 // Append CFString-style format + arguments
304 static void _plistAppendFormat(CFMutableDataRef mData
, CFStringRef format
, ...) {
308 va_start(argList
, format
);
309 fStr
= CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault
, NULL
, format
, argList
);
312 CFAssert1(fStr
, __kCFLogAssertion
, "%s(): Error writing plist", __PRETTY_FUNCTION__
);
313 _plistAppendString(mData
, fStr
);
319 static void _appendIndents(CFIndex numIndents
, CFMutableDataRef str
) {
321 static const UniChar tabs
[NUMTABS
] = {'\t','\t','\t','\t'};
322 for (; numIndents
> 0; numIndents
-= NUMTABS
) _plistAppendCharacters(str
, tabs
, (numIndents
>= NUMTABS
) ? NUMTABS
: numIndents
);
325 /* Append the escaped version of origStr to mStr.
327 static void _appendEscapedString(CFStringRef origStr
, CFMutableDataRef mStr
) {
329 CFIndex i
, length
= CFStringGetLength(origStr
);
331 UniChar buf
[BUFSIZE
];
332 CFStringInlineBuffer inlineBuffer
;
334 CFStringInitInlineBuffer(origStr
, &inlineBuffer
, CFRangeMake(0, length
));
336 for (i
= 0; i
< length
; i
++) {
337 UniChar ch
= __CFStringGetCharacterFromInlineBufferQuick(&inlineBuffer
, i
);
340 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
342 _plistAppendUTF8CString(mStr
, "<");
345 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
347 _plistAppendUTF8CString(mStr
, ">");
350 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
352 _plistAppendUTF8CString(mStr
, "&");
356 if (bufCnt
== BUFSIZE
) {
357 _plistAppendCharacters(mStr
, buf
, bufCnt
);
363 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
368 /* Base-64 encoding/decoding */
370 /* The base-64 encoding packs three 8-bit bytes into four 7-bit ASCII
371 * characters. If the number of bytes in the original data isn't divisable
372 * by three, "=" characters are used to pad the encoded data. The complete
373 * set of characters used in base-64 are:
383 // Write the inputData to the mData using Base 64 encoding
385 static void _XMLPlistAppendDataUsingBase64(CFMutableDataRef mData
, CFDataRef inputData
, CFIndex indent
) {
386 static const char __CFPLDataEncodeTable
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
387 #define MAXLINELEN 76
388 char buf
[MAXLINELEN
+ 4 + 2]; // For the slop and carriage return and terminating NULL
390 const uint8_t *bytes
= CFDataGetBytePtr(inputData
);
391 CFIndex length
= CFDataGetLength(inputData
);
395 if (indent
> 8) indent
= 8; // refuse to indent more than 64 characters
397 pos
= 0; // position within buf
399 for (i
= 0, p
= bytes
; i
< length
; i
++, p
++) {
400 /* 3 bytes are encoded as 4 */
403 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[0] >> 2) & 0x3f)];
406 buf
[pos
++] = __CFPLDataEncodeTable
[ ((((p
[-1] << 8) | p
[0]) >> 4) & 0x3f)];
409 buf
[pos
++] = __CFPLDataEncodeTable
[ ((((p
[-1] << 8) | p
[0]) >> 6) & 0x3f)];
410 buf
[pos
++] = __CFPLDataEncodeTable
[ (p
[0] & 0x3f)];
413 /* Flush the line out every 76 (or fewer) chars --- indents count against the line length*/
414 if (pos
>= MAXLINELEN
- 8 * indent
) {
417 _appendIndents(indent
, mData
);
418 _plistAppendUTF8CString(mData
, buf
);
427 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[-1] << 4) & 0x30)];
432 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[-1] << 2) & 0x3c)];
440 _appendIndents(indent
, mData
);
441 _plistAppendUTF8CString(mData
, buf
);
445 extern CFStringRef
__CFNumberCopyFormattingDescriptionAsFloat64(CFTypeRef cf
);
447 static void _CFAppendXML0(CFTypeRef object
, UInt32 indentation
, CFMutableDataRef xmlString
) {
448 UInt32 typeID
= CFGetTypeID(object
);
449 _appendIndents(indentation
, xmlString
);
450 if (typeID
== stringtype
) {
451 _plistAppendUTF8CString(xmlString
, "<");
452 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[STRING_IX
], STRING_TAG_LENGTH
);
453 _plistAppendUTF8CString(xmlString
, ">");
454 _appendEscapedString((CFStringRef
)object
, xmlString
);
455 _plistAppendUTF8CString(xmlString
, "</");
456 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[STRING_IX
], STRING_TAG_LENGTH
);
457 _plistAppendUTF8CString(xmlString
, ">\n");
458 #if DEPLOYMENT_TARGET_MACOSX
459 } else if (typeID
== _CFKeyedArchiverUIDGetTypeID()) {
460 _plistAppendUTF8CString(xmlString
, "<");
461 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
462 _plistAppendUTF8CString(xmlString
, ">\n");
463 _appendIndents(indentation
+1, xmlString
);
464 _plistAppendUTF8CString(xmlString
, "<");
465 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
);
466 _plistAppendUTF8CString(xmlString
, ">");
467 _appendEscapedString(CFSTR("CF$UID"), xmlString
);
468 _plistAppendUTF8CString(xmlString
, "</");
469 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
);
470 _plistAppendUTF8CString(xmlString
, ">\n");
471 _appendIndents(indentation
+ 1, xmlString
);
472 _plistAppendUTF8CString(xmlString
, "<");
473 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
474 _plistAppendUTF8CString(xmlString
, ">");
476 uint64_t v
= _CFKeyedArchiverUIDGetValue((CFKeyedArchiverUIDRef
)object
);
477 CFNumberRef num
= CFNumberCreate(kCFAllocatorSystemDefault
, kCFNumberSInt64Type
, &v
);
478 _plistAppendFormat(xmlString
, CFSTR("%@"), num
);
481 _plistAppendUTF8CString(xmlString
, "</");
482 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
483 _plistAppendUTF8CString(xmlString
, ">\n");
484 _appendIndents(indentation
, xmlString
);
485 _plistAppendUTF8CString(xmlString
, "</");
486 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
487 _plistAppendUTF8CString(xmlString
, ">\n");
490 #error Unknown or unspecified DEPLOYMENT_TARGET
492 } else if (typeID
== arraytype
) {
493 UInt32 i
, count
= CFArrayGetCount((CFArrayRef
)object
);
495 _plistAppendUTF8CString(xmlString
, "<");
496 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
497 _plistAppendUTF8CString(xmlString
, "/>\n");
500 _plistAppendUTF8CString(xmlString
, "<");
501 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
502 _plistAppendUTF8CString(xmlString
, ">\n");
503 for (i
= 0; i
< count
; i
++) {
504 _CFAppendXML0(CFArrayGetValueAtIndex((CFArrayRef
)object
, i
), indentation
+1, xmlString
);
506 _appendIndents(indentation
, xmlString
);
507 _plistAppendUTF8CString(xmlString
, "</");
508 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
509 _plistAppendUTF8CString(xmlString
, ">\n");
510 } else if (typeID
== dicttype
) {
511 UInt32 i
, count
= CFDictionaryGetCount((CFDictionaryRef
)object
);
512 CFMutableArrayRef keyArray
;
515 _plistAppendUTF8CString(xmlString
, "<");
516 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
517 _plistAppendUTF8CString(xmlString
, "/>\n");
520 _plistAppendUTF8CString(xmlString
, "<");
521 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
522 _plistAppendUTF8CString(xmlString
, ">\n");
523 keys
= (CFTypeRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(CFTypeRef
), 0);
524 CFDictionaryGetKeysAndValues((CFDictionaryRef
)object
, keys
, NULL
);
525 keyArray
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, count
, &kCFTypeArrayCallBacks
);
526 CFArrayReplaceValues(keyArray
, CFRangeMake(0, 0), keys
, count
);
527 CFArraySortValues(keyArray
, CFRangeMake(0, count
), (CFComparatorFunction
)CFStringCompare
, NULL
);
528 CFArrayGetValues(keyArray
, CFRangeMake(0, count
), keys
);
530 for (i
= 0; i
< count
; i
++) {
531 CFTypeRef key
= keys
[i
];
532 _appendIndents(indentation
+1, xmlString
);
533 _plistAppendUTF8CString(xmlString
, "<");
534 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
);
535 _plistAppendUTF8CString(xmlString
, ">");
536 _appendEscapedString((CFStringRef
)key
, xmlString
);
537 _plistAppendUTF8CString(xmlString
, "</");
538 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
);
539 _plistAppendUTF8CString(xmlString
, ">\n");
540 _CFAppendXML0(CFDictionaryGetValue((CFDictionaryRef
)object
, key
), indentation
+1, xmlString
);
542 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, keys
);
543 _appendIndents(indentation
, xmlString
);
544 _plistAppendUTF8CString(xmlString
, "</");
545 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
546 _plistAppendUTF8CString(xmlString
, ">\n");
547 } else if (typeID
== datatype
) {
548 _plistAppendUTF8CString(xmlString
, "<");
549 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
);
550 _plistAppendUTF8CString(xmlString
, ">\n");
551 _XMLPlistAppendDataUsingBase64(xmlString
, (CFDataRef
)object
, indentation
);
552 _appendIndents(indentation
, xmlString
);
553 _plistAppendUTF8CString(xmlString
, "</");
554 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
);
555 _plistAppendUTF8CString(xmlString
, ">\n");
556 } else if (typeID
== datetype
) {
557 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
558 int32_t y
= 0, M
= 0, d
= 0, H
= 0, m
= 0, s
= 0;
560 CFGregorianDate date
= CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime((CFDateRef
)object
), NULL
);
566 s
= (int32_t)date
.second
;
568 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, kCFGregorianCalendar
);
569 CFTimeZoneRef tz
= CFTimeZoneCreateWithName(kCFAllocatorSystemDefault
, CFSTR("GMT"), true);
570 CFCalendarSetTimeZone(calendar
, tz
);
571 CFCalendarDecomposeAbsoluteTime(calendar
, CFDateGetAbsoluteTime((CFDateRef
)object
), (const uint8_t *)"yMdHms", &y
, &M
, &d
, &H
, &m
, &s
);
575 _plistAppendUTF8CString(xmlString
, "<");
576 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
);
577 _plistAppendUTF8CString(xmlString
, ">");
578 _plistAppendFormat(xmlString
, CFSTR("%04d-%02d-%02dT%02d:%02d:%02dZ"), y
, M
, d
, H
, m
, s
);
579 _plistAppendUTF8CString(xmlString
, "</");
580 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
);
581 _plistAppendUTF8CString(xmlString
, ">\n");
582 } else if (typeID
== numbertype
) {
583 if (CFNumberIsFloatType((CFNumberRef
)object
)) {
584 _plistAppendUTF8CString(xmlString
, "<");
585 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
);
586 _plistAppendUTF8CString(xmlString
, ">");
587 CFStringRef s
= __CFNumberCopyFormattingDescriptionAsFloat64(object
);
588 _plistAppendString(xmlString
, s
);
590 _plistAppendUTF8CString(xmlString
, "</");
591 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
);
592 _plistAppendUTF8CString(xmlString
, ">\n");
594 _plistAppendUTF8CString(xmlString
, "<");
595 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
596 _plistAppendUTF8CString(xmlString
, ">");
598 _plistAppendFormat(xmlString
, CFSTR("%@"), object
);
600 _plistAppendUTF8CString(xmlString
, "</");
601 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
602 _plistAppendUTF8CString(xmlString
, ">\n");
604 } else if (typeID
== booltype
) {
605 if (CFBooleanGetValue((CFBooleanRef
)object
)) {
606 _plistAppendUTF8CString(xmlString
, "<");
607 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
);
608 _plistAppendUTF8CString(xmlString
, "/>\n");
610 _plistAppendUTF8CString(xmlString
, "<");
611 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
);
612 _plistAppendUTF8CString(xmlString
, "/>\n");
617 static void _CFGenerateXMLPropertyListToData(CFMutableDataRef xml
, CFTypeRef propertyList
) {
618 _plistAppendUTF8CString(xml
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE ");
619 _plistAppendCharacters(xml
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
);
620 _plistAppendUTF8CString(xml
, " PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<");
621 _plistAppendCharacters(xml
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
);
622 _plistAppendUTF8CString(xml
, " version=\"1.0\">\n");
624 _CFAppendXML0(propertyList
, 0, xml
);
626 _plistAppendUTF8CString(xml
, "</");
627 _plistAppendCharacters(xml
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
);
628 _plistAppendUTF8CString(xml
, ">\n");
631 CFDataRef
CFPropertyListCreateXMLData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
) {
633 CFMutableDataRef xml
;
634 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
635 __CFAssertIsPList(propertyList
);
636 if (!CFPropertyListIsValid(propertyList
, kCFPropertyListXMLFormat_v1_0
)) return NULL
;
637 xml
= CFDataCreateMutable(allocator
, 0);
638 _CFGenerateXMLPropertyListToData(xml
, propertyList
);
642 CFDataRef
_CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator
, CFPropertyListRef propertyList
) {
644 CFMutableDataRef xml
;
645 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
646 xml
= CFDataCreateMutable(allocator
, 0);
647 _CFGenerateXMLPropertyListToData(xml
, propertyList
);
651 // ========================================================================
654 // ------------------------- Reading plists ------------------
657 static void skipInlineDTD(_CFXMLPlistParseInfo
*pInfo
);
658 static CFTypeRef
parseXMLElement(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
);
660 // warning: doesn't have a good idea of Unicode line separators
661 static UInt32
lineNumber(_CFXMLPlistParseInfo
*pInfo
) {
662 const UniChar
*p
= pInfo
->begin
;
664 while (p
< pInfo
->curr
) {
667 if (*(p
+ 1) == '\n')
669 } else if (*p
== '\n') {
677 // warning: doesn't have a good idea of Unicode white space
678 CF_INLINE
void skipWhitespace(_CFXMLPlistParseInfo
*pInfo
) {
679 while (pInfo
->curr
< pInfo
->end
) {
680 switch (*(pInfo
->curr
)) {
693 /* 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. */
695 // pInfo should be just past "<!--"
696 static void skipXMLComment(_CFXMLPlistParseInfo
*pInfo
) {
697 const UniChar
*p
= pInfo
->curr
;
698 const UniChar
*end
= pInfo
->end
- 3; // Need at least 3 characters to compare against
700 if (*p
== '-' && *(p
+1) == '-' && *(p
+2) == '>') {
706 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unterminated comment started on line %d"), lineNumber(pInfo
));
709 // stringToMatch and buf must both be of at least len
710 static Boolean
matchString(const UniChar
*buf
, const UniChar
*stringToMatch
, UInt32 len
) {
712 case 10: if (buf
[9] != stringToMatch
[9]) return false;
713 case 9: if (buf
[8] != stringToMatch
[8]) return false;
714 case 8: if (buf
[7] != stringToMatch
[7]) return false;
715 case 7: if (buf
[6] != stringToMatch
[6]) return false;
716 case 6: if (buf
[5] != stringToMatch
[5]) return false;
717 case 5: if (buf
[4] != stringToMatch
[4]) return false;
718 case 4: if (buf
[3] != stringToMatch
[3]) return false;
719 case 3: if (buf
[2] != stringToMatch
[2]) return false;
720 case 2: if (buf
[1] != stringToMatch
[1]) return false;
721 case 1: if (buf
[0] != stringToMatch
[0]) return false;
724 return false; // internal error
727 // pInfo should be set to the first character after "<?"
728 static void skipXMLProcessingInstruction(_CFXMLPlistParseInfo
*pInfo
) {
729 const UniChar
*begin
= pInfo
->curr
, *end
= pInfo
->end
- 2; // Looking for "?>" so we need at least 2 characters
730 while (pInfo
->curr
< end
) {
731 if (*(pInfo
->curr
) == '?' && *(pInfo
->curr
+1) == '>') {
738 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected EOF while parsing the processing instruction begun on line %d"), lineNumber(pInfo
));
741 // first character should be immediately after the "<!"
742 static void skipDTD(_CFXMLPlistParseInfo
*pInfo
) {
743 // First pass "DOCTYPE"
744 if (pInfo
->end
- pInfo
->curr
< DOCTYPE_TAG_LENGTH
|| !matchString(pInfo
->curr
, CFXMLPlistTags
[DOCTYPE_IX
], DOCTYPE_TAG_LENGTH
)) {
745 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Malformed DTD on line %d"), lineNumber(pInfo
));
748 pInfo
->curr
+= DOCTYPE_TAG_LENGTH
;
749 skipWhitespace(pInfo
);
751 // Look for either the beginning of a complex DTD or the end of the DOCTYPE structure
752 while (pInfo
->curr
< pInfo
->end
) {
753 UniChar ch
= *(pInfo
->curr
);
754 if (ch
== '[') break; // inline DTD
755 if (ch
== '>') { // End of the DTD
761 if (pInfo
->curr
== pInfo
->end
) {
762 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF while parsing DTD", CFStringGetSystemEncoding());
766 // *Sigh* Must parse in-line DTD
767 skipInlineDTD(pInfo
);
768 if (pInfo
->errorString
) return;
769 skipWhitespace(pInfo
);
770 if (pInfo
->errorString
) return;
771 if (pInfo
->curr
< pInfo
->end
) {
772 if (*(pInfo
->curr
) == '>') {
775 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c on line %d while parsing DTD"), *(pInfo
->curr
), lineNumber(pInfo
));
778 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF while parsing DTD", CFStringGetSystemEncoding());
782 static void skipPERef(_CFXMLPlistParseInfo
*pInfo
) {
783 const UniChar
*p
= pInfo
->curr
;
784 while (p
< pInfo
->end
) {
791 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected EOF while parsing percent-escape sequence begun on line %d"), lineNumber(pInfo
));
794 // First character should be just past '['
795 static void skipInlineDTD(_CFXMLPlistParseInfo
*pInfo
) {
796 while (!pInfo
->errorString
&& pInfo
->curr
< pInfo
->end
) {
798 skipWhitespace(pInfo
);
803 } else if (ch
== '<') {
805 if (pInfo
->curr
>= pInfo
->end
) {
806 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF while parsing inline DTD", CFStringGetSystemEncoding());
812 skipXMLProcessingInstruction(pInfo
);
813 } else if (ch
== '!') {
814 if (pInfo
->curr
+ 2 < pInfo
->end
&& (*(pInfo
->curr
+1) == '-' && *(pInfo
->curr
+2) == '-')) {
816 skipXMLComment(pInfo
);
818 // Skip the myriad of DTD declarations of the form "<!string" ... ">"
819 pInfo
->curr
++; // Past both '<' and '!'
820 while (pInfo
->curr
< pInfo
->end
) {
821 if (*(pInfo
->curr
) == '>') break;
824 if (*(pInfo
->curr
) != '>') {
825 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF while parsing inline DTD", CFStringGetSystemEncoding());
832 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch
, lineNumber(pInfo
));
835 } else if (ch
== ']') {
839 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch
, lineNumber(pInfo
));
843 if (!pInfo
->errorString
)
844 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF while parsing inline DTD", CFStringGetSystemEncoding());
847 /* 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. */
849 static const signed char __CFPLDataDecodeTable
[128] = {
850 /* 000 */ -1, -1, -1, -1, -1, -1, -1, -1,
851 /* 010 */ -1, -1, -1, -1, -1, -1, -1, -1,
852 /* 020 */ -1, -1, -1, -1, -1, -1, -1, -1,
853 /* 030 */ -1, -1, -1, -1, -1, -1, -1, -1,
854 /* ' ' */ -1, -1, -1, -1, -1, -1, -1, -1,
855 /* '(' */ -1, -1, -1, 62, -1, -1, -1, 63,
856 /* '0' */ 52, 53, 54, 55, 56, 57, 58, 59,
857 /* '8' */ 60, 61, -1, -1, -1, 0, -1, -1,
858 /* '@' */ -1, 0, 1, 2, 3, 4, 5, 6,
859 /* 'H' */ 7, 8, 9, 10, 11, 12, 13, 14,
860 /* 'P' */ 15, 16, 17, 18, 19, 20, 21, 22,
861 /* 'X' */ 23, 24, 25, -1, -1, -1, -1, -1,
862 /* '`' */ -1, 26, 27, 28, 29, 30, 31, 32,
863 /* 'h' */ 33, 34, 35, 36, 37, 38, 39, 40,
864 /* 'p' */ 41, 42, 43, 44, 45, 46, 47, 48,
865 /* 'x' */ 49, 50, 51, -1, -1, -1, -1, -1
868 static CFDataRef
__CFPLDataDecode(_CFXMLPlistParseInfo
*pInfo
, Boolean isMutable
) {
876 tmpbuf
= (uint8_t *)CFAllocatorAllocate(pInfo
->allocator
, tmpbuflen
, 0);
877 for (; pInfo
->curr
< pInfo
->end
; pInfo
->curr
++) {
878 UniChar c
= *(pInfo
->curr
);
884 } else if (!isspace(c
)) {
887 if (__CFPLDataDecodeTable
[c
] < 0)
891 acc
+= __CFPLDataDecodeTable
[c
];
892 if (0 == (cntr
& 0x3)) {
893 if (tmpbuflen
<= tmpbufpos
+ 2) {
894 if (tmpbuflen
< 256 * 1024) {
896 } else if (tmpbuflen
< 16 * 1024 * 1024) {
899 // once in this stage, this will be really slow
900 // and really potentially fragment memory
901 tmpbuflen
+= 256 * 1024;
903 tmpbuf
= (uint8_t *)CFAllocatorReallocate(pInfo
->allocator
, tmpbuf
, tmpbuflen
, 0);
906 tmpbuf
[tmpbufpos
++] = (acc
>> 16) & 0xff;
908 tmpbuf
[tmpbufpos
++] = (acc
>> 8) & 0xff;
910 tmpbuf
[tmpbufpos
++] = acc
& 0xff;
914 CFMutableDataRef result
= CFDataCreateMutable(pInfo
->allocator
, 0);
915 CFDataAppendBytes(result
, tmpbuf
, tmpbufpos
);
916 CFAllocatorDeallocate(pInfo
->allocator
, tmpbuf
);
919 return CFDataCreateWithBytesNoCopy(pInfo
->allocator
, tmpbuf
, tmpbufpos
, pInfo
->allocator
);
923 // content ::== (element | CharData | Reference | CDSect | PI | Comment)*
924 // 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).
925 static CFTypeRef
getContentObject(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
) {
926 if (isKey
) *isKey
= false;
927 while (!pInfo
->errorString
&& pInfo
->curr
< pInfo
->end
) {
928 skipWhitespace(pInfo
);
929 if (pInfo
->curr
>= pInfo
->end
) {
930 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
933 if (*(pInfo
->curr
) != '<') {
934 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
938 if (pInfo
->curr
>= pInfo
->end
) {
939 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
942 switch (*(pInfo
->curr
)) {
944 // Processing instruction
945 skipXMLProcessingInstruction(pInfo
);
948 // Could be a comment
949 if (pInfo
->curr
+2 >= pInfo
->end
) {
950 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
953 if (*(pInfo
->curr
+1) == '-' && *(pInfo
->curr
+2) == '-') {
955 skipXMLComment(pInfo
);
957 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
962 // Whoops! Looks like we got to the end tag for the element whose content we're parsing
963 pInfo
->curr
--; // Back off to the '<'
966 // Should be an element
967 return parseXMLElement(pInfo
, isKey
);
970 // 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"
974 static void _catFromMarkToBuf(const UniChar
*mark
, const UniChar
*buf
, CFMutableStringRef
*string
, CFAllocatorRef allocator
) {
976 *string
= CFStringCreateMutable(allocator
, 0);
978 CFStringAppendCharacters(*string
, mark
, buf
-mark
);
981 static void parseCDSect_pl(_CFXMLPlistParseInfo
*pInfo
, CFMutableStringRef string
) {
982 const UniChar
*end
, *begin
;
983 if (pInfo
->end
- pInfo
->curr
< CDSECT_TAG_LENGTH
) {
984 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
987 if (!matchString(pInfo
->curr
, CFXMLPlistTags
[CDSECT_IX
], CDSECT_TAG_LENGTH
)) {
988 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered improper CDATA opening at line %d"), lineNumber(pInfo
));
991 pInfo
->curr
+= CDSECT_TAG_LENGTH
;
992 begin
= pInfo
->curr
; // Marks the first character of the CDATA content
993 end
= pInfo
->end
-2; // So we can safely look 2 characters beyond p
994 while (pInfo
->curr
< end
) {
995 if (*(pInfo
->curr
) == ']' && *(pInfo
->curr
+1) == ']' && *(pInfo
->curr
+2) == '>') {
997 CFStringAppendCharacters(string
, begin
, pInfo
->curr
-begin
);
1003 // Never found the end mark
1004 pInfo
->curr
= begin
;
1005 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Could not find end of CDATA started on line %d"), lineNumber(pInfo
));
1008 // Only legal references are {lt, gt, amp, apos, quote, #ddd, #xAAA}
1009 static void parseEntityReference_pl(_CFXMLPlistParseInfo
*pInfo
, CFMutableStringRef string
) {
1012 pInfo
->curr
++; // move past the '&';
1013 len
= pInfo
->end
- pInfo
->curr
; // how many characters we can safely scan
1015 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
1018 switch (*(pInfo
->curr
)) {
1020 if (len
>= 3 && *(pInfo
->curr
+1) == 't' && *(pInfo
->curr
+2) == ';') {
1025 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1028 if (len
>= 3 && *(pInfo
->curr
+1) == 't' && *(pInfo
->curr
+2) == ';') {
1033 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1035 case 'a': // "apos" or "amp"
1036 if (len
< 4) { // Not enough characters for either conversion
1037 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
1040 if (*(pInfo
->curr
+1) == 'm') {
1042 if (*(pInfo
->curr
+2) == 'p' && *(pInfo
->curr
+3) == ';') {
1047 } else if (*(pInfo
->curr
+1) == 'p') {
1049 if (len
> 4 && *(pInfo
->curr
+2) == 'o' && *(pInfo
->curr
+3) == 's' && *(pInfo
->curr
+4) == ';') {
1055 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1057 case 'q': // "quote"
1058 if (len
>= 5 && *(pInfo
->curr
+1) == 'u' && *(pInfo
->curr
+2) == 'o' && *(pInfo
->curr
+3) == 't' && *(pInfo
->curr
+4) == ';') {
1063 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1068 Boolean isHex
= false;
1069 if ( len
< 4) { // Not enough characters to make it all fit! Need at least "&#d;"
1070 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
1074 if (*(pInfo
->curr
) == 'x') {
1078 while (pInfo
->curr
< pInfo
->end
) {
1079 ch
= *(pInfo
->curr
);
1082 CFStringAppendCharacters(string
, &num
, 1);
1085 if (!isHex
) num
= num
*10;
1086 else num
= num
<< 4;
1087 if (ch
<= '9' && ch
>= '0') {
1089 } else if (!isHex
) {
1090 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c at line %d"), ch
, lineNumber(pInfo
));
1092 } else if (ch
>= 'a' && ch
<= 'f') {
1093 num
+= 10 + (ch
- 'a');
1094 } else if (ch
>= 'A' && ch
<= 'F') {
1095 num
+= 10 + (ch
- 'A');
1097 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c at line %d"), ch
, lineNumber(pInfo
));
1101 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
1105 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1108 CFStringAppendCharacters(string
, &ch
, 1);
1111 static CFStringRef
_uniqueStringForString(_CFXMLPlistParseInfo
*pInfo
, CFStringRef stringToUnique
) {
1112 if (!pInfo
->stringSet
) {
1113 pInfo
->stringSet
= CFSetCreateMutable(pInfo
->allocator
, 0, &kCFCopyStringSetCallBacks
);
1114 _CFSetSetCapacity(pInfo
->stringSet
, 160); // set capacity high to avoid lots of rehashes, though waste some memory
1116 CFSetAddValue(pInfo
->stringSet
, stringToUnique
);
1117 return (CFStringRef
)CFSetGetValue(pInfo
->stringSet
, stringToUnique
);
1120 extern void _CFStrSetDesiredCapacity(CFMutableStringRef str
, CFIndex len
);
1122 static CFStringRef
_uniqueStringForCharacters(_CFXMLPlistParseInfo
*pInfo
, const UniChar
*base
, CFIndex length
) {
1124 uint8_t *ascii
, buffer
[1024];
1126 if (!pInfo
->stringSet
) {
1127 pInfo
->stringSet
= CFSetCreateMutable(pInfo
->allocator
, 0, &kCFCopyStringSetCallBacks
);
1128 _CFSetSetCapacity(pInfo
->stringSet
, 160); // set capacity high to avoid lots of rehashes, though waste some memory
1130 if (pInfo
->tmpString
) {
1131 CFStringDelete(pInfo
->tmpString
, CFRangeMake(0, CFStringGetLength(pInfo
->tmpString
)));
1133 pInfo
->tmpString
= CFStringCreateMutable(pInfo
->allocator
, 0);
1134 _CFStrSetDesiredCapacity(pInfo
->tmpString
, 512);
1136 // This is to avoid having to promote the buffers of all the strings compared against
1137 // during the set probe; if a Unicode string is passed in, that's what happens.
1139 for (idx
= 0; isASCII
&& idx
< length
; idx
++) isASCII
= isASCII
&& (base
[idx
] < 0x80);
1141 ascii
= (length
< (CFIndex
)sizeof(buffer
)) ? buffer
: (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault
, length
+ 1, 0);
1142 for (idx
= 0; idx
< length
; idx
++) ascii
[idx
] = (uint8_t)base
[idx
];
1143 ascii
[length
] = '\0';
1144 CFStringAppendCString(pInfo
->tmpString
, (char *)ascii
, kCFStringEncodingASCII
);
1145 if (ascii
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, ascii
);
1147 CFStringAppendCharacters(pInfo
->tmpString
, base
, length
);
1149 CFSetAddValue(pInfo
->stringSet
, pInfo
->tmpString
);
1150 return (CFStringRef
)CFSetGetValue(pInfo
->stringSet
, pInfo
->tmpString
);
1154 // String could be comprised of characters, CDSects, or references to one of the "well-known" entities ('<', '>', '&', ''', '"')
1155 // returns a retained object in *string.
1156 static CFStringRef
getString(_CFXMLPlistParseInfo
*pInfo
) {
1157 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
1158 CFMutableStringRef string
= NULL
;
1159 while (!pInfo
->errorString
&& pInfo
->curr
< pInfo
->end
) {
1160 UniChar ch
= *(pInfo
->curr
);
1162 if (pInfo
->curr
+ 1 >= pInfo
->end
) break;
1163 // Could be a CDSect; could be the end of the string
1164 if (*(pInfo
->curr
+1) != '!') break; // End of the string
1165 _catFromMarkToBuf(mark
, pInfo
->curr
, &string
, pInfo
->allocator
);
1166 parseCDSect_pl(pInfo
, string
);
1168 } else if (ch
== '&') {
1169 _catFromMarkToBuf(mark
, pInfo
->curr
, &string
, pInfo
->allocator
);
1170 parseEntityReference_pl(pInfo
, string
);
1177 if (pInfo
->errorString
) {
1178 if (string
) CFRelease(string
);
1182 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
1183 CFStringRef uniqueString
= _uniqueStringForCharacters(pInfo
, mark
, pInfo
->curr
-mark
);
1184 if (uniqueString
) CFRetain(uniqueString
);
1185 return uniqueString
;
1187 string
= CFStringCreateMutable(pInfo
->allocator
, 0);
1188 CFStringAppendCharacters(string
, mark
, pInfo
->curr
- mark
);
1192 _catFromMarkToBuf(mark
, pInfo
->curr
, &string
, pInfo
->allocator
);
1193 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
1194 CFStringRef uniqueString
= _uniqueStringForString(pInfo
, string
);
1195 if (uniqueString
) CFRetain(uniqueString
);
1197 return uniqueString
;
1202 static Boolean
checkForCloseTag(_CFXMLPlistParseInfo
*pInfo
, const UniChar
*tag
, CFIndex tagLen
) {
1203 if (pInfo
->end
- pInfo
->curr
< tagLen
+ 3) {
1204 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
1207 if (*(pInfo
->curr
) != '<' || *(++pInfo
->curr
) != '/') {
1208 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
1212 if (!matchString(pInfo
->curr
, tag
, tagLen
)) {
1213 CFStringRef str
= CFStringCreateWithCharactersNoCopy(pInfo
->allocator
, tag
, tagLen
, kCFAllocatorNull
);
1214 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Close tag on line %d does not match open tag %@"), lineNumber(pInfo
), str
);
1218 pInfo
->curr
+= tagLen
;
1219 skipWhitespace(pInfo
);
1220 if (pInfo
->curr
== pInfo
->end
) {
1221 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
1224 if (*(pInfo
->curr
) != '>') {
1225 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
1232 // pInfo should be set to the first content character of the <plist>
1233 static CFTypeRef
parsePListTag(_CFXMLPlistParseInfo
*pInfo
) {
1234 CFTypeRef result
, tmp
= NULL
;
1235 const UniChar
*save
;
1236 result
= getContentObject(pInfo
, NULL
);
1238 if (!pInfo
->errorString
) pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered empty plist tag", CFStringGetSystemEncoding());
1241 save
= pInfo
->curr
; // Save this in case the next step fails
1242 tmp
= getContentObject(pInfo
, NULL
);
1244 // Got an extra object
1248 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected element at line %d (plist can only include one object)"), lineNumber(pInfo
));
1251 if (pInfo
->errorString
) {
1252 // Parse failed catastrophically
1256 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
)) {
1263 static int allowImmutableCollections
= -1;
1265 static void checkImmutableCollections(void) {
1266 allowImmutableCollections
= (NULL
== getenv("CFPropertyListAllowImmutableCollections")) ? 0 : 1;
1269 static CFTypeRef
parseArrayTag(_CFXMLPlistParseInfo
*pInfo
) {
1270 CFMutableArrayRef array
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
1271 CFTypeRef tmp
= getContentObject(pInfo
, NULL
);
1273 CFArrayAppendValue(array
, tmp
);
1275 tmp
= getContentObject(pInfo
, NULL
);
1277 if (pInfo
->errorString
) { // getContentObject encountered a parse error
1281 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
)) {
1282 if (-1 == allowImmutableCollections
) checkImmutableCollections();
1283 if (1 == allowImmutableCollections
) {
1284 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1285 CFArrayRef newArray
= CFArrayCreateCopy(pInfo
->allocator
, array
);
1287 array
= (CFMutableArrayRef
)newArray
;
1296 static CFTypeRef
parseDictTag(_CFXMLPlistParseInfo
*pInfo
) {
1297 CFMutableDictionaryRef dict
= NULL
;
1298 CFTypeRef key
=NULL
, value
=NULL
;
1300 const UniChar
*base
= pInfo
->curr
;
1301 key
= getContentObject(pInfo
, &gotKey
);
1304 if (key
) CFRelease(key
);
1305 if (dict
) CFRelease(dict
);
1307 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo
));
1310 value
= getContentObject(pInfo
, NULL
);
1312 if (key
) CFRelease(key
);
1313 if (dict
) CFRelease(dict
);
1314 if (!pInfo
->errorString
)
1315 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo
));
1319 dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1320 _CFDictionarySetCapacity(dict
, 10);
1322 CFDictionarySetValue(dict
, key
, value
);
1328 key
= getContentObject(pInfo
, &gotKey
);
1330 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
)) {
1332 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1333 dict
= (CFMutableDictionaryRef
)CFDictionaryCreate(pInfo
->allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1335 dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1338 CFIndex cnt
= CFDictionaryGetCount(dict
);
1339 #if DEPLOYMENT_TARGET_MACOSX
1341 CFTypeRef val
= CFDictionaryGetValue(dict
, CFSTR("CF$UID"));
1342 if (val
&& CFGetTypeID(val
) == numbertype
) {
1345 CFNumberGetValue((CFNumberRef
)val
, kCFNumberSInt32Type
, &v
);
1346 uid
= (CFTypeRef
)_CFKeyedArchiverUIDCreate(pInfo
->allocator
, v
);
1352 if (-1 == allowImmutableCollections
) checkImmutableCollections();
1353 if (1 == allowImmutableCollections
) {
1354 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1355 CFDictionaryRef newDict
= CFDictionaryCreateCopy(pInfo
->allocator
, dict
);
1357 dict
= (CFMutableDictionaryRef
)newDict
;
1363 if (dict
) CFRelease(dict
);
1367 static CFTypeRef
parseDataTag(_CFXMLPlistParseInfo
*pInfo
) {
1369 const UniChar
*base
= pInfo
->curr
;
1370 result
= __CFPLDataDecode(pInfo
, pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
);
1373 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Could not interpret <data> at line %d (should be base64-encoded)"), lineNumber(pInfo
));
1376 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
)) return result
;
1381 CF_INLINE Boolean
read2DigitNumber(_CFXMLPlistParseInfo
*pInfo
, int32_t *result
) {
1383 if (pInfo
->curr
+ 2 >= pInfo
->end
) return false;
1385 ch2
= *(pInfo
->curr
+ 1);
1387 if (!isdigit(ch1
) || !isdigit(ch2
)) return false;
1388 *result
= (ch1
- '0')*10 + (ch2
- '0');
1392 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
1393 static CFTypeRef
parseDateTag(_CFXMLPlistParseInfo
*pInfo
) {
1394 int32_t year
= 0, month
= 0, day
= 0, hour
= 0, minute
= 0, second
= 0;
1396 Boolean badForm
= false;
1398 while (pInfo
->curr
< pInfo
->end
&& isdigit(*pInfo
->curr
)) {
1399 year
= 10*year
+ (*pInfo
->curr
) - '0';
1402 if (pInfo
->curr
>= pInfo
->end
|| *pInfo
->curr
!= '-') {
1408 if (!badForm
&& read2DigitNumber(pInfo
, &month
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== '-') {
1414 if (!badForm
&& read2DigitNumber(pInfo
, &day
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== 'T') {
1420 if (!badForm
&& read2DigitNumber(pInfo
, &hour
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== ':') {
1426 if (!badForm
&& read2DigitNumber(pInfo
, &minute
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== ':') {
1432 if (!badForm
&& read2DigitNumber(pInfo
, &num
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== 'Z') {
1440 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Could not interpret <date> at line %d"), lineNumber(pInfo
));
1443 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
)) return NULL
;
1445 CFAbsoluteTime at
= 0.0;
1447 CFGregorianDate date
= {year
, month
, day
, hour
, minute
, second
};
1448 at
= CFGregorianDateGetAbsoluteTime(date
, NULL
);
1450 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault
, kCFGregorianCalendar
);
1451 CFTimeZoneRef tz
= CFTimeZoneCreateWithName(kCFAllocatorSystemDefault
, CFSTR("GMT"), true);
1452 CFCalendarSetTimeZone(calendar
, tz
);
1453 CFCalendarComposeAbsoluteTime(calendar
, &at
, (const uint8_t *)"yMdHms", year
, month
, day
, hour
, minute
, second
);
1454 CFRelease(calendar
);
1457 return CFDateCreate(pInfo
->allocator
, at
);
1460 static CFTypeRef
parseRealTag(_CFXMLPlistParseInfo
*pInfo
) {
1461 CFStringRef str
= getString(pInfo
);
1465 CFStringInlineBuffer buf
;
1467 if (!pInfo
->errorString
)
1468 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo
));
1472 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("nan"), kCFCompareCaseInsensitive
)) {
1474 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberNaN
) : NULL
;
1476 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("+infinity"), kCFCompareCaseInsensitive
)) {
1478 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberPositiveInfinity
) : NULL
;
1480 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("-infinity"), kCFCompareCaseInsensitive
)) {
1482 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberNegativeInfinity
) : NULL
;
1484 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("infinity"), kCFCompareCaseInsensitive
)) {
1486 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberPositiveInfinity
) : NULL
;
1488 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("-inf"), kCFCompareCaseInsensitive
)) {
1490 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberNegativeInfinity
) : NULL
;
1492 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("inf"), kCFCompareCaseInsensitive
)) {
1494 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberPositiveInfinity
) : NULL
;
1496 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("+inf"), kCFCompareCaseInsensitive
)) {
1498 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberPositiveInfinity
) : NULL
;
1501 len
= CFStringGetLength(str
);
1502 CFStringInitInlineBuffer(str
, &buf
, CFRangeMake(0, len
));
1504 if (!__CFStringScanDouble(&buf
, NULL
, &idx
, &val
) || idx
!= len
) {
1506 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered misformatted real on line %d"), lineNumber(pInfo
));
1510 result
= CFNumberCreate(pInfo
->allocator
, kCFNumberDoubleType
, &val
);
1511 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) return result
;
1516 #define GET_CH if (pInfo->curr == pInfo->end) { \
1517 pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Premature end of file after <integer> on line %d"), lineNumber(pInfo)); \
1528 kCFNumberSInt128Type
= 17
1531 static CFTypeRef
parseIntegerTag(_CFXMLPlistParseInfo
*pInfo
) {
1532 bool isHex
= false, isNeg
= false, hadLeadingZero
= false;
1535 // decimal_constant S*(-|+)?S*[0-9]+ (S == space)
1536 // hex_constant S*(-|+)?S*0[xX][0-9a-fA-F]+ (S == space)
1538 while (pInfo
->curr
< pInfo
->end
&& __CFIsWhitespace(*(pInfo
->curr
))) pInfo
->curr
++;
1541 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo
));
1544 if ('-' == ch
|| '+' == ch
) {
1545 isNeg
= ('-' == ch
);
1547 while (pInfo
->curr
< pInfo
->end
&& __CFIsWhitespace(*(pInfo
->curr
))) pInfo
->curr
++;
1551 if (pInfo
->curr
+ 1 < pInfo
->end
&& ('x' == *(pInfo
->curr
+ 1) || 'X' == *(pInfo
->curr
+ 1))) {
1555 hadLeadingZero
= true;
1561 hadLeadingZero
= true;
1565 if ('<' == ch
&& hadLeadingZero
) { // nothing but zeros
1567 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
)) {
1568 // checkForCloseTag() sets error string
1571 return CFNumberCreate(pInfo
->allocator
, kCFNumberSInt32Type
, &val
);
1574 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Incomplete <integer> on line %d"), lineNumber(pInfo
));
1578 uint32_t multiplier
= (isHex
? 16 : 10);
1580 uint32_t new_digit
= 0;
1582 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
1583 new_digit
= (ch
- '0');
1585 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1586 new_digit
= (ch
- 'a' + 10);
1588 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1589 new_digit
= (ch
- 'A' + 10);
1591 default: // other character
1592 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unknown character '%c' (0x%x) in <integer> on line %d"), ch
, ch
, lineNumber(pInfo
));
1595 if (!isHex
&& new_digit
> 9) {
1596 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Hex digit in non-hex <integer> on line %d"), lineNumber(pInfo
));
1599 if (UINT64_MAX
/ multiplier
< value
) {
1600 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo
));
1603 value
= multiplier
* value
;
1604 if (UINT64_MAX
- new_digit
< value
) {
1605 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo
));
1608 value
= value
+ new_digit
;
1609 if (isNeg
&& (uint64_t)INT64_MAX
+ 1 < value
) {
1610 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Integer underflow in <integer> on line %d"), lineNumber(pInfo
));
1616 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
)) {
1617 // checkForCloseTag() sets error string
1620 if (isNeg
|| value
<= INT64_MAX
) {
1622 if (isNeg
) v
= -v
; // no-op if INT64_MIN
1623 return CFNumberCreate(pInfo
->allocator
, kCFNumberSInt64Type
, &v
);
1625 CFSInt128Struct val
;
1628 return CFNumberCreate(pInfo
->allocator
, kCFNumberSInt128Type
, &val
);
1633 // Returned object is retained; caller must free. pInfo->curr expected to point to the first character after the '<'
1634 static CFTypeRef
parseXMLElement(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
) {
1635 const UniChar
*marker
= pInfo
->curr
;
1636 int markerLength
= -1;
1640 if (isKey
) *isKey
= false;
1641 while (pInfo
->curr
< pInfo
->end
) {
1642 UniChar ch
= *(pInfo
->curr
);
1643 if (ch
== ' ' || ch
== '\t' || ch
== '\n' || ch
=='\r') {
1644 if (markerLength
== -1) markerLength
= pInfo
->curr
- marker
;
1645 } else if (ch
== '>') {
1650 if (pInfo
->curr
>= pInfo
->end
) return NULL
;
1651 isEmpty
= (*(pInfo
->curr
-1) == '/');
1652 if (markerLength
== -1)
1653 markerLength
= pInfo
->curr
- (isEmpty
? 1 : 0) - marker
;
1654 pInfo
->curr
++; // Advance past '>'
1655 if (markerLength
== 0) {
1656 // Back up to the beginning of the marker
1657 pInfo
->curr
= marker
;
1658 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Malformed tag on line %d"), lineNumber(pInfo
));
1663 if (markerLength
== ARRAY_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
))
1664 markerIx
= ARRAY_IX
;
1666 case 'd': // Dictionary, data, or date; Fortunately, they all have the same marker length....
1667 if (markerLength
!= DICT_TAG_LENGTH
)
1669 if (matchString(marker
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
))
1671 else if (matchString(marker
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
))
1673 else if (matchString(marker
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
))
1676 case 'f': // false (boolean)
1677 if (markerLength
== FALSE_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
)) {
1678 markerIx
= FALSE_IX
;
1681 case 'i': // integer
1682 if (markerLength
== INTEGER_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
))
1683 markerIx
= INTEGER_IX
;
1685 case 'k': // Key of a dictionary
1686 if (markerLength
== KEY_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
)) {
1688 if (isKey
) *isKey
= true;
1692 if (markerLength
== PLIST_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
))
1693 markerIx
= PLIST_IX
;
1696 if (markerLength
== REAL_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
))
1700 if (markerLength
== STRING_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[STRING_IX
], STRING_TAG_LENGTH
))
1701 markerIx
= STRING_IX
;
1703 case 't': // true (boolean)
1704 if (markerLength
== TRUE_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
))
1709 if (!pInfo
->allowNewTypes
&& markerIx
!= PLIST_IX
&& markerIx
!= ARRAY_IX
&& markerIx
!= DICT_IX
&& markerIx
!= STRING_IX
&& markerIx
!= KEY_IX
&& markerIx
!= DATA_IX
) {
1710 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered new tag when expecting only old-style property list objects", CFStringGetSystemEncoding());
1717 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered empty plist tag", CFStringGetSystemEncoding());
1720 return parsePListTag(pInfo
);
1723 return pInfo
->mutabilityOption
== kCFPropertyListImmutable
? CFArrayCreate(pInfo
->allocator
, NULL
, 0, &kCFTypeArrayCallBacks
) : CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
1725 return parseArrayTag(pInfo
);
1729 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1730 return CFDictionaryCreate(pInfo
->allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1732 return CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1735 return parseDictTag(pInfo
);
1741 int tagLen
= (markerIx
== KEY_IX
) ? KEY_TAG_LENGTH
: STRING_TAG_LENGTH
;
1743 return pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
? CFStringCreateMutable(pInfo
->allocator
, 0) : CFStringCreateWithCharacters(pInfo
->allocator
, NULL
, 0);
1745 str
= getString(pInfo
);
1746 if (!str
) return NULL
; // getString will already have set the error string
1747 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[markerIx
], tagLen
)) {
1755 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered empty <data> on line %d"), lineNumber(pInfo
));
1758 return parseDataTag(pInfo
);
1762 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered empty <date> on line %d"), lineNumber(pInfo
));
1765 return parseDateTag(pInfo
);
1769 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
)) return NULL
;
1771 return CFRetain(kCFBooleanTrue
);
1774 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
)) return NULL
;
1776 return CFRetain(kCFBooleanFalse
);
1779 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo
));
1782 return parseRealTag(pInfo
);
1786 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo
));
1789 return parseIntegerTag(pInfo
);
1792 CFStringRef markerStr
= CFStringCreateWithCharacters(pInfo
->allocator
, marker
, markerLength
);
1793 pInfo
->curr
= marker
;
1794 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unknown tag %@ on line %d"), markerStr
, lineNumber(pInfo
));
1795 CFRelease(markerStr
);
1801 static CFTypeRef
parseXMLPropertyList(_CFXMLPlistParseInfo
*pInfo
) {
1802 while (!pInfo
->errorString
&& pInfo
->curr
< pInfo
->end
) {
1804 skipWhitespace(pInfo
);
1805 if (pInfo
->curr
+1 >= pInfo
->end
) {
1806 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "No XML content found", CFStringGetSystemEncoding());
1809 if (*(pInfo
->curr
) != '<') {
1810 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unexpected character %c at line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
1813 ch
= *(++ pInfo
->curr
);
1817 if (pInfo
->curr
+1 < pInfo
->end
&& *pInfo
->curr
== '-' && *(pInfo
->curr
+1) == '-') {
1820 skipXMLComment(pInfo
);
1824 } else if (ch
== '?') {
1825 // Processing instruction
1827 skipXMLProcessingInstruction(pInfo
);
1830 return parseXMLElement(pInfo
, NULL
);
1831 // 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
1834 // Should never get here
1835 if (!(pInfo
->errorString
))
1836 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
1840 static CFStringEncoding
encodingForXMLData(CFDataRef data
, CFStringRef
*error
) {
1841 const uint8_t *bytes
= (uint8_t *)CFDataGetBytePtr(data
);
1842 UInt32 length
= CFDataGetLength(data
);
1843 const uint8_t *idx
, *end
;
1846 // Check for the byte order mark first
1848 ((*bytes
== 0xFF && *(bytes
+1) == 0xFE) ||
1849 (*bytes
== 0xFE && *(bytes
+1) == 0xFF) ||
1850 *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
1851 return kCFStringEncodingUnicode
;
1853 // Scan for the <?xml.... ?> opening
1854 if (length
< 5 || strncmp((char const *) bytes
, "<?xml", 5) != 0) return kCFStringEncodingUTF8
;
1856 end
= bytes
+ length
;
1857 // Found "<?xml"; now we scan for "encoding"
1860 const uint8_t *scan
;
1861 if ( ch
== '?' || ch
== '>') return kCFStringEncodingUTF8
;
1864 if (ch
== 'e' && *scan
++ == 'n' && *scan
++ == 'c' && *scan
++ == 'o' && *scan
++ == 'd' && *scan
++ == 'i'
1865 && *scan
++ == 'n' && *scan
++ == 'g' && *scan
++ == '=') {
1870 if (idx
>= end
) return kCFStringEncodingUTF8
;
1872 if (quote
!= '\'' && quote
!= '\"') return kCFStringEncodingUTF8
;
1874 CFStringRef encodingName
;
1875 const uint8_t *base
= idx
+1; // Move past the quote character
1876 CFStringEncoding enc
;
1879 while (idx
< end
&& *idx
!= quote
) idx
++;
1880 if (idx
>= end
) return kCFStringEncodingUTF8
;
1882 if (len
== 5 && (*base
== 'u' || *base
== 'U') && (base
[1] == 't' || base
[1] == 'T') && (base
[2] == 'f' || base
[2] == 'F') && (base
[3] == '-') && (base
[4] == '8'))
1883 return kCFStringEncodingUTF8
;
1884 encodingName
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, base
, len
, kCFStringEncodingISOLatin1
, false);
1885 enc
= CFStringConvertIANACharSetNameToEncoding(encodingName
);
1886 if (enc
!= kCFStringEncodingInvalidId
) {
1887 CFRelease(encodingName
);
1892 *error
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("Encountered unknown encoding (%@)"), encodingName
);
1893 CFRelease(encodingName
);
1899 extern bool __CFTryParseBinaryPlist(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFPropertyListRef
*plist
, CFStringRef
*errorString
);
1900 int32_t _CFPropertyListAllowNonUTF8
= 0;
1902 #define SAVE_PLISTS 0
1905 static Boolean
__savePlistData(CFDataRef data
, CFOptionFlags opt
) {
1908 uint32_t pnlen
= sizeof(pn
);
1909 uint8_t *pnp
= NULL
;
1910 if (0 == _NSGetExecutablePath((char *)pn
, &pnlen
)) {
1911 pnp
= strrchr((char *)pn
, '/');
1918 if (0 == strcmp((char *)pnp
, "parse_plists")) return true;
1919 CFUUIDRef r
= CFUUIDCreate(kCFAllocatorSystemDefault
);
1920 CFStringRef s
= CFUUIDCreateString(kCFAllocatorSystemDefault
, r
);
1921 CFStringRef p
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("/tmp/plists/%s#%@#0x%x"), pnp
, s
, opt
);
1922 _CFStringGetFileSystemRepresentation(p
, fn
, sizeof(fn
));
1926 int fd
= open((const char *)fn
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
1927 if (fd
< 0) return false;
1928 int len
= CFDataGetLength(data
);
1929 int w
= write(fd
, CFDataGetBytePtr(data
), len
);
1932 if (w
!= len
) return false;
1937 CFTypeRef
_CFPropertyListCreateFromXMLData(CFAllocatorRef allocator
, CFDataRef xmlData
, CFOptionFlags option
, CFStringRef
*errorString
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
1939 CFStringEncoding encoding
;
1940 CFStringRef xmlString
;
1942 CFPropertyListRef plist
;
1944 if (errorString
) *errorString
= NULL
;
1945 if (!xmlData
|| CFDataGetLength(xmlData
) == 0) {
1947 *errorString
= CFSTR("Cannot parse a NULL or zero-length data");
1948 CFRetain(*errorString
); // Caller expects to release
1954 __savePlistData(xmlData
, option
);
1957 if (__CFTryParseBinaryPlist(allocator
, xmlData
, option
, &plist
, errorString
)) {
1958 if (format
) *format
= kCFPropertyListBinaryFormat_v1_0
;
1962 allocator
= allocator
? allocator
: __CFGetDefaultAllocator();
1963 CFRetain(allocator
);
1965 encoding
= encodingForXMLData(xmlData
, errorString
); // 0 is an error return, NOT MacRoman.
1967 if (encoding
== 0) {
1968 // Couldn't find an encoding; encodingForXMLData already set *errorString if necessary
1969 // Note that encodingForXMLData() will give us the right values for a standard plist, too.
1970 if (errorString
) *errorString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("Could not determine the encoding of the XML data"));
1974 xmlString
= CFStringCreateWithBytes(allocator
, CFDataGetBytePtr(xmlData
), CFDataGetLength(xmlData
), encoding
, true);
1975 if (NULL
== xmlString
&& (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard
) || _CFPropertyListAllowNonUTF8
)) { // conversion failed, probably because not in proper encoding
1976 // Call __CFStringCreateImmutableFunnel3() the same way CFStringCreateWithBytes() does, except with the addt'l flag
1977 if (encoding
== kCFStringEncodingUTF8
) xmlString
= __CFStringCreateImmutableFunnel3(allocator
, CFDataGetBytePtr(xmlData
), CFDataGetLength(xmlData
), kCFStringEncodingUTF8
, true, true, false, false, false, (CFAllocatorRef
)-1 /* ALLOCATORSFREEFUNC */, kCFStringEncodingLenientUTF8Conversion
);
1979 length
= xmlString
? CFStringGetLength(xmlString
) : 0;
1982 _CFXMLPlistParseInfo pInfoBuf
;
1983 _CFXMLPlistParseInfo
*pInfo
= &pInfoBuf
;
1985 UniChar
*buf
= (UniChar
*)CFStringGetCharactersPtr(xmlString
);
1987 if (errorString
) *errorString
= NULL
;
1989 buf
= (UniChar
*)CFAllocatorAllocate(allocator
, length
* sizeof(UniChar
), 0);
1990 CFStringGetCharacters(xmlString
, CFRangeMake(0, length
), buf
);
1991 CFRelease(xmlString
);
1995 pInfo
->end
= buf
+length
;
1997 pInfo
->allocator
= allocator
;
1998 pInfo
->errorString
= NULL
;
1999 pInfo
->stringSet
= NULL
;
2000 pInfo
->tmpString
= NULL
;
2001 pInfo
->mutabilityOption
= option
;
2002 pInfo
->allowNewTypes
= allowNewTypes
;
2004 // 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.....
2005 result
= parseXMLPropertyList(pInfo
);
2006 if (result
&& format
) *format
= kCFPropertyListXMLFormat_v1_0
;
2008 CFStringRef err
= pInfo
->errorString
;
2009 // Reset pInfo so we can try again
2010 pInfo
->curr
= pInfo
->begin
;
2011 pInfo
->errorString
= NULL
;
2013 result
= parseOldStylePropertyListOrStringsFile(pInfo
);
2014 if (result
&& format
) *format
= kCFPropertyListOpenStepFormat
;
2016 if (errorString
) *errorString
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("XML parser error:\n\t%@\nOld-style plist parser error:\n\t%@\n"), err
, pInfo
->errorString
);
2018 if (err
) CFRelease(err
);
2019 if (pInfo
->errorString
) CFRelease(pInfo
->errorString
);
2022 CFRelease(xmlString
);
2024 CFAllocatorDeallocate(allocator
, (void *)pInfo
->begin
);
2026 if (pInfo
->stringSet
) CFRelease(pInfo
->stringSet
);
2027 if (pInfo
->tmpString
) CFRelease(pInfo
->tmpString
);
2028 CFRelease(allocator
);
2032 *errorString
= (CFStringRef
)CFRetain(CFSTR("Conversion of data failed. The file is not UTF-8, or in the encoding specified in XML header if XML."));
2037 CFTypeRef
CFPropertyListCreateFromXMLData(CFAllocatorRef allocator
, CFDataRef xmlData
, CFOptionFlags option
, CFStringRef
*errorString
) {
2039 CFAssert1(xmlData
!= NULL
, __kCFLogAssertion
, "%s(): NULL data not allowed", __PRETTY_FUNCTION__
);
2040 CFAssert2(option
== kCFPropertyListImmutable
|| option
== kCFPropertyListMutableContainers
|| option
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, option
);
2041 return _CFPropertyListCreateFromXMLData(allocator
, xmlData
, option
, errorString
, true, NULL
);
2044 CFIndex
CFPropertyListWriteToStream(CFPropertyListRef propertyList
, CFWriteStreamRef stream
, CFPropertyListFormat format
, CFStringRef
*errorString
) {
2046 CFAssert1(stream
!= NULL
, __kCFLogAssertion
, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__
);
2047 CFAssert2(format
== kCFPropertyListOpenStepFormat
|| format
== kCFPropertyListXMLFormat_v1_0
|| format
== kCFPropertyListBinaryFormat_v1_0
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, format
);
2048 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
2049 __CFAssertIsPList(propertyList
);
2050 CFAssert1(CFWriteStreamGetTypeID() == CFGetTypeID(stream
), __kCFLogAssertion
, "%s(): stream argument is not a write stream", __PRETTY_FUNCTION__
);
2051 CFAssert1(kCFStreamStatusOpen
== CFWriteStreamGetStatus(stream
) || kCFStreamStatusWriting
== CFWriteStreamGetStatus(stream
), __kCFLogAssertion
, "%s(): stream is not open", __PRETTY_FUNCTION__
);
2053 if (errorString
) *errorString
= NULL
;
2054 if (!CFPropertyListIsValid(propertyList
, format
)) {
2055 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Property list invalid for format"));
2058 if (format
== kCFPropertyListOpenStepFormat
) {
2059 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2062 if (format
== kCFPropertyListXMLFormat_v1_0
) {
2063 CFDataRef data
= CFPropertyListCreateXMLData(kCFAllocatorSystemDefault
, propertyList
);
2065 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Property list in XML format could not be created for unknown reasons."));
2068 CFIndex len
= CFDataGetLength(data
);
2069 const uint8_t *ptr
= CFDataGetBytePtr(data
);
2071 CFIndex ret
= CFWriteStreamWrite(stream
, ptr
, len
);
2073 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Property list writing could not be completed because stream is full."));
2077 CFErrorRef err
= CFWriteStreamCopyError(stream
);
2078 CFStringRef str
= err
? CFErrorCopyDescription(err
) : NULL
;
2079 if (errorString
) *errorString
= str
? str
: (CFStringRef
)CFRetain(CFSTR("Property list writing could not be completed because the stream had an unknown error."));
2080 if (err
) CFRelease(err
);
2086 len
= CFDataGetLength(data
);
2090 if (format
== kCFPropertyListBinaryFormat_v1_0
) {
2091 CFIndex len
= __CFBinaryPlistWriteToStream(propertyList
, stream
);
2094 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("Unknown format option"));
2098 static void __CFConvertReadStreamToBytes(CFReadStreamRef stream
, CFIndex max
, uint8_t **buffer
, CFIndex
*length
) {
2099 int32_t buflen
= 0, bufsize
= 0, retlen
;
2100 uint8_t *buf
= NULL
, sbuf
[8192];
2102 retlen
= CFReadStreamRead(stream
, sbuf
, __CFMin(8192, max
));
2108 if (bufsize
< buflen
+ retlen
) {
2109 if (bufsize
< 256 * 1024) {
2111 } else if (bufsize
< 16 * 1024 * 1024) {
2114 // once in this stage, this will be really slow
2115 // and really potentially fragment memory
2116 bufsize
+= 256 * 1024;
2118 if (bufsize
< buflen
+ retlen
) bufsize
= buflen
+ retlen
;
2119 buf
= (uint8_t *)CFAllocatorReallocate(kCFAllocatorSystemDefault
, buf
, bufsize
, 0);
2122 memmove(buf
+ buflen
, sbuf
, retlen
);
2133 CFPropertyListRef
CFPropertyListCreateFromStream(CFAllocatorRef allocator
, CFReadStreamRef stream
, CFIndex length
, CFOptionFlags mutabilityOption
, CFPropertyListFormat
*format
, CFStringRef
*errorString
) {
2135 CFPropertyListRef pl
;
2138 uint8_t *buffer
= NULL
;
2139 CFAssert1(stream
!= NULL
, __kCFLogAssertion
, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__
);
2140 CFAssert1(CFReadStreamGetTypeID() == CFGetTypeID(stream
), __kCFLogAssertion
, "%s(): stream argument is not a read stream", __PRETTY_FUNCTION__
);
2141 CFAssert1(kCFStreamStatusOpen
== CFReadStreamGetStatus(stream
) || kCFStreamStatusReading
== CFReadStreamGetStatus(stream
), __kCFLogAssertion
, "%s(): stream is not open", __PRETTY_FUNCTION__
);
2142 CFAssert2(mutabilityOption
== kCFPropertyListImmutable
|| mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, mutabilityOption
);
2144 if (errorString
) *errorString
= NULL
;
2145 if (0 == length
) length
= INT_MAX
;
2146 __CFConvertReadStreamToBytes(stream
, length
, &buffer
, &buflen
);
2147 if (!buffer
|| buflen
< 6) {
2148 if (buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buffer
);
2149 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("stream had too few bytes"));
2152 data
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, buffer
, buflen
, kCFAllocatorSystemDefault
);
2153 pl
= _CFPropertyListCreateFromXMLData(allocator
, data
, mutabilityOption
, errorString
, true, format
);
2158 // ========================================================================
2161 // Old NeXT-style property lists
2164 static CFTypeRef
parsePlistObject(_CFXMLPlistParseInfo
*pInfo
, bool requireObject
);
2166 #define isValidUnquotedStringCharacter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z') || ((x) >= '0' && (x) <= '9') || (x) == '_' || (x) == '$' || (x) == '/' || (x) == ':' || (x) == '.' || (x) == '-')
2168 static void advanceToNonSpace(_CFXMLPlistParseInfo
*pInfo
) {
2170 while (pInfo
->curr
< pInfo
->end
) {
2171 ch2
= *(pInfo
->curr
);
2173 if (ch2
>= 9 && ch2
<= 0x0d) continue; // tab, newline, vt, form feed, carriage return
2174 if (ch2
== ' ' || ch2
== 0x2028 || ch2
== 0x2029) continue; // space and Unicode line sep, para sep
2176 if (pInfo
->curr
>= pInfo
->end
) {
2177 // whoops; back up and return
2180 } else if (*(pInfo
->curr
) == '/') {
2182 while (pInfo
->curr
< pInfo
->end
) { // go to end of comment line
2183 UniChar ch3
= *(pInfo
->curr
);
2184 if (ch3
== '\n' || ch3
== '\r' || ch3
== 0x2028 || ch3
== 0x2029) break;
2187 } else if (*(pInfo
->curr
) == '*') { // handle /* ... */
2189 while (pInfo
->curr
< pInfo
->end
) {
2190 ch2
= *(pInfo
->curr
);
2192 if (ch2
== '*' && pInfo
->curr
< pInfo
->end
&& *(pInfo
->curr
) == '/') {
2193 pInfo
->curr
++; // advance past the '/'
2208 static UniChar
getSlashedChar(_CFXMLPlistParseInfo
*pInfo
) {
2209 UniChar ch
= *(pInfo
->curr
);
2220 uint8_t num
= ch
- '0';
2222 CFIndex usedCharLen
;
2223 /* three digits maximum to avoid reading \000 followed by 5 as \5 ! */
2224 if ((ch
= *(pInfo
->curr
)) >= '0' && ch
<= '7') { // we use in this test the fact that the buffer is zero-terminated
2226 num
= (num
<< 3) + ch
- '0';
2227 if ((pInfo
->curr
< pInfo
->end
) && (ch
= *(pInfo
->curr
)) >= '0' && ch
<= '7') {
2229 num
= (num
<< 3) + ch
- '0';
2232 CFStringEncodingBytesToUnicode(kCFStringEncodingNextStepLatin
, 0, &num
, sizeof(uint8_t), NULL
, &result
, 1, &usedCharLen
);
2233 return (usedCharLen
== 1) ? result
: 0;
2236 unsigned num
= 0, numDigits
= 4; /* Parse four digits */
2237 while (pInfo
->curr
< pInfo
->end
&& numDigits
--) {
2238 if (((ch
= *(pInfo
->curr
)) < 128) && isxdigit(ch
)) {
2240 num
= (num
<< 4) + ((ch
<= '9') ? (ch
- '0') : ((ch
<= 'F') ? (ch
- 'A' + 10) : (ch
- 'a' + 10)));
2245 case 'a': return '\a'; // Note: the meaning of '\a' varies with -traditional to gcc
2246 case 'b': return '\b';
2247 case 'f': return '\f';
2248 case 'n': return '\n';
2249 case 'r': return '\r';
2250 case 't': return '\t';
2251 case 'v': return '\v';
2252 case '"': return '\"';
2253 case '\n': return '\n';
2258 static CFStringRef
parseQuotedPlistString(_CFXMLPlistParseInfo
*pInfo
, UniChar quote
) {
2259 CFMutableStringRef str
= NULL
;
2260 const UniChar
*startMark
= pInfo
->curr
;
2261 const UniChar
*mark
= pInfo
->curr
;
2262 while (pInfo
->curr
< pInfo
->end
) {
2263 UniChar ch
= *(pInfo
->curr
);
2264 if (ch
== quote
) break;
2266 _catFromMarkToBuf(mark
, pInfo
->curr
, &str
, pInfo
->allocator
);
2268 ch
= getSlashedChar(pInfo
);
2269 CFStringAppendCharacters(str
, &ch
, 1);
2272 // 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.
2276 if (pInfo
->end
<= pInfo
->curr
) {
2277 if (str
) CFRelease(str
);
2278 pInfo
->curr
= startMark
;
2279 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unterminated quoted string starting on line %d"), lineNumber(pInfo
));
2283 if (pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
2284 _catFromMarkToBuf(mark
, pInfo
->curr
, &str
, pInfo
->allocator
);
2286 str
= (CFMutableStringRef
)_uniqueStringForCharacters(pInfo
, mark
, pInfo
->curr
-mark
);
2290 if (mark
!= pInfo
->curr
) {
2291 _catFromMarkToBuf(mark
, pInfo
->curr
, &str
, pInfo
->allocator
);
2293 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
2294 CFStringRef uniqueString
= _uniqueStringForString(pInfo
, str
);
2295 CFRetain(uniqueString
);
2297 str
= (CFMutableStringRef
)uniqueString
;
2300 pInfo
->curr
++; // Advance past the quote character before returning.
2301 if (pInfo
->errorString
) {
2302 CFRelease(pInfo
->errorString
);
2303 pInfo
->errorString
= NULL
;
2308 static CFStringRef
parseUnquotedPlistString(_CFXMLPlistParseInfo
*pInfo
) {
2309 const UniChar
*mark
= pInfo
->curr
;
2310 while (pInfo
->curr
< pInfo
->end
) {
2311 UniChar ch
= *pInfo
->curr
;
2312 if (isValidUnquotedStringCharacter(ch
))
2316 if (pInfo
->curr
!= mark
) {
2317 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
2318 CFStringRef str
= _uniqueStringForCharacters(pInfo
, mark
, pInfo
->curr
-mark
);
2322 CFMutableStringRef str
= CFStringCreateMutable(pInfo
->allocator
, 0);
2323 CFStringAppendCharacters(str
, mark
, pInfo
->curr
- mark
);
2327 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unexpected EOF"));
2331 static CFStringRef
parsePlistString(_CFXMLPlistParseInfo
*pInfo
, bool requireObject
) {
2333 advanceToNonSpace(pInfo
);
2334 if (pInfo
->curr
>= pInfo
->end
) {
2335 if (requireObject
) {
2336 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unexpected EOF while parsing string"));
2340 ch
= *(pInfo
->curr
);
2341 if (ch
== '\'' || ch
== '\"') {
2343 return parseQuotedPlistString(pInfo
, ch
);
2344 } else if (isValidUnquotedStringCharacter(ch
)) {
2345 return parseUnquotedPlistString(pInfo
);
2347 if (requireObject
) {
2348 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Invalid string character at line %d"), lineNumber(pInfo
));
2354 static CFTypeRef
parsePlistArray(_CFXMLPlistParseInfo
*pInfo
) {
2355 CFMutableArrayRef array
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
2356 CFTypeRef tmp
= parsePlistObject(pInfo
, false);
2358 CFArrayAppendValue(array
, tmp
);
2360 advanceToNonSpace(pInfo
);
2361 if (*pInfo
->curr
!= ',') {
2365 tmp
= parsePlistObject(pInfo
, false);
2368 advanceToNonSpace(pInfo
);
2369 if (*pInfo
->curr
!= ')') {
2371 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Expected terminating ')' for array at line %d"), lineNumber(pInfo
));
2374 if (pInfo
->errorString
) {
2375 CFRelease(pInfo
->errorString
);
2376 pInfo
->errorString
= NULL
;
2382 static CFDictionaryRef
parsePlistDictContent(_CFXMLPlistParseInfo
*pInfo
) {
2383 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2384 CFStringRef key
= NULL
;
2385 Boolean failedParse
= false;
2386 key
= parsePlistString(pInfo
, false);
2389 advanceToNonSpace(pInfo
);
2390 if (*pInfo
->curr
== ';') {
2391 /* This is a strings file using the shortcut format */
2392 /* although this check here really applies to all plists. */
2393 value
= CFRetain(key
);
2394 } else if (*pInfo
->curr
== '=') {
2396 value
= parsePlistObject(pInfo
, true);
2402 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unexpected ';' or '=' after key at line %d"), lineNumber(pInfo
));
2406 CFDictionarySetValue(dict
, key
, value
);
2411 advanceToNonSpace(pInfo
);
2412 if (*pInfo
->curr
== ';') {
2414 key
= parsePlistString(pInfo
, false);
2415 } else if (!allowMissingSemi
&& _CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2416 CFLog(kCFLogLevelWarning
, CFSTR("CFPropertyListCreateFromXMLData(): Old-style plist parser: missing semicolon in dictionary."));
2418 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Missing ';' on line %d"), lineNumber(pInfo
));
2420 // on pre-Jaguar systems, do nothing except silently ignore the rest
2421 // of the dictionary, which is what happened on those systems.
2426 if (key
) CFRelease(key
);
2430 if (pInfo
->errorString
) {
2431 CFRelease(pInfo
->errorString
);
2432 pInfo
->errorString
= NULL
;
2437 static CFTypeRef
parsePlistDict(_CFXMLPlistParseInfo
*pInfo
) {
2438 CFDictionaryRef dict
= parsePlistDictContent(pInfo
);
2439 if (!dict
) return NULL
;
2440 advanceToNonSpace(pInfo
);
2441 if (*pInfo
->curr
!= '}') {
2443 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Expected terminating '}' for dictionary at line %d"), lineNumber(pInfo
));
2450 CF_INLINE
unsigned char fromHexDigit(unsigned char ch
) {
2451 if (isdigit(ch
)) return ch
- '0';
2452 if ((ch
>= 'a') && (ch
<= 'f')) return ch
- 'a' + 10;
2453 if ((ch
>= 'A') && (ch
<= 'F')) return ch
- 'A' + 10;
2454 return 0xff; // Just choose a large number for the error code
2457 /* Gets up to bytesSize bytes from a plist data. Returns number of bytes actually read. Leaves cursor at first non-space, non-hex character.
2458 -1 is returned for unexpected char, -2 for uneven number of hex digits
2460 static int getDataBytes(_CFXMLPlistParseInfo
*pInfo
, unsigned char *bytes
, int bytesSize
) {
2461 int numBytesRead
= 0;
2462 while ((pInfo
->curr
< pInfo
->end
) && (numBytesRead
< bytesSize
)) {
2464 UniChar ch1
= *pInfo
->curr
;
2465 if (ch1
== '>') return numBytesRead
; // Meaning we're done
2466 first
= fromHexDigit((unsigned char)ch1
);
2467 if (first
!= 0xff) { // If the first char is a hex, then try to read a second hex
2469 if (pInfo
->curr
>= pInfo
->end
) return -2; // Error: uneven number of hex digits
2470 UniChar ch2
= *pInfo
->curr
;
2471 second
= fromHexDigit((unsigned char)ch2
);
2472 if (second
== 0xff) return -2; // Error: uneven number of hex digits
2473 bytes
[numBytesRead
++] = (first
<< 4) + second
;
2475 } else if (ch1
== ' ' || ch1
== '\n' || ch1
== '\t' || ch1
== '\r' || ch1
== 0x2028 || ch1
== 0x2029) {
2478 return -1; // Error: unexpected character
2481 return numBytesRead
; // This does likely mean we didn't encounter a '>', but we'll let the caller deal with that
2484 #define numBytes 400
2485 static CFTypeRef
parsePlistData(_CFXMLPlistParseInfo
*pInfo
) {
2486 CFMutableDataRef result
= CFDataCreateMutable(pInfo
->allocator
, 0);
2488 // Read hex bytes and append them to result
2490 unsigned char bytes
[numBytes
];
2491 int numBytesRead
= getDataBytes(pInfo
, bytes
, numBytes
);
2492 if (numBytesRead
< 0) {
2494 switch (numBytesRead
) {
2495 case -2: pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Malformed data byte group at line %d; uneven length"), lineNumber(pInfo
)); break;
2496 default: pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Malformed data byte group at line %d; invalid hex"), lineNumber(pInfo
)); break;
2500 if (numBytesRead
== 0) break;
2501 CFDataAppendBytes(result
, bytes
, numBytesRead
);
2504 if (pInfo
->errorString
) {
2505 CFRelease(pInfo
->errorString
);
2506 pInfo
->errorString
= NULL
;
2509 if (*(pInfo
->curr
) == '>') {
2510 pInfo
->curr
++; // Move past '>'
2514 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Expected terminating '>' for data at line %d"), lineNumber(pInfo
));
2520 // Returned object is retained; caller must free.
2521 static CFTypeRef
parsePlistObject(_CFXMLPlistParseInfo
*pInfo
, bool requireObject
) {
2523 advanceToNonSpace(pInfo
);
2524 if (pInfo
->curr
+ 1 >= pInfo
->end
) {
2525 if (requireObject
) {
2526 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unexpected EOF while parsing plist"));
2530 ch
= *(pInfo
->curr
);
2533 return parsePlistDict(pInfo
);
2534 } else if (ch
== '(') {
2535 return parsePlistArray(pInfo
);
2536 } else if (ch
== '<') {
2537 return parsePlistData(pInfo
);
2538 } else if (ch
== '\'' || ch
== '\"') {
2539 return parseQuotedPlistString(pInfo
, ch
);
2540 } else if (isValidUnquotedStringCharacter(ch
)) {
2542 return parseUnquotedPlistString(pInfo
);
2544 pInfo
->curr
--; // Must back off the charcter we just read
2545 if (requireObject
) {
2546 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unexpected character '0x%x' at line %d"), ch
, lineNumber(pInfo
));
2552 static CFTypeRef
parseOldStylePropertyListOrStringsFile(_CFXMLPlistParseInfo
*pInfo
) {
2553 const UniChar
*begin
= pInfo
->curr
;
2555 advanceToNonSpace(pInfo
);
2556 // A file consisting only of whitespace (or empty) is now defined to be an empty dictionary
2557 if (pInfo
->curr
>= pInfo
->end
) return CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2558 result
= parsePlistObject(pInfo
, true);
2559 advanceToNonSpace(pInfo
);
2560 if (pInfo
->curr
>= pInfo
->end
) return result
;
2561 if (!result
) return NULL
;
2562 if (CFGetTypeID(result
) != stringtype
) {
2564 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Junk after plist at line %d"), lineNumber(pInfo
));
2568 // Check for a strings file (looks like a dictionary without the opening/closing curly braces)
2569 pInfo
->curr
= begin
;
2570 return parsePlistDictContent(pInfo
);
2573 #undef isValidUnquotedStringCharacter
2575 static CFArrayRef
_arrayDeepImmutableCopy(CFAllocatorRef allocator
, CFArrayRef array
, CFOptionFlags mutabilityOption
) {
2576 CFArrayRef result
= NULL
;
2577 CFIndex i
, c
= CFArrayGetCount(array
);
2580 result
= CFArrayCreate(allocator
, NULL
, 0, &kCFTypeArrayCallBacks
);
2581 } else if ((values
= (CFTypeRef
*)CFAllocatorAllocate(allocator
, c
*sizeof(CFTypeRef
), 0)) != NULL
) {
2582 CFArrayGetValues(array
, CFRangeMake(0, c
), values
);
2583 for (i
= 0; i
< c
; i
++) {
2584 values
[i
] = CFPropertyListCreateDeepCopy(allocator
, values
[i
], mutabilityOption
);
2585 if (values
[i
] == NULL
) {
2589 result
= (i
== c
) ? CFArrayCreate(allocator
, values
, c
, &kCFTypeArrayCallBacks
) : NULL
;
2591 for (i
= 0; i
< c
; i
++) {
2592 CFRelease(values
[i
]);
2594 CFAllocatorDeallocate(allocator
, values
);
2599 static CFMutableArrayRef
_arrayDeepMutableCopy(CFAllocatorRef allocator
, CFArrayRef array
, CFOptionFlags mutabilityOption
) {
2600 CFIndex i
, c
= CFArrayGetCount(array
);
2601 CFMutableArrayRef result
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2603 for (i
= 0; i
< c
; i
++) {
2604 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, CFArrayGetValueAtIndex(array
, i
), mutabilityOption
);
2605 if (!newValue
) break;
2606 CFArrayAppendValue(result
, newValue
);
2607 CFRelease(newValue
);
2617 CFPropertyListRef
CFPropertyListCreateDeepCopy(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, CFOptionFlags mutabilityOption
) {
2620 CFPropertyListRef result
= NULL
;
2621 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): cannot copy a NULL property list", __PRETTY_FUNCTION__
);
2622 __CFAssertIsPList(propertyList
);
2623 CFAssert2(mutabilityOption
== kCFPropertyListImmutable
|| mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, mutabilityOption
);
2624 if (!CFPropertyListIsValid(propertyList
, kCFPropertyListBinaryFormat_v1_0
)) return NULL
;
2626 if (allocator
== NULL
) {
2627 allocator
= (CFAllocatorRef
)CFRetain(__CFGetDefaultAllocator());
2629 CFRetain(allocator
);
2632 typeID
= CFGetTypeID(propertyList
);
2633 if (typeID
== dicttype
) {
2634 CFDictionaryRef dict
= (CFDictionaryRef
)propertyList
;
2635 Boolean isMutable
= (mutabilityOption
!= kCFPropertyListImmutable
);
2636 CFIndex count
= CFDictionaryGetCount(dict
);
2637 CFTypeRef
*keys
, *values
;
2639 result
= isMutable
? CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
): CFDictionaryCreate(allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2640 } else if ((keys
= (CFTypeRef
*)CFAllocatorAllocate(allocator
, 2 * count
* sizeof(CFTypeRef
), 0)) != NULL
) {
2642 values
= keys
+count
;
2643 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
2644 for (i
= 0; i
< count
; i
++) {
2645 keys
[i
] = CFStringCreateCopy(allocator
, (CFStringRef
)keys
[i
]);
2646 if (keys
[i
] == NULL
) {
2649 values
[i
] = CFPropertyListCreateDeepCopy(allocator
, values
[i
], mutabilityOption
);
2650 if (values
[i
] == NULL
) {
2656 result
= isMutable
? CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
) : CFDictionaryCreate(allocator
, keys
, values
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2657 for (i
= 0; i
< count
; i
++) {
2659 CFDictionarySetValue((CFMutableDictionaryRef
)result
, keys
[i
], values
[i
]);
2662 CFRelease(values
[i
]);
2667 for (i
= 0; i
< count
; i
++) {
2669 CFRelease(values
[i
]);
2672 CFAllocatorDeallocate(allocator
, keys
);
2676 } else if (typeID
== arraytype
) {
2677 if (mutabilityOption
== kCFPropertyListImmutable
) {
2678 result
= _arrayDeepImmutableCopy(allocator
, (CFArrayRef
)propertyList
, mutabilityOption
);
2680 result
= _arrayDeepMutableCopy(allocator
, (CFArrayRef
)propertyList
, mutabilityOption
);
2682 } else if (typeID
== datatype
) {
2683 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
2684 result
= CFDataCreateMutableCopy(allocator
, 0, (CFDataRef
)propertyList
);
2686 result
= CFDataCreateCopy(allocator
, (CFDataRef
)propertyList
);
2688 } else if (typeID
== numbertype
) {
2689 // Warning - this will break if byteSize is ever greater than 32
2691 CFNumberType numType
= CFNumberGetType((CFNumberRef
)propertyList
);
2692 CFNumberGetValue((CFNumberRef
)propertyList
, numType
, (void *)bytes
);
2693 result
= CFNumberCreate(allocator
, numType
, (void *)bytes
);
2694 } else if (typeID
== booltype
) {
2695 // Booleans are immutable & shared instances
2696 CFRetain(propertyList
);
2697 result
= propertyList
;
2698 } else if (typeID
== datetype
) {
2699 // Dates are immutable
2700 result
= CFDateCreate(allocator
, CFDateGetAbsoluteTime((CFDateRef
)propertyList
));
2701 } else if (typeID
== stringtype
) {
2702 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
2703 result
= CFStringCreateMutableCopy(allocator
, 0, (CFStringRef
)propertyList
);
2705 result
= CFStringCreateCopy(allocator
, (CFStringRef
)propertyList
);
2708 CFAssert2(false, __kCFLogAssertion
, "%s(): %p is not a property list type", __PRETTY_FUNCTION__
, propertyList
);
2711 CFRelease(allocator
);