2 * Copyright (c) 2006-2007,2011,2013-2014 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 return NULL
; // error
132 if (isdigit(*format
) || *format
== '-')
134 else if (isalpha(*format
))
144 CFTypeRef
CFMake::makeformat()
148 case 'b': // blob (pointer, length)
150 const void *data
= va_arg(args
, const void *);
151 size_t length
= va_arg(args
, size_t);
152 return CFDataCreate(allocator
, (const UInt8
*)data
, length
);
154 case F_BOOLEAN
: // boolean (with int promotion)
155 return va_arg(args
, int) ? kCFBooleanTrue
: kCFBooleanFalse
;
157 return makeCFNumber(va_arg(args
, int));
159 return CFStringCreateWithCString(allocator
, va_arg(args
, const char *),
160 kCFStringEncodingUTF8
);
162 return CFRetain(va_arg(args
, CFTypeRef
));
164 return makeCFNumber(va_arg(args
, unsigned int));
172 CFTypeRef
CFMake::makespecial()
180 return kCFBooleanTrue
;
183 return kCFBooleanFalse
;
191 CFTypeRef
CFMake::makenumber()
193 double value
= strtod(format
, (char **)&format
);
194 return CFNumberCreate(allocator
, kCFNumberDoubleType
, &value
);
199 // Embedded strings can either be alphanumeric (only), or delimited with single quotes ''.
200 // No escapes are processed within such quotes. If you want arbitrary string values, use %s.
202 CFTypeRef
CFMake::makestring()
204 const char *start
, *end
;
205 if (*format
== '\'') {
206 start
= ++format
; // next quote
207 if (!(end
= strchr(format
, '\''))) {
214 for (end
= start
+ 1; isalnum(*end
); ++end
) ;
217 return CFStringCreateWithBytes(allocator
,
218 (const UInt8
*)start
, end
- start
,
219 kCFStringEncodingUTF8
, false);
224 // Construct a CFDictionary
226 CFTypeRef
CFMake::makedictionary()
228 ++format
; // next '{'
229 next('!'); // indicates mutable (currently always true)
230 CFMutableDictionaryRef dict
;
231 if (next('+')) { // {+%O, => copy dictionary argument, then proceed
232 if (next('%') && next('O')) {
233 CFDictionaryRef source
= va_arg(args
, CFDictionaryRef
);
234 dict
= CFDictionaryCreateMutableCopy(allocator
, NULL
, source
);
238 return NULL
; // bad syntax
240 dict
= CFDictionaryCreateMutable(allocator
, 0,
241 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
250 CFDictionaryRef
CFMake::add(CFMutableDictionaryRef dict
)
252 while (next() != '}') {
253 CFTypeRef key
= make();
260 if (CFTypeRef value
= make()) {
261 CFDictionaryAddValue(dict
, key
, value
);
274 CFDictionaryRef
CFMake::addto(CFMutableDictionaryRef dict
)
286 // Construct a CFArray
288 CFTypeRef
CFMake::makearray()
290 ++format
; // next '['
291 next('!'); // indicates mutable (currently always)
292 CFMutableArrayRef array
= NULL
;
293 if (next('+')) { // {+%O, => copy array argument, then proceed
294 if (next('%') && next('O')) {
295 CFArrayRef source
= va_arg(args
, CFArrayRef
);
296 array
= CFArrayCreateMutableCopy(allocator
, 0, source
);
300 return NULL
; // bad syntax
302 array
= makeCFMutableArray(0);
304 while (next() != ']') {
305 CFTypeRef value
= make();
310 CFArrayAppendValue(array
, value
);
319 // A CFScan processes its format by parsing through an existing CF object
320 // structure, matching and extracting values as directed. Note that CFScan
321 // is a structure (tree) scanner rather than a linear parser, and will happily
322 // parse out a subset of the input object graph.
324 class CFScan
: public CFMake
{
326 CFScan(const char *format
, va_list args
)
327 : CFMake(format
, args
), suppress(false) { }
329 bool scan(CFTypeRef obj
);
330 CFTypeRef
dictpath(CFTypeRef obj
);
333 bool scandictionary(CFDictionaryRef obj
);
334 bool scanarray(CFArrayRef obj
);
335 bool scanformat(CFTypeRef obj
);
337 enum Typescan
{ fail
= -1, more
= 0, done
= 1 };
338 Typescan
typescan(CFTypeRef obj
, CFTypeID type
);
340 template <class Value
>
341 bool scannumber(CFTypeRef obj
);
343 template <class Type
>
344 void store(Type value
);
346 bool suppress
; // output suppression
351 // Master scan function
353 bool CFScan::scan(CFTypeRef obj
)
355 while (next() == '@')
359 return true; // done, okay
361 if (obj
&& CFGetTypeID(obj
) != CFDictionaryGetTypeID())
363 return scandictionary(CFDictionaryRef(obj
));
365 if (obj
&& CFGetTypeID(obj
) != CFArrayGetTypeID())
367 return scanarray(CFArrayRef(obj
));
368 case '%': // return this value in some form
369 return scanformat(obj
);
370 case '=': // match value
373 CFTypeRef match
= make();
374 bool rc
= CFEqual(obj
, match
);
380 assert(false); // unexpected
390 // Primitive type-match helper.
391 // Ensures the object has the CF runtime type required, and processes
392 // the %?o format (return CFTypeRef) and %?n format (ignore value).
394 CFScan::Typescan
CFScan::typescan(CFTypeRef obj
, CFTypeID type
)
396 if (obj
&& CFGetTypeID(obj
) != type
)
399 case F_OBJECT
: // return CFTypeRef
401 store
<CFTypeRef
>(obj
);
403 case 'n': // suppress assignment
413 // Store a value into the next varargs slot, unless output suppression is on.
415 template <class Type
>
416 void CFScan::store(Type value
)
419 *va_arg(args
, Type
*) = value
;
424 // Convert a CFNumber to an external numeric form
426 template <class Value
>
427 bool CFScan::scannumber(CFTypeRef obj
)
429 ++format
; // consume format code
431 return true; // suppressed, okay
432 if (CFGetTypeID(obj
) != CFNumberGetTypeID())
434 store
<Value
>(cfNumber
<Value
>(CFNumberRef(obj
)));
440 // Process % scan forms.
441 // This delivers the object value, scanf-style, somehow.
443 bool CFScan::scanformat(CFTypeRef obj
)
447 store
<CFTypeRef
>(obj
);
450 return typescan(obj
, CFArrayGetTypeID()) == done
;
452 if (Typescan rc
= typescan(obj
, CFBooleanGetTypeID()))
455 case 'f': // %Bf - two arguments (value, &variable)
457 unsigned flag
= va_arg(args
, unsigned);
458 unsigned *value
= va_arg(args
, unsigned *);
459 if (obj
== kCFBooleanTrue
&& !suppress
)
463 default: // %b - CFBoolean as int boolean
464 store
<int>(obj
== kCFBooleanTrue
);
468 return typescan(obj
, CFDictionaryGetTypeID()) == done
;
469 case 'd': // %d - int
470 return scannumber
<int>(obj
);
472 return typescan(obj
, CFNumberGetTypeID()) == done
;
475 if (Typescan rc
= typescan(obj
, CFStringGetTypeID()))
478 store
<std::string
>(cfString(CFStringRef(obj
)));
481 return scannumber
<unsigned int>(obj
);
483 return typescan(obj
, CFDataGetTypeID()) == done
;
491 bool CFScan::scandictionary(CFDictionaryRef obj
)
493 ++format
; // skip '{'
494 while (next() != '}') {
495 bool optional
= next('?');
496 if (CFTypeRef key
= make()) {
497 bool oldSuppress
= suppress
;
498 CFTypeRef elem
= obj
? CFDictionaryGetValue(obj
, key
) : NULL
;
499 if (elem
|| optional
) {
500 suppress
|= (elem
== NULL
);
503 suppress
= oldSuppress
; // restore
512 assert(false); // bad format
520 bool CFScan::scanarray(CFArrayRef obj
)
522 ++format
; // skip '['
523 CFIndex length
= CFArrayGetCount(obj
);
524 for (int pos
= 0; pos
< length
; ++pos
) {
527 if (!scan(CFArrayGetValueAtIndex(obj
, pos
)))
530 return false; // array length exceeded
535 // Run down a "dictionary path", validating heavily.
537 CFTypeRef
CFScan::dictpath(CFTypeRef obj
)
539 while (next()) { // while we've got more text
540 next('.'); // optional
541 if (obj
== NULL
|| CFGetTypeID(obj
) != CFDictionaryGetTypeID())
543 CFTypeRef key
= make();
544 obj
= CFDictionaryGetValue(CFDictionaryRef(obj
), key
);
552 // The public functions
554 CFTypeRef
cfmake(const char *format
, ...)
557 va_start(args
, format
);
558 CFTypeRef result
= CFMake(format
, args
).make();
563 CFTypeRef
vcfmake(const char *format
, va_list args
)
565 return CFMake(format
, args
).make();
568 CFDictionaryRef
cfadd(CFMutableDictionaryRef dict
, const char *format
, ...)
571 va_start(args
, format
);
572 CFDictionaryRef result
= CFMake(format
, args
).addto(dict
);
578 bool cfscan(CFTypeRef obj
, const char *format
, ...)
581 va_start(args
, format
);
582 bool result
= vcfscan(obj
, format
, args
);
587 bool vcfscan(CFTypeRef obj
, const char *format
, va_list args
)
589 return CFScan(format
, args
).scan(obj
);
593 CFTypeRef
cfget(CFTypeRef obj
, const char *format
, ...)
596 va_start(args
, format
);
597 CFTypeRef result
= vcfget(obj
, format
, args
);
602 CFTypeRef
vcfget(CFTypeRef obj
, const char *format
, va_list args
)
604 return CFScan(format
, args
).dictpath(obj
);
607 } // end namespace Security