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
);
237 CFDictionaryRef
CFMake::add(CFMutableDictionaryRef dict
)
239 while (next() != '}') {
240 CFTypeRef key
= make();
247 if (CFTypeRef value
= make()) {
248 CFDictionaryAddValue(dict
, key
, value
);
261 CFDictionaryRef
CFMake::addto(CFMutableDictionaryRef dict
)
273 // Construct a CFArray
275 CFTypeRef CF_RETURNS_RETAINED
CFMake::makearray()
277 ++format
; // next '['
278 next('!'); // indicates mutable (currently always)
279 CFMutableArrayRef array
= NULL
;
280 if (next('+')) { // {+%O, => copy array argument, then proceed
281 if (next('%') && next('O')) {
282 CFArrayRef source
= va_arg(*args
, CFArrayRef
);
283 array
= CFArrayCreateMutableCopy(allocator
, 0, source
);
287 return NULL
; // bad syntax
289 array
= makeCFMutableArray(0);
291 while (next() != ']') {
292 CFTypeRef value
= make();
297 CFArrayAppendValue(array
, value
);
306 // A CFScan processes its format by parsing through an existing CF object
307 // structure, matching and extracting values as directed. Note that CFScan
308 // is a structure (tree) scanner rather than a linear parser, and will happily
309 // parse out a subset of the input object graph.
311 class CFScan
: public CFMake
{
313 CFScan(const char *format
, va_list *args
)
314 : CFMake(format
, args
), suppress(false) { }
316 bool scan(CFTypeRef obj
);
317 CFTypeRef
dictpath(CFTypeRef obj
);
320 bool scandictionary(CFDictionaryRef obj
);
321 bool scanarray(CFArrayRef obj
);
322 bool scanformat(CFTypeRef obj
);
324 enum Typescan
{ fail
= -1, more
= 0, done
= 1 };
325 Typescan
typescan(CFTypeRef obj
, CFTypeID type
);
327 template <class Value
>
328 bool scannumber(CFTypeRef obj
);
330 template <class Type
>
331 void store(Type value
);
333 bool suppress
; // output suppression
338 // Master scan function
340 bool CFScan::scan(CFTypeRef obj
)
342 while (next() == '@')
346 return true; // done, okay
348 if (obj
&& CFGetTypeID(obj
) != CFDictionaryGetTypeID())
350 return scandictionary(CFDictionaryRef(obj
));
352 if (obj
&& CFGetTypeID(obj
) != CFArrayGetTypeID())
354 return scanarray(CFArrayRef(obj
));
355 case '%': // return this value in some form
356 return scanformat(obj
);
357 case '=': // match value
360 CFTypeRef match
= make();
361 bool rc
= CFEqual(obj
, match
);
367 assert(false); // unexpected
377 // Primitive type-match helper.
378 // Ensures the object has the CF runtime type required, and processes
379 // the %?o format (return CFTypeRef) and %?n format (ignore value).
381 CFScan::Typescan
CFScan::typescan(CFTypeRef obj
, CFTypeID type
)
383 if (obj
&& CFGetTypeID(obj
) != type
)
386 case F_OBJECT
: // return CFTypeRef
388 store
<CFTypeRef
>(obj
);
390 case 'n': // suppress assignment
400 // Store a value into the next varargs slot, unless output suppression is on.
402 template <class Type
>
403 void CFScan::store(Type value
)
406 *va_arg(*args
, Type
*) = value
;
411 // Convert a CFNumber to an external numeric form
413 template <class Value
>
414 bool CFScan::scannumber(CFTypeRef obj
)
416 ++format
; // consume format code
418 return true; // suppressed, okay
419 if (CFGetTypeID(obj
) != CFNumberGetTypeID())
421 store
<Value
>(cfNumber
<Value
>(CFNumberRef(obj
)));
427 // Process % scan forms.
428 // This delivers the object value, scanf-style, somehow.
430 bool CFScan::scanformat(CFTypeRef obj
)
434 store
<CFTypeRef
>(obj
);
437 return typescan(obj
, CFArrayGetTypeID()) == done
;
439 if (Typescan rc
= typescan(obj
, CFBooleanGetTypeID()))
442 case 'f': // %Bf - two arguments (value, &variable)
444 unsigned flag
= va_arg(*args
, unsigned);
445 unsigned *value
= va_arg(*args
, unsigned *);
446 if (obj
== kCFBooleanTrue
&& !suppress
)
450 default: // %b - CFBoolean as int boolean
451 store
<int>(obj
== kCFBooleanTrue
);
455 return typescan(obj
, CFDictionaryGetTypeID()) == done
;
456 case 'd': // %d - int
457 return scannumber
<int>(obj
);
459 return typescan(obj
, CFNumberGetTypeID()) == done
;
462 if (Typescan rc
= typescan(obj
, CFStringGetTypeID()))
465 store
<std::string
>(cfString(CFStringRef(obj
)));
468 return scannumber
<unsigned int>(obj
);
470 return typescan(obj
, CFDataGetTypeID()) == done
;
478 bool CFScan::scandictionary(CFDictionaryRef obj
)
480 ++format
; // skip '{'
481 while (next() != '}') {
482 bool optional
= next('?');
483 if (CFTypeRef key
= make()) {
484 bool oldSuppress
= suppress
;
485 CFTypeRef elem
= obj
? CFDictionaryGetValue(obj
, key
) : NULL
;
486 if (elem
|| optional
) {
487 suppress
|= (elem
== NULL
);
490 suppress
= oldSuppress
; // restore
499 assert(false); // bad format
507 bool CFScan::scanarray(CFArrayRef obj
)
509 ++format
; // skip '['
510 CFIndex length
= CFArrayGetCount(obj
);
511 for (int pos
= 0; pos
< length
; ++pos
) {
514 if (!scan(CFArrayGetValueAtIndex(obj
, pos
)))
517 return false; // array length exceeded
522 // Run down a "dictionary path", validating heavily.
524 CFTypeRef
CFScan::dictpath(CFTypeRef obj
)
526 while (next()) { // while we've got more text
527 next('.'); // optional
528 if (obj
== NULL
|| CFGetTypeID(obj
) != CFDictionaryGetTypeID())
530 CFTypeRef key
= make();
531 obj
= CFDictionaryGetValue(CFDictionaryRef(obj
), key
);
539 // The public functions
541 CFTypeRef CF_RETURNS_RETAINED
cfmake(const char *format
, ...)
544 va_start(args
, format
);
545 CFTypeRef result
= CFMake(format
, &args
).make();
550 CFTypeRef CF_RETURNS_RETAINED
vcfmake(const char *format
, va_list *args
)
552 return CFMake(format
, args
).make();
555 CFDictionaryRef
cfadd(CFMutableDictionaryRef dict
, const char *format
, ...)
558 va_start(args
, format
);
559 CFDictionaryRef result
= CFMake(format
, &args
).addto(dict
);
565 bool cfscan(CFTypeRef obj
, const char *format
, ...)
568 va_start(args
, format
);
569 bool result
= vcfscan(obj
, format
, &args
);
574 bool vcfscan(CFTypeRef obj
, const char *format
, va_list *args
)
576 return CFScan(format
, args
).scan(obj
);
580 CFTypeRef
cfget(CFTypeRef obj
, const char *format
, ...)
583 va_start(args
, format
);
584 CFTypeRef result
= vcfget(obj
, format
, &args
);
589 CFTypeRef
vcfget(CFTypeRef obj
, const char *format
, va_list *args
)
591 return CFScan(format
, args
).dictpath(obj
);
594 } // end namespace Security