]> git.saurik.com Git - apple/cf.git/blob - CFOldStylePList.c
CF-1152.14.tar.gz
[apple/cf.git] / CFOldStylePList.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 /* CFOldStylePList.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/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
44 CF_INLINE void __CFPListRelease(CFTypeRef cf, CFAllocatorRef allocator) {
45 if (cf && !(0)) CFRelease(cf);
46 }
47
48 CF_PRIVATE CFErrorRef __CFPropertyListCreateError(CFIndex code, CFStringRef debugString, ...);
49
50 typedef 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
61 static 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
77 static 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
82 static 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
123 static 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
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];
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
205 static 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
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;
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
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))
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
288 static 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
311 static 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
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);
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
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));
421 return NULL;
422 }
423 pInfo->curr ++;
424 return dict;
425 }
426
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
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 */
437 static 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
462 static 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.
502 static 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
535 CF_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