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>
32 #include <utilities/SecCFRelease.h>
38 // Format codes for consistency
43 #define F_DICTIONARY 'D'
50 // Initialize a CFMunge. We start out with the default CFAllocator, and
51 // we do not throw errors.
53 CFMunge::CFMunge(const char *fmt
, va_list arg
)
54 : format(fmt
), allocator(NULL
), error(errSecSuccess
)
66 // Skip whitespace and other fluff and deliver the next significant character.
70 while (*format
&& (isspace(*format
) || *format
== ',')) ++format
;
76 // Locate and consume an optional character
78 bool CFMunge::next(char c
)
89 // Process @? parameter specifications.
90 // The @ operator is used for side effects, and does not return a value.
92 bool CFMunge::parameter()
97 allocator
= va_arg(args
, CFAllocatorRef
);
101 error
= va_arg(args
, OSStatus
);
110 // The top constructor.
112 CFTypeRef
CFMake::make()
114 while (next() == '@')
120 return makedictionary();
128 return makespecial();
131 return NULL
; // error
133 if (isdigit(*format
) || *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
= NULL
;
294 if (next('+')) { // {+%O, => copy array argument, then proceed
295 if (next('%') && next('O')) {
296 CFArrayRef source
= va_arg(args
, CFArrayRef
);
297 array
= CFArrayCreateMutableCopy(allocator
, 0, source
);
301 return NULL
; // bad syntax
303 array
= makeCFMutableArray(0);
305 while (next() != ']') {
306 CFTypeRef value
= make();
311 CFArrayAppendValue(array
, value
);
320 // A CFScan processes its format by parsing through an existing CF object
321 // structure, matching and extracting values as directed. Note that CFScan
322 // is a structure (tree) scanner rather than a linear parser, and will happily
323 // parse out a subset of the input object graph.
325 class CFScan
: public CFMake
{
327 CFScan(const char *format
, va_list args
)
328 : CFMake(format
, args
), suppress(false) { }
330 bool scan(CFTypeRef obj
);
331 CFTypeRef
dictpath(CFTypeRef obj
);
334 bool scandictionary(CFDictionaryRef obj
);
335 bool scanarray(CFArrayRef obj
);
336 bool scanformat(CFTypeRef obj
);
338 enum Typescan
{ fail
= -1, more
= 0, done
= 1 };
339 Typescan
typescan(CFTypeRef obj
, CFTypeID type
);
341 template <class Value
>
342 bool scannumber(CFTypeRef obj
);
344 template <class Type
>
345 void store(Type value
);
347 bool suppress
; // output suppression
352 // Master scan function
354 bool CFScan::scan(CFTypeRef obj
)
356 while (next() == '@')
360 return true; // done, okay
362 if (obj
&& CFGetTypeID(obj
) != CFDictionaryGetTypeID())
364 return scandictionary(CFDictionaryRef(obj
));
366 if (obj
&& CFGetTypeID(obj
) != CFArrayGetTypeID())
368 return scanarray(CFArrayRef(obj
));
369 case '%': // return this value in some form
370 return scanformat(obj
);
371 case '=': // match value
374 CFTypeRef match
= make();
375 bool rc
= CFEqual(obj
, match
);
381 assert(false); // unexpected
391 // Primitive type-match helper.
392 // Ensures the object has the CF runtime type required, and processes
393 // the %?o format (return CFTypeRef) and %?n format (ignore value).
395 CFScan::Typescan
CFScan::typescan(CFTypeRef obj
, CFTypeID type
)
397 if (obj
&& CFGetTypeID(obj
) != type
)
400 case F_OBJECT
: // return CFTypeRef
402 store
<CFTypeRef
>(obj
);
404 case 'n': // suppress assignment
414 // Store a value into the next varargs slot, unless output suppression is on.
416 template <class Type
>
417 void CFScan::store(Type value
)
420 *va_arg(args
, Type
*) = value
;
425 // Convert a CFNumber to an external numeric form
427 template <class Value
>
428 bool CFScan::scannumber(CFTypeRef obj
)
430 ++format
; // consume format code
432 return true; // suppressed, okay
433 if (CFGetTypeID(obj
) != CFNumberGetTypeID())
435 store
<Value
>(cfNumber
<Value
>(CFNumberRef(obj
)));
441 // Process % scan forms.
442 // This delivers the object value, scanf-style, somehow.
444 bool CFScan::scanformat(CFTypeRef obj
)
448 store
<CFTypeRef
>(obj
);
451 return typescan(obj
, CFArrayGetTypeID()) == done
;
453 if (Typescan rc
= typescan(obj
, CFBooleanGetTypeID()))
456 case 'f': // %Bf - two arguments (value, &variable)
458 unsigned flag
= va_arg(args
, unsigned);
459 unsigned *value
= va_arg(args
, unsigned *);
460 if (obj
== kCFBooleanTrue
&& !suppress
)
464 default: // %b - CFBoolean as int boolean
465 store
<int>(obj
== kCFBooleanTrue
);
469 return typescan(obj
, CFDictionaryGetTypeID()) == done
;
470 case 'd': // %d - int
471 return scannumber
<int>(obj
);
473 return typescan(obj
, CFNumberGetTypeID()) == done
;
476 if (Typescan rc
= typescan(obj
, CFStringGetTypeID()))
479 store
<std::string
>(cfString(CFStringRef(obj
)));
482 return scannumber
<unsigned int>(obj
);
484 return typescan(obj
, CFDataGetTypeID()) == done
;
492 bool CFScan::scandictionary(CFDictionaryRef obj
)
494 ++format
; // skip '{'
495 while (next() != '}') {
496 bool optional
= next('?');
497 if (CFTypeRef key
= make()) {
498 bool oldSuppress
= suppress
;
499 CFTypeRef elem
= obj
? CFDictionaryGetValue(obj
, key
) : NULL
;
500 if (elem
|| optional
) {
501 suppress
|= (elem
== NULL
);
504 suppress
= oldSuppress
; // restore
513 assert(false); // bad format
521 bool CFScan::scanarray(CFArrayRef obj
)
523 ++format
; // skip '['
524 CFIndex length
= CFArrayGetCount(obj
);
525 for (int pos
= 0; pos
< length
; ++pos
) {
528 if (!scan(CFArrayGetValueAtIndex(obj
, pos
)))
531 return false; // array length exceeded
536 // Run down a "dictionary path", validating heavily.
538 CFTypeRef
CFScan::dictpath(CFTypeRef obj
)
540 while (next()) { // while we've got more text
541 next('.'); // optional
542 if (obj
== NULL
|| CFGetTypeID(obj
) != CFDictionaryGetTypeID())
544 CFTypeRef key
= make();
545 obj
= CFDictionaryGetValue(CFDictionaryRef(obj
), key
);
553 // The public functions
555 CFTypeRef
cfmake(const char *format
, ...)
558 va_start(args
, format
);
559 CFTypeRef result
= CFMake(format
, args
).make();
564 CFTypeRef
vcfmake(const char *format
, va_list args
)
566 return CFMake(format
, args
).make();
569 CFDictionaryRef
cfadd(CFMutableDictionaryRef dict
, const char *format
, ...)
572 va_start(args
, format
);
573 CFDictionaryRef result
= CFMake(format
, args
).addto(dict
);
579 bool cfscan(CFTypeRef obj
, const char *format
, ...)
582 va_start(args
, format
);
583 bool result
= vcfscan(obj
, format
, args
);
588 bool vcfscan(CFTypeRef obj
, const char *format
, va_list args
)
590 return CFScan(format
, args
).scan(obj
);
594 CFTypeRef
cfget(CFTypeRef obj
, const char *format
, ...)
597 va_start(args
, format
);
598 CFTypeRef result
= vcfget(obj
, format
, args
);
603 CFTypeRef
vcfget(CFTypeRef obj
, const char *format
, va_list args
)
605 return CFScan(format
, args
).dictpath(obj
);
608 } // end namespace Security