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