#include "cfmunge.h"
#include <security_utilities/cfutilities.h>
#include <security_utilities/errors.h>
namespace Security {
#define F_ARRAY 'A'
#define F_BOOLEAN 'B'
#define F_DATA 'X'
#define F_DICTIONARY 'D'
#define F_OBJECT 'O'
#define F_STRING 'S'
#define F_NUMBER 'N'
CFMunge::CFMunge(const char *fmt, va_list arg)
: format(fmt), allocator(NULL), error(noErr)
{
va_copy(args, arg);
}
CFMunge::~CFMunge()
{
va_end(args);
}
char CFMunge::next()
{
while (*format && (isspace(*format) || *format == ',')) ++format;
return *format;
}
bool CFMunge::next(char c)
{
if (next() == c) {
++format;
return true;
} else
return false;
}
bool CFMunge::parameter()
{
switch (*++format) {
case 'A':
++format;
allocator = va_arg(args, CFAllocatorRef);
return true;
case 'E':
++format;
error = va_arg(args, OSStatus);
return true;
default:
return false;
}
}
CFTypeRef CFMake::make()
{
while (next() == '@')
parameter();
switch (next()) {
case '\0':
return NULL;
case '{':
return makedictionary();
case '[':
return makearray();
case '\'':
return makestring();
case '%':
return makeformat();
case '#':
return makespecial();
case ']':
case '}':
assert(false); return NULL; default:
if (isdigit(*format))
return makenumber();
else if (isalpha(*format))
return makestring();
else {
assert(false);
return NULL;
}
}
}
CFTypeRef CFMake::makeformat()
{
++format;
switch (*format++) {
case 'b': {
const void *data = va_arg(args, const void *);
size_t length = va_arg(args, size_t);
return CFDataCreate(allocator, (const UInt8 *)data, length);
}
case F_BOOLEAN: return va_arg(args, int) ? kCFBooleanTrue : kCFBooleanFalse;
case 'd':
return makeCFNumber(va_arg(args, int));
case 's':
return CFStringCreateWithCString(allocator, va_arg(args, const char *),
kCFStringEncodingUTF8);
case F_OBJECT:
return CFRetain(va_arg(args, CFTypeRef));
case 'u':
return makeCFNumber(va_arg(args, unsigned int));
default:
assert(false);
return NULL;
}
}
CFTypeRef CFMake::makespecial()
{
++format;
switch (*format++) {
case 'N':
return kCFNull;
case 't':
case 'T':
return kCFBooleanTrue;
case 'f':
case 'F':
return kCFBooleanFalse;
default:
assert(false);
return NULL;
}
}
CFTypeRef CFMake::makenumber()
{
double value = strtod(format, (char **)&format);
return CFNumberCreate(allocator, kCFNumberDoubleType, &value);
}
CFTypeRef CFMake::makestring()
{
const char *start, *end;
if (*format == '\'') {
start = ++format; if (!(end = strchr(format, '\''))) {
assert(false);
return NULL;
}
format = end + 1;
} else {
start = format;
for (end = start + 1; isalnum(*end); ++end) ;
format = end;
}
return CFStringCreateWithBytes(allocator,
(const UInt8 *)start, end - start,
kCFStringEncodingUTF8, false);
}
CFTypeRef CFMake::makedictionary()
{
++format; next('!'); CFMutableDictionaryRef dict;
if (next('+')) { if (next('%') && next('O')) {
CFDictionaryRef source = va_arg(args, CFDictionaryRef);
dict = CFDictionaryCreateMutableCopy(allocator, NULL, source);
if (next('}'))
return dict;
} else
return NULL; } else
dict = CFDictionaryCreateMutable(allocator, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (add(dict))
return dict;
else {
CFRelease(dict);
return NULL;
}
}
CFDictionaryRef CFMake::add(CFMutableDictionaryRef dict)
{
while (next() != '}') {
CFTypeRef key = make();
if (key == NULL)
return NULL;
if (!next('=')) {
CFRelease(key);
return NULL;
}
if (CFTypeRef value = make()) {
CFDictionaryAddValue(dict, key, value);
CFRelease(key);
CFRelease(value);
} else {
CFRelease(key);
return NULL;
}
}
++format;
return dict;
}
CFDictionaryRef CFMake::addto(CFMutableDictionaryRef dict)
{
if (next('{'))
return add(dict);
else {
assert(false);
return NULL;
}
}
CFTypeRef CFMake::makearray()
{
++format; next('!'); CFMutableArrayRef array = makeCFMutableArray(0);
while (next() != ']') {
CFTypeRef value = make();
if (value == NULL) {
CFRelease(array);
return NULL;
}
CFArrayAppendValue(array, value);
CFRelease(value);
}
++format;
return array;
}
class CFScan : public CFMake {
public:
CFScan(const char *format, va_list args)
: CFMake(format, args), suppress(false) { }
bool scan(CFTypeRef obj);
CFTypeRef dictpath(CFTypeRef obj);
protected:
bool scandictionary(CFDictionaryRef obj);
bool scanarray(CFArrayRef obj);
bool scanformat(CFTypeRef obj);
enum Typescan { fail = -1, more = 0, done = 1 };
Typescan typescan(CFTypeRef obj, CFTypeID type);
template <class Value>
bool scannumber(CFTypeRef obj);
template <class Type>
void store(Type value);
bool suppress; };
bool CFScan::scan(CFTypeRef obj)
{
while (next() == '@')
parameter();
switch (next()) {
case '\0':
return true; case '{':
if (obj && CFGetTypeID(obj) != CFDictionaryGetTypeID())
return false;
return scandictionary(CFDictionaryRef(obj));
case '[':
if (obj && CFGetTypeID(obj) != CFArrayGetTypeID())
return false;
return scanarray(CFArrayRef(obj));
case '%': return scanformat(obj);
case '=': {
++format;
CFTypeRef match = make();
bool rc = CFEqual(obj, match);
CFRelease(match);
return rc;
}
case ']':
case '}':
assert(false); return false;
default:
assert(false);
return false;
}
}
CFScan::Typescan CFScan::typescan(CFTypeRef obj, CFTypeID type)
{
if (obj && CFGetTypeID(obj) != type)
return fail;
switch (*++format) {
case F_OBJECT: ++format;
store<CFTypeRef>(obj);
return done;
case 'n': ++format;
return done;
default:
return more;
}
}
template <class Type>
void CFScan::store(Type value)
{
if (!suppress)
*va_arg(args, Type *) = value;
}
template <class Value>
bool CFScan::scannumber(CFTypeRef obj)
{
++format; if (!obj)
return true; if (CFGetTypeID(obj) != CFNumberGetTypeID())
return false;
store<Value>(cfNumber<Value>(CFNumberRef(obj)));
return true;
}
bool CFScan::scanformat(CFTypeRef obj)
{
switch (*++format) {
case F_OBJECT:
store<CFTypeRef>(obj);
return true;
case F_ARRAY: return typescan(obj, CFArrayGetTypeID()) == done;
case F_BOOLEAN:
if (Typescan rc = typescan(obj, CFBooleanGetTypeID()))
return rc == done;
switch (*format) {
case 'f': {
unsigned flag = va_arg(args, unsigned);
unsigned *value = va_arg(args, unsigned *);
if (obj == kCFBooleanTrue && !suppress)
*value |= flag;
return true;
}
default: store<int>(obj == kCFBooleanTrue);
return true;
}
case F_DICTIONARY:
return typescan(obj, CFDictionaryGetTypeID()) == done;
case 'd': return scannumber<int>(obj);
case F_NUMBER:
return typescan(obj, CFNumberGetTypeID()) == done;
case F_STRING:
case 's':
if (Typescan rc = typescan(obj, CFStringGetTypeID()))
return rc == done;
store<std::string>(cfString(CFStringRef(obj)));
return true;
case 'u':
return scannumber<unsigned int>(obj);
case F_DATA:
return typescan(obj, CFDataGetTypeID()) == done;
default:
assert(false);
return false;
}
}
bool CFScan::scandictionary(CFDictionaryRef obj)
{
++format; while (next() != '}') {
bool optional = next('?');
if (CFTypeRef key = make()) {
bool oldSuppress = suppress;
CFTypeRef elem = obj ? CFDictionaryGetValue(obj, key) : NULL;
if (elem || optional) {
suppress |= (elem == NULL);
if (next('=')) {
if (scan(elem)) {
suppress = oldSuppress; CFRelease(key);
continue;
}
}
}
CFRelease(key);
return false;
} else {
assert(false); return false;
}
}
return true;
}
bool CFScan::scanarray(CFArrayRef obj)
{
++format; CFIndex length = CFArrayGetCount(obj);
for (int pos = 0; pos < length; ++pos) {
if (next() == ']')
return true;
if (!scan(CFArrayGetValueAtIndex(obj, pos)))
return false;
}
return false; }
CFTypeRef CFScan::dictpath(CFTypeRef obj)
{
while (next()) { next('.'); if (obj == NULL || CFGetTypeID(obj) != CFDictionaryGetTypeID())
return NULL;
CFTypeRef key = make();
obj = CFDictionaryGetValue(CFDictionaryRef(obj), key);
CFRelease(key);
}
return obj;
}
CFTypeRef cfmake(const char *format, ...)
{
va_list args;
va_start(args, format);
CFTypeRef result = CFMake(format, args).make();
va_end(args);
return result;
}
CFTypeRef vcfmake(const char *format, va_list args)
{
return CFMake(format, args).make();
}
CFDictionaryRef cfadd(CFMutableDictionaryRef dict, const char *format, ...)
{
va_list args;
va_start(args, format);
CFDictionaryRef result = CFMake(format, args).addto(dict);
va_end(args);
return result;
}
bool cfscan(CFTypeRef obj, const char *format, ...)
{
va_list args;
va_start(args, format);
bool result = vcfscan(obj, format, args);
va_end(args);
return result;
}
bool vcfscan(CFTypeRef obj, const char *format, va_list args)
{
return CFScan(format, args).scan(obj);
}
CFTypeRef cfget(CFTypeRef obj, const char *format, ...)
{
va_list args;
va_start(args, format);
CFTypeRef result = vcfget(obj, format, args);
va_end(args);
return result;
}
CFTypeRef vcfget(CFTypeRef obj, const char *format, va_list args)
{
return CFScan(format, args).dictpath(obj);
}
}