]> git.saurik.com Git - apple/cf.git/blobdiff - CFPropertyList.c
CF-476.10.tar.gz
[apple/cf.git] / CFPropertyList.c
diff --git a/CFPropertyList.c b/CFPropertyList.c
new file mode 100644 (file)
index 0000000..e3a5392
--- /dev/null
@@ -0,0 +1,2714 @@
+/*
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/*     CFPropertyList.c
+       Copyright 1999-2002, Apple, Inc. All rights reserved.
+       Responsibility: Christopher Kane
+*/
+
+#include <CoreFoundation/CFPropertyList.h>
+#include <CoreFoundation/CFDate.h>
+#include <CoreFoundation/CFNumber.h>
+#include <CoreFoundation/CFSet.h>
+#include "CFPriv.h"
+#include "CFStringEncodingConverter.h"
+#include "CFInternal.h"
+#include <CoreFoundation/CFStream.h>
+#include <CoreFoundation/CFCalendar.h>
+#include <limits.h>
+#include <float.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <ctype.h>
+#if DEPLOYMENT_TARGET_MACOSX
+#include <CoreFoundation/CFPreferences.h>
+#include <mach-o/dyld.h>
+#endif
+
+
+__private_extern__ bool allowMissingSemi = false;
+
+// Should move this somewhere else
+intptr_t _CFDoOperation(intptr_t code, intptr_t subcode1, intptr_t subcode2) {
+    switch (code) {
+    case 15317: allowMissingSemi = subcode1 ? true : false; break;
+    }
+    return code;
+}
+
+#define PLIST_IX    0
+#define ARRAY_IX    1
+#define DICT_IX     2
+#define KEY_IX      3
+#define STRING_IX   4
+#define DATA_IX     5
+#define DATE_IX     6
+#define REAL_IX     7
+#define INTEGER_IX  8
+#define TRUE_IX     9
+#define FALSE_IX    10
+#define DOCTYPE_IX  11
+#define CDSECT_IX   12
+
+#define PLIST_TAG_LENGTH       5
+#define ARRAY_TAG_LENGTH       5
+#define DICT_TAG_LENGTH                4
+#define KEY_TAG_LENGTH         3
+#define STRING_TAG_LENGTH      6
+#define DATA_TAG_LENGTH                4
+#define DATE_TAG_LENGTH                4
+#define REAL_TAG_LENGTH                4
+#define INTEGER_TAG_LENGTH     7
+#define TRUE_TAG_LENGTH                4
+#define FALSE_TAG_LENGTH       5
+#define DOCTYPE_TAG_LENGTH     7
+#define CDSECT_TAG_LENGTH      9
+
+// don't allow _CFKeyedArchiverUID here
+#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);
+
+static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, CFMutableSetRef set, CFPropertyListFormat format);
+
+static CFTypeID stringtype = -1, datatype = -1, numbertype = -1, datetype = -1;
+static CFTypeID booltype = -1, nulltype = -1, dicttype = -1, arraytype = -1, settype = -1;
+
+static void initStatics() {
+    if ((CFTypeID)-1 == stringtype) {
+        stringtype = CFStringGetTypeID();
+    }
+    if ((CFTypeID)-1 == datatype) {
+        datatype = CFDataGetTypeID();
+    }
+    if ((CFTypeID)-1 == numbertype) {
+        numbertype = CFNumberGetTypeID();
+    }
+    if ((CFTypeID)-1 == booltype) {
+        booltype = CFBooleanGetTypeID();
+    }
+    if ((CFTypeID)-1 == datetype) {
+        datetype = CFDateGetTypeID();
+    }
+    if ((CFTypeID)-1 == dicttype) {
+        dicttype = CFDictionaryGetTypeID();
+    }
+    if ((CFTypeID)-1 == arraytype) {
+        arraytype = CFArrayGetTypeID();
+    }
+    if ((CFTypeID)-1 == settype) {
+        settype = CFSetGetTypeID();
+    }
+    if ((CFTypeID)-1 == nulltype) {
+        nulltype = CFNullGetTypeID();
+    }
+}
+
+struct context {
+    bool answer;
+    CFMutableSetRef set;
+    CFPropertyListFormat format;
+};
+
+static void __CFPropertyListIsArrayPlistAux(const void *value, void *context) {
+    struct context *ctx = (struct context *)context;
+    if (!ctx->answer) return;
+#if defined(DEBUG)
+    if (!value) CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property list arrays cannot contain NULL"));
+#endif
+    ctx->answer = value && __CFPropertyListIsValidAux(value, true, ctx->set, ctx->format);
+}
+
+static void __CFPropertyListIsDictPlistAux(const void *key, const void *value, void *context) {
+    struct context *ctx = (struct context *)context;
+    if (!ctx->answer) return;
+#if defined(DEBUG)
+    if (!key) CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property list dictionaries cannot contain NULL keys"));
+    if (!value) CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property list dictionaries cannot contain NULL values"));
+    if (stringtype != CFGetTypeID(key)) {
+       CFStringRef desc = CFCopyTypeIDDescription(CFGetTypeID(key));
+       CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property list dictionaries may only have keys which are CFStrings, not '%@'"), desc);
+       CFRelease(desc);
+    }
+#endif
+    ctx->answer = key && value && (stringtype == CFGetTypeID(key)) && __CFPropertyListIsValidAux(value, true, ctx->set, ctx->format);
+}
+
+static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, CFMutableSetRef set, CFPropertyListFormat format) {
+    CFTypeID type;
+#if defined(DEBUG)
+    if (!plist) CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property lists cannot contain NULL"));
+#endif
+    if (!plist) return false;
+    type = CFGetTypeID(plist);
+    if (stringtype == type) return true;
+    if (datatype == type) return true;
+    if (kCFPropertyListOpenStepFormat != format) {
+       if (booltype == type) return true;
+       if (numbertype == type) return true;
+       if (datetype == type) return true;
+#if DEPLOYMENT_TARGET_MACOSX
+       if (_CFKeyedArchiverUIDGetTypeID() == type) return true;
+#endif
+    }
+    if (!recursive && arraytype == type) return true;
+    if (!recursive && dicttype == type) return true;
+    // at any one invocation of this function, set should contain the objects in the "path" down to this object
+#if defined(DEBUG)
+    if (CFSetContainsValue(set, plist)) CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property lists cannot contain recursive container references"));
+#endif
+    if (CFSetContainsValue(set, plist)) return false;
+    if (arraytype == type) {
+       struct context ctx = {true, set, format}; 
+       CFSetAddValue(set, plist);
+       CFArrayApplyFunction((CFArrayRef)plist, CFRangeMake(0, CFArrayGetCount((CFArrayRef)plist)), __CFPropertyListIsArrayPlistAux, &ctx);
+       CFSetRemoveValue(set, plist);
+       return ctx.answer;
+    }
+    if (dicttype == type) {
+       struct context ctx = {true, set, format}; 
+       CFSetAddValue(set, plist);
+       CFDictionaryApplyFunction((CFDictionaryRef)plist, __CFPropertyListIsDictPlistAux, &ctx);
+       CFSetRemoveValue(set, plist);
+       return ctx.answer;
+    }
+#if defined(DEBUG)
+    {
+       CFStringRef desc = CFCopyTypeIDDescription(type);
+       CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): property lists cannot contain objects of type '%@'"), desc);
+       CFRelease(desc);
+    }
+#endif
+    return false;
+}
+
+Boolean CFPropertyListIsValid(CFPropertyListRef plist, CFPropertyListFormat format) {
+    initStatics();
+    CFAssert1(plist != NULL, __kCFLogAssertion, "%s(): NULL is not a property list", __PRETTY_FUNCTION__);
+    CFMutableSetRef set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
+    bool result = __CFPropertyListIsValidAux(plist, true, set, format);
+    CFRelease(set);
+    return result;
+}
+
+static const UniChar CFXMLPlistTags[13][10]= {
+{'p', 'l', 'i', 's', 't',   '\0', '\0', '\0', '\0', '\0'},
+{'a', 'r', 'r', 'a', 'y',   '\0', '\0', '\0', '\0', '\0'},
+{'d', 'i', 'c', 't',  '\0', '\0', '\0', '\0', '\0', '\0'},
+{'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
+{'s', 't', 'r', 'i', 'n', 'g',    '\0', '\0', '\0', '\0'},
+{'d', 'a', 't', 'a',  '\0', '\0', '\0', '\0', '\0', '\0'},
+{'d', 'a', 't', 'e',  '\0', '\0', '\0', '\0', '\0', '\0'},
+{'r', 'e', 'a', 'l',  '\0', '\0', '\0', '\0', '\0', '\0'},
+{'i', 'n', 't', 'e', 'g', 'e', 'r',     '\0', '\0', '\0'},
+{'t', 'r', 'u', 'e',  '\0', '\0', '\0', '\0', '\0', '\0'},
+{'f', 'a', 'l', 's', 'e',   '\0', '\0', '\0', '\0', '\0'},
+{'D', 'O', 'C', 'T', 'Y', 'P', 'E',     '\0', '\0', '\0'},
+{'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[',       '\0'}
+};
+
+typedef struct {
+    const UniChar *begin; // first character of the XML to be parsed
+    const UniChar *curr;  // current parse location
+    const UniChar *end;   // the first character _after_ the end of the XML
+    CFStringRef errorString;
+    CFAllocatorRef allocator;
+    UInt32 mutabilityOption;
+    CFMutableSetRef stringSet;  // set of all strings involved in this parse; allows us to share non-mutable strings in the returned plist
+    CFMutableStringRef tmpString; // Mutable string with external characters that functions can feel free to use as temporary storage as the parse progresses
+    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)
+    char _padding[3];
+} _CFXMLPlistParseInfo;
+
+static CFTypeRef parseOldStylePropertyListOrStringsFile(_CFXMLPlistParseInfo *pInfo);
+
+
+
+// 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.
+
+// Null-terminated, ASCII or UTF8 string
+//
+static void _plistAppendUTF8CString(CFMutableDataRef mData, const char *cString) {
+    CFDataAppendBytes (mData, (const UInt8 *)cString, strlen(cString));
+}
+
+// UniChars
+//
+static void _plistAppendCharacters(CFMutableDataRef mData, const UniChar *chars, CFIndex length) {
+    CFIndex curLoc = 0;
+
+    do {       // Flush out ASCII chars, BUFLEN at a time
+        #define BUFLEN 400
+       UInt8 buf[BUFLEN], *bufPtr = buf;
+       CFIndex cnt = 0;
+        while (cnt < length && (cnt - curLoc < BUFLEN) && (chars[cnt] < 128)) *bufPtr++ = (UInt8)(chars[cnt++]);
+        if (cnt > curLoc) {    // Flush any ASCII bytes
+            CFDataAppendBytes(mData, buf, cnt - curLoc);
+            curLoc = cnt;
+        }
+    } while (curLoc < length && (chars[curLoc] < 128));        // We will exit out of here when we run out of chars or hit a non-ASCII char
+
+    if (curLoc < length) {     // Now deal with non-ASCII chars
+        CFDataRef data = NULL;
+        CFStringRef str = NULL;
+        if ((str = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, chars + curLoc, length - curLoc, kCFAllocatorNull))) {
+            if ((data = CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault, str, kCFStringEncodingUTF8, 0))) {
+                CFDataAppendBytes (mData, CFDataGetBytePtr(data), CFDataGetLength(data));
+                CFRelease(data);
+            }
+            CFRelease(str);
+        }
+        CFAssert1(str && data, __kCFLogAssertion, "%s(): Error writing plist", __PRETTY_FUNCTION__); 
+    }
+}
+
+// Append CFString
+//
+static void _plistAppendString(CFMutableDataRef mData, CFStringRef str) {
+    const UniChar *chars;
+    const char *cStr;
+    CFDataRef data;
+    if ((chars = CFStringGetCharactersPtr(str))) {
+        _plistAppendCharacters(mData, chars, CFStringGetLength(str));
+    } else if ((cStr = CFStringGetCStringPtr(str, kCFStringEncodingASCII)) || (cStr = CFStringGetCStringPtr(str, kCFStringEncodingUTF8))) {
+        _plistAppendUTF8CString(mData, cStr);
+    } else if ((data = CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault, str, kCFStringEncodingUTF8, 0))) {
+        CFDataAppendBytes (mData, CFDataGetBytePtr(data), CFDataGetLength(data));
+        CFRelease(data);
+    } else {
+       CFAssert1(TRUE, __kCFLogAssertion, "%s(): Error in plist writing", __PRETTY_FUNCTION__);
+    }
+}
+
+
+// Append CFString-style format + arguments
+//
+static void _plistAppendFormat(CFMutableDataRef mData, CFStringRef format, ...) {
+    CFStringRef fStr; 
+    va_list argList;
+
+    va_start(argList, format);
+    fStr = CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault, NULL, format, argList);
+    va_end(argList);
+
+    CFAssert1(fStr, __kCFLogAssertion, "%s(): Error writing plist", __PRETTY_FUNCTION__);
+    _plistAppendString(mData, fStr);
+    CFRelease(fStr);
+}
+
+
+
+static void _appendIndents(CFIndex numIndents, CFMutableDataRef str) {
+#define NUMTABS 4
+    static const UniChar tabs[NUMTABS] = {'\t','\t','\t','\t'};
+    for (; numIndents > 0; numIndents -= NUMTABS) _plistAppendCharacters(str, tabs, (numIndents >= NUMTABS) ? NUMTABS : numIndents);
+}
+
+/* Append the escaped version of origStr to mStr.
+*/
+static void _appendEscapedString(CFStringRef origStr, CFMutableDataRef mStr) {
+#define BUFSIZE 64
+    CFIndex i, length = CFStringGetLength(origStr);
+    CFIndex bufCnt = 0;
+    UniChar buf[BUFSIZE];
+    CFStringInlineBuffer inlineBuffer;
+
+    CFStringInitInlineBuffer(origStr, &inlineBuffer, CFRangeMake(0, length));
+
+    for (i = 0; i < length; i ++) {
+       UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&inlineBuffer, i);
+        switch(ch) {
+            case '<':
+               if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt);
+               bufCnt = 0;
+               _plistAppendUTF8CString(mStr, "&lt;");
+                break;
+            case '>':
+               if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt);
+               bufCnt = 0;
+               _plistAppendUTF8CString(mStr, "&gt;");
+                break;
+            case '&':
+               if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt);
+               bufCnt = 0;
+               _plistAppendUTF8CString(mStr, "&amp;");
+                break;
+            default:
+               buf[bufCnt++] = ch;
+               if (bufCnt == BUFSIZE) {
+                   _plistAppendCharacters(mStr, buf, bufCnt);
+                   bufCnt = 0;
+               }
+               break;
+        }
+    }
+    if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt);
+}
+
+
+
+/* Base-64 encoding/decoding */
+
+/* The base-64 encoding packs three 8-bit bytes into four 7-bit ASCII
+ * characters.  If the number of bytes in the original data isn't divisable
+ * by three, "=" characters are used to pad the encoded data.  The complete
+ * set of characters used in base-64 are:
+ *
+ *      'A'..'Z' => 00..25
+ *      'a'..'z' => 26..51
+ *      '0'..'9' => 52..61
+ *      '+'      => 62
+ *      '/'      => 63
+ *      '='      => pad
+ */
+
+// Write the inputData to the mData using Base 64 encoding
+
+static void _XMLPlistAppendDataUsingBase64(CFMutableDataRef mData, CFDataRef inputData, CFIndex indent) {
+    static const char __CFPLDataEncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+    #define MAXLINELEN 76
+    char buf[MAXLINELEN + 4 + 2];      // For the slop and carriage return and terminating NULL
+    
+    const uint8_t *bytes = CFDataGetBytePtr(inputData);
+    CFIndex length = CFDataGetLength(inputData);
+    CFIndex i, pos;
+    const uint8_t *p;
+
+    if (indent > 8) indent = 8; // refuse to indent more than 64 characters
+
+    pos = 0;           // position within buf
+
+    for (i = 0, p = bytes; i < length; i++, p++) {
+        /* 3 bytes are encoded as 4 */
+        switch (i % 3) {
+            case 0:
+                buf[pos++] = __CFPLDataEncodeTable [ ((p[0] >> 2) & 0x3f)];
+                break;
+            case 1:
+                buf[pos++] = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 4) & 0x3f)];
+                break;
+            case 2:
+                buf[pos++] = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 6) & 0x3f)];
+                buf[pos++] = __CFPLDataEncodeTable [ (p[0] & 0x3f)];
+                break;
+        }
+        /* Flush the line out every 76 (or fewer) chars --- indents count against the line length*/
+        if (pos >= MAXLINELEN - 8 * indent) {
+            buf[pos++] = '\n';
+            buf[pos++] = 0;
+            _appendIndents(indent, mData);
+            _plistAppendUTF8CString(mData, buf);
+            pos = 0;
+        }
+    }
+        
+    switch (i % 3) {
+       case 0:
+            break;
+       case 1:
+            buf[pos++] = __CFPLDataEncodeTable [ ((p[-1] << 4) & 0x30)];
+            buf[pos++] = '=';
+            buf[pos++] = '=';
+            break;
+       case 2:
+            buf[pos++] =  __CFPLDataEncodeTable [ ((p[-1] << 2) & 0x3c)];
+            buf[pos++] = '=';
+            break;
+    }
+    
+    if (pos > 0) {
+        buf[pos++] = '\n';
+        buf[pos++] = 0;
+        _appendIndents(indent, mData);
+        _plistAppendUTF8CString(mData, buf);
+    }
+}
+
+extern CFStringRef __CFNumberCopyFormattingDescriptionAsFloat64(CFTypeRef cf);
+
+static void _CFAppendXML0(CFTypeRef object, UInt32 indentation, CFMutableDataRef xmlString) {
+    UInt32 typeID = CFGetTypeID(object);
+    _appendIndents(indentation, xmlString);
+    if (typeID == stringtype) {
+        _plistAppendUTF8CString(xmlString, "<");
+        _plistAppendCharacters(xmlString, CFXMLPlistTags[STRING_IX], STRING_TAG_LENGTH);
+        _plistAppendUTF8CString(xmlString, ">");
+       _appendEscapedString((CFStringRef)object, xmlString);
+        _plistAppendUTF8CString(xmlString, "</");
+        _plistAppendCharacters(xmlString, CFXMLPlistTags[STRING_IX], STRING_TAG_LENGTH);
+        _plistAppendUTF8CString(xmlString, ">\n");
+#if DEPLOYMENT_TARGET_MACOSX
+    } else if (typeID == _CFKeyedArchiverUIDGetTypeID()) {
+        _plistAppendUTF8CString(xmlString, "<");
+        _plistAppendCharacters(xmlString, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH);
+        _plistAppendUTF8CString(xmlString, ">\n");
+           _appendIndents(indentation+1, xmlString);
+           _plistAppendUTF8CString(xmlString, "<");
+           _plistAppendCharacters(xmlString, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH);
+           _plistAppendUTF8CString(xmlString, ">");
+           _appendEscapedString(CFSTR("CF$UID"), xmlString);
+           _plistAppendUTF8CString(xmlString, "</");
+           _plistAppendCharacters(xmlString, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH);
+           _plistAppendUTF8CString(xmlString, ">\n");
+           _appendIndents(indentation + 1, xmlString);
+            _plistAppendUTF8CString(xmlString, "<");
+            _plistAppendCharacters(xmlString, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH);
+            _plistAppendUTF8CString(xmlString, ">");
+
+           uint64_t v = _CFKeyedArchiverUIDGetValue((CFKeyedArchiverUIDRef)object);
+           CFNumberRef num = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt64Type, &v);
+            _plistAppendFormat(xmlString, CFSTR("%@"), num);
+           CFRelease(num);
+
+            _plistAppendUTF8CString(xmlString, "</");
+            _plistAppendCharacters(xmlString, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH);
+            _plistAppendUTF8CString(xmlString, ">\n");
+        _appendIndents(indentation, xmlString);
+        _plistAppendUTF8CString(xmlString, "</");
+        _plistAppendCharacters(xmlString, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH);
+        _plistAppendUTF8CString(xmlString, ">\n");
+#elif 0
+#else
+#error Unknown or unspecified DEPLOYMENT_TARGET
+#endif
+    } else if (typeID == arraytype) {
+        UInt32 i, count = CFArrayGetCount((CFArrayRef)object);
+        if (count == 0) {
+            _plistAppendUTF8CString(xmlString, "<");
+            _plistAppendCharacters(xmlString, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH);
+            _plistAppendUTF8CString(xmlString, "/>\n");
+            return;
+        }
+        _plistAppendUTF8CString(xmlString, "<");
+        _plistAppendCharacters(xmlString, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH);
+        _plistAppendUTF8CString(xmlString, ">\n");
+        for (i = 0; i < count; i ++) {
+            _CFAppendXML0(CFArrayGetValueAtIndex((CFArrayRef)object, i), indentation+1, xmlString);
+        }
+        _appendIndents(indentation, xmlString);
+        _plistAppendUTF8CString(xmlString, "</");
+        _plistAppendCharacters(xmlString, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH);
+        _plistAppendUTF8CString(xmlString, ">\n");
+    } else if (typeID == dicttype) {
+        UInt32 i, count = CFDictionaryGetCount((CFDictionaryRef)object);
+        CFMutableArrayRef keyArray;
+        CFTypeRef *keys;
+        if (count == 0) {
+            _plistAppendUTF8CString(xmlString, "<");
+            _plistAppendCharacters(xmlString, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH);
+            _plistAppendUTF8CString(xmlString, "/>\n");
+            return;
+        }
+        _plistAppendUTF8CString(xmlString, "<");
+        _plistAppendCharacters(xmlString, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH);
+        _plistAppendUTF8CString(xmlString, ">\n");
+        keys = (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(CFTypeRef), 0);
+        CFDictionaryGetKeysAndValues((CFDictionaryRef)object, keys, NULL);
+        keyArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, count, &kCFTypeArrayCallBacks);
+        CFArrayReplaceValues(keyArray, CFRangeMake(0, 0), keys, count);
+        CFArraySortValues(keyArray, CFRangeMake(0, count), (CFComparatorFunction)CFStringCompare, NULL);
+        CFArrayGetValues(keyArray, CFRangeMake(0, count), keys);
+        CFRelease(keyArray);
+        for (i = 0; i < count; i ++) {
+            CFTypeRef key = keys[i];
+            _appendIndents(indentation+1, xmlString);
+            _plistAppendUTF8CString(xmlString, "<");
+            _plistAppendCharacters(xmlString, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH);
+            _plistAppendUTF8CString(xmlString, ">");
+           _appendEscapedString((CFStringRef)key, xmlString);
+            _plistAppendUTF8CString(xmlString, "</");
+            _plistAppendCharacters(xmlString, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH);
+            _plistAppendUTF8CString(xmlString, ">\n");
+            _CFAppendXML0(CFDictionaryGetValue((CFDictionaryRef)object, key), indentation+1, xmlString);
+        }
+        CFAllocatorDeallocate(kCFAllocatorSystemDefault, keys);
+        _appendIndents(indentation, xmlString);
+        _plistAppendUTF8CString(xmlString, "</");
+        _plistAppendCharacters(xmlString, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH);
+        _plistAppendUTF8CString(xmlString, ">\n");
+    } else if (typeID == datatype) {
+        _plistAppendUTF8CString(xmlString, "<");
+        _plistAppendCharacters(xmlString, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH);
+        _plistAppendUTF8CString(xmlString, ">\n");
+        _XMLPlistAppendDataUsingBase64(xmlString, (CFDataRef)object, indentation);       
+        _appendIndents(indentation, xmlString);
+        _plistAppendUTF8CString(xmlString, "</");
+        _plistAppendCharacters(xmlString, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH);
+        _plistAppendUTF8CString(xmlString, ">\n");
+    } else if (typeID == datetype) {
+        // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
+       int32_t y = 0, M = 0, d = 0, H = 0, m = 0, s = 0;
+#if 1
+        CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime((CFDateRef)object), NULL);
+       y = date.year;
+       M = date.month;
+       d = date.day;
+       H = date.hour;
+       m = date.minute;
+       s = (int32_t)date.second;
+#else
+       CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, kCFGregorianCalendar);
+       CFTimeZoneRef tz = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, CFSTR("GMT"), true);
+       CFCalendarSetTimeZone(calendar, tz);
+       CFCalendarDecomposeAbsoluteTime(calendar, CFDateGetAbsoluteTime((CFDateRef)object), (const uint8_t *)"yMdHms", &y, &M, &d, &H, &m, &s);
+       CFRelease(calendar);
+       CFRelease(tz);
+#endif
+        _plistAppendUTF8CString(xmlString, "<");
+        _plistAppendCharacters(xmlString, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH);
+        _plistAppendUTF8CString(xmlString, ">");
+        _plistAppendFormat(xmlString, CFSTR("%04d-%02d-%02dT%02d:%02d:%02dZ"), y, M, d, H, m, s);
+        _plistAppendUTF8CString(xmlString, "</");
+        _plistAppendCharacters(xmlString, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH);
+        _plistAppendUTF8CString(xmlString, ">\n");
+    } else if (typeID == numbertype) {
+        if (CFNumberIsFloatType((CFNumberRef)object)) {
+            _plistAppendUTF8CString(xmlString, "<");
+            _plistAppendCharacters(xmlString, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH);
+            _plistAppendUTF8CString(xmlString, ">");
+                CFStringRef s = __CFNumberCopyFormattingDescriptionAsFloat64(object);
+                _plistAppendString(xmlString, s);
+                CFRelease(s);
+            _plistAppendUTF8CString(xmlString, "</");
+            _plistAppendCharacters(xmlString, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH);
+            _plistAppendUTF8CString(xmlString, ">\n");
+        } else {
+            _plistAppendUTF8CString(xmlString, "<");
+            _plistAppendCharacters(xmlString, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH);
+            _plistAppendUTF8CString(xmlString, ">");
+
+            _plistAppendFormat(xmlString, CFSTR("%@"), object);
+
+            _plistAppendUTF8CString(xmlString, "</");
+            _plistAppendCharacters(xmlString, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH);
+            _plistAppendUTF8CString(xmlString, ">\n");
+        }
+    } else if (typeID == booltype) {
+        if (CFBooleanGetValue((CFBooleanRef)object)) {
+            _plistAppendUTF8CString(xmlString, "<");
+            _plistAppendCharacters(xmlString, CFXMLPlistTags[TRUE_IX], TRUE_TAG_LENGTH);
+            _plistAppendUTF8CString(xmlString, "/>\n");
+        } else {
+            _plistAppendUTF8CString(xmlString, "<");
+            _plistAppendCharacters(xmlString, CFXMLPlistTags[FALSE_IX], FALSE_TAG_LENGTH);
+            _plistAppendUTF8CString(xmlString, "/>\n");
+        }
+    }
+}
+
+static void _CFGenerateXMLPropertyListToData(CFMutableDataRef xml, CFTypeRef propertyList) {
+    _plistAppendUTF8CString(xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE ");
+    _plistAppendCharacters(xml, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH);
+    _plistAppendUTF8CString(xml, " PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<");
+    _plistAppendCharacters(xml, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH);
+    _plistAppendUTF8CString(xml, " version=\"1.0\">\n");
+
+    _CFAppendXML0(propertyList, 0, xml);
+
+    _plistAppendUTF8CString(xml, "</");
+    _plistAppendCharacters(xml, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH);
+    _plistAppendUTF8CString(xml, ">\n");
+}
+
+CFDataRef CFPropertyListCreateXMLData(CFAllocatorRef allocator, CFPropertyListRef propertyList) {
+    initStatics();
+    CFMutableDataRef xml;
+    CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__);
+    __CFAssertIsPList(propertyList);
+       if (!CFPropertyListIsValid(propertyList, kCFPropertyListXMLFormat_v1_0)) return NULL;
+    xml = CFDataCreateMutable(allocator, 0);
+    _CFGenerateXMLPropertyListToData(xml, propertyList);
+    return xml;
+}
+
+CFDataRef _CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator, CFPropertyListRef propertyList) {
+    initStatics();
+    CFMutableDataRef xml;
+    CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__);
+    xml = CFDataCreateMutable(allocator, 0);
+    _CFGenerateXMLPropertyListToData(xml, propertyList);
+    return xml;
+}
+
+// ========================================================================
+
+//
+// ------------------------- Reading plists ------------------
+// 
+
+static void skipInlineDTD(_CFXMLPlistParseInfo *pInfo);
+static CFTypeRef parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey);
+
+// warning: doesn't have a good idea of Unicode line separators
+static UInt32 lineNumber(_CFXMLPlistParseInfo *pInfo) {
+    const UniChar *p = pInfo->begin;
+    UInt32 count = 1;
+    while (p < pInfo->curr) {
+        if (*p == '\r') {
+            count ++;
+            if (*(p + 1) == '\n')
+                p ++;
+        } else if (*p == '\n') {
+            count ++;
+        }
+        p ++;
+    }
+    return count;
+}
+
+// warning: doesn't have a good idea of Unicode white space
+CF_INLINE void skipWhitespace(_CFXMLPlistParseInfo *pInfo) {
+    while (pInfo->curr < pInfo->end) {
+        switch (*(pInfo->curr)) {
+            case ' ':
+            case '\t':
+            case '\n':
+            case '\r':
+                pInfo->curr ++;
+                continue;
+            default:
+                return;
+        }
+    }
+}
+
+/* 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. */
+
+// pInfo should be just past "<!--"
+static void skipXMLComment(_CFXMLPlistParseInfo *pInfo) {
+    const UniChar *p = pInfo->curr;
+    const UniChar *end = pInfo->end - 3; // Need at least 3 characters to compare against
+    while (p < end) {
+        if (*p == '-' && *(p+1) == '-' && *(p+2) == '>') {
+            pInfo->curr = p+3;
+            return;
+        }
+        p ++; 
+    }
+    pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unterminated comment started on line %d"), lineNumber(pInfo));
+}
+
+// stringToMatch and buf must both be of at least len
+static Boolean matchString(const UniChar *buf, const UniChar *stringToMatch, UInt32 len) {
+    switch (len) {
+       case 10: if (buf[9] != stringToMatch[9]) return false;
+       case 9: if (buf[8] != stringToMatch[8]) return false;
+       case 8: if (buf[7] != stringToMatch[7]) return false;
+       case 7: if (buf[6] != stringToMatch[6]) return false;
+       case 6: if (buf[5] != stringToMatch[5]) return false;
+       case 5: if (buf[4] != stringToMatch[4]) return false;
+       case 4: if (buf[3] != stringToMatch[3]) return false;
+       case 3: if (buf[2] != stringToMatch[2]) return false;
+       case 2: if (buf[1] != stringToMatch[1]) return false;
+       case 1: if (buf[0] != stringToMatch[0]) return false;
+       case 0: return true;
+    }
+    return false; // internal error
+}
+
+// pInfo should be set to the first character after "<?"
+static void skipXMLProcessingInstruction(_CFXMLPlistParseInfo *pInfo) {
+    const UniChar *begin = pInfo->curr, *end = pInfo->end - 2; // Looking for "?>" so we need at least 2 characters
+    while (pInfo->curr < end) {
+        if (*(pInfo->curr) == '?' && *(pInfo->curr+1) == '>') {
+            pInfo->curr += 2;
+            return;
+        }
+        pInfo->curr ++; 
+    }
+    pInfo->curr = begin;
+    pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected EOF while parsing the processing instruction begun on line %d"), lineNumber(pInfo));
+}
+
+// first character should be immediately after the "<!"
+static void skipDTD(_CFXMLPlistParseInfo *pInfo) {
+    // First pass "DOCTYPE"
+    if (pInfo->end - pInfo->curr < DOCTYPE_TAG_LENGTH || !matchString(pInfo->curr, CFXMLPlistTags[DOCTYPE_IX], DOCTYPE_TAG_LENGTH)) {
+        pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Malformed DTD on line %d"), lineNumber(pInfo));
+        return;
+    }
+    pInfo->curr += DOCTYPE_TAG_LENGTH;
+    skipWhitespace(pInfo);
+
+    // Look for either the beginning of a complex DTD or the end of the DOCTYPE structure
+    while (pInfo->curr < pInfo->end) {
+        UniChar ch = *(pInfo->curr);
+        if (ch == '[') break; // inline DTD
+        if (ch == '>') {  // End of the DTD
+            pInfo->curr ++;
+            return;
+        }
+        pInfo->curr ++;
+    }
+    if (pInfo->curr == pInfo->end) {
+        pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF while parsing DTD", CFStringGetSystemEncoding());
+        return;
+    }
+
+    // *Sigh* Must parse in-line DTD
+    skipInlineDTD(pInfo);
+    if (pInfo->errorString)  return;
+    skipWhitespace(pInfo);
+    if (pInfo->errorString) return;
+    if (pInfo->curr < pInfo->end) {
+        if (*(pInfo->curr) == '>') {
+            pInfo->curr ++;
+        } else {
+            pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c on line %d while parsing DTD"), *(pInfo->curr), lineNumber(pInfo));
+        }
+    } else {
+        pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF while parsing DTD", CFStringGetSystemEncoding());
+    }
+}
+
+static void skipPERef(_CFXMLPlistParseInfo *pInfo) {
+    const UniChar *p = pInfo->curr;
+    while (p < pInfo->end) {
+        if (*p == ';') {
+            pInfo->curr = p+1;
+            return;
+        }
+        p ++;
+    }
+    pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected EOF while parsing percent-escape sequence begun on line %d"), lineNumber(pInfo));
+}
+
+// First character should be just past '['
+static void skipInlineDTD(_CFXMLPlistParseInfo *pInfo) {
+    while (!pInfo->errorString && pInfo->curr < pInfo->end) {
+        UniChar ch;
+        skipWhitespace(pInfo);
+        ch = *pInfo->curr;
+        if (ch == '%') {
+            pInfo->curr ++;
+            skipPERef(pInfo);
+        } else if (ch == '<') {
+            pInfo->curr ++;
+            if (pInfo->curr >= pInfo->end) {
+                pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF while parsing inline DTD", CFStringGetSystemEncoding());
+                return;
+            }
+            ch = *(pInfo->curr);
+            if (ch == '?') {
+                pInfo->curr ++;
+                skipXMLProcessingInstruction(pInfo);
+            } else if (ch == '!') {
+                if (pInfo->curr + 2 < pInfo->end && (*(pInfo->curr+1) == '-' && *(pInfo->curr+2) == '-')) {
+                    pInfo->curr += 3;
+                    skipXMLComment(pInfo);
+                } else {
+                    // Skip the myriad of DTD declarations of the form "<!string" ... ">"
+                    pInfo->curr ++; // Past both '<' and '!'
+                    while (pInfo->curr < pInfo->end) {
+                        if (*(pInfo->curr) == '>') break;
+                        pInfo->curr ++;
+                    }
+                    if (*(pInfo->curr) != '>') {
+                        pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF while parsing inline DTD", CFStringGetSystemEncoding());
+                        return;
+                 
+   }
+                    pInfo->curr ++;
+                }
+            } else {
+                pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch, lineNumber(pInfo));
+                return;
+            }
+        } else if (ch == ']') {
+            pInfo->curr ++;
+            return;
+        } else {
+            pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch, lineNumber(pInfo));
+            return;
+        }
+    }
+    if (!pInfo->errorString)
+        pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF while parsing inline DTD", CFStringGetSystemEncoding());
+}
+
+/* A bit wasteful to do everything with unichars (since we know all the characters we're going to see are 7-bit ASCII), but since our data is coming from or going to a CFString, this prevents the extra cost of converting formats. */
+
+static const signed char __CFPLDataDecodeTable[128] = {
+    /* 000 */ -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 010 */ -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 020 */ -1, -1, -1, -1, -1, -1, -1, -1,
+    /* 030 */ -1, -1, -1, -1, -1, -1, -1, -1,
+    /* ' ' */ -1, -1, -1, -1, -1, -1, -1, -1,
+    /* '(' */ -1, -1, -1, 62, -1, -1, -1, 63,
+    /* '0' */ 52, 53, 54, 55, 56, 57, 58, 59,
+    /* '8' */ 60, 61, -1, -1, -1,  0, -1, -1,
+    /* '@' */ -1,  0,  1,  2,  3,  4,  5,  6,
+    /* 'H' */  7,  8,  9, 10, 11, 12, 13, 14,
+    /* 'P' */ 15, 16, 17, 18, 19, 20, 21, 22,
+    /* 'X' */ 23, 24, 25, -1, -1, -1, -1, -1,
+    /* '`' */ -1, 26, 27, 28, 29, 30, 31, 32,
+    /* 'h' */ 33, 34, 35, 36, 37, 38, 39, 40,
+    /* 'p' */ 41, 42, 43, 44, 45, 46, 47, 48,
+    /* 'x' */ 49, 50, 51, -1, -1, -1, -1, -1
+};
+
+static CFDataRef __CFPLDataDecode(_CFXMLPlistParseInfo *pInfo, Boolean isMutable) {
+    int tmpbufpos = 0;
+    int tmpbuflen = 256;
+    uint8_t *tmpbuf;
+    int numeq = 0;
+    int acc = 0;
+    int cntr = 0;
+
+    tmpbuf = (uint8_t *)CFAllocatorAllocate(pInfo->allocator, tmpbuflen, 0);
+    for (; pInfo->curr < pInfo->end; pInfo->curr++) {
+        UniChar c = *(pInfo->curr);
+        if (c == '<') {
+            break;
+       }
+        if ('=' == c) {
+            numeq++;
+        } else if (!isspace(c)) {
+            numeq = 0;
+        }
+        if (__CFPLDataDecodeTable[c] < 0)
+            continue;
+        cntr++;
+        acc <<= 6;
+        acc += __CFPLDataDecodeTable[c];
+        if (0 == (cntr & 0x3)) {
+            if (tmpbuflen <= tmpbufpos + 2) {
+               if (tmpbuflen < 256 * 1024) {
+                   tmpbuflen *= 4;
+               } else if (tmpbuflen < 16 * 1024 * 1024) {
+                   tmpbuflen *= 2;
+               } else {
+                   // once in this stage, this will be really slow
+                   // and really potentially fragment memory
+                   tmpbuflen += 256 * 1024;
+               }
+                tmpbuf = (uint8_t *)CFAllocatorReallocate(pInfo->allocator, tmpbuf, tmpbuflen, 0);
+               if (!tmpbuf) HALT;
+            }
+            tmpbuf[tmpbufpos++] = (acc >> 16) & 0xff;
+            if (numeq < 2)
+                tmpbuf[tmpbufpos++] = (acc >> 8) & 0xff;
+            if (numeq < 1)
+                tmpbuf[tmpbufpos++] = acc & 0xff;
+        }
+    }
+    if (isMutable) {
+        CFMutableDataRef result = CFDataCreateMutable(pInfo->allocator, 0);
+        CFDataAppendBytes(result, tmpbuf, tmpbufpos);
+       CFAllocatorDeallocate(pInfo->allocator, tmpbuf);
+        return result;
+    } else {
+        return CFDataCreateWithBytesNoCopy(pInfo->allocator, tmpbuf, tmpbufpos, pInfo->allocator);
+    }
+}
+
+// content ::== (element | CharData | Reference | CDSect | PI | Comment)*
+// 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).
+static CFTypeRef getContentObject(_CFXMLPlistParseInfo *pInfo, Boolean *isKey) {
+    if (isKey) *isKey = false;
+    while (!pInfo->errorString && pInfo->curr < pInfo->end) {
+        skipWhitespace(pInfo);
+        if (pInfo->curr >= pInfo->end) {
+            pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding());
+            return NULL;
+        }
+        if (*(pInfo->curr) != '<') {
+            pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo->curr), lineNumber(pInfo));
+            return NULL;
+        }
+        pInfo->curr ++;
+        if (pInfo->curr >= pInfo->end) {
+            pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding());
+            return NULL;
+        }
+        switch (*(pInfo->curr)) {
+            case '?':
+                // Processing instruction
+                skipXMLProcessingInstruction(pInfo);
+                break;
+            case '!':
+                // Could be a comment
+                if (pInfo->curr+2 >= pInfo->end) {
+                    pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding());
+                    return NULL;
+                }
+                if (*(pInfo->curr+1) == '-' && *(pInfo->curr+2) == '-') {
+                    pInfo->curr += 2;
+                    skipXMLComment(pInfo);
+                } else {
+                    pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding());
+                    return NULL;
+                }
+                break;
+            case '/':
+                // Whoops!  Looks like we got to the end tag for the element whose content we're parsing
+                pInfo->curr --; // Back off to the '<'
+                return NULL;
+            default:
+                // Should be an element
+                return parseXMLElement(pInfo, isKey);
+        }
+    }
+    // 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"
+    return NULL;
+}
+
+static void _catFromMarkToBuf(const UniChar *mark, const UniChar *buf, CFMutableStringRef *string, CFAllocatorRef allocator ) {
+    if (!(*string)) {
+        *string = CFStringCreateMutable(allocator, 0);
+    }
+    CFStringAppendCharacters(*string, mark, buf-mark);
+}
+
+static void parseCDSect_pl(_CFXMLPlistParseInfo *pInfo, CFMutableStringRef string) {
+    const UniChar *end, *begin;
+    if (pInfo->end - pInfo->curr < CDSECT_TAG_LENGTH) {
+        pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding());
+        return;
+    }
+    if (!matchString(pInfo->curr, CFXMLPlistTags[CDSECT_IX], CDSECT_TAG_LENGTH)) {
+        pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered improper CDATA opening at line %d"), lineNumber(pInfo));
+        return;
+    }
+    pInfo->curr += CDSECT_TAG_LENGTH;
+    begin = pInfo->curr; // Marks the first character of the CDATA content
+    end = pInfo->end-2; // So we can safely look 2 characters beyond p
+    while (pInfo->curr < end) {
+        if (*(pInfo->curr) == ']' && *(pInfo->curr+1) == ']' && *(pInfo->curr+2) == '>') {
+           // Found the end!
+            CFStringAppendCharacters(string, begin, pInfo->curr-begin);
+            pInfo->curr += 3;
+            return;
+        }
+        pInfo->curr ++;
+    }
+    // Never found the end mark
+    pInfo->curr = begin;
+    pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Could not find end of CDATA started on line %d"), lineNumber(pInfo));
+}
+
+// Only legal references are {lt, gt, amp, apos, quote, #ddd, #xAAA}
+static void parseEntityReference_pl(_CFXMLPlistParseInfo *pInfo, CFMutableStringRef string) {
+    int len;
+    UniChar ch;
+    pInfo->curr ++; // move past the '&';
+    len = pInfo->end - pInfo->curr; // how many characters we can safely scan
+    if (len < 1) {
+        pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding());
+        return;
+    }
+    switch (*(pInfo->curr)) {
+        case 'l':  // "lt"
+            if (len >= 3 && *(pInfo->curr+1) == 't' && *(pInfo->curr+2) == ';') {
+                ch = '<';
+                pInfo->curr += 3;
+                break;
+            }
+            pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo));
+            return;
+        case 'g': // "gt"
+            if (len >= 3 && *(pInfo->curr+1) == 't' && *(pInfo->curr+2) == ';') {
+                ch = '>';
+                pInfo->curr += 3;
+                break;
+            }
+            pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo));
+            return;
+        case 'a': // "apos" or "amp"
+            if (len < 4) {   // Not enough characters for either conversion
+                pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding());
+                return;
+            }
+            if (*(pInfo->curr+1) == 'm') {
+                // "amp"
+                if (*(pInfo->curr+2) == 'p' && *(pInfo->curr+3) == ';') {
+                    ch = '&';
+                    pInfo->curr += 4;
+                    break;
+                }
+            } else if (*(pInfo->curr+1) == 'p') {
+                // "apos"
+                if (len > 4 && *(pInfo->curr+2) == 'o' && *(pInfo->curr+3) == 's' && *(pInfo->curr+4) == ';') {
+                    ch = '\'';
+                    pInfo->curr += 5;
+                    break;
+                }
+            }
+            pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo));
+            return;
+        case 'q':  // "quote"
+            if (len >= 5 && *(pInfo->curr+1) == 'u' && *(pInfo->curr+2) == 'o' && *(pInfo->curr+3) == 't' && *(pInfo->curr+4) == ';') {
+                ch = '\"';
+                pInfo->curr += 5;
+                break;
+            }
+            pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo));
+            return;
+        case '#':
+        {
+            uint16_t num = 0;
+            Boolean isHex = false;
+            if ( len < 4) {  // Not enough characters to make it all fit!  Need at least "&#d;"
+                pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding());
+                return;
+            }
+            pInfo->curr ++;
+            if (*(pInfo->curr) == 'x') {
+                isHex = true;
+                pInfo->curr ++;
+            }
+            while (pInfo->curr < pInfo->end) {
+                ch = *(pInfo->curr);
+                pInfo->curr ++;
+                if (ch == ';') {
+                    CFStringAppendCharacters(string, &num, 1);
+                    return;
+                }
+                if (!isHex) num = num*10;
+                else num = num << 4;
+                if (ch <= '9' && ch >= '0') {
+                    num += (ch - '0');
+                } else if (!isHex) {
+                    pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c at line %d"), ch, lineNumber(pInfo));
+                    return;
+                } else if (ch >= 'a' && ch <= 'f') {
+                    num += 10 + (ch - 'a');
+                } else if (ch >= 'A' && ch <= 'F') {
+                    num += 10 + (ch - 'A');
+                } else {
+                    pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c at line %d"), ch, lineNumber(pInfo));
+                    return;                    
+                }
+            }
+            pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding());
+            return;
+        }
+        default:
+            pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo));
+            return;
+    }
+    CFStringAppendCharacters(string, &ch, 1);
+}
+
+static CFStringRef _uniqueStringForString(_CFXMLPlistParseInfo *pInfo, CFStringRef stringToUnique) {
+    if (!pInfo->stringSet) {
+        pInfo->stringSet = CFSetCreateMutable(pInfo->allocator, 0, &kCFCopyStringSetCallBacks);
+       _CFSetSetCapacity(pInfo->stringSet, 160);       // set capacity high to avoid lots of rehashes, though waste some memory
+    }
+    CFSetAddValue(pInfo->stringSet, stringToUnique);
+    return (CFStringRef)CFSetGetValue(pInfo->stringSet, stringToUnique);
+}
+
+extern void _CFStrSetDesiredCapacity(CFMutableStringRef str, CFIndex len);
+
+static CFStringRef _uniqueStringForCharacters(_CFXMLPlistParseInfo *pInfo, const UniChar *base, CFIndex length) {
+    CFIndex idx;
+    uint8_t *ascii, buffer[1024];
+    bool isASCII;
+    if (!pInfo->stringSet) {
+        pInfo->stringSet = CFSetCreateMutable(pInfo->allocator, 0, &kCFCopyStringSetCallBacks);
+       _CFSetSetCapacity(pInfo->stringSet, 160);       // set capacity high to avoid lots of rehashes, though waste some memory
+    }
+    if (pInfo->tmpString) {
+       CFStringDelete(pInfo->tmpString, CFRangeMake(0, CFStringGetLength(pInfo->tmpString)));
+    } else {
+        pInfo->tmpString = CFStringCreateMutable(pInfo->allocator, 0);
+       _CFStrSetDesiredCapacity(pInfo->tmpString, 512);
+    }
+    // This is to avoid having to promote the buffers of all the strings compared against
+    // during the set probe; if a Unicode string is passed in, that's what happens.
+    isASCII = true;
+    for (idx = 0; isASCII && idx < length; idx++) isASCII = isASCII && (base[idx] < 0x80);
+    if (isASCII) {
+       ascii = (length < (CFIndex)sizeof(buffer)) ? buffer : (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, length + 1, 0);
+       for (idx = 0; idx < length; idx++) ascii[idx] = (uint8_t)base[idx];
+       ascii[length] = '\0';
+       CFStringAppendCString(pInfo->tmpString, (char *)ascii, kCFStringEncodingASCII);
+       if (ascii != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ascii);
+    } else {
+       CFStringAppendCharacters(pInfo->tmpString, base, length);
+    }
+    CFSetAddValue(pInfo->stringSet, pInfo->tmpString);
+    return (CFStringRef)CFSetGetValue(pInfo->stringSet, pInfo->tmpString);
+}
+
+
+// String could be comprised of characters, CDSects, or references to one of the "well-known" entities ('<', '>', '&', ''', '"')
+// returns a retained object in *string.
+static CFStringRef getString(_CFXMLPlistParseInfo *pInfo) {
+    const UniChar *mark = pInfo->curr; // At any time in the while loop below, the characters between mark and p have not yet been added to *string
+    CFMutableStringRef string = NULL;
+    while (!pInfo->errorString && pInfo->curr < pInfo->end) {
+        UniChar ch = *(pInfo->curr);
+        if (ch == '<') {
+           if (pInfo->curr + 1 >= pInfo->end) break;
+            // Could be a CDSect; could be the end of the string
+            if (*(pInfo->curr+1) != '!') break; // End of the string
+            _catFromMarkToBuf(mark, pInfo->curr, &string, pInfo->allocator);
+            parseCDSect_pl(pInfo, string);
+            mark = pInfo->curr;
+        } else if (ch == '&') {
+            _catFromMarkToBuf(mark, pInfo->curr, &string, pInfo->allocator);
+            parseEntityReference_pl(pInfo, string);
+            mark = pInfo->curr;
+        } else {
+            pInfo->curr ++;
+        }
+    }
+
+    if (pInfo->errorString) {
+        if (string) CFRelease(string);
+        return NULL;
+    }
+    if (!string) {
+        if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) {
+            CFStringRef uniqueString = _uniqueStringForCharacters(pInfo, mark, pInfo->curr-mark);
+            if (uniqueString) CFRetain(uniqueString);
+            return uniqueString;
+        } else {
+            string = CFStringCreateMutable(pInfo->allocator, 0);
+            CFStringAppendCharacters(string, mark, pInfo->curr - mark);
+            return string;
+        }
+    }
+    _catFromMarkToBuf(mark, pInfo->curr, &string, pInfo->allocator);
+    if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) {
+        CFStringRef uniqueString = _uniqueStringForString(pInfo, string);
+        if (uniqueString) CFRetain(uniqueString);
+        CFRelease(string);
+        return uniqueString;
+    }
+    return string;
+}
+
+static Boolean checkForCloseTag(_CFXMLPlistParseInfo *pInfo, const UniChar *tag, CFIndex tagLen) {
+    if (pInfo->end - pInfo->curr < tagLen + 3) {
+        pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding());
+        return false;
+    }
+    if (*(pInfo->curr) != '<' || *(++pInfo->curr) != '/') {
+        pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo->curr), lineNumber(pInfo));
+        return false;
+    }
+    pInfo->curr ++;
+    if (!matchString(pInfo->curr, tag, tagLen)) {
+        CFStringRef str = CFStringCreateWithCharactersNoCopy(pInfo->allocator, tag, tagLen, kCFAllocatorNull);
+        pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Close tag on line %d does not match open tag %@"), lineNumber(pInfo), str);
+        CFRelease(str);
+        return false;
+    }
+    pInfo->curr += tagLen;
+    skipWhitespace(pInfo);
+    if (pInfo->curr == pInfo->end) {
+        pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding());
+        return false;
+    }
+    if (*(pInfo->curr) != '>') {
+        pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo->curr), lineNumber(pInfo));
+        return false;
+    }
+    pInfo->curr ++;
+    return true;
+}
+
+// pInfo should be set to the first content character of the <plist>
+static CFTypeRef parsePListTag(_CFXMLPlistParseInfo *pInfo) {
+    CFTypeRef result, tmp = NULL;
+    const UniChar *save;
+    result = getContentObject(pInfo, NULL);
+    if (!result) {
+        if (!pInfo->errorString) pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered empty plist tag", CFStringGetSystemEncoding());
+        return NULL;
+    }
+    save = pInfo->curr; // Save this in case the next step fails
+    tmp = getContentObject(pInfo, NULL);
+    if (tmp) {
+        // Got an extra object
+        CFRelease(tmp);
+        CFRelease(result);
+        pInfo->curr = save;
+        pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unexpected element at line %d (plist can only include one object)"), lineNumber(pInfo));
+        return NULL;
+    }
+    if (pInfo->errorString) {
+        // Parse failed catastrophically
+        CFRelease(result);
+        return NULL;
+    }
+    if (checkForCloseTag(pInfo, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH)) {
+        return result;
+    }
+    CFRelease(result);
+    return NULL;
+}
+
+static int allowImmutableCollections = -1;
+
+static void checkImmutableCollections(void) {
+    allowImmutableCollections = (NULL == getenv("CFPropertyListAllowImmutableCollections")) ? 0 : 1;
+}
+
+static CFTypeRef parseArrayTag(_CFXMLPlistParseInfo *pInfo) {
+    CFMutableArrayRef array = CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks);
+    CFTypeRef tmp = getContentObject(pInfo, NULL);
+    while (tmp) {
+        CFArrayAppendValue(array, tmp);
+        CFRelease(tmp);
+        tmp = getContentObject(pInfo, NULL);
+    }
+    if (pInfo->errorString) { // getContentObject encountered a parse error
+        CFRelease(array);
+        return NULL;
+    }
+    if (checkForCloseTag(pInfo, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH)) {
+       if (-1 == allowImmutableCollections) checkImmutableCollections();
+       if (1 == allowImmutableCollections) {
+           if (pInfo->mutabilityOption == kCFPropertyListImmutable) {
+               CFArrayRef newArray = CFArrayCreateCopy(pInfo->allocator, array);
+               CFRelease(array);
+               array = (CFMutableArrayRef)newArray;
+           }
+       }
+       return array;
+    }
+    CFRelease(array);
+    return NULL;
+}
+
+static CFTypeRef parseDictTag(_CFXMLPlistParseInfo *pInfo) {
+    CFMutableDictionaryRef dict = NULL;
+    CFTypeRef key=NULL, value=NULL;
+    Boolean gotKey;
+    const UniChar *base = pInfo->curr;
+    key = getContentObject(pInfo, &gotKey);
+    while (key) {
+        if (!gotKey) {
+            if (key) CFRelease(key);
+            if (dict) CFRelease(dict);
+            pInfo->curr = base;
+            pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo));
+            return NULL;
+        }
+        value = getContentObject(pInfo, NULL);
+        if (!value) {
+            if (key) CFRelease(key);
+            if (dict) CFRelease(dict);
+            if (!pInfo->errorString)
+                pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo));
+            return NULL;
+        }
+       if (NULL == dict) {
+           dict = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+           _CFDictionarySetCapacity(dict, 10);
+       }
+        CFDictionarySetValue(dict, key, value);
+        CFRelease(key);
+        key = NULL;
+        CFRelease(value);
+        value = NULL;
+        base = pInfo->curr;
+        key = getContentObject(pInfo, &gotKey);
+    }
+    if (checkForCloseTag(pInfo, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH)) {
+       if (NULL == dict) {
+           if (pInfo->mutabilityOption == kCFPropertyListImmutable) {
+               dict = (CFMutableDictionaryRef)CFDictionaryCreate(pInfo->allocator, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+           } else {
+               dict = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+           }
+       } else {
+           CFIndex cnt = CFDictionaryGetCount(dict);
+#if DEPLOYMENT_TARGET_MACOSX
+           if (1 == cnt) {
+               CFTypeRef val = CFDictionaryGetValue(dict, CFSTR("CF$UID"));
+               if (val && CFGetTypeID(val) == numbertype) {
+                   CFTypeRef uid;
+                   uint32_t v;
+                   CFNumberGetValue((CFNumberRef)val, kCFNumberSInt32Type, &v);
+                   uid = (CFTypeRef)_CFKeyedArchiverUIDCreate(pInfo->allocator, v);
+                   CFRelease(dict);
+                   return uid;
+               }
+           }
+#endif
+           if (-1 == allowImmutableCollections) checkImmutableCollections();
+           if (1 == allowImmutableCollections) {
+               if (pInfo->mutabilityOption == kCFPropertyListImmutable) {
+                   CFDictionaryRef newDict = CFDictionaryCreateCopy(pInfo->allocator, dict);
+                   CFRelease(dict);
+                   dict = (CFMutableDictionaryRef)newDict;
+               }
+           }
+       }
+        return dict;
+    }
+    if (dict) CFRelease(dict);
+    return NULL;
+}
+
+static CFTypeRef parseDataTag(_CFXMLPlistParseInfo *pInfo) {
+    CFDataRef result;
+    const UniChar *base = pInfo->curr;
+    result = __CFPLDataDecode(pInfo, pInfo->mutabilityOption == kCFPropertyListMutableContainersAndLeaves);
+    if (!result) {
+        pInfo->curr = base;
+        pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Could not interpret <data> at line %d (should be base64-encoded)"), lineNumber(pInfo));
+        return NULL;
+    }
+    if (checkForCloseTag(pInfo, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH)) return result;
+    CFRelease(result);
+    return NULL;
+}
+
+CF_INLINE Boolean read2DigitNumber(_CFXMLPlistParseInfo *pInfo, int32_t *result) {
+    UniChar ch1, ch2;
+    if (pInfo->curr + 2 >= pInfo->end) return false;
+    ch1 = *pInfo->curr;
+    ch2 = *(pInfo->curr + 1);
+    pInfo->curr += 2;
+    if (!isdigit(ch1) || !isdigit(ch2)) return false;
+    *result = (ch1 - '0')*10 + (ch2 - '0');
+    return true;
+}
+
+// YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
+static CFTypeRef parseDateTag(_CFXMLPlistParseInfo *pInfo) {
+    int32_t year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
+    int32_t num = 0;
+    Boolean badForm = false;
+
+    while (pInfo->curr < pInfo->end && isdigit(*pInfo->curr)) {
+        year = 10*year + (*pInfo->curr) - '0';
+        pInfo->curr ++;
+    }
+    if (pInfo->curr >= pInfo->end || *pInfo->curr != '-') {
+        badForm = true;
+    } else {
+        pInfo->curr ++;
+    }
+
+    if (!badForm && read2DigitNumber(pInfo, &month) && pInfo->curr < pInfo->end && *pInfo->curr == '-') {
+        pInfo->curr ++;
+    } else {
+        badForm = true;
+    }
+
+    if (!badForm && read2DigitNumber(pInfo, &day) && pInfo->curr < pInfo->end && *pInfo->curr == 'T') {
+        pInfo->curr ++;
+    } else {
+        badForm = true;
+    }
+
+    if (!badForm && read2DigitNumber(pInfo, &hour) && pInfo->curr < pInfo->end && *pInfo->curr == ':') {
+        pInfo->curr ++;
+    } else {
+        badForm = true;
+    }
+
+    if (!badForm && read2DigitNumber(pInfo, &minute) && pInfo->curr < pInfo->end && *pInfo->curr == ':') {
+        pInfo->curr ++;
+    } else {
+        badForm = true;
+    }
+
+    if (!badForm && read2DigitNumber(pInfo, &num) && pInfo->curr < pInfo->end && *pInfo->curr == 'Z') {
+        second = num;
+        pInfo->curr ++;
+    } else {
+        badForm = true;
+    }
+
+    if (badForm) {
+        pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Could not interpret <date> at line %d"), lineNumber(pInfo));
+        return NULL;
+    }
+    if (!checkForCloseTag(pInfo, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH)) return NULL;
+
+    CFAbsoluteTime at = 0.0;
+#if 1
+    CFGregorianDate date = {year, month, day, hour, minute, second};
+    at = CFGregorianDateGetAbsoluteTime(date, NULL);
+#else
+    CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, kCFGregorianCalendar);
+    CFTimeZoneRef tz = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, CFSTR("GMT"), true);
+    CFCalendarSetTimeZone(calendar, tz);
+    CFCalendarComposeAbsoluteTime(calendar, &at, (const uint8_t *)"yMdHms", year, month, day, hour, minute, second);
+    CFRelease(calendar);
+    CFRelease(tz);
+#endif
+    return CFDateCreate(pInfo->allocator, at);
+}
+
+static CFTypeRef parseRealTag(_CFXMLPlistParseInfo *pInfo) {
+    CFStringRef str = getString(pInfo);
+    SInt32 idx, len;
+    double val;
+    CFNumberRef result;
+    CFStringInlineBuffer buf;
+    if (!str) {
+        if (!pInfo->errorString)
+            pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo));
+        return NULL;
+    }
+    
+       if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("nan"), kCFCompareCaseInsensitive)) {
+           CFRelease(str);
+           return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberNaN) : NULL;
+       }
+       if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("+infinity"), kCFCompareCaseInsensitive)) {
+           CFRelease(str);
+           return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberPositiveInfinity) : NULL;
+       }
+       if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("-infinity"), kCFCompareCaseInsensitive)) {
+           CFRelease(str);
+           return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberNegativeInfinity) : NULL;
+       }
+       if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("infinity"), kCFCompareCaseInsensitive)) {
+           CFRelease(str);
+           return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberPositiveInfinity) : NULL;
+       }
+       if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("-inf"), kCFCompareCaseInsensitive)) {
+           CFRelease(str);
+           return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberNegativeInfinity) : NULL;
+       }
+       if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("inf"), kCFCompareCaseInsensitive)) {
+           CFRelease(str);
+           return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberPositiveInfinity) : NULL;
+       }
+       if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("+inf"), kCFCompareCaseInsensitive)) {
+           CFRelease(str);
+           return (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) ? CFRetain(kCFNumberPositiveInfinity) : NULL;
+       }
+
+    len = CFStringGetLength(str);
+    CFStringInitInlineBuffer(str, &buf, CFRangeMake(0, len));
+    idx = 0;
+    if (!__CFStringScanDouble(&buf, NULL, &idx, &val) || idx != len) {
+        CFRelease(str);
+        pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered misformatted real on line %d"), lineNumber(pInfo));
+        return NULL;
+    }
+    CFRelease(str);
+    result = CFNumberCreate(pInfo->allocator, kCFNumberDoubleType, &val);
+    if (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) return result;
+    CFRelease(result);
+    return NULL;
+}
+
+#define GET_CH if (pInfo->curr == pInfo->end) {        \
+                       pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Premature end of file after <integer> on line %d"), lineNumber(pInfo)); \
+                       return NULL;                    \
+               }                                       \
+               ch = *(pInfo->curr)
+
+typedef struct {
+    int64_t high;
+    uint64_t low;
+} CFSInt128Struct;
+
+enum {
+    kCFNumberSInt128Type = 17
+};
+
+static CFTypeRef parseIntegerTag(_CFXMLPlistParseInfo *pInfo) {
+    bool isHex = false, isNeg = false, hadLeadingZero = false;
+    UniChar ch = 0;
+
+       // decimal_constant     S*(-|+)?S*[0-9]+                (S == space)
+       // hex_constant         S*(-|+)?S*0[xX][0-9a-fA-F]+     (S == space)
+
+    while (pInfo->curr < pInfo->end && __CFIsWhitespace(*(pInfo->curr))) pInfo->curr++;
+    GET_CH;
+    if ('<' == ch) {
+       pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo));
+       return NULL;
+    }
+    if ('-' == ch || '+' == ch) {
+       isNeg = ('-' == ch);
+       pInfo->curr++;
+       while (pInfo->curr < pInfo->end && __CFIsWhitespace(*(pInfo->curr))) pInfo->curr++;
+    }
+    GET_CH;
+    if ('0' == ch) {
+       if (pInfo->curr + 1 < pInfo->end && ('x' == *(pInfo->curr + 1) || 'X' == *(pInfo->curr + 1))) {
+           pInfo->curr++;
+           isHex = true;
+       } else {
+           hadLeadingZero = true;
+       }
+       pInfo->curr++;
+    }
+    GET_CH;
+    while ('0' == ch) {
+       hadLeadingZero = true;
+       pInfo->curr++;
+       GET_CH;
+    }
+    if ('<' == ch && hadLeadingZero) { // nothing but zeros
+       int32_t val = 0;
+        if (!checkForCloseTag(pInfo, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH)) {
+           // checkForCloseTag() sets error string
+           return NULL;
+        }
+       return CFNumberCreate(pInfo->allocator, kCFNumberSInt32Type, &val);
+    }
+    if ('<' == ch) {
+       pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Incomplete <integer> on line %d"), lineNumber(pInfo));
+       return NULL;
+    }
+    uint64_t value = 0;
+    uint32_t multiplier = (isHex ? 16 : 10);
+    while ('<' != ch) {
+       uint32_t new_digit = 0;
+       switch (ch) {
+       case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+           new_digit = (ch - '0');
+           break;
+       case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+           new_digit = (ch - 'a' + 10);
+           break;
+       case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+           new_digit = (ch - 'A' + 10);
+           break;
+       default:        // other character
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unknown character '%c' (0x%x) in <integer> on line %d"), ch, ch, lineNumber(pInfo));
+           return NULL;
+       }
+       if (!isHex && new_digit > 9) {
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Hex digit in non-hex <integer> on line %d"), lineNumber(pInfo));
+           return NULL;
+       }
+       if (UINT64_MAX / multiplier < value) {
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo));
+           return NULL;
+       }
+       value = multiplier * value;
+       if (UINT64_MAX - new_digit < value) {
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo));
+           return NULL;
+       }
+       value = value + new_digit;
+       if (isNeg && (uint64_t)INT64_MAX + 1 < value) {
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Integer underflow in <integer> on line %d"), lineNumber(pInfo));
+           return NULL;
+       }
+       pInfo->curr++;
+       GET_CH;
+    }
+    if (!checkForCloseTag(pInfo, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH)) {
+       // checkForCloseTag() sets error string
+       return NULL;
+    }
+    if (isNeg || value <= INT64_MAX) {
+       int64_t v = value;
+       if (isNeg) v = -v;      // no-op if INT64_MIN
+        return CFNumberCreate(pInfo->allocator, kCFNumberSInt64Type, &v);
+    }
+    CFSInt128Struct val;
+    val.high = 0;
+    val.low = value;
+    return CFNumberCreate(pInfo->allocator, kCFNumberSInt128Type, &val);
+}
+
+#undef GET_CH
+
+// Returned object is retained; caller must free.  pInfo->curr expected to point to the first character after the '<'
+static CFTypeRef parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey) {
+    const UniChar *marker = pInfo->curr;
+    int markerLength = -1;
+    Boolean isEmpty;
+    int markerIx = -1;
+    
+    if (isKey) *isKey = false;
+    while (pInfo->curr < pInfo->end) {
+        UniChar ch = *(pInfo->curr);
+        if (ch == ' ' || ch ==  '\t' || ch == '\n' || ch =='\r') {
+            if (markerLength == -1) markerLength = pInfo->curr - marker;
+        } else if (ch == '>') {
+            break;
+        }
+        pInfo->curr ++;
+    }
+    if (pInfo->curr >= pInfo->end) return NULL;
+    isEmpty = (*(pInfo->curr-1) == '/');
+    if (markerLength == -1)
+        markerLength = pInfo->curr - (isEmpty ? 1 : 0) - marker;
+    pInfo->curr ++; // Advance past '>'
+    if (markerLength == 0) {
+        // Back up to the beginning of the marker
+        pInfo->curr = marker;
+        pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Malformed tag on line %d"), lineNumber(pInfo));
+        return NULL;
+    }
+    switch (*marker) {
+        case 'a':   // Array
+            if (markerLength == ARRAY_TAG_LENGTH && matchString(marker, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH))
+                markerIx = ARRAY_IX;
+            break;
+        case 'd': // Dictionary, data, or date; Fortunately, they all have the same marker length....
+            if (markerLength != DICT_TAG_LENGTH)
+                break;
+            if (matchString(marker, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH))
+                markerIx = DICT_IX;
+            else if (matchString(marker, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH))
+                markerIx = DATA_IX;
+            else if (matchString(marker, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH))
+                markerIx = DATE_IX;
+            break;
+        case 'f': // false (boolean)
+            if (markerLength == FALSE_TAG_LENGTH && matchString(marker, CFXMLPlistTags[FALSE_IX], FALSE_TAG_LENGTH)) {
+                markerIx = FALSE_IX;
+            }
+            break;
+        case 'i': // integer
+            if (markerLength == INTEGER_TAG_LENGTH && matchString(marker, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH))
+                markerIx = INTEGER_IX;
+            break;
+        case 'k': // Key of a dictionary
+            if (markerLength == KEY_TAG_LENGTH && matchString(marker, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH)) {
+                markerIx = KEY_IX;
+                if (isKey) *isKey = true;
+            }
+            break;
+        case 'p': // Plist
+            if (markerLength == PLIST_TAG_LENGTH && matchString(marker, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH))
+                markerIx = PLIST_IX;
+            break;
+        case 'r': // real
+            if (markerLength == REAL_TAG_LENGTH && matchString(marker, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH))
+                markerIx = REAL_IX;
+            break;
+        case 's': // String
+            if (markerLength == STRING_TAG_LENGTH && matchString(marker, CFXMLPlistTags[STRING_IX], STRING_TAG_LENGTH))
+                markerIx = STRING_IX;
+            break;
+        case 't': // true (boolean)
+            if (markerLength == TRUE_TAG_LENGTH && matchString(marker, CFXMLPlistTags[TRUE_IX], TRUE_TAG_LENGTH))
+                markerIx = TRUE_IX;
+            break;
+    }
+
+    if (!pInfo->allowNewTypes && markerIx != PLIST_IX && markerIx != ARRAY_IX && markerIx != DICT_IX && markerIx != STRING_IX && markerIx != KEY_IX && markerIx != DATA_IX) {
+        pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered new tag when expecting only old-style property list objects", CFStringGetSystemEncoding());
+        return NULL;
+    }
+
+    switch (markerIx) {
+        case PLIST_IX:
+            if (isEmpty) {
+                pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered empty plist tag", CFStringGetSystemEncoding());
+                return NULL;
+            }
+            return parsePListTag(pInfo);
+        case ARRAY_IX: 
+            if (isEmpty) {
+                return pInfo->mutabilityOption == kCFPropertyListImmutable ?  CFArrayCreate(pInfo->allocator, NULL, 0, &kCFTypeArrayCallBacks) : CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks);
+            } else {
+                return parseArrayTag(pInfo);
+            }
+        case DICT_IX:
+            if (isEmpty) {
+                if (pInfo->mutabilityOption == kCFPropertyListImmutable) {
+                    return CFDictionaryCreate(pInfo->allocator, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+                } else {
+                    return CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+                }
+            } else {
+                return parseDictTag(pInfo);
+            }
+        case KEY_IX:
+        case STRING_IX:
+        {
+            CFStringRef str;
+            int tagLen = (markerIx == KEY_IX) ? KEY_TAG_LENGTH : STRING_TAG_LENGTH;
+            if (isEmpty) {
+                return pInfo->mutabilityOption == kCFPropertyListMutableContainersAndLeaves ? CFStringCreateMutable(pInfo->allocator, 0) : CFStringCreateWithCharacters(pInfo->allocator, NULL, 0);
+            }
+            str = getString(pInfo);
+            if (!str) return NULL; // getString will already have set the error string
+            if (!checkForCloseTag(pInfo, CFXMLPlistTags[markerIx], tagLen)) {
+                CFRelease(str);
+                return NULL;
+            }
+            return str;
+        }
+        case DATA_IX:
+            if (isEmpty) {
+                pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered empty <data> on line %d"), lineNumber(pInfo));
+                return NULL;
+            } else {
+                return parseDataTag(pInfo);
+            }
+        case DATE_IX:
+            if (isEmpty) {
+                pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered empty <date> on line %d"), lineNumber(pInfo));
+                return NULL;
+            } else {
+                return parseDateTag(pInfo);
+            }
+        case TRUE_IX:
+            if (!isEmpty) {
+               if (!checkForCloseTag(pInfo, CFXMLPlistTags[TRUE_IX], TRUE_TAG_LENGTH)) return NULL;
+           }
+           return CFRetain(kCFBooleanTrue);
+        case FALSE_IX:
+            if (!isEmpty) {
+               if (!checkForCloseTag(pInfo, CFXMLPlistTags[FALSE_IX], FALSE_TAG_LENGTH)) return NULL;
+           }
+            return CFRetain(kCFBooleanFalse);
+        case REAL_IX:
+            if (isEmpty) {
+                pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo));
+                return NULL;
+            } else {
+                return parseRealTag(pInfo);
+            }
+        case INTEGER_IX:
+            if (isEmpty) {
+                pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo));
+                return NULL;
+            } else {
+                return parseIntegerTag(pInfo);
+            }
+        default:  {
+            CFStringRef markerStr = CFStringCreateWithCharacters(pInfo->allocator, marker, markerLength);
+            pInfo->curr = marker;
+            pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Encountered unknown tag %@ on line %d"), markerStr, lineNumber(pInfo));
+            CFRelease(markerStr);
+            return NULL;
+        }
+    }
+}
+
+static CFTypeRef parseXMLPropertyList(_CFXMLPlistParseInfo *pInfo) {
+    while (!pInfo->errorString && pInfo->curr < pInfo->end) {
+        UniChar ch;
+        skipWhitespace(pInfo);
+        if (pInfo->curr+1 >= pInfo->end) {
+            pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "No XML content found", CFStringGetSystemEncoding());
+            return NULL;
+        }
+        if (*(pInfo->curr) != '<') {
+            pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unexpected character %c at line %d"), *(pInfo->curr), lineNumber(pInfo));
+            return NULL;
+        }
+        ch = *(++ pInfo->curr);
+        if (ch == '!') {
+            // Comment or DTD
+            ++ pInfo->curr;
+            if (pInfo->curr+1 < pInfo->end && *pInfo->curr == '-' && *(pInfo->curr+1) == '-') {
+                // Comment
+                pInfo->curr += 2;
+                skipXMLComment(pInfo);
+            } else {
+                skipDTD(pInfo);
+            }
+        } else if (ch == '?') {
+            // Processing instruction
+            pInfo->curr++;
+            skipXMLProcessingInstruction(pInfo);
+        } else {
+            // Tag or malformed
+            return parseXMLElement(pInfo, NULL);
+            // 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
+        }
+    }
+    // Should never get here
+    if (!(pInfo->errorString))
+        pInfo->errorString = CFStringCreateWithCString(pInfo->allocator, "Encountered unexpected EOF", CFStringGetSystemEncoding());
+    return NULL;
+}
+
+static CFStringEncoding encodingForXMLData(CFDataRef data, CFStringRef *error) {
+    const uint8_t *bytes = (uint8_t *)CFDataGetBytePtr(data);
+    UInt32 length = CFDataGetLength(data);
+    const uint8_t *idx, *end;
+    char quote;
+    
+    // Check for the byte order mark first
+    if (length > 2 &&
+        ((*bytes == 0xFF && *(bytes+1) == 0xFE) ||
+         (*bytes == 0xFE && *(bytes+1) == 0xFF) ||
+         *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
+        return kCFStringEncodingUnicode;
+    
+    // Scan for the <?xml.... ?> opening
+    if (length < 5 || strncmp((char const *) bytes, "<?xml", 5) != 0) return kCFStringEncodingUTF8;
+    idx = bytes + 5;
+    end = bytes + length;
+    // Found "<?xml"; now we scan for "encoding"
+    while (idx < end) {
+        uint8_t ch = *idx;
+        const uint8_t *scan;
+        if ( ch == '?' || ch == '>') return kCFStringEncodingUTF8;
+        idx ++;
+        scan = idx;
+        if (ch == 'e' && *scan++ == 'n' && *scan++ == 'c' && *scan++ == 'o' && *scan++ == 'd' && *scan++ == 'i'
+            && *scan++ == 'n' && *scan++ == 'g' && *scan++ == '=') {
+            idx = scan;
+            break;
+        }
+    }
+    if (idx >= end) return kCFStringEncodingUTF8;
+    quote = *idx;
+    if (quote != '\'' && quote != '\"') return kCFStringEncodingUTF8;
+    else {
+        CFStringRef encodingName;
+        const uint8_t *base = idx+1; // Move past the quote character
+        CFStringEncoding enc;
+        UInt32 len;
+        idx ++;
+        while (idx < end && *idx != quote) idx ++;
+        if (idx >= end) return kCFStringEncodingUTF8;
+        len = idx - base;
+        if (len == 5 && (*base == 'u' || *base == 'U') && (base[1] == 't' || base[1] == 'T') && (base[2] == 'f' || base[2] == 'F') && (base[3] == '-') && (base[4] == '8'))
+            return kCFStringEncodingUTF8;
+        encodingName = CFStringCreateWithBytes(kCFAllocatorSystemDefault, base, len, kCFStringEncodingISOLatin1, false);
+        enc = CFStringConvertIANACharSetNameToEncoding(encodingName);
+        if (enc != kCFStringEncodingInvalidId) {
+            CFRelease(encodingName);
+            return enc;
+        }
+
+        if (error) {
+            *error = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("Encountered unknown encoding (%@)"), encodingName);
+            CFRelease(encodingName);
+        }
+        return 0;
+    }
+}
+
+extern bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString);
+int32_t _CFPropertyListAllowNonUTF8 = 0;
+
+#define SAVE_PLISTS 0
+
+#if SAVE_PLISTS
+static Boolean __savePlistData(CFDataRef data, CFOptionFlags opt) {
+    uint8_t pn[2048];
+    uint8_t fn[2048];
+    uint32_t pnlen = sizeof(pn);
+    uint8_t *pnp = NULL;
+    if (0 == _NSGetExecutablePath((char *)pn, &pnlen)) {
+       pnp = strrchr((char *)pn, '/');
+    }
+    if (!pnp) {
+       pnp = pn;
+    } else {
+       pnp++;
+    }
+    if (0 == strcmp((char *)pnp, "parse_plists")) return true;
+    CFUUIDRef r = CFUUIDCreate(kCFAllocatorSystemDefault);
+    CFStringRef s = CFUUIDCreateString(kCFAllocatorSystemDefault, r);
+    CFStringRef p = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("/tmp/plists/%s#%@#0x%x"), pnp, s, opt);
+    _CFStringGetFileSystemRepresentation(p, fn, sizeof(fn));
+    CFRelease(r);
+    CFRelease(s);
+    CFRelease(p);
+    int fd = open((const char *)fn, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+    if (fd < 0) return false;
+    int len = CFDataGetLength(data);
+    int w = write(fd, CFDataGetBytePtr(data), len);
+    fsync(fd);
+    close(fd);
+    if (w != len) return false;
+    return true;
+}
+#endif
+
+CFTypeRef _CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xmlData, CFOptionFlags option, CFStringRef *errorString, Boolean allowNewTypes, CFPropertyListFormat *format) {
+    initStatics();
+    CFStringEncoding encoding;
+    CFStringRef xmlString;
+    UInt32 length;
+    CFPropertyListRef plist;
+
+    if (errorString) *errorString = NULL;
+    if (!xmlData || CFDataGetLength(xmlData) == 0) {
+        if (errorString) {
+            *errorString = CFSTR("Cannot parse a NULL or zero-length data");
+            CFRetain(*errorString); // Caller expects to release
+        }
+        return NULL;
+    }
+
+#if SAVE_PLISTS
+    __savePlistData(xmlData, option);
+#endif
+
+    if (__CFTryParseBinaryPlist(allocator, xmlData, option, &plist, errorString)) {
+       if (format) *format = kCFPropertyListBinaryFormat_v1_0;
+       return plist;
+    }
+    
+    allocator = allocator ? allocator : __CFGetDefaultAllocator();
+    CFRetain(allocator);
+    
+    encoding = encodingForXMLData(xmlData, errorString); // 0 is an error return, NOT MacRoman.
+
+    if (encoding == 0) {
+        // Couldn't find an encoding; encodingForXMLData already set *errorString if necessary
+        // Note that encodingForXMLData() will give us the right values for a standard plist, too.
+        if (errorString) *errorString = CFStringCreateWithFormat(allocator, NULL, CFSTR("Could not determine the encoding of the XML data"));
+        return NULL;
+    }
+
+    xmlString = CFStringCreateWithBytes(allocator, CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData), encoding, true);
+    if (NULL == xmlString && (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard) || _CFPropertyListAllowNonUTF8)) { // conversion failed, probably because not in proper encoding
+        // Call __CFStringCreateImmutableFunnel3() the same way CFStringCreateWithBytes() does, except with the addt'l flag
+               if (encoding == kCFStringEncodingUTF8) xmlString = __CFStringCreateImmutableFunnel3(allocator, CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData), kCFStringEncodingUTF8, true, true, false, false, false, (CFAllocatorRef)-1  /* ALLOCATORSFREEFUNC */, kCFStringEncodingLenientUTF8Conversion);
+    }
+    length = xmlString ? CFStringGetLength(xmlString) : 0;
+
+    if (length) {
+        _CFXMLPlistParseInfo pInfoBuf;
+        _CFXMLPlistParseInfo *pInfo = &pInfoBuf;
+        CFTypeRef result;
+        UniChar *buf = (UniChar *)CFStringGetCharactersPtr(xmlString);
+
+        if (errorString) *errorString = NULL;
+        if (!buf) {
+            buf = (UniChar *)CFAllocatorAllocate(allocator, length * sizeof(UniChar), 0);
+            CFStringGetCharacters(xmlString, CFRangeMake(0, length), buf);
+            CFRelease(xmlString);
+            xmlString = NULL;
+        }
+        pInfo->begin = buf;
+        pInfo->end = buf+length;
+        pInfo->curr = buf;
+        pInfo->allocator = allocator;
+        pInfo->errorString = NULL;
+        pInfo->stringSet = NULL;
+        pInfo->tmpString = NULL;
+        pInfo->mutabilityOption = option;
+        pInfo->allowNewTypes = allowNewTypes;
+        
+        // Haven't done anything XML-specific to this point.  However, the encoding we used to translate the bytes should be kept in mind; we used Unicode if the byte-order mark was present; UTF-8 otherwise.  If the system encoding is not UTF-8 or some variant of 7-bit ASCII, we'll be in trouble.....
+        result = parseXMLPropertyList(pInfo);
+        if (result && format) *format = kCFPropertyListXMLFormat_v1_0;
+        if (!result) {
+           CFStringRef err = pInfo->errorString;
+            // Reset pInfo so we can try again
+            pInfo->curr = pInfo->begin;
+            pInfo->errorString = NULL;
+            // Try pList
+            result = parseOldStylePropertyListOrStringsFile(pInfo);
+           if (result && format) *format = kCFPropertyListOpenStepFormat;
+            if (!result) {
+               if (errorString) *errorString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("XML parser error:\n\t%@\nOld-style plist parser error:\n\t%@\n"), err, pInfo->errorString);
+            }
+           if (err) CFRelease(err);
+           if (pInfo->errorString) CFRelease(pInfo->errorString);
+        }
+        if (xmlString) {
+            CFRelease(xmlString);
+        } else {
+            CFAllocatorDeallocate(allocator, (void *)pInfo->begin);
+        }
+        if (pInfo->stringSet) CFRelease(pInfo->stringSet);
+        if (pInfo->tmpString) CFRelease(pInfo->tmpString);
+        CFRelease(allocator);
+        return result;
+    } else {
+        if (errorString)
+            *errorString = (CFStringRef)CFRetain(CFSTR("Conversion of data failed. The file is not UTF-8, or in the encoding specified in XML header if XML."));
+        return NULL;
+    }
+}
+
+CFTypeRef CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xmlData, CFOptionFlags option, CFStringRef *errorString) {
+    initStatics();
+    CFAssert1(xmlData != NULL, __kCFLogAssertion, "%s(): NULL data not allowed", __PRETTY_FUNCTION__);
+    CFAssert2(option == kCFPropertyListImmutable || option == kCFPropertyListMutableContainers || option == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, option);
+    return _CFPropertyListCreateFromXMLData(allocator, xmlData, option, errorString, true, NULL);
+}
+
+CFIndex CFPropertyListWriteToStream(CFPropertyListRef propertyList, CFWriteStreamRef stream, CFPropertyListFormat format, CFStringRef *errorString) {
+    initStatics();
+    CFAssert1(stream != NULL, __kCFLogAssertion, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__);
+    CFAssert2(format == kCFPropertyListOpenStepFormat || format == kCFPropertyListXMLFormat_v1_0 || format == kCFPropertyListBinaryFormat_v1_0, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, format);
+    CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__);
+    __CFAssertIsPList(propertyList);
+    CFAssert1(CFWriteStreamGetTypeID() == CFGetTypeID(stream), __kCFLogAssertion, "%s(): stream argument is not a write stream", __PRETTY_FUNCTION__);
+    CFAssert1(kCFStreamStatusOpen == CFWriteStreamGetStatus(stream) || kCFStreamStatusWriting == CFWriteStreamGetStatus(stream), __kCFLogAssertion, "%s():  stream is not open", __PRETTY_FUNCTION__);
+
+    if (errorString) *errorString = NULL;
+    if (!CFPropertyListIsValid(propertyList, format)) {
+        if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Property list invalid for format"));
+        return 0;
+    }
+    if (format == kCFPropertyListOpenStepFormat) {
+        if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing"));
+        return 0;
+    }
+    if (format == kCFPropertyListXMLFormat_v1_0) {
+        CFDataRef data = CFPropertyListCreateXMLData(kCFAllocatorSystemDefault, propertyList);
+       if (!data) {
+           if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Property list in XML format could not be created for unknown reasons."));
+           return 0;
+       }
+        CFIndex len = CFDataGetLength(data);
+       const uint8_t *ptr = CFDataGetBytePtr(data);
+       while (0 < len) {
+           CFIndex ret = CFWriteStreamWrite(stream, ptr, len);
+           if (ret == 0) {
+               if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Property list writing could not be completed because stream is full."));
+               return 0;
+           }
+           if (ret < 0) {
+               CFErrorRef err = CFWriteStreamCopyError(stream);
+               CFStringRef str = err ? CFErrorCopyDescription(err) : NULL;
+               if (errorString) *errorString = str ? str : (CFStringRef)CFRetain(CFSTR("Property list writing could not be completed because the stream had an unknown error."));
+               if (err) CFRelease(err);
+               return 0;
+           }
+           ptr += ret;
+           len -= ret;
+       }
+       len = CFDataGetLength(data);
+       CFRelease(data);
+        return len;
+    }
+    if (format == kCFPropertyListBinaryFormat_v1_0) {
+       CFIndex len = __CFBinaryPlistWriteToStream(propertyList, stream);
+        return len;
+    }
+    if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Unknown format option"));
+    return 0;
+}
+
+static void __CFConvertReadStreamToBytes(CFReadStreamRef stream, CFIndex max, uint8_t **buffer, CFIndex *length) {
+    int32_t buflen = 0, bufsize = 0, retlen;
+    uint8_t *buf = NULL, sbuf[8192];
+    for (;;) {
+       retlen = CFReadStreamRead(stream, sbuf, __CFMin(8192, max));
+       if (retlen <= 0) {
+           *buffer = buf;
+           *length = buflen;
+           return;
+       }
+        if (bufsize < buflen + retlen) {
+           if (bufsize < 256 * 1024) {
+               bufsize *= 4;
+           } else if (bufsize < 16 * 1024 * 1024) {
+               bufsize *= 2;
+           } else {
+               // once in this stage, this will be really slow
+               // and really potentially fragment memory
+               bufsize += 256 * 1024;
+           }
+           if (bufsize < buflen + retlen) bufsize = buflen + retlen;
+           buf = (uint8_t *)CFAllocatorReallocate(kCFAllocatorSystemDefault, buf, bufsize, 0);
+           if (!buf) HALT;
+       }
+       memmove(buf + buflen, sbuf, retlen);
+       buflen += retlen;
+        max -= retlen;
+       if (max <= 0) {
+           *buffer = buf;
+           *length = buflen;
+           return;
+       }
+    }
+}
+
+CFPropertyListRef CFPropertyListCreateFromStream(CFAllocatorRef allocator, CFReadStreamRef stream, CFIndex length, CFOptionFlags mutabilityOption, CFPropertyListFormat *format, CFStringRef *errorString) {
+    initStatics();
+    CFPropertyListRef pl;
+    CFDataRef data;
+    CFIndex buflen = 0;
+    uint8_t *buffer = NULL;
+    CFAssert1(stream != NULL, __kCFLogAssertion, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__);
+    CFAssert1(CFReadStreamGetTypeID() == CFGetTypeID(stream), __kCFLogAssertion, "%s(): stream argument is not a read stream", __PRETTY_FUNCTION__);
+    CFAssert1(kCFStreamStatusOpen == CFReadStreamGetStatus(stream) || kCFStreamStatusReading == CFReadStreamGetStatus(stream), __kCFLogAssertion, "%s():  stream is not open", __PRETTY_FUNCTION__);
+    CFAssert2(mutabilityOption == kCFPropertyListImmutable || mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, mutabilityOption);
+
+    if (errorString) *errorString = NULL;
+    if (0 == length) length = INT_MAX;
+    __CFConvertReadStreamToBytes(stream, length, &buffer, &buflen);
+    if (!buffer || buflen < 6) {
+        if (buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, buffer);
+        if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("stream had too few bytes"));
+        return NULL;
+    }
+    data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, buffer, buflen, kCFAllocatorSystemDefault);
+    pl = _CFPropertyListCreateFromXMLData(allocator, data, mutabilityOption, errorString, true, format);
+    CFRelease(data);
+    return pl;
+}
+
+// ========================================================================
+
+//
+// Old NeXT-style property lists
+//
+
+static CFTypeRef parsePlistObject(_CFXMLPlistParseInfo *pInfo, bool requireObject);
+
+#define isValidUnquotedStringCharacter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z') || ((x) >= '0' && (x) <= '9') || (x) == '_' || (x) == '$' || (x) == '/' || (x) == ':' || (x) == '.' || (x) == '-')
+
+static void advanceToNonSpace(_CFXMLPlistParseInfo *pInfo) {
+    UniChar ch2;
+    while (pInfo->curr < pInfo->end) {
+       ch2 = *(pInfo->curr);
+        pInfo->curr ++;
+        if (ch2 >= 9 && ch2 <= 0x0d) continue; // tab, newline, vt, form feed, carriage return
+        if (ch2 == ' ' || ch2 == 0x2028 || ch2 == 0x2029) continue;    // space and Unicode line sep, para sep
+       if (ch2 == '/') {
+            if (pInfo->curr >= pInfo->end) {
+                // whoops; back up and return
+                pInfo->curr --;
+                return;
+            } else if (*(pInfo->curr) == '/') {
+                pInfo->curr ++;
+                while (pInfo->curr < pInfo->end) {     // go to end of comment line
+                    UniChar ch3 = *(pInfo->curr);
+                    if (ch3 == '\n' || ch3 == '\r' || ch3 == 0x2028 || ch3 == 0x2029) break;
+                    pInfo->curr ++;
+               }
+           } else if (*(pInfo->curr) == '*') {         // handle /* ... */
+                pInfo->curr ++;
+               while (pInfo->curr < pInfo->end) {
+                   ch2 = *(pInfo->curr);
+                    pInfo->curr ++;
+                   if (ch2 == '*' && pInfo->curr < pInfo->end && *(pInfo->curr) == '/') {
+                        pInfo->curr ++; // advance past the '/'
+                        break;
+                    }
+                }
+            } else {
+                pInfo->curr --;
+                return;
+           }
+        } else {
+            pInfo->curr --;
+            return;
+        }
+    }
+}
+
+static UniChar getSlashedChar(_CFXMLPlistParseInfo *pInfo) {
+    UniChar ch = *(pInfo->curr);
+    pInfo->curr ++;
+    switch (ch) {
+       case '0':
+       case '1':       
+       case '2':       
+       case '3':       
+       case '4':       
+       case '5':       
+       case '6':       
+       case '7':  {
+            uint8_t num = ch - '0';
+            UniChar result;
+            CFIndex usedCharLen;
+           /* three digits maximum to avoid reading \000 followed by 5 as \5 ! */
+           if ((ch = *(pInfo->curr)) >= '0' && ch <= '7') { // we use in this test the fact that the buffer is zero-terminated
+                pInfo->curr ++;
+               num = (num << 3) + ch - '0';
+               if ((pInfo->curr < pInfo->end) && (ch = *(pInfo->curr)) >= '0' && ch <= '7') {
+                    pInfo->curr ++;
+                   num = (num << 3) + ch - '0';
+               }
+           }
+            CFStringEncodingBytesToUnicode(kCFStringEncodingNextStepLatin, 0, &num, sizeof(uint8_t), NULL,  &result, 1, &usedCharLen);
+            return (usedCharLen == 1) ? result : 0;
+       }
+       case 'U': {
+           unsigned num = 0, numDigits = 4;    /* Parse four digits */
+           while (pInfo->curr < pInfo->end && numDigits--) {
+                if (((ch = *(pInfo->curr)) < 128) && isxdigit(ch)) { 
+                    pInfo->curr ++;
+                   num = (num << 4) + ((ch <= '9') ? (ch - '0') : ((ch <= 'F') ? (ch - 'A' + 10) : (ch - 'a' + 10)));
+               }
+           }
+           return num;
+       }
+       case 'a':       return '\a';    // Note: the meaning of '\a' varies with -traditional to gcc
+       case 'b':       return '\b';
+       case 'f':       return '\f';
+       case 'n':       return '\n';
+       case 'r':       return '\r';
+       case 't':       return '\t';
+       case 'v':       return '\v';
+       case '"':       return '\"';
+       case '\n':      return '\n';
+    }
+    return ch;
+}
+
+static CFStringRef parseQuotedPlistString(_CFXMLPlistParseInfo *pInfo, UniChar quote) {
+    CFMutableStringRef str = NULL;
+    const UniChar *startMark = pInfo->curr;
+    const UniChar *mark = pInfo->curr;
+    while (pInfo->curr < pInfo->end) {
+       UniChar ch = *(pInfo->curr);
+        if (ch == quote) break;
+        if (ch == '\\') {
+            _catFromMarkToBuf(mark, pInfo->curr, &str, pInfo->allocator);
+            pInfo->curr ++;
+            ch = getSlashedChar(pInfo);
+            CFStringAppendCharacters(str, &ch, 1);
+            mark = pInfo->curr;
+       } else {
+            // Note that the original NSParser code was much more complex at this point, but it had to deal with 8-bit characters in a non-UniChar stream.  We always have UniChar (we translated the data by the system encoding at the very beginning, hopefully), so this is safe.
+            pInfo->curr ++;
+        }
+    }
+    if (pInfo->end <= pInfo->curr) {
+        if (str) CFRelease(str);
+           pInfo->curr = startMark;
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unterminated quoted string starting on line %d"), lineNumber(pInfo));
+        return NULL;
+    }
+    if (!str) {
+        if (pInfo->mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
+            _catFromMarkToBuf(mark, pInfo->curr, &str, pInfo->allocator);
+        } else {
+            str = (CFMutableStringRef)_uniqueStringForCharacters(pInfo, mark, pInfo->curr-mark);
+            CFRetain(str);
+        }
+    } else {
+        if (mark != pInfo->curr) {
+            _catFromMarkToBuf(mark, pInfo->curr, &str, pInfo->allocator);
+        }
+        if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) {
+            CFStringRef uniqueString = _uniqueStringForString(pInfo, str);
+            CFRetain(uniqueString);
+            CFRelease(str);
+            str = (CFMutableStringRef)uniqueString;
+        }
+    }
+    pInfo->curr ++;  // Advance past the quote character before returning.
+    if (pInfo->errorString) {
+       CFRelease(pInfo->errorString);
+       pInfo->errorString = NULL;
+    }
+    return str;
+}
+
+static CFStringRef parseUnquotedPlistString(_CFXMLPlistParseInfo *pInfo) {
+    const UniChar *mark = pInfo->curr;
+    while (pInfo->curr < pInfo->end) {
+        UniChar ch = *pInfo->curr;
+        if (isValidUnquotedStringCharacter(ch))
+            pInfo->curr ++;
+        else break;
+    }
+    if (pInfo->curr != mark) {
+        if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) {
+            CFStringRef str = _uniqueStringForCharacters(pInfo, mark, pInfo->curr-mark);
+            CFRetain(str);
+            return str;
+        } else {
+            CFMutableStringRef str = CFStringCreateMutable(pInfo->allocator, 0);
+            CFStringAppendCharacters(str, mark, pInfo->curr - mark);
+            return str;
+        }
+    }
+       pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unexpected EOF"));
+    return NULL;
+}
+
+static CFStringRef parsePlistString(_CFXMLPlistParseInfo *pInfo, bool requireObject) {
+    UniChar ch;
+    advanceToNonSpace(pInfo);
+    if (pInfo->curr >= pInfo->end) {
+       if (requireObject) {
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unexpected EOF while parsing string"));
+       }
+        return NULL;
+    }
+    ch = *(pInfo->curr);
+    if (ch == '\'' || ch == '\"') {
+        pInfo->curr ++;
+        return parseQuotedPlistString(pInfo, ch);
+    } else if (isValidUnquotedStringCharacter(ch)) {
+        return parseUnquotedPlistString(pInfo);
+    } else {
+       if (requireObject) {
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Invalid string character at line %d"), lineNumber(pInfo));
+       }
+        return NULL;
+    }
+}
+
+static CFTypeRef parsePlistArray(_CFXMLPlistParseInfo *pInfo) {
+    CFMutableArrayRef array = CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks);
+    CFTypeRef tmp = parsePlistObject(pInfo, false);
+    while (tmp) {
+        CFArrayAppendValue(array, tmp);
+        CFRelease(tmp);
+        advanceToNonSpace(pInfo);
+        if (*pInfo->curr != ',') {
+            tmp = NULL;
+        } else {
+            pInfo->curr ++;
+            tmp = parsePlistObject(pInfo, false);
+        }
+    }
+    advanceToNonSpace(pInfo);
+    if (*pInfo->curr != ')') {
+        CFRelease(array);
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Expected terminating ')' for array at line %d"), lineNumber(pInfo));
+        return NULL;
+    }
+    if (pInfo->errorString) {
+       CFRelease(pInfo->errorString);
+       pInfo->errorString = NULL;
+    }
+    pInfo->curr ++;
+    return array;
+}
+
+static CFDictionaryRef parsePlistDictContent(_CFXMLPlistParseInfo *pInfo) {
+    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    CFStringRef key = NULL;
+    Boolean failedParse = false;
+    key = parsePlistString(pInfo, false);
+    while (key) {
+        CFTypeRef value;
+        advanceToNonSpace(pInfo);
+       if (*pInfo->curr == ';') {
+           /* This is a strings file using the shortcut format */
+           /* although this check here really applies to all plists. */
+           value = CFRetain(key);
+       } else if (*pInfo->curr == '=') {
+           pInfo->curr ++;
+           value = parsePlistObject(pInfo, true);
+           if (!value) {
+               failedParse = true;
+               break;
+           }
+       } else {
+               pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unexpected ';' or '=' after key at line %d"), lineNumber(pInfo));
+           failedParse = true;
+           break;
+       }
+       CFDictionarySetValue(dict, key, value);
+       CFRelease(key);
+       key = NULL;
+       CFRelease(value);
+       value = NULL;
+       advanceToNonSpace(pInfo);
+       if (*pInfo->curr == ';') {
+           pInfo->curr ++;
+           key = parsePlistString(pInfo, false);
+       } else if (!allowMissingSemi && _CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) {
+               CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListCreateFromXMLData(): Old-style plist parser: missing semicolon in dictionary."));
+           failedParse = true;
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Missing ';' on line %d"), lineNumber(pInfo));
+       } else {
+           // on pre-Jaguar systems, do nothing except silently ignore the rest
+           // of the dictionary, which is what happened on those systems.
+       }
+    }
+
+    if (failedParse) {
+        if (key) CFRelease(key);
+        CFRelease(dict);
+        return NULL;
+    }
+    if (pInfo->errorString) {
+       CFRelease(pInfo->errorString);
+       pInfo->errorString = NULL;
+    }
+    return dict;
+}
+
+static CFTypeRef parsePlistDict(_CFXMLPlistParseInfo *pInfo) {
+    CFDictionaryRef dict = parsePlistDictContent(pInfo);
+    if (!dict) return NULL;
+    advanceToNonSpace(pInfo);
+    if (*pInfo->curr != '}') {
+        CFRelease(dict);
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Expected terminating '}' for dictionary at line %d"), lineNumber(pInfo));
+        return NULL;
+    }
+    pInfo->curr ++;
+    return dict;
+}
+
+CF_INLINE unsigned char fromHexDigit(unsigned char ch) {
+    if (isdigit(ch)) return ch - '0';
+    if ((ch >= 'a') && (ch <= 'f')) return ch - 'a' + 10;
+    if ((ch >= 'A') && (ch <= 'F')) return ch - 'A' + 10;
+    return 0xff; // Just choose a large number for the error code
+}
+
+/* Gets up to bytesSize bytes from a plist data. Returns number of bytes actually read. Leaves cursor at first non-space, non-hex character.
+   -1 is returned for unexpected char, -2 for uneven number of hex digits
+*/
+static int getDataBytes(_CFXMLPlistParseInfo *pInfo, unsigned char *bytes, int bytesSize) {
+    int numBytesRead = 0;
+    while ((pInfo->curr < pInfo->end) && (numBytesRead < bytesSize)) {
+       int first, second;
+       UniChar ch1 = *pInfo->curr;
+       if (ch1 == '>') return numBytesRead;  // Meaning we're done
+       first = fromHexDigit((unsigned char)ch1);
+       if (first != 0xff) {    // If the first char is a hex, then try to read a second hex
+           pInfo->curr++;
+           if (pInfo->curr >= pInfo->end) return -2;   // Error: uneven number of hex digits
+           UniChar ch2 = *pInfo->curr;
+           second = fromHexDigit((unsigned char)ch2);
+           if (second == 0xff) return -2;  // Error: uneven number of hex digits
+           bytes[numBytesRead++] = (first << 4) + second;
+           pInfo->curr++;
+       } else if (ch1 == ' ' || ch1 == '\n' || ch1 == '\t' || ch1 == '\r' || ch1 == 0x2028 || ch1 == 0x2029) {
+           pInfo->curr++;
+       } else {
+           return -1;  // Error: unexpected character
+       }
+    }
+    return numBytesRead;    // This does likely mean we didn't encounter a '>', but we'll let the caller deal with that
+}
+
+#define numBytes 400
+static CFTypeRef parsePlistData(_CFXMLPlistParseInfo *pInfo) {
+    CFMutableDataRef result = CFDataCreateMutable(pInfo->allocator, 0);
+
+    // Read hex bytes and append them to result
+    while (1) {
+       unsigned char bytes[numBytes];
+       int numBytesRead = getDataBytes(pInfo, bytes, numBytes);
+       if (numBytesRead < 0) {
+           CFRelease(result);
+               switch (numBytesRead) {
+                   case -2: pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Malformed data byte group at line %d; uneven length"), lineNumber(pInfo)); break;
+                   default: pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Malformed data byte group at line %d; invalid hex"), lineNumber(pInfo)); break;
+               }
+           return NULL;
+       }
+       if (numBytesRead == 0) break;
+       CFDataAppendBytes(result, bytes, numBytesRead);
+    }
+
+    if (pInfo->errorString) {
+       CFRelease(pInfo->errorString);
+       pInfo->errorString = NULL;
+    }
+
+    if (*(pInfo->curr) == '>') {
+        pInfo->curr ++; // Move past '>'
+        return result;
+    } else {
+        CFRelease(result);
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Expected terminating '>' for data at line %d"), lineNumber(pInfo));
+        return NULL;
+    }
+}
+#undef numBytes
+
+// Returned object is retained; caller must free.
+static CFTypeRef parsePlistObject(_CFXMLPlistParseInfo *pInfo, bool requireObject) {
+    UniChar ch;
+    advanceToNonSpace(pInfo);
+    if (pInfo->curr + 1 >= pInfo->end) {
+       if (requireObject) {
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unexpected EOF while parsing plist"));
+       }
+        return NULL;
+    }
+    ch = *(pInfo->curr);
+    pInfo->curr ++;
+    if (ch == '{') {
+        return parsePlistDict(pInfo);
+    } else if (ch == '(') {
+        return parsePlistArray(pInfo);
+    } else if (ch == '<') {
+        return parsePlistData(pInfo);
+    } else if (ch == '\'' || ch == '\"') {
+        return parseQuotedPlistString(pInfo, ch);
+    } else if (isValidUnquotedStringCharacter(ch)) {
+        pInfo->curr --;
+        return parseUnquotedPlistString(pInfo);
+    } else {
+        pInfo->curr --;  // Must back off the charcter we just read
+       if (requireObject) {
+           pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Unexpected character '0x%x' at line %d"), ch, lineNumber(pInfo));
+       }
+        return NULL;
+    }
+}
+
+static CFTypeRef parseOldStylePropertyListOrStringsFile(_CFXMLPlistParseInfo *pInfo) {
+    const UniChar *begin = pInfo->curr;
+    CFTypeRef result;
+    advanceToNonSpace(pInfo);
+    // A file consisting only of whitespace (or empty) is now defined to be an empty dictionary
+    if (pInfo->curr >= pInfo->end) return CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    result = parsePlistObject(pInfo, true);
+    advanceToNonSpace(pInfo);
+    if (pInfo->curr >= pInfo->end) return result;
+    if (!result) return NULL;
+    if (CFGetTypeID(result) != stringtype) {
+        CFRelease(result);
+       pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Junk after plist at line %d"), lineNumber(pInfo));
+        return NULL;
+    }
+    CFRelease(result);
+    // Check for a strings file (looks like a dictionary without the opening/closing curly braces)
+    pInfo->curr = begin;
+    return parsePlistDictContent(pInfo);
+}
+
+#undef isValidUnquotedStringCharacter
+
+static CFArrayRef _arrayDeepImmutableCopy(CFAllocatorRef allocator, CFArrayRef array, CFOptionFlags mutabilityOption) {
+    CFArrayRef result = NULL;
+    CFIndex i, c = CFArrayGetCount(array);
+    CFTypeRef *values;
+    if (c == 0) {
+        result = CFArrayCreate(allocator, NULL, 0, &kCFTypeArrayCallBacks);
+    } else if ((values = (CFTypeRef *)CFAllocatorAllocate(allocator, c*sizeof(CFTypeRef), 0)) != NULL) {
+        CFArrayGetValues(array, CFRangeMake(0, c), values);
+        for (i = 0; i < c; i ++) {
+            values[i] = CFPropertyListCreateDeepCopy(allocator, values[i], mutabilityOption);
+            if (values[i] == NULL) {
+                break;
+            }
+        }
+        result = (i == c) ? CFArrayCreate(allocator, values, c, &kCFTypeArrayCallBacks) : NULL;
+        c = i;
+        for (i = 0; i < c; i ++) {
+            CFRelease(values[i]);
+        }
+        CFAllocatorDeallocate(allocator, values);
+    }
+    return result;
+}
+
+static CFMutableArrayRef _arrayDeepMutableCopy(CFAllocatorRef allocator, CFArrayRef array, CFOptionFlags mutabilityOption) {
+    CFIndex i, c = CFArrayGetCount(array);
+    CFMutableArrayRef result = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
+    if (result) {
+        for (i = 0; i < c; i ++) {
+            CFTypeRef newValue = CFPropertyListCreateDeepCopy(allocator, CFArrayGetValueAtIndex(array, i), mutabilityOption);
+            if (!newValue) break;
+            CFArrayAppendValue(result, newValue);
+            CFRelease(newValue);
+        }
+        if (i != c) {
+            CFRelease(result);
+            result = NULL;
+        }
+    }
+    return result;
+}
+
+CFPropertyListRef CFPropertyListCreateDeepCopy(CFAllocatorRef allocator, CFPropertyListRef propertyList, CFOptionFlags mutabilityOption) {
+    initStatics();
+    CFTypeID typeID;
+    CFPropertyListRef result = NULL;
+    CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): cannot copy a NULL property list", __PRETTY_FUNCTION__);
+    __CFAssertIsPList(propertyList);
+    CFAssert2(mutabilityOption == kCFPropertyListImmutable || mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, mutabilityOption);
+       if (!CFPropertyListIsValid(propertyList, kCFPropertyListBinaryFormat_v1_0)) return NULL;
+    
+    if (allocator == NULL) {
+        allocator = (CFAllocatorRef)CFRetain(__CFGetDefaultAllocator());
+    } else {
+        CFRetain(allocator);
+    }
+    
+    typeID = CFGetTypeID(propertyList);
+    if (typeID == dicttype) {
+        CFDictionaryRef dict = (CFDictionaryRef)propertyList;
+        Boolean isMutable = (mutabilityOption != kCFPropertyListImmutable);
+        CFIndex count = CFDictionaryGetCount(dict);
+        CFTypeRef *keys, *values;
+        if (count == 0) {
+            result = isMutable ? CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks): CFDictionaryCreate(allocator, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+        } else if ((keys = (CFTypeRef *)CFAllocatorAllocate(allocator, 2 * count * sizeof(CFTypeRef), 0)) != NULL)   {
+            CFIndex i;
+            values = keys+count;
+            CFDictionaryGetKeysAndValues(dict, keys, values);
+            for (i = 0; i < count; i ++) {
+                keys[i] = CFStringCreateCopy(allocator, (CFStringRef)keys[i]);
+                if (keys[i] == NULL) {
+                    break;
+                }
+                values[i] = CFPropertyListCreateDeepCopy(allocator, values[i], mutabilityOption);
+                if (values[i] == NULL) {
+                    CFRelease(keys[i]);
+                    break;
+                }
+            }
+            if (i == count) {
+                result = isMutable ? CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) : CFDictionaryCreate(allocator, keys, values, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+                for (i = 0; i < count; i ++) {
+                    if (isMutable) {
+                        CFDictionarySetValue((CFMutableDictionaryRef)result, keys[i], values[i]);
+                    }
+                    CFRelease(keys[i]);
+                    CFRelease(values[i]);
+                }
+            } else {
+                result = NULL;
+                count = i;
+                for (i = 0; i < count; i ++) {
+                    CFRelease(keys[i]);
+                    CFRelease(values[i]);
+                }
+            }
+            CFAllocatorDeallocate(allocator, keys);
+        } else {
+            result = NULL;
+        }
+    } else if (typeID == arraytype) {
+        if (mutabilityOption == kCFPropertyListImmutable) {
+            result = _arrayDeepImmutableCopy(allocator, (CFArrayRef)propertyList, mutabilityOption);
+        } else {
+            result = _arrayDeepMutableCopy(allocator, (CFArrayRef)propertyList, mutabilityOption);
+        }
+    } else if (typeID == datatype) {
+        if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
+            result = CFDataCreateMutableCopy(allocator, 0, (CFDataRef)propertyList);
+        } else {
+            result = CFDataCreateCopy(allocator, (CFDataRef)propertyList);
+        }
+    } else if (typeID == numbertype) {
+        // Warning - this will break if byteSize is ever greater than 32
+        uint8_t bytes[32];
+        CFNumberType numType = CFNumberGetType((CFNumberRef)propertyList);
+        CFNumberGetValue((CFNumberRef)propertyList, numType, (void *)bytes);
+        result = CFNumberCreate(allocator, numType, (void *)bytes);
+    } else if (typeID == booltype) {
+        // Booleans are immutable & shared instances
+        CFRetain(propertyList);
+        result = propertyList;
+    } else if (typeID == datetype) {
+        // Dates are immutable
+        result = CFDateCreate(allocator, CFDateGetAbsoluteTime((CFDateRef)propertyList));
+    } else if (typeID == stringtype) {
+        if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
+            result = CFStringCreateMutableCopy(allocator, 0, (CFStringRef)propertyList);
+        } else {
+            result = CFStringCreateCopy(allocator, (CFStringRef)propertyList);
+        }
+    } else {
+        CFAssert2(false, __kCFLogAssertion, "%s(): %p is not a property list type", __PRETTY_FUNCTION__, propertyList);
+        result = NULL;
+    }
+    CFRelease(allocator);
+    return result;
+}
+