2 * Copyright (c) 2014 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1999-2014, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
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>
41 // Old NeXT-style property lists
44 CF_INLINE
void __CFPListRelease(CFTypeRef cf
, CFAllocatorRef allocator
) {
45 if (cf
&& !(0)) CFRelease(cf
);
48 CF_PRIVATE CFErrorRef
__CFPropertyListCreateError(CFIndex code
, CFStringRef debugString
, ...);
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
;
60 // warning: doesn't have a good idea of Unicode line separators
61 static UInt32
lineNumberStrings(_CFStringsFileParseInfo
*pInfo
) {
62 const UniChar
*p
= pInfo
->begin
;
64 while (p
< pInfo
->curr
) {
69 } else if (*p
== '\n') {
77 static CFTypeRef
parsePlistObject(_CFStringsFileParseInfo
*pInfo
, bool requireObject
);
79 #define isValidUnquotedStringCharacter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z') || ((x) >= '0' && (x) <= '9') || (x) == '_' || (x) == '$' || (x) == '/' || (x) == ':' || (x) == '.' || (x) == '-')
81 // Returns true if the advance found something before the end of the buffer, false otherwise
82 static Boolean
advanceToNonSpace(_CFStringsFileParseInfo
*pInfo
) {
84 while (pInfo
->curr
< pInfo
->end
) {
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
90 if (pInfo
->curr
>= pInfo
->end
) {
91 // whoops; back up and return
94 } else if (*(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;
101 } else if (*(pInfo
->curr
) == '*') { // handle /* ... */
103 while (pInfo
->curr
< pInfo
->end
) {
104 ch2
= *(pInfo
->curr
);
106 if (ch2
== '*' && pInfo
->curr
< pInfo
->end
&& *(pInfo
->curr
) == '/') {
107 pInfo
->curr
++; // advance past the '/'
123 static UniChar
getSlashedChar(_CFStringsFileParseInfo
*pInfo
) {
124 UniChar ch
= *(pInfo
->curr
);
135 uint8_t num
= ch
- '0';
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
141 num
= (num
<< 3) + ch
- '0';
142 if ((pInfo
->curr
< pInfo
->end
) && (ch
= *(pInfo
->curr
)) >= '0' && ch
<= '7') {
144 num
= (num
<< 3) + ch
- '0';
147 CFStringEncodingBytesToUnicode(kCFStringEncodingNextStepLatin
, 0, &num
, sizeof(uint8_t), NULL
, &result
, 1, &usedCharLen
);
148 return (usedCharLen
== 1) ? result
: 0;
151 unsigned num
= 0, numDigits
= 4; /* Parse four digits */
152 while (pInfo
->curr
< pInfo
->end
&& numDigits
--) {
153 if (((ch
= *(pInfo
->curr
)) < 128) && isxdigit(ch
)) {
155 num
= (num
<< 4) + ((ch
<= '9') ? (ch
- '0') : ((ch
<= 'F') ? (ch
- 'A' + 10) : (ch
- 'a' + 10)));
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';
173 static 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
];
184 ascii
[idx
] = (uint8_t)ch
;
186 stringToUnique
= CFStringCreateWithCharacters(pInfo
->allocator
, base
, length
);
190 if (!stringToUnique
) {
191 ascii
[length
] = '\0';
192 stringToUnique
= CFStringCreateWithBytes(pInfo
->allocator
, ascii
, length
, kCFStringEncodingASCII
, false);
194 if (ascii
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, ascii
);
195 CFStringRef uniqued
= (CFStringRef
)CFSetGetValue(pInfo
->stringSet
, stringToUnique
);
197 CFSetAddValue(pInfo
->stringSet
, stringToUnique
);
198 uniqued
= stringToUnique
;
200 __CFPListRelease(stringToUnique
, pInfo
->allocator
);
201 if (uniqued
&& !(0)) CFRetain(uniqued
);
205 static CFStringRef
_uniqueStringForString(_CFStringsFileParseInfo
*pInfo
, CFStringRef stringToUnique
) {
206 CFStringRef uniqued
= (CFStringRef
)CFSetGetValue(pInfo
->stringSet
, stringToUnique
);
208 uniqued
= (CFStringRef
)__CFStringCollectionCopy(pInfo
->allocator
, stringToUnique
);
209 CFSetAddValue(pInfo
->stringSet
, uniqued
);
210 __CFTypeCollectionRelease(pInfo
->allocator
, uniqued
);
212 if (uniqued
&& !(0)) CFRetain(uniqued
);
216 static 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;
224 if (!str
) str
= CFStringCreateMutable(pInfo
->allocator
, 0);
225 CFStringAppendCharacters(str
, mark
, pInfo
->curr
- mark
);
227 ch
= getSlashedChar(pInfo
);
228 CFStringAppendCharacters(str
, &ch
, 1);
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.
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
));
242 if (pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
243 str
= CFStringCreateMutable(pInfo
->allocator
, 0);
244 CFStringAppendCharacters(str
, mark
, pInfo
->curr
- mark
);
246 str
= (CFMutableStringRef
)_uniqueStringForCharacters(pInfo
, mark
, pInfo
->curr
-mark
);
249 if (mark
!= pInfo
->curr
) {
250 CFStringAppendCharacters(str
, mark
, pInfo
->curr
- mark
);
252 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
253 CFStringRef uniqueString
= _uniqueStringForString(pInfo
, str
);
254 __CFPListRelease(str
, pInfo
->allocator
);
255 str
= (CFMutableStringRef
)uniqueString
;
258 pInfo
->curr
++; // Advance past the quote character before returning.
260 CFRelease(pInfo
->error
);
266 static 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
))
274 if (pInfo
->curr
!= mark
) {
275 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
276 CFStringRef str
= _uniqueStringForCharacters(pInfo
, mark
, pInfo
->curr
-mark
);
279 CFMutableStringRef str
= CFStringCreateMutable(pInfo
->allocator
, 0);
280 CFStringAppendCharacters(str
, mark
, pInfo
->curr
- mark
);
284 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected EOF"));
288 static CFStringRef
parsePlistString(_CFStringsFileParseInfo
*pInfo
, bool requireObject
) {
290 Boolean foundChar
= advanceToNonSpace(pInfo
);
293 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected EOF while parsing string"));
298 if (ch
== '\'' || ch
== '\"') {
300 return parseQuotedPlistString(pInfo
, ch
);
301 } else if (isValidUnquotedStringCharacter(ch
)) {
302 return parseUnquotedPlistString(pInfo
);
305 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Invalid string character at line %d"), lineNumberStrings(pInfo
));
311 static CFTypeRef
parsePlistArray(_CFStringsFileParseInfo
*pInfo
) {
312 CFMutableArrayRef array
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
313 CFTypeRef tmp
= parsePlistObject(pInfo
, false);
316 CFArrayAppendValue(array
, tmp
);
317 __CFPListRelease(tmp
, pInfo
->allocator
);
318 foundChar
= advanceToNonSpace(pInfo
);
320 __CFPListRelease(array
, pInfo
->allocator
);
321 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Expected ',' for array at line %d"), lineNumberStrings(pInfo
));
324 if (*pInfo
->curr
!= ',') {
328 tmp
= parsePlistObject(pInfo
, false);
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
));
338 CFRelease(pInfo
->error
);
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
);
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
);
353 static 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);
360 Boolean foundChar
= advanceToNonSpace(pInfo
);
362 UInt32 line
= lineNumberStrings(pInfo
);
363 _CFPropertyListMissingSemicolonOrValue(line
);
365 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Missing ';' on line %d"), line
);
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
== '=') {
375 value
= parsePlistObject(pInfo
, true);
381 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected ';' or '=' after key at line %d"), lineNumberStrings(pInfo
));
385 CFDictionarySetValue(dict
, key
, value
);
386 __CFPListRelease(key
, pInfo
->allocator
);
388 __CFPListRelease(value
, pInfo
->allocator
);
390 foundChar
= advanceToNonSpace(pInfo
);
391 if (foundChar
&& *pInfo
->curr
== ';') {
393 key
= parsePlistString(pInfo
, false);
394 } else if (true || !foundChar
) {
395 UInt32 line
= lineNumberStrings(pInfo
);
396 _CFPropertyListMissingSemicolon(line
);
398 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Missing ';' on line %d"), line
);
403 __CFPListRelease(key
, pInfo
->allocator
);
404 __CFPListRelease(dict
, pInfo
->allocator
);
408 CFRelease(pInfo
->error
);
414 static 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
));
427 CF_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
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
437 static int getDataBytes(_CFStringsFileParseInfo
*pInfo
, unsigned char *bytes
, int bytesSize
) {
438 int numBytesRead
= 0;
439 while ((pInfo
->curr
< pInfo
->end
) && (numBytesRead
< bytesSize
)) {
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
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
;
452 } else if (ch1
== ' ' || ch1
== '\n' || ch1
== '\t' || ch1
== '\r' || ch1
== 0x2028 || ch1
== 0x2029) {
455 return -1; // Error: unexpected character
458 return numBytesRead
; // This does likely mean we didn't encounter a '>', but we'll let the caller deal with that
462 static CFTypeRef
parsePlistData(_CFStringsFileParseInfo
*pInfo
) {
463 CFMutableDataRef result
= CFDataCreateMutable(pInfo
->allocator
, 0);
465 // Read hex bytes and append them to result
467 unsigned char bytes
[numBytes
];
468 int numBytesRead
= getDataBytes(pInfo
, bytes
, numBytes
);
469 if (numBytesRead
< 0) {
470 __CFPListRelease(result
, pInfo
->allocator
);
471 switch (numBytesRead
) {
473 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Malformed data byte group at line %d; uneven length"), lineNumberStrings(pInfo
));
476 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Malformed data byte group at line %d; invalid hex"), lineNumberStrings(pInfo
));
481 if (numBytesRead
== 0) break;
482 CFDataAppendBytes(result
, bytes
, numBytesRead
);
486 CFRelease(pInfo
->error
);
490 if (*(pInfo
->curr
) == '>') {
491 pInfo
->curr
++; // Move past '>'
494 __CFPListRelease(result
, pInfo
->allocator
);
495 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Expected terminating '>' for data at line %d"), lineNumberStrings(pInfo
));
501 // Returned object is retained; caller must free.
502 static CFTypeRef
parsePlistObject(_CFStringsFileParseInfo
*pInfo
, bool requireObject
) {
504 Boolean foundChar
= advanceToNonSpace(pInfo
);
507 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected EOF while parsing plist"));
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
)) {
523 return parseUnquotedPlistString(pInfo
);
525 pInfo
->curr
--; // Must back off the charcter we just read
527 pInfo
->error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unexpected character '0x%x' at line %d"), ch
, lineNumberStrings(pInfo
));
533 // CFAllocatorRef allocator, CFDataRef xmlData, CFStringRef originalString, CFStringEncoding guessedEncoding, CFOptionFlags option, CFErrorRef *outError, Boolean allowNewTypes, CFPropertyListFormat *format, CFSetRef keyPaths
535 CF_PRIVATE CFTypeRef
__CFCreateOldStylePropertyListOrStringsFile(CFAllocatorRef allocator
, CFDataRef xmlData
, CFStringRef originalString
, CFStringEncoding guessedEncoding
, CFOptionFlags option
, CFErrorRef
*outError
,CFPropertyListFormat
*format
) {
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
);
542 originalString
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, CFDataGetBytePtr(xmlData
), CFDataGetLength(xmlData
), guessedEncoding
, NO
);
543 if (!originalString
) {
545 if (outError
) *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Conversion of string failed."));
551 Boolean createdBuffer
= false;
552 length
= CFStringGetLength(originalString
);
554 if (outError
) *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Conversion of string failed. The string is empty."));
558 UniChar
*buf
= (UniChar
*)CFStringGetCharactersPtr(originalString
);
560 buf
= (UniChar
*)CFAllocatorAllocate(allocator
, length
* sizeof(UniChar
), 0);
562 CRSetCrashLogMessage("CFPropertyList ran out of memory while attempting to allocate temporary storage.");
565 CFStringGetCharacters(originalString
, CFRangeMake(0, length
), buf
);
566 createdBuffer
= true;
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
;
578 const UniChar
*begin
= stringsPInfo
.curr
;
579 CFTypeRef result
= NULL
;
580 Boolean foundChar
= advanceToNonSpace(&stringsPInfo
);
582 // A file consisting only of whitespace (or empty) is now defined to be an empty dictionary
583 result
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
585 result
= parsePlistObject(&stringsPInfo
, true);
587 foundChar
= advanceToNonSpace(&stringsPInfo
);
589 if (CFGetTypeID(result
) != CFStringGetTypeID()) {
590 __CFPListRelease(result
, allocator
);
592 if (stringsPInfo
.error
) CFRelease(stringsPInfo
.error
);
593 stringsPInfo
.error
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Junk after plist at line %d"), lineNumberStrings(&stringsPInfo
));
595 // Reset info and keep parsing
596 __CFPListRelease(result
, allocator
);
597 if (stringsPInfo
.error
) CFRelease(stringsPInfo
.error
);
598 stringsPInfo
.error
= NULL
;
600 // Check for a strings file (looks like a dictionary without the opening/closing curly braces)
601 stringsPInfo
.curr
= begin
;
602 result
= parsePlistDictContent(&stringsPInfo
);
609 // Must return some kind of error if requested
611 if (stringsPInfo
.error
) {
612 // Transfer ownership
613 *outError
= stringsPInfo
.error
;
615 *outError
= __CFPropertyListCreateError(kCFPropertyListReadCorruptError
, CFSTR("Unknown error parsing property list around line %d"), lineNumberStrings(&stringsPInfo
));
617 } else if (stringsPInfo
.error
) {
618 // Caller doesn't want it, so we need to free it
619 CFRelease(stringsPInfo
.error
);
623 if (result
&& format
) *format
= kCFPropertyListOpenStepFormat
;
625 if (createdBuffer
&& !(0)) CFAllocatorDeallocate(allocator
, buf
);
626 CFRelease(stringsPInfo
.stringSet
);
627 CFRelease(originalString
);
631 #undef isValidUnquotedStringCharacter