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