2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 Copyright 1999-2002, Apple, Inc. All rights reserved.
27 Responsibility: Christopher Kane
30 #include <CoreFoundation/CFPropertyList.h>
31 #include <CoreFoundation/CFDate.h>
32 #include <CoreFoundation/CFNumber.h>
33 #include <CoreFoundation/CFSet.h>
34 #include "CFUtilities.h"
35 #include "CFStringEncodingConverter.h"
36 #include "CFInternal.h"
42 #if defined(__MACH__) || defined(__WIN32__)
44 #elif !defined(__WIN32__)
45 #define isspace(x) ((x)==' ' || (x)=='\n' || (x)=='\f' || (x)=='\r' || (x)=='\t' || (x)=='\v')
46 #define isdigit(x) ((x) <= '9' && (x) >= '0')
47 #define isxdigit(x) (((x) <= '9' && (x) >= '0') || ((x) >= 'a' && (x) <= 'f') || ((x) >= 'A' && (x) <= 'F'))
50 __private_extern__
bool allowMissingSemi
= false;
52 // Should move this somewhere else
53 intptr_t _CFDoOperation(intptr_t code
, intptr_t subcode1
, intptr_t subcode2
) {
55 case 15317: allowMissingSemi
= subcode1
? true : false; break;
74 #define PLIST_TAG_LENGTH 5
75 #define ARRAY_TAG_LENGTH 5
76 #define DICT_TAG_LENGTH 4
77 #define KEY_TAG_LENGTH 3
78 #define STRING_TAG_LENGTH 6
79 #define DATA_TAG_LENGTH 4
80 #define DATE_TAG_LENGTH 4
81 #define REAL_TAG_LENGTH 4
82 #define INTEGER_TAG_LENGTH 7
83 #define TRUE_TAG_LENGTH 4
84 #define FALSE_TAG_LENGTH 5
85 #define DOCTYPE_TAG_LENGTH 7
86 #define CDSECT_TAG_LENGTH 9
88 // don't allow _CFKeyedArchiverUID here
89 #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(): 0x%x not of a property list type", __PRETTY_FUNCTION__, (UInt32)cf);
91 static bool __CFPropertyListIsValidAux(CFPropertyListRef plist
, bool recursive
, CFMutableSetRef set
, CFPropertyListFormat format
);
96 CFPropertyListFormat format
;
99 static void __CFPropertyListIsArrayPlistAux(const void *value
, void *context
) {
100 struct context
*ctx
= (struct context
*)context
;
101 if (!ctx
->answer
) return;
103 if (!value
) CFLog(0, CFSTR("CFPropertyListIsValid(): property list arrays cannot contain NULL"));
105 ctx
->answer
= value
&& __CFPropertyListIsValidAux(value
, true, ctx
->set
, ctx
->format
);
108 static void __CFPropertyListIsDictPlistAux(const void *key
, const void *value
, void *context
) {
109 struct context
*ctx
= (struct context
*)context
;
110 if (!ctx
->answer
) return;
112 if (!key
) CFLog(0, CFSTR("CFPropertyListIsValid(): property list dictionaries cannot contain NULL keys"));
113 if (!value
) CFLog(0, CFSTR("CFPropertyListIsValid(): property list dictionaries cannot contain NULL values"));
114 if (CFStringGetTypeID() != CFGetTypeID(key
)) {
115 CFStringRef desc
= CFCopyTypeIDDescription(CFGetTypeID(key
));
116 CFLog(0, CFSTR("CFPropertyListIsValid(): property list dictionaries may only have keys which are CFStrings, not '%@'"), desc
);
120 ctx
->answer
= key
&& value
&& (CFStringGetTypeID() == CFGetTypeID(key
)) && __CFPropertyListIsValidAux(value
, true, ctx
->set
, ctx
->format
);
123 static bool __CFPropertyListIsValidAux(CFPropertyListRef plist
, bool recursive
, CFMutableSetRef set
, CFPropertyListFormat format
) {
126 if (!plist
) CFLog(0, CFSTR("CFPropertyListIsValid(): property lists cannot contain NULL"));
128 if (!plist
) return false;
129 type
= CFGetTypeID(plist
);
130 if (CFStringGetTypeID() == type
) return true;
131 if (CFDataGetTypeID() == type
) return true;
132 if (kCFPropertyListOpenStepFormat
!= format
) {
133 if (CFBooleanGetTypeID() == type
) return true;
134 if (CFNumberGetTypeID() == type
) return true;
135 if (CFDateGetTypeID() == type
) return true;
136 if (_CFKeyedArchiverUIDGetTypeID() == type
) return true;
138 if (!recursive
&& CFArrayGetTypeID() == type
) return true;
139 if (!recursive
&& CFDictionaryGetTypeID() == type
) return true;
140 // at any one invocation of this function, set should contain the objects in the "path" down to this object
142 if (CFSetContainsValue(set
, plist
)) CFLog(0, CFSTR("CFPropertyListIsValid(): property lists cannot contain recursive container references"));
144 if (CFSetContainsValue(set
, plist
)) return false;
145 if (CFArrayGetTypeID() == type
) {
146 struct context ctx
= {true, set
, format
};
147 CFSetAddValue(set
, plist
);
148 CFArrayApplyFunction(plist
, CFRangeMake(0, CFArrayGetCount(plist
)), __CFPropertyListIsArrayPlistAux
, &ctx
);
149 CFSetRemoveValue(set
, plist
);
152 if (CFDictionaryGetTypeID() == type
) {
153 struct context ctx
= {true, set
, format
};
154 CFSetAddValue(set
, plist
);
155 CFDictionaryApplyFunction(plist
, __CFPropertyListIsDictPlistAux
, &ctx
);
156 CFSetRemoveValue(set
, plist
);
161 CFStringRef desc
= CFCopyTypeIDDescription(type
);
162 CFLog(0, CFSTR("CFPropertyListIsValid(): property lists cannot contain objects of type '%@'"), desc
);
169 Boolean
CFPropertyListIsValid(CFPropertyListRef plist
, CFPropertyListFormat format
) {
172 CFAssert1(plist
!= NULL
, __kCFLogAssertion
, "%s(): NULL is not a property list", __PRETTY_FUNCTION__
);
173 set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
174 result
= __CFPropertyListIsValidAux(plist
, true, set
, format
);
179 static const UniChar CFXMLPlistTags
[13][10]= {
180 {'p', 'l', 'i', 's', 't', '\0', '\0', '\0', '\0', '\0'},
181 {'a', 'r', 'r', 'a', 'y', '\0', '\0', '\0', '\0', '\0'},
182 {'d', 'i', 'c', 't', '\0', '\0', '\0', '\0', '\0', '\0'},
183 {'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'},
184 {'s', 't', 'r', 'i', 'n', 'g', '\0', '\0', '\0', '\0'},
185 {'d', 'a', 't', 'a', '\0', '\0', '\0', '\0', '\0', '\0'},
186 {'d', 'a', 't', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
187 {'r', 'e', 'a', 'l', '\0', '\0', '\0', '\0', '\0', '\0'},
188 {'i', 'n', 't', 'e', 'g', 'e', 'r', '\0', '\0', '\0'},
189 {'t', 'r', 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0'},
190 {'f', 'a', 'l', 's', 'e', '\0', '\0', '\0', '\0', '\0'},
191 {'D', 'O', 'C', 'T', 'Y', 'P', 'E', '\0', '\0', '\0'},
192 {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[', '\0'}
196 const UniChar
*begin
; // first character of the XML to be parsed
197 const UniChar
*curr
; // current parse location
198 const UniChar
*end
; // the first character _after_ the end of the XML
199 CFStringRef errorString
;
200 CFAllocatorRef allocator
;
201 UInt32 mutabilityOption
;
202 CFMutableSetRef stringSet
; // set of all strings involved in this parse; allows us to share non-mutable strings in the returned plist
203 CFMutableStringRef tmpString
; // Mutable string with external characters that functions can feel free to use as temporary storage as the parse progresses
204 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)
206 } _CFXMLPlistParseInfo
;
208 static CFTypeRef
parseOldStylePropertyListOrStringsFile(_CFXMLPlistParseInfo
*pInfo
);
212 // 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.
214 // Null-terminated, ASCII or UTF8 string
216 static void _plistAppendUTF8CString(CFMutableDataRef mData
, const char *cString
) {
217 CFDataAppendBytes (mData
, (const UInt8
*)cString
, strlen(cString
));
222 static void _plistAppendCharacters(CFMutableDataRef mData
, const UniChar
*chars
, CFIndex length
) {
225 do { // Flush out ASCII chars, BUFLEN at a time
227 UInt8 buf
[BUFLEN
], *bufPtr
= buf
;
229 while (cnt
< length
&& (cnt
- curLoc
< BUFLEN
) && (chars
[cnt
] < 128)) *bufPtr
++ = (UInt8
)(chars
[cnt
++]);
230 if (cnt
> curLoc
) { // Flush any ASCII bytes
231 CFDataAppendBytes(mData
, buf
, cnt
- curLoc
);
234 } while (curLoc
< length
&& (chars
[curLoc
] < 128)); // We will exit out of here when we run out of chars or hit a non-ASCII char
236 if (curLoc
< length
) { // Now deal with non-ASCII chars
237 CFDataRef data
= NULL
;
238 CFStringRef str
= NULL
;
239 if ((str
= CFStringCreateWithCharactersNoCopy(NULL
, chars
+ curLoc
, length
- curLoc
, kCFAllocatorNull
))) {
240 if ((data
= CFStringCreateExternalRepresentation(NULL
, str
, kCFStringEncodingUTF8
, 0))) {
241 CFDataAppendBytes (mData
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
246 CFAssert1(str
&& data
, __kCFLogAssertion
, "%s(): Error writing plist", __PRETTY_FUNCTION__
);
252 static void _plistAppendString(CFMutableDataRef mData
, CFStringRef str
) {
253 const UniChar
*chars
;
256 if ((chars
= CFStringGetCharactersPtr(str
))) {
257 _plistAppendCharacters(mData
, chars
, CFStringGetLength(str
));
258 } else if ((cStr
= CFStringGetCStringPtr(str
, kCFStringEncodingASCII
)) || (cStr
= CFStringGetCStringPtr(str
, kCFStringEncodingUTF8
))) {
259 _plistAppendUTF8CString(mData
, cStr
);
260 } else if ((data
= CFStringCreateExternalRepresentation(NULL
, str
, kCFStringEncodingUTF8
, 0))) {
261 CFDataAppendBytes (mData
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
264 CFAssert1(TRUE
, __kCFLogAssertion
, "%s(): Error in plist writing", __PRETTY_FUNCTION__
);
269 // Append CFString-style format + arguments
271 static void _plistAppendFormat(CFMutableDataRef mData
, CFStringRef format
, ...) {
275 va_start(argList
, format
);
276 fStr
= CFStringCreateWithFormatAndArguments(NULL
, NULL
, format
, argList
);
279 CFAssert1(fStr
, __kCFLogAssertion
, "%s(): Error writing plist", __PRETTY_FUNCTION__
);
280 _plistAppendString(mData
, fStr
);
286 static void _appendIndents(CFIndex numIndents
, CFMutableDataRef str
) {
288 static const UniChar tabs
[NUMTABS
] = {'\t','\t','\t','\t'};
289 for (; numIndents
> 0; numIndents
-= NUMTABS
) _plistAppendCharacters(str
, tabs
, (numIndents
>= NUMTABS
) ? NUMTABS
: numIndents
);
292 /* Append the escaped version of origStr to mStr.
294 static void _appendEscapedString(CFStringRef origStr
, CFMutableDataRef mStr
) {
296 CFIndex i
, length
= CFStringGetLength(origStr
);
298 UniChar buf
[BUFSIZE
];
299 CFStringInlineBuffer inlineBuffer
;
301 CFStringInitInlineBuffer(origStr
, &inlineBuffer
, CFRangeMake(0, length
));
303 for (i
= 0; i
< length
; i
++) {
304 UniChar ch
= __CFStringGetCharacterFromInlineBufferQuick(&inlineBuffer
, i
);
307 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
309 _plistAppendUTF8CString(mStr
, "<");
312 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
314 _plistAppendUTF8CString(mStr
, ">");
317 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
319 _plistAppendUTF8CString(mStr
, "&");
323 if (bufCnt
== BUFSIZE
) {
324 _plistAppendCharacters(mStr
, buf
, bufCnt
);
330 if (bufCnt
) _plistAppendCharacters(mStr
, buf
, bufCnt
);
335 /* Base-64 encoding/decoding */
337 /* The base-64 encoding packs three 8-bit bytes into four 7-bit ASCII
338 * characters. If the number of bytes in the original data isn't divisable
339 * by three, "=" characters are used to pad the encoded data. The complete
340 * set of characters used in base-64 are:
350 // Write the inputData to the mData using Base 64 encoding
352 static void _XMLPlistAppendDataUsingBase64(CFMutableDataRef mData
, CFDataRef inputData
, CFIndex indent
) {
353 static const char __CFPLDataEncodeTable
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
354 #define MAXLINELEN 76
355 char buf
[MAXLINELEN
+ 4 + 2]; // For the slop and carriage return and terminating NULL
357 const uint8_t *bytes
= CFDataGetBytePtr(inputData
);
358 CFIndex length
= CFDataGetLength(inputData
);
362 if (indent
> 8) indent
= 8; // refuse to indent more than 64 characters
364 pos
= 0; // position within buf
366 for (i
= 0, p
= bytes
; i
< length
; i
++, p
++) {
367 /* 3 bytes are encoded as 4 */
370 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[0] >> 2) & 0x3f)];
373 buf
[pos
++] = __CFPLDataEncodeTable
[ ((((p
[-1] << 8) | p
[0]) >> 4) & 0x3f)];
376 buf
[pos
++] = __CFPLDataEncodeTable
[ ((((p
[-1] << 8) | p
[0]) >> 6) & 0x3f)];
377 buf
[pos
++] = __CFPLDataEncodeTable
[ (p
[0] & 0x3f)];
380 /* Flush the line out every 76 (or fewer) chars --- indents count against the line length*/
381 if (pos
>= MAXLINELEN
- 8 * indent
) {
384 _appendIndents(indent
, mData
);
385 _plistAppendUTF8CString(mData
, buf
);
394 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[-1] << 4) & 0x30)];
399 buf
[pos
++] = __CFPLDataEncodeTable
[ ((p
[-1] << 2) & 0x3c)];
407 _appendIndents(indent
, mData
);
408 _plistAppendUTF8CString(mData
, buf
);
412 extern CFStringRef
__CFNumberCopyFormattingDescriptionAsFloat64(CFTypeRef cf
);
414 static void _CFAppendXML0(CFTypeRef object
, UInt32 indentation
, CFMutableDataRef xmlString
) {
415 UInt32 typeID
= CFGetTypeID(object
);
416 _appendIndents(indentation
, xmlString
);
417 if (typeID
== CFStringGetTypeID()) {
418 _plistAppendUTF8CString(xmlString
, "<");
419 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[STRING_IX
], STRING_TAG_LENGTH
);
420 _plistAppendUTF8CString(xmlString
, ">");
421 _appendEscapedString(object
, xmlString
);
422 _plistAppendUTF8CString(xmlString
, "</");
423 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[STRING_IX
], STRING_TAG_LENGTH
);
424 _plistAppendUTF8CString(xmlString
, ">\n");
425 } else if (typeID
== _CFKeyedArchiverUIDGetTypeID()) {
426 uint64_t v
= _CFKeyedArchiverUIDGetValue(object
);
427 CFNumberRef num
= CFNumberCreate(kCFAllocatorSystemDefault
, kCFNumberSInt64Type
, &v
);
428 _plistAppendUTF8CString(xmlString
, "<");
429 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
430 _plistAppendUTF8CString(xmlString
, ">\n");
431 _appendIndents(indentation
+1, xmlString
);
432 _plistAppendUTF8CString(xmlString
, "<");
433 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
);
434 _plistAppendUTF8CString(xmlString
, ">");
435 _appendEscapedString(CFSTR("CF$UID"), xmlString
);
436 _plistAppendUTF8CString(xmlString
, "</");
437 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
);
438 _plistAppendUTF8CString(xmlString
, ">\n");
439 _CFAppendXML0(num
, indentation
+1, xmlString
);
440 _appendIndents(indentation
, xmlString
);
441 _plistAppendUTF8CString(xmlString
, "</");
442 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
443 _plistAppendUTF8CString(xmlString
, ">\n");
444 } else if (typeID
== CFArrayGetTypeID()) {
445 UInt32 i
, count
= CFArrayGetCount(object
);
447 _plistAppendUTF8CString(xmlString
, "<");
448 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
449 _plistAppendUTF8CString(xmlString
, "/>\n");
452 _plistAppendUTF8CString(xmlString
, "<");
453 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
454 _plistAppendUTF8CString(xmlString
, ">\n");
455 for (i
= 0; i
< count
; i
++) {
456 _CFAppendXML0(CFArrayGetValueAtIndex(object
, i
), indentation
+1, xmlString
);
458 _appendIndents(indentation
, xmlString
);
459 _plistAppendUTF8CString(xmlString
, "</");
460 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
);
461 _plistAppendUTF8CString(xmlString
, ">\n");
462 } else if (typeID
== CFDictionaryGetTypeID()) {
463 UInt32 i
, count
= CFDictionaryGetCount(object
);
464 CFAllocatorRef allocator
= CFGetAllocator(xmlString
);
465 CFMutableArrayRef keyArray
;
468 _plistAppendUTF8CString(xmlString
, "<");
469 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
470 _plistAppendUTF8CString(xmlString
, "/>\n");
473 _plistAppendUTF8CString(xmlString
, "<");
474 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
475 _plistAppendUTF8CString(xmlString
, ">\n");
476 keys
= (CFTypeRef
*)CFAllocatorAllocate(allocator
, count
* sizeof(CFTypeRef
), 0);
477 CFDictionaryGetKeysAndValues(object
, keys
, NULL
);
478 keyArray
= CFArrayCreateMutable(allocator
, count
, &kCFTypeArrayCallBacks
);
479 CFArrayReplaceValues(keyArray
, CFRangeMake(0, 0), keys
, count
);
480 CFArraySortValues(keyArray
, CFRangeMake(0, count
), (CFComparatorFunction
)CFStringCompare
, NULL
);
481 CFArrayGetValues(keyArray
, CFRangeMake(0, count
), keys
);
483 for (i
= 0; i
< count
; i
++) {
484 CFTypeRef key
= keys
[i
];
485 _appendIndents(indentation
+1, xmlString
);
486 _plistAppendUTF8CString(xmlString
, "<");
487 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
);
488 _plistAppendUTF8CString(xmlString
, ">");
489 _appendEscapedString(key
, xmlString
);
490 _plistAppendUTF8CString(xmlString
, "</");
491 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
);
492 _plistAppendUTF8CString(xmlString
, ">\n");
493 _CFAppendXML0(CFDictionaryGetValue(object
, key
), indentation
+1, xmlString
);
495 CFAllocatorDeallocate(allocator
, keys
);
496 _appendIndents(indentation
, xmlString
);
497 _plistAppendUTF8CString(xmlString
, "</");
498 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
);
499 _plistAppendUTF8CString(xmlString
, ">\n");
500 } else if (typeID
== CFDataGetTypeID()) {
501 _plistAppendUTF8CString(xmlString
, "<");
502 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
);
503 _plistAppendUTF8CString(xmlString
, ">\n");
504 _XMLPlistAppendDataUsingBase64(xmlString
, object
, indentation
);
505 _appendIndents(indentation
, xmlString
);
506 _plistAppendUTF8CString(xmlString
, "</");
507 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
);
508 _plistAppendUTF8CString(xmlString
, ">\n");
509 } else if (typeID
== CFDateGetTypeID()) {
510 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
511 CFGregorianDate date
= CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime(object
), NULL
);
513 _plistAppendUTF8CString(xmlString
, "<");
514 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
);
515 _plistAppendUTF8CString(xmlString
, ">");
517 _plistAppendFormat(xmlString
, CFSTR("%04d-%02d-%02dT%02d:%02d:%02dZ"), date
.year
, date
.month
, date
.day
, date
.hour
, date
.minute
, (int)date
.second
);
519 _plistAppendUTF8CString(xmlString
, "</");
520 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
);
521 _plistAppendUTF8CString(xmlString
, ">\n");
522 } else if (typeID
== CFNumberGetTypeID()) {
523 if (CFNumberIsFloatType(object
)) {
524 _plistAppendUTF8CString(xmlString
, "<");
525 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
);
526 _plistAppendUTF8CString(xmlString
, ">");
528 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
529 CFStringRef s
= __CFNumberCopyFormattingDescriptionAsFloat64(object
);
530 _plistAppendString(xmlString
, s
);
532 } else if (CFNumberGetType(object
) == kCFNumberFloat64Type
|| CFNumberGetType(object
) == kCFNumberDoubleType
) {
534 static CFStringRef doubleFormatString
= NULL
;
535 CFNumberGetValue(object
, kCFNumberDoubleType
, &doubleVal
);
536 if (!doubleFormatString
) {
537 doubleFormatString
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%%.%de"), DBL_DIG
);
539 _plistAppendFormat(xmlString
, doubleFormatString
, doubleVal
);
542 static CFStringRef floatFormatString
= NULL
;
543 CFNumberGetValue(object
, kCFNumberFloatType
, &floatVal
);
544 if (!floatFormatString
) {
545 floatFormatString
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%%.%de"), FLT_DIG
);
547 _plistAppendFormat(xmlString
, floatFormatString
, floatVal
);
550 _plistAppendUTF8CString(xmlString
, "</");
551 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
);
552 _plistAppendUTF8CString(xmlString
, ">\n");
554 _plistAppendUTF8CString(xmlString
, "<");
555 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
556 _plistAppendUTF8CString(xmlString
, ">");
558 _plistAppendFormat(xmlString
, CFSTR("%@"), object
);
560 _plistAppendUTF8CString(xmlString
, "</");
561 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
);
562 _plistAppendUTF8CString(xmlString
, ">\n");
564 } else if (typeID
== CFBooleanGetTypeID()) {
565 if (CFBooleanGetValue(object
)) {
566 _plistAppendUTF8CString(xmlString
, "<");
567 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
);
568 _plistAppendUTF8CString(xmlString
, "/>\n");
570 _plistAppendUTF8CString(xmlString
, "<");
571 _plistAppendCharacters(xmlString
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
);
572 _plistAppendUTF8CString(xmlString
, "/>\n");
577 static void _CFGenerateXMLPropertyListToData(CFMutableDataRef xml
, CFTypeRef propertyList
) {
578 _plistAppendUTF8CString(xml
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE ");
579 _plistAppendCharacters(xml
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
);
580 _plistAppendUTF8CString(xml
, " PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<");
581 _plistAppendCharacters(xml
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
);
582 _plistAppendUTF8CString(xml
, " version=\"1.0\">\n");
584 _CFAppendXML0(propertyList
, 0, xml
);
586 _plistAppendUTF8CString(xml
, "</");
587 _plistAppendCharacters(xml
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
);
588 _plistAppendUTF8CString(xml
, ">\n");
591 CFDataRef
CFPropertyListCreateXMLData(CFAllocatorRef allocator
, CFPropertyListRef propertyList
) {
592 CFMutableDataRef xml
;
593 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
594 __CFAssertIsPList(propertyList
);
595 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
596 if (!CFPropertyListIsValid(propertyList
, kCFPropertyListXMLFormat_v1_0
)) return NULL
;
598 xml
= CFDataCreateMutable(allocator
, 0);
599 _CFGenerateXMLPropertyListToData(xml
, propertyList
);
603 CFDataRef
_CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator
, CFPropertyListRef propertyList
) {
604 CFMutableDataRef xml
;
605 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__
);
606 xml
= CFDataCreateMutable(allocator
, 0);
607 _CFGenerateXMLPropertyListToData(xml
, propertyList
);
611 // ========================================================================
614 // ------------------------- Reading plists ------------------
617 static void skipInlineDTD(_CFXMLPlistParseInfo
*pInfo
);
618 static CFTypeRef
parseXMLElement(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
);
620 // warning: doesn't have a good idea of Unicode line separators
621 static UInt32
lineNumber(_CFXMLPlistParseInfo
*pInfo
) {
622 const UniChar
*p
= pInfo
->begin
;
624 while (p
< pInfo
->curr
) {
627 if (*(p
+ 1) == '\n')
629 } else if (*p
== '\n') {
637 // warning: doesn't have a good idea of Unicode white space
638 CF_INLINE
void skipWhitespace(_CFXMLPlistParseInfo
*pInfo
) {
639 while (pInfo
->curr
< pInfo
->end
) {
640 switch (*(pInfo
->curr
)) {
653 /* 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. */
655 // pInfo should be just past "<!--"
656 static void skipXMLComment(_CFXMLPlistParseInfo
*pInfo
) {
657 const UniChar
*p
= pInfo
->curr
;
658 const UniChar
*end
= pInfo
->end
- 3; // Need at least 3 characters to compare against
660 if (*p
== '-' && *(p
+1) == '-' && *(p
+2) == '>') {
666 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unterminated comment started on line %d"), lineNumber(pInfo
));
669 // stringToMatch and buf must both be of at least len
670 static Boolean
matchString(const UniChar
*buf
, const UniChar
*stringToMatch
, UInt32 len
) {
672 case 10: if (buf
[9] != stringToMatch
[9]) return false;
673 case 9: if (buf
[8] != stringToMatch
[8]) return false;
674 case 8: if (buf
[7] != stringToMatch
[7]) return false;
675 case 7: if (buf
[6] != stringToMatch
[6]) return false;
676 case 6: if (buf
[5] != stringToMatch
[5]) return false;
677 case 5: if (buf
[4] != stringToMatch
[4]) return false;
678 case 4: if (buf
[3] != stringToMatch
[3]) return false;
679 case 3: if (buf
[2] != stringToMatch
[2]) return false;
680 case 2: if (buf
[1] != stringToMatch
[1]) return false;
681 case 1: if (buf
[0] != stringToMatch
[0]) return false;
684 return false; // internal error
687 // pInfo should be set to the first character after "<?"
688 static void skipXMLProcessingInstruction(_CFXMLPlistParseInfo
*pInfo
) {
689 const UniChar
*begin
= pInfo
->curr
, *end
= pInfo
->end
- 2; // Looking for "?>" so we need at least 2 characters
690 while (pInfo
->curr
< end
) {
691 if (*(pInfo
->curr
) == '?' && *(pInfo
->curr
+1) == '>') {
698 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected EOF while parsing the processing instruction begun on line %d"), lineNumber(pInfo
));
701 // first character should be immediately after the "<!"
702 static void skipDTD(_CFXMLPlistParseInfo
*pInfo
) {
703 // First pass "DOCTYPE"
704 if (pInfo
->end
- pInfo
->curr
< DOCTYPE_TAG_LENGTH
|| !matchString(pInfo
->curr
, CFXMLPlistTags
[DOCTYPE_IX
], DOCTYPE_TAG_LENGTH
)) {
705 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Malformed DTD on line %d"), lineNumber(pInfo
));
708 pInfo
->curr
+= DOCTYPE_TAG_LENGTH
;
709 skipWhitespace(pInfo
);
711 // Look for either the beginning of a complex DTD or the end of the DOCTYPE structure
712 while (pInfo
->curr
< pInfo
->end
) {
713 UniChar ch
= *(pInfo
->curr
);
714 if (ch
== '[') break; // inline DTD
715 if (ch
== '>') { // End of the DTD
721 if (pInfo
->curr
== pInfo
->end
) {
722 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF while parsing DTD", CFStringGetSystemEncoding());
726 // *Sigh* Must parse in-line DTD
727 skipInlineDTD(pInfo
);
728 if (pInfo
->errorString
) return;
729 skipWhitespace(pInfo
);
730 if (pInfo
->errorString
) return;
731 if (pInfo
->curr
< pInfo
->end
) {
732 if (*(pInfo
->curr
) == '>') {
735 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c on line %d while parsing DTD"), *(pInfo
->curr
), lineNumber(pInfo
));
738 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF while parsing DTD", CFStringGetSystemEncoding());
742 static void skipPERef(_CFXMLPlistParseInfo
*pInfo
) {
743 const UniChar
*p
= pInfo
->curr
;
744 while (p
< pInfo
->end
) {
751 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected EOF while parsing percent-escape sequence begun on line %d"), lineNumber(pInfo
));
754 // First character should be just past '['
755 static void skipInlineDTD(_CFXMLPlistParseInfo
*pInfo
) {
756 while (!pInfo
->errorString
&& pInfo
->curr
< pInfo
->end
) {
758 skipWhitespace(pInfo
);
763 } else if (ch
== '<') {
765 if (pInfo
->curr
>= pInfo
->end
) {
766 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF while parsing inline DTD", CFStringGetSystemEncoding());
772 skipXMLProcessingInstruction(pInfo
);
773 } else if (ch
== '!') {
774 if (pInfo
->curr
+ 2 < pInfo
->end
&& (*(pInfo
->curr
+1) == '-' && *(pInfo
->curr
+2) == '-')) {
776 skipXMLComment(pInfo
);
778 // Skip the myriad of DTD declarations of the form "<!string" ... ">"
779 pInfo
->curr
++; // Past both '<' and '!'
780 while (pInfo
->curr
< pInfo
->end
) {
781 if (*(pInfo
->curr
) == '>') break;
784 if (*(pInfo
->curr
) != '>') {
785 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF while parsing inline DTD", CFStringGetSystemEncoding());
792 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch
, lineNumber(pInfo
));
795 } else if (ch
== ']') {
799 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch
, lineNumber(pInfo
));
803 if (!pInfo
->errorString
)
804 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF while parsing inline DTD", CFStringGetSystemEncoding());
807 /* 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. */
809 static const signed char __CFPLDataDecodeTable
[128] = {
810 /* 000 */ -1, -1, -1, -1, -1, -1, -1, -1,
811 /* 010 */ -1, -1, -1, -1, -1, -1, -1, -1,
812 /* 020 */ -1, -1, -1, -1, -1, -1, -1, -1,
813 /* 030 */ -1, -1, -1, -1, -1, -1, -1, -1,
814 /* ' ' */ -1, -1, -1, -1, -1, -1, -1, -1,
815 /* '(' */ -1, -1, -1, 62, -1, -1, -1, 63,
816 /* '0' */ 52, 53, 54, 55, 56, 57, 58, 59,
817 /* '8' */ 60, 61, -1, -1, -1, 0, -1, -1,
818 /* '@' */ -1, 0, 1, 2, 3, 4, 5, 6,
819 /* 'H' */ 7, 8, 9, 10, 11, 12, 13, 14,
820 /* 'P' */ 15, 16, 17, 18, 19, 20, 21, 22,
821 /* 'X' */ 23, 24, 25, -1, -1, -1, -1, -1,
822 /* '`' */ -1, 26, 27, 28, 29, 30, 31, 32,
823 /* 'h' */ 33, 34, 35, 36, 37, 38, 39, 40,
824 /* 'p' */ 41, 42, 43, 44, 45, 46, 47, 48,
825 /* 'x' */ 49, 50, 51, -1, -1, -1, -1, -1
828 static CFDataRef
__CFPLDataDecode(_CFXMLPlistParseInfo
*pInfo
, Boolean
mutable) {
836 tmpbuf
= CFAllocatorAllocate(pInfo
->allocator
, tmpbuflen
, 0);
837 for (; pInfo
->curr
< pInfo
->end
; pInfo
->curr
++) {
838 UniChar c
= *(pInfo
->curr
);
844 } else if (!isspace(c
)) {
847 if (__CFPLDataDecodeTable
[c
] < 0)
851 acc
+= __CFPLDataDecodeTable
[c
];
852 if (0 == (cntr
& 0x3)) {
853 if (tmpbuflen
<= tmpbufpos
+ 2) {
855 tmpbuf
= CFAllocatorReallocate(pInfo
->allocator
, tmpbuf
, tmpbuflen
, 0);
857 tmpbuf
[tmpbufpos
++] = (acc
>> 16) & 0xff;
859 tmpbuf
[tmpbufpos
++] = (acc
>> 8) & 0xff;
861 tmpbuf
[tmpbufpos
++] = acc
& 0xff;
865 CFMutableDataRef result
= CFDataCreateMutable(pInfo
->allocator
, 0);
866 CFDataAppendBytes(result
, tmpbuf
, tmpbufpos
);
867 CFAllocatorDeallocate(pInfo
->allocator
, tmpbuf
);
870 return CFDataCreateWithBytesNoCopy(pInfo
->allocator
, (char const *) tmpbuf
, tmpbufpos
, pInfo
->allocator
);
874 // content ::== (element | CharData | Reference | CDSect | PI | Comment)*
875 // 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).
876 static CFTypeRef
getContentObject(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
) {
877 if (isKey
) *isKey
= false;
878 while (!pInfo
->errorString
&& pInfo
->curr
< pInfo
->end
) {
879 skipWhitespace(pInfo
);
880 if (pInfo
->curr
>= pInfo
->end
) {
881 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
884 if (*(pInfo
->curr
) != '<') {
885 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
889 if (pInfo
->curr
>= pInfo
->end
) {
890 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
893 switch (*(pInfo
->curr
)) {
895 // Processing instruction
896 skipXMLProcessingInstruction(pInfo
);
899 // Could be a comment
900 if (pInfo
->curr
+2 >= pInfo
->end
) {
901 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
904 if (*(pInfo
->curr
+1) == '-' && *(pInfo
->curr
+2) == '-') {
906 skipXMLComment(pInfo
);
908 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
913 // Whoops! Looks like we got to the end tag for the element whose content we're parsing
914 pInfo
->curr
--; // Back off to the '<'
917 // Should be an element
918 return parseXMLElement(pInfo
, isKey
);
921 // 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"
925 static void _catFromMarkToBuf(const UniChar
*mark
, const UniChar
*buf
, CFMutableStringRef
*string
, CFAllocatorRef allocator
) {
927 *string
= CFStringCreateMutable(allocator
, 0);
929 CFStringAppendCharacters(*string
, mark
, buf
-mark
);
932 static void parseCDSect_pl(_CFXMLPlistParseInfo
*pInfo
, CFMutableStringRef string
) {
933 const UniChar
*end
, *begin
;
934 if (pInfo
->end
- pInfo
->curr
< CDSECT_TAG_LENGTH
) {
935 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
938 if (!matchString(pInfo
->curr
, CFXMLPlistTags
[CDSECT_IX
], CDSECT_TAG_LENGTH
)) {
939 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered improper CDATA opening at line %d"), lineNumber(pInfo
));
942 pInfo
->curr
+= CDSECT_TAG_LENGTH
;
943 begin
= pInfo
->curr
; // Marks the first character of the CDATA content
944 end
= pInfo
->end
-2; // So we can safely look 2 characters beyond p
945 while (pInfo
->curr
< end
) {
946 if (*(pInfo
->curr
) == ']' && *(pInfo
->curr
+1) == ']' && *(pInfo
->curr
+2) == '>') {
948 CFStringAppendCharacters(string
, begin
, pInfo
->curr
-begin
);
954 // Never found the end mark
956 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Could not find end of CDATA started on line %d"), lineNumber(pInfo
));
959 // Only legal references are {lt, gt, amp, apos, quote, #ddd, #xAAA}
960 static void parseEntityReference_pl(_CFXMLPlistParseInfo
*pInfo
, CFMutableStringRef string
) {
963 pInfo
->curr
++; // move past the '&';
964 len
= pInfo
->end
- pInfo
->curr
; // how many characters we can safely scan
966 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
969 switch (*(pInfo
->curr
)) {
971 if (len
>= 3 && *(pInfo
->curr
+1) == 't' && *(pInfo
->curr
+2) == ';') {
976 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
979 if (len
>= 3 && *(pInfo
->curr
+1) == 't' && *(pInfo
->curr
+2) == ';') {
984 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
986 case 'a': // "apos" or "amp"
987 if (len
< 4) { // Not enough characters for either conversion
988 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
991 if (*(pInfo
->curr
+1) == 'm') {
993 if (*(pInfo
->curr
+2) == 'p' && *(pInfo
->curr
+3) == ';') {
998 } else if (*(pInfo
->curr
+1) == 'p') {
1000 if (len
> 4 && *(pInfo
->curr
+2) == 'o' && *(pInfo
->curr
+3) == 's' && *(pInfo
->curr
+4) == ';') {
1006 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1008 case 'q': // "quote"
1009 if (len
>= 5 && *(pInfo
->curr
+1) == 'u' && *(pInfo
->curr
+2) == 'o' && *(pInfo
->curr
+3) == 't' && *(pInfo
->curr
+4) == ';') {
1014 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1019 Boolean isHex
= false;
1020 if ( len
< 4) { // Not enough characters to make it all fit! Need at least "&#d;"
1021 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
1025 if (*(pInfo
->curr
) == 'x') {
1029 while (pInfo
->curr
< pInfo
->end
) {
1030 ch
= *(pInfo
->curr
);
1033 CFStringAppendCharacters(string
, &num
, 1);
1036 if (!isHex
) num
= num
*10;
1037 else num
= num
<< 4;
1038 if (ch
<= '9' && ch
>= '0') {
1040 } else if (!isHex
) {
1041 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c at line %d"), ch
, lineNumber(pInfo
));
1043 } else if (ch
>= 'a' && ch
<= 'f') {
1044 num
+= 10 + (ch
- 'a');
1045 } else if (ch
>= 'A' && ch
<= 'F') {
1046 num
+= 10 + (ch
- 'A');
1048 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c at line %d"), ch
, lineNumber(pInfo
));
1052 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
1056 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo
));
1059 CFStringAppendCharacters(string
, &ch
, 1);
1062 extern const void *__CFSetAddValueAndReturn(CFMutableSetRef set
, const void *value
);
1064 static CFStringRef
_uniqueStringForString(_CFXMLPlistParseInfo
*pInfo
, CFStringRef stringToUnique
) {
1065 if (!pInfo
->stringSet
) {
1066 pInfo
->stringSet
= CFSetCreateMutable(pInfo
->allocator
, 0, &kCFCopyStringSetCallBacks
);
1067 _CFSetSetCapacity(pInfo
->stringSet
, 160); // set capacity high to avoid lots of rehashes, though waste some memory
1069 return __CFSetAddValueAndReturn(pInfo
->stringSet
, stringToUnique
);
1072 extern void _CFStrSetDesiredCapacity(CFMutableStringRef str
, CFIndex len
);
1074 static CFStringRef
_uniqueStringForCharacters(_CFXMLPlistParseInfo
*pInfo
, const UniChar
*base
, CFIndex length
) {
1076 uint8_t *ascii
, buffer
[1024];
1078 if (!pInfo
->stringSet
) {
1079 pInfo
->stringSet
= CFSetCreateMutable(pInfo
->allocator
, 0, &kCFCopyStringSetCallBacks
);
1080 _CFSetSetCapacity(pInfo
->stringSet
, 160); // set capacity high to avoid lots of rehashes, though waste some memory
1082 if (pInfo
->tmpString
) {
1083 CFStringDelete(pInfo
->tmpString
, CFRangeMake(0, CFStringGetLength(pInfo
->tmpString
)));
1085 pInfo
->tmpString
= CFStringCreateMutable(pInfo
->allocator
, 0);
1086 _CFStrSetDesiredCapacity(pInfo
->tmpString
, 512);
1088 // This is to avoid having to promote the buffers of all the strings compared against
1089 // during the set probe; if a Unicode string is passed in, that's what happens.
1091 for (idx
= 0; isASCII
&& idx
< length
; idx
++) isASCII
= isASCII
&& (base
[idx
] < 0x80);
1093 ascii
= (length
< (CFIndex
)sizeof(buffer
)) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, length
+ 1, 0);
1094 for (idx
= 0; idx
< length
; idx
++) ascii
[idx
] = (uint8_t)base
[idx
];
1095 ascii
[length
] = '\0';
1096 CFStringAppendCString(pInfo
->tmpString
, ascii
, kCFStringEncodingASCII
);
1097 if (ascii
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, ascii
);
1099 CFStringAppendCharacters(pInfo
->tmpString
, base
, length
);
1101 return __CFSetAddValueAndReturn(pInfo
->stringSet
, pInfo
->tmpString
);
1105 // String could be comprised of characters, CDSects, or references to one of the "well-known" entities ('<', '>', '&', ''', '"')
1106 // returns a retained object in *string.
1107 static CFStringRef
getString(_CFXMLPlistParseInfo
*pInfo
) {
1108 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
1109 CFMutableStringRef string
= NULL
;
1110 while (!pInfo
->errorString
&& pInfo
->curr
< pInfo
->end
) {
1111 UniChar ch
= *(pInfo
->curr
);
1113 // Could be a CDSect; could be the end of the string
1114 if (*(pInfo
->curr
+1) != '!') break; // End of the string
1115 _catFromMarkToBuf(mark
, pInfo
->curr
, &string
, pInfo
->allocator
);
1116 parseCDSect_pl(pInfo
, string
);
1118 } else if (ch
== '&') {
1119 _catFromMarkToBuf(mark
, pInfo
->curr
, &string
, pInfo
->allocator
);
1120 parseEntityReference_pl(pInfo
, string
);
1127 if (pInfo
->errorString
) {
1128 if (string
) CFRelease(string
);
1132 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
1133 CFStringRef uniqueString
= _uniqueStringForCharacters(pInfo
, mark
, pInfo
->curr
-mark
);
1134 CFRetain(uniqueString
);
1135 return uniqueString
;
1137 string
= CFStringCreateMutable(pInfo
->allocator
, 0);
1138 CFStringAppendCharacters(string
, mark
, pInfo
->curr
- mark
);
1142 _catFromMarkToBuf(mark
, pInfo
->curr
, &string
, pInfo
->allocator
);
1143 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
1144 CFStringRef uniqueString
= _uniqueStringForString(pInfo
, string
);
1145 CFRetain(uniqueString
);
1147 return uniqueString
;
1152 static Boolean
checkForCloseTag(_CFXMLPlistParseInfo
*pInfo
, const UniChar
*tag
, CFIndex tagLen
) {
1153 if (pInfo
->end
- pInfo
->curr
< tagLen
+ 3) {
1154 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
1157 if (*(pInfo
->curr
) != '<' || *(++pInfo
->curr
) != '/') {
1158 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
1162 if (!matchString(pInfo
->curr
, tag
, tagLen
)) {
1163 CFStringRef str
= CFStringCreateWithCharactersNoCopy(pInfo
->allocator
, tag
, tagLen
, kCFAllocatorNull
);
1164 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Close tag on line %d does not match open tag %@"), lineNumber(pInfo
), str
);
1168 pInfo
->curr
+= tagLen
;
1169 skipWhitespace(pInfo
);
1170 if (pInfo
->curr
== pInfo
->end
) {
1171 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
1174 if (*(pInfo
->curr
) != '>') {
1175 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected character %c on line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
1182 // pInfo should be set to the first content character of the <plist>
1183 static CFTypeRef
parsePListTag(_CFXMLPlistParseInfo
*pInfo
) {
1184 CFTypeRef result
, tmp
= NULL
;
1185 const UniChar
*save
;
1186 result
= getContentObject(pInfo
, NULL
);
1188 if (!pInfo
->errorString
) pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered empty plist tag", CFStringGetSystemEncoding());
1191 save
= pInfo
->curr
; // Save this in case the next step fails
1192 tmp
= getContentObject(pInfo
, NULL
);
1194 // Got an extra object
1198 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unexpected element at line %d (plist can only include one object)"), lineNumber(pInfo
));
1201 if (pInfo
->errorString
) {
1202 // Parse failed catastrophically
1206 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
)) {
1213 static int allowImmutableCollections
= -1;
1215 static void checkImmutableCollections(void) {
1216 allowImmutableCollections
= (NULL
== getenv("CFPropertyListAllowImmutableCollections")) ? 0 : 1;
1219 static CFTypeRef
parseArrayTag(_CFXMLPlistParseInfo
*pInfo
) {
1220 CFMutableArrayRef array
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
1221 CFTypeRef tmp
= getContentObject(pInfo
, NULL
);
1223 CFArrayAppendValue(array
, tmp
);
1225 tmp
= getContentObject(pInfo
, NULL
);
1227 if (pInfo
->errorString
) { // getContentObject encountered a parse error
1231 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
)) {
1232 if (-1 == allowImmutableCollections
) checkImmutableCollections();
1233 if (1 == allowImmutableCollections
) {
1234 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1235 CFArrayRef newArray
= CFArrayCreateCopy(pInfo
->allocator
, array
);
1237 array
= (CFMutableArrayRef
)newArray
;
1246 static CFTypeRef
parseDictTag(_CFXMLPlistParseInfo
*pInfo
) {
1247 CFMutableDictionaryRef dict
= NULL
;
1248 CFTypeRef key
=NULL
, value
=NULL
;
1250 const UniChar
*base
= pInfo
->curr
;
1251 key
= getContentObject(pInfo
, &gotKey
);
1254 if (key
) CFRelease(key
);
1255 if (dict
) CFRelease(dict
);
1257 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo
));
1260 value
= getContentObject(pInfo
, NULL
);
1262 if (key
) CFRelease(key
);
1263 if (dict
) CFRelease(dict
);
1264 if (!pInfo
->errorString
)
1265 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo
));
1269 dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1270 _CFDictionarySetCapacity(dict
, 10);
1272 CFDictionarySetValue(dict
, key
, value
);
1278 key
= getContentObject(pInfo
, &gotKey
);
1280 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
)) {
1282 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1283 dict
= (CFMutableDictionaryRef
)CFDictionaryCreate(pInfo
->allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1285 dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1288 CFIndex cnt
= CFDictionaryGetCount(dict
);
1290 CFTypeRef val
= CFDictionaryGetValue(dict
, CFSTR("CF$UID"));
1291 if (val
&& CFGetTypeID(val
) == CFNumberGetTypeID()) {
1294 CFNumberGetValue(val
, kCFNumberSInt32Type
, &v
);
1295 uid
= (CFTypeRef
)_CFKeyedArchiverUIDCreate(pInfo
->allocator
, v
);
1300 if (-1 == allowImmutableCollections
) checkImmutableCollections();
1301 if (1 == allowImmutableCollections
) {
1302 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1303 CFDictionaryRef newDict
= CFDictionaryCreateCopy(pInfo
->allocator
, dict
);
1305 dict
= (CFMutableDictionaryRef
)newDict
;
1311 if (dict
) CFRelease(dict
);
1315 static CFTypeRef
parseDataTag(_CFXMLPlistParseInfo
*pInfo
) {
1317 const UniChar
*base
= pInfo
->curr
;
1318 result
= __CFPLDataDecode(pInfo
, pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
);
1321 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Could not interpret <data> at line %d (should be base64-encoded)"), lineNumber(pInfo
));
1324 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
)) return result
;
1329 CF_INLINE Boolean
read2DigitNumber(_CFXMLPlistParseInfo
*pInfo
, int8_t *result
) {
1331 if (pInfo
->curr
+ 2 >= pInfo
->end
) return false;
1333 ch2
= *(pInfo
->curr
+ 1);
1335 if (!isdigit(ch1
) || !isdigit(ch2
)) return false;
1336 *result
= (ch1
- '0')*10 + (ch2
- '0');
1340 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z'
1341 static CFTypeRef
parseDateTag(_CFXMLPlistParseInfo
*pInfo
) {
1342 CFGregorianDate date
;
1344 Boolean badForm
= false;
1347 while (pInfo
->curr
< pInfo
->end
&& isdigit(*pInfo
->curr
)) {
1348 date
.year
= 10*date
.year
+ (*pInfo
->curr
) - '0';
1351 if (pInfo
->curr
>= pInfo
->end
|| *pInfo
->curr
!= '-') {
1357 if (!badForm
&& read2DigitNumber(pInfo
, &date
.month
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== '-') {
1363 if (!badForm
&& read2DigitNumber(pInfo
, &date
.day
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== 'T') {
1369 if (!badForm
&& read2DigitNumber(pInfo
, &date
.hour
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== ':') {
1375 if (!badForm
&& read2DigitNumber(pInfo
, &date
.minute
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== ':') {
1381 if (!badForm
&& read2DigitNumber(pInfo
, &num
) && pInfo
->curr
< pInfo
->end
&& *pInfo
->curr
== 'Z') {
1389 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Could not interpret <date> at line %d"), lineNumber(pInfo
));
1392 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
)) return NULL
;
1393 return CFDateCreate(pInfo
->allocator
, CFGregorianDateGetAbsoluteTime(date
, NULL
));
1396 static CFTypeRef
parseRealTag(_CFXMLPlistParseInfo
*pInfo
) {
1397 CFStringRef str
= getString(pInfo
);
1401 CFStringInlineBuffer buf
;
1403 if (!pInfo
->errorString
)
1404 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo
));
1408 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
1409 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("nan"), kCFCompareCaseInsensitive
)) {
1411 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberNaN
) : NULL
;
1413 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("+infinity"), kCFCompareCaseInsensitive
)) {
1415 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberPositiveInfinity
) : NULL
;
1417 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("-infinity"), kCFCompareCaseInsensitive
)) {
1419 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberNegativeInfinity
) : NULL
;
1421 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("infinity"), kCFCompareCaseInsensitive
)) {
1423 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberPositiveInfinity
) : NULL
;
1425 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("-inf"), kCFCompareCaseInsensitive
)) {
1427 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberNegativeInfinity
) : NULL
;
1429 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("inf"), kCFCompareCaseInsensitive
)) {
1431 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberPositiveInfinity
) : NULL
;
1433 if (kCFCompareEqualTo
== CFStringCompare(str
, CFSTR("+inf"), kCFCompareCaseInsensitive
)) {
1435 return (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) ? CFRetain(kCFNumberPositiveInfinity
) : NULL
;
1439 len
= CFStringGetLength(str
);
1440 CFStringInitInlineBuffer(str
, &buf
, CFRangeMake(0, len
));
1442 if (!__CFStringScanDouble(&buf
, NULL
, &idx
, &val
) || idx
!= len
) {
1444 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered misformatted real on line %d"), lineNumber(pInfo
));
1448 result
= CFNumberCreate(pInfo
->allocator
, kCFNumberDoubleType
, &val
);
1449 if (checkForCloseTag(pInfo
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
)) return result
;
1454 #define GET_CH if (pInfo->curr == pInfo->end) { \
1455 pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Premature end of file after <integer> on line %d"), lineNumber(pInfo)); \
1460 static CFTypeRef
parseIntegerTag(_CFXMLPlistParseInfo
*pInfo
) {
1461 bool isHex
= false, isNeg
= false, hadLeadingZero
= false;
1462 int64_t value
= (int64_t)0;
1465 // decimal_constant S*(-|+)?S*[0-9]+ (S == space)
1466 // hex_constant S*(-|+)?S*0[xX][0-9a-fA-F]+ (S == space)
1468 while (pInfo
->curr
< pInfo
->end
&& __CFIsWhitespace(*(pInfo
->curr
))) pInfo
->curr
++;
1471 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo
));
1474 if ('-' == ch
|| '+' == ch
) {
1475 isNeg
= ('-' == ch
);
1477 while (pInfo
->curr
< pInfo
->end
&& __CFIsWhitespace(*(pInfo
->curr
))) pInfo
->curr
++;
1481 if (pInfo
->curr
+ 1 < pInfo
->end
&& ('x' == *(pInfo
->curr
+ 1) || 'X' == *(pInfo
->curr
+ 1))) {
1485 hadLeadingZero
= true;
1491 hadLeadingZero
= true;
1495 if ('<' == ch
&& hadLeadingZero
) { // nothing but zeros
1497 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
)) {
1498 // checkForCloseTag() sets error string
1501 return CFNumberCreate(pInfo
->allocator
, kCFNumberSInt32Type
, &val
);
1504 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Incomplete <integer> on line %d"), lineNumber(pInfo
));
1508 int64_t old_value
= value
;
1510 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
1511 value
= (isHex
? 16 : 10) * value
+ (ch
- '0');
1513 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1515 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Hex digit in non-hex <integer> on line %d"), lineNumber(pInfo
));
1518 value
= 16 * value
+ (ch
- 'a' + 10);
1520 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1522 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Hex digit in non-hex <integer> on line %d"), lineNumber(pInfo
));
1525 value
= 16 * value
+ (ch
- 'A' + 10);
1527 default: // other character
1528 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unknown character '%c' (0x%x) in <integer> on line %d"), ch
, ch
, lineNumber(pInfo
));
1531 if (isNeg
&& LLONG_MIN
== value
) {
1532 // overflow by one when isNeg gives the proper value, if we're done with the number
1533 if (pInfo
->curr
+ 1 < pInfo
->end
&& '<' == *(pInfo
->curr
+ 1)) {
1539 if (value
< old_value
) {
1540 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered <integer> too large to represent on line %d"), lineNumber(pInfo
));
1546 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
)) {
1547 // checkForCloseTag() sets error string
1550 if (isNeg
) value
= -value
;
1551 return CFNumberCreate(pInfo
->allocator
, kCFNumberSInt64Type
, &value
);
1556 // Returned object is retained; caller must free. pInfo->curr expected to point to the first character after the '<'
1557 static CFTypeRef
parseXMLElement(_CFXMLPlistParseInfo
*pInfo
, Boolean
*isKey
) {
1558 const UniChar
*marker
= pInfo
->curr
;
1559 int markerLength
= -1;
1563 if (isKey
) *isKey
= false;
1564 while (pInfo
->curr
< pInfo
->end
) {
1565 UniChar ch
= *(pInfo
->curr
);
1566 if (ch
== ' ' || ch
== '\t' || ch
== '\n' || ch
=='\r') {
1567 if (markerLength
== -1) markerLength
= pInfo
->curr
- marker
;
1568 } else if (ch
== '>') {
1573 if (pInfo
->curr
>= pInfo
->end
) return NULL
;
1574 isEmpty
= (*(pInfo
->curr
-1) == '/');
1575 if (markerLength
== -1)
1576 markerLength
= pInfo
->curr
- (isEmpty
? 1 : 0) - marker
;
1577 pInfo
->curr
++; // Advance past '>'
1578 if (markerLength
== 0) {
1579 // Back up to the beginning of the marker
1580 pInfo
->curr
= marker
;
1581 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Malformed tag on line %d"), lineNumber(pInfo
));
1586 if (markerLength
== ARRAY_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[ARRAY_IX
], ARRAY_TAG_LENGTH
))
1587 markerIx
= ARRAY_IX
;
1589 case 'd': // Dictionary, data, or date; Fortunately, they all have the same marker length....
1590 if (markerLength
!= DICT_TAG_LENGTH
)
1592 if (matchString(marker
, CFXMLPlistTags
[DICT_IX
], DICT_TAG_LENGTH
))
1594 else if (matchString(marker
, CFXMLPlistTags
[DATA_IX
], DATA_TAG_LENGTH
))
1596 else if (matchString(marker
, CFXMLPlistTags
[DATE_IX
], DATE_TAG_LENGTH
))
1599 case 'f': // false (boolean)
1600 if (markerLength
== FALSE_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[FALSE_IX
], FALSE_TAG_LENGTH
)) {
1601 markerIx
= FALSE_IX
;
1604 case 'i': // integer
1605 if (markerLength
== INTEGER_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[INTEGER_IX
], INTEGER_TAG_LENGTH
))
1606 markerIx
= INTEGER_IX
;
1608 case 'k': // Key of a dictionary
1609 if (markerLength
== KEY_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[KEY_IX
], KEY_TAG_LENGTH
)) {
1611 if (isKey
) *isKey
= true;
1615 if (markerLength
== PLIST_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[PLIST_IX
], PLIST_TAG_LENGTH
))
1616 markerIx
= PLIST_IX
;
1619 if (markerLength
== REAL_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[REAL_IX
], REAL_TAG_LENGTH
))
1623 if (markerLength
== STRING_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[STRING_IX
], STRING_TAG_LENGTH
))
1624 markerIx
= STRING_IX
;
1626 case 't': // true (boolean)
1627 if (markerLength
== TRUE_TAG_LENGTH
&& matchString(marker
, CFXMLPlistTags
[TRUE_IX
], TRUE_TAG_LENGTH
))
1632 if (!pInfo
->allowNewTypes
&& markerIx
!= PLIST_IX
&& markerIx
!= ARRAY_IX
&& markerIx
!= DICT_IX
&& markerIx
!= STRING_IX
&& markerIx
!= KEY_IX
&& markerIx
!= DATA_IX
) {
1633 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered new tag when expecting only old-style property list objects", CFStringGetSystemEncoding());
1640 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered empty plist tag", CFStringGetSystemEncoding());
1643 return parsePListTag(pInfo
);
1646 return pInfo
->mutabilityOption
== kCFPropertyListImmutable
? CFArrayCreate(pInfo
->allocator
, NULL
, 0, &kCFTypeArrayCallBacks
) : CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
1648 return parseArrayTag(pInfo
);
1652 if (pInfo
->mutabilityOption
== kCFPropertyListImmutable
) {
1653 return CFDictionaryCreate(pInfo
->allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1655 return CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1658 return parseDictTag(pInfo
);
1664 int tagLen
= (markerIx
== KEY_IX
) ? KEY_TAG_LENGTH
: STRING_TAG_LENGTH
;
1666 return pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
? CFStringCreateMutable(pInfo
->allocator
, 0) : CFStringCreateWithCharacters(pInfo
->allocator
, NULL
, 0);
1668 str
= getString(pInfo
);
1669 if (!str
) return NULL
; // getString will already have set the error string
1670 if (!checkForCloseTag(pInfo
, CFXMLPlistTags
[markerIx
], tagLen
)) {
1678 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered empty <data> on line %d"), lineNumber(pInfo
));
1681 return parseDataTag(pInfo
);
1685 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered empty <date> on line %d"), lineNumber(pInfo
));
1688 return parseDateTag(pInfo
);
1692 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered non-empty <true> tag on line %d"), lineNumber(pInfo
));
1695 return CFRetain(kCFBooleanTrue
);
1699 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered non-empty <false> tag on line %d"), lineNumber(pInfo
));
1702 return CFRetain(kCFBooleanFalse
);
1706 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo
));
1709 return parseRealTag(pInfo
);
1713 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo
));
1716 return parseIntegerTag(pInfo
);
1719 CFStringRef markerStr
= CFStringCreateWithCharacters(pInfo
->allocator
, marker
, markerLength
);
1720 pInfo
->curr
= marker
;
1721 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Encountered unknown tag %@ on line %d"), markerStr
, lineNumber(pInfo
));
1722 CFRelease(markerStr
);
1728 static CFTypeRef
parseXMLPropertyList(_CFXMLPlistParseInfo
*pInfo
) {
1729 while (!pInfo
->errorString
&& pInfo
->curr
< pInfo
->end
) {
1731 skipWhitespace(pInfo
);
1732 if (pInfo
->curr
+1 >= pInfo
->end
) {
1733 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "No XML content found", CFStringGetSystemEncoding());
1736 if (*(pInfo
->curr
) != '<') {
1737 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unexpected character %c at line %d"), *(pInfo
->curr
), lineNumber(pInfo
));
1740 ch
= *(++ pInfo
->curr
);
1744 if (pInfo
->curr
+1 < pInfo
->end
&& *pInfo
->curr
== '-' && *(pInfo
->curr
+1) == '-') {
1747 skipXMLComment(pInfo
);
1751 } else if (ch
== '?') {
1752 // Processing instruction
1754 skipXMLProcessingInstruction(pInfo
);
1757 return parseXMLElement(pInfo
, NULL
);
1758 // 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
1761 // Should never get here
1762 if (!(pInfo
->errorString
))
1763 pInfo
->errorString
= CFStringCreateWithCString(pInfo
->allocator
, "Encountered unexpected EOF", CFStringGetSystemEncoding());
1767 static CFStringEncoding
encodingForXMLData(CFDataRef data
, CFStringRef
*error
) {
1768 const uint8_t *bytes
= (uint8_t *)CFDataGetBytePtr(data
);
1769 UInt32 length
= CFDataGetLength(data
);
1770 const uint8_t *idx
, *end
;
1773 // Check for the byte order mark first
1775 ((*bytes
== 0xFF && *(bytes
+1) == 0xFE) ||
1776 (*bytes
== 0xFE && *(bytes
+1) == 0xFF) ||
1777 *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
1778 return kCFStringEncodingUnicode
;
1780 // Scan for the <?xml.... ?> opening
1781 if (length
< 5 || strncmp((char const *) bytes
, "<?xml", 5) != 0) return kCFStringEncodingUTF8
;
1783 end
= bytes
+ length
;
1784 // Found "<?xml"; now we scan for "encoding"
1787 const uint8_t *scan
;
1788 if ( ch
== '?' || ch
== '>') return kCFStringEncodingUTF8
;
1791 if (ch
== 'e' && *scan
++ == 'n' && *scan
++ == 'c' && *scan
++ == 'o' && *scan
++ == 'd' && *scan
++ == 'i'
1792 && *scan
++ == 'n' && *scan
++ == 'g' && *scan
++ == '=') {
1797 if (idx
>= end
) return kCFStringEncodingUTF8
;
1799 if (quote
!= '\'' && quote
!= '\"') return kCFStringEncodingUTF8
;
1801 CFStringRef encodingName
;
1802 const uint8_t *base
= idx
+1; // Move past the quote character
1803 CFStringEncoding enc
;
1806 while (idx
< end
&& *idx
!= quote
) idx
++;
1807 if (idx
>= end
) return kCFStringEncodingUTF8
;
1809 if (len
== 5 && (*base
== 'u' || *base
== 'U') && (base
[1] == 't' || base
[1] == 'T') && (base
[2] == 'f' || base
[2] == 'F') && (base
[3] == '-') && (base
[4] == '8'))
1810 return kCFStringEncodingUTF8
;
1811 encodingName
= CFStringCreateWithBytes(NULL
, base
, len
, kCFStringEncodingISOLatin1
, false);
1812 enc
= CFStringConvertIANACharSetNameToEncoding(encodingName
);
1813 if (enc
!= kCFStringEncodingInvalidId
) {
1814 CFRelease(encodingName
);
1819 *error
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Encountered unknown encoding (%@)"), encodingName
);
1820 CFRelease(encodingName
);
1826 CFTypeRef __CFNastyFile__
= NULL
;
1827 static CFSpinLock_t __CFNastyFileLock__
= 0;
1829 void __CFSetNastyFile(CFTypeRef cf
) {
1830 __CFSpinLock(&__CFNastyFileLock__
);
1831 if (__CFNastyFile__
) CFRelease(__CFNastyFile__
);
1832 __CFNastyFile__
= cf
? CFRetain(cf
) : cf
;
1833 __CFSpinUnlock(&__CFNastyFileLock__
);
1836 extern bool __CFTryParseBinaryPlist(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFPropertyListRef
*plist
, CFStringRef
*errorString
);
1837 int _CFPropertyListAllowNonUTF8
= 1;
1839 static CFTypeRef
_CFPropertyListCreateFromXMLData(CFAllocatorRef allocator
, CFDataRef xmlData
, CFOptionFlags option
, CFStringRef
*errorString
, Boolean allowNewTypes
, CFPropertyListFormat
*format
) {
1840 CFStringEncoding encoding
;
1841 CFStringRef xmlString
;
1843 CFPropertyListRef plist
;
1845 if (!xmlData
|| CFDataGetLength(xmlData
) == 0) {
1847 *errorString
= CFSTR("Cannot parse a NULL or zero-length data");
1848 CFRetain(*errorString
); // Caller expects to release
1853 if (__CFTryParseBinaryPlist(allocator
, xmlData
, option
, &plist
, errorString
)) {
1854 if (format
) *format
= kCFPropertyListBinaryFormat_v1_0
;
1858 allocator
= allocator
? allocator
: __CFGetDefaultAllocator();
1859 CFRetain(allocator
);
1861 if (errorString
) *errorString
= NULL
;
1862 encoding
= encodingForXMLData(xmlData
, errorString
); // 0 is an error return, NOT MacRoman.
1864 if (encoding
== 0) {
1865 // Couldn't find an encoding; encodingForXMLData already set *errorString if necessary
1866 // Note that encodingForXMLData() will give us the right values for a standard plist, too.
1867 if (errorString
) *errorString
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("Could not determine the encoding of the XML data"));
1871 xmlString
= CFStringCreateWithBytes(allocator
, CFDataGetBytePtr(xmlData
), CFDataGetLength(xmlData
), encoding
, true);
1872 if (NULL
== xmlString
&& !_CFExecutableLinkedOnOrAfter(CFSystemVersionMerlot
) && _CFPropertyListAllowNonUTF8
) { // conversion failed, probably because not in proper encoding
1873 static int yanmode
= -1;
1874 if (-1 == yanmode
) yanmode
= (getenv("YanMode") != NULL
);
1875 if (1 != yanmode
&& _CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
1876 CFTypeRef f
= (__CFNastyFile__
) ? (__CFNastyFile__
) : CFSTR("(UNKNOWN)");
1877 if (encoding
== kCFStringEncodingUTF8
) {
1878 CFLog(0, CFSTR("\n\tCFPropertyListCreateFromXMLData(): plist parse failed; the data is not proper UTF-8. The file name for this data could be:\n\t%@\n\tThe parser will retry as in 10.2, but the problem should be corrected in the plist."), f
);
1881 CFLog(0, CFSTR("\n\tCFPropertyListCreateFromXMLData(): conversion of data failed.\n\tThe file is not in the encoding specified in XML header if XML.\n\tThe file name for this data could be:\n\t\t%@\n."), f
);
1885 // Call __CFStringCreateImmutableFunnel3() the same way CFStringCreateWithBytes() does, except with the addt'l flag
1886 if (encoding
== kCFStringEncodingUTF8
) xmlString
= __CFStringCreateImmutableFunnel3(allocator
, CFDataGetBytePtr(xmlData
), CFDataGetLength(xmlData
), kCFStringEncodingUTF8
, true, true, false, false, false, (void *)-1 /* ALLOCATORSFREEFUNC */, kCFStringEncodingLenientUTF8Conversion
);
1888 length
= xmlString
? CFStringGetLength(xmlString
) : 0;
1891 _CFXMLPlistParseInfo pInfoBuf
;
1892 _CFXMLPlistParseInfo
*pInfo
= &pInfoBuf
;
1894 UniChar
*buf
= (UniChar
*)CFStringGetCharactersPtr(xmlString
);
1896 if (errorString
) *errorString
= NULL
;
1898 buf
= (UniChar
*)CFAllocatorAllocate(allocator
, length
* sizeof(UniChar
), 0);
1899 CFStringGetCharacters(xmlString
, CFRangeMake(0, length
), buf
);
1900 CFRelease(xmlString
);
1904 pInfo
->end
= buf
+length
;
1906 pInfo
->allocator
= allocator
;
1907 pInfo
->errorString
= NULL
;
1908 pInfo
->stringSet
= NULL
;
1909 pInfo
->tmpString
= NULL
;
1910 pInfo
->mutabilityOption
= option
;
1911 pInfo
->allowNewTypes
= allowNewTypes
;
1913 // 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.....
1914 result
= parseXMLPropertyList(pInfo
);
1915 if (result
&& format
) *format
= kCFPropertyListXMLFormat_v1_0
;
1917 CFStringRef err
= pInfo
->errorString
;
1918 // Reset pInfo so we can try again
1919 pInfo
->curr
= pInfo
->begin
;
1920 pInfo
->errorString
= NULL
;
1922 result
= parseOldStylePropertyListOrStringsFile(pInfo
);
1923 if (result
&& format
) *format
= kCFPropertyListOpenStepFormat
;
1925 if (errorString
) *errorString
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("XML parser error:\n\t%@\nOld-style plist parser error:\n\t%@\n"), err
, pInfo
->errorString
);
1927 if (err
) CFRelease(err
);
1928 if (pInfo
->errorString
) CFRelease(pInfo
->errorString
);
1931 CFRelease(xmlString
);
1933 CFAllocatorDeallocate(allocator
, (void *)pInfo
->begin
);
1935 if (pInfo
->stringSet
) CFRelease(pInfo
->stringSet
);
1936 if (pInfo
->tmpString
) CFRelease(pInfo
->tmpString
);
1937 CFRelease(allocator
);
1941 *errorString
= CFRetain(CFSTR("Conversion of data failed. The file is not UTF-8, or in the encoding specified in XML header if XML."));
1946 CFTypeRef
CFPropertyListCreateFromXMLData(CFAllocatorRef allocator
, CFDataRef xmlData
, CFOptionFlags option
, CFStringRef
*errorString
) {
1947 CFAssert1(xmlData
!= NULL
, __kCFLogAssertion
, "%s(): NULL data not allowed", __PRETTY_FUNCTION__
);
1948 CFAssert2(option
== kCFPropertyListImmutable
|| option
== kCFPropertyListMutableContainers
|| option
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, option
);
1949 return _CFPropertyListCreateFromXMLData(allocator
, xmlData
, option
, errorString
, true, NULL
);
1953 // ========================================================================
1956 // Old NeXT-style property lists
1959 static CFTypeRef
parsePlistObject(_CFXMLPlistParseInfo
*pInfo
, bool requireObject
);
1961 #define isValidUnquotedStringCharacter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z') || ((x) >= '0' && (x) <= '9') || (x) == '_' || (x) == '$' || (x) == '/' || (x) == ':' || (x) == '.' || (x) == '-')
1963 static void advanceToNonSpace(_CFXMLPlistParseInfo
*pInfo
) {
1965 while (pInfo
->curr
< pInfo
->end
) {
1966 ch2
= *(pInfo
->curr
);
1968 if (ch2
>= 9 && ch2
<= 0x0d) continue; // tab, newline, vt, form feed, carriage return
1969 if (ch2
== ' ' || ch2
== 0x2028 || ch2
== 0x2029) continue; // space and Unicode line sep, para sep
1971 if (pInfo
->curr
>= pInfo
->end
) {
1972 // whoops; back up and return
1975 } else if (*(pInfo
->curr
) == '/') {
1977 while (pInfo
->curr
< pInfo
->end
) { // go to end of comment line
1978 UniChar ch3
= *(pInfo
->curr
);
1979 if (ch3
== '\n' || ch3
== '\r' || ch3
== 0x2028 || ch3
== 0x2029) break;
1982 } else if (*(pInfo
->curr
) == '*') { // handle /* ... */
1984 while (pInfo
->curr
< pInfo
->end
) {
1985 ch2
= *(pInfo
->curr
);
1987 if (ch2
== '*' && pInfo
->curr
< pInfo
->end
&& *(pInfo
->curr
) == '/') {
1988 pInfo
->curr
++; // advance past the '/'
2003 static UniChar
getSlashedChar(_CFXMLPlistParseInfo
*pInfo
) {
2004 UniChar ch
= *(pInfo
->curr
);
2015 uint8_t num
= ch
- '0';
2018 /* three digits maximum to avoid reading \000 followed by 5 as \5 ! */
2019 if ((ch
= *(pInfo
->curr
)) >= '0' && ch
<= '7') { // we use in this test the fact that the buffer is zero-terminated
2021 num
= (num
<< 3) + ch
- '0';
2022 if ((pInfo
->curr
< pInfo
->end
) && (ch
= *(pInfo
->curr
)) >= '0' && ch
<= '7') {
2024 num
= (num
<< 3) + ch
- '0';
2027 CFStringEncodingBytesToUnicode(kCFStringEncodingNextStepLatin
, 0, &num
, sizeof(uint8_t), NULL
, &result
, 1, &usedCharLen
);
2028 return (usedCharLen
== 1) ? result
: 0;
2031 unsigned num
= 0, numDigits
= 4; /* Parse four digits */
2032 while (pInfo
->curr
< pInfo
->end
&& numDigits
--) {
2033 if (((ch
= *(pInfo
->curr
)) < 128) && isxdigit(ch
)) {
2035 num
= (num
<< 4) + ((ch
<= '9') ? (ch
- '0') : ((ch
<= 'F') ? (ch
- 'A' + 10) : (ch
- 'a' + 10)));
2040 case 'a': return '\a'; // Note: the meaning of '\a' varies with -traditional to gcc
2041 case 'b': return '\b';
2042 case 'f': return '\f';
2043 case 'n': return '\n';
2044 case 'r': return '\r';
2045 case 't': return '\t';
2046 case 'v': return '\v';
2047 case '"': return '\"';
2048 case '\n': return '\n';
2053 static CFStringRef
parseQuotedPlistString(_CFXMLPlistParseInfo
*pInfo
, UniChar quote
) {
2054 CFMutableStringRef str
= NULL
;
2055 const UniChar
*startMark
= pInfo
->curr
;
2056 const UniChar
*mark
= pInfo
->curr
;
2057 while (pInfo
->curr
< pInfo
->end
) {
2058 UniChar ch
= *(pInfo
->curr
);
2059 if (ch
== quote
) break;
2061 _catFromMarkToBuf(mark
, pInfo
->curr
, &str
, pInfo
->allocator
);
2063 ch
= getSlashedChar(pInfo
);
2064 CFStringAppendCharacters(str
, &ch
, 1);
2067 // 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.
2071 if (pInfo
->end
<= pInfo
->curr
) {
2072 if (str
) CFRelease(str
);
2073 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2074 pInfo
->curr
= startMark
;
2075 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unterminated quoted string starting on line %d"), lineNumber(pInfo
));
2080 if (pInfo
->mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
2081 _catFromMarkToBuf(mark
, pInfo
->curr
, &str
, pInfo
->allocator
);
2083 str
= (CFMutableStringRef
)_uniqueStringForCharacters(pInfo
, mark
, pInfo
->curr
-mark
);
2087 if (mark
!= pInfo
->curr
) {
2088 _catFromMarkToBuf(mark
, pInfo
->curr
, &str
, pInfo
->allocator
);
2090 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
2091 CFStringRef uniqueString
= _uniqueStringForString(pInfo
, str
);
2093 CFRetain(uniqueString
);
2094 str
= (CFMutableStringRef
)uniqueString
;
2097 pInfo
->curr
++; // Advance past the quote character before returning.
2098 if (pInfo
->errorString
) {
2099 CFRelease(pInfo
->errorString
);
2100 pInfo
->errorString
= NULL
;
2105 static CFStringRef
parseUnquotedPlistString(_CFXMLPlistParseInfo
*pInfo
) {
2106 const UniChar
*mark
= pInfo
->curr
;
2107 while (pInfo
->curr
< pInfo
->end
) {
2108 UniChar ch
= *pInfo
->curr
;
2109 if (isValidUnquotedStringCharacter(ch
))
2113 if (pInfo
->curr
!= mark
) {
2114 if (pInfo
->mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
) {
2115 CFStringRef str
= _uniqueStringForCharacters(pInfo
, mark
, pInfo
->curr
-mark
);
2119 CFMutableStringRef str
= CFStringCreateMutable(pInfo
->allocator
, 0);
2120 CFStringAppendCharacters(str
, mark
, pInfo
->curr
- mark
);
2124 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2125 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unexpected EOF"));
2130 static CFStringRef
parsePlistString(_CFXMLPlistParseInfo
*pInfo
, bool requireObject
) {
2132 advanceToNonSpace(pInfo
);
2133 if (pInfo
->curr
>= pInfo
->end
) {
2134 if (requireObject
&& _CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2135 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unexpected EOF while parsing string"));
2139 ch
= *(pInfo
->curr
);
2140 if (ch
== '\'' || ch
== '\"') {
2142 return parseQuotedPlistString(pInfo
, ch
);
2143 } else if (isValidUnquotedStringCharacter(ch
)) {
2144 return parseUnquotedPlistString(pInfo
);
2146 if (requireObject
&& _CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2147 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Invalid string character at line %d"), lineNumber(pInfo
));
2153 static CFTypeRef
parsePlistArray(_CFXMLPlistParseInfo
*pInfo
) {
2154 CFMutableArrayRef array
= CFArrayCreateMutable(pInfo
->allocator
, 0, &kCFTypeArrayCallBacks
);
2155 CFTypeRef tmp
= parsePlistObject(pInfo
, false);
2157 CFArrayAppendValue(array
, tmp
);
2159 advanceToNonSpace(pInfo
);
2160 if (*pInfo
->curr
!= ',') {
2164 tmp
= parsePlistObject(pInfo
, false);
2167 advanceToNonSpace(pInfo
);
2168 if (*pInfo
->curr
!= ')') {
2170 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2171 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Expected terminating ')' for array at line %d"), lineNumber(pInfo
));
2175 if (pInfo
->errorString
) {
2176 CFRelease(pInfo
->errorString
);
2177 pInfo
->errorString
= NULL
;
2183 static CFDictionaryRef
parsePlistDictContent(_CFXMLPlistParseInfo
*pInfo
) {
2184 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2185 CFStringRef key
= NULL
;
2186 Boolean failedParse
= false;
2187 key
= parsePlistString(pInfo
, false);
2190 advanceToNonSpace(pInfo
);
2191 if (*pInfo
->curr
== ';') {
2192 /* This is a strings file using the shortcut format */
2193 /* although this check here really applies to all plists. */
2194 value
= CFRetain(key
);
2195 } else if (*pInfo
->curr
== '=') {
2197 value
= parsePlistObject(pInfo
, true);
2203 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2204 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unexpected ';' or '=' after key at line %d"), lineNumber(pInfo
));
2209 CFDictionarySetValue(dict
, key
, value
);
2214 advanceToNonSpace(pInfo
);
2215 if (*pInfo
->curr
== ';') {
2217 key
= parsePlistString(pInfo
, false);
2218 } else if (!allowMissingSemi
&& _CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2219 static int yanmode
= -1;
2220 if (-1 == yanmode
) yanmode
= (getenv("YanMode") != NULL
);
2222 CFLog(0, CFSTR("CFPropertyListCreateFromXMLData(): Old-style plist parser: missing semicolon in dictionary."));
2223 if (__CFNastyFile__
) {
2224 CFLog(0, CFSTR("CFPropertyListCreateFromXMLData(): The file name for this data might be (or it might not): %@"), __CFNastyFile__
);
2228 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Missing ';' on line %d"), lineNumber(pInfo
));
2230 // on pre-Jaguar systems, do nothing except silently ignore the rest
2231 // of the dictionary, which is what happened on those systems.
2236 if (key
) CFRelease(key
);
2240 if (pInfo
->errorString
) {
2241 CFRelease(pInfo
->errorString
);
2242 pInfo
->errorString
= NULL
;
2247 static CFTypeRef
parsePlistDict(_CFXMLPlistParseInfo
*pInfo
) {
2248 CFDictionaryRef dict
= parsePlistDictContent(pInfo
);
2249 if (!dict
) return NULL
;
2250 advanceToNonSpace(pInfo
);
2251 if (*pInfo
->curr
!= '}') {
2253 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2254 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Expected terminating '}' for dictionary at line %d"), lineNumber(pInfo
));
2262 static unsigned char fromHexDigit(unsigned char ch
) {
2263 if (isdigit(ch
)) return ch
- '0';
2264 if ((ch
>= 'a') && (ch
<= 'f')) return ch
- 'a' + 10;
2265 if ((ch
>= 'A') && (ch
<= 'F')) return ch
- 'A' + 10;
2266 return 0xff; // Just choose a large number for the error code
2269 static CFTypeRef
parsePlistData(_CFXMLPlistParseInfo
*pInfo
) {
2271 unsigned length
= 0;
2272 CFMutableDataRef result
= CFDataCreateMutable(pInfo
->allocator
, 0);
2274 advanceToNonSpace(pInfo
);
2275 while ( (token
= parseUnquotedPlistString(pInfo
)) ) {
2276 unsigned tlength
= CFStringGetLength(token
);
2277 unsigned char *bytes
;
2279 if (tlength
& 1) { // Token must have an even number of characters
2282 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2283 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Malformed data byte group at line %d; uneven length"), lineNumber(pInfo
));
2287 CFDataSetLength(result
, length
+ tlength
/2);
2288 bytes
= (unsigned char *) CFDataGetMutableBytePtr(result
) + length
;
2289 length
+= tlength
/ 2;
2290 for (idx
= 0; idx
< tlength
; idx
+= 2) {
2291 unsigned char hi
= fromHexDigit(CFStringGetCharacterAtIndex(token
, idx
)), lo
= fromHexDigit(CFStringGetCharacterAtIndex(token
, idx
+1));
2292 if (hi
== 0xff || lo
== 0xff) {
2295 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2296 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Malformed data byte group at line %d; invalid hex"), lineNumber(pInfo
));
2300 *bytes
= (hi
<< 4) + lo
;
2305 advanceToNonSpace(pInfo
);
2307 if (pInfo
->errorString
) {
2308 CFRelease(pInfo
->errorString
);
2309 pInfo
->errorString
= NULL
;
2312 if (*(pInfo
->curr
) == '>') {
2313 pInfo
->curr
++; // Move past '>'
2317 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2318 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Expected terminating '>' for data at line %d"), lineNumber(pInfo
));
2324 // Returned object is retained; caller must free.
2325 static CFTypeRef
parsePlistObject(_CFXMLPlistParseInfo
*pInfo
, bool requireObject
) {
2327 advanceToNonSpace(pInfo
);
2328 if (pInfo
->curr
+ 1 >= pInfo
->end
) {
2329 if (requireObject
&& _CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2330 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unexpected EOF while parsing plist"));
2334 ch
= *(pInfo
->curr
);
2337 return parsePlistDict(pInfo
);
2338 } else if (ch
== '(') {
2339 return parsePlistArray(pInfo
);
2340 } else if (ch
== '<') {
2341 return parsePlistData(pInfo
);
2342 } else if (ch
== '\'' || ch
== '\"') {
2343 return parseQuotedPlistString(pInfo
, ch
);
2344 } else if (isValidUnquotedStringCharacter(ch
)) {
2346 return parseUnquotedPlistString(pInfo
);
2348 pInfo
->curr
--; // Must back off the charcter we just read
2349 if (requireObject
&& _CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2350 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Unexpected character '0x%x' at line %d"), ch
, lineNumber(pInfo
));
2356 static CFTypeRef
parseOldStylePropertyListOrStringsFile(_CFXMLPlistParseInfo
*pInfo
) {
2357 const UniChar
*begin
= pInfo
->curr
;
2359 advanceToNonSpace(pInfo
);
2360 // A file consisting only of whitespace (or empty) is now defined to be an empty dictionary
2361 if (pInfo
->curr
>= pInfo
->end
) return CFDictionaryCreateMutable(pInfo
->allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2362 result
= parsePlistObject(pInfo
, true);
2363 advanceToNonSpace(pInfo
);
2364 if (pInfo
->curr
>= pInfo
->end
) return result
;
2365 if (!result
) return NULL
;
2366 if (CFGetTypeID(result
) != CFStringGetTypeID()) {
2368 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2369 pInfo
->errorString
= CFStringCreateWithFormat(pInfo
->allocator
, NULL
, CFSTR("Junk after plist at line %d"), lineNumber(pInfo
));
2374 // Check for a strings file (looks like a dictionary without the opening/closing curly braces)
2375 pInfo
->curr
= begin
;
2376 return parsePlistDictContent(pInfo
);
2379 #undef isValidUnquotedStringCharacter
2381 static CFArrayRef
_arrayDeepImmutableCopy(CFAllocatorRef allocator
, CFArrayRef array
, CFOptionFlags mutabilityOption
) {
2382 CFArrayRef result
= NULL
;
2383 CFIndex i
, c
= CFArrayGetCount(array
);
2386 result
= CFArrayCreate(allocator
, NULL
, 0, &kCFTypeArrayCallBacks
);
2387 } else if ((values
= CFAllocatorAllocate(allocator
, c
*sizeof(CFTypeRef
), 0)) != NULL
) {
2388 CFArrayGetValues(array
, CFRangeMake(0, c
), values
);
2389 for (i
= 0; i
< c
; i
++) {
2390 values
[i
] = CFPropertyListCreateDeepCopy(allocator
, values
[i
], mutabilityOption
);
2391 if (values
[i
] == NULL
) {
2395 result
= (i
== c
) ? CFArrayCreate(allocator
, values
, c
, &kCFTypeArrayCallBacks
) : NULL
;
2397 for (i
= 0; i
< c
; i
++) {
2398 CFRelease(values
[i
]);
2400 CFAllocatorDeallocate(allocator
, values
);
2405 static CFMutableArrayRef
_arrayDeepMutableCopy(CFAllocatorRef allocator
, CFArrayRef array
, CFOptionFlags mutabilityOption
) {
2406 CFIndex i
, c
= CFArrayGetCount(array
);
2407 CFMutableArrayRef result
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2409 for (i
= 0; i
< c
; i
++) {
2410 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(allocator
, CFArrayGetValueAtIndex(array
, i
), mutabilityOption
);
2411 if (!newValue
) break;
2412 CFArrayAppendValue(result
, newValue
);
2413 CFRelease(newValue
);
2423 CFPropertyListRef
CFPropertyListCreateDeepCopy(CFAllocatorRef allocator
, CFPropertyListRef propertyList
, CFOptionFlags mutabilityOption
) {
2425 CFPropertyListRef result
= NULL
;
2426 CFAssert1(propertyList
!= NULL
, __kCFLogAssertion
, "%s(): cannot copy a NULL property list", __PRETTY_FUNCTION__
);
2427 __CFAssertIsPList(propertyList
);
2428 CFAssert2(mutabilityOption
== kCFPropertyListImmutable
|| mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
, __kCFLogAssertion
, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__
, mutabilityOption
);
2429 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar
)) {
2430 if (!CFPropertyListIsValid(propertyList
, kCFPropertyListBinaryFormat_v1_0
)) return NULL
;
2433 if (allocator
== NULL
) {
2434 allocator
= CFRetain(__CFGetDefaultAllocator());
2436 CFRetain(allocator
);
2439 typeID
= CFGetTypeID(propertyList
);
2440 if (typeID
== CFDictionaryGetTypeID()) {
2441 CFDictionaryRef dict
= (CFDictionaryRef
)propertyList
;
2442 Boolean
mutable = (mutabilityOption
!= kCFPropertyListImmutable
);
2443 CFIndex count
= CFDictionaryGetCount(dict
);
2444 CFTypeRef
*keys
, *values
;
2446 result
= mutable ? CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
): CFDictionaryCreate(allocator
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2447 } else if ((keys
= CFAllocatorAllocate(allocator
, 2 * count
* sizeof(CFTypeRef
), 0)) != NULL
) {
2449 values
= keys
+count
;
2450 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
2451 for (i
= 0; i
< count
; i
++) {
2452 keys
[i
] = CFStringCreateCopy(allocator
, keys
[i
]);
2453 if (keys
[i
] == NULL
) {
2456 values
[i
] = CFPropertyListCreateDeepCopy(allocator
, values
[i
], mutabilityOption
);
2457 if (values
[i
] == NULL
) {
2463 result
= mutable ? CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
) : CFDictionaryCreate(allocator
, keys
, values
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2464 for (i
= 0; i
< count
; i
++) {
2466 CFDictionarySetValue((CFMutableDictionaryRef
)result
, keys
[i
], values
[i
]);
2469 CFRelease(values
[i
]);
2474 for (i
= 0; i
< count
; i
++) {
2476 CFRelease(values
[i
]);
2479 CFAllocatorDeallocate(allocator
, keys
);
2483 } else if (typeID
== CFArrayGetTypeID()) {
2484 if (mutabilityOption
== kCFPropertyListImmutable
) {
2485 result
= _arrayDeepImmutableCopy(allocator
, (CFArrayRef
)propertyList
, mutabilityOption
);
2487 result
= _arrayDeepMutableCopy(allocator
, (CFArrayRef
)propertyList
, mutabilityOption
);
2489 } else if (typeID
== CFDataGetTypeID()) {
2490 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
2491 result
= CFDataCreateMutableCopy(allocator
, 0, (CFDataRef
)propertyList
);
2493 result
= CFDataCreateCopy(allocator
, (CFDataRef
)propertyList
);
2495 } else if (typeID
== CFNumberGetTypeID()) {
2496 // Warning - this will break if byteSize is ever greater than 16
2498 CFNumberType numType
= CFNumberGetType((CFNumberRef
)propertyList
);
2499 CFNumberGetValue((CFNumberRef
)propertyList
, numType
, (void *)bytes
);
2500 result
= CFNumberCreate(allocator
, numType
, (void *)bytes
);
2501 } else if (typeID
== CFBooleanGetTypeID()) {
2502 // Booleans are immutable & shared instances
2503 CFRetain(propertyList
);
2504 result
= propertyList
;
2505 } else if (typeID
== CFDateGetTypeID()) {
2506 // Dates are immutable
2507 result
= CFDateCreate(allocator
, CFDateGetAbsoluteTime((CFDateRef
)propertyList
));
2508 } else if (typeID
== CFStringGetTypeID()) {
2509 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
2510 result
= CFStringCreateMutableCopy(allocator
, 0, (CFStringRef
)propertyList
);
2512 result
= CFStringCreateCopy(allocator
, (CFStringRef
)propertyList
);
2515 CFAssert2(false, __kCFLogAssertion
, "%s(): 0x%x is not a property list type", __PRETTY_FUNCTION__
, propertyList
);
2518 CFRelease(allocator
);