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 // Skip whitespace and other fluff and deliver the next significant character.
54 while (*format
&& (isspace(*format
) || *format
== ',')) ++format
;
60 // Locate and consume an optional character
62 bool CFMunge::next(char c
)
73 // Process @? parameter specifications.
74 // The @ operator is used for side effects, and does not return a value.
76 bool CFMunge::parameter()
81 allocator
= va_arg(*args
, CFAllocatorRef
);
85 error
= va_arg(*args
, OSStatus
);
94 // The top constructor.
96 CFTypeRef CF_RETURNS_RETAINED
CFMake::make()
104 return makedictionary();
112 return makespecial();
115 return NULL
; // error
117 if (isdigit(*format
) || *format
== '-')
119 else if (isalpha(*format
))
129 CFTypeRef CF_RETURNS_RETAINED
CFMake::makeformat()
133 case 'b': // blob (pointer, length)
135 const void *data
= va_arg(*args
, const void *);
136 size_t length
= va_arg(*args
, size_t);
137 return CFDataCreate(allocator
, (const UInt8
*)data
, length
);
139 case F_BOOLEAN
: // boolean (with int promotion)
140 return va_arg(*args
, int) ? kCFBooleanTrue
: kCFBooleanFalse
;
142 return makeCFNumber(va_arg(*args
, int));
144 return CFStringCreateWithCString(allocator
, va_arg(*args
, const char *),
145 kCFStringEncodingUTF8
);
147 return CFRetain(va_arg(*args
, CFTypeRef
));
149 return makeCFNumber(va_arg(*args
, unsigned int));
157 CFTypeRef
CFMake::makespecial()
165 return kCFBooleanTrue
;
168 return kCFBooleanFalse
;
176 CFTypeRef
CFMake::makenumber()
178 double value
= strtod(format
, (char **)&format
);
179 return CFNumberCreate(allocator
, kCFNumberDoubleType
, &value
);
184 // Embedded strings can either be alphanumeric (only), or delimited with single quotes ''.
185 // No escapes are processed within such quotes. If you want arbitrary string values, use %s.
187 CFTypeRef CF_RETURNS_RETAINED
CFMake::makestring()
189 const char *start
, *end
;
190 if (*format
== '\'') {
191 start
= ++format
; // next quote
192 if (!(end
= strchr(format
, '\''))) {
199 for (end
= start
+ 1; isalnum(*end
); ++end
) ;
202 return CFStringCreateWithBytes(allocator
,
203 (const UInt8
*)start
, end
- start
,
204 kCFStringEncodingUTF8
, false);
209 // Construct a CFDictionary
211 CFTypeRef CF_RETURNS_RETAINED
CFMake::makedictionary()
213 ++format
; // next '{'
214 next('!'); // indicates mutable (currently always true)
215 CFMutableDictionaryRef dict
;
216 if (next('+')) { // {+%O, => copy dictionary argument, then proceed
217 if (next('%') && next('O')) {
218 CFDictionaryRef source
= va_arg(*args
, CFDictionaryRef
);
219 dict
= CFDictionaryCreateMutableCopy(allocator
, NULL
, source
);
223 return NULL
; // bad syntax
225 dict
= CFDictionaryCreateMutable(allocator
, 0,
226 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
241 CFDictionaryRef
CFMake::add(CFMutableDictionaryRef dict
)
243 while (next() != '}') {
244 CFTypeRef key
= make();
251 if (CFTypeRef value
= make()) {
252 CFDictionaryAddValue(dict
, key
, value
);
265 CFDictionaryRef
CFMake::addto(CFMutableDictionaryRef dict
)
277 // Construct a CFArray
279 CFTypeRef CF_RETURNS_RETAINED
CFMake::makearray()
281 ++format
; // next '['
282 next('!'); // indicates mutable (currently always)
283 CFMutableArrayRef array
= NULL
;
284 if (next('+')) { // {+%O, => copy array argument, then proceed
285 if (next('%') && next('O')) {
286 CFArrayRef source
= va_arg(*args
, CFArrayRef
);
287 array
= CFArrayCreateMutableCopy(allocator
, 0, source
);
291 return NULL
; // bad syntax
293 array
= makeCFMutableArray(0);
295 while (next() != ']') {
296 CFTypeRef value
= make();
301 CFArrayAppendValue(array
, value
);
310 // A CFScan processes its format by parsing through an existing CF object
311 // structure, matching and extracting values as directed. Note that CFScan
312 // is a structure (tree) scanner rather than a linear parser, and will happily
313 // parse out a subset of the input object graph.
315 class CFScan
: public CFMake
{
317 CFScan(const char *format
, va_list *args
)
318 : CFMake(format
, args
), suppress(false) { }
320 bool scan(CFTypeRef obj
);
321 CFTypeRef
dictpath(CFTypeRef obj
);
324 bool scandictionary(CFDictionaryRef obj
);
325 bool scanarray(CFArrayRef obj
);
326 bool scanformat(CFTypeRef obj
);
328 enum Typescan
{ fail
= -1, more
= 0, done
= 1 };
329 Typescan
typescan(CFTypeRef obj
, CFTypeID type
);
331 template <class Value
>
332 bool scannumber(CFTypeRef obj
);
334 template <class Type
>
335 void store(Type value
);
337 bool suppress
; // output suppression
342 // Master scan function
344 bool CFScan::scan(CFTypeRef obj
)
346 while (next() == '@')
350 return true; // done, okay
352 if (obj
&& CFGetTypeID(obj
) != CFDictionaryGetTypeID())
354 return scandictionary(CFDictionaryRef(obj
));
356 if (obj
&& CFGetTypeID(obj
) != CFArrayGetTypeID())
358 return scanarray(CFArrayRef(obj
));
359 case '%': // return this value in some form
360 return scanformat(obj
);
361 case '=': // match value
364 CFTypeRef match
= make();
365 bool rc
= CFEqual(obj
, match
);
371 assert(false); // unexpected
381 // Primitive type-match helper.
382 // Ensures the object has the CF runtime type required, and processes
383 // the %?o format (return CFTypeRef) and %?n format (ignore value).
385 CFScan::Typescan
CFScan::typescan(CFTypeRef obj
, CFTypeID type
)
387 if (obj
&& CFGetTypeID(obj
) != type
)
390 case F_OBJECT
: // return CFTypeRef
392 store
<CFTypeRef
>(obj
);
394 case 'n': // suppress assignment
404 // Store a value into the next varargs slot, unless output suppression is on.
406 template <class Type
>
407 void CFScan::store(Type value
)
410 *va_arg(*args
, Type
*) = value
;
415 // Convert a CFNumber to an external numeric form
417 template <class Value
>
418 bool CFScan::scannumber(CFTypeRef obj
)
420 ++format
; // consume format code
422 return true; // suppressed, okay
423 if (CFGetTypeID(obj
) != CFNumberGetTypeID())
425 store
<Value
>(cfNumber
<Value
>(CFNumberRef(obj
)));
431 // Process % scan forms.
432 // This delivers the object value, scanf-style, somehow.
434 bool CFScan::scanformat(CFTypeRef obj
)
438 store
<CFTypeRef
>(obj
);
441 return typescan(obj
, CFArrayGetTypeID()) == done
;
443 if (Typescan rc
= typescan(obj
, CFBooleanGetTypeID()))
446 case 'f': // %Bf - two arguments (value, &variable)
448 unsigned flag
= va_arg(*args
, unsigned);
449 unsigned *value
= va_arg(*args
, unsigned *);
450 if (obj
== kCFBooleanTrue
&& !suppress
)
454 default: // %b - CFBoolean as int boolean
455 store
<int>(obj
== kCFBooleanTrue
);
459 return typescan(obj
, CFDictionaryGetTypeID()) == done
;
460 case 'd': // %d - int
461 return scannumber
<int>(obj
);
463 return typescan(obj
, CFNumberGetTypeID()) == done
;
466 if (Typescan rc
= typescan(obj
, CFStringGetTypeID()))
469 store
<std::string
>(cfString(CFStringRef(obj
)));
472 return scannumber
<unsigned int>(obj
);
474 return typescan(obj
, CFDataGetTypeID()) == done
;
482 bool CFScan::scandictionary(CFDictionaryRef obj
)
484 ++format
; // skip '{'
485 while (next() != '}') {
486 bool optional
= next('?');
487 if (CFTypeRef key
= make()) {
488 bool oldSuppress
= suppress
;
489 CFTypeRef elem
= obj
? CFDictionaryGetValue(obj
, key
) : NULL
;
490 if (elem
|| optional
) {
491 suppress
|= (elem
== NULL
);
494 suppress
= oldSuppress
; // restore
503 assert(false); // bad format
511 bool CFScan::scanarray(CFArrayRef obj
)
513 ++format
; // skip '['
514 CFIndex length
= CFArrayGetCount(obj
);
515 for (int pos
= 0; pos
< length
; ++pos
) {
518 if (!scan(CFArrayGetValueAtIndex(obj
, pos
)))
521 return false; // array length exceeded
526 // Run down a "dictionary path", validating heavily.
528 CFTypeRef
CFScan::dictpath(CFTypeRef obj
)
530 while (next()) { // while we've got more text
531 next('.'); // optional
532 if (obj
== NULL
|| CFGetTypeID(obj
) != CFDictionaryGetTypeID())
534 CFTypeRef key
= make();
535 obj
= CFDictionaryGetValue(CFDictionaryRef(obj
), key
);
543 // The public functions
545 CFTypeRef CF_RETURNS_RETAINED
cfmake(const char *format
, ...)
548 va_start(args
, format
);
549 CFTypeRef result
= CFMake(format
, &args
).make();
554 CFTypeRef CF_RETURNS_RETAINED
vcfmake(const char *format
, va_list *args
)
556 return CFMake(format
, args
).make();
559 CFDictionaryRef
cfadd(CFMutableDictionaryRef dict
, const char *format
, ...)
562 va_start(args
, format
);
563 CFDictionaryRef result
= CFMake(format
, &args
).addto(dict
);
569 bool cfscan(CFTypeRef obj
, const char *format
, ...)
572 va_start(args
, format
);
573 bool result
= vcfscan(obj
, format
, &args
);
578 bool vcfscan(CFTypeRef obj
, const char *format
, va_list *args
)
580 return CFScan(format
, args
).scan(obj
);
584 CFTypeRef
cfget(CFTypeRef obj
, const char *format
, ...)
587 va_start(args
, format
);
588 CFTypeRef result
= vcfget(obj
, format
, &args
);
593 CFTypeRef
vcfget(CFTypeRef obj
, const char *format
, va_list *args
)
595 return CFScan(format
, args
).dictpath(obj
);
598 } // end namespace Security