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 // Initialize a CFMunge. We start out with the default CFAllocator, and 
  51 // we do not throw errors. 
  53 CFMunge::CFMunge(const char *fmt
, va_list arg
) 
  54         : format(fmt
), allocator(NULL
), error(errSecSuccess
) 
  66 // Skip whitespace and other fluff and deliver the next significant character. 
  70         while (*format 
&& (isspace(*format
) || *format 
== ',')) ++format
; 
  76 // Locate and consume an optional character 
  78 bool CFMunge::next(char c
) 
  89 // Process @? parameter specifications. 
  90 // The @ operator is used for side effects, and does not return a value. 
  92 bool CFMunge::parameter() 
  97                 allocator 
= va_arg(args
, CFAllocatorRef
); 
 101                 error 
= va_arg(args
, OSStatus
); 
 110 // The top constructor. 
 112 CFTypeRef 
CFMake::make() 
 114         while (next() == '@') 
 120                 return makedictionary(); 
 128                 return makespecial(); 
 131                 return NULL
;    // error 
 133                 if (isdigit(*format
) || *format 
== '-') 
 135                 else if (isalpha(*format
)) 
 145 CFTypeRef 
CFMake::makeformat() 
 149         case 'b':       // blob (pointer, length) 
 151                         const void *data 
= va_arg(args
, const void *); 
 152                         size_t length 
= va_arg(args
, size_t); 
 153                         return CFDataCreate(allocator
, (const UInt8 
*)data
, length
); 
 155         case F_BOOLEAN
: // boolean (with int promotion) 
 156                 return va_arg(args
, int) ? kCFBooleanTrue 
: kCFBooleanFalse
; 
 158                 return makeCFNumber(va_arg(args
, int)); 
 160                 return CFStringCreateWithCString(allocator
, va_arg(args
, const char *), 
 161                         kCFStringEncodingUTF8
); 
 163                 return CFRetain(va_arg(args
, CFTypeRef
)); 
 165                 return makeCFNumber(va_arg(args
, unsigned int)); 
 173 CFTypeRef 
CFMake::makespecial() 
 181                 return kCFBooleanTrue
; 
 184                 return kCFBooleanFalse
; 
 192 CFTypeRef 
CFMake::makenumber() 
 194         double value 
= strtod(format
, (char **)&format
); 
 195         return CFNumberCreate(allocator
, kCFNumberDoubleType
, &value
); 
 200 // Embedded strings can either be alphanumeric (only), or delimited with single quotes ''. 
 201 // No escapes are processed within such quotes. If you want arbitrary string values, use %s. 
 203 CFTypeRef 
CFMake::makestring() 
 205         const char *start
