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