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
= makeCFMutableArray(0);
293 while (next() != ']') {
294 CFTypeRef value
= make();
299 CFArrayAppendValue(array
, value
);
308 // A CFScan processes its format by parsing through an existing CF object
309 // structure, matching and extracting values as directed. Note that CFScan
310 // is a structure (tree) scanner rather than a linear parser, and will happily
311 // parse out a subset of the input object graph.
313 class CFScan
: public CFMake
{
315 CFScan(const char *format
, va_list args
)
316 : CFMake(format
, args
), suppress(false) { }
318 bool scan(CFTypeRef obj
);
319 CFTypeRef
dictpath(CFTypeRef obj
);
322 bool scandictionary(CFDictionaryRef obj
);
323 bool scanarray(CFArrayRef obj
);
324 bool scanformat(CFTypeRef obj
);
326 enum Typescan
{ fail
= -1, more
= 0, done
= 1 };
327 Typescan
typescan(CFTypeRef obj
, CFTypeID type
);
329 template <class Value
>
330 bool scannumber(CFTypeRef obj
);
332 template <class Type
>
333 void store(Type value
);
335 bool suppress
; // output suppression
340 // Master scan function
342 bool CFScan::scan(CFTypeRef obj
)
344 while (next() == '@')
348 return true; // done, okay
350 if (obj
&& CFGetTypeID(obj
) != CFDictionaryGetTypeID())
352 return scandictionary(CFDictionaryRef(obj
));
354 if (obj
&& CFGetTypeID(obj
) != CFArrayGetTypeID())
356 return scanarray(CFArrayRef(obj
));
357 case '%': // return this value in some form
358 return scanformat(obj
);
359 case '=': // match value
362 CFTypeRef match
= make();
363 bool rc
= CFEqual(obj
, match
);
369 assert(false); // unexpected
379 // Primitive type-match helper.
380 // Ensures the object has the CF runtime type required, and processes
381 // the %?o format (return CFTypeRef) and %?n format (ignore value).
383 CFScan::Typescan
CFScan::typescan(CFTypeRef obj
, CFTypeID type
)
385 if (obj
&& CFGetTypeID(obj
) != type
)
388 case F_OBJECT
: // return CFTypeRef
390 store
<CFTypeRef
>(obj
);
392 case 'n': // suppress assignment
402 // Store a value into the next varargs slot, unless output suppression is on.
404 template <class Type
>
405 void CFScan::store(Type value
)
408 *va_arg(args
, Type
*) = value
;
413 // Convert a CFNumber to an external numeric form
415 template <class Value
>
416 bool CFScan::scannumber(CFTypeRef obj
)
418 ++format
; // consume format code
420 return true; // suppressed, okay
421 if (CFGetTypeID(obj
) != CFNumberGetTypeID())
423 store
<Value
>(cfNumber
<Value
>(CFNumberRef(obj
)));
429 // Process % scan forms.
430 // This delivers the object value, scanf-style, somehow.
432 bool CFScan::scanformat(CFTypeRef obj
)
436 store
<CFTypeRef
>(obj
);
439 return typescan(obj
, CFArrayGetTypeID()) == done
;
441 if (Typescan rc
= typescan(obj
, CFBooleanGetTypeID()))
444 case 'f': // %Bf - two arguments (value, &variable)
446 unsigned flag
= va_arg(args
, unsigned);
447 unsigned *value
= va_arg(args
, unsigned *);
448 if (obj
== kCFBooleanTrue
&& !suppress
)
452 default: // %b - CFBoolean as int boolean
453 store
<int>(obj
== kCFBooleanTrue
);
457 return typescan(obj
, CFDictionaryGetTypeID()) == done
;
458 case 'd': // %d - int
459 return scannumber
<int>(obj
);
461 return typescan(obj
, CFNumberGetTypeID()) == done
;
464 if (Typescan rc
= typescan(obj
, CFStringGetTypeID()))
467 store
<std::string
>(cfString(CFStringRef(obj
)));
470 return scannumber
<unsigned int>(obj
);
472 return typescan(obj
, CFDataGetTypeID()) == done
;
480 bool CFScan::scandictionary(CFDictionaryRef obj
)
482 ++format
; // skip '{'
483 while (next() != '}') {
484 bool optional
= next('?');
485 if (CFTypeRef key
= make()) {
486 bool oldSuppress
= suppress
;
487 CFTypeRef elem
= obj
? CFDictionaryGetValue(obj
, key
) : NULL
;
488 if (elem
|| optional
) {
489 suppress
|= (elem
== NULL
);
492 suppress
= oldSuppress
; // restore
501 assert(false); // bad format
509 bool CFScan::scanarray(CFArrayRef obj
)
511 ++format
; // skip '['
512 CFIndex length
= CFArrayGetCount(obj
);
513 for (int pos
= 0; pos
< length
; ++pos
) {
516 if (!scan(CFArrayGetValueAtIndex(obj
, pos
)))
519 return false; // array length exceeded
524 // Run down a "dictionary path", validating heavily.
526 CFTypeRef
CFScan::dictpath(CFTypeRef obj
)
528 while (next()) { // while we've got more text
529 next('.'); // optional
530 if (obj
== NULL
|| CFGetTypeID(obj
) != CFDictionaryGetTypeID())
532 CFTypeRef key
= make();
533 obj
= CFDictionaryGetValue(CFDictionaryRef(obj
), key
);
541 // The public functions
543 CFTypeRef
cfmake(const char *format
, ...)
546 va_start(args
, format
);
547 CFTypeRef result
= CFMake(format
, args
).make();
552 CFTypeRef
vcfmake(const char *format
, va_list args
)
554 return CFMake(format
, args
).make();
557 CFDictionaryRef
cfadd(CFMutableDictionaryRef dict
, const char *format
, ...)
560 va_start(args
, format
);
561 CFDictionaryRef result
= CFMake(format
, args
).addto(dict
);
567 bool cfscan(CFTypeRef obj
, const char *format
, ...)
570 va_start(args
, format
);
571 bool result
= vcfscan(obj
, format
, args
);
576 bool vcfscan(CFTypeRef obj
, const char *format
, va_list args
)
578 return CFScan(format
, args
).scan(obj
);
582 CFTypeRef
cfget(CFTypeRef obj
, const char *format
, ...)
585 va_start(args
, format
);
586 CFTypeRef result
= vcfget(obj
, format
, args
);
591 CFTypeRef
vcfget(CFTypeRef obj
, const char *format
, va_list args
)
593 return CFScan(format
, args
).dictpath(obj
);
596 } // end namespace Security