X-Git-Url: https://git.saurik.com/apple/cf.git/blobdiff_plain/47a9ab1f151d80a00a045f81937ddac81c51a463..bd5b749cf7786ae858ab372fc8f64179736c6515:/CFPropertyList.c diff --git a/CFPropertyList.c b/CFPropertyList.c new file mode 100644 index 0000000..e3a5392 --- /dev/null +++ b/CFPropertyList.c @@ -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 +#include +#include +#include +#include "CFPriv.h" +#include "CFStringEncodingConverter.h" +#include "CFInternal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#if DEPLOYMENT_TARGET_MACOSX +#include +#include +#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, "<"); + break; + case '>': + if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt); + bufCnt = 0; + _plistAppendUTF8CString(mStr, ">"); + break; + case '&': + if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt); + bufCnt = 0; + _plistAppendUTF8CString(mStr, "&"); + 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, "\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, "\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, "\n"); + _appendIndents(indentation, xmlString); + _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, "\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, "\n"); + _CFAppendXML0(CFDictionaryGetValue((CFDictionaryRef)object, key), indentation+1, xmlString); + } + CFAllocatorDeallocate(kCFAllocatorSystemDefault, keys); + _appendIndents(indentation, xmlString); + _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, "\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, "\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, "\n"); + } else { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + + _plistAppendFormat(xmlString, CFSTR("%@"), object); + + _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, "\n\n<"); + _plistAppendCharacters(xml, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH); + _plistAppendUTF8CString(xml, " version=\"1.0\">\n"); + + _CFAppendXML0(propertyList, 0, xml); + + _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 "