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