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