1/* 2 * Copyright (c) 2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* CFOldStylePList.c 25 Copyright (c) 1999-2013, Apple Inc. All rights reserved. 26 Responsibility: Tony Parker 27*/ 28 29#include <CoreFoundation/CFPropertyList.h> 30#include <CoreFoundation/CFDate.h> 31#include <CoreFoundation/CFNumber.h> 32#include <CoreFoundation/CFError.h> 33#include <CoreFoundation/CFStringEncodingConverter.h> 34#include "CFInternal.h" 35#include <CoreFoundation/CFCalendar.h> 36#include <CoreFoundation/CFSet.h> 37 38#include <ctype.h> 39 40// 41// Old NeXT-style property lists 42// 43 44CF_INLINE void __CFPListRelease(CFTypeRef cf, CFAllocatorRef allocator) { 45 if (cf && !(0)) CFRelease(cf); 46} 47 48CF_PRIVATE CFErrorRef __CFPropertyListCreateError(CFIndex code, CFStringRef debugString, ...); 49 50typedef struct { 51 const UniChar *begin; 52 const UniChar *curr; 53 const UniChar *end; 54 CFErrorRef error; 55 CFAllocatorRef allocator; 56 UInt32 mutabilityOption; 57 CFMutableSetRef stringSet; // set of all strings involved in this parse; allows us to share non-mutable strings in the returned plist 58} _CFStringsFileParseInfo; 59 60// warning: doesn't have a good idea of Unicode line separators 61static UInt32 lineNumberStrings(_CFStringsFileParseInfo *pInfo) { 62 const UniChar *p = pInfo->begin; 63 UInt32 count = 1; 64 while (p < pInfo->curr) { 65 if (*p == '\r') { 66 count ++; 67 if (*(p + 1) == '\n') 68 p ++; 69 } else if (*p == '\n') { 70 count ++; 71 } 72 p ++; 73 } 74 return count; 75} 76 77static CFTypeRef parsePlistObject(_CFStringsFileParseInfo *pInfo, bool requireObject); 78 79#define isValidUnquotedStringCharacter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z') || ((x) >= '0' && (x) <= '9') || (x) == '_' || (x) == '$' || (x) == '/' || (x) == ':' || (x) == '.' || (x) == '-') 80 81// Returns true if the advance found something before the end of the buffer, false otherwise 82static Boolean advanceToNonSpace(_CFStringsFileParseInfo *pInfo) { 83 UniChar ch2; 84 while (pInfo->curr < pInfo->end) { 85 ch2 = *(pInfo->curr); 86 pInfo->curr ++; 87 if (ch2 >= 9 && ch2 <= 0x0d) continue; // tab, newline, vt, form feed, carriage return 88 if (ch2 == ' ' || ch2 == 0x2028 || ch2 == 0x2029) continue; // space and Unicode line sep, para sep 89 if (ch2 == '/') { 90 if (pInfo->curr >= pInfo->end) { 91 // whoops; back up and return 92 pInfo->curr --; 93 return true; 94 } else if (*(pInfo->curr) == '/') { 95 pInfo->curr ++; 96 while (pInfo->curr < pInfo->end) { // go to end of comment line 97 UniChar ch3 = *(pInfo->curr); 98 if (ch3 == '\n' || ch3 == '\r' || ch3 == 0x2028 || ch3 == 0x2029) break; 99 pInfo->curr ++; 100 } 101 } else if (*(pInfo->curr) == '*') { // handle /* ... */ 102 pInfo->curr ++; 103 while (pInfo->curr < pInfo->end) { 104 ch2 = *(pInfo->curr); 105 pInfo->curr ++; 106 if (ch2 == '*' && pInfo->curr < pInfo->end && *(pInfo->curr) == '/') { 107 pInfo->curr ++; // advance past the '/' 108 break; 109 } 110 } 111 } else { 112 pInfo->curr --; 113 return true; 114 } 115 } else { 116 pInfo->curr --; 117 return true; 118 } 119 } 120 return false; 121} 122 123static UniChar getSlashedChar(_CFStringsFileParseInfo *pInfo) { 124 UniChar ch = *(pInfo->curr); 125 pInfo->curr ++; 126 switch (ch) { 127 case '0': 128 case '1': 129 case '2': 130 case '3': 131 case '4': 132 case '5': 133 case '6': 134 case '7': { 135 uint8_t num = ch - '0'; 136 UniChar result; 137 CFIndex usedCharLen; 138 /* three digits maximum to avoid reading \000 followed by 5 as \5 ! */ 139 if ((ch = *(pInfo->curr)) >= '0' && ch <= '7') { // we use in this test the fact that the buffer is zero-terminated 140 pInfo->curr ++; 141 num = (num << 3) + ch - '0'; 142 if ((pInfo->curr < pInfo->end) && (ch = *(pInfo->curr)) >= '0' && ch <= '7') { 143 pInfo->curr ++; 144 num = (num << 3) + ch - '0'; 145 } 146 } 147 CFStringEncodingBytesToUnicode(kCFStringEncodingNextStepLatin, 0, &num, sizeof(uint8_t), NULL, &result, 1, &usedCharLen); 148 return (usedCharLen == 1) ? result : 0; 149 } 150 case 'U': { 151 unsigned num = 0, numDigits = 4; /* Parse four digits */ 152 while (pInfo->curr < pInfo->end && numDigits--) { 153 if (((ch = *(pInfo->curr)) < 128) && isxdigit(ch)) { 154 pInfo->curr ++; 155 num = (num << 4) + ((ch <= '9') ? (ch - '0') : ((ch <= 'F') ? (ch - 'A' + 10) : (ch - 'a' + 10))); 156 } 157 } 158 return num; 159 } 160 case 'a': return '\a'; // Note: the meaning of '\a' varies with -traditional to gcc 161 case 'b': return '\b'; 162 case 'f': return '\f'; 163 case 'n': return '\n'; 164 case 'r': return '\r'; 165 case 't': return '\t'; 166 case 'v': return '\v'; 167 case '"': return '\"'; 168 case '\n': return '\n'; 169 } 170 return ch; 171} 172 173static CFStringRef _uniqueStringForCharacters(_CFStringsFileParseInfo *pInfo, const UniChar *base, CFIndex length) { 174 if (0 == length) return !(0) ? (CFStringRef)CFRetain(CFSTR("")) : CFSTR(""); 175 // This is to avoid having to promote the buffers of all the strings compared against 176 // during the set probe; if a Unicode string is passed in, that's what happens. 177 CFStringRef stringToUnique = NULL; 178 Boolean use_stack = (length < 2048); 179 STACK_BUFFER_DECL(uint8_t, buffer, use_stack ? length + 1 : 1); 180 uint8_t *ascii = use_stack ? buffer : (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, length + 1, 0); 181 for (CFIndex idx = 0; idx < length; idx++) { 182 UniChar ch = base[idx]; 183 if (ch < 0x80) { 184 ascii[idx] = (uint8_t)ch; 185 } else { 186 stringToUnique = CFStringCreateWithCharacters(pInfo->allocator, base, length); 187 break; 188 } 189 } 190 if (!stringToUnique) { 191 ascii[length] = '\0'; 192 stringToUnique = CFStringCreateWithBytes(pInfo->allocator, ascii, length, kCFStringEncodingASCII, false); 193 } 194 if (ascii != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ascii); 195 CFStringRef uniqued = (CFStringRef)CFSetGetValue(pInfo->stringSet, stringToUnique); 196 if (!uniqued) { 197 CFSetAddValue(pInfo->stringSet, stringToUnique); 198 uniqued = stringToUnique; 199 } 200 __CFPListRelease(stringToUnique, pInfo->allocator); 201 if (uniqued && !(0)) CFRetain(uniqued); 202 return uniqued; 203} 204 205static CFStringRef _uniqueStringForString(_CFStringsFileParseInfo *pInfo, CFStringRef stringToUnique) { 206 CFStringRef uniqued = (CFStringRef)CFSetGetValue(pInfo->stringSet, stringToUnique); 207 if (!uniqued) { 208 uniqued = (CFStringRef)__CFStringCollectionCopy(pInfo->allocator, stringToUnique); 209 CFSetAddValue(pInfo->stringSet, uniqued); 210 __CFTypeCollectionRelease(pInfo->allocator, uniqued); 211 } 212 if (uniqued && !(0)) CFRetain(uniqued); 213 return uniqued; 214} 215 216static CFStringRef parseQuotedPlistString(_CFStringsFileParseInfo *pInfo, UniChar quote) { 217 CFMutableStringRef str = NULL; 218 const UniChar *startMark = pInfo->curr; 219 const UniChar *mark = pInfo->curr; 220 while (pInfo->curr < pInfo->end) { 221 UniChar ch = *(pInfo->curr); 222 if (ch == quote) break; 223 if (ch == '\\') { 224 if (!str) str = CFStringCreateMutable(pInfo->allocator, 0); 225 CFStringAppendCharacters(str, mark, pInfo->curr - mark); 226 pInfo->curr ++; 227 ch = getSlashedChar(pInfo); 228 CFStringAppendCharacters(str, &ch, 1); 229 mark = pInfo->curr; 230 } else { 231 // Note that the original NSParser code was much more complex at this point, but it had to deal with 8-bit characters in a non-UniChar stream. We always have UniChar (we translated the data by the system encoding at the very beginning, hopefully), so this is safe. 232 pInfo->curr ++; 233 } 234 } 235 if (pInfo->end <= pInfo->curr) { 236 __CFPListRelease(str, pInfo->allocator); 237 pInfo->curr = startMark; 238 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unterminated quoted string starting on line %d"), lineNumberStrings(pInfo)); 239 return NULL; 240 } 241 if (!str) { 242 if (pInfo->mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { 243 str = CFStringCreateMutable(pInfo->allocator, 0); 244 CFStringAppendCharacters(str, mark, pInfo->curr - mark); 245 } else { 246 str = (CFMutableStringRef)_uniqueStringForCharacters(pInfo, mark, pInfo->curr-mark); 247 } 248 } else { 249 if (mark != pInfo->curr) { 250 CFStringAppendCharacters(str, mark, pInfo->curr - mark); 251 } 252 if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) { 253 CFStringRef uniqueString = _uniqueStringForString(pInfo, str); 254 __CFPListRelease(str, pInfo->allocator); 255 str = (CFMutableStringRef)uniqueString; 256 } 257 } 258 pInfo->curr ++; // Advance past the quote character before returning. 259 if (pInfo->error) { 260 CFRelease(pInfo->error); 261 pInfo->error = NULL; 262 } 263 return str; 264} 265 266static CFStringRef parseUnquotedPlistString(_CFStringsFileParseInfo *pInfo) { 267 const UniChar *mark = pInfo->curr; 268 while (pInfo->curr < pInfo->end) { 269 UniChar ch = *pInfo->curr; 270 if (isValidUnquotedStringCharacter(ch)) 271 pInfo->curr ++; 272 else break; 273 } 274 if (pInfo->curr != mark) { 275 if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) { 276 CFStringRef str = _uniqueStringForCharacters(pInfo, mark, pInfo->curr-mark); 277 return str; 278 } else { 279 CFMutableStringRef str = CFStringCreateMutable(pInfo->allocator, 0); 280 CFStringAppendCharacters(str, mark, pInfo->curr - mark); 281 return str; 282 } 283 } 284 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unexpected EOF")); 285 return NULL; 286} 287 288static CFStringRef parsePlistString(_CFStringsFileParseInfo *pInfo, bool requireObject) { 289 UniChar ch; 290 Boolean foundChar = advanceToNonSpace(pInfo); 291 if (!foundChar) { 292 if (requireObject) { 293 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unexpected EOF while parsing string")); 294 } 295 return NULL; 296 } 297 ch = *(pInfo->curr); 298 if (ch == '\'' || ch == '\"') { 299 pInfo->curr ++; 300 return parseQuotedPlistString(pInfo, ch); 301 } else if (isValidUnquotedStringCharacter(ch)) { 302 return parseUnquotedPlistString(pInfo); 303 } else { 304 if (requireObject) { 305 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Invalid string character at line %d"), lineNumberStrings(pInfo)); 306 } 307 return NULL; 308 } 309} 310 311static CFTypeRef parsePlistArray(_CFStringsFileParseInfo *pInfo) { 312 CFMutableArrayRef array = CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks); 313 CFTypeRef tmp = parsePlistObject(pInfo, false); 314 Boolean foundChar; 315 while (tmp) { 316 CFArrayAppendValue(array, tmp); 317 __CFPListRelease(tmp, pInfo->allocator); 318 foundChar = advanceToNonSpace(pInfo); 319 if (!foundChar) { 320 __CFPListRelease(array, pInfo->allocator); 321 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Expected ',' for array at line %d"), lineNumberStrings(pInfo)); 322 return NULL; 323 } 324 if (*pInfo->curr != ',') { 325 tmp = NULL; 326 } else { 327 pInfo->curr ++; 328 tmp = parsePlistObject(pInfo, false); 329 } 330 } 331 foundChar = advanceToNonSpace(pInfo); 332 if (!foundChar || *pInfo->curr != ')') { 333 __CFPListRelease(array, pInfo->allocator); 334 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Expected terminating ')' for array at line %d"), lineNumberStrings(pInfo)); 335 return NULL; 336 } 337 if (pInfo->error) { 338 CFRelease(pInfo->error); 339 pInfo->error = NULL; 340 } 341 pInfo->curr ++; 342 return array; 343} 344 345__attribute__((noinline)) void _CFPropertyListMissingSemicolon(UInt32 line) { 346 CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListCreateFromXMLData(): Old-style plist parser: missing semicolon in dictionary on line %d. Parsing will be abandoned. Break on _CFPropertyListMissingSemicolon to debug."), line); 347} 348 349__attribute__((noinline)) void _CFPropertyListMissingSemicolonOrValue(UInt32 line) { 350 CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListCreateFromXMLData(): Old-style plist parser: missing semicolon or value in dictionary on line %d. Parsing will be abandoned. Break on _CFPropertyListMissingSemicolonOrValue to debug."), line); 351} 352 353static CFDictionaryRef parsePlistDictContent(_CFStringsFileParseInfo *pInfo) { 354 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 355 CFStringRef key = NULL; 356 Boolean failedParse = false; 357 key = parsePlistString(pInfo, false); 358 while (key) { 359 CFTypeRef value; 360 Boolean foundChar = advanceToNonSpace(pInfo); 361 if (!foundChar) { 362 UInt32 line = lineNumberStrings(pInfo); 363 _CFPropertyListMissingSemicolonOrValue(line); 364 failedParse = true; 365 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Missing ';' on line %d"), line); 366 break; 367 } 368 369 if (*pInfo->curr == ';') { 370 /* This is a strings file using the shortcut format */ 371 /* although this check here really applies to all plists. */ 372 value = CFRetain(key); 373 } else if (*pInfo->curr == '=') { 374 pInfo->curr ++; 375 value = parsePlistObject(pInfo, true); 376 if (!value) { 377 failedParse = true; 378 break; 379 } 380 } else { 381 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unexpected ';' or '=' after key at line %d"), lineNumberStrings(pInfo)); 382 failedParse = true; 383 break; 384 } 385 CFDictionarySetValue(dict, key, value); 386 __CFPListRelease(key, pInfo->allocator); 387 key = NULL; 388 __CFPListRelease(value, pInfo->allocator); 389 value = NULL; 390 foundChar = advanceToNonSpace(pInfo); 391 if (foundChar && *pInfo->curr == ';') { 392 pInfo->curr ++; 393 key = parsePlistString(pInfo, false); 394 } else if (true || !foundChar) { 395 UInt32 line = lineNumberStrings(pInfo); 396 _CFPropertyListMissingSemicolon(line); 397 failedParse = true; 398 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Missing ';' on line %d"), line); 399 } 400 } 401 402 if (failedParse) { 403 __CFPListRelease(key, pInfo->allocator); 404 __CFPListRelease(dict, pInfo->allocator); 405 return NULL; 406 } 407 if (pInfo->error) { 408 CFRelease(pInfo->error); 409 pInfo->error = NULL; 410 } 411 return dict; 412} 413 414static CFTypeRef parsePlistDict(_CFStringsFileParseInfo *pInfo) { 415 CFDictionaryRef dict = parsePlistDictContent(pInfo); 416 if (!dict) return NULL; 417 Boolean foundChar = advanceToNonSpace(pInfo); 418 if (!foundChar || *pInfo->curr != '}') { 419 __CFPListRelease(dict, pInfo->allocator); 420 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Expected terminating '}' for dictionary at line %d"), lineNumberStrings(pInfo)); 421 return NULL; 422 } 423 pInfo->curr ++; 424 return dict; 425} 426 427CF_INLINE unsigned char fromHexDigit(unsigned char ch) { 428 if (isdigit(ch)) return ch - '0'; 429 if ((ch >= 'a') && (ch <= 'f')) return ch - 'a' + 10; 430 if ((ch >= 'A') && (ch <= 'F')) return ch - 'A' + 10; 431 return 0xff; // Just choose a large number for the error code 432} 433 434/* Gets up to bytesSize bytes from a plist data. Returns number of bytes actually read. Leaves cursor at first non-space, non-hex character. 435 -1 is returned for unexpected char, -2 for uneven number of hex digits 436 */ 437static int getDataBytes(_CFStringsFileParseInfo *pInfo, unsigned char *bytes, int bytesSize) { 438 int numBytesRead = 0; 439 while ((pInfo->curr < pInfo->end) && (numBytesRead < bytesSize)) { 440 int first, second; 441 UniChar ch1 = *pInfo->curr; 442 if (ch1 == '>') return numBytesRead; // Meaning we're done 443 first = fromHexDigit((unsigned char)ch1); 444 if (first != 0xff) { // If the first char is a hex, then try to read a second hex 445 pInfo->curr++; 446 if (pInfo->curr >= pInfo->end) return -2; // Error: uneven number of hex digits 447 UniChar ch2 = *pInfo->curr; 448 second = fromHexDigit((unsigned char)ch2); 449 if (second == 0xff) return -2; // Error: uneven number of hex digits 450 bytes[numBytesRead++] = (first << 4) + second; 451 pInfo->curr++; 452 } else if (ch1 == ' ' || ch1 == '\n' || ch1 == '\t' || ch1 == '\r' || ch1 == 0x2028 || ch1 == 0x2029) { 453 pInfo->curr++; 454 } else { 455 return -1; // Error: unexpected character 456 } 457 } 458 return numBytesRead; // This does likely mean we didn't encounter a '>', but we'll let the caller deal with that 459} 460 461#define numBytes 400 462static CFTypeRef parsePlistData(_CFStringsFileParseInfo *pInfo) { 463 CFMutableDataRef result = CFDataCreateMutable(pInfo->allocator, 0); 464 465 // Read hex bytes and append them to result 466 while (1) { 467 unsigned char bytes[numBytes]; 468 int numBytesRead = getDataBytes(pInfo, bytes, numBytes); 469 if (numBytesRead < 0) { 470 __CFPListRelease(result, pInfo->allocator); 471 switch (numBytesRead) { 472 case -2: 473 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Malformed data byte group at line %d; uneven length"), lineNumberStrings(pInfo)); 474 break; 475 default: 476 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Malformed data byte group at line %d; invalid hex"), lineNumberStrings(pInfo)); 477 break; 478 } 479 return NULL; 480 } 481 if (numBytesRead == 0) break; 482 CFDataAppendBytes(result, bytes, numBytesRead); 483 } 484 485 if (pInfo->error) { 486 CFRelease(pInfo->error); 487 pInfo->error = NULL; 488 } 489 490 if (*(pInfo->curr) == '>') { 491 pInfo->curr ++; // Move past '>' 492 return result; 493 } else { 494 __CFPListRelease(result, pInfo->allocator); 495 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Expected terminating '>' for data at line %d"), lineNumberStrings(pInfo)); 496 return NULL; 497 } 498} 499#undef numBytes 500 501// Returned object is retained; caller must free. 502static CFTypeRef parsePlistObject(_CFStringsFileParseInfo *pInfo, bool requireObject) { 503 UniChar ch; 504 Boolean foundChar = advanceToNonSpace(pInfo); 505 if (!foundChar) { 506 if (requireObject) { 507 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unexpected EOF while parsing plist")); 508 } 509 return NULL; 510 } 511 ch = *(pInfo->curr); 512 pInfo->curr ++; 513 if (ch == '{') { 514 return parsePlistDict(pInfo); 515 } else if (ch == '(') { 516 return parsePlistArray(pInfo); 517 } else if (ch == '<') { 518 return parsePlistData(pInfo); 519 } else if (ch == '\'' || ch == '\"') { 520 return parseQuotedPlistString(pInfo, ch); 521 } else if (isValidUnquotedStringCharacter(ch)) { 522 pInfo->curr --; 523 return parseUnquotedPlistString(pInfo); 524 } else { 525 pInfo->curr --; // Must back off the charcter we just read 526 if (requireObject) { 527 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unexpected character '0x%x' at line %d"), ch, lineNumberStrings(pInfo)); 528 } 529 return NULL; 530 } 531} 532 533// CFAllocatorRef allocator, CFDataRef xmlData, CFStringRef originalString, CFStringEncoding guessedEncoding, CFOptionFlags option, CFErrorRef *outError, Boolean allowNewTypes, CFPropertyListFormat *format, CFSetRef keyPaths 534 535CF_PRIVATE CFTypeRef __CFCreateOldStylePropertyListOrStringsFile(CFAllocatorRef allocator, CFDataRef xmlData, CFStringRef originalString, CFStringEncoding guessedEncoding, CFOptionFlags option, CFErrorRef *outError,CFPropertyListFormat *format) { 536 537 // Convert the string to UTF16 for parsing old-style 538 if (originalString) { 539 // Ensure that originalString is not collected while we are using it 540 CFRetain(originalString); 541 } else { 542 originalString = CFStringCreateWithBytes(kCFAllocatorSystemDefault, CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData), guessedEncoding, NO); 543 if (!originalString) { 544 // Couldn't convert 545 if (outError) *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Conversion of string failed.")); 546 return NULL; 547 } 548 } 549 550 UInt32 length; 551 Boolean createdBuffer = false; 552 length = CFStringGetLength(originalString); 553 if (!length) { 554 if (outError) *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Conversion of string failed. The string is empty.")); 555 return NULL; 556 } 557 558 UniChar *buf = (UniChar *)CFStringGetCharactersPtr(originalString); 559 if (!buf) { 560 buf = (UniChar *)CFAllocatorAllocate(allocator, length * sizeof(UniChar), 0); 561 if (!buf) { 562 CRSetCrashLogMessage("CFPropertyList ran out of memory while attempting to allocate temporary storage."); 563 return NULL; 564 } 565 CFStringGetCharacters(originalString, CFRangeMake(0, length), buf); 566 createdBuffer = true; 567 } 568 569 _CFStringsFileParseInfo stringsPInfo; 570 stringsPInfo.begin = buf; 571 stringsPInfo.end = buf+length; 572 stringsPInfo.curr = buf; 573 stringsPInfo.allocator = allocator; 574 stringsPInfo.mutabilityOption = option; 575 stringsPInfo.stringSet = CFSetCreateMutable(allocator, 0, &kCFTypeSetCallBacks); 576 stringsPInfo.error = NULL; 577 578 const UniChar *begin = stringsPInfo.curr; 579 CFTypeRef result = NULL; 580 Boolean foundChar = advanceToNonSpace(&stringsPInfo); 581 if (!foundChar) { 582 // A file consisting only of whitespace (or empty) is now defined to be an empty dictionary 583 result = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 584 } else { 585 result = parsePlistObject(&stringsPInfo, true); 586 if (result) { 587 foundChar = advanceToNonSpace(&stringsPInfo); 588 if (foundChar) { 589 if (CFGetTypeID(result) != CFStringGetTypeID()) { 590 __CFPListRelease(result, allocator); 591 result = NULL; 592 if (stringsPInfo.error) CFRelease(stringsPInfo.error); 593 stringsPInfo.error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Junk after plist at line %d"), lineNumberStrings(&stringsPInfo)); 594 } else { 595 // Reset info and keep parsing 596 __CFPListRelease(result, allocator); 597 if (stringsPInfo.error) CFRelease(stringsPInfo.error); 598 stringsPInfo.error = NULL; 599 600 // Check for a strings file (looks like a dictionary without the opening/closing curly braces) 601 stringsPInfo.curr = begin; 602 result = parsePlistDictContent(&stringsPInfo); 603 } 604 } 605 } 606 } 607 608 if (!result) { 609 // Must return some kind of error if requested 610 if (outError) { 611 if (stringsPInfo.error) { 612 // Transfer ownership 613 *outError = stringsPInfo.error; 614 } else { 615 *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unknown error parsing property list around line %d"), lineNumberStrings(&stringsPInfo)); 616 } 617 } else if (stringsPInfo.error) { 618 // Caller doesn't want it, so we need to free it 619 CFRelease(stringsPInfo.error); 620 } 621 } 622 623 if (result && format) *format = kCFPropertyListOpenStepFormat; 624 625 if (createdBuffer && !(0)) CFAllocatorDeallocate(allocator, buf); 626 CFRelease(stringsPInfo.stringSet); 627 CFRelease(originalString); 628 return result; 629} 630 631#undef isValidUnquotedStringCharacter 632