, *end
; 
 206         if (*format 
== '\'') { 
 207                 start 
= ++format
;       // next quote 
 208                 if (!(end 
= strchr(format
, '\''))) { 
 215                 for (end 
= start 
+ 1; isalnum(*end
); ++end
) ; 
 218         return CFStringCreateWithBytes(allocator
, 
 219                 (const UInt8 
*)start
, end 
- start
, 
 220                 kCFStringEncodingUTF8
, false); 
 225 // Construct a CFDictionary 
 227 CFTypeRef 
CFMake::makedictionary() 
 229         ++format
;       // next '{' 
 230         next('!');      // indicates mutable (currently always true) 
 231         CFMutableDictionaryRef dict
; 
 232         if (next('+')) { // {+%O, => copy dictionary argument, then proceed 
 233                 if (next('%') && next('O')) { 
 234                         CFDictionaryRef source 
= va_arg(args
, CFDictionaryRef
); 
 235                         dict 
= CFDictionaryCreateMutableCopy(allocator
, NULL
, source
); 
 239                         return NULL
;    // bad syntax 
 241                 dict 
= CFDictionaryCreateMutable(allocator
, 0, 
 242                         &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
 251 CFDictionaryRef 
CFMake::add(CFMutableDictionaryRef dict
) 
 253         while (next() != '}') { 
 254                 CFTypeRef key 
= make(); 
 261                 if (CFTypeRef value 
= make()) { 
 262                         CFDictionaryAddValue(dict
, key
, value
); 
 275 CFDictionaryRef 
CFMake::addto(CFMutableDictionaryRef dict
) 
 287 // Construct a CFArray 
 289 CFTypeRef 
CFMake::makearray() 
 291         ++format
;       // next '[' 
 292         next('!');      // indicates mutable (currently always) 
 293         CFMutableArrayRef array 
= NULL
; 
 294         if (next('+')) { // {+%O, => copy array argument, then proceed 
 295                 if (next('%') && next('O')) { 
 296                         CFArrayRef source 
= va_arg(args
, CFArrayRef
); 
 297                         array 
= CFArrayCreateMutableCopy(allocator
, 0, source
); 
 301                         return NULL
;    // bad syntax 
 303                 array 
= makeCFMutableArray(0); 
 305         while (next() != ']') { 
 306                 CFTypeRef value 
= make(); 
 311                 CFArrayAppendValue(array
, value
); 
 320 // A CFScan processes its format by parsing through an existing CF object 
 321 // structure, matching and extracting values as directed. Note that CFScan 
 322 // is a structure (tree) scanner rather than a linear parser, and will happily 
 323 // parse out a subset of the input object graph. 
 325 class CFScan 
: public CFMake 
{ 
 327         CFScan(const char *format
, va_list args
) 
 328                 : CFMake(format
, args
), suppress(false) { } 
 330         bool scan(CFTypeRef obj
); 
 331         CFTypeRef 
dictpath(CFTypeRef obj
); 
 334         bool scandictionary(CFDictionaryRef obj
); 
 335         bool scanarray(CFArrayRef obj
); 
 336         bool scanformat(CFTypeRef obj
); 
 338         enum Typescan 
{ fail 
= -1, more 
= 0, done 
= 1 }; 
 339         Typescan 
typescan(CFTypeRef obj
, CFTypeID type
); 
 341         template <class Value
> 
 342         bool scannumber(CFTypeRef obj
); 
 344         template <class Type
> 
 345         void store(Type value
); 
 347         bool suppress
;                          // output suppression 
 352 // Master scan function 
 354 bool CFScan::scan(CFTypeRef obj
) 
 356         while (next() == '@') 
 360                 return true;    // done, okay 
 362                 if (obj 
&& CFGetTypeID(obj
) != CFDictionaryGetTypeID()) 
 364                 return scandictionary(CFDictionaryRef(obj
)); 
 366                 if (obj 
&& CFGetTypeID(obj
) != CFArrayGetTypeID()) 
 368                 return scanarray(CFArrayRef(obj
)); 
 369         case '%':       // return this value in some form 
 370                 return scanformat(obj
); 
 371         case '=':       // match value 
 374                         CFTypeRef match 
= make(); 
 375                         bool rc 
= CFEqual(obj
, match
); 
 381                 assert(false);  // unexpected 
 391 // Primitive type-match helper. 
 392 // Ensures the object has the CF runtime type required, and processes 
 393 // the %?o format (return CFTypeRef) and %?n format (ignore value). 
 395 CFScan::Typescan 
CFScan::typescan(CFTypeRef obj
, CFTypeID type
) 
 397         if (obj 
&& CFGetTypeID(obj
) != type
) 
 400         case F_OBJECT
:  // return CFTypeRef 
 402                 store
<CFTypeRef
>(obj
); 
 404         case 'n':       // suppress assignment 
 414 // Store a value into the next varargs slot, unless output suppression is on. 
 416 template <class Type
> 
 417 void CFScan::store(Type value
) 
 420                 *va_arg(args
, Type 
*) = value
; 
 425 // Convert a CFNumber to an external numeric form 
 427 template <class Value
> 
 428 bool CFScan::scannumber(CFTypeRef obj
) 
 430         ++format
;       // consume format code 
 432                 return true; // suppressed, okay 
 433         if (CFGetTypeID(obj
) != CFNumberGetTypeID()) 
 435         store
<Value
>(cfNumber
<Value
>(CFNumberRef(obj
))); 
 441 // Process % scan forms. 
 442 // This delivers the object value, scanf-style, somehow. 
 444 bool CFScan::scanformat(CFTypeRef obj
) 
 448                 store
<CFTypeRef
>(obj
); 
 451                 return typescan(obj
, CFArrayGetTypeID()) == done
; 
 453                 if (Typescan rc 
= typescan(obj
, CFBooleanGetTypeID())) 
 456                 case 'f':       // %Bf - two arguments (value, &variable) 
 458                                 unsigned flag 
= va_arg(args
, unsigned); 
 459                                 unsigned *value 
= va_arg(args
, unsigned *); 
 460                                 if (obj 
== kCFBooleanTrue 
&& !suppress
) 
 464                 default:        // %b - CFBoolean as int boolean 
 465                         store
<int>(obj 
== kCFBooleanTrue
); 
 469                 return typescan(obj
, CFDictionaryGetTypeID()) == done
; 
 470         case 'd':       // %d - int 
 471                 return scannumber
<int>(obj
); 
 473                 return typescan(obj
, CFNumberGetTypeID()) == done
; 
 476                 if (Typescan rc 
= typescan(obj
, CFStringGetTypeID())) 
 479                 store
<std::string
>(cfString(CFStringRef(obj
))); 
 482                 return scannumber
<unsigned int>(obj
); 
 484                 return typescan(obj
, CFDataGetTypeID()) == done
; 
 492 bool CFScan::scandictionary(CFDictionaryRef obj
) 
 494         ++format
;       // skip '{' 
 495         while (next() != '}') { 
 496                 bool optional 
= next('?'); 
 497                 if (CFTypeRef key 
= make()) { 
 498                         bool oldSuppress 
= suppress
; 
 499                         CFTypeRef elem 
= obj 
? CFDictionaryGetValue(obj
, key
) : NULL
; 
 500                         if (elem 
|| optional
) { 
 501                                 suppress 
|= (elem 
== NULL
); 
 504                                                 suppress 
= oldSuppress
; // restore 
 513                         assert(false);  // bad format 
 521 bool CFScan::scanarray(CFArrayRef obj
) 
 523         ++format
;       // skip '[' 
 524         CFIndex length 
= CFArrayGetCount(obj
); 
 525         for (int pos 
= 0; pos 
< length
; ++pos
) { 
 528                 if (!scan(CFArrayGetValueAtIndex(obj
, pos
))) 
 531         return false;   // array length exceeded 
 536 // Run down a "dictionary path", validating heavily. 
 538 CFTypeRef 
CFScan::dictpath(CFTypeRef obj
) 
 540         while (next()) {        // while we've got more text 
 541                 next('.');              // optional 
 542                 if (obj 
== NULL 
|| CFGetTypeID(obj
) != CFDictionaryGetTypeID()) 
 544                 CFTypeRef key 
= make(); 
 545                 obj 
= CFDictionaryGetValue(CFDictionaryRef(obj
), key
); 
 553 // The public functions 
 555 CFTypeRef 
cfmake(const char *format
, ...) 
 558         va_start(args
, format
); 
 559         CFTypeRef result 
= CFMake(format
, args
).make(); 
 564 CFTypeRef 
vcfmake(const char *format
, va_list args
) 
 566         return CFMake(format
, args
).make(); 
 569 CFDictionaryRef 
cfadd(CFMutableDictionaryRef dict
, const char *format
, ...) 
 572         va_start(args
, format
); 
 573         CFDictionaryRef result 
= CFMake(format
, args
).addto(dict
); 
 579 bool cfscan(CFTypeRef obj
, const char *format
, ...) 
 582         va_start(args
, format
); 
 583         bool result 
= vcfscan(obj
, format
, args
); 
 588 bool vcfscan(CFTypeRef obj
, const char *format
, va_list args
) 
 590         return CFScan(format
, args
).scan(obj
); 
 594 CFTypeRef 
cfget(CFTypeRef obj
, const char *format
, ...) 
 597         va_start(args
, format
); 
 598         CFTypeRef result 
= vcfget(obj
, format
, args
); 
 603 CFTypeRef 
vcfget(CFTypeRef obj
, const char *format
, va_list args
) 
 605         return CFScan(format
, args
).dictpath(obj
); 
 608 }       // end namespace Security