/* * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * 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 "CFUtilities.h" #include "CFStringEncodingConverter.h" #include "CFInternal.h" #include #include #include #include #include #if defined(__MACH__) || defined(__WIN32__) #include #elif !defined(__WIN32__) #define isspace(x) ((x)==' ' || (x)=='\n' || (x)=='\f' || (x)=='\r' || (x)=='\t' || (x)=='\v') #define isdigit(x) ((x) <= '9' && (x) >= '0') #define isxdigit(x) (((x) <= '9' && (x) >= '0') || ((x) >= 'a' && (x) <= 'f') || ((x) >= 'A' && (x) <= 'F')) #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(): 0x%x not of a property list type", __PRETTY_FUNCTION__, (UInt32)cf); static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, CFMutableSetRef set, CFPropertyListFormat format); 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(0, 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(0, CFSTR("CFPropertyListIsValid(): property list dictionaries cannot contain NULL keys")); if (!value) CFLog(0, CFSTR("CFPropertyListIsValid(): property list dictionaries cannot contain NULL values")); if (CFStringGetTypeID() != CFGetTypeID(key)) { CFStringRef desc = CFCopyTypeIDDescription(CFGetTypeID(key)); CFLog(0, CFSTR("CFPropertyListIsValid(): property list dictionaries may only have keys which are CFStrings, not '%@'"), desc); CFRelease(desc); } #endif ctx->answer = key && value && (CFStringGetTypeID() == 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(0, CFSTR("CFPropertyListIsValid(): property lists cannot contain NULL")); #endif if (!plist) return false; type = CFGetTypeID(plist); if (CFStringGetTypeID() == type) return true; if (CFDataGetTypeID() == type) return true; if (kCFPropertyListOpenStepFormat != format) { if (CFBooleanGetTypeID() == type) return true; if (CFNumberGetTypeID() == type) return true; if (CFDateGetTypeID() == type) return true; if (_CFKeyedArchiverUIDGetTypeID() == type) return true; } if (!recursive && CFArrayGetTypeID() == type) return true; if (!recursive && CFDictionaryGetTypeID() == 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(0, CFSTR("CFPropertyListIsValid(): property lists cannot contain recursive container references")); #endif if (CFSetContainsValue(set, plist)) return false; if (CFArrayGetTypeID() == type) { struct context ctx = {true, set, format}; CFSetAddValue(set, plist); CFArrayApplyFunction(plist, CFRangeMake(0, CFArrayGetCount(plist)), __CFPropertyListIsArrayPlistAux, &ctx); CFSetRemoveValue(set, plist); return ctx.answer; } if (CFDictionaryGetTypeID() == type) { struct context ctx = {true, set, format}; CFSetAddValue(set, plist); CFDictionaryApplyFunction(plist, __CFPropertyListIsDictPlistAux, &ctx); CFSetRemoveValue(set, plist); return ctx.answer; } #if defined(DEBUG) { CFStringRef desc = CFCopyTypeIDDescription(type); CFLog(0, CFSTR("CFPropertyListIsValid(): property lists cannot contain objects of type '%@'"), desc); CFRelease(desc); } #endif return false; } Boolean CFPropertyListIsValid(CFPropertyListRef plist, CFPropertyListFormat format) { CFMutableSetRef set; bool result; CFAssert1(plist != NULL, __kCFLogAssertion, "%s(): NULL is not a property list", __PRETTY_FUNCTION__); set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL); 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(NULL, chars + curLoc, length - curLoc, kCFAllocatorNull))) { if ((data = CFStringCreateExternalRepresentation(NULL, 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(NULL, 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(NULL, 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 == CFStringGetTypeID()) { _plistAppendUTF8CString(xmlString, "<"); _plistAppendCharacters(xmlString, CFXMLPlistTags[STRING_IX], STRING_TAG_LENGTH); _plistAppendUTF8CString(xmlString, ">"); _appendEscapedString(object, xmlString); _plistAppendUTF8CString(xmlString, "\n"); } else if (typeID == _CFKeyedArchiverUIDGetTypeID()) { uint64_t v = _CFKeyedArchiverUIDGetValue(object); CFNumberRef num = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt64Type, &v); _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"); _CFAppendXML0(num, indentation+1, xmlString); _appendIndents(indentation, xmlString); _plistAppendUTF8CString(xmlString, "\n"); } else if (typeID == CFArrayGetTypeID()) { UInt32 i, count = CFArrayGetCount(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(object, i), indentation+1, xmlString); } _appendIndents(indentation, xmlString); _plistAppendUTF8CString(xmlString, "\n"); } else if (typeID == CFDictionaryGetTypeID()) { UInt32 i, count = CFDictionaryGetCount(object); CFAllocatorRef allocator = CFGetAllocator(xmlString); 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(allocator, count * sizeof(CFTypeRef), 0); CFDictionaryGetKeysAndValues(object, keys, NULL); keyArray = CFArrayCreateMutable(allocator, 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(key, xmlString); _plistAppendUTF8CString(xmlString, "\n"); _CFAppendXML0(CFDictionaryGetValue(object, key), indentation+1, xmlString); } CFAllocatorDeallocate(allocator, keys); _appendIndents(indentation, xmlString); _plistAppendUTF8CString(xmlString, "\n"); } else if (typeID == CFDataGetTypeID()) { _plistAppendUTF8CString(xmlString, "<"); _plistAppendCharacters(xmlString, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH); _plistAppendUTF8CString(xmlString, ">\n"); _XMLPlistAppendDataUsingBase64(xmlString, object, indentation); _appendIndents(indentation, xmlString); _plistAppendUTF8CString(xmlString, "\n"); } else if (typeID == CFDateGetTypeID()) { // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z' CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime(object), NULL); _plistAppendUTF8CString(xmlString, "<"); _plistAppendCharacters(xmlString, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH); _plistAppendUTF8CString(xmlString, ">"); _plistAppendFormat(xmlString, CFSTR("%04d-%02d-%02dT%02d:%02d:%02dZ"), date.year, date.month, date.day, date.hour, date.minute, (int)date.second); _plistAppendUTF8CString(xmlString, "\n"); } else if (typeID == CFNumberGetTypeID()) { if (CFNumberIsFloatType(object)) { _plistAppendUTF8CString(xmlString, "<"); _plistAppendCharacters(xmlString, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH); _plistAppendUTF8CString(xmlString, ">"); if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { CFStringRef s = __CFNumberCopyFormattingDescriptionAsFloat64(object); _plistAppendString(xmlString, s); CFRelease(s); } else if (CFNumberGetType(object) == kCFNumberFloat64Type || CFNumberGetType(object) == kCFNumberDoubleType) { double doubleVal; static CFStringRef doubleFormatString = NULL; CFNumberGetValue(object, kCFNumberDoubleType, &doubleVal); if (!doubleFormatString) { doubleFormatString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%%.%de"), DBL_DIG); } _plistAppendFormat(xmlString, doubleFormatString, doubleVal); } else { float floatVal; static CFStringRef floatFormatString = NULL; CFNumberGetValue(object, kCFNumberFloatType, &floatVal); if (!floatFormatString) { floatFormatString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%%.%de"), FLT_DIG); } _plistAppendFormat(xmlString, floatFormatString, floatVal); } _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 == CFBooleanGetTypeID()) { if (CFBooleanGetValue(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) { CFMutableDataRef xml; CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__); __CFAssertIsPList(propertyList); if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { if (!CFPropertyListIsValid(propertyList, kCFPropertyListXMLFormat_v1_0)) return NULL; } xml = CFDataCreateMutable(allocator, 0); _CFGenerateXMLPropertyListToData(xml, propertyList); return xml; } CFDataRef _CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator, CFPropertyListRef propertyList) { 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 "