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