1/*
2 * Copyright (c) 2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*	CFPropertyList.c
25	Copyright (c) 1999-2013, Apple Inc. All rights reserved.
26	Responsibility: Tony Parker
27*/
28
29#include <CoreFoundation/CFPropertyList.h>
30#include <CoreFoundation/CFDate.h>
31#include <CoreFoundation/CFNumber.h>
32#include <CoreFoundation/CFSet.h>
33#include <CoreFoundation/CFError.h>
34#include <CoreFoundation/CFError_Private.h>
35#include <CoreFoundation/CFPriv.h>
36#include <CoreFoundation/CFStringEncodingConverter.h>
37#include "CFInternal.h"
38#include <CoreFoundation/CFBurstTrie.h>
39#include <CoreFoundation/CFString.h>
40#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
41#include <CoreFoundation/CFStream.h>
42#endif
43#include <CoreFoundation/CFCalendar.h>
44#include "CFLocaleInternal.h"
45#include <limits.h>
46#include <float.h>
47#include <string.h>
48#include <stdlib.h>
49#include <math.h>
50#include <ctype.h>
51
52
53CF_EXPORT CFNumberType _CFNumberGetType2(CFNumberRef number);
54
55#define PLIST_IX    0
56#define ARRAY_IX    1
57#define DICT_IX     2
58#define KEY_IX      3
59#define STRING_IX   4
60#define DATA_IX     5
61#define DATE_IX     6
62#define REAL_IX     7
63#define INTEGER_IX  8
64#define TRUE_IX     9
65#define FALSE_IX    10
66#define DOCTYPE_IX  11
67#define CDSECT_IX   12
68
69#define PLIST_TAG_LENGTH	5
70#define ARRAY_TAG_LENGTH	5
71#define DICT_TAG_LENGTH		4
72#define KEY_TAG_LENGTH		3
73#define STRING_TAG_LENGTH	6
74#define DATA_TAG_LENGTH		4
75#define DATE_TAG_LENGTH		4
76#define REAL_TAG_LENGTH		4
77#define INTEGER_TAG_LENGTH	7
78#define TRUE_TAG_LENGTH		4
79#define FALSE_TAG_LENGTH	5
80#define DOCTYPE_TAG_LENGTH	7
81#define CDSECT_TAG_LENGTH	9
82
83#if !defined(new_cftype_array)
84#define new_cftype_array(N, C) \
85    size_t N ## _count__ = (C); \
86    if (N ## _count__ > LONG_MAX / sizeof(CFTypeRef)) { \
87        CRSetCrashLogMessage("CFPropertyList ran out of memory while attempting to allocate temporary storage."); \
88        HALT; \
89    } \
90    Boolean N ## _is_stack__ = (N ## _count__ <= 256); \
91    if (N ## _count__ == 0) N ## _count__ = 1; \
92    STACK_BUFFER_DECL(CFTypeRef, N ## _buffer__, N ## _is_stack__ ? N ## _count__ : 1); \
93    if (N ## _is_stack__) memset(N ## _buffer__, 0, N ## _count__ * sizeof(CFTypeRef)); \
94    CFTypeRef * N = N ## _is_stack__ ? N ## _buffer__ : (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, (N ## _count__) * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory); \
95    if (! N) { \
96        CRSetCrashLogMessage("CFPropertyList ran out of memory while attempting to allocate temporary storage."); \
97        HALT; \
98    } \
99    do {} while (0)
100#endif
101
102#if !defined(free_cftype_array)
103#define free_cftype_array(N) \
104    if (! N ## _is_stack__) { \
105        CFAllocatorDeallocate(kCFAllocatorSystemDefault, N); \
106    } \
107    do {} while (0)
108#endif
109
110// Used to reference an old-style plist parser error inside of a more general XML error
111#define CFPropertyListOldStyleParserErrorKey     CFSTR("kCFPropertyListOldStyleParsingError")
112
113static CFTypeID stringtype, datatype, numbertype, datetype;
114static CFTypeID booltype, nulltype, dicttype, arraytype, settype;
115
116static void initStatics() {
117    static dispatch_once_t once;
118    dispatch_once(&once, ^{
119        stringtype = CFStringGetTypeID();
120        datatype = CFDataGetTypeID();
121        numbertype = CFNumberGetTypeID();
122        booltype = CFBooleanGetTypeID();
123        datetype = CFDateGetTypeID();
124        dicttype = CFDictionaryGetTypeID();
125        arraytype = CFArrayGetTypeID();
126        settype = CFSetGetTypeID();
127        nulltype = CFNullGetTypeID();
128    });
129}
130
131CF_PRIVATE CFErrorRef __CFPropertyListCreateError(CFIndex code, CFStringRef debugString, ...) {
132    va_list argList;
133    CFErrorRef error = NULL;
134
135    if (debugString != NULL) {
136        CFStringRef debugMessage = NULL;
137        va_start(argList, debugString);
138        debugMessage = CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault, NULL, debugString, argList);
139        va_end(argList);
140
141        CFMutableDictionaryRef userInfo = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
142        CFDictionarySetValue(userInfo, kCFErrorDebugDescriptionKey, debugMessage);
143
144        error = CFErrorCreate(kCFAllocatorSystemDefault, kCFErrorDomainCocoa, code, userInfo);
145
146        CFRelease(debugMessage);
147        CFRelease(userInfo);
148    } else {
149        error = CFErrorCreate(kCFAllocatorSystemDefault, kCFErrorDomainCocoa, code, NULL);
150    }
151
152    return error;
153}
154
155static CFStringRef __copyErrorDebugDescription(CFErrorRef error) {
156    CFStringRef result = NULL;
157    if (error) {
158        CFDictionaryRef userInfo = CFErrorCopyUserInfo(error);
159        if (userInfo != NULL) {
160            CFStringRef desc = (CFStringRef) CFDictionaryGetValue(userInfo, kCFErrorDebugDescriptionKey);
161            if (desc != NULL) {
162                result = CFStringCreateCopy(kCFAllocatorSystemDefault, desc);
163            }
164            CFRelease(userInfo);
165        }
166    }
167    return result;
168}
169
170#pragma mark -
171#pragma mark Property List Validation
172
173// don't allow _CFKeyedArchiverUID here
174#define __CFAssertIsPList(cf) CFAssert2(CFGetTypeID(cf) == CFStringGetTypeID() || CFGetTypeID(cf) == CFArrayGetTypeID() || CFGetTypeID(cf) == CFBooleanGetTypeID() || CFGetTypeID(cf) == CFNumberGetTypeID() || CFGetTypeID(cf) == CFDictionaryGetTypeID() || CFGetTypeID(cf) == CFDateGetTypeID() || CFGetTypeID(cf) == CFDataGetTypeID(), __kCFLogAssertion, "%s(): %p not of a property list type", __PRETTY_FUNCTION__, cf);
175
176struct context {
177    bool answer;
178    CFMutableSetRef set;
179    CFPropertyListFormat format;
180    CFStringRef *error;
181};
182
183static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, CFMutableSetRef set, CFPropertyListFormat format, CFStringRef *error);
184
185static void __CFPropertyListIsArrayPlistAux(const void *value, void *context) {
186    struct context *ctx = (struct context *)context;
187    if (!ctx->answer) return;
188    if (!value && !*(ctx->error)) {
189	*(ctx->error) = (CFStringRef)CFRetain(CFSTR("property list arrays cannot contain NULL"));
190    }
191    ctx->answer = value && __CFPropertyListIsValidAux(value, true, ctx->set, ctx->format, ctx->error);
192}
193
194static void __CFPropertyListIsDictPlistAux(const void *key, const void *value, void *context) {
195    struct context *ctx = (struct context *)context;
196    if (!ctx->answer) return;
197    if (!key && !*(ctx->error)) *(ctx->error) = (CFStringRef)CFRetain(CFSTR("property list dictionaries cannot contain NULL keys"));
198    if (!value && !*(ctx->error)) *(ctx->error) = (CFStringRef)CFRetain(CFSTR("property list dictionaries cannot contain NULL values"));
199    if (stringtype != CFGetTypeID(key) && !*(ctx->error)) {
200	CFStringRef desc = CFCopyTypeIDDescription(CFGetTypeID(key));
201	*(ctx->error) = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("property list dictionaries may only have keys which are CFStrings, not '%@'"), desc);
202	CFRelease(desc);
203    }
204    ctx->answer = key && value && (stringtype == CFGetTypeID(key)) && __CFPropertyListIsValidAux(value, true, ctx->set, ctx->format, ctx->error);
205}
206
207static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, CFMutableSetRef set, CFPropertyListFormat format, CFStringRef *error) {
208    CFTypeID type;
209    if (!plist) {
210	*error = (CFStringRef)CFRetain(CFSTR("property lists cannot contain NULL"));
211    	return false;
212    }
213    type = CFGetTypeID(plist);
214    if (stringtype == type) return true;
215    if (datatype == type) return true;
216    if (kCFPropertyListOpenStepFormat != format) {
217	if (booltype == type) return true;
218	if (numbertype == type) return true;
219	if (datetype == type) return true;
220	if (_CFKeyedArchiverUIDGetTypeID() == type) return true;
221    }
222    if (!recursive && arraytype == type) return true;
223    if (!recursive && dicttype == type) return true;
224    // at any one invocation of this function, set should contain the objects in the "path" down to this object
225    if (CFSetContainsValue(set, plist)) {
226	*error = (CFStringRef)CFRetain(CFSTR("property lists cannot contain recursive container references"));
227	return false;
228    }
229    if (arraytype == type) {
230	struct context ctx = {true, set, format, error};
231	CFSetAddValue(set, plist);
232	CFArrayApplyFunction((CFArrayRef)plist, CFRangeMake(0, CFArrayGetCount((CFArrayRef)plist)), __CFPropertyListIsArrayPlistAux, &ctx);
233	CFSetRemoveValue(set, plist);
234	return ctx.answer;
235    }
236    if (dicttype == type) {
237	struct context ctx = {true, set, format, error};
238	CFSetAddValue(set, plist);
239	CFDictionaryApplyFunction((CFDictionaryRef)plist, __CFPropertyListIsDictPlistAux, &ctx);
240	CFSetRemoveValue(set, plist);
241	return ctx.answer;
242    }
243
244    CFStringRef desc = CFCopyTypeIDDescription(type);
245    *error = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("property lists cannot contain objects of type '%@'"), desc);
246    CFRelease(desc);
247
248    return false;
249}
250
251static Boolean _CFPropertyListIsValidWithErrorString(CFPropertyListRef plist, CFPropertyListFormat format, CFStringRef *error) {
252    initStatics();
253    CFAssert1(plist != NULL, __kCFLogAssertion, "%s(): NULL is not a property list", __PRETTY_FUNCTION__);
254    CFMutableSetRef set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
255    bool result = __CFPropertyListIsValidAux(plist, true, set, format, error);
256    CFRelease(set);
257    return result;
258}
259
260#pragma mark -
261#pragma mark Writing Property Lists
262
263static const char CFXMLPlistTags[13][10]= {
264{'p', 'l', 'i', 's', 't',   '\0', '\0', '\0', '\0', '\0'},
265{'a', 'r', 'r', 'a', 'y',   '\0', '\0', '\0', '\0', '\0'},
266{'d', 'i', 'c', 't',  '\0', '\0', '\0', '\0', '\0', '\0'},
267{'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
268{'s', 't', 'r', 'i', 'n', 'g',    '\0', '\0', '\0', '\0'},
269{'d', 'a', 't', 'a',  '\0', '\0', '\0', '\0', '\0', '\0'},
270{'d', 'a', 't', 'e',  '\0', '\0', '\0', '\0', '\0', '\0'},
271{'r', 'e', 'a', 'l',  '\0', '\0', '\0', '\0', '\0', '\0'},
272{'i', 'n', 't', 'e', 'g', 'e', 'r',     '\0', '\0', '\0'},
273{'t', 'r', 'u', 'e',  '\0', '\0', '\0', '\0', '\0', '\0'},
274{'f', 'a', 'l', 's', 'e',   '\0', '\0', '\0', '\0', '\0'},
275{'D', 'O', 'C', 'T', 'Y', 'P', 'E',     '\0', '\0', '\0'},
276{'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[',       '\0'}
277};
278
279static const UniChar CFXMLPlistTagsUnicode[13][10]= {
280    {'p', 'l', 'i', 's', 't',   '\0', '\0', '\0', '\0', '\0'},
281    {'a', 'r', 'r', 'a', 'y',   '\0', '\0', '\0', '\0', '\0'},
282    {'d', 'i', 'c', 't',  '\0', '\0', '\0', '\0', '\0', '\0'},
283    {'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
284    {'s', 't', 'r', 'i', 'n', 'g',    '\0', '\0', '\0', '\0'},
285    {'d', 'a', 't', 'a',  '\0', '\0', '\0', '\0', '\0', '\0'},
286    {'d', 'a', 't', 'e',  '\0', '\0', '\0', '\0', '\0', '\0'},
287    {'r', 'e', 'a', 'l',  '\0', '\0', '\0', '\0', '\0', '\0'},
288    {'i', 'n', 't', 'e', 'g', 'e', 'r',     '\0', '\0', '\0'},
289    {'t', 'r', 'u', 'e',  '\0', '\0', '\0', '\0', '\0', '\0'},
290    {'f', 'a', 'l', 's', 'e',   '\0', '\0', '\0', '\0', '\0'},
291    {'D', 'O', 'C', 'T', 'Y', 'P', 'E',     '\0', '\0', '\0'},
292    {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[',       '\0'}
293};
294
295typedef struct {
296    const char *begin; // first character of the XML to be parsed
297    const char *curr;  // current parse location
298    const char *end;   // the first character _after_ the end of the XML
299    CFErrorRef error;
300    CFAllocatorRef allocator;
301    UInt32 mutabilityOption;
302    CFBurstTrieRef stringTrie; // map of cached strings
303    CFMutableArrayRef stringCache; // retaining array of strings
304    Boolean allowNewTypes; // Whether to allow the new types supported by XML property lists, but not by the old, OPENSTEP ASCII property lists (CFNumber, CFBoolean, CFDate)
305    CFSetRef keyPaths; // if NULL, no filtering
306    Boolean skip; // if true, do not create any objects.
307} _CFXMLPlistParseInfo;
308
309CF_PRIVATE CFTypeRef __CFCreateOldStylePropertyListOrStringsFile(CFAllocatorRef allocator, CFDataRef xmlData, CFStringRef originalString, CFStringEncoding guessedEncoding, CFOptionFlags option, CFErrorRef *outError,CFPropertyListFormat *format);
310
311CF_INLINE void __CFPListRelease(CFTypeRef cf, CFAllocatorRef allocator) {
312    if (cf && !(0)) CFRelease(cf);
313}
314
315
316// The following set of _plist... functions append various things to a mutable data which is in UTF8 encoding. These are pretty general. Assumption is call characters and CFStrings can be converted to UTF8 and appeneded.
317
318// Null-terminated, ASCII or UTF8 string
319//
320static void _plistAppendUTF8CString(CFMutableDataRef mData, const char *cString) {
321    CFDataAppendBytes (mData, (const UInt8 *)cString, strlen(cString));
322}
323
324// UniChars
325//
326static void _plistAppendCharacters(CFMutableDataRef mData, const UniChar *chars, CFIndex length) {
327    CFIndex curLoc = 0;
328
329    do {	// Flush out ASCII chars, BUFLEN at a time
330        #define BUFLEN 400
331	UInt8 buf[BUFLEN], *bufPtr = buf;
332	CFIndex cnt = 0;
333        while (cnt < length && (cnt - curLoc < BUFLEN) && (chars[cnt] < 128)) *bufPtr++ = (UInt8)(chars[cnt++]);
334        if (cnt > curLoc) {	// Flush any ASCII bytes
335            CFDataAppendBytes(mData, buf, cnt - curLoc);
336            curLoc = cnt;
337        }
338    } while (curLoc < length && (chars[curLoc] < 128));	// We will exit out of here when we run out of chars or hit a non-ASCII char
339
340    if (curLoc < length) {	// Now deal with non-ASCII chars
341        CFDataRef data = NULL;
342        CFStringRef str = NULL;
343        if ((str = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, chars + curLoc, length - curLoc, kCFAllocatorNull))) {
344            if ((data = CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault, str, kCFStringEncodingUTF8, 0))) {
345                CFDataAppendBytes (mData, CFDataGetBytePtr(data), CFDataGetLength(data));
346                CFRelease(data);
347            }
348            CFRelease(str);
349        }
350        CFAssert1(str && data, __kCFLogAssertion, "%s(): Error writing plist", __PRETTY_FUNCTION__);
351    }
352}
353
354// Append CFString
355//
356static void _plistAppendString(CFMutableDataRef mData, CFStringRef str) {
357    const UniChar *chars;
358    const char *cStr;
359    CFDataRef data;
360    if ((chars = CFStringGetCharactersPtr(str))) {
361        _plistAppendCharacters(mData, chars, CFStringGetLength(str));
362    } else if ((cStr = CFStringGetCStringPtr(str, kCFStringEncodingASCII)) || (cStr = CFStringGetCStringPtr(str, kCFStringEncodingUTF8))) {
363        _plistAppendUTF8CString(mData, cStr);
364    } else if ((data = CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault, str, kCFStringEncodingUTF8, 0))) {
365        CFDataAppendBytes (mData, CFDataGetBytePtr(data), CFDataGetLength(data));
366        CFRelease(data);
367    } else {
368	CFAssert1(TRUE, __kCFLogAssertion, "%s(): Error in plist writing", __PRETTY_FUNCTION__);
369    }
370}
371
372
373// Append CFString-style format + arguments
374//
375static void _plistAppendFormat(CFMutableDataRef mData, CFStringRef format, ...) {
376    CFStringRef fStr;
377    va_list argList;
378
379    va_start(argList, format);
380    fStr = CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault, NULL, format, argList);
381    va_end(argList);
382
383    CFAssert1(fStr, __kCFLogAssertion, "%s(): Error writing plist", __PRETTY_FUNCTION__);
384    _plistAppendString(mData, fStr);
385    CFRelease(fStr);
386}
387
388
389
390static void _appendIndents(CFIndex numIndents, CFMutableDataRef str) {
391#define NUMTABS 4
392    static const UniChar tabs[NUMTABS] = {'\t','\t','\t','\t'};
393    for (; numIndents > 0; numIndents -= NUMTABS) _plistAppendCharacters(str, tabs, (numIndents >= NUMTABS) ? NUMTABS : numIndents);
394}
395
396/* Append the escaped version of origStr to mStr.
397*/
398static void _appendEscapedString(CFStringRef origStr, CFMutableDataRef mStr) {
399#define BUFSIZE 64
400    CFIndex i, length = CFStringGetLength(origStr);
401    CFIndex bufCnt = 0;
402    UniChar buf[BUFSIZE];
403    CFStringInlineBuffer inlineBuffer;
404
405    CFStringInitInlineBuffer(origStr, &inlineBuffer, CFRangeMake(0, length));
406
407    for (i = 0; i < length; i ++) {
408	UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&inlineBuffer, i);
409	if (CFStringIsSurrogateHighCharacter(ch) && (bufCnt + 2 >= BUFSIZE)) {
410	    // flush the buffer first so we have room for a low/high pair and do not split them
411	    _plistAppendCharacters(mStr, buf, bufCnt);
412	    bufCnt = 0;
413	}
414
415        switch(ch) {
416            case '<':
417		if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt);
418		bufCnt = 0;
419	  	_plistAppendUTF8CString(mStr, "&lt;");
420                break;
421            case '>':
422		if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt);
423		bufCnt = 0;
424	  	_plistAppendUTF8CString(mStr, "&gt;");
425                break;
426            case '&':
427		if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt);
428		bufCnt = 0;
429	  	_plistAppendUTF8CString(mStr, "&amp;");
430                break;
431            default:
432		buf[bufCnt++] = ch;
433		if (bufCnt == BUFSIZE) {
434		    _plistAppendCharacters(mStr, buf, bufCnt);
435		    bufCnt = 0;
436		}
437		break;
438        }
439    }
440    if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt);
441}
442
443
444
445/* Base-64 encoding/decoding */
446
447/* The base-64 encoding packs three 8-bit bytes into four 7-bit ASCII
448 * characters.  If the number of bytes in the original data isn't divisable
449 * by three, "=" characters are used to pad the encoded data.  The complete
450 * set of characters used in base-64 are:
451 *
452 *      'A'..'Z' => 00..25
453 *      'a'..'z' => 26..51
454 *      '0'..'9' => 52..61
455 *      '+'      => 62
456 *      '/'      => 63
457 *      '='      => pad
458 */
459
460// Write the inputData to the mData using Base 64 encoding
461
462static void _XMLPlistAppendDataUsingBase64(CFMutableDataRef mData, CFDataRef inputData, CFIndex indent) {
463    static const char __CFPLDataEncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
464    #define MAXLINELEN 76
465    char buf[MAXLINELEN + 4 + 2];	// For the slop and carriage return and terminating NULL
466
467    const uint8_t *bytes = CFDataGetBytePtr(inputData);
468    CFIndex length = CFDataGetLength(inputData);
469    CFIndex i, pos;
470    const uint8_t *p;
471
472    if (indent > 8) indent = 8; // refuse to indent more than 64 characters
473
474    pos = 0;		// position within buf
475
476    for (i = 0, p = bytes; i < length; i++, p++) {
477        /* 3 bytes are encoded as 4 */
478        switch (i % 3) {
479            case 0:
480                buf[pos++] = __CFPLDataEncodeTable [ ((p[0] >> 2) & 0x3f)];
481                break;
482            case 1:
483                buf[pos++] = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 4) & 0x3f)];
484                break;
485            case 2:
486                buf[pos++] = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 6) & 0x3f)];
487                buf[pos++] = __CFPLDataEncodeTable [ (p[0] & 0x3f)];
488                break;
489        }
490        /* Flush the line out every 76 (or fewer) chars --- indents count against the line length*/
491        if (pos >= MAXLINELEN - 8 * indent) {
492            buf[pos++] = '\n';
493            buf[pos++] = 0;
494            _appendIndents(indent, mData);
495            _plistAppendUTF8CString(mData, buf);
496            pos = 0;
497        }
498    }
499
500    switch (i % 3) {
501	case 0:
502            break;
503	case 1:
504            buf[pos++] = __CFPLDataEncodeTable [ ((p[-1] << 4) & 0x30)];
505            buf[pos++] = '=';
506            buf[pos++] = '=';
507            break;
508	case 2:
509            buf[pos++] =  __CFPLDataEncodeTable [ ((p[-1] << 2) & 0x3c)];
510            buf[pos++] = '=';
511            break;
512    }
513
514    if (pos > 0) {
515        buf[pos++] = '\n';
516        buf[pos++] = 0;
517        _appendIndents(indent, mData);
518        _plistAppendUTF8CString(mData, buf);
519    }
520}
521
522extern CFStringRef __CFNumberCopyFormattingDescriptionAsFloat64(CFTypeRef cf);
523
524static void _CFAppendXML0(CFTypeRef object, UInt32 indentation, CFMutableDataRef xmlString) {
525    UInt32 typeID = CFGetTypeID(object);
526    _appendIndents(indentation, xmlString);
527    if (typeID == stringtype) {
528        _plistAppendUTF8CString(xmlString, "<");
529        _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[STRING_IX], STRING_TAG_LENGTH);
530        _plistAppendUTF8CString(xmlString, ">");
531	_appendEscapedString((CFStringRef)object, xmlString);
532        _plistAppendUTF8CString(xmlString, "</");
533        _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[STRING_IX], STRING_TAG_LENGTH);
534        _plistAppendUTF8CString(xmlString, ">\n");
535    } else if (typeID == arraytype) {
536        UInt32 i, count = CFArrayGetCount((CFArrayRef)object);
537        if (count == 0) {
538            _plistAppendUTF8CString(xmlString, "<");
539            _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[ARRAY_IX], ARRAY_TAG_LENGTH);
540            _plistAppendUTF8CString(xmlString, "/>\n");
541            return;
542        }
543        _plistAppendUTF8CString(xmlString, "<");
544        _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[ARRAY_IX], ARRAY_TAG_LENGTH);
545        _plistAppendUTF8CString(xmlString, ">\n");
546        for (i = 0; i < count; i ++) {
547            _CFAppendXML0(CFArrayGetValueAtIndex((CFArrayRef)object, i), indentation+1, xmlString);
548        }
549        _appendIndents(indentation, xmlString);
550        _plistAppendUTF8CString(xmlString, "</");
551        _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[ARRAY_IX], ARRAY_TAG_LENGTH);
552        _plistAppendUTF8CString(xmlString, ">\n");
553    } else if (typeID == dicttype) {
554        UInt32 i, count = CFDictionaryGetCount((CFDictionaryRef)object);
555        CFMutableArrayRef keyArray;
556        if (count == 0) {
557            _plistAppendUTF8CString(xmlString, "<");
558            _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DICT_IX], DICT_TAG_LENGTH);
559            _plistAppendUTF8CString(xmlString, "/>\n");
560            return;
561        }
562        _plistAppendUTF8CString(xmlString, "<");
563        _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DICT_IX], DICT_TAG_LENGTH);
564        _plistAppendUTF8CString(xmlString, ">\n");
565        new_cftype_array(keys, count);
566        CFDictionaryGetKeysAndValues((CFDictionaryRef)object, keys, NULL);
567        keyArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, count, &kCFTypeArrayCallBacks);
568        CFArrayReplaceValues(keyArray, CFRangeMake(0, 0), keys, count);
569        CFArraySortValues(keyArray, CFRangeMake(0, count), (CFComparatorFunction)CFStringCompare, NULL);
570        CFArrayGetValues(keyArray, CFRangeMake(0, count), keys);
571        CFRelease(keyArray);
572        for (i = 0; i < count; i ++) {
573            CFTypeRef key = keys[i];
574            _appendIndents(indentation+1, xmlString);
575            _plistAppendUTF8CString(xmlString, "<");
576            _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[KEY_IX], KEY_TAG_LENGTH);
577            _plistAppendUTF8CString(xmlString, ">");
578	    _appendEscapedString((CFStringRef)key, xmlString);
579            _plistAppendUTF8CString(xmlString, "</");
580            _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[KEY_IX], KEY_TAG_LENGTH);
581            _plistAppendUTF8CString(xmlString, ">\n");
582            _CFAppendXML0(CFDictionaryGetValue((CFDictionaryRef)object, key), indentation+1, xmlString);
583        }
584        free_cftype_array(keys);
585        _appendIndents(indentation, xmlString);
586        _plistAppendUTF8CString(xmlString, "</");
587        _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DICT_IX], DICT_TAG_LENGTH);
588        _plistAppendUTF8CString(xmlString, ">\n");
589    } else if (typeID == datatype) {
590        _plistAppendUTF8CString(xmlString, "<");
591        _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DATA_IX], DATA_TAG_LENGTH);
592        _plistAppendUTF8CString(xmlString, ">\n");
593        _XMLPlistAppendDataUsingBase64(xmlString, (CFDataRef)object, indentation);
594        _appendIndents(indentation, xmlString);
595        _plistAppendUTF8CString(xmlString, "</");
596        _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DATA_IX], DATA_TAG_LENGTH);
597        _plistAppendUTF8CString(xmlString, ">\n");
598    } else if (typeID == datetype) {
599        // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
600	int32_t y = 0, M = 0, d = 0, H = 0, m = 0, s = 0;
601#if 1
602        CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime((CFDateRef)object), NULL);
603	y = date.year;
604	M = date.month;
605	d = date.day;
606	H = date.hour;
607	m = date.minute;
608	s = (int32_t)date.second;
609#else
610	CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, kCFCalendarIdentifierGregorian);
611	CFTimeZoneRef tz = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, CFSTR("GMT"), true);
612	CFCalendarSetTimeZone(calendar, tz);
613	CFCalendarDecomposeAbsoluteTime(calendar, CFDateGetAbsoluteTime((CFDateRef)object), (const uint8_t *)"yMdHms", &y, &M, &d, &H, &m, &s);
614	CFRelease(calendar);
615	CFRelease(tz);
616#endif
617        _plistAppendUTF8CString(xmlString, "<");
618        _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DATE_IX], DATE_TAG_LENGTH);
619        _plistAppendUTF8CString(xmlString, ">");
620        _plistAppendFormat(xmlString, CFSTR("%04d-%02d-%02dT%02d:%02d:%02dZ"), y, M, d, H, m, s);
621        _plistAppendUTF8CString(xmlString, "</");
622        _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DATE_IX], DATE_TAG_LENGTH);
623        _plistAppendUTF8CString(xmlString, ">\n");
624    } else if (typeID == numbertype) {
625        if (CFNumberIsFloatType((CFNumberRef)object)) {
626            _plistAppendUTF8CString(xmlString, "<");
627            _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[REAL_IX], REAL_TAG_LENGTH);
628            _plistAppendUTF8CString(xmlString, ">");
629                CFStringRef s = __CFNumberCopyFormattingDescriptionAsFloat64(object);
630                _plistAppendString(xmlString, s);
631                CFRelease(s);
632            _plistAppendUTF8CString(xmlString, "</");
633            _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[REAL_IX], REAL_TAG_LENGTH);
634            _plistAppendUTF8CString(xmlString, ">\n");
635        } else {
636            _plistAppendUTF8CString(xmlString, "<");
637            _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[INTEGER_IX], INTEGER_TAG_LENGTH);
638            _plistAppendUTF8CString(xmlString, ">");
639
640            _plistAppendFormat(xmlString, CFSTR("%@"), object);
641
642            _plistAppendUTF8CString(xmlString, "</");
643            _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[INTEGER_IX], INTEGER_TAG_LENGTH);
644            _plistAppendUTF8CString(xmlString, ">\n");
645        }
646    } else if (typeID == booltype) {
647        if (CFBooleanGetValue((CFBooleanRef)object)) {
648            _plistAppendUTF8CString(xmlString, "<");
649            _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[TRUE_IX], TRUE_TAG_LENGTH);
650            _plistAppendUTF8CString(xmlString, "/>\n");
651        } else {
652            _plistAppendUTF8CString(xmlString, "<");
653            _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[FALSE_IX], FALSE_TAG_LENGTH);
654            _plistAppendUTF8CString(xmlString, "/>\n");
655        }
656    }
657}
658
659static void _CFGenerateXMLPropertyListToData(CFMutableDataRef xml, CFTypeRef propertyList) {
660    _plistAppendUTF8CString(xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE ");
661    _plistAppendCharacters(xml, CFXMLPlistTagsUnicode[PLIST_IX], PLIST_TAG_LENGTH);
662    _plistAppendUTF8CString(xml, " PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<");
663    _plistAppendCharacters(xml, CFXMLPlistTagsUnicode[PLIST_IX], PLIST_TAG_LENGTH);
664    _plistAppendUTF8CString(xml, " version=\"1.0\">\n");
665
666    _CFAppendXML0(propertyList, 0, xml);
667
668    _plistAppendUTF8CString(xml, "</");
669    _plistAppendCharacters(xml, CFXMLPlistTagsUnicode[PLIST_IX], PLIST_TAG_LENGTH);
670    _plistAppendUTF8CString(xml, ">\n");
671}
672
673// ========================================================================
674#pragma mark -
675#pragma mark Exported Creation Functions
676
677CFDataRef _CFPropertyListCreateXMLData(CFAllocatorRef allocator, CFPropertyListRef propertyList, Boolean checkValidPlist) {
678    initStatics();
679    CFMutableDataRef xml;
680    CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__);
681    if (checkValidPlist && !CFPropertyListIsValid(propertyList, kCFPropertyListXMLFormat_v1_0)) {
682        __CFAssertIsPList(propertyList);
683        return NULL;
684    }
685    xml = CFDataCreateMutable(allocator, 0);
686    _CFGenerateXMLPropertyListToData(xml, propertyList);
687    return xml;
688}
689
690CFDataRef CFPropertyListCreateXMLData(CFAllocatorRef allocator, CFPropertyListRef propertyList) {
691    return _CFPropertyListCreateXMLData(allocator, propertyList, true);
692}
693
694CF_EXPORT CFDataRef _CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator, CFPropertyListRef propertyList) {
695    return _CFPropertyListCreateXMLData(allocator, propertyList, false);
696}
697
698Boolean CFPropertyListIsValid(CFPropertyListRef plist, CFPropertyListFormat format) {
699    initStatics();
700    CFAssert1(plist != NULL, __kCFLogAssertion, "%s(): NULL is not a property list", __PRETTY_FUNCTION__);
701    CFMutableSetRef set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
702    CFStringRef error = NULL;
703    bool result = __CFPropertyListIsValidAux(plist, true, set, format, &error);
704    if (error) {
705#if defined(DEBUG)
706	CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): %@"), error);
707#endif
708	CFRelease(error);
709    }
710    CFRelease(set);
711    return result;
712}
713
714// ========================================================================
715#pragma mark -
716#pragma mark Reading Plists
717
718//
719// ------------------------- Reading plists ------------------
720//
721
722static void skipInlineDTD(_CFXMLPlistParseInfo *pInfo);
723static Boolean parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFTypeRef *out);
724
725// warning: doesn't have a good idea of Unicode line separators
726static UInt32 lineNumber(_CFXMLPlistParseInfo *pInfo) {
727    const char *p = pInfo->begin;
728    UInt32 count = 1;
729    while (p < pInfo->curr) {
730        if (*p == '\r') {
731            count ++;
732            if (*(p + 1) == '\n')
733                p ++;
734        } else if (*p == '\n') {
735            count ++;
736        }
737        p ++;
738    }
739    return count;
740}
741
742// warning: doesn't have a good idea of Unicode white space
743CF_INLINE void skipWhitespace(_CFXMLPlistParseInfo *pInfo) {
744    while (pInfo->curr < pInfo->end) {
745        switch (*(pInfo->curr)) {
746            case ' ':
747            case '\t':
748            case '\n':
749            case '\r':
750                pInfo->curr ++;
751                continue;
752            default:
753                return;
754        }
755    }
756}
757
758/* All of these advance to the end of the given construct and return a pointer to the first character beyond the construct.  If the construct doesn't parse properly, NULL is returned. */
759
760// pInfo should be just past "<!--"
761static void skipXMLComment(_CFXMLPlistParseInfo *pInfo) {
762    const char *p = pInfo->curr;
763    const char *end = pInfo->end - 3; // Need at least 3 characters to compare against
764    while (p < end) {
765        if (*p == '-' && *(p+1) == '-' && *(p+2) == '>') {
766            pInfo->curr = p+3;
767            return;
768        }
769        p ++;
770    }
771    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unterminated comment started on line %d"), lineNumber(pInfo));
772}
773
774// pInfo should be set to the first character after "<?"
775static void skipXMLProcessingInstruction(_CFXMLPlistParseInfo *pInfo) {
776    const char *begin = pInfo->curr, *end = pInfo->end - 2; // Looking for "?>" so we need at least 2 characters
777    while (pInfo->curr < end) {
778        if (*(pInfo->curr) == '?' && *(pInfo->curr+1) == '>') {
779            pInfo->curr += 2;
780            return;
781        }
782        pInfo->curr ++;
783    }
784    pInfo->curr = begin;
785    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing the processing instruction begun on line %d"), lineNumber(pInfo));
786}
787
788// first character should be immediately after the "<!"
789static void skipDTD(_CFXMLPlistParseInfo *pInfo) {
790    // First pass "DOCTYPE"
791    if (pInfo->end - pInfo->curr < DOCTYPE_TAG_LENGTH || memcmp(pInfo->curr, CFXMLPlistTags[DOCTYPE_IX], DOCTYPE_TAG_LENGTH)) {
792        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Malformed DTD on line %d"), lineNumber(pInfo));
793        return;
794    }
795    pInfo->curr += DOCTYPE_TAG_LENGTH;
796    skipWhitespace(pInfo);
797
798    // Look for either the beginning of a complex DTD or the end of the DOCTYPE structure
799    while (pInfo->curr < pInfo->end) {
800        char ch = *(pInfo->curr);
801        if (ch == '[') break; // inline DTD
802        if (ch == '>') {  // End of the DTD
803            pInfo->curr ++;
804            return;
805        }
806        pInfo->curr ++;
807    }
808    if (pInfo->curr == pInfo->end) {
809        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing DTD"));
810        return;
811    }
812
813    // *Sigh* Must parse in-line DTD
814    skipInlineDTD(pInfo);
815    if (pInfo->error)  return;
816    skipWhitespace(pInfo);
817    if (pInfo->error) return;
818    if (pInfo->curr < pInfo->end) {
819        if (*(pInfo->curr) == '>') {
820            pInfo->curr ++;
821        } else {
822            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while parsing DTD"), *(pInfo->curr), lineNumber(pInfo));
823        }
824    } else {
825        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing DTD"));
826    }
827}
828
829static void skipPERef(_CFXMLPlistParseInfo *pInfo) {
830    const char *p = pInfo->curr;
831    while (p < pInfo->end) {
832        if (*p == ';') {
833            pInfo->curr = p+1;
834            return;
835        }
836        p ++;
837    }
838    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing percent-escape sequence begun on line %d"), lineNumber(pInfo));
839}
840
841// First character should be just past '['
842static void skipInlineDTD(_CFXMLPlistParseInfo *pInfo) {
843    while (!pInfo->error && pInfo->curr < pInfo->end) {
844        UniChar ch;
845        skipWhitespace(pInfo);
846        ch = *pInfo->curr;
847        if (ch == '%') {
848            pInfo->curr ++;
849            skipPERef(pInfo);
850        } else if (ch == '<') {
851            pInfo->curr ++;
852            if (pInfo->curr >= pInfo->end) {
853                pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
854                return;
855            }
856            ch = *(pInfo->curr);
857            if (ch == '?') {
858                pInfo->curr ++;
859                skipXMLProcessingInstruction(pInfo);
860            } else if (ch == '!') {
861                if (pInfo->curr + 2 < pInfo->end && (*(pInfo->curr+1) == '-' && *(pInfo->curr+2) == '-')) {
862                    pInfo->curr += 3;
863                    skipXMLComment(pInfo);
864                } else {
865                    // Skip the myriad of DTD declarations of the form "<!string" ... ">"
866                    pInfo->curr ++; // Past both '<' and '!'
867                    while (pInfo->curr < pInfo->end) {
868                        if (*(pInfo->curr) == '>') break;
869                        pInfo->curr ++;
870                    }
871                    if (*(pInfo->curr) != '>') {
872                        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
873                        return;
874                    }
875                    pInfo->curr ++;
876                }
877            } else {
878                pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch, lineNumber(pInfo));
879                return;
880            }
881        } else if (ch == ']') {
882            pInfo->curr ++;
883            return;
884        } else {
885            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch, lineNumber(pInfo));
886            return;
887        }
888    }
889    if (!pInfo->error) {
890        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing inline DTD"));
891    }
892}
893
894// content ::== (element | CharData | Reference | CDSect | PI | Comment)*
895// In the context of a plist, CharData, Reference and CDSect are not legal (they all resolve to strings).  Skipping whitespace, then, the next character should be '<'.  From there, we figure out which of the three remaining cases we have (element, PI, or Comment).
896static Boolean getContentObject(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFTypeRef *out) {
897    if (isKey) *isKey = false;
898    while (!pInfo->error && pInfo->curr < pInfo->end) {
899        skipWhitespace(pInfo);
900        if (pInfo->curr >= pInfo->end) {
901            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF"));
902            return false;
903        }
904        if (*(pInfo->curr) != '<') {
905            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while looking for open tag"), *(pInfo->curr), lineNumber(pInfo));
906            return false;
907        }
908        pInfo->curr ++;
909        if (pInfo->curr >= pInfo->end) {
910            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF"));
911            return false;
912        }
913        switch (*(pInfo->curr)) {
914            case '?':
915                // Processing instruction
916                skipXMLProcessingInstruction(pInfo);
917                break;
918            case '!':
919                // Could be a comment
920                if (pInfo->curr+2 >= pInfo->end) {
921                    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF"));
922                    return false;
923                }
924                if (*(pInfo->curr+1) == '-' && *(pInfo->curr+2) == '-') {
925                    pInfo->curr += 2;
926                    skipXMLComment(pInfo);
927                } else {
928                    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF"));
929                    return false;
930                }
931                break;
932            case '/':
933                // Whoops!  Looks like we got to the end tag for the element whose content we're parsing
934                pInfo->curr --; // Back off to the '<'
935                return false;
936            default:
937                // Should be an element
938                return parseXMLElement(pInfo, isKey, out);
939        }
940    }
941    // Do not set the error string here; if it wasn't already set by one of the recursive parsing calls, the caller will quickly detect the failure (b/c pInfo->curr >= pInfo->end) and provide a more useful one of the form "end tag for <blah> not found"
942    return false;
943}
944
945static void parseCDSect_pl(_CFXMLPlistParseInfo *pInfo, CFMutableDataRef stringData) {
946    const char *end, *begin;
947    if (pInfo->end - pInfo->curr < CDSECT_TAG_LENGTH) {
948        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF"));
949        return;
950    }
951    if (memcmp(pInfo->curr, CFXMLPlistTags[CDSECT_IX], CDSECT_TAG_LENGTH)) {
952        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered improper CDATA opening at line %d"), lineNumber(pInfo));
953        return;
954    }
955    pInfo->curr += CDSECT_TAG_LENGTH;
956    begin = pInfo->curr; // Marks the first character of the CDATA content
957    end = pInfo->end-2; // So we can safely look 2 characters beyond p
958    while (pInfo->curr < end) {
959        if (*(pInfo->curr) == ']' && *(pInfo->curr+1) == ']' && *(pInfo->curr+2) == '>') {
960            // Found the end!
961            CFDataAppendBytes(stringData, (const UInt8 *)begin, pInfo->curr-begin);
962            pInfo->curr += 3;
963            return;
964        }
965        pInfo->curr ++;
966    }
967    // Never found the end mark
968    pInfo->curr = begin;
969    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Could not find end of CDATA started on line %d"), lineNumber(pInfo));
970}
971
972// Only legal references are {lt, gt, amp, apos, quote, #ddd, #xAAA}
973static void parseEntityReference_pl(_CFXMLPlistParseInfo *pInfo, CFMutableDataRef stringData) {
974    int len;
975    pInfo->curr ++; // move past the '&';
976    len = pInfo->end - pInfo->curr; // how many bytes we can safely scan
977    if (len < 1) {
978        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF"));
979        return;
980    }
981
982    char ch;
983    switch (*(pInfo->curr)) {
984        case 'l':  // "lt"
985            if (len >= 3 && *(pInfo->curr+1) == 't' && *(pInfo->curr+2) == ';') {
986                ch = '<';
987                pInfo->curr += 3;
988                break;
989            }
990            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo));
991            return;
992        case 'g': // "gt"
993            if (len >= 3 && *(pInfo->curr+1) == 't' && *(pInfo->curr+2) == ';') {
994                ch = '>';
995                pInfo->curr += 3;
996                break;
997            }
998            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo));
999            return;
1000        case 'a': // "apos" or "amp"
1001            if (len < 4) {   // Not enough characters for either conversion
1002                pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF"));
1003                return;
1004            }
1005            if (*(pInfo->curr+1) == 'm') {
1006                // "amp"
1007                if (*(pInfo->curr+2) == 'p' && *(pInfo->curr+3) == ';') {
1008                    ch = '&';
1009                    pInfo->curr += 4;
1010                    break;
1011                }
1012            } else if (*(pInfo->curr+1) == 'p') {
1013                // "apos"
1014                if (len > 4 && *(pInfo->curr+2) == 'o' && *(pInfo->curr+3) == 's' && *(pInfo->curr+4) == ';') {
1015                    ch = '\'';
1016                    pInfo->curr += 5;
1017                    break;
1018                }
1019            }
1020            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo));
1021            return;
1022        case 'q':  // "quote"
1023            if (len >= 5 && *(pInfo->curr+1) == 'u' && *(pInfo->curr+2) == 'o' && *(pInfo->curr+3) == 't' && *(pInfo->curr+4) == ';') {
1024                ch = '\"';
1025                pInfo->curr += 5;
1026                break;
1027            }
1028            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo));
1029            return;
1030        case '#':
1031        {
1032            uint16_t num = 0;
1033            Boolean isHex = false;
1034            if ( len < 4) {  // Not enough characters to make it all fit!  Need at least "&#d;"
1035                pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF"));
1036                return;
1037            }
1038            pInfo->curr ++;
1039            if (*(pInfo->curr) == 'x') {
1040                isHex = true;
1041                pInfo->curr ++;
1042            }
1043            while (pInfo->curr < pInfo->end) {
1044                ch = *(pInfo->curr);
1045                pInfo->curr ++;
1046                if (ch == ';') {
1047                    // The value in num always refers to the unicode code point. We'll have to convert since the calling function expects UTF8 data.
1048                    CFStringRef oneChar = CFStringCreateWithBytes(pInfo->allocator, (const uint8_t *)&num, 2, kCFStringEncodingUnicode, NO);
1049                    uint8_t tmpBuf[6]; // max of 6 bytes for UTF8
1050                    CFIndex tmpBufLength = 0;
1051                    CFStringGetBytes(oneChar, CFRangeMake(0, 1), kCFStringEncodingUTF8, 0, NO, tmpBuf, 6, &tmpBufLength);
1052                    CFDataAppendBytes(stringData, tmpBuf, tmpBufLength);
1053                    __CFPListRelease(oneChar, pInfo->allocator);
1054                    return;
1055                }
1056                if (!isHex) num = num*10;
1057                else num = num << 4;
1058                if (ch <= '9' && ch >= '0') {
1059                    num += (ch - '0');
1060                } else if (!isHex) {
1061                    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c at line %d while parsing data"), ch, lineNumber(pInfo));
1062                    return;
1063                } else if (ch >= 'a' && ch <= 'f') {
1064                    num += 10 + (ch - 'a');
1065                } else if (ch >= 'A' && ch <= 'F') {
1066                    num += 10 + (ch - 'A');
1067                } else {
1068                    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c at line %d while parsing data"), ch, lineNumber(pInfo));
1069                    return;
1070                }
1071            }
1072            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF"));
1073            return;
1074        }
1075        default:
1076            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo));
1077            return;
1078    }
1079    CFDataAppendBytes(stringData, (const UInt8 *)&ch, 1);
1080}
1081
1082static void _createStringMap(_CFXMLPlistParseInfo *pInfo) {
1083    pInfo->stringTrie = CFBurstTrieCreate();
1084    pInfo->stringCache = CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks);
1085}
1086
1087static void _cleanupStringMap(_CFXMLPlistParseInfo *pInfo) {
1088    CFBurstTrieRelease(pInfo->stringTrie);
1089    CFRelease(pInfo->stringCache);
1090    pInfo->stringTrie = NULL;
1091    pInfo->stringCache = NULL;
1092}
1093
1094static CFStringRef _createUniqueStringWithUTF8Bytes(_CFXMLPlistParseInfo *pInfo, const char *base, CFIndex length) {
1095    if (length == 0) return !(0) ? (CFStringRef)CFRetain(CFSTR("")) : CFSTR("");
1096
1097    CFStringRef result = NULL;
1098    uint32_t payload = 0;
1099    Boolean uniqued = CFBurstTrieContainsUTF8String(pInfo->stringTrie, (UInt8 *)base, length, &payload);
1100    if (uniqued && payload > 0) {
1101        // The index is at payload - 1 (see below).
1102        result = (CFStringRef)CFArrayGetValueAtIndex(pInfo->stringCache, (CFIndex)payload - 1);
1103        CFRetain(result);
1104    } else {
1105        result = CFStringCreateWithBytes(pInfo->allocator, (const UInt8 *)base, length, kCFStringEncodingUTF8, NO);
1106        if (!result) return NULL;
1107        // Payload must be >0, so the actual index of the value is at payload - 1
1108        // We also get add to the array after we make sure that CFBurstTrieAddUTF8String succeeds (it can fail, if the string is too large, for example)
1109        payload = CFArrayGetCount(pInfo->stringCache) + 1;
1110        Boolean didAddToBurstTrie = CFBurstTrieAddUTF8String(pInfo->stringTrie, (UInt8 *)base, length, payload);
1111        if (didAddToBurstTrie) {
1112            CFArrayAppendValue(pInfo->stringCache, result);
1113        }
1114    }
1115    return result;
1116}
1117
1118// String could be comprised of characters, CDSects, or references to one of the "well-known" entities ('<', '>', '&', ''', '"')
1119static Boolean parseStringTag(_CFXMLPlistParseInfo *pInfo, CFStringRef *out) {
1120    const char *mark = pInfo->curr;
1121    CFMutableDataRef stringData = NULL;
1122    while (!pInfo->error && pInfo->curr < pInfo->end) {
1123        char ch = *(pInfo->curr);
1124        if (ch == '<') {
1125	    if (pInfo->curr + 1 >= pInfo->end) break;
1126            // Could be a CDSect; could be the end of the string
1127            if (*(pInfo->curr+1) != '!') break; // End of the string
1128            if (!stringData) stringData = CFDataCreateMutable(pInfo->allocator, 0);
1129            CFDataAppendBytes(stringData, (const UInt8 *)mark, pInfo->curr - mark);
1130            parseCDSect_pl(pInfo, stringData); // TODO: move to return boolean
1131            mark = pInfo->curr;
1132        } else if (ch == '&') {
1133            if (!stringData) stringData = CFDataCreateMutable(pInfo->allocator, 0);
1134            CFDataAppendBytes(stringData, (const UInt8 *)mark, pInfo->curr - mark);
1135            parseEntityReference_pl(pInfo, stringData); // TODO: move to return boolean
1136            mark = pInfo->curr;
1137        } else {
1138            pInfo->curr ++;
1139        }
1140    }
1141
1142    if (pInfo->error) {
1143        __CFPListRelease(stringData, pInfo->allocator);
1144        return false;
1145    }
1146
1147    if (!stringData) {
1148        if (pInfo->skip) {
1149            *out = NULL;
1150        } else {
1151            if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) {
1152                CFStringRef s = _createUniqueStringWithUTF8Bytes(pInfo, mark, pInfo->curr - mark);
1153                if (!s) {
1154                    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unable to convert string to correct encoding"));
1155                    return false;
1156                }
1157                *out = s;
1158            } else {
1159                CFStringRef s = CFStringCreateWithBytes(pInfo->allocator, (const UInt8 *)mark, pInfo->curr - mark, kCFStringEncodingUTF8, NO);
1160                if (!s) {
1161                    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unable to convert string to correct encoding"));
1162                    return false;
1163                }
1164                *out = CFStringCreateMutableCopy(pInfo->allocator, 0, s);
1165                __CFPListRelease(s, pInfo->allocator);
1166            }
1167        }
1168        return true;
1169    } else {
1170        if (pInfo->skip) {
1171            *out = NULL;
1172        } else {
1173            CFDataAppendBytes(stringData, (const UInt8 *)mark, pInfo->curr - mark);
1174            if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) {
1175                CFStringRef s = _createUniqueStringWithUTF8Bytes(pInfo, (const char *)CFDataGetBytePtr(stringData), CFDataGetLength(stringData));
1176                if (!s) {
1177                    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unable to convert string to correct encoding"));
1178                    return false;
1179                }
1180                *out = s;
1181            } else {
1182                CFStringRef s = CFStringCreateWithBytes(pInfo->allocator, (const UInt8 *)CFDataGetBytePtr(stringData), CFDataGetLength(stringData), kCFStringEncodingUTF8, NO);
1183                if (!s) {
1184                    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unable to convert string to correct encoding"));
1185                    return false;
1186                }
1187                *out = CFStringCreateMutableCopy(pInfo->allocator, 0, s);
1188                __CFPListRelease(s, pInfo->allocator);
1189            }
1190        }
1191        __CFPListRelease(stringData, pInfo->allocator);
1192        return true;
1193    }
1194}
1195
1196static Boolean checkForCloseTag(_CFXMLPlistParseInfo *pInfo, const char *tag, CFIndex tagLen) {
1197    if (pInfo->end - pInfo->curr < tagLen + 3) {
1198        if (!pInfo->error) {
1199            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF"));
1200        }
1201        return false;
1202    }
1203    if (*(pInfo->curr) != '<' || *(++pInfo->curr) != '/') {
1204        if (!pInfo->error) {
1205            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while looking for close tag"), *(pInfo->curr), lineNumber(pInfo));
1206        }
1207        return false;
1208    }
1209    pInfo->curr ++;
1210    if (memcmp(pInfo->curr, tag, tagLen)) {
1211        CFStringRef str = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const UInt8 *)tag, tagLen, kCFStringEncodingUTF8, NO);
1212        if (!pInfo->error) {
1213            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Close tag on line %d does not match open tag %@"), lineNumber(pInfo), str);
1214        }
1215        CFRelease(str);
1216        return false;
1217    }
1218    pInfo->curr += tagLen;
1219    skipWhitespace(pInfo);
1220    if (pInfo->curr == pInfo->end) {
1221        if (!pInfo->error) {
1222            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF"));
1223        }
1224        return false;
1225    }
1226    if (*(pInfo->curr) != '>') {
1227        if (!pInfo->error) {
1228            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while looking for close tag"), *(pInfo->curr), lineNumber(pInfo));
1229        }
1230        return false;
1231    }
1232    pInfo->curr ++;
1233    return true;
1234}
1235
1236// pInfo should be set to the first content character of the <plist>
1237static Boolean parsePListTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) {
1238    CFTypeRef result = NULL;
1239    if (!getContentObject(pInfo, NULL, &result)) {
1240        if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty plist tag"));
1241        return false;
1242    }
1243    const char *save = pInfo->curr; // Save this in case the next step fails
1244    CFTypeRef tmp = NULL;
1245    if (getContentObject(pInfo, NULL, &tmp)) {
1246        // Got an extra object
1247        __CFPListRelease(tmp, pInfo->allocator);
1248        __CFPListRelease(result, pInfo->allocator);
1249        pInfo->curr = save;
1250        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected element at line %d (plist can only include one object)"), lineNumber(pInfo));
1251        return false;
1252    }
1253    if (pInfo->error) {
1254        // Parse failed catastrophically
1255        __CFPListRelease(result, pInfo->allocator);
1256        return false;
1257    }
1258    if (!checkForCloseTag(pInfo, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH)) {
1259        __CFPListRelease(result, pInfo->allocator);
1260        return false;
1261    }
1262    *out = result;
1263    return true;
1264}
1265
1266static int allowImmutableCollections = -1;
1267
1268static void checkImmutableCollections(void) {
1269    allowImmutableCollections = (NULL == __CFgetenv("CFPropertyListAllowImmutableCollections")) ? 0 : 1;
1270}
1271
1272// This converts the input value, a set of strings, into the form that's efficient for using during recursive decent parsing, a set of arrays
1273static CFSetRef createTopLevelKeypaths(CFAllocatorRef allocator, CFSetRef keyPaths) {
1274    if (!keyPaths) return NULL;
1275
1276    CFIndex count = CFSetGetCount(keyPaths);
1277    new_cftype_array(keyPathValues, count);
1278    CFSetGetValues(keyPaths, keyPathValues);
1279    CFMutableSetRef splitKeyPathSet = CFSetCreateMutable(allocator, count, &kCFTypeSetCallBacks);
1280    for (CFIndex i = 0; i < count; i++) {
1281        // Split each key path and add it to the split path set, which we will reference throughout parsing
1282        CFArrayRef split = CFStringCreateArrayBySeparatingStrings(allocator, (CFStringRef)(keyPathValues[i]), CFSTR(":"));
1283        CFSetAddValue(splitKeyPathSet, split);
1284        __CFPListRelease(split, allocator);
1285    }
1286    free_cftype_array(keyPathValues);
1287    return splitKeyPathSet;
1288}
1289
1290// This splits up the keypaths into the ones relevant for this level (of array or dictionary), and the ones for the next level (of array or dictionary)
1291CF_PRIVATE void __CFPropertyListCreateSplitKeypaths(CFAllocatorRef allocator, CFSetRef currentKeys, CFSetRef *theseKeys, CFSetRef *nextKeys) {
1292    if (!currentKeys) { *theseKeys = NULL; *nextKeys = NULL; return; }
1293
1294    CFIndex count = CFSetGetCount(currentKeys);
1295
1296    // For each array in the current key path set, grab the item at the start of the list and put it into theseKeys. The rest of the array goes into nextKeys.
1297    CFMutableSetRef outTheseKeys = NULL;
1298    CFMutableSetRef outNextKeys = NULL;
1299
1300    new_cftype_array(currentKeyPaths, count);
1301    CFSetGetValues(currentKeys, currentKeyPaths);
1302    for (CFIndex i = 0; i < count; i++) {
1303        CFArrayRef oneKeyPath = (CFArrayRef)currentKeyPaths[i];
1304        CFIndex keyPathCount = CFArrayGetCount(oneKeyPath);
1305
1306        if (keyPathCount > 0) {
1307            if (!outTheseKeys) outTheseKeys = CFSetCreateMutable(allocator, 0, &kCFTypeSetCallBacks);
1308
1309            CFSetAddValue(outTheseKeys, CFArrayGetValueAtIndex(oneKeyPath, 0));
1310        }
1311
1312        if (keyPathCount > 1) {
1313            if (!outNextKeys) outNextKeys = CFSetCreateMutable(allocator, 0, &kCFTypeSetCallBacks);
1314
1315            // Create an array with values from 1 - end of list
1316            new_cftype_array(restOfKeys, keyPathCount - 1);
1317            CFArrayGetValues(oneKeyPath, CFRangeMake(1, CFArrayGetCount(oneKeyPath) - 1), restOfKeys);
1318            CFArrayRef newNextKeys = CFArrayCreate(allocator, restOfKeys, CFArrayGetCount(oneKeyPath) - 1, &kCFTypeArrayCallBacks);
1319            CFSetAddValue(outNextKeys, newNextKeys);
1320            __CFPListRelease(newNextKeys, allocator);
1321            free_cftype_array(restOfKeys);
1322
1323        }
1324    }
1325
1326    *theseKeys = outTheseKeys;
1327    *nextKeys = outNextKeys;
1328}
1329
1330static Boolean parseArrayTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) {
1331    CFTypeRef tmp = NULL;
1332
1333    if (pInfo->skip) {
1334        Boolean result = getContentObject(pInfo, NULL, &tmp);
1335        while (result) {
1336            if (tmp) {
1337                // Shouldn't happen (if skipping, all content values should be null), but just in case
1338                __CFPListRelease(tmp, pInfo->allocator);
1339            }
1340            result = getContentObject(pInfo, NULL, &tmp);
1341        }
1342
1343        if (pInfo->error) {
1344            // getContentObject encountered a parse error
1345            return false;
1346        }
1347        if (!checkForCloseTag(pInfo, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH)) {
1348            return false;
1349        } else {
1350            *out = NULL;
1351            return true;
1352        }
1353    }
1354
1355    CFMutableArrayRef array = CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks);
1356    Boolean result;
1357
1358    CFIndex count = 0;
1359    CFSetRef oldKeyPaths = pInfo->keyPaths;
1360    CFSetRef newKeyPaths, keys;
1361    __CFPropertyListCreateSplitKeypaths(pInfo->allocator, pInfo->keyPaths, &keys, &newKeyPaths);
1362
1363    if (keys) {
1364        CFStringRef countString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("%ld"), count);
1365        if (!CFSetContainsValue(keys, countString)) pInfo->skip = true;
1366        __CFPListRelease(countString, pInfo->allocator);
1367        count++;
1368        pInfo->keyPaths = newKeyPaths;
1369    }
1370    result = getContentObject(pInfo, NULL, &tmp);
1371    if (keys) {
1372        pInfo->keyPaths = oldKeyPaths;
1373        pInfo->skip = false;
1374    }
1375
1376    while (result) {
1377        if (tmp) {
1378            CFArrayAppendValue(array, tmp);
1379            __CFPListRelease(tmp, pInfo->allocator);
1380        }
1381
1382        if (keys) {
1383            // prep for getting next object
1384            CFStringRef countString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("%ld"), count);
1385            if (!CFSetContainsValue(keys, countString)) pInfo->skip = true;
1386            __CFPListRelease(countString, pInfo->allocator);
1387            count++;
1388            pInfo->keyPaths = newKeyPaths;
1389        }
1390        result = getContentObject(pInfo, NULL, &tmp);
1391        if (keys) {
1392            // reset after getting object
1393            pInfo->keyPaths = oldKeyPaths;
1394            pInfo->skip = false;
1395        }
1396
1397    }
1398
1399    __CFPListRelease(newKeyPaths, pInfo->allocator);
1400    __CFPListRelease(keys, pInfo->allocator);
1401
1402    if (pInfo->error) { // getContentObject encountered a parse error
1403        __CFPListRelease(array, pInfo->allocator);
1404        return false;
1405    }
1406    if (!checkForCloseTag(pInfo, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH)) {
1407        __CFPListRelease(array, pInfo->allocator);
1408        return false;
1409    }
1410    if (-1 == allowImmutableCollections) {
1411        checkImmutableCollections();
1412    }
1413    if (1 == allowImmutableCollections) {
1414        if (pInfo->mutabilityOption == kCFPropertyListImmutable) {
1415            CFArrayRef newArray = CFArrayCreateCopy(pInfo->allocator, array);
1416            __CFPListRelease(array, pInfo->allocator);
1417            array = (CFMutableArrayRef)newArray;
1418        }
1419    }
1420    *out = array;
1421    return true;
1422}
1423
1424static Boolean parseDictTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) {
1425    Boolean gotKey;
1426    Boolean result;
1427    CFTypeRef key = NULL, value = NULL;
1428
1429    if (pInfo->skip) {
1430        result = getContentObject(pInfo, &gotKey, &key);
1431        while (result) {
1432            if (!gotKey) {
1433                if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo));
1434                return false;
1435            }
1436            result = getContentObject(pInfo, NULL, &value);
1437            if (!result) {
1438                if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo));
1439                return false;
1440            }
1441            // key and value should be null, but we'll release just in case here
1442            __CFPListRelease(key, pInfo->allocator);
1443            key = NULL;
1444            __CFPListRelease(value, pInfo->allocator);
1445            value = NULL;
1446            result = getContentObject(pInfo, &gotKey, &key);
1447        }
1448        if (checkForCloseTag(pInfo, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH)) {
1449            *out = NULL;
1450            return true;
1451        } else {
1452            return false;
1453        }
1454    }
1455
1456    CFSetRef oldKeyPaths = pInfo->keyPaths;
1457    CFSetRef nextKeyPaths, theseKeyPaths;
1458    __CFPropertyListCreateSplitKeypaths(pInfo->allocator, pInfo->keyPaths, &theseKeyPaths, &nextKeyPaths);
1459
1460    CFMutableDictionaryRef dict = NULL;
1461
1462    result = getContentObject(pInfo, &gotKey, &key);
1463    while (result && key) {
1464        if (!gotKey) {
1465            if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo));
1466            __CFPListRelease(key, pInfo->allocator);
1467            __CFPListRelease(nextKeyPaths, pInfo->allocator);
1468            __CFPListRelease(theseKeyPaths, pInfo->allocator);
1469            __CFPListRelease(dict, pInfo->allocator);
1470            return false;
1471        }
1472
1473        if (theseKeyPaths) {
1474            if (!CFSetContainsValue(theseKeyPaths, key)) pInfo->skip = true;
1475            pInfo->keyPaths = nextKeyPaths;
1476        }
1477        result = getContentObject(pInfo, NULL, &value);
1478        if (theseKeyPaths) {
1479            pInfo->keyPaths = oldKeyPaths;
1480            pInfo->skip = false;
1481        }
1482
1483        if (!result) {
1484            if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo));
1485            __CFPListRelease(key, pInfo->allocator);
1486            __CFPListRelease(nextKeyPaths, pInfo->allocator);
1487            __CFPListRelease(theseKeyPaths, pInfo->allocator);
1488            __CFPListRelease(dict, pInfo->allocator);
1489            return false;
1490        }
1491
1492        if (key && value) {
1493            if (NULL == dict) {
1494                dict = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1495                _CFDictionarySetCapacity(dict, 10);
1496            }
1497            CFDictionarySetValue(dict, key, value);
1498        }
1499
1500        __CFPListRelease(key, pInfo->allocator);
1501        key = NULL;
1502        __CFPListRelease(value, pInfo->allocator);
1503        value = NULL;
1504
1505        result = getContentObject(pInfo, &gotKey, &key);
1506    }
1507
1508    __CFPListRelease(nextKeyPaths, pInfo->allocator);
1509    __CFPListRelease(theseKeyPaths, pInfo->allocator);
1510
1511    if (checkForCloseTag(pInfo, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH)) {
1512	if (NULL == dict) {
1513	    if (pInfo->mutabilityOption == kCFPropertyListImmutable) {
1514		dict = (CFMutableDictionaryRef)CFDictionaryCreate(pInfo->allocator, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1515	    } else {
1516		dict = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1517	    }
1518	} else {
1519	    CFIndex cnt = CFDictionaryGetCount(dict);
1520	    if (1 == cnt) {
1521		CFTypeRef val = CFDictionaryGetValue(dict, CFSTR("CF$UID"));
1522		if (val && CFGetTypeID(val) == numbertype) {
1523		    CFTypeRef uid;
1524		    uint32_t v;
1525		    CFNumberGetValue((CFNumberRef)val, kCFNumberSInt32Type, &v);
1526		    uid = (CFTypeRef)_CFKeyedArchiverUIDCreate(pInfo->allocator, v);
1527		    __CFPListRelease(dict, pInfo->allocator);
1528                    *out = uid;
1529                    return true;
1530		}
1531	    }
1532	    if (-1 == allowImmutableCollections) checkImmutableCollections();
1533	    if (1 == allowImmutableCollections) {
1534		if (pInfo->mutabilityOption == kCFPropertyListImmutable) {
1535		    CFDictionaryRef newDict = CFDictionaryCreateCopy(pInfo->allocator, dict);
1536		    __CFPListRelease(dict, pInfo->allocator);
1537		    dict = (CFMutableDictionaryRef)newDict;
1538		}
1539	    }
1540	}
1541        *out = dict;
1542        return true;
1543    }
1544
1545    if (dict) __CFPListRelease(dict, pInfo->allocator);
1546    return false;
1547}
1548
1549static Boolean parseDataTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) {
1550    const char *base = pInfo->curr;
1551    static const signed char dataDecodeTable[128] = {
1552        /* 000 */ -1, -1, -1, -1, -1, -1, -1, -1,
1553        /* 010 */ -1, -1, -1, -1, -1, -1, -1, -1,
1554        /* 020 */ -1, -1, -1, -1, -1, -1, -1, -1,
1555        /* 030 */ -1, -1, -1, -1, -1, -1, -1, -1,
1556        /* ' ' */ -1, -1, -1, -1, -1, -1, -1, -1,
1557        /* '(' */ -1, -1, -1, 62, -1, -1, -1, 63,
1558        /* '0' */ 52, 53, 54, 55, 56, 57, 58, 59,
1559        /* '8' */ 60, 61, -1, -1, -1,  0, -1, -1,
1560        /* '@' */ -1,  0,  1,  2,  3,  4,  5,  6,
1561        /* 'H' */  7,  8,  9, 10, 11, 12, 13, 14,
1562        /* 'P' */ 15, 16, 17, 18, 19, 20, 21, 22,
1563        /* 'X' */ 23, 24, 25, -1, -1, -1, -1, -1,
1564        /* '`' */ -1, 26, 27, 28, 29, 30, 31, 32,
1565        /* 'h' */ 33, 34, 35, 36, 37, 38, 39, 40,
1566        /* 'p' */ 41, 42, 43, 44, 45, 46, 47, 48,
1567        /* 'x' */ 49, 50, 51, -1, -1, -1, -1, -1
1568    };
1569
1570    int tmpbufpos = 0;
1571    int tmpbuflen = 256;
1572    uint8_t *tmpbuf = pInfo->skip ? NULL : (uint8_t *)CFAllocatorAllocate(pInfo->allocator, tmpbuflen, 0);
1573    int numeq = 0;
1574    int acc = 0;
1575    int cntr = 0;
1576
1577    for (; pInfo->curr < pInfo->end; pInfo->curr++) {
1578        signed char c = *(pInfo->curr);
1579        if (c == '<') {
1580            break;
1581        }
1582        if ('=' == c) {
1583            numeq++;
1584        } else if (!isspace(c)) {
1585            numeq = 0;
1586        }
1587        if (dataDecodeTable[c] < 0)
1588            continue;
1589        cntr++;
1590        acc <<= 6;
1591        acc += dataDecodeTable[c];
1592        if (!pInfo->skip && 0 == (cntr & 0x3)) {
1593            if (tmpbuflen <= tmpbufpos + 2) {
1594                if (tmpbuflen < 256 * 1024) {
1595                    tmpbuflen *= 4;
1596                } else if (tmpbuflen < 16 * 1024 * 1024) {
1597                    tmpbuflen *= 2;
1598                } else {
1599                    // once in this stage, this will be really slow
1600                    // and really potentially fragment memory
1601                    tmpbuflen += 256 * 1024;
1602                }
1603                tmpbuf = (uint8_t *)CFAllocatorReallocate(pInfo->allocator, tmpbuf, tmpbuflen, 0);
1604                if (!tmpbuf) HALT; // out of memory
1605            }
1606            tmpbuf[tmpbufpos++] = (acc >> 16) & 0xff;
1607            if (numeq < 2) tmpbuf[tmpbufpos++] = (acc >> 8) & 0xff;
1608            if (numeq < 1) tmpbuf[tmpbufpos++] = acc & 0xff;
1609        }
1610    }
1611
1612    CFDataRef result = NULL;
1613    if (!pInfo->skip) {
1614        if (pInfo->mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
1615            result = (CFDataRef)CFDataCreateMutable(pInfo->allocator, 0);
1616            CFDataAppendBytes((CFMutableDataRef)result, tmpbuf, tmpbufpos);
1617            CFAllocatorDeallocate(pInfo->allocator, tmpbuf);
1618        } else {
1619            result = CFDataCreateWithBytesNoCopy(pInfo->allocator, tmpbuf, tmpbufpos, pInfo->allocator);
1620        }
1621        if (!result) {
1622            pInfo->curr = base;
1623            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Could not interpret <data> at line %d (should be base64-encoded)"), lineNumber(pInfo));
1624            return false;
1625        }
1626    }
1627
1628    if (checkForCloseTag(pInfo, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH)) {
1629        *out = result;
1630        return true;
1631    } else {
1632        __CFPListRelease(result, pInfo->allocator);
1633        return false;
1634    }
1635}
1636
1637CF_INLINE Boolean read2DigitNumber(_CFXMLPlistParseInfo *pInfo, int32_t *result) {
1638    char ch1, ch2;
1639    if (pInfo->curr + 2 >= pInfo->end) {
1640        return false;
1641    }
1642    ch1 = *pInfo->curr;
1643    ch2 = *(pInfo->curr + 1);
1644    pInfo->curr += 2;
1645    if (!isdigit(ch1) || !isdigit(ch2)) {
1646        return false;
1647    }
1648    *result = (ch1 - '0')*10 + (ch2 - '0');
1649    return true;
1650}
1651
1652// YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
1653static Boolean parseDateTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) {
1654    int32_t year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
1655    int32_t num = 0;
1656    Boolean badForm = false;
1657    Boolean yearIsNegative = false;
1658
1659    if (pInfo->curr < pInfo->end && *pInfo->curr == '-') {
1660        yearIsNegative = true;
1661        pInfo->curr++;
1662    }
1663
1664    while (pInfo->curr < pInfo->end && isdigit(*pInfo->curr)) {
1665        year = 10*year + (*pInfo->curr) - '0';
1666        pInfo->curr ++;
1667    }
1668    if (pInfo->curr >= pInfo->end || *pInfo->curr != '-') {
1669        badForm = true;
1670    } else {
1671        pInfo->curr ++;
1672    }
1673
1674    if (!badForm && read2DigitNumber(pInfo, &month) && pInfo->curr < pInfo->end && *pInfo->curr == '-') {
1675        pInfo->curr ++;
1676    } else {
1677        badForm = true;
1678    }
1679
1680    if (!badForm && read2DigitNumber(pInfo, &day) && pInfo->curr < pInfo->end && *pInfo->curr == 'T') {
1681        pInfo->curr ++;
1682    } else {
1683        badForm = true;
1684    }
1685
1686    if (!badForm && read2DigitNumber(pInfo, &hour) && pInfo->curr < pInfo->end && *pInfo->curr == ':') {
1687        pInfo->curr ++;
1688    } else {
1689        badForm = true;
1690    }
1691
1692    if (!badForm && read2DigitNumber(pInfo, &minute) && pInfo->curr < pInfo->end && *pInfo->curr == ':') {
1693        pInfo->curr ++;
1694    } else {
1695        badForm = true;
1696    }
1697
1698    if (!badForm && read2DigitNumber(pInfo, &num) && pInfo->curr < pInfo->end && *pInfo->curr == 'Z') {
1699        second = num;
1700        pInfo->curr ++;
1701    } else {
1702        badForm = true;
1703    }
1704
1705    if (badForm || !checkForCloseTag(pInfo, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH)) {
1706        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Could not interpret <date> at line %d"), lineNumber(pInfo));
1707        return false;
1708    }
1709
1710    CFAbsoluteTime at = 0.0;
1711#if 1
1712    CFGregorianDate date = {yearIsNegative ? -year : year, month, day, hour, minute, second};
1713    at = CFGregorianDateGetAbsoluteTime(date, NULL);
1714#else
1715    // this doesn't work
1716    CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, kCFCalendarIdentifierGregorian);
1717    CFTimeZoneRef tz = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, CFSTR("GMT"), true);
1718    CFCalendarSetTimeZone(calendar, tz);
1719    CFCalendarComposeAbsoluteTime(calendar, &at, (const uint8_t *)"yMdHms", year, month, day, hour, minute, second);
1720    CFRelease(calendar);
1721    CFRelease(tz);
1722#endif
1723    if (pInfo->skip) {
1724        *out = NULL;
1725    } else {
1726        *out = CFDateCreate(pInfo->allocator, at);
1727    }
1728    return true;
1729}
1730
1731static Boolean parseRealTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) {
1732    CFStringRef str = NULL;
1733    if (!parseStringTag(pInfo, &str)) {
1734        if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo));
1735        return false;
1736    }
1737
1738    CFNumberRef result = NULL;
1739
1740    if (!pInfo->skip) {
1741        if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("nan"), kCFCompareCaseInsensitive)) result = kCFNumberNaN;
1742        else if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("+infinity"), kCFCompareCaseInsensitive)) result = kCFNumberPositiveInfinity;
1743        else if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("-infinity"), kCFCompareCaseInsensitive)) result = kCFNumberNegativeInfinity;
1744        else if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("infinity"), kCFCompareCaseInsensitive)) result = kCFNumberPositiveInfinity;
1745        else if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("-inf"), kCFCompareCaseInsensitive)) result = kCFNumberNegativeInfinity;
1746        else if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("inf"), kCFCompareCaseInsensitive)) result = kCFNumberPositiveInfinity;
1747        else if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("+inf"), kCFCompareCaseInsensitive)) result = kCFNumberPositiveInfinity;
1748
1749        if (result) {
1750            CFRetain(result);
1751        } else {
1752            CFIndex len = CFStringGetLength(str);
1753            CFStringInlineBuffer buf;
1754            CFStringInitInlineBuffer(str, &buf, CFRangeMake(0, len));
1755            SInt32 idx = 0;
1756            double val;
1757            if (!__CFStringScanDouble(&buf, NULL, &idx, &val) || idx != len) {
1758                __CFPListRelease(str, pInfo->allocator);
1759                pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered misformatted real on line %d"), lineNumber(pInfo));
1760                return false;
1761            }
1762            result = CFNumberCreate(pInfo->allocator, kCFNumberDoubleType, &val);
1763        }
1764    }
1765
1766    __CFPListRelease(str, pInfo->allocator);
1767    if (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) {
1768        *out = result;
1769        return true;
1770    } else {
1771        __CFPListRelease(result, pInfo->allocator);
1772        return false;
1773    }
1774}
1775
1776#define GET_CH	if (pInfo->curr == pInfo->end) {	\
1777			pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Premature end of file after <integer> on line %d"), lineNumber(pInfo)); \
1778			return false;			\
1779		}					\
1780		ch = *(pInfo->curr)
1781
1782typedef struct {
1783    int64_t high;
1784    uint64_t low;
1785} CFSInt128Struct;
1786
1787enum {
1788    kCFNumberSInt128Type = 17
1789};
1790
1791CF_INLINE bool isWhitespace(const char *utf8bytes, const char *end) {
1792    // Converted UTF-16 isWhitespace from CFString to UTF8 bytes to get full list of UTF8 whitespace
1793    /*
1794     0020 -> <20>
1795     0009 -> <09>
1796     00a0 -> <c2a0>
1797     1680 -> <e19a80>
1798     2000 -> <e28080>
1799     2001 -> <e28081>
1800     2002 -> <e28082>
1801     2003 -> <e28083>
1802     2004 -> <e28084>
1803     2005 -> <e28085>
1804     2006 -> <e28086>
1805     2007 -> <e28087>
1806     2008 -> <e28088>
1807     2009 -> <e28089>
1808     200a -> <e2808a>
1809     200b -> <e2808b>
1810     202f -> <e280af>
1811     205f -> <e2819f>
1812     3000 -> <e38080>
1813     */
1814    // Except we consider some additional values from 0x0 to 0x21 and 0x7E to 0xA1 as whitespace, for compatability
1815    unsigned char byte1 = *utf8bytes;
1816    if (byte1 < 0x21 || (byte1 > 0x7E && byte1 < 0xA1)) return true;
1817    if ((byte1 == 0xe2 || byte1 == 0xe3) && (end - utf8bytes >= 3)) {
1818        // Check other possibilities in the 3-bytes range
1819        unsigned char byte2 = *(utf8bytes + 1);
1820        unsigned char byte3 = *(utf8bytes + 2);
1821        if (byte1 == 0xe2 && byte2 == 0x80) {
1822            return ((byte3 >= 80 && byte3 <= 0x8b) || byte3 == 0xaf);
1823        } else if (byte1 == 0xe2 && byte2 == 0x81) {
1824            return byte3 == 0x9f;
1825        } else if (byte1 == 0xe3 && byte2 == 0x80 && byte3 == 0x80) {
1826            return true;
1827        }
1828    }
1829    return false;
1830}
1831
1832static Boolean parseIntegerTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) {
1833    bool isHex = false, isNeg = false, hadLeadingZero = false;
1834    char ch = 0;
1835
1836    // decimal_constant         S*(-|+)?S*[0-9]+		(S == space)
1837    // hex_constant		S*(-|+)?S*0[xX][0-9a-fA-F]+	(S == space)
1838
1839    while (pInfo->curr < pInfo->end && isWhitespace(pInfo->curr, pInfo->end)) pInfo->curr++;
1840    GET_CH;
1841    if ('<' == ch) {
1842        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo));
1843        return false;
1844    }
1845    if ('-' == ch || '+' == ch) {
1846	isNeg = ('-' == ch);
1847	pInfo->curr++;
1848	while (pInfo->curr < pInfo->end && isWhitespace(pInfo->curr, pInfo->end)) pInfo->curr++;
1849    }
1850    GET_CH;
1851    if ('0' == ch) {
1852	if (pInfo->curr + 1 < pInfo->end && ('x' == *(pInfo->curr + 1) || 'X' == *(pInfo->curr + 1))) {
1853	    pInfo->curr++;
1854	    isHex = true;
1855	} else {
1856	    hadLeadingZero = true;
1857	}
1858	pInfo->curr++;
1859    }
1860    GET_CH;
1861    while ('0' == ch) {
1862	hadLeadingZero = true;
1863	pInfo->curr++;
1864	GET_CH;
1865    }
1866    if ('<' == ch && hadLeadingZero) {	// nothing but zeros
1867	int32_t val = 0;
1868        if (!checkForCloseTag(pInfo, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH)) {
1869	    // checkForCloseTag() sets error string
1870	    return false;
1871        }
1872        if (pInfo->skip) {
1873            *out = NULL;
1874        } else {
1875            *out = CFNumberCreate(pInfo->allocator, kCFNumberSInt32Type, &val);
1876        }
1877        return true;
1878    }
1879    if ('<' == ch) {
1880        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Incomplete <integer> on line %d"), lineNumber(pInfo));
1881	return false;
1882    }
1883    uint64_t value = 0;
1884    uint32_t multiplier = (isHex ? 16 : 10);
1885    while ('<' != ch) {
1886	uint32_t new_digit = 0;
1887	switch (ch) {
1888            case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
1889                new_digit = (ch - '0');
1890                break;
1891            case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1892                new_digit = (ch - 'a' + 10);
1893                break;
1894            case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1895                new_digit = (ch - 'A' + 10);
1896                break;
1897            default:	// other character
1898                pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unknown character '%c' (0x%x) in <integer> on line %d"), ch, ch, lineNumber(pInfo));
1899                return false;
1900	}
1901	if (!isHex && new_digit > 9) {
1902            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Hex digit in non-hex <integer> on line %d"), lineNumber(pInfo));
1903	    return false;
1904	}
1905	if (UINT64_MAX / multiplier < value) {
1906            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo));
1907	    return false;
1908	}
1909	value = multiplier * value;
1910	if (UINT64_MAX - new_digit < value) {
1911            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo));
1912	    return false;
1913	}
1914	value = value + new_digit;
1915	if (isNeg && (uint64_t)INT64_MAX + 1 < value) {
1916            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Integer underflow in <integer> on line %d"), lineNumber(pInfo));
1917	    return false;
1918	}
1919	pInfo->curr++;
1920	GET_CH;
1921    }
1922    if (!checkForCloseTag(pInfo, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH)) {
1923	// checkForCloseTag() sets error string
1924	return false;
1925    }
1926
1927    if (pInfo->skip) {
1928        *out = NULL;
1929    } else {
1930        if (isNeg || value <= INT64_MAX) {
1931            int64_t v = value;
1932            if (isNeg) v = -v;	// no-op if INT64_MIN
1933            *out = CFNumberCreate(pInfo->allocator, kCFNumberSInt64Type, &v);
1934        } else {
1935            CFSInt128Struct val;
1936            val.high = 0;
1937            val.low = value;
1938            *out = CFNumberCreate(pInfo->allocator, kCFNumberSInt128Type, &val);
1939        }
1940    }
1941    return true;
1942}
1943
1944#undef GET_CH
1945
1946// Returned object is retained; caller must free.  pInfo->curr expected to point to the first character after the '<'
1947static Boolean parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFTypeRef *out) {
1948    const char *marker = pInfo->curr;
1949    int markerLength = -1;
1950    Boolean isEmpty;
1951    int markerIx = -1;
1952
1953    if (isKey) *isKey = false;
1954    while (pInfo->curr < pInfo->end) {
1955        char ch = *(pInfo->curr);
1956        if (ch == ' ' || ch ==  '\t' || ch == '\n' || ch =='\r') {
1957            if (markerLength == -1) markerLength = pInfo->curr - marker;
1958        } else if (ch == '>') {
1959            break;
1960        }
1961        pInfo->curr ++;
1962    }
1963    if (pInfo->curr >= pInfo->end) {
1964        return false;
1965    }
1966    isEmpty = (*(pInfo->curr-1) == '/');
1967    if (markerLength == -1)
1968        markerLength = pInfo->curr - (isEmpty ? 1 : 0) - marker;
1969    pInfo->curr ++; // Advance past '>'
1970    if (markerLength == 0) {
1971        // Back up to the beginning of the marker
1972        pInfo->curr = marker;
1973        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Malformed tag on line %d"), lineNumber(pInfo));
1974        return false;
1975    }
1976    switch (*marker) {
1977        case 'a':   // Array
1978            if (markerLength == ARRAY_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH))
1979                markerIx = ARRAY_IX;
1980            break;
1981        case 'd': // Dictionary, data, or date; Fortunately, they all have the same marker length....
1982            if (markerLength != DICT_TAG_LENGTH)
1983                break;
1984            if (!memcmp(marker, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH))
1985                markerIx = DICT_IX;
1986            else if (!memcmp(marker, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH))
1987                markerIx = DATA_IX;
1988            else if (!memcmp(marker, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH))
1989                markerIx = DATE_IX;
1990            break;
1991        case 'f': // false (boolean)
1992            if (markerLength == FALSE_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[FALSE_IX], FALSE_TAG_LENGTH)) {
1993                markerIx = FALSE_IX;
1994            }
1995            break;
1996        case 'i': // integer
1997            if (markerLength == INTEGER_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH))
1998                markerIx = INTEGER_IX;
1999            break;
2000        case 'k': // Key of a dictionary
2001            if (markerLength == KEY_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH)) {
2002                markerIx = KEY_IX;
2003                if (isKey) *isKey = true;
2004            }
2005            break;
2006        case 'p': // Plist
2007            if (markerLength == PLIST_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH))
2008                markerIx = PLIST_IX;
2009            break;
2010        case 'r': // real
2011            if (markerLength == REAL_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH))
2012                markerIx = REAL_IX;
2013            break;
2014        case 's': // String
2015            if (markerLength == STRING_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[STRING_IX], STRING_TAG_LENGTH))
2016                markerIx = STRING_IX;
2017            break;
2018        case 't': // true (boolean)
2019            if (markerLength == TRUE_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[TRUE_IX], TRUE_TAG_LENGTH))
2020                markerIx = TRUE_IX;
2021            break;
2022    }
2023
2024    if (!pInfo->allowNewTypes && markerIx != PLIST_IX && markerIx != ARRAY_IX && markerIx != DICT_IX && markerIx != STRING_IX && markerIx != KEY_IX && markerIx != DATA_IX) {
2025        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered new tag when expecting only old-style property list objects"));
2026        return false;
2027    }
2028
2029    switch (markerIx) {
2030        case PLIST_IX:
2031            if (isEmpty) {
2032                pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty plist tag"));
2033                return false;
2034            }
2035            return parsePListTag(pInfo, out);
2036        case ARRAY_IX:
2037            if (isEmpty) {
2038                if (pInfo->skip) {
2039                    *out = NULL;
2040                } else {
2041                    if (pInfo->mutabilityOption == kCFPropertyListImmutable) {
2042                        *out = CFArrayCreate(pInfo->allocator, NULL, 0, &kCFTypeArrayCallBacks);
2043                    } else {
2044                        *out = CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks);
2045                    }
2046                }
2047                return true;
2048            } else {
2049                return parseArrayTag(pInfo, out);
2050            }
2051        case DICT_IX:
2052            if (isEmpty) {
2053                if (pInfo->skip) {
2054                    *out = NULL;
2055                } else {
2056                    if (pInfo->mutabilityOption == kCFPropertyListImmutable) {
2057                        *out = CFDictionaryCreate(pInfo->allocator, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2058                    } else {
2059                        *out = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2060                    }
2061                }
2062                return true;
2063            } else {
2064                return parseDictTag(pInfo, out);
2065            }
2066        case KEY_IX:
2067        case STRING_IX:
2068        {
2069            int tagLen = (markerIx == KEY_IX) ? KEY_TAG_LENGTH : STRING_TAG_LENGTH;
2070            if (isEmpty) {
2071                if (pInfo->skip) {
2072                    *out = NULL;
2073                } else {
2074                    if (pInfo->mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
2075                        *out = CFStringCreateMutable(pInfo->allocator, 0);
2076                    } else {
2077                        *out = CFStringCreateWithCharacters(pInfo->allocator, NULL, 0);
2078                    }
2079                }
2080                return true;
2081            }
2082            if (!parseStringTag(pInfo, (CFStringRef *)out)) {
2083                return false; // parseStringTag will already have set the error string
2084            }
2085            if (!checkForCloseTag(pInfo, CFXMLPlistTags[markerIx], tagLen)) {
2086                __CFPListRelease(*out, pInfo->allocator);
2087                return false;
2088            } else {
2089                return true;
2090            }
2091        }
2092        case DATA_IX:
2093            if (isEmpty) {
2094                pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty <data> on line %d"), lineNumber(pInfo));
2095                return false;
2096            } else {
2097                return parseDataTag(pInfo, out);
2098            }
2099        case DATE_IX:
2100            if (isEmpty) {
2101                pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty <date> on line %d"), lineNumber(pInfo));
2102                return false;
2103            } else {
2104                return parseDateTag(pInfo, out);
2105            }
2106        case TRUE_IX:
2107            if (!isEmpty) {
2108		if (!checkForCloseTag(pInfo, CFXMLPlistTags[TRUE_IX], TRUE_TAG_LENGTH)) {
2109                    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered non-empty <true> on line %d"), lineNumber(pInfo));
2110                    return false;
2111                }
2112	    }
2113            if (pInfo->skip) {
2114                *out = NULL;
2115            } else {
2116                *out = CFRetain(kCFBooleanTrue);
2117            }
2118            return true;
2119        case FALSE_IX:
2120            if (!isEmpty) {
2121		if (!checkForCloseTag(pInfo, CFXMLPlistTags[FALSE_IX], FALSE_TAG_LENGTH)) {
2122                    pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered non-empty <false> on line %d"), lineNumber(pInfo));
2123                    return false;
2124                }
2125	    }
2126            if (pInfo->skip) {
2127                *out = NULL;
2128            } else {
2129                *out = CFRetain(kCFBooleanFalse);
2130            }
2131            return true;
2132        case REAL_IX:
2133            if (isEmpty) {
2134                pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo));
2135                return false;
2136            } else {
2137                return parseRealTag(pInfo, out);
2138            }
2139        case INTEGER_IX:
2140            if (isEmpty) {
2141                pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo));
2142                return false;
2143            } else {
2144                return parseIntegerTag(pInfo, out);
2145            }
2146        default:  {
2147            CFStringRef markerStr = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const UInt8 *)marker, markerLength, kCFStringEncodingUTF8, NO);
2148            pInfo->curr = marker;
2149            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown tag %@ on line %d"), markerStr ? markerStr : CFSTR("<unknown>"), lineNumber(pInfo));
2150            if (markerStr) CFRelease(markerStr);
2151            return false;
2152        }
2153    }
2154}
2155
2156static Boolean parseXMLPropertyList(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) {
2157    while (!pInfo->error && pInfo->curr < pInfo->end) {
2158        UniChar ch;
2159        skipWhitespace(pInfo);
2160        if (pInfo->curr+1 >= pInfo->end) {
2161            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("No XML content found"));
2162            return false;
2163        }
2164        if (*(pInfo->curr) != '<') {
2165            pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unexpected character %c at line %d"), *(pInfo->curr), lineNumber(pInfo));
2166            return false;
2167        }
2168        ch = *(++ pInfo->curr);
2169        if (ch == '!') {
2170            // Comment or DTD
2171            ++ pInfo->curr;
2172            if (pInfo->curr+1 < pInfo->end && *pInfo->curr == '-' && *(pInfo->curr+1) == '-') {
2173                // Comment
2174                pInfo->curr += 2;
2175                skipXMLComment(pInfo);
2176            } else {
2177                skipDTD(pInfo);
2178            }
2179        } else if (ch == '?') {
2180            // Processing instruction
2181            pInfo->curr++;
2182            skipXMLProcessingInstruction(pInfo);
2183        } else {
2184            // Tag or malformed
2185            return parseXMLElement(pInfo, NULL, out);
2186            // Note we do not verify that there was only one element, so a file that has garbage after the first element will nonetheless successfully parse
2187        }
2188    }
2189    // Should never get here
2190    if (!(pInfo->error)) {
2191        pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF"));
2192    }
2193    return false;
2194}
2195
2196static CFStringEncoding encodingForXMLData(CFDataRef data, CFErrorRef *error, CFIndex *skip) {
2197    const uint8_t *bytes = (uint8_t *)CFDataGetBytePtr(data);
2198    UInt32 length = CFDataGetLength(data);
2199    const uint8_t *idx, *end;
2200    char quote;
2201
2202    // Check for the byte order mark first. If we find it, set the skip value so the parser doesn't attempt to parse the BOM itself.
2203    if (length > 4) {
2204        if (*bytes == 0x00 && *(bytes+1) == 0x00 && *(bytes+2) == 0xFE && *(bytes+3) == 0xFF) {
2205            *skip = 4;
2206            return kCFStringEncodingUTF32BE;
2207        } else if (*bytes == 0xFF && *(bytes+1) == 0xFE && *(bytes+2) == 0x00 && *(bytes+3) == 0x00) {
2208            *skip = 4;
2209            return kCFStringEncodingUTF32LE;
2210        }
2211    }
2212
2213    if (length > 3) {
2214        if (*bytes == 0xEF && *(bytes+1) == 0xBB && *(bytes+2) == 0xBF) {
2215            *skip = 3;
2216            return kCFStringEncodingUTF8;
2217        }
2218    }
2219
2220    if (length > 2) {
2221        if (*bytes == 0xFF && *(bytes+1) == 0xFE) {
2222            *skip = 2;
2223            return kCFStringEncodingUTF16LE;
2224        } else if (*bytes == 0xFE && *(bytes+1) == 0xFF) {
2225            *skip = 2;
2226            return kCFStringEncodingUTF16BE;
2227        } else if (*bytes == 0x00 || *(bytes+1) == 0x00) { // This clause checks for a Unicode sequence lacking the byte order mark; technically an error, but this check is recommended by the XML spec
2228            *skip = 2;
2229            return kCFStringEncodingUnicode;
2230        }
2231    }
2232
2233    // Scan for the <?xml.... ?> opening
2234    if (length < 5 || strncmp((char const *) bytes, "<?xml", 5) != 0) return kCFStringEncodingUTF8;
2235    idx = bytes + 5;
2236    end = bytes + length;
2237    // Found "<?xml"; now we scan for "encoding"
2238    while (idx < end) {
2239        uint8_t ch = *idx;
2240        const uint8_t *scan;
2241        if ( ch == '?' || ch == '>') return kCFStringEncodingUTF8;
2242        idx ++;
2243        scan = idx;
2244	if (idx + 8 >= end) {
2245	    if (error) *error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("End of buffer while looking for encoding name"));
2246	    return 0;
2247	}
2248        if (ch == 'e' && *scan++ == 'n' && *scan++ == 'c' && *scan++ == 'o' && *scan++ == 'd' && *scan++ == 'i'
2249            && *scan++ == 'n' && *scan++ == 'g' && *scan++ == '=') {
2250            idx = scan;
2251            break;
2252        }
2253    }
2254    if (idx >= end) return kCFStringEncodingUTF8;
2255    quote = *idx;
2256    if (quote != '\'' && quote != '\"') return kCFStringEncodingUTF8;
2257    else {
2258        CFStringRef encodingName;
2259        const uint8_t *base = idx+1; // Move past the quote character
2260        UInt32 len;
2261        idx ++;
2262        while (idx < end && *idx != quote) idx ++;
2263        if (idx >= end) return kCFStringEncodingUTF8;
2264        len = idx - base;
2265        if (len == 5 && (*base == 'u' || *base == 'U') && (base[1] == 't' || base[1] == 'T') && (base[2] == 'f' || base[2] == 'F') && (base[3] == '-') && (base[4] == '8'))
2266            return kCFStringEncodingUTF8;
2267        encodingName = CFStringCreateWithBytes(kCFAllocatorSystemDefault, base, len, kCFStringEncodingISOLatin1, false);
2268#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
2269        CFStringEncoding enc = CFStringConvertIANACharSetNameToEncoding(encodingName);
2270        if (enc != kCFStringEncodingInvalidId) {
2271            CFRelease(encodingName);
2272            return enc;
2273        }
2274#endif
2275
2276        if (error) {
2277            *error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown encoding (%@)"), encodingName);
2278            CFRelease(encodingName);
2279        }
2280        return 0;
2281    }
2282}
2283
2284bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString);
2285
2286#define SAVE_PLISTS 0
2287
2288#if SAVE_PLISTS
2289static Boolean __savePlistData(CFDataRef data, CFOptionFlags opt) {
2290    uint8_t pn[2048];
2291    uint8_t fn[2048];
2292    uint32_t pnlen = sizeof(pn);
2293    uint8_t *pnp = NULL;
2294    if (0 == _NSGetExecutablePath((char *)pn, &pnlen)) {
2295	pnp = strrchr((char *)pn, '/');
2296    }
2297    if (!pnp) {
2298	pnp = pn;
2299    } else {
2300	pnp++;
2301    }
2302    if (0 == strcmp((char *)pnp, "parse_plists")) return true;
2303    CFUUIDRef r = CFUUIDCreate(kCFAllocatorSystemDefault);
2304    CFStringRef s = CFUUIDCreateString(kCFAllocatorSystemDefault, r);
2305    CFStringRef p = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("/tmp/plists/%s#%@#0x%x"), pnp, s, opt);
2306    _CFStringGetFileSystemRepresentation(p, fn, sizeof(fn));
2307    CFRelease(r);
2308    CFRelease(s);
2309    CFRelease(p);
2310    int fd = open((const char *)fn, O_WRONLY|O_CREAT|O_TRUNC, 0666);
2311    if (fd < 0) return false;
2312    int len = CFDataGetLength(data);
2313    int w = write(fd, CFDataGetBytePtr(data), len);
2314    fsync(fd);
2315    close(fd);
2316    if (w != len) return false;
2317    return true;
2318}
2319#endif
2320
2321// If the data is from a converted string, then originalString is non-NULL. If originalString is NULL, then pass in guessedEncoding.
2322// keyPaths is a set of CFStrings, ':'-separated paths
2323static Boolean _CFPropertyListCreateFromUTF8Data(CFAllocatorRef allocator, CFDataRef xmlData, CFIndex skipBytes, CFStringRef originalString, CFStringEncoding guessedEncoding, CFOptionFlags option, CFErrorRef *outError, Boolean allowNewTypes, CFPropertyListFormat *format, CFSetRef keyPaths, CFTypeRef *out) {
2324    initStatics();
2325
2326    CFAssert1(xmlData != NULL, __kCFLogAssertion, "%s(): NULL data not allowed", __PRETTY_FUNCTION__);
2327    CFAssert2(option == kCFPropertyListImmutable || option == kCFPropertyListMutableContainers || option == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, option);
2328
2329    CFIndex length = CFDataGetLength(xmlData);
2330    if (!length) {
2331        if (outError) *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Conversion of string failed. The string is empty."));
2332        return false;
2333    }
2334
2335    _CFXMLPlistParseInfo pInfoBuf;
2336    _CFXMLPlistParseInfo *pInfo = &pInfoBuf;
2337    CFTypeRef result;
2338
2339    // Ensure that the data is not collected while we are using it
2340    CFRetain(xmlData);
2341    const char *buf = (const char *)CFDataGetBytePtr(xmlData);
2342
2343    // We may have to skip over starting stuff like BOM markers.
2344    buf += skipBytes;
2345
2346    pInfo->begin = buf;
2347    pInfo->end = buf+length;
2348    pInfo->curr = buf;
2349    pInfo->allocator = allocator;
2350    pInfo->error = NULL;
2351    _createStringMap(pInfo);
2352    pInfo->mutabilityOption = option;
2353    pInfo->allowNewTypes = allowNewTypes;
2354    pInfo->skip = false;
2355    pInfo->keyPaths = createTopLevelKeypaths(allocator, keyPaths);
2356
2357    Boolean success = parseXMLPropertyList(pInfo, &result);
2358    if (success && result && format) *format = kCFPropertyListXMLFormat_v1_0;
2359
2360    _cleanupStringMap(pInfo);
2361    if (pInfo->keyPaths && !(0)) CFRelease(pInfo->keyPaths);
2362    CFRelease(xmlData);
2363
2364    if (success) {
2365        *out = result; // caller releases
2366        return true;
2367    }
2368
2369    // Try again, old-style
2370    CFErrorRef oldStyleError = NULL;
2371    result = __CFCreateOldStylePropertyListOrStringsFile(allocator, xmlData, originalString, guessedEncoding, option, outError ? &oldStyleError : NULL, format);
2372    if (result) {
2373        // Release old error, return
2374        if (pInfo->error) CFRelease(pInfo->error);
2375        *out = result;
2376        return true;
2377    }
2378
2379    // Failure, both ways. Set up the error to be given back to caller.
2380    if (!outError) {
2381        if (pInfo->error) CFRelease(pInfo->error);
2382        return false;
2383    }
2384
2385    // Caller's responsibility to release outError
2386    if (pInfo->error && oldStyleError) {
2387        // Add the error from the old-style property list parser to the user info of the original error (pInfo->error), which had better exist
2388        CFDictionaryRef oldUserInfo = CFErrorCopyUserInfo(pInfo->error);
2389        CFMutableDictionaryRef newUserInfo = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, CFDictionaryGetCount(oldUserInfo) + 1, oldUserInfo);
2390        CFDictionaryAddValue(newUserInfo, CFPropertyListOldStyleParserErrorKey, oldStyleError);
2391
2392        // Re-create the xml parser error with this new user info dictionary
2393        CFErrorRef newError = CFErrorCreate(kCFAllocatorSystemDefault, CFErrorGetDomain(pInfo->error), CFErrorGetCode(pInfo->error), newUserInfo);
2394
2395        CFRelease(oldUserInfo);
2396        CFRelease(newUserInfo);
2397        CFRelease(oldStyleError);
2398        CFRelease(pInfo->error);
2399        *outError = newError;
2400
2401    } else if (pInfo->error && !oldStyleError) {
2402        // Return original error
2403        *outError = pInfo->error;
2404    } else if (!pInfo->error && oldStyleError) {
2405        // Return only old-style error
2406        // Probably shouldn't get here
2407        *outError = oldStyleError;
2408    } else if (!pInfo->error && !oldStyleError) {
2409        // Return unknown error
2410        *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown error during parse"));
2411    }
2412    return false;
2413}
2414
2415static CFDataRef _createUTF8DataFromString(CFAllocatorRef allocator, CFStringRef str) {
2416    CFIndex bytesNeeded = 0;
2417    CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, false, NULL, 0, &bytesNeeded);
2418
2419    const char *bytes = (const char *)CFAllocatorAllocate(allocator, bytesNeeded, 0);
2420    CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, false, (uint8_t *)bytes, bytesNeeded, NULL);
2421
2422    CFDataRef utf8Data = CFDataCreateWithBytesNoCopy(allocator, (const UInt8 *)bytes, bytesNeeded, allocator);
2423    return utf8Data;
2424}
2425
2426// Set topLevelKeys to a set of top level keys to decode. If NULL, all keys are decoded. If the top level object is not a dictionary, all objects are decoded. If the plist is not XML, all objects are decoded.
2427static Boolean _CFPropertyListCreateWithData(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFErrorRef *outError, Boolean allowNewTypes, CFPropertyListFormat *format, CFSetRef topLevelKeys, CFTypeRef *out) {
2428    initStatics();
2429    CFStringEncoding encoding;
2430
2431    if (!data || CFDataGetLength(data) == 0) {
2432        if (outError) {
2433            *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Cannot parse a NULL or zero-length data"));
2434        }
2435        return false;
2436    }
2437
2438#if SAVE_PLISTS
2439    __savePlistData(data, option);
2440#endif
2441
2442    // Ignore the error from CFTryParseBinaryPlist -- if it doesn't work, we're going to try again anyway using the XML parser
2443    if (__CFTryParseBinaryPlist(allocator, data, option, out, NULL)) {
2444	if (format) *format = kCFPropertyListBinaryFormat_v1_0;
2445        return true;
2446    }
2447
2448    // Use our own error variable here so we can check it against NULL later
2449    CFErrorRef subError = NULL;
2450    CFIndex skip = 0;
2451    encoding = encodingForXMLData(data, &subError, &skip); // 0 is an error return, NOT MacRoman.
2452
2453    if (encoding == 0) {
2454        // Couldn't find an encoding
2455        // Note that encodingForXMLData() will give us the right values for a standard plist, too.
2456        if (outError && subError == NULL) {
2457	    // encodingForXMLData didn't set an error, so we create a new one here
2458            *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Could not determine the encoding of the XML data"));
2459        } else if (outError && subError) {
2460	    // give the caller the subError, they will release
2461	    *outError = subError;
2462	} else if (!outError && subError) {
2463	    // Release the error
2464	    CFRelease(subError);
2465	}
2466        return false;
2467    }
2468
2469    if (encoding == kCFStringEncodingUTF8) {
2470        // Use fast path
2471        return _CFPropertyListCreateFromUTF8Data(allocator, data, skip, NULL, encoding, option, outError, allowNewTypes, format, topLevelKeys, out);
2472    }
2473
2474    // Convert to UTF8 first
2475    CFStringRef xmlString = CFStringCreateWithBytes(allocator, CFDataGetBytePtr(data) + skip, CFDataGetLength(data) - skip, encoding, false);
2476    if (!xmlString) {
2477        if (outError) *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Could not determine the encoding of the XML data (string creation failed)"));
2478        return false;
2479    }
2480
2481    CFDataRef utf8Data = _createUTF8DataFromString(allocator, xmlString);
2482
2483    Boolean result = _CFPropertyListCreateFromUTF8Data(allocator, utf8Data, 0, xmlString, 0, option, outError, allowNewTypes, format, topLevelKeys, out);
2484
2485    if (xmlString && !(0)) CFRelease(xmlString);
2486    if (utf8Data && !(0)) CFRelease(utf8Data);
2487
2488    return result;
2489}
2490
2491// -----------------------------------------------------------------------------------------------------------------------
2492
2493#pragma mark -
2494#pragma mark Exported Parsing Functions
2495
2496CFTypeRef _CFPropertyListCreateFromXMLStringError(CFAllocatorRef allocator, CFStringRef xmlString, CFOptionFlags option, CFErrorRef *error, Boolean allowNewTypes, CFPropertyListFormat *format) {
2497    // Convert to UTF8 first
2498    CFDataRef utf8Data = _createUTF8DataFromString(allocator, xmlString);
2499    CFTypeRef result = NULL;
2500    _CFPropertyListCreateFromUTF8Data(allocator, utf8Data, 0, xmlString, 0, option, error, allowNewTypes, format, NULL, &result);
2501    if (utf8Data && !(0)) CFRelease(utf8Data);
2502
2503    return result;
2504}
2505
2506CFTypeRef _CFPropertyListCreateFromXMLString(CFAllocatorRef allocator, CFStringRef xmlString, CFOptionFlags option, CFStringRef *errorString, Boolean allowNewTypes, CFPropertyListFormat *format) {
2507    initStatics();
2508    if (errorString) *errorString = NULL;
2509    CFErrorRef error = NULL;
2510    CFTypeRef result = _CFPropertyListCreateFromXMLStringError(allocator, xmlString, option, &error, allowNewTypes, format);
2511
2512    if (errorString && error) {
2513        // The caller is interested in receiving an error message. Pull the debug string out of the CFError returned above
2514        CFDictionaryRef userInfo = CFErrorCopyUserInfo(error);
2515        CFStringRef debugString = NULL;
2516
2517        // Handle a special-case for compatibility - if the XML parse failed and the old-style plist parse failed, construct a special string
2518        CFErrorRef underlyingError = NULL;
2519
2520        Boolean oldStyleFailed = CFDictionaryGetValueIfPresent(userInfo, CFPropertyListOldStyleParserErrorKey, (const void **)&underlyingError);
2521
2522        if (oldStyleFailed) {
2523            CFStringRef xmlParserErrorString, oldStyleParserErrorString;
2524            xmlParserErrorString = (CFStringRef)CFDictionaryGetValue(userInfo, kCFErrorDebugDescriptionKey);
2525
2526            CFDictionaryRef oldStyleParserUserInfo = CFErrorCopyUserInfo(underlyingError);
2527            oldStyleParserErrorString = (CFStringRef)CFDictionaryGetValue(userInfo, kCFErrorDebugDescriptionKey);
2528
2529            // Combine the two strings into a single one that matches the previous implementation
2530            debugString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("XML parser error:\n\t%@\nOld-style plist parser error:\n\t%@\n"), xmlParserErrorString, oldStyleParserErrorString);
2531
2532            CFRelease(oldStyleParserUserInfo);
2533        } else {
2534            debugString = (CFStringRef)CFDictionaryGetValue(userInfo, kCFErrorDebugDescriptionKey);
2535            if (debugString) CFRetain(debugString);
2536        }
2537
2538        // Give the debugString to the caller, who is responsible for releasing it
2539        CFRelease(userInfo);
2540        *errorString = debugString;
2541    }
2542
2543    if (error) {
2544        CFRelease(error);
2545    }
2546
2547    return result;
2548}
2549
2550CF_PRIVATE bool __CFBinaryPlistCreateObjectFiltered(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFMutableSetRef set, CFIndex curDepth, CFSetRef keyPaths, CFPropertyListRef *plist);
2551
2552// Returns a subset of the property list, only including the key paths in the CFSet.
2553bool _CFPropertyListCreateFiltered(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFSetRef keyPaths, CFPropertyListRef *value, CFErrorRef *error) {
2554
2555    initStatics();
2556
2557    if (!keyPaths || !data) {
2558        return false;
2559    }
2560
2561    uint8_t marker;
2562    CFBinaryPlistTrailer trailer;
2563    uint64_t offset;
2564    const uint8_t *databytes = CFDataGetBytePtr(data);
2565    uint64_t datalen = CFDataGetLength(data);
2566    Boolean success = false;
2567    CFTypeRef out = NULL;
2568
2569    // First check to see if it is a binary property list
2570    if (8 <= datalen && __CFBinaryPlistGetTopLevelInfo(databytes, datalen, &marker, &offset, &trailer)) {
2571        uint64_t valueOffset = offset;
2572
2573        // Split up the key path
2574        CFSetRef splitKeyPaths = createTopLevelKeypaths(allocator, keyPaths);
2575
2576        // Create a dictionary to cache objects in
2577        CFMutableDictionaryRef objects = CFDictionaryCreateMutable(allocator, 0, NULL, &kCFTypeDictionaryValueCallBacks);
2578        success = __CFBinaryPlistCreateObjectFiltered(databytes, datalen, valueOffset, &trailer, allocator, option, objects, NULL, 0, splitKeyPaths, &out);
2579
2580        CFRelease(splitKeyPaths);
2581        CFRelease(objects);
2582    } else {
2583	// Try an XML property list
2584        success = _CFPropertyListCreateWithData(allocator, data, option, error, true, NULL, keyPaths, &out);
2585    }
2586
2587    if (success && value) {
2588        *value = out; // caller releases
2589    } else if (out) {
2590        CFRelease(out);
2591    }
2592    return success;
2593}
2594
2595/* Get a single value for a given key in a top-level dictionary in a property list.
2596 @param allocator The allocator to use.
2597 @param data The property list data.
2598 @param option Currently unused, set to 0.
2599 @param keyPath The keyPath to search for in the property list. Keys are colon-separated. Indexes into arrays are specified with an integer (zero-based).
2600 @param value If the key is found and the parameter is non-NULL, this will be set to a reference to the value. It is the caller's responsibility to release the object. If no object is found, will be set to NULL. If this parameter is NULL, this function can be used to check for the existence of a key without creating it by just checking the return value.
2601 @param error If an error occurs, will be set to a valid CFErrorRef. It is the caller's responsibility to release this value.
2602 @return True if the key is found, false otherwise.
2603 */
2604bool _CFPropertyListCreateSingleValue(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFStringRef keyPath, CFPropertyListRef *value, CFErrorRef *error) {
2605
2606    initStatics();
2607
2608    if (!keyPath || CFStringGetLength(keyPath) == 0) {
2609        return false;
2610    }
2611
2612    uint8_t marker;
2613    CFBinaryPlistTrailer trailer;
2614    uint64_t offset;
2615    const uint8_t *databytes = CFDataGetBytePtr(data);
2616    uint64_t datalen = CFDataGetLength(data);
2617    Boolean success = false;
2618
2619    // First check to see if it is a binary property list
2620    if (8 <= datalen && __CFBinaryPlistGetTopLevelInfo(databytes, datalen, &marker, &offset, &trailer)) {
2621        // Split up the key path
2622        CFArrayRef keyPathArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorSystemDefault, keyPath, CFSTR(":"));
2623        uint64_t keyOffset, valueOffset = offset;
2624
2625        // Create a dictionary to cache objects in
2626        CFMutableDictionaryRef objects = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
2627        CFIndex keyPathCount = CFArrayGetCount(keyPathArray);
2628        _CFDictionarySetCapacity(objects, keyPathCount + 1);
2629
2630        for (CFIndex i = 0; i < keyPathCount; i++) {
2631            CFStringRef oneKey = (CFStringRef)CFArrayGetValueAtIndex(keyPathArray, i);
2632            SInt32 intValue = CFStringGetIntValue(oneKey);
2633            if ((intValue == 0 && CFStringCompare(CFSTR("0"), oneKey, 0) != kCFCompareEqualTo) || intValue == INT_MAX || intValue == INT_MIN || intValue < 0) {
2634                // Treat as a string key into a dictionary
2635                success = __CFBinaryPlistGetOffsetForValueFromDictionary3(databytes, datalen, valueOffset, &trailer, (CFTypeRef)oneKey, &keyOffset, &valueOffset, false, objects);
2636            } else {
2637                // Treat as integer index into an array
2638                success = __CFBinaryPlistGetOffsetForValueFromArray2(databytes, datalen, valueOffset, &trailer, intValue, &valueOffset, objects);
2639            }
2640
2641            if (!success) {
2642                break;
2643            }
2644        }
2645
2646        // value could be null if the caller wanted to check for the existence of a key but not bother creating it
2647        if (success && value) {
2648            CFPropertyListRef pl;
2649	    success = __CFBinaryPlistCreateObjectFiltered(databytes, datalen, valueOffset, &trailer, allocator, option, objects, NULL, 0, NULL, &pl);
2650	    if (success) {
2651		// caller's responsibility to release the created object
2652		*value = pl;
2653	    }
2654	}
2655
2656        CFRelease(keyPathArray);
2657        CFRelease(objects);
2658    } else {
2659	// Try an XML property list
2660	// Note: This is currently not any more efficient than grabbing the whole thing. This could be improved in the future.
2661	CFPropertyListRef plist = CFPropertyListCreateWithData(allocator, data, option, NULL, error);
2662        if (plist) {
2663            CFPropertyListRef nextObject = plist;
2664            success = true;
2665            CFArrayRef keyPathArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorSystemDefault, keyPath, CFSTR(":"));
2666            for (CFIndex i = 0;  i < CFArrayGetCount(keyPathArray); i++) {
2667                CFStringRef oneKey = (CFStringRef)CFArrayGetValueAtIndex(keyPathArray, i);
2668                SInt32 intValue = CFStringGetIntValue(oneKey);
2669                if (((intValue == 0 && CFStringCompare(CFSTR("0"), oneKey, 0) != kCFCompareEqualTo) || intValue == INT_MAX || intValue == INT_MIN) && CFGetTypeID((CFTypeRef)nextObject) == dicttype) {
2670                    // Treat as a string key into a dictionary
2671                    nextObject = (CFPropertyListRef)CFDictionaryGetValue((CFDictionaryRef)nextObject, oneKey);
2672                } else if (CFGetTypeID((CFTypeRef)nextObject) == arraytype) {
2673                    // Treat as integer index into an array
2674                    nextObject = (CFPropertyListRef)CFArrayGetValueAtIndex((CFArrayRef)nextObject, intValue);
2675                } else {
2676                    success = false;
2677                    break;
2678                }
2679            }
2680
2681            if (success && nextObject && value) {
2682                *value = nextObject;
2683                // caller's responsibility to release the created object
2684                CFRetain(*value);
2685            } else if (!nextObject) {
2686                success = false;
2687            }
2688
2689            CFRelease(keyPathArray);
2690            CFRelease(plist);
2691        }
2692    }
2693
2694    return success;
2695}
2696
2697// Legacy
2698CFTypeRef _CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xmlData, CFOptionFlags option, CFStringRef *errorString, Boolean allowNewTypes, CFPropertyListFormat *format) {
2699    initStatics();
2700    CFTypeRef out = NULL;
2701    if (errorString) *errorString = NULL;
2702    CFErrorRef error = NULL;
2703    Boolean result = _CFPropertyListCreateWithData(allocator, xmlData, option, &error, allowNewTypes, format, NULL, &out);
2704    if (!result && error && errorString) {
2705        *errorString = __copyErrorDebugDescription(error);
2706    }
2707    if (error) CFRelease(error);
2708    return out;
2709}
2710
2711CFPropertyListRef CFPropertyListCreateWithData(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags options, CFPropertyListFormat *format, CFErrorRef *error) {
2712    initStatics();
2713    CFAssert1(data != NULL, __kCFLogAssertion, "%s(): NULL data not allowed", __PRETTY_FUNCTION__);
2714    CFAssert2(options == kCFPropertyListImmutable || options == kCFPropertyListMutableContainers || options == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, options);
2715    CFPropertyListRef out = NULL;
2716    _CFPropertyListCreateWithData(allocator, data, options, error, true, format, NULL, &out);
2717    return out;
2718}
2719
2720CFPropertyListRef CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xmlData, CFOptionFlags option, CFStringRef *errorString) {
2721    initStatics();
2722    if (errorString) *errorString = NULL;
2723    CFErrorRef error = NULL;
2724    CFPropertyListRef result = CFPropertyListCreateWithData(allocator, xmlData, option, NULL, &error);
2725    if (error && errorString) {
2726        *errorString = __copyErrorDebugDescription(error);
2727    }
2728    if (error) CFRelease(error);
2729    return result;
2730}
2731
2732CFDataRef CFPropertyListCreateData(CFAllocatorRef allocator, CFPropertyListRef propertyList, CFPropertyListFormat format, CFOptionFlags options, CFErrorRef *error) {
2733    initStatics();
2734    CFAssert1(format != kCFPropertyListOpenStepFormat, __kCFLogAssertion, "%s(): kCFPropertyListOpenStepFormat not supported for writing", __PRETTY_FUNCTION__);
2735    CFAssert2(format == kCFPropertyListXMLFormat_v1_0 || format == kCFPropertyListBinaryFormat_v1_0, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, format);
2736    CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__);
2737    __CFAssertIsPList(propertyList);
2738
2739    CFDataRef data = NULL;
2740
2741
2742    CFStringRef validErr = NULL;
2743    if (!_CFPropertyListIsValidWithErrorString(propertyList, format, &validErr)) {
2744        CFLog(kCFLogLevelError, CFSTR("Property list invalid for format: %d (%@)"), format, validErr);
2745	if (validErr) CFRelease(validErr);
2746        return NULL;
2747    }
2748
2749    if (format == kCFPropertyListOpenStepFormat) {
2750        CFLog(kCFLogLevelError, CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2751        return NULL;
2752    } else if (format == kCFPropertyListXMLFormat_v1_0) {
2753        data = _CFPropertyListCreateXMLData(allocator, propertyList, true);
2754    } else if (format == kCFPropertyListBinaryFormat_v1_0) {
2755#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2756        // TODO: Is it more efficient to create a stream here or just use a mutable data?
2757        CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorSystemDefault, allocator);
2758        CFWriteStreamOpen(stream);
2759        CFIndex len = CFPropertyListWrite(propertyList, stream, format, options, error);
2760        if (0 < len) {
2761            data = (CFDataRef)CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten);
2762        }
2763        CFWriteStreamClose(stream);
2764	CFRelease(stream);
2765#else
2766        CFMutableDataRef dataForPlist = CFDataCreateMutable(allocator, 0);
2767        __CFBinaryPlistWrite(propertyList, dataForPlist, 0, options, error);
2768        return dataForPlist;
2769#endif
2770    } else {
2771	CFLog(kCFLogLevelError, CFSTR("Unknown format option"));
2772    }
2773
2774    return data;
2775}
2776
2777#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2778
2779CFIndex CFPropertyListWrite(CFPropertyListRef propertyList, CFWriteStreamRef stream, CFPropertyListFormat format, CFOptionFlags options, CFErrorRef *error) {
2780    initStatics();
2781    CFAssert1(stream != NULL, __kCFLogAssertion, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__);
2782    CFAssert1(format != kCFPropertyListOpenStepFormat, __kCFLogAssertion, "%s(): kCFPropertyListOpenStepFormat not supported for writing", __PRETTY_FUNCTION__);
2783    CFAssert2(format == kCFPropertyListXMLFormat_v1_0 || format == kCFPropertyListBinaryFormat_v1_0, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, format);
2784    CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__);
2785    __CFAssertIsPList(propertyList);
2786    CFAssert1(CFWriteStreamGetTypeID() == CFGetTypeID(stream), __kCFLogAssertion, "%s(): stream argument is not a write stream", __PRETTY_FUNCTION__);
2787    CFAssert1(kCFStreamStatusOpen == CFWriteStreamGetStatus(stream) || kCFStreamStatusWriting == CFWriteStreamGetStatus(stream), __kCFLogAssertion, "%s():  stream is not open", __PRETTY_FUNCTION__);
2788
2789    CFStringRef validErr = NULL;
2790    if (!_CFPropertyListIsValidWithErrorString(propertyList, format, &validErr)) {
2791        CFLog(kCFLogLevelError, CFSTR("Property list invalid for format: %d (%@)"), format, validErr);
2792	if (validErr) CFRelease(validErr);
2793        return 0;
2794    }
2795    if (format == kCFPropertyListOpenStepFormat) {
2796        CFLog(kCFLogLevelError, CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2797        return 0;
2798    }
2799    if (format == kCFPropertyListXMLFormat_v1_0) {
2800        CFDataRef data = _CFPropertyListCreateXMLData(kCFAllocatorSystemDefault, propertyList, true);
2801        if (!data) {
2802            CFLog(kCFLogLevelError, CFSTR("Property list format kCFPropertyListXMLFormat_v1_0 specified but was not a valid property list type"));
2803            return 0;
2804        }
2805        CFIndex len = CFDataGetLength(data);
2806	const uint8_t *ptr = CFDataGetBytePtr(data);
2807	while (0 < len) {
2808	    CFIndex ret = CFWriteStreamWrite(stream, ptr, len);
2809	    if (ret == 0) {
2810                if (error) *error = __CFPropertyListCreateError(kCFPropertyListWriteStreamError, CFSTR("Property list writing could not be completed because stream is full."));
2811                CFRelease(data);
2812	        return 0;
2813	    }
2814	    if (ret < 0) {
2815		CFErrorRef underlyingError = CFWriteStreamCopyError(stream);
2816                if (underlyingError) {
2817                    if (error) {
2818                        // Wrap the error from CFWriteStreamCopy in a new error
2819                        CFMutableDictionaryRef userInfo = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2820                        CFDictionarySetValue(userInfo, kCFErrorDebugDescriptionKey, CFSTR("Property list writing could not be completed because the stream had an unknown error."));
2821                        CFDictionarySetValue(userInfo, kCFErrorUnderlyingErrorKey, underlyingError);
2822                        *error = CFErrorCreate(kCFAllocatorSystemDefault, kCFErrorDomainCocoa, kCFPropertyListWriteStreamError, userInfo);
2823                        CFRelease(userInfo);
2824                    }
2825                    CFRelease(underlyingError);
2826                }
2827		CFRelease(data);
2828		return 0;
2829	    }
2830	    ptr += ret;
2831	    len -= ret;
2832	}
2833	len = CFDataGetLength(data);
2834	CFRelease(data);
2835        return len;
2836    }
2837    if (format == kCFPropertyListBinaryFormat_v1_0) {
2838        CFIndex len = __CFBinaryPlistWrite(propertyList, stream, 0, options, error);
2839        return len;
2840    }
2841    CFLog(kCFLogLevelError, CFSTR("Unknown format option"));
2842    return 0;
2843}
2844
2845CFIndex CFPropertyListWriteToStream(CFPropertyListRef propertyList, CFWriteStreamRef stream, CFPropertyListFormat format, CFStringRef *errorString) {
2846    initStatics();
2847    if (errorString) *errorString = NULL;
2848    CFErrorRef error = NULL;
2849
2850    // For backwards compatibility, we check the format parameter up front since these do not have CFError counterparts in the newer API
2851    CFStringRef validErr = NULL;
2852    if (!_CFPropertyListIsValidWithErrorString(propertyList, format, &validErr)) {
2853	if (errorString) *errorString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("Property list invalid for format (%@)"), validErr);
2854	if (validErr) CFRelease(validErr);
2855        return 0;
2856    }
2857    if (format == kCFPropertyListOpenStepFormat) {
2858        if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
2859        return 0;
2860    }
2861    if (format != kCFPropertyListBinaryFormat_v1_0 && format != kCFPropertyListXMLFormat_v1_0) {
2862        if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Unknown format option"));
2863        return 0;
2864    }
2865
2866    CFIndex result = CFPropertyListWrite(propertyList, stream, format, 0, &error);
2867    if (error && errorString) {
2868        *errorString = __copyErrorDebugDescription(error);
2869    }
2870    if (error) CFRelease(error);
2871    return result;
2872}
2873
2874static bool __convertReadStreamToBytes(CFReadStreamRef stream, CFIndex max, uint8_t **buffer, CFIndex *length, CFErrorRef *error) {
2875    int32_t buflen = 0, bufsize = 0, retlen;
2876    uint8_t *buf = NULL, sbuf[8192];
2877    for (;;) {
2878	retlen = CFReadStreamRead(stream, sbuf, __CFMin(8192, max));
2879        if (retlen <= 0) {
2880            *buffer = buf;
2881            *length = buflen;
2882
2883            if (retlen < 0 && error) {
2884                // Copy the error out
2885                *error = CFReadStreamCopyError(stream);
2886                return false;
2887            }
2888
2889	    return true;
2890	}
2891        if (bufsize < buflen + retlen) {
2892	    if (bufsize < 256 * 1024) {
2893		bufsize *= 4;
2894	    } else if (bufsize < 16 * 1024 * 1024) {
2895		bufsize *= 2;
2896	    } else {
2897		// once in this stage, this will be really slow
2898		// and really potentially fragment memory
2899		bufsize += 256 * 1024;
2900	    }
2901	    if (bufsize < buflen + retlen) bufsize = buflen + retlen;
2902	    buf = (uint8_t *)CFAllocatorReallocate(kCFAllocatorSystemDefault, buf, bufsize, 0);
2903	    if (!buf) HALT;
2904	}
2905	memmove(buf + buflen, sbuf, retlen);
2906	buflen += retlen;
2907        max -= retlen;
2908	if (max <= 0) {
2909	    *buffer = buf;
2910	    *length = buflen;
2911	    return true;
2912	}
2913    }
2914    return true;
2915}
2916
2917CFPropertyListRef CFPropertyListCreateWithStream(CFAllocatorRef allocator, CFReadStreamRef stream, CFIndex streamLength, CFOptionFlags mutabilityOption, CFPropertyListFormat *format, CFErrorRef *error) {
2918    initStatics();
2919
2920    CFAssert1(stream != NULL, __kCFLogAssertion, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__);
2921    CFAssert1(CFReadStreamGetTypeID() == CFGetTypeID(stream), __kCFLogAssertion, "%s(): stream argument is not a read stream", __PRETTY_FUNCTION__);
2922    CFAssert1(kCFStreamStatusOpen == CFReadStreamGetStatus(stream) || kCFStreamStatusReading == CFReadStreamGetStatus(stream), __kCFLogAssertion, "%s():  stream is not open", __PRETTY_FUNCTION__);
2923    CFAssert2(mutabilityOption == kCFPropertyListImmutable || mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, mutabilityOption);
2924
2925    if (0 == streamLength) streamLength = LONG_MAX;
2926    CFErrorRef underlyingError = NULL;
2927    CFIndex buflen = 0;
2928    uint8_t *buffer = NULL;
2929    if (!__convertReadStreamToBytes(stream, streamLength, &buffer, &buflen, &underlyingError)) {
2930        if (error) {
2931            // Wrap the error from CFReadStream in a new error in the cocoa domain
2932            CFMutableDictionaryRef userInfo = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2933            CFDictionarySetValue(userInfo, kCFErrorDebugDescriptionKey, CFSTR("Property list reading could not be completed because the stream had an unknown error. Did you forget to open the stream?"));
2934            if (underlyingError) {
2935                CFDictionarySetValue(userInfo, kCFErrorUnderlyingErrorKey, underlyingError);
2936            }
2937            *error = CFErrorCreate(kCFAllocatorSystemDefault, kCFErrorDomainCocoa, kCFPropertyListReadStreamError, userInfo);
2938            CFRelease(userInfo);
2939        }
2940        if (underlyingError) {
2941            CFRelease(underlyingError);
2942        }
2943        return NULL;
2944    }
2945
2946    if (!buffer || buflen < 6) {
2947        if (buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, buffer);
2948        if (error) *error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("stream had too few bytes"));
2949        return NULL;
2950    }
2951
2952    CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, buffer, buflen, kCFAllocatorSystemDefault);
2953    CFPropertyListRef pl = NULL; // initialize to null, because if the following call fails we must return NULL
2954    _CFPropertyListCreateWithData(allocator, data, mutabilityOption, error, true, format, NULL, &pl);
2955    CFRelease(data);
2956
2957    return pl;
2958}
2959
2960CFPropertyListRef CFPropertyListCreateFromStream(CFAllocatorRef allocator, CFReadStreamRef stream, CFIndex length, CFOptionFlags mutabilityOption, CFPropertyListFormat *format, CFStringRef *errorString) {
2961    initStatics();
2962    if (errorString) *errorString = NULL;
2963    CFErrorRef error = NULL;
2964    CFPropertyListRef result = CFPropertyListCreateWithStream(allocator, stream, length, mutabilityOption, format, &error);
2965    if (error && errorString) {
2966        *errorString = __copyErrorDebugDescription(error);
2967    }
2968    if (error) CFRelease(error);
2969    return result;
2970}
2971
2972#endif //DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
2973
2974#pragma mark -
2975#pragma mark Property List Copies
2976
2977static CFArrayRef _arrayDeepImmutableCopy(CFAllocatorRef allocator, CFArrayRef array, CFOptionFlags mutabilityOption) {
2978    CFArrayRef result = NULL;
2979    CFIndex i, c = CFArrayGetCount(array);
2980    if (c == 0) {
2981        result = CFArrayCreate(allocator, NULL, 0, &kCFTypeArrayCallBacks);
2982    } else {
2983        new_cftype_array(values, c);
2984        CFArrayGetValues(array, CFRangeMake(0, c), values);
2985        for (i = 0; i < c; i ++) {
2986            CFTypeRef newValue = CFPropertyListCreateDeepCopy(allocator, values[i], mutabilityOption);
2987            if (newValue == NULL) {
2988                break;
2989            }
2990            __CFAssignWithWriteBarrier((void **)values + i, (void *)newValue);
2991        }
2992        result = (i == c) ? CFArrayCreate(allocator, values, c, &kCFTypeArrayCallBacks) : NULL;
2993        c = i;
2994        for (i = 0; i < c; i ++) CFRelease(values[i]);
2995        free_cftype_array(values);
2996    }
2997    return result;
2998}
2999
3000static CFMutableArrayRef _arrayDeepMutableCopy(CFAllocatorRef allocator, CFArrayRef array, CFOptionFlags mutabilityOption) {
3001    CFIndex i, c = CFArrayGetCount(array);
3002    CFMutableArrayRef result = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
3003    if (result) {
3004        for (i = 0; i < c; i ++) {
3005            CFTypeRef newValue = CFPropertyListCreateDeepCopy(allocator, CFArrayGetValueAtIndex(array, i), mutabilityOption);
3006            if (!newValue) break;
3007            CFArrayAppendValue(result, newValue);
3008            CFRelease(newValue);
3009        }
3010        if (i != c) {
3011            CFRelease(result);
3012            result = NULL;
3013        }
3014    }
3015    return result;
3016}
3017
3018CFPropertyListRef CFPropertyListCreateDeepCopy(CFAllocatorRef allocator, CFPropertyListRef propertyList, CFOptionFlags mutabilityOption) {
3019    initStatics();
3020    CFPropertyListRef result = NULL;
3021    CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): cannot copy a NULL property list", __PRETTY_FUNCTION__);
3022    __CFAssertIsPList(propertyList);
3023    CFAssert2(mutabilityOption == kCFPropertyListImmutable || mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, mutabilityOption);
3024	if (!CFPropertyListIsValid(propertyList, kCFPropertyListBinaryFormat_v1_0)) return NULL;
3025
3026    CFTypeID typeID = CFGetTypeID(propertyList);
3027    if (typeID == dicttype) {
3028        CFDictionaryRef dict = (CFDictionaryRef)propertyList;
3029        Boolean isMutable = (mutabilityOption != kCFPropertyListImmutable);
3030        CFIndex count = CFDictionaryGetCount(dict);
3031        if (count == 0) {
3032            result = isMutable ? CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks): CFDictionaryCreate(allocator, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
3033        } else {
3034            new_cftype_array(keys, 2 * count);
3035            CFTypeRef *values;
3036            CFIndex i;
3037            values = keys+count;
3038            CFDictionaryGetKeysAndValues(dict, keys, values);
3039            for (i = 0; i < count; i ++) {
3040                CFTypeRef newKey = CFStringCreateCopy(allocator, (CFStringRef)keys[i]);
3041                if (newKey == NULL) {
3042                    break;
3043                }
3044                __CFAssignWithWriteBarrier((void **)keys + i, (void *)newKey);
3045                CFTypeRef newValue = CFPropertyListCreateDeepCopy(allocator, values[i], mutabilityOption);
3046                if (newValue == NULL) {
3047                    CFRelease(keys[i]);
3048                    break;
3049                }
3050                __CFAssignWithWriteBarrier((void **)values + i, (void *)newValue);
3051            }
3052            if (i == count) {
3053                result = isMutable ? CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) : CFDictionaryCreate(allocator, keys, values, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
3054                for (i = 0; i < count; i ++) {
3055                    if (isMutable) {
3056                        CFDictionarySetValue((CFMutableDictionaryRef)result, keys[i], values[i]);
3057                    }
3058                    CFRelease(keys[i]);
3059                    CFRelease(values[i]);
3060                }
3061            } else {
3062                result = NULL;
3063                count = i;
3064                for (i = 0; i < count; i ++) {
3065                    CFRelease(keys[i]);
3066                    CFRelease(values[i]);
3067                }
3068            }
3069            free_cftype_array(keys);
3070        }
3071    } else if (typeID == arraytype) {
3072        if (mutabilityOption == kCFPropertyListImmutable) {
3073            result = _arrayDeepImmutableCopy(allocator, (CFArrayRef)propertyList, mutabilityOption);
3074        } else {
3075            result = _arrayDeepMutableCopy(allocator, (CFArrayRef)propertyList, mutabilityOption);
3076        }
3077    } else if (typeID == datatype) {
3078        if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
3079            result = CFDataCreateMutableCopy(allocator, 0, (CFDataRef)propertyList);
3080        } else {
3081            result = CFDataCreateCopy(allocator, (CFDataRef)propertyList);
3082        }
3083    } else if (typeID == numbertype) {
3084        // Warning - this will break if byteSize is ever greater than 128
3085        uint8_t bytes[128];
3086        CFNumberType numType = _CFNumberGetType2((CFNumberRef)propertyList);
3087        CFNumberGetValue((CFNumberRef)propertyList, numType, (void *)bytes);
3088        result = CFNumberCreate(allocator, numType, (void *)bytes);
3089    } else if (typeID == booltype) {
3090        // Booleans are immutable & shared instances
3091        CFRetain(propertyList);
3092        result = propertyList;
3093    } else if (typeID == datetype) {
3094        // Dates are immutable
3095        result = CFDateCreate(allocator, CFDateGetAbsoluteTime((CFDateRef)propertyList));
3096    } else if (typeID == stringtype) {
3097        if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
3098            result = CFStringCreateMutableCopy(allocator, 0, (CFStringRef)propertyList);
3099        } else {
3100            result = CFStringCreateCopy(allocator, (CFStringRef)propertyList);
3101        }
3102    } else {
3103        CFAssert2(false, __kCFLogAssertion, "%s(): %p is not a property list type", __PRETTY_FUNCTION__, propertyList);
3104        result = NULL;
3105    }
3106    return result;
3107}
3108
3109