2 * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 // CoreFoundation building and parsing functions.
26 // These classes provide a printf/scanf-like interface to nested data structures
27 // of the Property List Subset of CoreFoundation.
30 #include <security_utilities/cfutilities.h>
31 #include <security_utilities/errors.h>
37 // Format codes for consistency
42 #define F_DICTIONARY 'D'
49 // Initialize a CFMunge. We start out with the default CFAllocator, and
50 // we do not throw errors.
52 CFMunge::CFMunge(const char *fmt
, va_list arg
)
53 : format(fmt
), allocator(NULL
), error(errSecSuccess
)
65 // Skip whitespace and other fluff and deliver the next significant character.
69 while (*format
&& (isspace(*format
) || *format
== ',')) ++format
;
75 // Locate and consume an optional character
77 bool CFMunge::next(char c
)
88 // Process @? parameter specifications.
89 // The @ operator is used for side effects, and does not return a value.
91 bool CFMunge::parameter()
96 allocator
= va_arg(args
, CFAllocatorRef
);
100 error
= va_arg(args
, OSStatus
);
109 // The top constructor.
111 CFTypeRef
CFMake::make()
113 while (next() == '@')
119 return makedictionary();
127 return makespecial();
130 assert(false); // unexpected
131 return NULL
; // error
133 if (isdigit(*format
))
135 else if (isalpha(*format
))
145 CFTypeRef
CFMake::makeformat()
149 case 'b': // blob (pointer, length)
151 const void *data
= va_arg(args
, const void *);
152 size_t length
= va_arg(args
, size_t);
153 return CFDataCreate(allocator
, (const UInt8
*)data
, length
);
155 case F_BOOLEAN
: // boolean (with int promotion)
156 return va_arg(args
, int) ? kCFBooleanTrue
: kCFBooleanFalse
;
158 return makeCFNumber(va_arg(args
, int));
160 return CFStringCreateWithCString(allocator
, va_arg(args
, const char *),
161 kCFStringEncodingUTF8
);
163 return CFRetain(va_arg(args
, CFTypeRef
));
165 return makeCFNumber(va_arg(args
, unsigned int));
173 CFTypeRef
CFMake::makespecial()
181 return kCFBooleanTrue
;
184 return kCFBooleanFalse
;
192 CFTypeRef
CFMake::makenumber()
194 double value
= strtod(format
, (char **)&format
);
195 return CFNumberCreate(allocator
, kCFNumberDoubleType
, &value
);
200 // Embedded strings can either be alphanumeric (only), or delimited with single quotes ''.
201 // No escapes are processed within such quotes. If you want arbitrary string values, use %s.
203 CFTypeRef
CFMake::makestring()
205 const char *start
, *end
;
206 if (*format
== '\'') {
207 start
= ++format
; // next quote
208 if (!(end
= strchr(format
, '\''))) {
215 for (end
= start
+ 1; isalnum(*end
); ++end
) ;
218 return CFStringCreateWithBytes(allocator
,
219 (const UInt8
*)start
, end
- start
,
220 kCFStringEncodingUTF8
, false);
225 // Construct a CFDictionary
227 CFTypeRef
CFMake::makedictionary()
229 ++format
; // next '{'
230 next('!'); // indicates mutable (currently always true)
231 CFMutableDictionaryRef dict
;
232 if (next('+')) { // {+%O, => copy dictionary argument, then proceed
233 if (next('%') && next('O')) {
234 CFDictionaryRef source
= va_arg(args
, CFDictionaryRef
);
235 dict
= CFDictionaryCreateMutableCopy(allocator
, NULL
, source
);
239 return NULL
; // bad syntax
241 dict
= CFDictionaryCreateMutable(allocator
, 0,
242 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
251 CFDictionaryRef
CFMake::add(CFMutableDictionaryRef dict
)
253 while (next() != '}') {
254 CFTypeRef key
= make();
261 if (CFTypeRef value
= make()) {
262 CFDictionaryAddValue(dict
, key
, value
);
275 CFDictionaryRef
CFMake::addto(CFMutableDictionaryRef dict
)
287 // Construct a CFArray
289 CFTypeRef
CFMake::makearray()
291 ++format
; // next '['
292 next('!'); // indicates mutable (currently always)
293 CFMutableArrayRef array
= makeCFMutableArray(0);
294 while (next() != ']') {
295 CFTypeRef value
= make();
300 CFArrayAppendValue(array
, value
);
309 // A CFScan processes its format by parsing through an existing CF object
310 // structure, matching and extracting values as directed. Note that CFScan
311 // is a structure (tree) scanner rather than a linear parser, and will happily
312 // parse out a subset of the input object graph.
314 class CFScan
: public CFMake
{
316 CFScan(const char *format
, va_list args
)
317 : CFMake(format
, args
), suppress(false) { }
319 bool scan(CFTypeRef obj
);
320 CFTypeRef
dictpath(CFTypeRef obj
);
323 bool scandictionary(CFDictionaryRef obj
);
324 bool scanarray(CFArrayRef obj
);
325 bool scanformat(CFTypeRef obj
);
327 enum Typescan
{ fail
= -1, more
= 0, done
= 1 };
328 Typescan
typescan(CFTypeRef obj
, CFTypeID type
);
330 template <class Value
>
331 bool scannumber(CFTypeRef obj
);
333 template <class Type
>
334 void store(Type value
);
336 bool suppress
; // output suppression
341 // Master scan function
343 bool CFScan::scan(CFTypeRef obj
)
345 while (next() == '@')
349 return true; // done, okay
351 if (obj
&& CFGetTypeID(obj
) != CFDictionaryGetTypeID())
353 return scandictionary(CFDictionaryRef(obj
));
355 if (obj
&& CFGetTypeID(obj
) != CFArrayGetTypeID())
357 return scanarray(CFArrayRef(obj
));
358 case '%': // return this value in some form
359 return scanformat(obj
);
360 case '=': // match value
363 CFTypeRef match
= make();
364 bool rc
= CFEqual(obj
, match
);
370 assert(false); // unexpected
380 // Primitive type-match helper.
381 // Ensures the object has the CF runtime type required, and processes
382 // the %?o format (return CFTypeRef) and %?n format (ignore value).
384 CFScan::Typescan
CFScan::typescan(CFTypeRef obj
, CFTypeID type
)
386 if (obj
&& CFGetTypeID(obj
) != type
)
389 case F_OBJECT
: // return CFTypeRef
391 store
<CFTypeRef
>(obj
);
393 case 'n': // suppress assignment
403 // Store a value into the next varargs slot, unless output suppression is on.
405 template <class Type
>
406 void CFScan::store(Type value
)
409 *va_arg(args
, Type
*) = value
;
414 // Convert a CFNumber to an external numeric form
416 template <class Value
>
417 bool CFScan::scannumber(CFTypeRef obj
)
419 ++format
; // consume format code
421 return true; // suppressed, okay
422 if (CFGetTypeID(obj
) != CFNumberGetTypeID())
424 store
<Value
>(cfNumber
<Value
>(CFNumberRef(obj
)));
430 // Process % scan forms.
431 // This delivers the object value, scanf-style, somehow.
433 bool CFScan::scanformat(CFTypeRef obj
)
437 store
<CFTypeRef
>(obj
);
440 return typescan(obj
, CFArrayGetTypeID()) == done
;
442 if (Typescan rc
= typescan(obj
, CFBooleanGetTypeID()))
445 case 'f': // %Bf - two arguments (value, &variable)
447 unsigned flag
= va_arg(args
, unsigned);
448 unsigned *value
= va_arg(args
, unsigned *);
449 if (obj
== kCFBooleanTrue
&& !suppress
)
453 default: // %b - CFBoolean as int boolean
454 store
<int>(obj
== kCFBooleanTrue
);
458 return typescan(obj
, CFDictionaryGetTypeID()) == done
;
459 case 'd': // %d - int
460 return scannumber
<int>(obj
);
462 return typescan(obj
, CFNumberGetTypeID()) == done
;
465 if (Typescan rc
= typescan(obj
, CFStringGetTypeID()))
468 store
<std::string
>(cfString(CFStringRef(obj
)));
471 return scannumber
<unsigned int>(obj
);
473 return typescan(obj
, CFDataGetTypeID()) == done
;
481 bool CFScan::scandictionary(CFDictionaryRef obj
)
483 ++format
; // skip '{'
484 while (next() != '}') {
485 bool optional
= next('?');
486 if (CFTypeRef key
= make()) {
487 bool oldSuppress
= suppress
;
488 CFTypeRef elem
= obj
? CFDictionaryGetValue(obj
, key
) : NULL
;
489 if (elem
|| optional
) {
490 suppress
|= (elem
== NULL
);
493 suppress
= oldSuppress
; // restore
502 assert(false); // bad format
510 bool CFScan::scanarray(CFArrayRef obj
)
512 ++format
; // skip '['
513 CFIndex length
= CFArrayGetCount(obj
);
514 for (int pos
= 0; pos
< length
; ++pos
) {
517 if (!scan(CFArrayGetValueAtIndex(obj
, pos
)))
520 return false; // array length exceeded
525 // Run down a "dictionary path", validating heavily.
527 CFTypeRef
CFScan::dictpath(CFTypeRef obj
)
529 while (next()) { // while we've got more text
530 next('.'); // optional
531 if (obj
== NULL
|| CFGetTypeID(obj
) != CFDictionaryGetTypeID())
533 CFTypeRef key
= make();
534 obj
= CFDictionaryGetValue(CFDictionaryRef(obj
), key
);
542 // The public functions
544 CFTypeRef
cfmake(const char *format
, ...)
547 va_start(args
, format
);
548 CFTypeRef result
= CFMake(format
, args
).make();
553 CFTypeRef
vcfmake(const char *format
, va_list args
)
555 return CFMake(format
, args
).make();
558 CFDictionaryRef
cfadd(CFMutableDictionaryRef dict
, const char *format
, ...)
561 va_start(args
, format
);
562 CFDictionaryRef result
= CFMake(format
, args
).addto(dict
);
568 bool cfscan(CFTypeRef obj
, const char *format
, ...)
571 va_start(args
, format
);
572 bool result
= vcfscan(obj
, format
, args
);
577 bool vcfscan(CFTypeRef obj
, const char *format
, va_list args
)
579 return CFScan(format
, args
).scan(obj
);
583 CFTypeRef
cfget(CFTypeRef obj
, const char *format
, ...)
586 va_start(args
, format
);
587 CFTypeRef result
= vcfget(obj
, format
, args
);
592 CFTypeRef
vcfget(CFTypeRef obj
, const char *format
, va_list args
)
594 return CFScan(format
, args
).dictpath(obj
);
597 } // end namespace Security